C# · 12月 20, 2021

A GDB Tutorial with Examples–转

A GDB Tutorial with Examples

By Manasij Mukherjee

A good debugger is one of the most important tools in a programmer’s toolkit. On a UNIX or Linux system,GDB (the GNU debugger) is a powerful and popular debugging tool; it lets you do whatever you like with your program running under GDB.

Should you read this?

You should… if you can relate to two or more of the following:

You have a general idea of programming with C or C++.You put a lot of cout or printf statements in the code if something goes wrong.You have used a debugger with an IDE,and are curious about how the command line works.You’ve just moved to a Unix-like operating system and would like to know about the toolchain better.A crash course on compiling with gcc (or g++)

 is the de facto compiler in Linux or any other *nix system. It also has Windows ports but on Windows,you’ll probably find the  ‘easier’.

Suppose you have a file called main.cpp containing your c++ code. You should compile it with the following command:

<pre class="example">g++ main.cpp -o main

While this will work fine and produce an executable file called main,you also need to put a -g flag to tell the compiler that you may want to debug your program later.

So the final command turns into:

<pre class="example">g++ main.cpp -g -Wall -Werror -o main

(If you’re wondering what -Wall and -Werror are,you may find  a useful read.)

Don’t worry if it looks cumbersome,you’ll get used to it! (If you’ve got multiple source files you should use a good build system like  or.)

The Basics of GDB

Provided you’ve compiled your program with the debugging symbols enabled,you’re ready to start debugging. Any time there is text you should replace,I’ve put it in .

Starting GDB

To start GDB,in the terminal,

<pre class="example">gdb

For the above example with a program named main,the command becomes

<pre class="example">gdb mainSetting Breakpoints

You’ll probably want you program to stop at some point so that you can review the condition of your program. The line at which you want the program to temporarily stop is called the breakpoint.

<pre class="example">break Running your program

To run your program,the command is,as you guessed,

<pre class="example">runLooking at the code

When the program is stopped,you can do a number of important things,but most importantly you need to see which part of the code you’ve stopped. The command for this purpose is “list”. It shows you the neighbouring 10 lines of code.

Next and Step

Just starting and stopping isn’t much of a control. GDB also lets you to run the program line-by-line by the commands ‘next’ and ‘step’. There is a little difference between the two,though. Next keeps the control strictly in the current scope whereas step follows the execution through function calls.

Look at this example carefully;

Suppose you have a line in the code like


If you use the next command,the line (and the function,provided there aren’t breakpoints in it) gets executed and the control advances to the next line,readinput(),where you can perhaps examine ‘value’ to get an idea of how display() worked.

But if you use the step command,you get to follow what display() does directly,and the control advances to the first line of display(),wherever it is.

Examining your Variables

When you want to find the misbehaving portion of your program,it often helps to examine local variables to see if anything unexpected has occurred. To examine a variable,just use

<pre class="example">print

Note: You can also modify variables’ values by

<pre class="example">set =

You can modify variables to see if an issue is resolved if the variable has another value or to force the program to follow a particular path to see if the reason for a bug was due to a variable having the wrong value.

Setting Watchpoints

Setting watchpoints is like asking the debugger to provide you with a running commentary of any changes that happen to the variables. Whenever a change occurs,the program pauses and provides you with the details of the change.

The command to set a simple watchpoint (a write watchpoint,i.e you are notified when the value is written) is

<pre class="example">watch

Here’s some example output when GDB pauses due to a change in :

<pre class="example">Continuing.Hardware watchpoint 2: variable

Old value = 0
New value = 1
0x08048754 at main.cpp:31
31 variable=isalpha(ch)

Note: You can only set watchpoints for a variable when it is in scope. So,to watch something within another function or a inner block,first set a breakpoint inside that scope and then when the program pauses there,set the watchpoint.


To stop your program,when it is paused,use kill and to quit GDB itself,use quit.

An Example Debugging Session

The given code computes the factorial of a number erroneously. The goal of the debugging session is to pinpoint the reason of the error.

</td> <code class="cpp keyword bold">namespace <code class="cpp plain">std; <code class="cpp plain">factorial(<code class="cpp color1 bold">int <code class="cpp plain">n); <code class="cpp plain">main()<code class="cpp color1 bold">int n;<code class="cpp color1 bold">long <code class="cpp plain">val=factorial(n);<code class="cpp plain">cout<<val;<code class="cpp plain">cin.get();<code class="cpp keyword bold">return <code class="cpp plain">0; <code class="cpp plain">factorial(<code class="cpp color1 bold">int <code class="cpp plain">n)<code class="cpp color1 bold">long <code class="cpp plain">result(1);<code class="cpp keyword bold">while<code class="cpp plain">(n--)<code class="cpp plain">{<code class="cpp plain">result*=n;<code class="cpp plain">}<code class="cpp keyword bold">return <code class="cpp plain">result;</td></tr></table>Into the Debugger

Now follow the commands and the outputs carefully,especially the watchpoints. What I'm doing is basically:

