以前写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() {
|
此界面中可以查看汇编代码、表达式、历史记录、内存使用、寄存器值、源代码(如果有的话)、堆栈信息和线程信息
接着使用s
命令(step into)步入执行下面的语句,遇到函数调用则步入此函数。在生成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的运行原理。