Lecture #7: Debugging Reading: none Handouts: The Dynamic Debugger gdb; Guide to Faster, Less Frustrating Debugging Homework: study for the midterm! 1. Greetings and felicitations 2. Debugging Ö programs have bugs; find and fix them Ö static debugging: insert debugging code into source, recompile and run Ö dynamic debugging: look at the program as it runs, observing (and maybe changing) variables, etc. 3. Static debugging Ö using printf to print variable values; mention %p (prints pointer value, usually as a hex integer) Ö using printf to print where you are (ie, on function entry printf(„in function\n¾); Ö #ifdef DEBUG ä #endif around the printfs so you can leave them in the source if you need them again Ö assert(x) macro: assert(0 <= i && i <= n) causes program to exit with error message if (0 <= I && I <= n) is false; must include . To delete, say #define NDEBUG and they will not be in the compiled code. 4. Dynamic debugging Ö debugging tool instruments executable program so it can be stopped, examined, altered, and continued interactively Ö go through the handout Ö mention the „where¾ command which shows you the program stack The Dynamic Debugger gdb Introduction This handout introduces the basics of using gdb, a very powerful dynamic debugging tool. No-one always writes programs that execute perfectly every time, and while reading the program source can help fiind bugs, some can only be discovered by running the program and seeing what happens. That's where a dynamic debugger comes in; it lets you stop execution during the run and look at variables. Setting It Up To use gdb, you must compile your program using gcc and give the ‚g flag: gcc -ansi -g program.c -o program The -g flag tells the compiler to add information for the debugger. To debug your program, simply say: gdb program If you omit program, gdb looks for an a.out file. The rest of this handout contains several sample programs and how to use gdb to find the bugs. I recommend you use gdb to get used to it; in particular, make extensive use of its help command! Just type help to its prompt, and it will tell you what to do. Some of the features that I did not show which you will find particularly useful are the backtrace facility, which shows you what routines have been called and the values of the parameters. Example 1 Here's a program that is supposed to add 2 to a variable j every time through the for loop: #include main() { int i, j = 0; for(i = 0; i < 100; i++); j += 2; printf("The value of j is: %d\n", j); } I compile and run it, and it does not work: % gcc -ansi -g -o sample1 sample1.c % sample1 The value of j is: 2 Oops! Let's get out gdb and see what happens. As usual, what I type is in boldface, what the computer prints is in plain face, and my comments are in italics. % gdb sample1 GDB is free software and you are welcome to 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. GDB 4.10.pl1 (sparc-sun-sunos4.1), Copyright 1993 Free Software Foundation, Inc... (gdb) l list 10 lines 1 #include 2 3 main() 4 { 5 int i, j = 0; 6 for(i = 0; i < 100; i++); 7 j += 2; 8 printf("The value of j is: %d\n", j); 9 } (gdb) b main put in a breakpoint the program will stop at main when it is executed Breakpoint 1 at 0x22b4: file sample1.c, line 4. (gdb) r run the program Starting program: /usr/home/bishop/sample1 Breakpoint 1, main () at sample1.c:5 5 int i, j = 0; (gdb) n do the next statement 6 for(i = 0; i < 100; i++); (gdb) p i print i's value $1 = 100 (gdb) p j print j's value $2 = 0 aha! why is it not 200? It's not getting incremented right, so let's check the for loop ä and sure enough, that's where the problem is! (gdb) q The program is running. Quit anyway (and kill it)? (y or n) y Example 2 Now for a more complex example. Here's a program that's supposed to multiply s by 2 until s is greater than 100: #include main() { int i = 1, s; s = 3; while(i = 1){ s += s; if (s > 100) i = 0; } } I compile and run it, and it hangs; I need to kill it with control-C: % gcc -ansi -g -o sample2 sample2.c % sample2 ^C Again, let's use gdb to figure out what happened: % gdb sample2 GDB is free software and you are welcome to 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. GDB 4.10.pl1 (sparc-sun-sunos4.1), Copyright 1993 Free Software Foundation, Inc... (gdb) l 1 #include 2 3 main() 4 { 5 int i = 1, s; 6 7 s = 3; 8 while(i = 1){ 9 s += s; 10 if (s > 100) (gdb) b 9 Breakpoint 1 at 0x22b4: file sample2.c, line 9. (gdb) r Starting program: /usr/export/home/bishop/ECS-40/gdb/sample2 Breakpoint 1, main () at sample2.c:9 9 s += s; (gdb) p s $1 = 3 (gdb) n 10 if (s > 100) (gdb) c continue the run Continuing. Breakpoint 1, main () at sample2.c:9 9 s += s; (gdb) p s $2 = 6 seems to be doubling s okay (gdb) c Continuing. Breakpoint 1, main () at sample2.c:9 9 s += s; (gdb) p s $3 = 12 I'm tired of always typing "p s" when the program stops, so associate commands with that breakpoint (gdb) commands 1 Type commands for when breakpoint 1 is hit, one per line. End with a line saying just "end". p s end (gdb) c Continuing. Breakpoint 1, main () at sample2.c:9 9 s += s; $4 = 24 (gdb) c Continuing. Breakpoint 1, main () at sample2.c:9 9 s += s; $5 = 48 (gdb) c Continuing. Breakpoint 1, main () at sample2.c:9 9 s += s; $6 = 96 (gdb) p i $7 = 1 (gdb) c Continuing. Breakpoint 1, main () at sample2.c:9 9 s += s; $8 = 192 wait ä s is high here. Let's watch the value of i (gdb) watch i break whenever i changes Watchpoint 2: i (gdb) c Continuing. Watchpoint 2, i i just changed value Old value = 1 New value = 0 main () at sample2.c:12 12 } (gdb) c Continuing. Breakpoint 1, main () at sample2.c:9 9 s += s; $9 = 384 (gdb) c Continuing. Watchpoint 2, i why does i change -- the "if" is not executed! Look in the while expression; we used =, not == Old value = 1 New value = 0 main () at sample2.c:12 12 } (gdb) q The program is running. Quit anyway (and kill it)? (y or n) y Example 3 This one is a character counter (with apologies to Kernighan and Ritchie, from whose book it was mangled): #include main() /* counts digits, white space, others */ { int c, i, num_digits[10], num_white, num_other; num_white = num_other = 0; for(i = 0; i < 10; i++) /* initialize */ num_digits[i] = 0; while(c = getchar() != EOF){ switch(c){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': num_digits[c - '0']++; break; case ' ': case '\t': case '\n': num_white++; break; default: num_other++; break; } } printf("digits ="); for(i = 0; i < 10; i++) printf(" %d", num_digits[i]); printf("white space = %d, other = %d\n", num_white, num_other); } Here's a data file for our testing and debugging pleasure: 7 4 88 6 6 3 4 7 87 8 2 34 5 7 3 21 123q52 I compile and run it, and it hangs: % gcc -ansi -g -o sample3 sample3.c % sample3 < sample3.data ^C % % gdb sample3 GDB is free software and you are welcome to 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. GDB 4.10.pl1 (sparc-sun-sunos4.1), Copyright 1993 Free Software Foundation, Inc... (gdb) l 1 #include 2 3 main() /* counts digits, white space, others */ 4 { 5 int c, i, num_digits[10], num_white, num_other; 6 num_white = num_other = 0; 7 for(i = 0; i <= 10; i++) /* initialize */ 8 num_digits[i] = 0; 9 while(c = getchar() != EOF){ 10 switch(c){ (gdb) b 8 Breakpoint 1 at 0x22f0: file sample3.c, line 8. (gdb) comm 1 Type commands for when breakpoint 1 is hit, one per line. End with a line saying just "end". p i end (gdb) r Starting program: /usr/export/home/bishop/ECS-40/gdb/sample3 Breakpoint 1, main () at sample3.c:8 8 num_digits[i] = 0; $1 = 0 (gdb) s s means to skip to the next statement; if it calls a function, you'll stop in that function. n means to skip to the next statement in this function; you skip over any functions that are called. 7 for(i = 0; i <= 10; i++) /* initialize */ (gdb) n Breakpoint 1, main () at sample3.c:8 8 num_digits[i] = 0; $2 = 1 (gdb) cond 1 i>=9 this means to skip breakpoint 1 until i >= 9 is true. (gdb) c Continuing. Breakpoint 1, main () at sample3.c:8 8 num_digits[i] = 0; $3 = 9 (gdb) c Continuing. Breakpoint 1, main () at sample3.c:8 8 num_digits[i] = 0; $4 = 10 now we should leave the loop (gdb) c Continuing. Breakpoint 1, main () at sample3.c:8 8 num_digits[i] = 0; $5 = 9 huh? This probably means we're overwriting i -- or our array has one less element than we think. (gdb) q The program is running. Quit anyway (and kill it)? (y or n) y We make the change, and run it: % sample3a < sample3.data digits = 0 0 0 0 0 0 0 0 0 0white space = 0, other = 48 % gdb sample3 GDB is free software and you are welcome to 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. GDB 4.10.pl1 (sparc-sun-sunos4.1), Copyright 1993 Free Software Foundation, Inc... (gdb) l 1 #include 2 3 main() /* counts digits, white space, others */ 4 { 5 int c, i, num_digits[10], num_white, num_other; 6 num_white = num_other = 0; 7 for(i = 0; i < 10; i++) /* initialize */ 8 num_digits[i] = 0; 9 while(c = getchar() != EOF){ 10 switch(c){ (gdb) b 10 since the for loop seems to work, it's probably in the while Breakpoint 1 at 0x23ac: file sample3.c, line 10. (gdb) r < sample3.data note I can redirect standard input, output, and error as if I were in the shell Starting program: /usr/export/home/bishop/ECS-40/gdb/sample3 < sample3.data Breakpoint 1, main () at sample3.c:10 10 switch(c){ (gdb) p c $1 = 1 oops ä there's no ^A in the file! (gdb) c Continuing. Breakpoint 1, main () at sample3.c:10 10 switch(c){ (gdb) p c $2 = 1 again! looks like an assignment when I should be comparing ä (gdb) quit The program is running. Quit anyway (and kill it)? (y or n) y Lecture Notes ECS 40 ‚ FALL 1997 Page 1 The Dynamic Debugger gdb ECS 40 ‚ FALL 1997 Page 3