debugging tools

79
Debugging Tools Towards better use of system tools to weed the nasty critters out of your programs Dr. Pedro Mejia Alvarez CINVESTAV-IPN

Upload: bart

Post on 03-Feb-2016

38 views

Category:

Documents


0 download

DESCRIPTION

Debugging Tools. Towards better use of system tools to weed the nasty critters out of your programs. Dr. Pedro Mejia Alvarez CINVESTAV-IPN. 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 : - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Debugging Tools

Debugging Tools

Towards better use of system tools to weed the nasty critters out of your

programs

Dr. Pedro Mejia Alvarez

CINVESTAV-IPN

Page 2: Debugging Tools

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)

Page 3: Debugging Tools

• allow programs to inspect the state of a running program

• become more important when debugging graphical or complex applications

• Common debuggers: adb, dbx, gdb, kdb, wdb, xdb.

• Microsoft Visual Studio has a built-in debugger.

• jdb and gjdb for java

• gdb for C/C++ programs on unix/Max

• MSVS for Win32

• kdb for the linux kernel

Debuggers

Page 4: Debugging Tools

Using gdb

• Compile debugging information into your program:

gcc -g <program>

• Read the manual:– man gdb

– in emacs (tex-info):http://www.cslab.vt.edu/manuals/gdb/gdb_toc.htmlM-x help <return> i <return>, arrow to GDB, <return>

– online:

Page 5: Debugging Tools

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.

Page 6: Debugging Tools

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

Page 7: Debugging Tools

General GDB

• p prints a value (can be formated)- /x hex

- /o octal

- /t binary (t is for two)

- /f float

- /u unsigned decimal

Page 8: Debugging Tools

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

Page 9: Debugging Tools

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.

Page 10: Debugging Tools

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..)

Page 11: Debugging Tools

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); }

int main(int argc, char**argv){

float d;int a,b[10],c, i, start,end;if(argc != 3) {printf("Usage:%s start, end\n",argv[0]);exit(-1); } start=atoi(argv[1]); end=atoi(argv[2]); /* bad style but shorter */ a=0; c=0; d=3.14159; /* bad style but shorter */ printf("Initally a %d, c %d, d %f, start %d, end %d\n",a,c,d, start,end); myinit(start,start,end,b+start); printf("finally a %d, c %d, d %f start %d, end %d \n",a,c,d, start, end); if(end>10) b[end-1]=134513723; return 0;

}

Page 12: Debugging Tools

JDB

• Compile your source file with -g option– Produce debugging information– For examplejavac –g rsdimu.java

• To run jdb,– Type “jdb”, use run command to load an executable

• run <class name>• For example, run rsdimu

– OR type “jdb <class name>, then• Type “run” to execute the program

– Type “quit” to exit jdb

Page 13: Debugging Tools

Breakpoint

• Make your program stops whenever a certain point in the program is reached

• For example– Make a breakpoint in

line 6stop at Demo:6

– Program stops beforeexecute line 6

– Allow you to examinecode, variables, etc.

public class Demo{ public static void main(…) { System.out.println(“A\n”); System.out.println(“B\n”); System.out.println(“C\n”); }}

123456789

Page 14: Debugging Tools

Breakpoint

• Add breakpoint– stop at MyClass:<line num>– stop in java.lang.String.length – stop in MyClass.<method name>

• Delete breakpoint– clear (clear all breakpoints)– clear <breakpoint>

• e.g. clear MyClasss:22

Page 15: Debugging Tools

Step

• Execute one source line, then stop and return to JDB

• Example

public void func(){ System.out.println(“A\n”); System.out.println(“B\n”); System.out.println(“C\n”); return 0;}

public void func(){ System.out.println(“A\n”); System.out.println(“B\n”); System.out.println(“C\n”); return 0;}

step

Page 16: Debugging Tools

Next

• Similar to step, but treat function call as one source line

• Example

next

public void func1() {println(“B\

n”);println(“C\

n”);}public void func() {

println(“A\n”);

func1();return 0;

}

