Difference between revisions of "Remote Debugging with GDB"

From Gumstix User Wiki
Jump to: navigation, search
(New Debugging Tutorial)
(remove spam)
 
(31 intermediate revisions by 4 users not shown)
Line 1: Line 1:
'''Tutorial for Debugging a program with a logical error'''
+
In this tutorial we will go over how to perform a basic debugging with GDB, the GNU debugger. This environment allows us to debug C, C++ and Java (compiled to machine code, not for running in a Java Virtual Machine) programs.
 +
*We will complete these tasks step by step
 +
:*Install the GDB debugger
 +
:*Run the debugger
 +
:*Set Breakpoints
 +
:*Navigate the code in your program
 +
:*Diagnose an error and how to fix it
  
:This sample program has some logical errors. The program is supposed to output the summation of
+
*Required Items
:(X^0)/0! + (X^1)/1! + (X^2)/2! + (X^3)/3! + (X^4)/4! + ... + (X^n)/n!, given x and n as inputs.
+
:*Overo COM with Expansion Board
: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:
+
:*Mini B to Standard A Cable
 +
:*Bootable MicroSD card
 +
:*5V Power Supply
  
----
+
*Required Reading (things you should know, or tutorials that explain)
----
+
:*[[HelloWorld | HelloWorld in Python, C and C++]]
----
+
:*[[Eclipse on Gumstix for new users | Eclipse Tutorial]]
----
+
::*Although the majority of this tutorial pertains to debugging and C programming, it contains useful information which leads to basic understanding of your Overo COM
 +
 
 +
==Installing the GDB Debugger==
 +
Before starting this, you should have Console access to your Overo COM and should know how to program and run a basic C++ program. If you do not know how to do this, see [[HelloWorld | HelloWorld in Python, C and C++]].
  
 +
If you haven't yet, install a C/C++ SDK with the following command
 +
opkg update
 +
opkg install task-native-sdk
  
