使用 gdb 调试 C++ 程序

查看调用栈

  可以使用backtrace命令查看当前函数的调用栈。下面用一个 C++ 程序演示一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
void bar()
{
std::cout << "I'm bar!" << std::endl;
}
void foo()
{
std::cout << "I'm foo!" << std::endl;
bar();
}
int main()
{
std::cout << "Hello, World!" << std::endl;
foo();
return 0;
}

  编译这个程序,并使用 gdb 调试。我们在代码第 5 行设置断点(在bar()函数里面),运行程序之后,就可以使用backtrace命令查看bar()函数的调用栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ g++ -std=c++11 -g -o hello hello.cpp
$ gdb ./hello -tui
(gdb) break 5
Breakpoint 1 at 0x40084a: file hello.cpp, line 5.
(gdb) run
Starting program: /home/ubuntu/hello
Hello, World!
I'm foo!
Breakpoint 1, bar () at hello.cpp:5
(gdb) backtrace
#0 bar () at hello.cpp:5
#1 0x000000000040088e in foo () at hello.cpp:11
#2 0x00000000004008b6 in main () at hello.cpp:17

调试正在运行的程序

  gdb 也可以用来调试正在运行的进程,例如下面的 C++ 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <chrono>
#include <thread>
void sleep()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
int i = 1000;
while (true)
{
std::cout << "Hello, World!" << std::endl;
sleep();
i -= 1;
}
return 0;
}

  编译并运行这个程序:

1
2
3
$ g++ -std=c++11 -g -o sleep sleep.cpp
$ ./sleep &
[1] 5507

  可以看到进程 ID 是5507,可以使用 gdb 调试这个进程:

1
2
3
4
5
$ sudo gdb ./sleep 5507
Attaching to program: /home/ubuntu/sleep, process 5507
0x00007f227c95d740 in __nanosleep_nocancel ()
at ../sysdeps/unix/syscall-template.S:84
(gdb)

  使用backtrace命令查看调用栈:

1
2
3
4
5
6
7
(gdb) backtrace
#0 0x00007f227c95d740 in __nanosleep_nocancel ()
at ../sysdeps/unix/syscall-template.S:84
#1 0x0000000000400d6e in std::this_thread::sleep_for<long, std::ratio<1l, 1l> > (
__rtime=...) at /usr/include/c++/5/thread:292
#2 0x0000000000400943 in sleep () at sleep.cpp:7
#3 0x000000000040098a in main () at sleep.cpp:17

  在调试的时候,这个进程会暂停执行,当调试结束的时候,进程会恢复执行。

参考资料