The dbx debugger

The dbx debugger is very useful for tracking down errors in your code. Used by itself, dbx has a rather primitive user interface, and requires some practice to use. However, the initial effort in learning to use it is often repaid by the time it saves you finding programming errors. There is a fancier X-windows interface for AIX, invoked with the command xde, but I prefer the Gnu-emacs interface, discussed below.

The dbx debugger is able to track the execution of your program line-by-line in the source code (C or Fortran) and tell you the status of every variable you are computing. It is also possible to tell it to watch a particular variable and report when it changes. In order for the debugger to perform this trick, it is necessary to compile your code with a special option, so the compiled code contains information about the symbolic names of your variables and has the appropriate cross references to the source code.




Required compiler options


Required compiler options

The first step in using dbx is to compile your code with the debugging option -g. Do this by inserting one or both of the following lines in your Makefile:

   .f.o: ; xlf -c -g $*.f
   .c.o: ; cc -c -g $*.c
(The first is for Fortran code, the second for C.) You may also use the -g option on the command line. For example,
   % cc -g foo.c -o foo
   % xlf -g foo.f -o foo
When you use the -g option, be sure not to call for optimization with the -O option, since the optimizer often puts compiled code in a different order from the source code. So execution will not follow the source code order, which leads to confusion.


Invoking Raw dbx


Invoking Raw dbx

To invoke raw (noninterfaced) dbx for your program foo, simply type

   dbx foo
in a shell window. You will see something like the following message, if you compiled your code with the correct options:
   dbx version 3.1 for AIX.
   Type 'help' for help.
   reading symbolic information ...
   (dbx)
The debugger actually looks for a file named core before it looks for your source code. If it doesn't find it, it simply loads your program. However, if you tried to run your code and it crashed with a core dump, you can use the debugger to do a post mortem. In that case invoke dbx and see the where command below.


Invoking the emacs interface


Invoking the emacs interface

To invoke the Gnu-emacs dbx interface, use the emacs command M-x dbx <Enter> followed by the name of the executable that you wish to debug. Emacs creates a new window and buffer for the shell interface. The commands in this window are just the raw dbx commands. Enter them at the /bin/dbx prompt, just as you would with raw dbx in a shell window. During execution, the emacs interface displays the source code in the other split window and tracks the execution there.


Getting Started; Summary of commands


Getting Started; Summary of commands

All commands are typed at the (dbx) prompt. When you first start the debugger, your program is ready to run, but has not yet started. The debugger is looking first at the file containing the main program and is waiting for instructions. To get a list of possible commands, type help. You get the following list.

   (dbx) help
   run                    - begin execution of the program
   print <exp>            - print the value of the expression
   where                  - print currently active procedures
   stop at <line>         - suspend execution at the line
   stop in <proc>         - suspend execution when <proc> is called
   cont                   - continue execution
   step                   - single step one line
   next                   - step to next line (skip over calls)
   trace <line#>          - trace execution of the line
   trace <proc>           - trace calls to the procedure
   trace <var>            - trace changes to the variable
   trace <exp> at <line#> - print <exp> when <line> is reached
   status                 - print trace/stop's in effect
   delete <number>        - remove trace or stop of given number
   screen                 - switch dbx to another virtual terminal
   call <proc>            - call a procedure in program
   whatis <name>          - print the declaration of the name
   list <line>, <line>    - list source lines
   registers              - display register set
   quit                   - exit dbx
   (dbx)
I won't go through all of these commands, but will discuss some of the most frequently used ones. Note that most of the commands can be abbreviated. Thus the print command can be abbreviated p.




list <line>, <line> - list source lines


list <line>, <line> - list source lines

