【转载】GDB的简单教程以及例子(A GDB Tutorial with Examples)

GDB的简单教程以及例子

这部分内容来自于A GDB Tutorial with Examples,我在这里用Markdown重写一下, 然后用有道机翻一下,方便后续参考。

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.

带示例的GDB教程

原文
来自 Manasij Mukherjee
一个好的调试器是一个程序员最重要的工具之一的工具包。在UNIX或Linux系统上,GDB (GNU调试器)是一个强大而流行的调试工具;它可以让你做任何你喜欢和你的程序在GDB下运行。

你应该读这个吗?

你应该……如果你能说明以下两个或两个以上的问题:

  • 你对C或C++编程有一个大致的了解。
  • 如果出现问题,你会在代码中添加很多cout或printf语句。
  • 你已经在IDE中使用了调试器,并且想知道命令行是如何工作的。
  • 你刚刚迁移到类unix操作系统,并且希望更好地了解工具链。

A crash course on compiling with gcc (or g++)

翻译
Gcc 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 debugger in Visual Studio ‘easier’.

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

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:

g++ main.cpp -g -Wall -Werror -o main

(If you’re wondering what -Wall and -Werror are, you may find this page on GCC 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 make or Scons.)

关于使用gcc(或g++)编译的速成班

原文
Gcc是Linux或任何其他*nix系统中事实上的编译器。它也有Windows端口,但在Windows上,你可能会发现Visual Studio中的调试器“更容易”。

假设你有一个名为main.cpp的文件,其中包含你的c++代码。你应该用下面的命令编译它:

g++ main.cpp -o main

虽然这将很好地工作并生成一个名为main的可执行文件,但你还需要添加-g标志,以告诉编译器你可能希望稍后调试你的程序。

所以最后的命令变成:

g++ main.cpp -g -Wall -Werror -o main

(如果你想知道-Wall和-Werror是什么,你会发现这个关于GCC的页面很有用。 )

如果它看起来很笨重,不要担心,你会习惯的!(如果你有多个源文件,你应该使用一个好的构建系统,比如 makeScons.)


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 <angle brackets>.

Starting GDB

To start GDB, in the terminal,

gdb <executable name>

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

gdb main

Setting 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.

break <source code line number>

Running your program

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

run

Looking 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

value=display();
readinput();

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

print <var name to print>

Note: You can also modify variables’ values by

set <var> = <value>

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

watch <var>

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

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.

Quit

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

GDB的基础知识

原文
如果你已经编译了启用了调试符号的程序,就可以开始调试了。只要有需要替换的文本,我就把它放在<尖括号>中。

启动GDB

要启动GDB,在终端中,

gdb <executable name>

对于上面的示例中名为main的程序,命令变为

gdb main

设置断点

你可能希望你的程序在某个点停止,以便你可以检查程序的状况。你希望程序暂时停止的那一行称为断点。

break <source code line number>

运行程序

如你所料,要运行程序,命令如下:

run

查看代码

当程序停止时,你可以做许多重要的事情,但最重要的是,你需要查看你停止了代码的哪一部分。用于此目的的命令是"list"。它显示了相邻的10行代码。

next 和 step

只是开始和停止并不是一个很大的控制。GDB还允许你通过命令’next’和’step’逐行运行程序。不过,这两者之间有一点区别。next将控制严格地保持在当前范围内,而step则通过函数调用跟踪执行。

仔细看看这个例子;

假设代码中有这样一行

value=display();
readinput();

如果使用next命令,则执行该行(以及函数,前提是其中没有断点),并将控制推进到下一行readinput(),在那里你可以检查’value’以了解display()的工作方式。

但是如果使用step命令,则可以直接执行display()的操作,并且控件将前进到display()的第一行,无论它在哪里。

检查变量

当你想要查找程序中行为不正常的部分时,检查局部变量以查看是否发生了意外情况通常会有所帮助。要检查变量,只需使用

print <var name to print>

注意:你也可以修改变量的值

set <var> = <value>

如果变量具有另一个值,你可以修改变量以查看问题是否得到解决,或者强制程序遵循特定的路径以查看错误的原因是否由于变量具有错误的值。

设置监测点

设置观察点就像要求调试器为你提供对变量发生的任何更改的运行注释。无论何时发生更改,程序都会暂停并提供更改的详细信息。

设置简单观察点(写观察点,即当值被写入时通知你)的命令是

watch <var>

以下是由于<var>更改而导致GDB暂停的一些示例输出:

Continuing.
Hardware watchpoint 2: variable

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

注意:只有当变量在作用域中时,才能为它设置观察点。因此,要观察另一个函数或内部块中的内容,首先在该范围内设置一个断点,然后当程序在那里暂停时,设置观察点。

退出

要停止程序,在暂停时使用kill;要退出GDB,使用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.

#include<iostream>

using namespace std;

long factorial(int n);

