Debugging: Catching Bugs ( I ) Ying Wu Electrical & Computer Engineering Northwestern University ECE230 Lectures Series
A Little Challenged? Keys in programming –Your solution design –Language syntax code –Debugging skills working programs –Never stop improving good programs The role of the compiler –Very very little: acting like a grammar checker Your role in programming –You need to have a solution –You need to make sure what you code is what you want –You need to make sure there is no bugs in your code –Overwhelmed? Don’t panic! Life is not as bad as you may think.
Outline Introduction –What is debugging –Why do we need debugging –What do you need to have A simple way –testing a black box –printing out intermediate results Assert yourself –assert( ) Stepping through your code (see next lecture) –Debugging tools
What is a bug? Syntax errors are easy to fix –Once you’ve understood all the syntax But a run-time failure is most likely caused by –Logic bugs –Overflow and underflow bugs –Data conversion bugs –Wild pointer bugs –Garbage memory bugs –…
What is debugging? Always make sure you know what you are expecting from the code If your program did not output what you expected, tract it down to the exact point You will find the bug! Go ahead and fix it. your code inputoutput Debugging
Why? The complier will never catch bugs –The complier only check syntax errors –It will never check if your code would fulfill your tasks. You are on you own to –make your program correct –make your program robust –make sure what is really going on in your code There is no magic in programming
What do you need? Deep understanding of the language itself Fundamentals on low-level programming Debugging tools Good coding style Right attitude
// Binary search int binarySearch( const int b[], int searchKey, int low, int high, int size ) { int middle; while ( low <= high ) { middle = ( low + high ) / 2; if ( searchKey == b[ middle ] ) // match return middle; else if ( searchKey < b[ middle ] ) high = middle - 1; // search low end of array else low = middle + 1; // search high end of array } return -1; // searchKey not found } print_array(b); cout << key << low << high << size << endl; cout << low << middle << high << endl; print_array(b, low, high); cout << low << middle << high << endl; print_array(b, low, high);
A Tale of Two Versions Release version –Clean –Fast Debug version –Contains many debug information –Big and slow –Using macro #ifdef, for example #ifdef DEBUG // print_out_stuff … #endif Maintain both versions for your program
// Binary search int binarySearch( const int b[], int searchKey, int low, int high, int size ) { int middle; while ( low <= high ) { middle = ( low + high ) / 2; if ( searchKey == b[ middle ] ) // match return middle; else if ( searchKey < b[ middle ] ) high = middle - 1; // search low end of array else low = middle + 1; // search high end of array } return -1; // searchKey not found } #ifdef DEBUG print_array(b); cout << key << low << high << size << endl; #endif #ifdef DEBUG cout << low << middle << high << endl; print_array( b, low, high ); #endif #define DEBUG
Introducing assert assert is a macro defined in Functionality –Test the value of an expression –If the value of the exp is 0, then assert prints an error message call function abort() (which is defined in ) –If not, do nothing To cancel all assertion, use #define NDEBUG Some symbolic constant –Line number of current code __LINE__ –Name of current source file __FILE__
Assert Yourself Do not wait for bugs to happen Use startup checks Use assertions –To validate function arguments –To detect impossible conditions –To catch illegal uses of undefined behavior
// Binary search int binarySearch( const int b[], int searchKey, int low, int high, int size ) { int middle; while ( low <= high ) { middle = ( low + high ) / 2; if ( searchKey == b[ middle ] ) // match return middle; else if ( searchKey < b[ middle ] ) high = middle - 1; // search low end of array else low = middle + 1; // search high end of array } return -1; // searchKey not found } assert ( b != NULL && size > 0 ); assert ( low >= 0 && high <= size-1 ); #include assert ( low <= high );
typedef unsigned char byte; // memcpy – copy a nonoverlapping memory block void *memcopy(void *pTo, void *pFrom, size_t size) { byte *pbTo = (byte *)pTo; byte *pbFrom = (byte *)pFrom; while ( size-- > 0) *pbTo++ = *pbFrom++; return (pTo); } assert ( pTo != NULL && pFrom != NULL); assert ( pbTo >= pbFrom + size || pbFrom >= pbTo + size ); pFrompTo pbFrompbTopbFrompbTo pFrompTosize = 10 ?