CSC 2400: Computer Systems Debugging C Programs Slides by Jennifer Rexford from Princeton University, slightly modified by Mirela Damian.
Goals of this Lecture • Help you learn about: ! Strategies for debugging your code ! The GDB debugger • Why? ! Debugging large programs can be difficult ! A power programmer knows a wide variety of debugging strategies ! A power programmer knows about tools that facilitate debugging – Debuggers 2
Testing vs. Debugging • Testing ! What should I do to try to break my program? • Debugging ! What should I do to try to fix my program? 3
Debugging Heuristics Debugging Heuristic When Applicable (1) Understand error messages Build-time (2) Look for familiar bugs (3) Display output (4) Use a debugger Run-time (5) Focus on recent changes 4
Understand Error Messages Debugging at build-time is easier than debugging at run- time , if and only if you… (1) Understand the error messages!!! ! Some are from the preprocessor Misspelled #include file #include <stdioo.h> int main(void) /* Print "hello, world" to stdout and return 0. Missing */ { printf("hello, world\n"); return 0; } $ gcc hello.c -o hello hello.c:1:20: stdioo.h: No such file or directory hello.c:3:1: unterminated comment hello.c:2: error: syntax error at end of input 5
Understand Error Messages (cont.) (1) Understand the error messages (cont.) ! Some are from the compiler #include <stdio.h> int main(void) /* Print "hello, world" to stdout and return 0. */ Misspelled { printf("hello, world\n") keyword retun 0; } $ gcc hello.c -o hello hello.c: In function `main': hello.c:7: error: `retun' undeclared (first use in this function) hello.c:7: error: (Each undeclared identifier is reported only once hello.c:7: error: for each function it appears in.) hello.c:7: error: syntax error before numeric constant 6
Understand Error Messages (cont.) (1) Understand error messages (cont.) ! Some are from the linker Misspelled #include <stdio.h> function name int main(void) /* Print "hello, world" to stdout and return 0. */ { prinf("hello, world\n") return 0; } Compiler warning (not error ): Linker error: Cannot find prinf() is called before declared definition of prinf() $ gcc hello.c -o hello hello.c: In function `main': hello.c:6: warning: implicit declaration of function `prinf' /tmp/cc43ebjk.o(.text+0x25): In function `main': : undefined reference to `prinf' collect2: ld returned 1 exit status 7
Look for Familiar Bugs (2) Look for familiar bugs ! Some of our favorites: switch (i) { int i; case 0: … … scanf("%d", i); /* missing break */ case 1: … char c; break; … … c = getchar(); } if (i = 5) while (c = getchar() != EOF) … … Note: enabling warnings if (5 < i < 10) if (i & j) will catch some (but not … … all) of these 8
Display Output (3) Display output ! Print values of important variables at critical spots ! Poor: stdout is buffered; program may crash printf("%d", keyvariable ); before output appears ! Maybe better: Printing '\n' flushes the stdout buffer, but not if printf("%d\n", keyvariable ); stdout is redirected to a file ! Better: Call fflush() to flush printf("%d", keyvariable ); stdout buffer explicitly fflush(stdout); 9
Display Output (cont.) (3) Display output (cont.) Write debugging output to stderr ; ! Maybe even better: debugging output can be separated fprintf(stderr, "%d", keyvariable ); from normal output via redirection ! Maybe better still: Bonus: stderr is unbuffered FILE *fp = fopen("logfile", "w"); … Write to a log file fprintf(fp, "%d", keyvariable ); fflush(fp); 10
Use a Debugger (4) Use a debugger ! Bugs often are the result of a flawed mental model; debugger can help correct mental model ! Sometimes (but not always) debugger is more convenient than inserting printing statements ! Debugger can load “core dumps” and let you step through state of program when it died ! Can “attach” to running programs to examine execution • The GDB Debugger ! Part of the GNU development environment ! Integrated with the EMACS editor 11
Using GDB • General GDB strategy: ! Execute the program to the point of interest – Use breakpoints and stepping to do that ! Examine the values of variables at that point 12
Using GDB (cont.) • Typical steps for using GDB: (1) Build with –g gcc –g testintmath.c –o testintmath • Adds extra information to executable file that GDB uses (2) Run GDB on executable file gdb ./testintmath (3) Set breakpoints, as desired break main • GDB sets a breakpoint at the first executable line of main() break gcd • GDB sets a breakpoint at the first executable line of gcd() 13
Using GDB (cont.) • Typical steps for using GDB (cont.): (4) Run the program run • GDB stops at the breakpoint in main() continue • GDB stops at the breakpoint in gcd() (5) Step through the program, as desired step (repeatedly) • GDB executes the next line (repeatedly) ! Note: When next line is a call of one of your functions: – step command steps into the function – next command steps over the function, that is, executes the next line without stepping into the function 14
Using GDB (cont.) • Typical steps for using GDB (cont.): (6) Examine variables, as desired print i print j print temp • GDB prints the value of each variable (7) Examine the function call stack, if desired where • GDB prints the function call stack • Useful for diagnosing crash in large program (8) Exit gdb quit 15
Using GDB (cont.) • Formatted printing: print /x i (print the contents of i in hexadecimal) print /t i (print the contents of i in binary) print /d i (print the contents of i in decimal) print /c i (print the contents of i as a character) 16
Using GDB (cont.) • GDB can do much more: ! Handle command-line arguments run arg1 arg2 ! Handle redirection of stdin, stdout, stderr run < somefile > someotherfile ! Print values of expressions ! Break conditionally ! Etc. 17
Focus on Recent Changes (5) Focus on recent changes ! Corollary: Debug now, not later – Difficult: Write entire program; test entire program; debug entire program – Easier: Write a little; test a little; debug a little; write a little; test a little; debug a little; … ! Corollary: Maintain previous versions – Difficult: Change code; note bug; try to remember what changed since last working version!!! – Easier: Backup code; change code; note bug; compare new version with last working version to determine what changed 18
Maintaining Previous Versions • To maintain previous versions ! Approach 1: Manually copy project directory … $ mkdir myproject $ cd myproject Create project files here. $ cd .. $ cp –r myproject myproject DateTime $ cd myproject Continue creating project files here. … – Repeat occasionally 19
Summary Debugging Heuristic When Applicable (1) Understand error messages Build-time (2) Look for familiar bugs (3) Display output (4) Use a debugger * Run-time (5) Focus on recent changes * Use GDB 20
Recommend
More recommend