1. Download the sample program [http://cs.baylor.edu/~donahoo/tools/gdb/broken.cpp Broken.cpp]
+
To install the GDB debugger, run the following command in your command line:
 +
opkg update
 +
opkg install gdb
 +
Once this has completed, you will have the GNU GDB debugger installed on your Overo COM
  
:::'''WARNING'''
+
==Creating the Test File==
::::If you copy and paste this code you will get an error!
+
Now that we have the debugging environment
::::To fix this error please follow these directions.
+
+
  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...'''
+
  
 +
Run the following in your command line:
 +
nano broken.cpp
 +
Then copy and paste the code from [http://cs.baylor.edu/~donahoo/tools/gdb/broken.cpp |Broken.cpp] into your shell. When you copy and paste there will be two syntax errors that you must fix at the lines:
 +
cout << "This program is used to compute the value of the following
 +
series : " << endl;
 +
and
 
  cout << "(x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ....
 
  cout << "(x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ....
 
  .... + (x^n)/n! " << endl
 
  .... + (x^n)/n! " << endl
  
::::To fix this you have to bring all of the code back onto a single line. It should end up looking like this.
+
Copying and pasting added an extra line break to these two lines, and we want to undo this by deleting one space at the beginning of each second line so it looks like this:
 +
cout << "This program is used to compute the value of the following series : " << endl;
 +
and
 +
cout << "(x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ........ + (x^n)/n! " << endl
  
cout << "(x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ........ + (x^n)/n! " << endl;
+
Now save and exit by pressing the following:
+
:*Ctrl + O
 +
:*<Enter>
 +
:*Ctrl + X
  
2. Compile the program and execute the program.
+
==Compile and Run broken.cpp==
 +
Now that we have our test file (broken.cpp) ready to go, it is time to run it.  
  
root@overo:~# g++ -g broken.cpp -o broken <enter>
+
Compile the program to create an executable file (broken) by entering the following command:
root@overo:~# ./broken <enter>
+
g++ -g broken.cpp -o broken
 +
The '-g' switch tells the compiler to include debugging symbols in the executable. Run the program as such:
 +
./broken
 +
----
 +
Note:
 +
Use 'g++ -g broken.cpp -o broken' to compile. If you use g++ -o, g++ --ggdb -Wall, or any other method of compilation the debugging will not follow this tutorial exactly.
 +
----
  
 +
Enter any numbers into the program for x and n. Run it a couple times, you should notice that the answer is always the same: infinity. This is obviously wrong.
  
:hint: Do not type “g++ -o broken broken.cpp”
+
Consider: x=1, n=1
 +
(1^0)/0! + (1^1)/1!
 +
1/1 + 1/1
 +
2
  
 +
NOT infinity
  
----
+
==Debugging the Program==
----
+
Now, it is time that we debugged our program.
  
 +
First, you need to start the GDB environment by issuing the following command:
 +
gdb
 +
You should see the following command line appear:
 +
(gdb)
 +
Then, you need to 'tell' the debugger which file it is that we are debugging, do this by issuing the command:
 +
(gdb) exec-file broken
 +
Now, it is time to run our program by issuing the command:
 +
(gdb) run
  
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
+
Notice that the program runs normally and again, gives us the answer inf. This is because the error we have is a computation error, and does not cause our program to crash. So, its time to set a Breakpoint in our program.
 
+
root@overo:~# gdb broken <enter>
+
 
+
  
 +
===Setting a Breakpoint===
 
----
 
----
 +
Hint:
 +
A breakpoint is a point in the program at which the debugger will stop the code from executing. Then, you can choose to 'step' through the program line by line to 'see' exactly what the CPU is doing. This way, you can effectively watch the program execute and discover where the problem is!
 
----
 
----
 
+
Lets set a breakpoint within the program so that we can see what is going on. Lets start by setting a breakpoint at line 43. Do this with the following command:
 
+
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
 
  (gdb) b 43
 +
You could also set a breakpoint to a specific functions call, such as:
 +
(gdb) b ComputeSeriesValue
 +
However, for the purpose of this tutorial, use the line number. If you wish, you can play around with using the function instead, but it will cause you to diverge from this tutorial.
  
::This puts a breakpoint at line 43 in the program's code.
+
If the program outputs: No symbol table is loaded. Use the "file" command. Then use the following command:
 
+
(gdb) file broken
 
+
This will set the file so that you can add a breakpoint without error
Doing that will make this appear below
+
  
 +
Line 43 is the following:
 
  double seriesValue = ComputeSeriesValue(x, n);
 
  double seriesValue = ComputeSeriesValue(x, n);
 +
So the program will stop executing immediately before it executes the function ComputeSeriesValue(x, n).
  
 
+
Now, lets run the program with our command:
----
+
----
+
 
+
 
+
5. Now, we start to run the program in the debugger.
+
 
+
+
 
  (gdb) run
 
  (gdb) run
 
+
Let's enter the values as x=2 and n=3, with an expected output value is 5.  
 
+
Here is a snapshot of what the program should output at this point:
----
+
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
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
 
  Breakpoint 1, main () at broken.cpp:43
 
  43  double seriesValue = ComputeSeriesValue(x, n);
 
  43  double seriesValue = ComputeSeriesValue(x, n);
 +
The program has stopped at our breakpoint, line 43.
  
 
+
===Stepping into a function===
---- 
+
Next, we are going to step into the function ComputeSeriesValue(x, n)
 
----
 
----
 
+
Hint:
 
+
'Stepping' through a function allows us to go line by line, and see exactly what lines of code are being executed. This way we can manually diagnose the computation problem.
7. Note that the program execution stopped at our first (and only) breakpoint. Step into the ComputeSeriesValue() function
+
----
     
+
To start stepping, issue the following command
To step into a function call, we use the following command:
+
 
+
 
  (gdb) step
 
  (gdb) step
 
+
----
+
Hint:
 +
A nice thing about the GDB Debugger is that it will save the last command you issued so that you don't need to type it over and over. This way you can keep stepping through the function by pressing <enter>. You do not need to retype 'step' <br>
 +
Hint:
 +
The GDB Debugger also has nice 'shortcuts'. For example, 'step' and 's' are the same command. So is 'breakpoint' and 'b'.
 +
----
 +
You should see the following outputted:
 
  ComputeSeriesValue (x=2, n=3) at broken.cpp:17
 
  ComputeSeriesValue (x=2, n=3) at broken.cpp:17
 
  17  double seriesValue=0.0;
 
  17  double seriesValue=0.0;
 +
At this point, the program has gone to line 17, and is at the first line of the function ComputeSeriesValue, which sets the double seriesValue to 0.0.
  
 
+
Now, we wish to use the 'next' function until we reach the function CompueFraction(). To do this, use the following commands:
----
+
----
+
 
+
 
+
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
 
  (gdb) next
 
  18  double xpow=1;
 
  18  double xpow=1;
  (gdb) n
+
  (gdb) <enter>
 
  20  for (int k = 0; k <= n; k++) {
 
  20  for (int k = 0; k <= n; k++) {
 
  (gdb) <enter>  
 
  (gdb) <enter>  
 
  21    seriesValue += xpow / ComputeFactorial(k) ;
 
  21    seriesValue += xpow / ComputeFactorial(k) ;
  (gdb) s
+
  (gdb) step
 
  ComputeFactorial (number=0) at broken.cpp:7
 
  ComputeFactorial (number=0) at broken.cpp:7
 
  7  int fact=0;
 
  7  int fact=0;
  
 
+
==Using Backtrace==
:Note: Step lets you get a closer look at each function and line, while next skips over whole functions to the next one.
+
*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
: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.)
+
#1  0x08048907 in ComputeSeriesValue (x=3, n=2) at broken.cpp:21
 
+
#2  0x08048a31 in main () at broken.cpp:43
 
+
 
----
 
----
 +
Hint:
 +
Backtrace lets you see where you have been and what you have done. In case, you get lost or confused it is very useful. <br>
 +
Hint:
 +
A stack is a data structure in Computer Science which stores data much like a stack of cards. It is a Last On, First Off data structure. For more explanation of how a stack functions, see the wikipedia article. <br>
 +
http://en.wikipedia.org/wiki/Stack_(data_structure)
 
----
 
----
 
+
See the error?
 
+
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.
 
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:
 
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;
+
change the line
::HINT: It is near the top of the program’s code.
+
int fact = 0;
 +
to
 +
int fact =1;
 +
and you're all set!
  
Recompile the code and run it, you will get the expected output.
+
==More Tutorial==
 +
For another tutorial which is more in depth, see:
 +
http://www.cs.cmu.edu/~gilpin/tutorial/

Latest revision as of 15:02, 23 November 2010

In this tutorial we will go over how to perform a basic debugging with GDB, the GNU debugger. This environment allows us to debug C, C++ and Java (compiled to machine code, not for running in a Java Virtual Machine) programs.

  • We will complete these tasks step by step
  • Install the GDB debugger
  • Run the debugger
  • Set Breakpoints
  • Navigate the code in your program
  • Diagnose an error and how to fix it
  • Required Items
  • Overo COM with Expansion Board
  • Mini B to Standard A Cable
  • Bootable MicroSD card
  • 5V Power Supply
  • Required Reading (things you should know, or tutorials that explain)
  • Although the majority of this tutorial pertains to debugging and C programming, it contains useful information which leads to basic understanding of your Overo COM

Installing the GDB Debugger

Before starting this, you should have Console access to your Overo COM and should know how to program and run a basic C++ program. If you do not know how to do this, see HelloWorld in Python, C and C++.

If you haven't yet, install a C/C++ SDK with the following command

opkg update 
opkg install task-native-sdk

To install the GDB debugger, run the following command in your command line:

opkg update
opkg install gdb

Once this has completed, you will have the GNU GDB debugger installed on your Overo COM

Creating the Test File

Now that we have the debugging environment

Run the following in your command line:

nano broken.cpp

Then copy and paste the code from |Broken.cpp into your shell. When you copy and paste there will be two syntax errors that you must fix at the lines:

cout << "This program is used to compute the value of the following
series : " << endl;

and

cout << "(x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ....
.... + (x^n)/n! " << endl

Copying and pasting added an extra line break to these two lines, and we want to undo this by deleting one space at the beginning of each second line so it looks like this:

cout << "This program is used to compute the value of the following series : " << endl;

and

cout << "(x^0)/0! + (x^1)/1! + (x^2)/2! + (x^3)/3! + (x^4)/4! + ........ + (x^n)/n! " << endl

Now save and exit by pressing the following:

  • Ctrl + O
  • <Enter>
  • Ctrl + X

Compile and Run broken.cpp

Now that we have our test file (broken.cpp) ready to go, it is time to run it.

Compile the program to create an executable file (broken) by entering the following command:

g++ -g broken.cpp -o broken

The '-g' switch tells the compiler to include debugging symbols in the executable. Run the program as such:

./broken

Note: Use 'g++ -g broken.cpp -o broken' to compile. If you use g++ -o, g++ --ggdb -Wall, or any other method of compilation the debugging will not follow this tutorial exactly.


Enter any numbers into the program for x and n. Run it a couple times, you should notice that the answer is always the same: infinity. This is obviously wrong.

Consider: x=1, n=1 (1^0)/0! + (1^1)/1! 1/1 + 1/1 2

NOT infinity

Debugging the Program

Now, it is time that we debugged our program.

First, you need to start the GDB environment by issuing the following command:

gdb

You should see the following command line appear:

(gdb)

Then, you need to 'tell' the debugger which file it is that we are debugging, do this by issuing the command:

(gdb) exec-file broken

Now, it is time to run our program by issuing the command:

(gdb) run

Notice that the program runs normally and again, gives us the answer inf. This is because the error we have is a computation error, and does not cause our program to crash. So, its time to set a Breakpoint in our program.

Setting a Breakpoint


Hint: A breakpoint is a point in the program at which the debugger will stop the code from executing. Then, you can choose to 'step' through the program line by line to 'see' exactly what the CPU is doing. This way, you can effectively watch the program execute and discover where the problem is!


Lets set a breakpoint within the program so that we can see what is going on. Lets start by setting a breakpoint at line 43. Do this with the following command:

(gdb) b 43

You could also set a breakpoint to a specific functions call, such as:

(gdb) b ComputeSeriesValue

However, for the purpose of this tutorial, use the line number. If you wish, you can play around with using the function instead, but it will cause you to diverge from this tutorial.

If the program outputs: No symbol table is loaded. Use the "file" command. Then use the following command:

(gdb) file broken

This will set the file so that you can add a breakpoint without error

Line 43 is the following:

double seriesValue = ComputeSeriesValue(x, n);

So the program will stop executing immediately before it executes the function ComputeSeriesValue(x, n).

Now, lets run the program with our command:

(gdb) run

Let's enter the values as x=2 and n=3, with an expected output value is 5. Here is a snapshot of what the program should output at this point:

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

The program has stopped at our breakpoint, line 43.

Stepping into a function

Next, we are going to step into the function ComputeSeriesValue(x, n)


Hint: 'Stepping' through a function allows us to go line by line, and see exactly what lines of code are being executed. This way we can manually diagnose the computation problem.


To start stepping, issue the following command

(gdb) step

Hint: A nice thing about the GDB Debugger is that it will save the last command you issued so that you don't need to type it over and over. This way you can keep stepping through the function by pressing <enter>. You do not need to retype 'step'
Hint: The GDB Debugger also has nice 'shortcuts'. For example, 'step' and 's' are the same command. So is 'breakpoint' and 'b'.


You should see the following outputted:

ComputeSeriesValue (x=2, n=3) at broken.cpp:17
17  double seriesValue=0.0;

At this point, the program has gone to line 17, and is at the first line of the function ComputeSeriesValue, which sets the double seriesValue to 0.0.

Now, we wish to use the 'next' function until we reach the function CompueFraction(). To do this, use the following commands:

(gdb) next
18  double xpow=1;
(gdb) <enter>
20  for (int k = 0; k <= n; k++) {
(gdb) <enter> 
21    seriesValue += xpow / ComputeFactorial(k) ;
(gdb) step
ComputeFactorial (number=0) at broken.cpp:7
7  int fact=0;

Using Backtrace

  • 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

Hint: Backtrace lets you see where you have been and what you have done. In case, you get lost or confused it is very useful.
Hint: A stack is a data structure in Computer Science which stores data much like a stack of cards. It is a Last On, First Off data structure. For more explanation of how a stack functions, see the wikipedia article.
http://en.wikipedia.org/wiki/Stack_(data_structure)


See the error?

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:

change the line

int fact = 0;

to

int fact =1;

and you're all set!

More Tutorial

For another tutorial which is more in depth, see: http://www.cs.cmu.edu/~gilpin/tutorial/