public void func1() {println(“B\

n”);println(“C\

n”);}public void func() {

println(“A\n”);

func1();return 0;

}

step

Page 17: Debugging Tools

Cont

• Resume continuous execution of the program until either one of the followings– Next breakpoint– End of program

Page 18: Debugging Tools

Print

• Print– Display the value of an expression

•print expression– print MyClass.myStaticField– print i + j + k– print myObj.myMethod() (if myMethod returns a non-null)– print new java.lang.String("Hello").length()

• Dump– Display all the content of an object

•dump <object>

Page 19: Debugging Tools

Visual Debugger

• Graphically Oriented

• Run from Visual Studio

• Can debug a failed process by selecting the Yes button at “Debug Application” dialog after a memory or other failure occurs

• Can attach to a running process by choosing the Tools->Start Debug->Attach to Process menu option

Page 20: Debugging Tools

The Visual Debugger

Page 21: Debugging Tools

Breakpoints

• Can stop execution at any line and in any function. (Location)

• Can set conditions on breakpoints if you are only interested in specific passes through a piece of code (Location->Condition)

• Conditional breakpoints detached from any one line in the program are also possible, but make program execution very slow (Data).

Page 22: Debugging Tools

Breakpoint Window

Page 23: Debugging Tools

Conditional Window

Page 24: Debugging Tools

Conditional Data Breakpoint

Page 25: Debugging Tools

Examining Program State

• Print and/or Change variable state.

• Walk up/down the stack trace.

• View disassembled code.

Page 26: Debugging Tools
Page 27: Debugging Tools

Quick Print/Change Variables

Page 28: Debugging Tools

Execution Flow

• Step Into - Execute code, step into a function if one is called

• Step Out - Continue execution until N-1’st region of stack frame reached

• Step Over - Execute code, execute any functions that are called without stopping.

Page 29: Debugging Tools

Debugging Techniques

• Use assertions liberally

• Add conditionally compilable debugging code

• Multiple platform execution has a way of bringing bugs to the surface

Page 30: Debugging Tools

Assertions

• Can be used to enforce function pre and post conditions

• Make your implicit assumptions explicit

• Can be turned off in final release for a performance boost or left in with messages to help in bug report creation

Page 31: Debugging Tools

Conditional Compilation

• Maintain multiple customized versions in one code base.

• Typically have one debug version of your code for bug killing and a release version (sans debug code) for high performance.

• Caveat 1: You do need to test the release version before shipping.

• Caveat 2: Conditional Compilation not available in all languages.

Page 32: Debugging Tools

Multiple Platform Execution

• Additional initial design effort

• Great debugging aid

• Can be a commercial selling point

Page 33: Debugging Tools

A few tricky cases before moving on . . .

• The library function calls go nuts, but only when they are called after function X . . .

• My program is freeing block x prematurely. How do I find out why (and more importantly because of where)?

• I am using files to synchronize two programs “halves” under nfs. The process periodically breaks when a file open fails.

Page 34: Debugging Tools

Analysis Tools

Page 35: Debugging 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.

Page 36: Debugging Tools

Two Types of Analysis Tools

• Static Analysis

• Run-time (dynamic) Analysis

Page 37: Debugging Tools

Static Analysis

• Examine a program for bugs without running the program.

• Examples: – Splint (www.splint.org), – PolySpace C Verifier (www.polyspace.com).

Page 38: Debugging Tools

Lint

• Lint is a semantic checker that identifies potential bugs in C programs

• Lint is a mistake!

• In the early days of C on UNIX complete semantic checking was removed from the C compiler as a design decision. This allowed for smaller, simpler, and faster compilers at the expense of potentially buggy code.

• Lint exists on UNIX systems (but not LINUX)

• Most modern ANSI C compilers include Lint semantic checking functionality but only some of Lint’s other features

• Use Lint Early and Often!

Page 39: Debugging Tools

What does Lint Do?

