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.
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 fooWhen 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.
To invoke raw (noninterfaced) dbx for your program foo, simply type
dbx fooin 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.
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.
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.
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.
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.
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.
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)
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)
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:
If you want to redirect standard input from the file fooin, you should issue the command
run < fooinand 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.
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.
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.0In 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.
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!
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.
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.
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.