gdb 用法总结

以前写C++时,遇到需要调试的程序都是放在VS下进行debug,方便快捷。但是某些时候需要在Linux下进行debug,这时候显然不能用VS了,所以要祭出我们的法宝——gdb。
GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。
这里总结一下gdb的一些常用命令和简单使用方法,为以后调试Hotspot JVM以及Golang编译的程序做准备。(顺便吐槽一下Golang,都出到1.5版本了官方还不发布一个调试器,还得借助gdb。。)


放上一段简单的程序(klass.cpp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
template<typename T>
class Bean {
private:
T object;
string name;
public:
explicit Bean() {};
explicit Bean(T obj,string name);
T& getObject();
string& getName() {
return this->name;
}
inline bool operator==(const Bean<T>& bean);
};
template<typename T>
class BeanFactory {
private:
Bean<T> bean;
public:
virtual Bean<T> getBean() = 0;
virtual void debug(string s) = 0;
};
template<typename T>
class ApplicationContext : public BeanFactory<T> {
private:
Bean<T> bean;
public:
explicit ApplicationContext();
explicit ApplicationContext(Bean<T> bean);
ApplicationContext(const BeanFactory<T>& b) = delete;
virtual Bean<T> getBean();
virtual void debug(string s);
};
template<typename T>
Bean<T>::Bean(T obj,string name):object(obj),name(name){}
template<typename T>
T& Bean<T>::getObject() {
return this->object;
}
template<typename T>
inline bool Bean<T>::operator==(const Bean<T>& bean) {
return this->object == bean.getObject;
}
template<typename T>
ApplicationContext<T>::ApplicationContext() {
this->bean = nullptr;
}
template<typename T>
ApplicationContext<T>::ApplicationContext(Bean<T> bean) {
this->bean = bean;
}
template<typename T>
Bean<T> ApplicationContext<T>::getBean() {
return this->bean;
}
template<typename T>
void ApplicationContext<T>::debug(string s) {
cout<<"debug:"<<bean.getName()<<" + "<<s<<endl;
}
int main() {
Bean<string> bean("Scala","fucking");
BeanFactory<string> *context = new ApplicationContext<string>(bean);
context->debug("hahaha");
context->debug(bean.getObject());
delete context;
return 0;
}

编译:g++ klass.cpp -o klass -g -std=c++11
注意:需要调试的时候,最好在用g++编译的时候加上-g参数,以便将源代码信息编译到可执行文件中。当然玩逆向的话就看汇编代码吧→_→
进入gdb,然后使用file <filename>命令加载文件。
然后可使用r命令(run)执行可执行文件。若没下断点,则相当于正常执行:

1
2
debug:fucking + hahaha
debug:fucking + Scala

下面在main函数处下一个断点(Breakpoint):b main
反馈:Breakpoint 1 at 0x400e61: file klass.cpp, line 75.表示断点地址在0x400e61处
然后我们再次使用r命令执行,程序将停在断点处:

1
2
Breakpoint 1, main () at klass.cpp:75
75 int main() {

此界面中可以查看汇编代码、表达式、历史记录、内存使用、寄存器值、源代码(如果有的话)、堆栈信息和线程信息

gdb

接着使用s命令(step into)步入执行下面的语句,遇到函数调用则步入此函数。在生成Bean对象实例的时候步入Bean的构造函数:

Bean的构造函数信息

顺着下去,可以观察完整的过程。这里只是总结gdb的使用,就不再阐述了。

下面是关于断点的一些用法:

命令 解释 示例
b <行号> 在此行号处下断点 b 75
b <函数名称> 在此函数处下断点 b service
b *<函数名称> 在此函数的入口点(prolog)处下断点 b *main
b *<函数地址> 在此函数地址处下断点 b *0x400e61
d <编号> 删除指定编号的断点或删除所有断点 d

查看某个变量可以用p <变量名>命令,比如查看bean的详细信息:

1
2
3
4
5
>>> p bean
$1 = {
object = "Scala",
name = "fucking"
}

继续执行可以用c命令,若下面还有断点则中断到下一个断点。
若需要看本行代码对应的汇编代码可用display /i $pc命令,这样就能在Outputs一栏显示出当前汇编代码,比如:

1
2
3
4
5
6
debug:fucking + hahaha
Breakpoint 4, main () at klass.cpp:79
79 context->debug(bean.getObject());
1: x/i $pc
=> 0x400f8f <main()+313>: mov -0x48(%rbp),%rax

若想逐行汇编代码步入执行,可以用si命令。
下面总结一下step命令:

命令 解释
s step into(单步跟踪进入)
n step over(单步跟踪)
si 逐行汇编代码步入
ni 逐行汇编代码步出

还有一个非常有用的命令就是i <信息>,用于显示对应信息,简单总结一下常用的:

命令 解释
i args 显示当前栈帧上的变量信息
i address <name> 显示对应对象、方法或变量name的地址
i breakpoints(缩写i b 显示当前的所有断点
i variables 显示当前所有的静态变量和全局变量
i vtbl <object> 显示object对象的虚函数表(vtable)
i frame(缩写i f 显示栈帧信息
i registers(缩写i r 显示寄存器信息

比如查看context对象的虚函数表:

1
2
3
4
5
>>> i vtbl context
warning: RTTI symbol not found for class 'ApplicationContext<std::string>'
vtable for 'BeanFactory<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >' @ 0x401530 (subobject @ 0x603070):
[0]: 0x40139a <ApplicationContext<std::string>::getBean()>
[1]: 0x4013c8 <ApplicationContext<std::string>::debug(std::string)>

最后退出当然是命令q咯~

后面将用gdb调试HotSpot JVM以便更深入地了解JVM的运行原理。

文章目录