Scheduled Maintenance: We are aware of an issue with Google, AOL, and Yahoo services as email providers which are blocking new registrations. We are trying to fix the issue and we have several internal and external support tickets in process to resolve the issue. Please see: viewtopic.php?t=158230

 

 

 

Using the gdb debugger for c/c++ code.

Share your HowTo, Documentation, Tips and Tricks. Not for support questions!.
Post Reply
Message
Author
User avatar
edbarx
Posts: 5401
Joined: 2007-07-18 06:19
Location: 35° 50 N, 14 º 35 E
Been thanked: 2 times

Using the gdb debugger for c/c++ code.

#1 Post by edbarx »

This howto is intended for anyone who never succeeded to use gdb successfully.
gdb, being a CLI application, may not appeal to the eye, but my experience in using it, has proven that it is a very good debugger.

Step 1:
Compile your source code using the g++/gcc -g option. This tells the compiler to include debugging information in the compiled executable.

Step2:
While at the directory containing the freshly compiled executable, invoke gdb as follows:

Code: Select all

gdb executable-name
Step 3:
Set breakpoints as follows:
For the main source file, ie where main() is implemented, use:

Code: Select all

b 51
The 51 is the line number where the breakpoint is placed. In your case, it will be a different line number.

For other files, say parser.cpp, use:

Code: Select all

b parser.cpp:125
Your project will most likely have differently named files. parser.cpp should be accompanied by a parser.h file: I don't know whether opting to only use .cpp files works.

Step 4:
Run the program by typing 'r' and by pressing Enter.

The actual debugging.
  1. To step over a line of code use: 'n'.
    Stepping a line a code forces the debugger to fully execute that line of code. That means, if there is a function call, say:

    Code: Select all

    length = getMinimalLength(a, b, c);
    the getMinimalLength function will be executed until it returns and the return value will be saved in length.
  2. To step into a line of code, for instance, when a function is called, use 's'.
    If you are unsure what is happening in your code, this is a best tool. Stepping into a line of code forces the debugger to step into any invoked functions. In our example, that means, the debugger will start debugging the code comprising the getMinimalLength function.
  3. To list a few lines of code use: 'list'.
    If you are unware which line of code the debugger is at, use list. This will list about ten lines of code showing the current line of code in the middle.
  4. To watch variable values, use 'p variable-name'.
    Debugging without being able to watch variable values is next to useless. This feature is central to any serious debugging. Let us assume, variable length is in the current scope and that we are debugging the line just after where getMinimalLength(..) function is called. To do this is as easy as:

    Code: Select all

    p length

An Example:
For this example we will consider the following c++ program:

Code: Select all

#include <iostream>
using namespace std;

long double fact (int n)
{
  int i;
  long double r = 1.0;
  
  for (i = 1; i <= n; i++)
    r = r * i;
  
  return r;
}  

int main()
{
  long double arr [100];
  int i, j, z;
  
  cout << "Start getting factorials from: ";
  cin >> z;
  
  cout << "Enter the number of iterations: ";
  cin >> j;
  
  if (j < 0 || j > 100)
  {
    cout << "Iteration count must lie within 0 and 100 including both ends\n";
    return 0;
  }  
  
  for (i = 0; i < j; i++)
  {  
    arr[i] = fact(z);
    z++;
  }  
  
  for (i = 0; i < j; i++)
    cout << arr[i] << '\n';
  return 0;
}
We will now compile the code. Assume the code is saved in a file named test07.cpp. Open a terminal, cd to the directory where you save test07.cpp, and invoke the g++ compiler in this way:

Code: Select all

g++ -g test07.cpp -o test07
Take special notice of the -g directive. This tells the compiler that we want debugging information in our executable file.

Now, invoke the gdb debugger. The latter needs to know what executable you want to debug - pass its name as a parameter.

Code: Select all

gdb test07
gdb will present its interface. This should look like the following:

Code: Select all