• Checks for consistency in function use across multiple files

• Finds

– bugs

– non-portable code

– wasteful code

• Typical Bugs Detected include

– Argument types transposed between function and call

– Function with wrong number of arguments takes junk from stack

– Variables being used before set or never used

Page 40: Debugging Tools

More about Lint

• See Unix man page

• OR “Checking C Programs with lint” By Ian F. Darwin

Page 41: Debugging Tools

Splint

• Open Source Static Analysis Tool developed at U.Va by Professor Dave Evans.

• Based on Lint.

Page 42: Debugging Tools

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.

Page 43: Debugging Tools

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.

Page 44: Debugging Tools

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

}

Page 45: Debugging Tools

What happens to the stack?

void foo()

{

char buff1[20]; char buff2[40];

//put some data into buff2

strcpy(buff1, buff2);

}

Page 46: Debugging Tools

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.

Page 47: Debugging Tools

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.

Page 48: Debugging Tools

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).

Page 49: Debugging Tools

Purify

• Purify modifies object files at link time.

• After execution, Purify will report bugs such as memory leaks and null dereferences.

Page 50: Debugging Tools

Purify

• Purify is a tool for locating runtime errors in a C/C++ program

• Purify can find– Array bounds errors

– Accesses through dangling pointers

– Uninitialized memory reads

– Memory allocation errors

– Memory leaks

• Purify is available on Windows and UNIX systems and is a product of Rational Software www.rational.com

Page 51: Debugging Tools

How Purify Works

• Purify instruments a program by adding protection instructions around every load and store operation

• When program is executed a viewer will be created to display errors as they happen

• Purify is flexible and can be run standalone with any executable (written in C) or within a debugging environment like Visual Studio

• Purify is customizable and can be set to ignore certain types of errors

Page 52: Debugging Tools

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.”

Page 53: Debugging Tools

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

Page 54: Debugging Tools

How to Use Purify

• add purify command to link command• program: $(OBJS)

purify [-option ...] $(CC) $(CFLAGS) -o\ program $(OBJS) $(LIBS)

• OR run purify in Visual Studio • OR load file in purify executable

Page 55: Debugging Tools
Page 56: Debugging Tools
Page 57: Debugging Tools

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.

Page 58: Debugging Tools

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.

Page 59: Debugging 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

Page 60: Debugging 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

Other linux Tools

Page 61: Debugging Tools

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 beautifierdoes not change how your codeworks after beautification!

Page 62: Debugging Tools

Linux Garbage Collection Aids

• If you are using C then checker-gcc is an excellent tool - compile your code using modified gcc compiler and memory errors flagged

• Options exist in C++ (checker-g++, ccmalloc, dmalloc), but they tend to be fragile and/or very slow.

Page 63: Debugging Tools

Performance & Programming Tuning

• Profiling

• Code tuning

• Refactoring

Page 64: Debugging Tools

Performance Tuning

• Why tune? Won’t processors be twice as fast next year?

– Customers want it faster NOW

– Processor speed isn’t always the bottleneck

– Algorithmic improvements can speed up your code far more than 2x

– Embedded systems

Page 65: Debugging Tools

When Should I Tune?

• Knuth: “Premature optimization is the root of all evil”• Tune after you test and debug your code– No point being fast if it’s wrong– Bug fixes can de-tune code– Tuning often makes code more complicated, making it

more difficult to debug• Maintain/Improve performance after you ship– Add performance tracking to regression suite to

prevent degradation

Page 66: Debugging Tools

The Tuning Process

• Don’t tune unless you really have to

• Iterative process

– Profile, tune, profile, tune . . .

– This continues until you reach the point of diminishing returns

Page 67: Debugging Tools

Profiling

• Profiling will tell you where you’re program is spending it’s time

– A typical program spends 90% of its time in 10% of the code

– You want to speed up the hot code• NEVER tune without profiling– With complex software difficult to tell where the

program spends its time• Profile under realistic conditions with realistic data