int main()
{
	int n(0);
	cin>>n;
	long val=factorial(n);
	cout<<val;
	cin.get();
	return 0;
}

long factorial(int n)
{
	long result(1);
	while(n--)
	{
		result*=n;
	}
	return result;
}

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 call
  • Stepping into the function from that line
  • Setting 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
1.      $ g++ main.cpp -g -Wall -o main
2.      $ gdb main
3.      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.      <http://www.gnu.org/software/gdb/bugs/>...
8.      Reading symbols from /home/manasij7479/Documents/main...done.
9.      (gdb) break 11
10.     Breakpoint 1 at 0x80485f9: file main.cpp, line 11.
11.     (gdb) run
12.     Starting program: /home/manasij7479/Documents/main
13.     3
14.     
15.     Breakpoint 1, main () at main.cpp:11
16.     11        long val=factorial(n);
17.     (gdb) step
18.     factorial (n=3) at main.cpp:19
19.     19        long result(1);
20.     (gdb) list
21.     14        return 0;
22.     15      }
23.     16
24.     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 n
32.     Hardware watchpoint 2: n
33.     (gdb) watch result
34.     Hardware watchpoint 3: result
35.     (gdb) continue
36.     Continuing.
37.     Hardware watchpoint 3: result
38.     
39.     Old value = 0
40.     New value = 1

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

41.     factorial (n=3) at main.cpp:20
42.     20        while(n--)
43.     (gdb)

Notice that I didn’t put in a command, I just hit <return>. It re-executes the last command.

44.     Continuing.
45.     Hardware watchpoint 2: n
46.     
47.     Old value = 3
48.     New value = 2

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

49.     0x08048654 in factorial (n=2) at main.cpp:20
50.     20        while(n--)
51.     (gdb)
52.     Continuing.
53.     Hardware watchpoint 3: result
54.     
55.     Old value = 1
56.     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.

57.     factorial (n=2) at main.cpp:20
58.     20        while(n--)
59.     (gdb)
60.     Continuing.
61.     Hardware watchpoint 2: n
62.     
63.     Old value = 2
64.     New value = 1

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

65.     0x08048654 in factorial (n=1) at main.cpp:20
66.     20        while(n--)
67.     (gdb)
68.     Continuing.
69.     Hardware watchpoint 2: n
70.     
71.     Old value = 1
72.     New value = 0

n gets decremented from 1 to 0.

73.     0x08048654 in factorial (n=0) at main.cpp:20
74.     20        while(n--)
75.     (gdb)
76.     Continuing.
77.     Hardware watchpoint 3: result
78.     
79.     Old value = 2
80.     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.

81.     factorial (n=0) at main.cpp:20
82.     20        while(n--)
83.     (gdb)
84.     Continuing.
85.     Hardware watchpoint 2: n
86.     
87.     Old value = 0
88.     New value = -1
89.     0x08048654 in factorial (n=-1) at main.cpp:20
90.     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.

93.     
94.     Watchpoint 2 deleted because the program has left the block in
95.     which its expression is valid.
96.     
97.     Watchpoint 3 deleted because the program has left the block in
98.     which its expression is valid.

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

99.     0x08048605 in main () at main.cpp:11
100.    11        long val=factorial(n);
101.    (gdb) print val
102.    $1 = 1293357044

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

103.    (gdb) next
104.    12        cout<<val;
105.    (gdb) continue
106.    Continuing.
107.    0[Inferior 1 (process 2499) exited normally]
108.    (gdb) quit

Here’s what the fix should look like:

while(n>0) //doesn't let n reach 0
{
	result*=n;
	n--;		//decrements only after the evaluation
}

一个调试会话示例

原文
给定的代码错误地计算了一个数字的阶乘。调试会话的目标是查明错误的原因。

#include<iostream>

using namespace std;

long factorial(int n);

int main()
{
	int n(0);
	cin>>n;
	long val=factorial(n);
	cout<<val;
	cin.get();
	return 0;
}

long factorial(int n)
{
	long result(1);
	while(n--)
	{
		result*=n;
	}
	return result;
}

进入调试器

现在仔细查看命令和输出,尤其是观察点。我所做的基本上是:

  • 在函数调用的行中设置断点
  • 从那一行进入函数
  • 为计算结果和输入数字的变化设置观察点。
  • 最后,从观察点分析结果,发现问题行为
1.      $ g++ main.cpp -g -Wall -o main
2.      $ gdb main
3.      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.      <http://www.gnu.org/software/gdb/bugs/>...
8.      Reading symbols from /home/manasij7479/Documents/main...done.
9.      (gdb) break 11
10.     Breakpoint 1 at 0x80485f9: file main.cpp, line 11.
11.     (gdb) run
12.     Starting program: /home/manasij7479/Documents/main
13.     3
14.     
15.     Breakpoint 1, main () at main.cpp:11
16.     11        long val=factorial(n);
17.     (gdb) step
18.     factorial (n=3) at main.cpp:19
19.     19        long result(1);
20.     (gdb) list
21.     14        return 0;
22.     15      }
23.     16
24.     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 n
32.     Hardware watchpoint 2: n
33.     (gdb) watch result
34.     Hardware watchpoint 3: result
35.     (gdb) continue
36.     Continuing.
37.     Hardware watchpoint 3: result
38.     
39.     Old value = 0
40.     New value = 1