This command is used to list your source code. (You won't need it if you are using the Gnu-emacs interface.) Each line of your source code is given a number. This is the number you use to give other commands to the debugger, so it is a good idea to know it. The listing shows the numbering. So type list. You will see the first 10 lines of your source code. If you type list again, you get the next 10, etc. You may also specify the range of lines to be listed, as shown in the command syntax above. You can get the source code line number in emacs by clicking on a line and running the command M-x what-line.


stop at <line> - suspend execution at the line


stop at <line> - suspend execution at the line

This command causes the debugger to stop before executing the statement at line <line> in the source code. This operation is called setting a ``breakpoint''. The statement must be executable. So for example, you might say

   (dbx) stop at 49
   [1] stop at "foo.f":49
   (dbx)
The [1] counts the commands you have given dbx.


stop in <proc> - suspend execution when <proc> is called


stop in <proc> - suspend execution when <proc> is called

This command causes the debugger to stop each time it enters the procedure <proc>. This is another type of breakpoint. Thus you could say

   (dbx) stop in foobar
   [2] stop in foobar.foobar
   (dbx)
The expression foobar.foobar says that the debugger is using the source code for the routine foobar that it finds in the file called foobar.f.


status - print trace/stop's in effect


status - print trace/stop's in effect

This command asks the debugger to list the commands it is remembering. Thus if you issued the two commands in the last two examples, you would then see

   (dbx) status
   [1] stop at "foo.f":49
   [2] stop in foobar.foobar
   (dbx)


delete <number> - remove trace or stop of given number


delete <number> - remove trace or stop of given number

Deletes the command with the number <number> from the debugger's list. Thus, continuing with the previous example, you may do

   (dbx) delete 1
   (dbx) status
   [2] stop in foobar.foobar
   (dbx)


run - begin execution of the program


run - begin execution of the program

This command causes the debugger to start running your program. Once execution has begun, your program takes over. If the program expects input from the keyboard, you should enter it as if the debugger weren't there. Output to the screen will also appear on the screen. Execution will continue until any of the following occurs:

Once the program has stopped, you may issue more debugging commands.

If you want to redirect standard input from the file fooin, you should issue the command

   run < fooin
and if you have command line options -a -b then you should include them with the run command, just as you would if you were invoking your program from the shell directly.


cont - continue execution


cont - continue execution

To resume execution after a pause, DO NOT use the run command again. That will restart execution from the beginning again. Instead, use the cont command.


print <exp> - print the value of the expression


print <exp> - print the value of the expression

To display the contents of one or more variables, use this command. Thus

   (dbx) print tol, regfal
   9.9999997473787516e-05 .false. 
   (dbx)
In this case tol is the name of a double precision variable and regfal is the name of a Fortran logical variable. You can also get a full listing of the contents of an array y with
   (dbx) print y
   (1)     5.0
   (2)     1.0
In this case the array y had only two elements. The print command will also evaluate simple expressions:
   (dbx) print y(i+1) + 2
   3.0 
   (dbx)
Here i is a variable in the source code and y is an array.


step - single step one line


step - single step one line

This command causes the debugger to execute just one line of source code. The line is displayed. This command, like the others, can be abbreviated. In this case the abbreviation is s. So just hit the ``s'' key and step away!


return - continue subprogram but stop upon return


return - continue subprogram but stop upon return

Use this command while stepping to cause the debugger to complete execution of the current subprogram and pause when it returns to the calling program. This command is especially useful if you are stepping away merrily with step, and you suddenly find yourself in one of the system subprograms, such as the one that handles input.


next - step to next line (skip over calls)


next - step to next line (skip over calls)

Use this command while stepping to avoid stepping into a system routine or calling a subprogram you don't want to follow in detail. The subprogram is executed even though you don't step through it line by line.


where - print currently active procedures


where - print currently active procedures

This command lists all of the procedure calls that took you to the current point in execution and tells you the number of the current line in the source code. It is especially useful in conjunction with the core post mortem feature of dbx. Suppose you ran your code without the aid of the debugger and it ended with a core dump. You can then invoke the debugger and type where. The command will tell you which statement the program was executing just before the crash. Sometimes you can also find the values of variables, as they were at the time of the crash.


trace


trace

These commands can be used to track changes in variables as execution progresses. Use this command with caution. If you trace the value of a variable, execution slows considerably, because the variable must be examined after executing each source line.


quit


quit

Exit dbx.