Symbolic Debuggers
The "oldest" approach to finding out what the internal state of a program
is at various stages is to add print statements to the source code.
This method is inefficient and ineffective.
- It is inefficient because it requires an
edit-compile-execute cycle each time you want to add or change a print
statement.
- It is ineffective because printing may be asynchronous,
so at the time the output appears the program may have executed many more
statements. The last (and most interesting) several print statements may
never appear although the program has reached them before crashing.
A symbolic debugger offers several features for monitoring the
internal state of a program. Most debuggers to date offer source-level
debugging.
- A stack trace provides a snapshot of all function (method) calls,
from "main" down to the last (innermost) function call, with for each call
the source line of the function call statement.
Stack traces are most useful for inspecting a dump after a crash.
- A running program can be interrupted to inspect values of variables.
Some debuggers let you do such inspection each time the program waits for
user-input, but others do not.
- You can set breakpoints at a line in the source code
(or in some instances also at exception handlers).
When the program reaches a breakpoint it stops to let you inspect variables.
In multi-threaded environments you need to specify whether all threads need
to be suspended or only the one which reaches the breakpoint.
- You can set watches (or traces) to monitor changes
in values of variables. Debuggers that are able to show changes to variables
immediately, irrespective of what causes the change, tend to be very slow.
The Unix debugger dbx is very slow when tracing variables,
because the values have to be verified after each machine instruction.
- You can single step through a program. A step is normally a line
of source code.
When the line contains a function call you can either execute the whole
function as one step or step into the function.