Page 68: Debugging Tools

Profilers

• All profilers are intrusive

– They perturb the program being profiled

– Want a profiler that minimizes the intrusion

Page 69: Debugging Tools

Do-It-Yourself Profilers

• Add timers to the source code

• Usually want time spent in your process, not real time

– Unix user+sys time not real time

• Use HW counters

– Often count cycles for all processes on the system, so you need to run on a quiescent machine

Page 70: Debugging Tools

Function-level profilers

• Two major types of profilers– Instrumentation:

• Automatically add code to the program to– count how often a function is called

– record how much time is spent in a function

• Usually requires recompiling or relinking

– Stochastic• Stops program every 10-100ms and check what function

the program counter is in• Some work out of the box, others require a relink

Page 71: Debugging Tools

Instruction-Level Profilers

• Good for tuning within a function (if you read assembly code)

• Usually stochastic profiler: requires longer run than function level since more fine-grained information

• Shade (Solaris) and Atom (Alpha) interpret the machine code and count the number of times a given instruction is executed

• CPU emulators can tell you anything you need to know (if you have the time)

Page 72: Debugging Tools

Code Tuning Techniques

• Change algorithm– Most gain, but also most difficult– Example: set data structure

• If sets are dense, bit vectors often better• If sets are sparse, hash tables, binary trees, or

another sparse data structure might be better

Page 73: Debugging Tools

Code Tuning Techniques II

• Make hot functions faster

– Throw more compiler optimizations at it

– Rewrite in assembly (often not worth it)

– Indirect calls -> direct calls

• C++: virtual functions -> non-virtual

• Java: non-static functions -> static

– Probably not worth it with latest JVM’s

– Move infrequently executed code out of the way

– Eliminate unnecessary I/O, system calls, allocation

Page 74: Debugging Tools

Code Tuning Techniques III

• Call hot functions less often

– Cache previously computed values (memoization)

– Inline: eliminates call overhead and allows compiler to do better job optimizing

• Inline by hand if compiler can’t (ex: indirect calls)

• Java: less synchronization

– Ex “a” + “b” + “c”

• NewStringBuffer(“a”).append(“b”).append(“c”).toString()

• 3 monitor enter instructions: All unnecessary

Page 75: Debugging Tools

Intuitive Approach

• Previous suggestions geared towards explicit speed improvements

• Alternative approach is to code algorithms in a simple easy-to-understand manner

• If it is easy for others to understand compiler can probably understand it, too

• Result: Compiler optimization can be much more effective

Page 76: Debugging Tools

Options Tuning

• Don’t optimize a program whose running time doesn’t matter

• Start with -O– Typical Speedup: 2x– Even local optimizations help 30-50%– YMMV

• Inlining: 10% if done blindly, 30% if done with profiling information

• Aliasing options: Allow compiler to eliminate more memory references

Page 77: Debugging Tools

Options Tuning for Java

• Increase max heap size for less frequent GC

• Experiment with vendor-specific options– Often many options for improving

synchronization performance

Page 78: Debugging Tools

Refactoring• Refactoring is:

– restructuring (rearranging) code in a series of small, semantics-preserving transformations (i.e. the code keeps working) in order to make the code easier to maintain and modify

• Refactoring is not just arbitrary restructuring– Code must still work– Small steps only so the semantics are preserved (i.e. not a major re-write)– Unit tests to prove the code still works– Code is

• More loosely coupled• More cohesive modules • More comprehensible

• There are numerous well-known refactoring techniques– You should be at least somewhat familiar with these before inventing your own– Refactoring “catalog”

Page 79: Debugging Tools

When to refactor

• You should refactor:– Any time that you see a better way to do things

• “Better” means making the code easier to understand and to modify in the future

– You can do so without breaking the code• Unit tests are essential for this

• You should not refactor:– Stable code that won’t need to change

– Someone else’s code • Unless the other person agrees to it or it belongs to you

• Not an issue in Agile Programming since code is communal