Using the Software Debugger GDB I. Purpose of a source-code debugger A source-code debugger is a piece of software which can be used to find semantic (i.e. run-time) program errors. It should not be used to fine syntax (i.e. compile-time) program errors. II. The gdb Source-Code Debugger The gdb source-code debugger is available in the UNIX operating system environment. gdb can be used to perform the following operations which are very helpful in the process of debugging a compiled program. A. Setting break-points: Program execution can be temporarily suspended at specified points (called "break-points"). At the point of program suspension, specific values/outcomes can be displayed to determine their correctness. Upon program suspension, the programmer can interact with gdb and use its full set of commands to investigate the performance of the executing program before resuming program execution. B. Displaying program values and attributes: dbx can be made to display the current contents of variables as the program executes. C. Step through a program line-by-line. Each line of the executable program can be executed one line at a time. III. Compiling a Program that is to be Debugged with gdb The (source-code) program that is to be debugged using gdb must first be successfully compiled (no compilation errors found during compilation). Once the program source-code compiles successfully, compile it one more time using the "-g" compiler option as in: g++ -g source_code_file.cpp The use of the "-g" compiler option will cause the compiler to build special files/tables of data that gdb will need for subsequent debugging. IV. Invoking the gdb Debugger After compiling the source code file in the manner described above, the gdb debugger can be invoked to assist the programmer in debugging his/her program. To invoke gdb, get to the UNIX command-line prompt and enter: gdb executable_file where "executable_file" is the name of the compiled executable form of the program (which will be "a.out" unless you have changed its name). The gdb debugger will access the special files/tables created by using the "-g" compiler option. The gdb prompt "(gdb)" will be displayed and you will now be in the gdb environment and will be able to enter only gdb commands (no UNIX commands will be recognized). V. Exiting the gdb debugger To exit the dbx debugger and return to the UNIX command-line prompt, enter: quit from the (gdb) command line. A typical debugging session might follow these steps: a. Invoke gdb on the executable file compiled with the -g option. b. Enter the gdb command break main c. Enter the gdb command run to start your progrma running Your program will suspend execution as soon as it gets to function "main" because the command above said to break in function "main" d. Enter the gdb command step or next to execute the current line of the program. Remember "step" will jump into a function call, while "next" will jump over a function call. e. Enter the gdb command print var_name to see the value stored in variable "var_name". You can then do another "step" (or "next") and another "print". Keep repeating this as long as you like. In all of the following discussion of dbx commands, the angle brackets <...> around command arguments are NOT part of the command. They just signify that the value they inclose must be entered by you. -----------------------(gdb) Commands --------------------- 1. The "stop" (set break-point) debugging statement. Keep in mind that although a "stop" will cause the program to be suspended on line "L" and will display line "L" of the source-code, the lines of code that have actually been executed when the "stop" occurs is only lines 1 through L-1. That is, line L will not have been executed as yet. It will be the next line to be executed if you do a "step" operation. a. stop at Sets a break-point such that when the "run" dbx statement is entered, the program will begin executing at its beginning, but program execution will be temporarily suspended when line "line#" is reached. The line of source-code at this line will then be displayed. However, if any action is performed on line "line#", that action will not have been carried out as yet. To have line actually executed, do a "step" command (see below). When program suspension occurs, you can set the dbx commands "trace" and "stop", and use the special dbx commands "list", "step", "next", "dump", "where", "delete", "status", "whatis", "print", "call", etc. discussed in later sections. To resume execution of the program, enter "cont" on the (dbx) command line. b. stop in Sets a break-point such that when the "run" dbx statement is entered, the program will begin executing at its beginning, but program execution will be temporarily suspended when the first line of code in function "funct_name" is reached. The line of source-code and its line number will then be displayed. When program suspension occurs, you can set the dbx commands "trace" and "stop", and use the special dbx commands "list", "step", "next", "dump", "where", "delete", "status", "whatis", "print", "call", etc. discussed in later sections. Before doing "print", "dump", "where", or "whatis" after a "stop in " command, do one "step" command first else you may get incorrect values output for the formal parameters passed to this function. To resume execution of the program, enter "cont" on the (dbx) command line. "stop in " is a very useful command for finding out the values that were passed into the FORMAL parameters of . To view the values of the parameters passed to the function, do: Step Action ---- ---------------------------------------------- 1 Set a "stop in " break-point where is the name of the function whose formal parameter values you want to see. 2 Enter "run" from the dbx command line prompt. 3 Enter "step" from the dbx command line prompt. 4 Enter "dump" or "where" from the dbx command line prompt. The values passed into this function will now be displayed. c. stop Sets a break-point such that when the "run" dbx statement is entered, the program will begin executing at its beginning, but program execution will be temporarily suspended the next time the value associated with "var" changes. The line of source-code immediately following the line where "var" changes is displayed (not the line where "var" is actually changed). When program suspension occurs, you can set the dbx commands "trace" and "stop", and use the special dbx commands "list", "step", "next", "dump", "where", "delete", "status", "whatis", "print", "call", etc. discussed in later sections. To resume execution of the program, enter "cont" on the (dbx) command line. 3. The special dbx debugging commands. The following commands are executed as soon as you type them in and press the enter key. You do not have to invoke "run" to make them work. a. dump Will display the values associated with: 1. All the parameters passed into the function you are in when the "dump" is invoked. 2. All the local variables declared in the function you are in when the "dump" is invoked. "dump" will NOT display the values associated with "global" variables or constants. Remember, if you are using "dump" to view the values passed into the formal parameters in a function call using the "stop in " dbx command, enter one "step" command BEFORE entering the "dump" command once the break-point is reached. b. where <#> Will display two kinds of information: 1. The name of the last # functions which have been called to get you to the function you are in when the "where" is invoked. "#" must be a positive literal whole number. If "#" is omitted, then the names of all the functions which have been called to get you to the function your are in when the "where" is invoked will be displayed. 2. The number of the line in the "calling" function where the current function was called. 3. The values associated with all the parameters passed into the function you are in when the "where" is invoked are displayed. Remember, if you are using "where" to view the values passed into the formal parameters in a function call using the "stop in " dbx command, enter one "step" command BEFORE entering the "where" command once the break-point is reached. c. delete <#> Will delete the "trace" or "stop" associated with the number #. The value of "#" must be a positive literal whole number. To see a list of all the currently set "traces" and "stops" (break-points), enter the dbx command "status". This will display the currently active "traces" and "stops" and the number (#) associated with each. You can then use these numbers in the "delete" statement. The next time you enter the "run" statement, the deleted "trace(s)" and "stop(s)" will no longer be carried out. More than one "#" can be listed after "delete" (e.g. "delete 3 6 21") so long as each is separated from the next by a space (not a comma). d. status Will display the currently active "traces" and "stops" (break-points) and the number (#) associated with each. You can use the number displayed in a "delete" command to delete active "traces" and "stops". e. whatis Will display the "data type" of the variable specified by "var". f. print Will evaluate "expr" and display the resulting value. "expr" must contain only the names of currently visible (accessible) data objects (variables and constants). These data objects are evaluated and the result of this evaluation (a single value) is displayed. If "expr" is a single variable, then the current value of that variable is displayed. If "expr" is the name of an array (fixed size array) then the value of each of its elements is displayed. Use "print" to examine individual elements of a composite data object e.g. individual elements of an ARRAY or field of a C STRUCTURE, etc. Example: print vector[k] print mtrx[j][k] print empl_rec.name print list->age The "print" command can also be used to display the values stored in GLOBAL variables and constants. g. call Will cause function "funct_name" to be executed (but, remember, program execution will not stop when a "call" in invoked). You must supply the list of parameters for "param list". h. "list" commands. list Will display on the screen the next 10 lines of source-code beginning at the current position in the program that is being executed. list Will display line "n". list Will display on the screen source-code lines "m" through "n". You can have all source-code lines in the current program displayed if you use 1 for "m" and some value for "n" that is greater than the number of source-code lines in your program. The comma between "m" and "n" MUST be present. list Will display on the screen all lines of source-code making up function "funct_name" (and also a few lines of source-code immediately above and immediately below function "funct_name"). i. step <#> Will cause the next "#" lines of program source-code (beginning at the current line) to be executed. "#" must be a positive whole number. No program output is displayed. If the "#" is omitted, then the value one is assumed by default. If the line of code being "stepped" through is a function call, then that function is entered. If the line of code being "stepped" through produces screen output, that output is displayed when "step" is executed. j. next <#> Will cause the next "#" lines of program source-code (beginning at the current line) to be executed. However, unlike "step" above, if one of the lines of code to be executed is a call to a function, that line of code is skipped (i.e. "next" will not enter that function, but will execute the line of code immediately following the call to the function). The action(s) taken by the skipped function will still be carried out, but you will not "step" through that function. Thus, to bypass a function call, use "next" rather than "step". "#" must be a positive whole number. If the "#" is omitted, then the value one is assumed by default. Some helpful hints: 1. To stop program execution at the bottom of a specified function. When debugging, it is often a good practice to stop program execution at the bottom of a function so you can do a "dump" or a "print" to see the current values stored in certain data objects after the function has altered them. There is no simple built-in dbx statement to do this. To accomplish this, you can do the following: For each function in the program (except not "main"), do the following: a. Do a "list funct_name" and find the number of the line (line#) in that function where the function end-block-delimiter (}) is; b. Set a break-point at EACH of these line numbers using: "stop at " using the line numbers you got from step "a" above. When you "run" the debugger, program execution will stop at the end of each function in your program. At that time you can do a "dump" or "print" to examine the values currently stored in program data objects (global, local, or parameters passed into the current function). 2. To find out where your program is crashing. You may get a "segmentation fault - core dumped" message, or a "bus error" message. These and other such messages are telling you that the logic at some point in your program is incorrect and that, for this reason, program execution cannot continue - that is, your program CRASHES. To find out the exact line of source code that caused your program to "crash", do the following: a. Compile your program using the -g compiler option (as shown above) if you have not already done so; b. Delete the "core" file from your directory if it exists by doing: rm core from the UNIX command line. You do not need this file and it occupies 5 to 8 million bytes of your disk space, so get rid of it. c. Enter from the UNIX command line: dbx a.out d. Enter the command "run". Your program will begin execution and will "crash" at the first line of illogical code in your program (you may have more than one run-time error in your code). When your program "crashes", an error message will be displayed. This message may or may not be understandable. If it is NOT understood (it makes no sense to you), enter the "dbx" command "list". In most cases, the TOP line of the lines of code listed will be the line that caused your program to crash. If the message displayed when your program crashes makes sense to you, it will be telling you the exact line in your source-code where your program crashed and will display the line of code that caused the program to crash. Get your source-code back into the "emacs" editor and go to the line of code specified above. Fix the logical error and then recompile and run your program to see if things are now working well. 3. To step through the entire program. a. Get the executable code up in the dbx debugger. b. Enter from the dbx command line: stop in main c. Enter from the dbx command line: run and the program will begin to execute and execution will suspend on line one of main (this works best if function "main" is the FIRST function in your program). d. Now repeatedly enter from the dbx command line: step ( or "next" ) At each step, you can execute a "print" or "dump" command to see the values of variables as the program executes. 4. To step through a specific function a. Get the executable code up in the dbx debugger. b. Enter from the dbx command line: stop in where "funct_name" is the name of the function you what to "step" through. c. Enter from the dbx command line: run and the program will begin to execute and execution will suspend on line one of function "funct_name". d. Now repeatedly enter from the dbx command line: step ( or "next" ) At each step, you can execute a "print" or "dump" command to see the values of variables as this function is executed. The dbx debugger has many other commands and options. To study these, just enter: man dbx from the UNIX command-line prompt. In addition, each semester there are 2-hour seminars on how to use "dbx" taught by University Computing Services (on campus). These seminars are free to USC students and their times and locations are posted in SAL-125 (or see the consultant in SAL-125).