110 likes | 385 Views
Why Debug a Program?. When your program crashes Finding out where it crashed Examining program memory at that point When a bug occurs in your program Watching program variables Identifying where a problem occurs (and why) Tracing through a program’s behaviors Learning what a program does
E N D
Why Debug a Program? • When your program crashes • Finding out where it crashed • Examining program memory at that point • When a bug occurs in your program • Watching program variables • Identifying where a problem occurs (and why) • Tracing through a program’s behaviors • Learning what a program does • Good basic testing approach
Debugging on Various Platforms • We’ll use Visual Studio’s graphical debugger • Much more on this as we go through the semester • Set command line arguments in the project properties • It’s also good to know about common text-based tools • gdb • Debugger provided with the g++ compiler (e.g., on Linux) • Using –g switch embeds debug info during compilation • dbx • Debugger provided with Sun CC • Also requires –g switch to embed debug symbols
Some Essential Debugging Commands • start (or restart) execution in the debugger • Visual Studio: F5 (Start Debugging) • gdb: run (also give its command line arguments) • execute one line, stepping into any functions • Visual Studio: F11 (Step Into) • gdb: step • execute one line, stepping over any functions • VS: F10 (Step Over) • gdb: next • set breakpoint at a line or function entry point • VS: F9 (Toggle Breakpoint) • gdb: break (can give file and line, function, etc.)
“Core” Dumps • A program dumps a memory image when it crashes • creates a file (called “core” on Linux) and then exits • this happens when a program receives certain signals: • Segmentation fault • accessed memory it does not own • or dereferenced a 0 pointer • Bus error • divided by zero • corrupted stack • Debuggers are very useful to examine “core” files • When your program crashes within Visual Studio • On Linux: gdb myprogram core
Debugging an Example Program • A debugger helps us observe a program’s behavior • Step through (and into) functions • Watching the call stack and variable values • But, before we start using the fancy tools… • What do we expect the program to do? • How might the program fail? • Can we make predictions and test them? • Thinking: the most powerful way to debug • Scientific method should guide what you do • hypothesis, prediction, experiment, analysis • Tools can help you follow this disciplined approach faster
What We Expect the Example Program To Do • Called with command line arguments ./prefix_adder + 8 + 9 10 • Calculates prefix addition expressions + 8 + 9 10 + + 8 9 10 • These are equivalent to their infix versions (8 + (9 + 10)) ((8 + 9) + 10) • Key idea: walk through expresion, calculate value same result different order 1 1 + + 2 3 2 5 8 + + 10 4 5 3 4 9 8 10 9
How the Example Program Can Fail • Too few arguments in expression ./prefix_adder + 8 + 9 • Cannot calculate result + 8 + 9 (needs another value to finish 2nd + operation) • Try this on your own in the studio, for practice 1 + 2 3 8 + 4 ??? 9
Example Program Header File // prefix_adder.h // // author: Chris Gill cdgill@cse.wustl.edu // // purpose: Declarations for a simple prefix adder program, which // takes the command line arguments as a prefix addition // expression and computes an integer result. #ifndef PREFIX_ADDER_H #define PREFIX_ADDER_H // Function prototypes. void usage (char * program_name); int parse_and_compute (int & current_index, int last_index, char *argv[]); #endif /* PREFIX_ADDER_H */
Example Program Source File / Usage Function // prefix_adder.cc // // author: Chris Gill cdgill@cse.wustl.edu // // purpose: definitions for a simple prefix adder program, which // takes the command line arguments as a prefix addition // expression and computes an integer result. #include "prefix_adder.h" #include <iostream> // For std output stream and manipulators. #include <string> // For standard C++ strings. #include <sstream> // For standard string streams. #include <cstring> // For C-style string functions // Helper function to print out the program's usage message. void usage (char * program_name) { cout << "Usage: " << program_name << " <argument> [<argument>]..." << endl << "Purpose: computes program arguments as prefix addition expression" << endl; }
Example Program Main Function int main (int argc, char *argv[]) { // A few useful constants for argument positions const int minimum_arguments = 2; const int starting_index = 1; const int program_name_index = 0; if (argc < minimum_arguments || strcmp (argv[starting_index], "--help") == 0) { usage (argv[program_name_index]); return 1; } try { // Pass the current and last index to use, and the array, to the // expression parsing function, and store the result. int current_position = starting_index; int value = parse_and_compute (current_position, argc - 1, argv); // Print out the result, and return success value. cout << "The value calculated is " << value << endl; return 0; } catch (...) { cout << "caught exception" << endl; return -1; } }
Example Program Parsing Function // Helper function to parse the input symbols and compute a value. int parse_and_compute (int & current_index, int last_index, char *argv[]) { // make sure we're still in the argument range if (current_index > last_index) { throw 1; } // look for a single-symbol addition operator if (strlen (argv[current_index]) == 1 && *(argv[current_index]) == '+') { int first_operand = parse_and_compute (++current_index, last_index, argv); int second_operand = parse_and_compute (current_index, last_index, argv); return first_operand + second_operand; } // treat anything else as an integer else { int result; istringstream i (argv[current_index++]); i >> result; return result; } }