Remote Debugging with GDB
Tutorial for Debugging a program with a logical error
- This sample program has some logical errors. The program is supposed to output the summation of
- (X^0)/0! + (X^1)/1! + (X^2)/2! + (X^3)/3! + (X^4)/4! + ... + (X^n)/n!, given x and n as inputs.
- However the program outputs a value of infinity, regardless of the inputs. We will take you step by step :through the debugging process and trace the errors:
1. Download the sample program Broken.cpp
- WARNING
- If you copy and paste this code you will get an error!
- To fix this error please follow these directions.
- WARNING
int main() {
cout << "This program is used to compute the value of the following series : " << endl;
cout << "(x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ........ + (x^n)/n! " << endl;
cout << "Please enter the value of x : " ;
- The line with several points "........" will drop down one line in your code and look someting like this...
cout << "(x^0)/0! + (x^1)/1
! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ....
.... + (x^n)/n! " << endl
2. Compile the program and execute the program.
root@overo:~# g++ -g broken.cpp -o broken <enter> root@overo:~# ./broken <enter>
- hint: Do not type “g++ -o broken broken.cpp”
3. In this program no matter the input the output always is infinity. The -g option is important because it enables meaningful GDB debugging. Start the debugger
root@overo:~# gdb broken <enter>
4. This only starts the debugger; it does not start running the program in the debugger. Look at the source code and set a breakpoint at line 43
(if GDB is not your command line do as follows
root@overo:~# gdb
- Complete the following steps:
(gdb) b 43
- This puts a breakpoint at line 43 in the program's code.
Doing that will make this appear below
double seriesValue = ComputeSeriesValue(x, n);
5. Now, we start to run the program in the debugger.
(gdb) run
6. Note: If you still need to supply the command-line arguments for the execution of the program, simply include them after the run command, just as normally done on the command line. The program starts running and asks us for the input.
- HINT: Now is the time to put in any commands you left out before.
Let's enter the values as x=2 and n=3.
The expected output value is 5.
The following is a snapshot of the program running in the debugger:
- This program is used to compute the value of the following series :
- (x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ........ + (x^n)/n!
- Please enter the value of x : 2
- Please enter an integer value for n : 3
Breakpoint 1, main () at broken.cpp:43 43 double seriesValue = ComputeSeriesValue(x, n);
7. Note that the program execution stopped at our first (and only) breakpoint. Step into the ComputeSeriesValue() function
To step into a function call, we use the following command:
(gdb) step
ComputeSeriesValue (x=2, n=3) at broken.cpp:17 17 double seriesValue=0.0;
8. At this point, the program control is at the first statement of the function ComputeSeriesValue (x=2, n=3) Next let's step through the program until we get into ComputeFactorial.
To stay on track follow these commands carefully.
(gdb) next 18 double xpow=1; (gdb) n 20 for (int k = 0; k <= n; k++) { (gdb) <enter> 21 seriesValue += xpow / ComputeFactorial(k) ; (gdb) s ComputeFactorial (number=0) at broken.cpp:7 7 int fact=0;
- Note: Step lets you get a closer look at each function and line, while next skips over whole functions to the next one.
- Here we use n and s instead of next and step, respectively. If the command is simply a repeat of the previous command, you can just hit return, which will execute the last command. Finally, we step (with s) into ComputeFactorial(). (If we'd used next, it would have stepped over ComputeFactorial.)
9. Where are we?
- If you want to know where you are in the program's execution (and how, to some extent, you got there), you can view the contents of the stack using the backtrace command as follows:
(gdb) bt #0 ComputeFactorial (number=0) at broken.cpp:7 #1 0x08048907 in ComputeSeriesValue (x=3, n=2) at broken.cpp:21 #2 0x08048a31 in main () at broken.cpp:43
- Note: Backtrace lets you see where you have been and what you have done, in the case you get lost or confused it is very useful.
10. Watching changes We can step through the program (using the next command) and examine the values using the print command.
- Once again, follow these commands carefully in order to stay on track.
(gdb) next 9 for (int j = 0; j <= number; j++) { (gdb) n 10 fact = fact * j; (gdb) n 9 for (int j = 0; j <= number; j++) { (gdb) print fact $2 = 0 (gdb) n 13 return fact; (gdb) quit
- Try to find the error.
- HINT: Look inside the “print fact” command
The print command (abbreviated p) reveals that the value of fact never changes. Note that the function is returning a value of 0 for the function call ComputeFactorial(number=0). This is an ERROR! The answer will always equal zero.
By taking a closer look at the values printed above, we realize that we are computing fact=fact * j where fact has been initialized to 0, (0 = 0 * j); fact should have been initialized to 1. We quit GDB with the quit command. Next we need to change the following line:
(gdb) quit
- int fact = 1;
- HINT: It is near the top of the program’s code.
Recompile the code and run it, you will get the expected output.