The main goal of debugging is to find bugs. The most important question is how? The entry criterion of bug hunting is obviously the knowledge of your code. If you don’t understand the code in an appropriate level you will find the fault with difficulties and your code fix will results in newer bugs, while the code becomes worse. Assume that your code understanding is sufficient for debugging. What to do next?
Most of the program executions resulting in a failure have a long trace containing lots of execution steps. Checking all of them is almost impossible and it would take too long. We need methods which reduces the number of execution steps to consider. The methods reducing the executions to be investigated are called active debugging.
On the contrary, debugging methods which leave all the execution steps to be visited are called passive debugging. What do these debuggers do? They display or log the available data of the inner state of an execution step. Traditional debuggers and loggers fall into this category.It seems, something is wrong with this argument since you can insert breakpoints at any place and skip most of the execution steps. However, inserting breakpoints can only be possible if you apply an active method.
Known active debugging techniques
During debugging you always make hypotheses. These can be positive and negative. A negative hypothesis is when you exclude some parts of the executed code as being faulty. If you assume, then a method cannot be a source of the failure you can step over it. That’s why code understanding is so important.
A positive hypothesis is on the contrary an assumption what can be wrong. If you have a clear hypotheses, you can concentrate on those part of the code which support or falsify your hypothesis. If the hypothesis is good you can narrow it, if it’s wrong you have to change it. For example, you can guess that the cause of a sorting problem is the erroneous handling of hyphens in family names.
Active debugging based on hypotheses cannot be automated. You have to use your brain. There are other methods, however, which can be automated. The first one is input reduction, also called as delta debugging, see http://java.dzone.com/articles/debugging-step-step-delta. This is the first step of bug hunting after understanding the code. Here is an example.
Consider a list of 100 employees in alphabetic order, and assume the order is false. What to do? We can use a traditional debugger and try to find the fault. However it’s a completely wrong solution. Firstly, we should reduce the input, i.e. the number of employees. Starting from the wrong ordering we observed in the list, we can construct a test that fails as well, with only quite a few names.
What did happen? We have a new execution trace with much less execution steps. This is clearly active debugging. By applying the method input is minimized so that the result of the execution be correct for any smaller input, but faulty for the selected one. The method can be automated, or you can do this work manually depending on the actual case.
Another active debugging method introduced by our Jidebug team is comparison debugging.
Assume we have a bug, i.e. a test execution failed. If we have another, but bug free execution we can use it as standard. Actually, if the inputs of the two executions are the same or similar so that the two executions should cover the same execution path, the difference is a good starting point of bug hunting, and analyzing the differences you either will find the fault or you can make a good hypothesis. For example, if the execution passes for loading a small image, but fails for a larger one, the first can be used as a standard. There should be some differences in the execution trace or the two executions should behave identically. The method can primarily be used for non-reproducible bugs when the test fails at the tester, but works well at the developer’s environment.
To learn more please wait for our subsequent article coming soon. If you cannot wait, you can jump to our home page http://jidebug.com, or try our tool based on comparison debugging.
Considering yet another active debugging technique, here is a short introduction of influence debugging.
Assume you detect a failure during a program execution (or which is the same, a test case). Using a traditional debugger you should analyze quite a lot statements superfluously. Why? Because about 90% of the code has no influence on the failure. If so, why we don’t omit them? Yes, this is the solution: let’s filter out all the code/statements which have no influence on the failure. Therefore, an appropriate tool (Jidebug) can collect all the influences and arrange them as influence chains. If you go back along these chains you may find the defect so that no superfluous statements are traced. This is obviously an active debugging method, which can be used for many cases, even if for missing case errors (when some code is missing).
We are planning a detailed article about influence debugging, but anxious reader can go to the Jidebug page above.
Efficient debugging applies active methods
Active debuggers can be used together. For example, after reducing the input (delta debugging), you can use comparison debugging. Starting from a selected execution difference you can apply both influence debugging and hypothesis to find the bug finally.
Passive and active debugging complete each other, thus you should use them together. However, to reduce debugging time you have to cut execution points to be visited as much as possible. This can be done only by applying active debugging methods. That’s why active debugging is so important.