Class 11: Understanding your code
Held:
We consider ways to approach debugging and other issues with your programs
Preliminaries
Overview
- The joy of segfaults
- Other memory problems
- Tracing errors with
gdb - Checking memory usage with
valgrind
Related Pages
Updates
Debuggers
- Believe it or not, but many people write incorrect code.
- Testing sometimes helps you identify that code is incorrect.
- Unit testing helps you narrow done a possible location of the problem - if the unit tests for a procedure fail, it’s likely that the procedure is incorrect.
- Clear box testing (or white box testing) helps you narrow it even further.
- In C, segmentation faults also help you identify that code is incorrect.
- Once you know that it is incorrect, you need tools that help you identify why it is incorrect.
- What tools are available?
- The most important tool is yourself - you should read through the code to see if there are things that don’t make sense.
- You’ll often find yourself saying “I’m pretty sure that the
value is this at this point.” How do you verify?
- Use
assert. - Use
printfstatements. (Not a great idea.) - Use a debugger - a program that lets you trace the execution of your program and inspect variables
- Use
- I can’t do much about about the ways you look at code, and I don’t
want you to use
printf, so we’ll look at using a debugger. - Detour: Why not use
printf? Because you spend a lot of time inserting and removing print statements. A debugger is usually much faster.
gdb
- gdb is the standard debugging tool for C.
- It’s a command-line tool.
- There are GUIs available, but my experience is that people are often faster without them.
- It’s fast.
- It lets you do all sorts of creative things, like attach to a running process.
- It has a huge array of options. We’ll focus on just a few.
Tracing Crashes
We’ll start with one typical use of gdb - figuring out what line in your program caused a segfault.
We’ll use boom.c, a program that I designed
to crash.
- You should compile with the
-gflag. This enables debugging information. - Start the debugger
$ gdb boom
- Run the program
(gdb) run
- Observe the output
Program received signal SIGSEGV, Segmentation fault. 0x08048440 in baz (i=0) at boom.c:64 64 *ip = i;
- Check the stack
(gdb) bt #0 0x08048440 in baz (i=0) at boom.c:64 #1 0x080483f5 in qux (i=1) at boom.c:55 #2 0x0804840c in qux (i=2) at boom.c:57 ... #12 0x0804844f in baz (i=67) at boom.c:66 #13 0x08048466 in main (argc=1, argv=0xbffffa54) at boom.c:77
- Analyze
- Clearly, 13 recursive calls is bad luck.
- Hmmm … the problem seems to be in the assignment
- Look at variables
(gdb) p i $1 = 0 (gdb) p ip $2 = (int *) 0x0
- Fix.
Some other operations
l *LINE_NUMBER*
- List code starting with that line
l *PROCEDURE*
- List code starting with that procedure
b *LINE_NUMBER*
- Stop when execution hits that line number
run
- Run the program until the first breakpoint
n
- Execute the next line
s
- Step into a procedure call (if the next line does not involve a procedure call, does the same thing as n* )
c
- Continue execution
p *EXPRESSION*
- Print the value of an expression
watch *variable*
- Stop when the variable has changed.
Another Example
- We’ll explore maplike.c, a program with a common (and perhaps subtle) bug.