This chapter discusses how to debug Ada programs. An incorrect Ada program may be handled in three ways by the GNAT compiler:
GDB is a general purpose, platform-independent debugger that can be used to debug mixed-language programs compiled with GCC, and in particular is capable of debugging Ada programs compiled with GNAT. The latest versions of GDB are Ada-aware and can handle complex Ada data structures. The manual Debugging with GDB contains full details on the usage of GDB, including a section on its usage on programs. This manual should be consulted for full details. The section that follows is a brief introduction to the philosophy and use of GDB.
When GNAT programs are compiled, the compiler optionally writes debugging information into the generated object file, including information on line numbers, and on declared types and variables. This information is separate from the generated code. It makes the object files considerably larger, but it does not add to the size of the actual executable that will be loaded into memory, and has no impact on run-time performance. The generation of debug information is triggered by the use of the -g switch in the gcc or gnatmake command used to carry out the compilations. It is important to emphasize that the use of these options does not change the generated code.
The debugging information is written in standard system formats that are used by many tools, including debuggers and profilers. The format of the information is typically designed to describe C types and semantics, but GNAT implements a translation scheme which allows full details about Ada types and variables to be encoded into these standard C formats. Details of this encoding scheme may be found in the file exp_dbug.ads in the GNAT source distribution. However, the details of this encoding are, in general, of no interest to a user, since GDB automatically performs the necessary decoding.
When a program is bound and linked, the debugging information is collected from the object files, and stored in the executable image of the program. Again, this process significantly increases the size of the generated executable file, but it does not increase the size of the executable program itself. Furthermore, if this program is run in the normal manner, it runs exactly as if the debug information were not present, and takes no more actual memory.
However, if the program is run under control of GDB, the debugger is activated. The image of the program is loaded, at which point it is ready to run. If a run command is given, then the program will run exactly as it would have if GDB were not present. This is a crucial part of the GDB design philosophy. GDB is entirely non-intrusive until a breakpoint is encountered. If no breakpoint is ever hit, the program will run exactly as it would if no debugger were present. When a breakpoint is hit, GDB accesses the debugging information and can respond to user commands to inspect variables, and more generally to report on the state of execution.
The debugger can be launched directly and simply from emacs which allows
to browse and modify directly the source code during the debugging
session, See section Ada Mode for emacs. Here is described the basic use of
GDB is text mode.
The command to run GDB is
$ gdb program
where program is the name of the executable file. This
activates the debugger and results in a prompt for debugger commands.
The simplest command is simply run, which causes the program to run
exactly as if the debugger were not present. The following section
describes some of the additional commands that can be given to GDB.
GDB contains a large repertoire of commands. The manual Debugging with GDB includes extensive documentation on the use of these commands, together with examples of their use. Furthermore, the command help invoked from within GDB activates a simple help facility which summarizes the available commands and their options. In this section we summarize a few of the most commonly used commands to give an idea of what GDB is about. You should create a simple program with debugging information and experiment with the use of these GDB commands on the program as you read through the following section.
set args arguments
set args
command is not needed if the program does not require arguments.
run
run command causes execution of the program to start from the
beginning. If the program is already running, that is to say if you
are currently positioned at a breakpoint,
then a prompt will ask for confirmation that you want
to abandon the current execution and restart.
breakpoint location
file:linenumber,
or it is the name of a subprogram. If you request that a breakpoint be set on
a subprogram that is overloaded, a prompt will ask you to specify on which of
those subprograms you want to breakpoint. You can also
specify that all of them should be breakpointed. If the program is run
and execution encounters the breakpoint, then the program
stops and GDB signals that the breakpoint was encountered by printing the
line of code before which the program is halted.
breakpoint exception name
print expression
continue
step
next
list
backtrace
up
up can be used to
examine the contents of other active frames, by moving the focus up
the stack, that is to say from callee to caller, one frame at a time.
down
frame n
The above list is a very short introduction to the commands that GDB provides. Important additional capabilities, including conditional breakpoints, the ability to execute command sequences on a breakpoint, the ability to debug at the machine instruction level and many other features are described in detail in Debugging with GDB. Note that most commands can be abbreviated (for example, c for continue, bt for backtrace).
GDB supports a fairly large subset of Ada expression syntax, with some extensions. The philosophy behind the design of this subset is
Thus, for brevity, the debugger acts as if there were
implicit with and use clauses in effect for all user-written
packages, thus making it unnecessary to fully qualify most names with
their packages, regardless of context. Where this causes ambiguity,
GDB asks the user's intent.
For details on the supported Ada syntax Debugging with GDB.
An important capability of GDB is the ability to call user-defined subprograms while debugging. This is achieved simply by entering a subprogram call statement in the form:
call subprogram-name (parameters)
The keyword call can be omitted in the normal case where the
subprogram-name does not coincide with any of the predefined
GDB commands.
The effect is to invoke the given subprogram, passing it the list of parameters that is supplied. The parameters can be expressions and can include variables from the program being debugged. The subprogram must be defined at the library level within your program, and GDB will call the subprogram within the environment of your program execution (which means that the subprogram is free to access or even modify variables within your program).
The most important use of this facility is in allowing the inclusion of
debugging routines that are tailored to particular data structures
in your program. Such debugging routines can be written to provide a suitably
high-level description of an abstract type, rather than a low-level dump
of its physical layout. After all, the standard
GDB print command only knows the physical layout of your
types, not their abstract meaning. Debugging routines can provide information
at the desired semantic level and are thus enormously useful.
For example, when debugging GNAT itself, it is crucial to have access to
the contents of the tree nodes used to represent the program internally.
But tree nodes are represented simply by an integer value (which in turn
is an index into a table of nodes).
Using the print command on a tree node would simply print this integer
value, which is not very useful. But the PN routine (defined in file
treepr.adb in the GNAT sources) takes a tree node as input, and displays
a useful high level representation of the tree node, which includes the
syntactic category of the node, its position in the source, the integers
that denote descendant nodes and parent node, as well as varied
semantic information. To study this example in more detail, you might want to
look at the body of the PN procedure in the stated file.
You can set breakpoints that trip when your program raises selected exceptions.
break exception
break exception name
break exception unhandled
info exceptions
info exceptions regexp
info exceptions command permits the user to examine all defined
exceptions within Ada programs. With a regular expression, regexp, as
argument, prints out only those exceptions whose name matches regexp.
GDB allows the following task-related commands:
info tasks
Perl needs to be installed on your machine to run this script.
Perl is freely available for almost every architecture and
Operating System via the Internet.
On Unix systems, you may want to modify the first line of the script
gnathtml, to explicitly tell the Operating system where Perl
is. The syntax of this line is :
#!full_path_name_to_perl
Alternatively, you may run the script using the following command line:
$ perl gnathtml.pl [switches] files
Go to the first, previous, next, last section, table of contents.