Linux下gdb调试工具的使用
gdb是GNU开源组织发布的一个强大的Linux下的程序调试工具。
gdb主要完成四个方面的功能:(1)、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序;(2)、可让被调试的程序在你所指定的调试的断点处停住(断点可以是条件表达式);(3)、当程序被停住时,可以检查此时你的程序中所发生的事;(4)、动态的改变你程序的执行环境。
要想运行准备调试的程序,可使用run命令,在它后面可以跟随发给该程序的任何参数,包括标准输入和标准输出说明符(<;和>;)和shell通配符(*、?、[、])在内。如果你使用不带参数的run命令,gdb就再次使用你给予前一条run命令的参数。利用set args命令就可以修改发送给程序的参数,而使用show args命令就可以查看其缺省参数的列表。
一般来说,gdb主要调试的是c/c++的程序,也可以调试Ada、Objective-C、Pascal及其它语言。要调试c/c++的程序,首先在编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的-g(-ggdb)参数可以做到这一点。如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。
启动gdb的方法:(1)、gdb <program> :program就是执行文件,一般在当前目录下;(2)、gdb <program> core:用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件;(3)、gdb <program> <PID> :如果是一个服务程序,你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试它。
gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令,在Linux下,可以敲击两次Tab键来补齐命令的全称,如果有重复的,那么gdb会把相关的全例出来。
gdb调试中常用的命令:
(1)、l或list命令,查看源码;
(2)、r或run命令,运行程序,会在有断点的地方停住,如果是有输入参数的,则为run argv[1] argv[2];
(3)、break n,在源程序第n行处设置断点;break fun,在函数fun()入口处设置断点;break filename:function ,在源文件filename的function函数的入口处设置断点;breakfilename:linenum ,在源文件filename的第linenum行处设置断点;break if<condition>, 在条件成立时停住;filename可以是相对路径,即你不仅可以在当前原文件中设置断点,也可以在该程序中用到的其它目录中的原文件中设置断点;
(4)、info命令,查看指定命令信息,如info break会显示所有设置的断点信息;
(5)、n或next命令,单条语句执行,如果有函数调用,它不会进入该函数;
(6)、c或continue命令,继续运行程序;
(7)、p或print 变量名var,打印变量var的值。print是gdb的一个功能很强的命令,利用它可以显示被调试的语言中任何有效的表达式。表达式除了包含你程序中的变量外,还可以包含以下内容:A、对程序中函数的调用;B、数据结构和其它复杂对象;
(8)、bt或backtrace命令,查看函数调用栈的所有信息,当程序执行异常时,可通过此命令查看程序的调用过程;
(9)、finish命令,运行程序,直到当前函数完成返回;
(10)、q或quit命令,退出gdb;
(11)、set命令,可指定运行时变量名的值;
(12)、watch <expr>,为表达式(变量)expr设置一个观察点,一旦表达式的值有变化时,马上停住程序;rwatch <expr>,当表达式(变量)expr被读时,停住程序;awatch <expr>,当表达式(变量)的值被读或被写时,停住程序;
(13)、clear命令,清除所有的已定义的停止点,如断点等;clear <filename:function>,清除所有设置在函数上的停止点;clear <filename:linenum>,清除所有设置在指定行上的停止点;
(14)、step命令,单步跟踪,如果有函数调用,它会进入该函数,如果想退出该函数返回到它的调用函数中,可以使用finish命令;
(15)、jump命令,可以修改程序的执行顺序,可以让程序执行随意跳跃;
(16)、whatis命令,可以显示某个变量的类型;
要想了解gdb的更详细的用法,可以从 http://www.gnu.org/software/gdb/documentation/下载gdb manuals。
以下以一个测试程序为例,说明其中的一些命令的具体用法,此例程的组织结构为:gdbtest为总目录,下面有include/demo/src三个目录,(1)、include/add/add.h, include/subtract/subtract.h;(2)、src/add/add.cpp,src/subtract/subtract.cpp;(3)、demo/test/test.cpp。
每个文件的详细内容为:
add.h:
#ifndef _ADD_H_ #define _ADD_H_ int CalAdd(int a, int b); float CalAdd(float a, float b, float c); #endif //_ADD_H_
subtract.h:
#ifndef _SUBTRACT_H_ #define _SUBTRACT_H_ int CalSubtract(int a, int b); float CalSubtract(float a, float b); #endif //_SUBTRACT_H_
add.cpp:
#include "../../include/add/add.h" int CalAdd(int a, int b) { int tmp1 = a / 2; if (b % 2 == 1) { b = b * 2 + 3; } else { b = b + 5; } int tmp2 = -1; tmp1 = a + tmp1 + b + tmp2; return tmp1; } float CalAdd(float a, float b, float c) { float tmp1 = a - b; for (int i = 0; i < 10; i ++) { float tmp2 = i * 0.5; tmp1 += tmp2; } float tmp2 = a + c - b - tmp1; return tmp2; }
subtract.cpp:
#include "../../include/subtract/subtract.h" int CalSubtract(int a, int b) { int tmp1 = 5; while (tmp1) { int tmp2 = 100; a += tmp2 / 2; -- tmp1; } int tmp3 = a - b; return tmp3; } float CalSubtract(float a, float b) { float tmp1 = -111; for (int i = 0; i < 100; i ++) { float tmp2 = -199; a -= b; a += tmp2; } float tmp3 = a - b; return tmp3; }
test.cpp:
#include <iostream> #include <stdlib.h> #include "../../include/add/add.h" #include "../../include/subtract/subtract.h" using namespace std; int main(int argc, char* argv[]) { cout<<"integer test, input integer:"<<endl; int a = 5, b = 10; if (argc > 1) a = atoi(argv[1]); if (argc > 2) b = atoi(argv[2]); int ret = 0; cout<<"start add:"<<endl; ret = CalAdd(a, b); cout<<"integer add :"<<ret<<endl; cout<<"start subtract:"<<endl; ret = CalSubtract(b, a); cout<<"integer subtract:"<<ret<<endl; cout<<"ok!!"<<endl; return 0; }
打开终端,定位为到/demo/test/目录下,依次输入:
g++ -c -g test.cpp ../../src/add/add.cpp ../../src/subtract/subtract.cpp g++ -o test test.o add.o subtract.o
会在test目录下生成test.o, add.o, subtract.o和test执行文件。
下面开始调试test:
(1)、输入gdb test,会显示:
(2)、先用l或list命令,查看当前原文件源码,每次会显示10行,每次显示行数也可以设置,如果想继续查看后面的代码,可以直接按Enter键,表示继续执行上次输入的命令:
(3)、为了不让程序一次性执行完,可以先设置一个断点,后面根据需要可以随时再增加断点,输入break 22,在当前文件test.cpp的第22行设置断点,输入info break查看此断点信息:
(4)、使用run命令开始正式调试test,输入run 10 20:
(5)、使用n或next命令,单步调试,接着输入step命令进入到CalAdd函数内部:
(6)、多设置几个断点:break ../../src/add/add.cpp:13,break ../../subtract/subtract.cpp:5, break 26, 输入info break查看:
(7)、输入finish命令,从CalAdd函数内部退出,使用whatis命令,查看变量ret类型,输入print ret查看变量值:
(8)、使用continue命令,执行到下一个断点设置处:
(9)、使用clear命令,清除指定的断点:
参考文献:
1. http://www.gnu.org/software/gdb/
2. http://blog.csdn.net/haoel/article/category/9197