While our primary debugger is gdb (along with its graphical interface ddd, and our primary profiling tool is gprof, there is another tool, valgrind that provides additional information both for profiling and for debugging.
Short summaries of the three tools are provided below.
valgrind attempts to analyze your program for potential flaws, and produces plain text output describing them (although the "plain text" does assume you have a solid understanding of how things work in C/C++) First, as with gdb, you must compile your program with the -g option, then you can run your program through valgrind, e.g. valgrind myprog arg1 arg2 arg3 etc valgrind can also provide information/analysis with respect to potential memory leaks if run with the option --leak-check=yes valgrind --leak-check=yes myprog arg1 arg2 arg3 etc valgrind can also perform an analysis of heap (dynamic memory) utilization if run as a two step process: (1) valgrind --tool=massif myprog arg1 arg2 arg3 etc this produces an extra file named massif.out.NNN where NNN is the process id your program ran under (2) run the analysis using the command ms_print massif.out.NNN (using the NNN from step 1)
Allows you to perform an analysis of cpu utilization for the functions in a program. (1) compile the program with the -pg option (2) run the program normally, it will create an extra file, named gmon.out (each run of the program overwrites this file) (3) run gprof progname (where progname is the name of your program) This last step produces the text analysis of cpu utilization in your program. The report contains two tables and a lot of explanatory text, we're interested in the two tables. The first table summarizes the cpu time spent in each of the functions called during the run of the program. This includes the total cpu time, the number of times the function was called, the average time per call, and the percent of overall cpu time that was spent in the function in question. The second table breaks this down further, identifying how much time was spent in the function and, of that, how much time was spent in the functions it called. E.g. if function f called functions g and h, it would specify how much time was spent in f (including the time for g and h) plus how much of that time was spent in g and how much of that time was spent in h. Additional options: -z: gprof will also list functions that were never called during that run of the program -r: gprof will suggest an ordering for your functions to try to minimize page faults during execution -R: gprof will suggest an ordering for linking .o files to try to minimize page faults during execution
Debugging approach: notice a problem (bug report) iterate through the following (not necessarily in order): establish/refine theories as to what the source of the problem might be try to isolate exactly what the problem is: conduct widening/narrowing cases to find the scope of the bug use the test cases and debugger to isolate the region wherein the error occurs and/or becomes observable once you're sure of the source of the problem, try and develop a fix - running it through the test cases to see if it solves the problem or reveals the need for more testing once the problem seems fixed, conduct regression tests to be sure you didn't break something else ======================================================================= GDB Notes --------- good for C or C++ or Java must compile with -g option special note: compiling with both -g and -O (optimize) can have funky effects, so don't do it (the optimizer from -O rearranges your code, the debugger shows you what is really there, which won't match what it looks like in your source code) you can run gdb with executable name as the command line parameter, eg gdb myprogram if you're running without full GUI support (e.g. just a terminal window from home) use the -nw (no windows) option (this is the default for our installation) if you want gui support there is also ddd, which is a GUI interface to gdb crash support since gdb keeps the program in memory even after a crash, it allows us to explore what state the program was in at the point of crash to see time and memory stats after each command use the -statistics option to quit use either quit or q Logging output -------------- To turn on/off recording of all output (goes to gdb.txt) use set logging on/off To change the logfile use set logging file YOURFILENAME You can also redirect output at the run command, e.g. run > YOURFILENAME Getting help ------------ General: help or h Specific: help command/topic Search: apropos keyword Running your program within the debugger ---------------------------------------- set up any command line arguments using set args val1 val2 etc run progname or r progname or r progname arg1 arg2 etc alternative is to use the 'start' command, which effectively puts a breakpoint in the main routine then issues the run command Examining the source code -------------------------- The list command allows you to display source code (in case you don't have it open in another window someplace) There are many options for list, two of the most common are: list functionname list linenumber Interrupting execution ---------------------- To see what the code is doing when it runs, we need to be able to interrupt the program in mid-execution to have a look around. There are three ways of specifying when a program should stop execution: - Breakpoints stop whenever a specific spot in the code is reached - Watchpoints stop whenever the value of an expression changes - Catchpoints stop whenever a specific kind of event takes place (e.g. exceptions) You can set up these points, then enable/disable them temporarily Setting up breakpoint: break FUNCTIONNAME break LINENUMBER break FILENAME:FUNCTIONNAME break FILENAME:LINENUMBER Optionally you can add a condition under which a breakpoint should be used, e.g. break FUNCTIONNAME if (SOMECONDITION) You set a temporary breakpoint, i.e. one that is only used once, using tbreak instead of break You can clear (remove) breakpoints using clear clear FUNCTIONNAME clear LINENUMBER etc You can enable/disable breakpoints using enable BREAKPOINT disable BREAKPOINT You can set up commands to run when a breakpoint is hit as follows: break foo if x>0 commands printf "x is %d \n",x end You can also add the command silent if you want it to ONLY print the stuff you tell it, break foo commands silent .... end You can set a watchpoint using watch EXPRESSION where the expression can be as simple as a variable name *** but when you set the watchpoint on one or more variables you must currently be somewhere in the program execution where all those variables are visible/in scope - this may mean setting a breakpoint in a pertinent function, running the program to the breakpoint, THEN setting the watchpoint *** You can set a catchpoint using catch EVENT where event can be any of throw, catch, exec, fork, load, unload You can get a list of the current break/watchpoints using info breakpoints or info watchpoints Resuming execution ------------------ To resume an interrupted run use continue or c To continue just to the next source line use step To continue just to the next machine code instruction (remember there may be multiple lines of machine code per line of source code) use stepi or nexti To continue for n source lines use step N To continue but count function calls as single lines use next next N To continue until a source line is reached use until LINE or advance LINE Looking at the call history on the stack ---------------------------------------- We can look at the sequence of (still active) function calls that lead to where we are now using backtrace, or bt, eg backtrace For just the last N calls use backtrace N We can get info on the current frame using info frame We can look at specific frames (i.e. for specific calls) using frame N where the current frame is 0, the one that invoked it is frame 1, the one that invoked that is frame 2, etc You can move up/down the call stack (up moving to the caller's stack frame) using the commands up down Looking at memory during an interuption --------------------------------------- We can display the value of variables or expressions at the point of interruption using the print or p statement, eg print x+y To specify which function or file contains a variable of interest, use ::, eg print FUNCTIONNAME::VARIABLENAME Note it will only recognize variables that are normally visible at the current point in the program (i.e. scoping rules apply) You can choose the format of the display using print/f where f is a format character - x for hex - d for decimal - u for unsigned - o for octal - t for binary - a for address - c for character - f for float If you want to refer to a previously printed value within an expression use $$N to refer to it ($ is the current value, $$ or $$1 is the one before it, etc) Suppose we just printed the address of a struct, and want to print a field: print *$.next (* for dereferencing the pointer specified in $, then .next is the field) We can also examine memory addresses using the x (examine) command: x ADDRESS Command completion ------------------ TAB causes gdb to attempt command completion, RETURN cycles through the possibilities Declaring temporary variables within gdb ---------------------------------------- You can set variables of convenience within the debugger using set $thevarname = somevalue You can then use/refer to the variable using $thevarname To see the list of variables of convenience use show convenience Configuring gdb --------------- There are a number of environment changes you can make to set gdb up the way you like: set prompt NEWPROMPT set height Hpixels set width Wpixels set verbose on/off set confirm on/off and a great many others If these are specified in your .gdbinit file then they will automatically be loaded by gdb each time it starts up. Defining your own commands and command sequences ------------------------------------------------ If there is a command or command sequence you find yourself using frequently during debugging, you can define it as a new command: define mycmd print/a end You can supply up to 10 command line arguments, which can be referenced within the definition as $arg0, $arg1, etc Such definitions can also be placed in your .gdbinit file