Debugging
Debugging
Interface checking
– check procedure parameter number/type (if not enforced by
compiler) and value
– defensive programming: check inputs/results from other
modules
– documents assumptions about caller/callee relationships in
modules, communication protocols, etc
Assertions: include range constraints or other
information with data.
Skipping code: comment out suspect code, then check
if error remains.
Other Functions of a Debugger
Disassembly (in context and with live data!)
Execution Tracing/Stack tracing
Symbol watches
Disassembly
Most basic form of debugging
Translating machine code into assembly
instructions that are more easily understood by the
user.
Typically implementable as a simple lookup table
No higher-level information (variable names, etc.)
Relatively easy to implement.
Execution Tracing
Follows the program through the execution. Users
can step through line-by-line, or use breakpoints.
Typically allows for “watches” on – registers,
memory locations, symbols
Allows for tracing up the stack of runtime errors
(back traces)
Allows user to trace the causes of unexpected
behavior and fix them
Symbol Information
Problem – a compiler/assembler translates
variable names and other symbols into internally
consistent memory addresses
How does a debugger know which location is
denoted by a particular symbol?
We need a “debug” executable.
Debug vs. Release Builds
Debug builds usually are not optimized
Debug executables contain:
– program's symbol tables
– location of the source file
– line number tags for assembly instructions.
methodology is key
knowing about lots of debugging tools helps
the most critical tool in your arsenal is your brain
second most important tool is a debugger
– Core dumps are your friends.. Learn how to use them.
Memory debugging tools third most important
Profiler fourth
Tools
Tracing programs:
– strace, truss (print out system calls),
– /bin/bash –X to get a shell script to say what its doing
Command-line debuggers :
– gdb (C, C++), jdb (java), “perl -d”
Random stuff:
– electric fence or malloc_debug, mtrace, etc (a specialized
malloc() for finding memory leaks in C/C++)
– purify (Part of Rational Suite, a really good memory
debugging tools for C/C++ programs)
Debuggers
allow programs to inspect the state of a running
program
become more important when debugging
graphical or complex applications
jdb and gjdb for java
gdb for C/C++ programs on unix/Max
MSVS for Win32
kdb for the linux kernel
Using gdb
Compile debugging information into your
program:
gcc -g <program>
– in emacs (tex-info):
http://www.cslab.vt.edu/manuals/gdb/gdb_toc.html
M-x help <return> i <return>, arrow to GDB, <return>
– online:
gdb comands
run/continue/next/step: start the program, continue
running until break, next line (don’t step into subroutines), next
line (step into subroutines).
break <name>: set a break point on the named subroutine.
Debugger will halt program execution when subroutine called.
backtrace: print a stack trace.
print <expr>: execute expression in the current program
state, and print results (expression may contain assignments,
function calls).
help: print the help menu. help <subject> prints a subject menu.
quit: exit gdb.
General GDB
easiest to use inside of emacs (M-x gdb)
run starts the program
set args controls the arguments to the program
breakpoints control where the debugger stops the
program (set with C-x space)
next moves one step forward
step moves one step forward and also traverses
function calls
continue runs the program until the next breakpoint
General GDB
p prints a value (can be formated)
- /x hex
- /o octal
- /t binary (t is for two)
- /f float
- /u unsigned decimal
General GDB
bt shows the current backtrace (also where)
up/down move up down the stack
f # lets you switch to frame # in the current
backtrace
set var=value
call allows call of a function (the syntax can get
funky)
jump (dump to a particular line of code)
Thread # lets you switch to a particular thread
Advanced GDB
watchpoints let you check if an expression
changes
catchpoints let you know when interesting things
like exec calls or library loads happen
x lets you inspect the contents of memory
Watchpoints can be quite slow, best to combine
with breakpoints.
Advanced GDB
gcc 3.1 and up provides macro information to gdb
if you specify the options -gdwardf2 and -g3 on
the command line
you can debug and already running process with
the attach pid command
you can apply a GDB command to all threads
with thread apply all
GDB can be used to debug remote embedded
systems (gdbserver, etc..)
The example in gdb
void myinit(int startindex, int startvalue, int length, int* vect){
int i;
for(i=startindex; i< startindex+length; i++)
*vect++ = startvalue++;
}
void whattheheck(){ printf("How did I ever get here????\n"); exit(2); }
Delete breakpoint
– clear (clear all breakpoints)
– clear <breakpoint>
• e.g. clear MyClasss:22
Step
Execute one source line, then stop and return to
JDB
Example
Dump
– Display all the content of an object
• dump <object>
Analysis Tools
Purpose of Analysis Tools
Need for a feasible method to catch bugs in large
projects. Formal verification techniques require
unreasonable effort on large projects.
Augment traditional debugging techniques
without adding unreasonable burden to the
development process.
Two Types of Analysis Tools
Static Analysis
Run-time (dynamic) Analysis
Static Analysis
Examine a program for bugs without running the
program.
Examples:
– Splint (www.splint.org),
– PolySpace C Verifier (www.polyspace.com).
Splint
Open Source Static Analysis Tool developed at
U.Va by Professor Dave Evans.
Based on Lint.
Errors Splint will detect
Dereferencing a possibly null pointer.
Using possibly undefined storage or returning
storage that is not properly defined.
Type mismatches, with greater precision and
flexibility than provided by C compilers.
Violations of information hiding.
Memory management errors including uses of
dangling references and memory leaks.
Dangerous aliasing.
Errors Splint will detect continued…
Modifications and global variable uses that are
inconsistent with specified interfaces.
Problematic control flow such as likely infinite loops.
Buffer overflow vulnerabilities.
Dangerous macro initializations and invocations.
Violations of customized naming conventions.
What’s wrong with this code?
void strcpy(char* str1, char* str2)
{
while (str2 != 0)
{
*str1 = *str2;
str1++;
str2++;
}
str1 = 0; //null terminate the string
}
What happens to the stack?
void foo()
{
char buff1[20]; char buff2[40];
…
//put some data into buff2
strcpy(buff1, buff2);
}
Secure Programming
Exploitable bugs such as buffer overflows in
software are the most costly bugs.
Incredibly frequent because they are so hard to
catch.
Analysis tools play a big part in finding and
fixing security holes in software.
How does Splint deal with false
positives?
Splint supports annotations to the code that
document assumptions the programmer makes
about a given piece of code
These annotations help limit false positives.
Run-time Analysis
Many bugs cannot be determined at compile
time. Run-time tools required to find these bugs.
Run-time analysis tools work at run-time instead
of compile time.
Example – Purify (www.rational.com).
Purify
Purify modifies object files at link time.
After execution, Purify will report bugs such as
memory leaks and null dereferences.
Purify continued…
From the purify manual: “Purify checks every
memory access operation, pinpointing where
errors occur and providing diagnostic information
to help you analyze why the errors occur.”
Types of errors found by Purify
Reading or writing beyond the bounds of an
array.
Using un-initialized memory.
Reading or writing freed memory.
Reading or writing beyond the stack pointer.
Reading or writing through null pointers.
Leaking memory and file descriptors.
Using a pointer whose memory location was just
deallocated
Static vs. Run-time Analysis
Probably good to use both.
Run-time analysis has fewer false positives, but
usually requires that a test harness test all
possible control flow paths.
Cons of Analysis Tools
Add time and effort to the development process.
Lots of false positives.
No guarantee of catching every bug.
However, in a commercial situation, probably
worth your time to use these tools.
Other Tools
Mallocdebug and debugmalloc libraries
valgrind is a purify-like tool which can really
help track down memory corruption (linux
only)
MemProf for memory profiling and leak
detection (linux only)
www.gnome.org/projects/memprof
electric fence is a library which helps you find
memory errors
c++filt demangles a mangled c++ name
Memory Leaks
Memory bugs
– Memory corruption: dangling refs, buffer overflows
– Memory leaks
• Lost objects: unreachable but not freed
• Useless objects: reachable but not used again
Memory Leaks
Memory bugs
– Memory corruption: dangling refs, buffer overflows
– Memory leaks
• Lost objects: unreachable but not freed
• Useless objects: reachable but not used again
Managed Languages
80% of new software in Java or C# by 2010
[Gartner] (personally TB does not believe it..)
Type safety & GC eliminate many bugs
Memory Leaks
Memory bugs
– Memory corruption: dangling refs, buffer overflows
– Memory leaks
• Lost objects: unreachable but not freed
• Useless objects: “reachable” but not used again
Managed Languages
80% of new software in Java or C# by 2010
[Gartner]
Type safety & GC eliminate many bugs
Memory Leaks
Leaks bugs
Memory occur in practice in managed
languages [Cork, JRockit, JProbe, LeakBot, .NET Memory
– Memory corruption: dangling refs, buffer overflows
Profiler]
– Memory leaks
• Lost objects: unreachable but not freed
• Useless objects: reachable but not used again
Managed Languages
80% of new software in Java or C# by 2010
[Gartner]
Type safety & GC eliminate many bugs
Other linux Tools
strace / truss / ktrace let you know what system
calls a process is making
Data Display Debugger (DDD) is good for
visualizing your program data
www.gnu.org/software/ddd
gcov lets you see which parts of your code are
getting executed
Profilers (gprof) to see where you are spending
“time” which can help with performance logic
bugs
Code beautifier
Improve indentation of your source code for
better readability
source code beautifier in UNIX/Cygwin
– Indent
– M-x indent-region in emacs
Make sure the code beautifier
does not change how your code
works after beautification!
Avoiding bugs in the first place
Coding style: use clear, consistent style and useful
naming standards.
Document everything, from architecture and interface
specification documents to comments on code lines.
Hold code reviews.
Program defensively.
Use/implement exception handling liberally; think
constantly about anomalous conditions.
Be suspicious of cut/paste.
Consider using an integrated development environment
(IDE) with dynamic syntax checking
Code reviews
Primary programmer(s) for some piece of code presents and
explains that code, line by line.
Audience of programmers experienced in language, code’s general
domain. Audience may also contain designers, testers, customers
and others less versed in code but concerned with quality and
consistency.
Review is a dialogue: audience pushes presenters to reevaluate and
rationalize their implementation decisions.
Extremely useful: reviews often turn up outright errors,
inconsistencies, inefficiencies and unconsidered exceptional
conditions.
Also useful in familiarizing a project team with a member’s code.
War Stories
Nasty Problems