Setting a breakpoint just in the line of the function callStepping into the function from that lineSetting watchpoints for both the result of the calculation and the input number as it changes.Finally,analyzing the results from the watchpoints to find problematic behaviour<pre class="example">1. $ g++ main.cpp -g -Wall -o main2. $ gdb main3. GNU gdb (GDB) Fedora (7.3-41.fc15)4. Copyright (C) 2011 Free Software Foundation,Inc.5. This GDB was configured as "i686-redhat-linux-gnu".6. For bug reporting instructions,please see:7. ...8. Reading symbols from /home/manasij7479/Documents/main...done.9. (gdb) break 1110. Breakpoint 1 at 0x80485f9: file main.cpp,line 11.11. (gdb) run12. Starting program: /home/manasij7479/Documents/main13. 314. 15. Breakpoint 1,main () at main.cpp:1116. 11 long val=factorial(n);17. (gdb) step18. factorial (n=3) at main.cpp:1919. 19 long result(1);20. (gdb) list21. 14 return 0;22. 15 }23. 1624. 17 long factorial(int n)25. 18 {26. 19 long result(1);27. 20 while(n--)28. 21 {29. 22 result*=n;30. 23 }31. (gdb) watch n32. Hardware watchpoint 2: n33. (gdb) watch result34. Hardware watchpoint 3: result35. (gdb) continue36. Continuing.37. Hardware watchpoint 3: result38. 39. Old value = 040. New value = 1

Notice that result starts from 0 and is initialized to 1.

<pre class="example">41. factorial (n=3) at main.cpp:2042. 20 while(n--)43. (gdb)

Notice that I didn't put in a command,I just hit . It re-executes the last command.

<pre class="example">44. Continuing.45. Hardware watchpoint 2: n46. 47. Old value = 348. New value = 2

Notice that n gets is immediately decremented from 3 to 2.

<pre class="example">49. 0x08048654 in factorial (n=2) at main.cpp:2050. 20 while(n--)51. (gdb)52. Continuing.53. Hardware watchpoint 3: result54. 55. Old value = 156. New value = 2

Now result becomes 2 (by multiplying result's earlier value with n's value). We've found the first bug! result is supposed to be evaluated by multiplying 3 * 2 * 1 but here the multiplication starts from 2. To correct it,we have to change the loop a bit,but before that,lets see if the rest of the calculation is correct.

<pre class="example">57. factorial (n=2) at main.cpp:2058. 20 while(n--)59. (gdb)60. Continuing.61. Hardware watchpoint 2: n62. 63. Old value = 264. New value = 1

n gets decremented from 2 to 1. Result doesn't change since n is 1.

<pre class="example">65. 0x08048654 in factorial (n=1) at main.cpp:2066. 20 while(n--)67. (gdb)68. Continuing.69. Hardware watchpoint 2: n70. 71. Old value = 172. New value = 0

n gets decremented from 1 to 0.

<pre class="example">73. 0x08048654 in factorial (n=0) at main.cpp:2074. 20 while(n--)75. (gdb)76. Continuing.77. Hardware watchpoint 3: result78. 79. Old value = 280. New value = 0

Now result becomes 0 (by multiplying result's earlier value with n's value,0). Another bug! How can result hold the value of the factorial when it is multiplied by 0? The loop must be stopped before n reaches 0.

<pre class="example">81. factorial (n=0) at main.cpp:2082. 20 while(n--)83. (gdb)84. Continuing.85. Hardware watchpoint 2: n86. 87. Old value = 088. New value = -189. 0x08048654 in factorial (n=-1) at main.cpp:2090. 20 while(n--)91. (gdb)92. Continuing.

Now n becomes -1 and the loop isn't permitted to run anymore because n-- returns 0,and the function returns result's current value 0. Let's see what happens when the function exits.

<pre class="example">93. 94. Watchpoint 2 deleted because the program has left the block in95. which its expression is valid.96. 97. Watchpoint 3 deleted because the program has left the block in98. which its expression is valid.

This is what happens to a watchpoint when the variable goes out of scope.

<pre class="example">99. 0x08048605 in main () at main.cpp:11100. 11 long val=factorial(n);101. (gdb) print val102. $1 = 1293357044

print val shows a garbage value because gdb points to a line before it is executed,not after.

<pre class="example">103. (gdb) next104. 12 cout<<val;105. (gdb) continue106. Continuing.107. 0[Inferior 1 (process 2499) exited normally]108. (gdb) quit

Here's what the fix should look like:

</td>0) <code class="cpp comments">//doesn't let n reach 0<code class="cpp plain">result*=n;<code class="cpp plain">n--;        <code class="cpp comments">//decrements only after the evaluation</td></tr></table>GDB in Conclusion

You have now seen enough to try GBD out on your own. Some important topics have not been touched upon here for the sake of simplicity,such as dealing with  or using tools like  to find memory leaks.

Remember that GDB comes built in with an excellent help system. Just type help in the (gdb) prompt and you will be presented with options of what you Could need help with. For details about a specific command,use the Syntax

<pre class="example">help

Another important point to note is the use of shortcuts (like 'q' for 'quit'). GDB lets you use shortcuts for commands when it is not ambigIoUs.

After learning about GDB,you do not have to panic the next time your program goes crazy. You have an excellent weapon in your arsenal Now.