Prologue
The goal of this article is to show a number of
basic concepts to users who have never used a
debugger before or having used it, is looking for a
graphical environment more pleasant for his daily
work. Much can be written about the capabilities and
robustness of the debugger described (gdb) however
we decided to keep things simple for didactical
purposes, as usual :)
What is a debugger?
" Once upon a time there was a programmer
whose only task when finding a bug in his code
was:
/*Code*/
(...)
loop
change_a_variable;
show_value_of_variable;
end_loop
(...)
He was forced to insert numerous times these
lines through out his source code in order to
inspect the values of the program variables during
running-time. It was a hard task and made his life
painful and the cost of debugging his code twice the
cost of writing it(..)".
Who has never found himself in such a situation?
often there is an error in our program, we have
changed and tried everything, at this point we are
almost convinced it is "the fault of the compiler"
since there is little less to try....Well here is
were the debugging software comes into play.
A debugger lets you control the execution of a
program step by step, this way it is easy to examine
the state of variables, their definitions, what
would happen upon certain conditions, etc.. All
this, once again, iteratively while the code being
debugged is running. If this pedestrian definition
is not very clear I hope to make it more transparent
during the article.
What would happen if the programmer in our story
had a debugger named "spy" that would let him do the
following:
jose# spy my_program
and what if after invoking "spy" our programmer
could take the following actions:
spy > execute my_program "up to line number 50"
spy > show me the value of the variable <X>
spy > what is the type of variable <X>
Very likely at this moment our imaginary
programmer would be jumping of happiness because he
finally found the reason for the bug.
Clearly the tool named "spy" has been very useful
because it has let the programmer to execute the
program at his will while examining the values and
definitions of the programs variables. This is in
essence a DEBUGGER, very grossly pictured of
course.
Warning !!: Debuggers can only operate on
programs that have been compiled with the debug
option, "-g" in the case of the GNU gcc
compiler.
There is a debugging tool available to all LINUX
users (and in many other platforms), is the GDB
" The GNU Source-Level Debugger".
It is available for the same price, and under the
same license than the operative system you are most
likely to read this article, the GNU General Public
License. It allows to debug code written in C, C++,
Modula-2 and assembler.
Most likely the Linux-distribution you are
currently running includes it, if not change your
distribution or find the sources somewhere in the
net where it is available in zillions of places
;).
Say you downloaded the sources into your /usr/src
directory, then go to
"/usr/src/gdb-4.xxxx/gdb", type
"./configure" and change to the directory
"doc". Here you can build the
documentation for gdb by running "make
gdb.dvi;make refcard.dvi" both files are easily
viewed or printable on any Linux box.
What is DDD?
Rather than continuing with a detailed
presentation of the functioning of gdb and all its
commands, it will be more useful for the novice user
to become familiar with a much more user-friendly
environment "ddd" which stands for Display Data
Debugger.
The ddd environment, generally speaking, provides
and interface more user-friendly and also much
easier to configure for the debugging
session. However we must remark that ddd is just a
graphical environment on top of gdb, therefore ddd
needs the later for its proper execution. In fact
ddd allows the user to manipulate gdb directly if
desired. There are other debuggers that could be
used with ddd, like dbx and xdb.
A good source of information about ddd is http://www.cs.tu-bs.de/softech/ddd/
although if you use Red Hat the sources can already
be found in .rpm format. There may be two versions
of ddd, one with the Motif library compiled
dynamically and the other statically. The static
version is for those users who do not own a Motif
library
I ignore the current situation of ddd versus
LESSTIF (http://www.lesstif.org),
I am not familiar with the recent status of the
lesstif, a freeware implementation of the Motif
graphic library. Not long ago ddd only compiled and
work under lesstif thanks to a patch; I used it on a
kernel 1.2.13 with lesstif 0.75 (I think ;). Please
check the page of the lesstif project to learn more
about the status of that project.
Getting to the point, upon running ddd we get:
Figur2 1. Main Screen of ddd
There are three different ways of invoking ddd;
the one already mentioned and the following two:
ddd <program> core
ddd <program> <process_id>
The file named "core" is produced
whenever a program crashes and it contains useful
information concerning the status of the program
during the error that generate the crash. If your
system does not generate core dumps then take a look
at the environment variables for the core ('ulimit
-a' it shows all of them, also use 'ulimit -c
<value>' to define the maximum size or other
values of interest).
The proccess id allows us to inspect the
program during runtime.
ddd's graphical environment always provides
multiple ways of performing a task; I cannot
describe all of them, only the simplest or more
direct ones. Also notice that the lowest subwindow
of the main ddd console shows a log of all the
transactions executed by ddd. The log window can be
very useful to learn the usage of gdb from the
command line.
GUI
Environment
Figure 1 shows that the main window is divided
into three subwindows. The lowest corresponds to
the "pure" debugger console (gdb in our case), here
we can input gdb commands directly as if we did not
have the ddd interface at all. The middle subwindow
shows the source for the program, and the upper
subwindow provides a graphical interface to the
variables and object of the program. Finally the
tool bar is a floating window that allow the control
and execution of ddd commands.
In addition to the main window there is an
execution window for the running process and another
one for the source code of the program to be
debugged. Both are optional.
ddd comes with multiple help resources that
provide users with necessary information at any
moment during the debugging session. For example, a
dialog box always appears whenever the cursor moves
over a variable or any of the buttons in the
interface; this dialog box provides relevant
information about the object underneath. Also the
lower part of the main window has the ddd status
line which shows what command is currently running
and its output status. To the right one finds a
pop-up menu with all the available help. More help
is available pushing the key F1 and selecting a
topic from a floating window. And last one can type
in the gdb console (bottom subwindow) the command
"help" to obtain general help about the debugger or
specific information about any of its commands.
By default the ddd interface offers three
subwindows joined in a single frame. However the
"Preferences" menu one can request a ddd interface
with separated windows instead of the default.
Figura 2. Help for the "File" menu
Beginning from the
Bottom
The "DDD:Debugger Console" is
the place to take our first steps on learning to use
the debugger; experience users already familiarized
with gdb can easily operate ddd from there. In my
experience it helps to watch what happens on the
Debugger console as we launch commands through the
graphical interface. The option
"Commands->Command History" let us see
in a separate window the list all the previous
commands launched till present.
To learn about the capabilities and specifics of
DDD is better to go directly to the original
documentation. In any case I will describe how to
perform a few simple tasks from the debugger
directly.
General Tasks
Source code for a debugging session can be loaded
from the ddd command line or through the menu option
"File"; the contents of the source
file are shown in the corresponding area. From this
moment on we can already navigate through the source
code, examine the value and type of a variable,
execute the program while controlling its
execution...
The output of a program can be monitored by an
execution window (Options -> Run in Execution
Window ), or by watching its output on the
debugger console (this method will not work if the
program is design to run under Motif or other
GUI-oriented library).
Try placing the cursor over any variable of the
source code and you will see its current value on
the state line of ddd. Also if you push the mouse
right button the following menu appears:
This menu lets us inspect the value of the
variable "fname", in the lower window,
show it in the upper window ("drawing area
"), whether it is a real variable or only
pointer (a variable containing the memory address of
other variable, not its value). Furthermore
"What is" shows the structure or
type of the shown variable; Lookup permits
the search of other occurrences of the same. Finally
Break at and Clear at allow the
handling of the so-called breakpoints) which
we will explain shortly.
A number of options are also available in the bar
of buttons under area of the source code, just type
the parameter wanted in the left empty box and
choose the appropriate action.
A break-point lets execute the program up to a
specified line of the program; execution then halts
and the user can inspect the value of the variables
up to that point, continue executing the program one
step at a time by hand, review the
(threads).... etc. Take into account that, in
general, in the absence of any breakpoints in the
program, this finishes it execution correctly or
crashes due to an existing bug, at then it is too
late to launch an action to inspect the program, it
is necessary to debug the program " during
run-time".
To place a breakpoint on your source code do the
following:
- Bringing the cursor to the left side of the
line where you would like to set a breakpoint,
pressing the right button and choosing Set
Breakpoint or Set Temporary Breakpoint.
The difference between the two is that the
temporal gets cleared after the first time the
execution thread reaches it, while the other gets
only cleared with the appropriate command (Can you
guess it? :) ).
- Selecting the button "Break at
()" from the source code window.
- Typing "break <line_number>"
in the debugger console.
- Through the menu option Source->Edit
Breakpoints, this also will open a window for
the control of this utility:
In the figure you can observe two breakpoints in
the lines 70 and 71 of the source code, the symbol
for breakpoint is quite self-explanatory.
The following menu serves to manage the
breakpoints:
- With the option Condition it can be
set conditional breakpoints. In this case the, the
program will stop only if the condition holds when
the program reached the breakpoint line. Another
kind of condition is the Ignore Count , this
condition is true only after the breakpoint line is
reached <n> times. For example, it can be used
to stop the program after the 15th iteration of a
loop.
Once the program has stopped at a break
point, we can inspect the value of the program
variables using the options of the proper
menu. Let's remember that all these functions are
located at the mail menu (i.e., menu Data).
- To control the code execution we will use the button
window dedicated exclusively to it. It is located at the
top-right of the main window.
It can be seen the parallelism between the button window and the
menu bar.
We can start and stop the program, if we use the menu bar
then it is possible to give some parameters to the program
with a dialog window. Step executes one program line
more (step by step), this is, if there is a function call
then the debugger go to the beginning of the function and
will wait for the next Step command. On the other
hand, the Next command execute a function call as an
atomic program line, executing at once the full function
code.
Continue permits to continue with the execution of
the stopped program after a breakpoint stop. Kill,
Interrupt y Abort are used to interrupt the program
execution.
Probably, the most exciting characteristic of this
graphical tool is the data window display, in the upper side
of the window. We can see graphically the structure and the
contents of the data, as well as the dependencies between
them. In the next example, an array (Arguments) and four of
its elements.
This window can display a wide variety of information,
just take a look at the menu Data->More Status
Displays , where you can configure all that you want to
see on the data window. In the previous example, we can also
display the processor register values, the required dynamic
libraries and the execution state of the program:
Final words
The ddd environment can be customized from the same program
with the menu Options->Preferences, and also by the
classical resource method of the Motif applications (file
$HOME/.dddinit). It is out of the scope of this article to
describe all the customizable resources and how to do it.
It is very advisable to read the manual that comes with the
ddd (ddd.ps) and the manual of the debugger it self
("Debugging with GDB"). Nevertheless, the
user with just a little of curiosity can teach him/her self in
little time, it is only needed to debug a well known code to
discover all the debugging capabilities.
And finally, my apologizes if I made any mistakes in the
article :)
|
|