You can use dbx to trace problems in a program at the source code level, rather than at the machine code level. dbx enables you to control a program's execution, symbolically monitoring program control flow, variables, and memory locations. You can also use dbx to trace the logic and flow of control to acquaint yourself with a program written by someone else.
This chapter introduces some basic dbx commands and discusses some tips about how to approach a debugging session. Specifically, this chapter covers:
Even if your program compiles successfully, it still can crash when you try to run it. When a program crashes, it generates a terminating signal that instructs the system to write out to a core file. The core file is the memory image of the program at the time it crashed.
You can examine the core file with dbx to determine at what point your program crashed. To determine the point of failure, follow these steps:
If the core file is not in the current directory, specify the pathname of the core file on the dbx command line.
For example, suppose you examine the core file for a program called test. Suppose the stack trace appears as follows:
(dbx) where > 0 foo2(i = 5) [“/usr/tmp/test.c”:44, 0x1000109c] 1 foo(i = 4) [“/usr/tmp/test.c”:38, 0x1000105c] 2 main(argc = 1, argv = 0xffffffad78) [“/usr/tmp/test.c”:55, 0x10001104] 3 __start() [“/shamu/crt1text.s”:137, 0x10000ee4]
In this case, test crashed at line 44 of the source file test.c. The program crashed while executing the function foo2. foo2 was called from line 38 in the function foo, which was in turn called from line 55 in the function main. You can use the other features of dbx to examine values of program variables and otherwise investigate why test crashed.
If you use dbx to debug code that was not compiled with the -g option, local variables are invisible to dbx, and source lines may appear to jump around as a result of various optimizations. If the code is stripped of its debugging information, dbx displays very little information.
You stop execution of your program by setting breakpoints in your program. Breakpoints can be unconditional , in which case they always stop your program when encountered, or conditional, in which case they stop your program only if a test condition that you specify is true. (See “Setting Breakpoints” in Chapter 6, for more information.)
To use breakpoints to debug your program, examine your program carefully to determine where problems are likely to occur, and set breakpoints in these problem areas. If your program crashes, first determine which line causes it to crash, then set a breakpoint just before that line.
You can use several dbx commands to trace a variable's value. Here's a simple method for tracing a program variable:
Use the stop command (see “Setting Breakpoints” in Chapter 6) to set breakpoints in the program at locations where you want to examine the state of the program stack or the values stored in program variables.
Use the run or rerun command (described in “Running Your Program (run, rerun, and sort)” in Chapter 2) to run your program under dbx. The program stops at the first breakpoint that it encounters during execution.
Use dbx to examine the flow of control in a program. When studying the flow of control within a program, use the dbx commands stop, run/rerun, print, next, step, and cont. Use the following procedure to study a new program.
Use the stop command to set breakpoints in the program. When you execute the program under dbx, it stops execution at the breakpoints.
If you want to review every line in the program, set a breakpoint on the first executable line. If you don't want to look at each line, set breakpoints just before the sections you intend to review.
Use the run and rerun commands to run the program under dbx. The program stops at the first breakpoint.
Use the step, next, or cont command to continue past a breakpoint and execute the rest of the program.
cont resumes execution of the program past a breakpoint and does not stop until it reaches the next breakpoint or the end of the program. cont is explained in “Continuing Execution after a Breakpoint” in Chapter 6.