$ gdb test07
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/edbarx/projects/test05/test07...done.
(gdb) 
Suppose we want to debug the fact function. Line 7 in our code is a good place for a breakpoint because this will precede the portion of the function where the actual calculation takes place.

To insert a breakpoint we use the b command as follows:

Code: Select all

(gdb) b 7
In the case our line of code we are interested in is in another file in the project, we would use the command as follows:

Code: Select all

(gdb) b test07.cpp:7
The debugger will tell us that the breakpoint has been accepted.

Code: Select all

Breakpoint 1 at 0x4009f3: file test07.cpp, line 7.
(gdb)
Now, we run the code:

Code: Select all

(gdb) r
Starting program: /path-to-our-program/test07 
Start getting factorials from: 
Follow, the program's prompts until the debugger halts the program at the breakpoint. Our program will display information similar to this.

Code: Select all

(gdb) r
Starting program: /path-to-our-program/test07 
Start getting factorials from: 1
Enter the number of iterations: 2

Breakpoint 1, fact (n=1) at test07.cpp:7
7	  long double r = 1.0;
(gdb) 
As you can see, the debugger duly stopped at the breakpoint. Typing n for next, moves the execution by one line as follows.

Code: Select all

(gdb) n
9	  for (i = 1; i <= n; i++)
Another n will move the point of execution by another line.

Code: Select all

(gdb) n
10	    r = r * i;
To see what values are in our variables we use the p command as follows:

Code: Select all

(gdb) p i
$1 = 1
(gdb) p n
$2 = 1
(gdb) p r
$3 = 1
(gdb) 
This is the first iteration where we are calculating the factorial of 1 which is 1. The for loop should exit immediately after this step. Let us see:

Code: Select all

(gdb) n
9	  for (i = 1; i <= n; i++)
(gdb) n
12	  return r;
(gdb) 
As you can confirm, the function is about to return the value r which should be 1. Let us check, if this is the case:

Code: Select all

(gdb) p r
$4 = 1
... and we have r = 1 as expected.

To list the code where return r is situated, we use the list command:

Code: Select all

(gdb) list
7	  long double r = 1.0;
8	  
9	  for (i = 1; i <= n; i++)
10	    r = r * i;
11	  
12	  return r;
13	}  
14	
15	int main()
16	{
(gdb) 
Stepping further, ie using n some more times, the debugger will exit from the fact function returning to the main function as follows:

Code: Select all

(gdb) n
13	}  
(gdb) n
main () at test07.cpp:35
35	    z++;
(gdb) n
32	  for (i = 0; i < j; i++)
(gdb) 
Now, we will examine the second fact call which should return the factorial of 2. Note that 2! = 2. Please, note that the use of s, forces the debugger to move more slowly showing all steps even those behing the scenes. In this case, it is telling us that the returned value from fact is saved in the arr array. As you can see, sometimes, s, behaves like n. This depens on the complexity of the line at the point of execution. Function calls force the debugger to branch into them when s is used.

Code: Select all

(gdb) s
34	    arr[i] = fact(z);
(gdb) s

Breakpoint 1, fact (n=2) at test07.cpp:7
7	  long double r = 1.0;
(gdb) s
9	  for (i = 1; i <= n; i++)
(gdb) s
10	    r = r * i;
(gdb) s
9	  for (i = 1; i <= n; i++)
(gdb) s
10	    r = r * i;
(gdb) p i
$5 = 2
(gdb) s
9	  for (i = 1; i <= n; i++)
(gdb) s
12	  return r;
(gdb) p r
$6 = 2
(gdb) 
The result from the function is 2 as expected. To run the program until it exits or it meets another breakpoint use c (continue).

Code: Select all

(gdb) c
Continuing.
1
2
[Inferior 1 (process 5331) exited normally]
Needless to state the obvious, the process number need not agree with mine.

To quit the debugger, use the q command as follows:

Code: Select all

(gdb) q

Enjoy. :)
Debian == { > 30, 000 packages }; Debian != systemd
The worst infection of all, is a false sense of security!
It is hard to get away from CLI tools.

Post Reply