|
Table of Contents
Traditional DebuggingCharacteristic features of traditional debuggersMost IDEs for Object-Oriented programming provide the same style of debugging facilities: Controlled executionThe programmer can place breakpoints in the code at specific locations to cause the execution of the software to be suspended when the location is reached (but before it is executed). It is usually possible to specify triggering conditions for a breakpoint, like a hit count, or conditions on the value of certain variables. Once the execution is suspended, one can perform step by step execution. The most common kinds of steps are:
Some IDEs also provide the ability to manipulate the call stack: they permit to restart the execution of the current method, or of any method in the call stack, providing a way to get “back in time” (for instance in Eclipse this feature is called “drop to frame”). Unfortunately, it only rewinds the call stack and doesn't undo any side effect of the code that has already been executed. Memory inspectionWhenever the execution is suspended, the programmer can inspect the values of the variables of the current scope, as well as variables that aren't in the current scope but that were registered for inspection during a previous suspend.
The previous screenshot shows how Eclipse presents in-scope variables when execution is suspended within a method. The left pane shows the code of the current method. The location at which execution is suspended is highlighted in green. The values of the variables are displayed in the right pane. Variables are presented in a tree in which scalar values are leaves, and objects are nodes below which there is one child per field of the object. Other traditional debuggers present the information in a very similar way. The issues of traditional debuggingFixing a bug is a two-step process:
This section illustrates the fact that both controlled execution and hierarchical memory inspection are simply inadequate for debugging even simple programs. Debugging using controlled execution is not efficientWith a traditional debugger, locating faulty instructions can be very tedious. The usual scheme is to put a breakpoint at a location that is executed before the bug occurs, and then stepping through the code until the misbehaviour is found. The issue is that even in simple systems, the faulty instructions can be buried deep down the call hierarchy. Consider the following Java example: public class ObjectX{ private Info info = new Info("name"); public String toString(){ updateInfo(); return "ObjectX: " + info.getName(); // NPE thrown here } private void updateInfo(){ update1(); update2(); update3(); } // Here goes the definitions of update1, update2 and update2 }
At runtime, the
A programmer commonly uses a combination of both approaches, which are both tedious, time-consuming and error-prone. Moreover debugging the program can have undesired side effects, for instance if the program writes to the filesystem, accesses a database, performs low-level IO, etc. Additionally, several factors can makes the problem even more complex:
Hierarchical memory inspection is not efficientOrthogonally to the difficulty of driving the code flow to the faulty instruction depicted above, there is the problem of understanding a program's data structures. A simple illustration of the inadequacy of hierarchical view of objects is the linked list, which a traditional debugger would show in the following way:
And this is only for a 3-elements list! Fortunately modern traditional debuggers have a set of built-in presentations for common data structures like lists, maps, etc. For instance Eclipse can, if the user so decides, represent the previous list in this way:
Eclipse also lets the user define his own presentation for custom types. For instance a tree structure that would be represented as follows…
… can be represented in a more natural way:
However the programmer cannot define presentations that do not map to a tree. A graph for instance can't be represented in a useful way, and the programmer must resolve references by hand; the only way to obtain a meaningful representation of his data is to draw it himself on a sheet of paper, which is extremely time-consuming. |