CPSC 324 | Fundamentals of Computer Graphics | Spring 2006 |
Debugging is one of those inevitable things - careful programming can reduce the need for debugging, but it seems to always be necessary to debug at some point.
The main approach to debugging is to observe what is going wrong and then locate the bug by methodically formulating theories about what the problem is and testing those theories. Often the "test the theory" phase involves examining the values of values and/or the flow of control in the program, to determine where the program's behavior diverges from the correct behavior.
The classic way to examine values or check flow of control is to print things out. This approach is fine, but a debugger can provide a number of more convenient features (especially if printing values out leads to large quantities of output). This document is a brief introduction to using the GNU debugger gdb.
You don't have to run gdb from within emacs, but emacs provides several handy features. This document will assume gdb is running within emacs. If you are new to emacs, you may wish to review this section.
emacs lets you edit multiple files at once. Each file is displayed in a separate buffer, and you can switch between buffers via the Buffers menu.
New files are created by opening a file (File->Open File) which doesn't already exist. The file is created on disk the first time it is saved.
The window where your emacs is displayed is known as a frame in emacs terminology. This is because you can split a frame into two or more windows (try the File->Split Window and File->Unsplit Windows menu options).
The single line displayed at the very bottom of your emacs window is the minibuffer. It is usually empty, but sometimes emacs will display feedback information or prompt you to enter something there.
The single line immediately above the minibuffer is the mode line. It displays various information such as the name of the buffer, the current line number, the current column, etc. (What exactly is displayed depends on how your emacs in configured.)
Notation like File->Open File means to select the "Open File" option from the "File" menu.
To begin debugging, you need to have a compiled executable. Compile your program now if you haven't already. (If your program doesn't compile, then a debugger is not what you need - debuggers are for examining the run-time behavior of your program.)
Also start emacs if you don't already have it running. Note: this document assumes you are using emacs (not xemacs - xemacs supports similar functionality, but the menus, prompts, and key commands may be different).
To start gdb:
Open one of your program's source files in emacs if you don't already have one open, or switch to a buffer containing one of your program's source files if one is already open but isn't in the current buffer. This step isn't necessary to start gdb, but it makes it easier to figure out the correct pathname for the next step.
Start gdb via the tools menu (Tools->Debugger). You should see the message Run gdb (like this): gdb appear in the minibuffer. At the blinking cursor, type the name of the executable you want to debug. (The focus should already be in the minibuffer - just type the name without clicking in the minibuffer.) If the executable is not the same directory as the source file displayed in the current buffer, enter the full absolute or relative pathname to the executable.
You should now have a new buffer which displays something like the following:
Current directory is /home/ssb/courses/324/s06/master/build/ GNU gdb 5.3.92 Copyright 2003 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-suse-linux"... (gdb)
Look carefully at the text displayed - in particular, check that there isn't a message like "render: No such file or directory" at the end. If you see this, gdb couldn't find your executable. Check that the current directory shown is the directory where the executable is located. If you did get the "No such file or directory" error, close the gdb buffer (File->Close (current buffer)) and repeat the two steps above.
When you are done with the debugger, close the gdb buffer (File->Close (current buffer) to terminate gdb (and your program).
Once you've successfully started gdb, you are ready to debug. The (gdb) prompt is where you will type commands to gdb. It is shown in the following to indicate commands which are to be typed at the prompt (vs. other commands which are entered in other ways) - don't type the (gdb) yourself!
(gdb) run (gdb) r
To run your program without commandline arguments, type run or r at the (gdb) prompt and press enter.
(gdb) run <commandline arguments> (gdb) r <commandline arguments>
If you want to include commandline arguments, enter them after the run (or r) command just as you would enter them on the commandline (but don't include the name of the executable) e.g.
(gdb) r -d s
would start the current executable, passing it -d s as the commandline arguments (this would tell the renderer to print scene-related debugging information, if your executable is the renderer).
If your program is currently running (but stopped), run will restart it from the beginning.
Run will detect if your executable has been recompiled, and will reload it as necessary - you don't need to stop and restart gdb if you fix a bug and recompile the program. (gdb does crash sometimes when you do this, though, so if that happens just restart it.)
If your program is currently running in the debugger and you want to stop it so you can examine a variable or see what line of code is being executed, choose Signals->BREAK. You'll get a (gdb) prompt back, and the program will be stopped wherever it was.
There are various ways to control the execution of your program:
command | abbreviation | meaning |
---|---|---|
(gdb) step (gdb) step n | s |
step the program to the next source line (steps into function calls) step n (replace 'n' with a number) repeats n times before stopping (will stop early if interrupted by the user or a breakpoint is reached) |
(gdb) next (gdb) next n | n |
step the program to the next source line, treating function/method calls as a
single instruction (i.e. steps over function calls) next n (replace 'n' with a number) repeats n times before stopping (will stop early if interrupted by the user or a breakpoint is reached) |
(gdb) continue (gdb) continue n | c |
resume execution of the program (program will run until stopped by the user or
a breakpoint is reached) continue n (replace 'n' with a number) repeats n times (if stopped at a breakpoint, this will continue past the next n-1 occurrences of that breakpoint and will stop at the nth) |
(gdb) run | r | restarts execution of the program from the beginning |
One of the very useful features of a debugger is the ability to set breakpoints. When executing, the program will stop and return control to the debugger when a breakpoint is reached.
To set a breakpoint, position the cursor on the line of source code where you want the breakpoint to be and choose Gud->Set Breakpoint. (This is done in a source code buffer, not the gdb buffer.)
To clear a breakpoint, position the cursor on the line of source code where the breakpoint currently is and choose Gud->Clear Breakpoint. (This is done in a source code buffer, not the gdb buffer.) If the program is currently stopped at a breakpoint, you can clear that breakpoint with:
(gdb) clear
When the program is stopped somewhere, you can examine the values of any variables in the current scope:
(gdb) print <variable name> (gdb) p <variable name>
For example, p x would print the value of the variable x in the current scope.
You can also print the values of expressions e.g. p s.length() would print the result of executing the length() method of the string variable s.
The program stack contains information about all of the functions/methods currently being executed - the bottom of the stack is main and the current function/method is at the top of the stack. Examining the stack will tell you what called the particular line of code that the program is stopped on. This can be useful if an assertion is failing or a value is going bad and you aren't sure where it is called from - put a breakpoint on the line in question, and use the commands below to examine the program stack when the program stops on that line.
command | abbreviation | meaning |
---|---|---|
(gdb) up | u | go up one stack frame from the current location (i.e. go to the caller) |
(gdb) down | go down one stack frame from the current location (i.e. to what was called) | |
(gdb) backtrace | print out the entire stack, from the current frame down to the bottom |