注意,结果从0开始,初始化为1。

41.     factorial (n=3) at main.cpp:20
42.     20        while(n--)
43.     (gdb)

注意,我没有输入命令,只是按了Enter。它重新执行最后一个命令。

44.     Continuing.
45.     Hardware watchpoint 2: n
46.     
47.     Old value = 3
48.     New value = 2

注意,n从3减到2。

49.     0x08048654 in factorial (n=2) at main.cpp:20
50.     20        while(n--)
51.     (gdb)
52.     Continuing.
53.     Hardware watchpoint 3: result
54.     
55.     Old value = 1
56.     New value = 2

现在结果变成了2(通过将结果之前的值与n的值相乘)。我们找到了第一个bug!结果应该是通过3 * 2 * 1来计算,但这里的乘法是从2开始的。为了纠正它,我们必须稍微改变一下循环,但在此之前,让我们看看其余的计算是否正确。

57.     factorial (n=2) at main.cpp:20
58.     20        while(n--)
59.     (gdb)
60.     Continuing.
61.     Hardware watchpoint 2: n
62.     
63.     Old value = 2
64.     New value = 1

n从2减到1。结果不会因为n = 1而改变。

65.     0x08048654 in factorial (n=1) at main.cpp:20
66.     20        while(n--)
67.     (gdb)
68.     Continuing.
69.     Hardware watchpoint 2: n
70.     
71.     Old value = 1
72.     New value = 0

N从1减到0。

73.     0x08048654 in factorial (n=0) at main.cpp:20
74.     20        while(n--)
75.     (gdb)
76.     Continuing.
77.     Hardware watchpoint 3: result
78.     
79.     Old value = 2
80.     New value = 0

现在结果变成了0(通过将结果之前的值乘以n的值,0)。当阶乘乘以0时,结果如何保存阶乘的值?循环必须在n达到0之前停止。

81.     factorial (n=0) at main.cpp:20
82.     20        while(n--)
83.     (gdb)
84.     Continuing.
85.     Hardware watchpoint 2: n
86.     
87.     Old value = 0
88.     New value = -1
89.     0x08048654 in factorial (n=-1) at main.cpp:20
90.     20        while(n--)
91.     (gdb)
92.     Continuing.

现在n变成-1,循环不再被允许运行,因为

n--

返回0,函数返回结果的当前值0。让我们看看当函数退出时会发生什么。

93.     
94.     Watchpoint 2 deleted because the program has left the block in
95.     which its expression is valid.
96.     
97.     Watchpoint 3 deleted because the program has left the block in
98.     which its expression is valid.

这就是当变量超出作用域时,观察点所发生的情况。

99.     0x08048605 in main () at main.cpp:11
100.    11        long val=factorial(n);
101.    (gdb) print val
102.    $1 = 1293357044

print val显示一个垃圾值,因为GDB在执行之前指向一行,而不是在执行之后。

103.    (gdb) next
104.    12        cout<<val;
105.    (gdb) continue
106.    Continuing.
107.    0[Inferior 1 (process 2499) exited normally]
108.    (gdb) quit

下面是修复应该是什么样的:

while(n>0) //doesn't let n reach 0
{
	result*=n;
	n--;		//decrements only after the evaluation
}

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 segmentation faults and other kinds of crashes or using tools like Valgrind 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

help <command>

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.

Read more…

Advanced GDB Debugging

Learn about Valgrind, a memory leak detector and memory error hunter

More information about finding and debugging segmentation faults (segfaults)

Debugging with Visual Studio

GDB总结

原文
你现在已经看到了足够的东西,可以自己尝试GBD了。为了简单起见,这里没有涉及一些重要的主题,例如处理分段错误和其他类型的崩溃,或者使用Valgrind之类的工具来查找内存泄漏。

请记住,GDB内置了一个出色的帮助系统。只需在(gdb)提示符中键入help,就会出现需要帮助的选项。要了解具体命令的详细信息,请使用语法

help <command>

另一个需要注意的要点是使用快捷键(比如“q”代表“quit”)。GDB允许你在命令没有歧义的情况下使用快捷方式。
在学习了GDB之后,下次程序出现异常时,你就不必惊慌失措了。你的军火库里现在有了一件极好的武器。

阅读更多

高级GDB调试

了解Valgrind,一个内存泄漏检测器和内存错误搜索器

关于查找和调试分段错误(segfault)的更多信息

用Visual Studio调试

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值