GDB 常用命令学习
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。在这里并不是要和Windows做个什么比较,所谓“寸有所长,尺有所短”,图形化工具还是有不如命令行的地方。gdb可以调试C、C++、D、Go、python、pascal、assemble(ANSI 汇编标准)等等编程语言。课件gdb调试工具的强大。在此总结了gdb调试C语言常用的一些命令。
- 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
- 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)。
- 当程序被停住时,可以检查此时你的程序中所发生的事。
- 动态的改变你程序的执行环境。
从上面看来,GDB和一般的调试工具没有什么两样,基本上也是完成这些功能,不过在细节上,你会发现GDB这个调试工具的强大,大家可能比较习惯了图形化的调试工具,但有时候,命令行的调试工具却有着图形化工具不同的功能。
GDB基本命令
GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。GDB中的命令固然很多,但我们只需掌握其中常用的命令,就大致可以完成日常的基本的程序调试工作。
RE 字符 | 意义与范例 | 意义与范例 |
---|---|---|
1 | 2 | 3 |
启动gdb:gdb
载入待调试文件,(gdb)file testb
载入文件;启动gdb并调试可执行文件:gdb test
。
file <文件名>
* 加载被调试的可执行程序文件。因为一般都在被调试程序所在目录下执行GDB,因而文本名不需要带路径。(gdb) file gdb-sample
。
r run的简写
* 运行被调试的程序。如果此前没有下过断点,则执行完整个程序;如果有断点,则程序暂停在第一个可用断点处。(gdb) r
。
c Continue的简写
* 继续执行被调试程序,直至下一个断点或程序结束。 (gdb) c
。
b <行号>,b <函数名称>,b <函数名称>,b <代码地址>d [编号]b: Breakpoint的简写,
* 设置断点。两可以使用“行号”“函数名称”“执行地址”等方式指定断点位置。
其中在函数名称前面加“*”符号表示将断点设置在“由编译器生成的prolog代码处”。如果不了解汇编,可以不予理会此用法。d: Delete breakpoint的简写,删除指定编号的某个断点,或删除所有断点。断点编号从1开始递增。(gdb) b 8,(gdb) b main,(gdb) b *main,(gdb) b *0x804835c,(gdb) d
;b test if a == 10
设置条件断点。
s, n;
* s: 执行一行源程序代码,如果此行代码中有函数调用,则进入该函数;n: 执行一行源程序代码,此行代码中的函数调用也一并执行。s 相当于其它调试器中的“Step Into (单步跟踪进入)”;n 相当于其它调试器中的“Step Over (单步跟踪)”。这两个命令必须在有源代码调试信息的情况下才可以使用(GCC编译时使用“-g”参数)。(gdb) s,(gdb) n
。
si, ni
* si命令类似于s命令,ni命令类似于n命令。所不同的是,这两个命令(si/ni)所针对的是汇编指令,而s/n针对的是源代码。(gdb) si,(gdb) ni
。
p print<变量名称>显示或修改变量;
* Print的简写,显示指定变量(临时变量或全局变量)的值。 (gdb) p i,(gdb) p nGlobalVar。p file::variable 查看文件作用于变量;p function::variable 查看函数作用域变量;修改变量值print x=4修改变量值。
display …,undisplay <编号>;
* display,设置程序中断后欲显示的数据及其格式。
例如,如果希望每次程序中断后可以看到即将被执行的下一条汇编指令,可以使用命令“display /i
pc”其中
pc 代表当前汇编指令,/i 表示以十六进行显示。当需要关心汇编代码时,此命令相当有用。undispaly,取消先前的display设置,编号从1开始递增。(gdb) display /i $pc,(gdb) undisplay 1。
i info;Info的简写,用于显示各类信息,详情请查阅“help i”。 (gdb) i r;
q quit;Quit的简写,退出GDB调试环境。(gdb) q。
help [命令名称];
* GDB帮助命令,提供对GDB名种命令的解释说明。如果指定了“命令名称”参数,则显示该命令的详细说明;如果没有指定参数,则分类显示所有GDB命令,供用户进一步浏览和查询。 (gdb) help display。
print varName=n或者set var varName=n;
* print x=4修改变量值:将x赋值为4并打印x的值set var width=47 将width的值设置为47;
* 补充:查看变量的类型whatis varName 查看变量varName的类型。
l、list查看源程序代码::
* list <line-num>
显示指定行前后的代码;list <func>
显示函数代码;list
显示当前行前后的代码;list +
从当前行往后显示代码;list -
从当前行往前显示代码;list <first>,<last>
显示first行与last行之间的代码;list ,<last>
显示当前行到last行之间的代码。
display、undisplay/delete自动显示的变量值的相关命令。
* 已经设置后的变量,当每执行一个调试命令后会自动显示在调试界面中。display <expr>
设置要自动显示值的变量;display /<fmt> <expr>
设置要自动显示的变量及数据的显示格式;undisplay display <dnum>
删除一个自动显示变量;delete display <dnum>
删除一个自动显示变量;undisplay/delete display <dnum1~dnum5>
删除一个范围内的自动变量;disable/enable display <dnum>
禁用/启用一个自动显示变量;info display
查看设置的自动显示变量。
info 查看信息;
* info threads 查看线程信息;info breakpoints 查看断点信息;info locals 显示局部变量;info args 显示函数变量;info registers 显示寄存器数据;
watch [expr]、rwatch [expr]、awatch [expr]观察
* 为表达式(变量)expr设置一个观察点,一旦表达式的值有变化时,马上停住程序;rwatch <expr>
,当表达式(变量)expr被读时,停住程序;awatch <expr>
,当表达式(变量)的值被读或被写时,停住程序;
clear清除命令
* 清除所有的已定义的停止点,如断点等;clear <filename:function>
,清除所有设置在函数上的停止点;clear <filename:linenum>
,清除所有设置在指定行上的停止点;
up和down命令:
* 遍历函数堆栈,up是将调用栈上移一个函数调用,down是将调用栈下移一个函数调用;
delete删除断点,clear删除断点:
* delete可删除单个断点,也可删除一个断点的集合,这个集合用连续的断点号来描述。例如:delete 5、delete 1-10; clear location。clear 删除所选定的环境中所有的断点,clear location location描述具体的断点。clear list_insert //删除函数的所有断点;clear list.c:list_delet //删除文件:函数的所有断点;clear 12 //删除行号的所有断点;clear list.c:12 //删除文件:行号的所有断点。clear 删除断点是基于行的,不是把所有的断点都删除。
r(run) 启动程序运行
n(nex)t 单步执行(不到函数内部)
s(step) 单步执行,跟踪到函数内部
f(finish) 继续执行,直到当前函数结束
c(continue) 继续执行,直到下一个断点
j(jump) 9 直接跳到指定行
where 查看调用堆栈(bt 或者 info s)
thread 2 切换线程
GDB单例的示例
示例的代码为一个文件,具体代码如下:
#include <stdio.h>
int nGlobalVar = 0;
int tempFunction(int a, int b)
{
printf("tempFunction is called, a = %d, b = %d /n", a, b);
return (a + b);
}
int main()
{
int n;
n = 1;
n++;
n--;
nGlobalVar += 100;
nGlobalVar -= 12;
printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar);
n = tempFunction(1, 2);
printf("n = %d", n);
return 0;
}
编译可调试文档:
* 请将此代码复制出来并保存到文件 gdb-sample.c 中,然后切换到此文件所在目录,用GCC编译之:gcc gdb-sample.c -o gdb-sample -g
或者 g++ -g gdb-sample -o gdb-sample
。
在上面的命令行中,使用 -o 参数指定了编译生成的可执行文件名为 gdb-sample,使用参数 -g 表示将源代码信息编译到可执行文件中。如果不使用参数 -g,会给后面的GDB调试造成不便。当然,如果我们没有程序的源代码,自然也无从使用 -g 参数,调试/跟踪时也只能是汇编代码级别的调试/跟踪。下面“gdb”命令启动GDB,将首先显示GDB说明,不管它:
file载入文件:
* 下面使用“file”命令载入被调试程序 gdb-sample(这里的 gdb-sample 即前面 GCC 编译输出的可执行文件):(gdb) file gdb-sample
。显示Reading symbols from gdb-sample...done.
。
命令执行(Run):
* 输入(gdb) r
。显示:Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample
……Program exited normally.
如果是有输入参数的,则为run argv[1] argv[2]
断点:b <行号>,b <函数名称>,b <函数名称>,b <代码地址>,d [编号];
* “b”命令在 main 函数开头设置一个断点:(gdb) b main
。显示:Breakpoint 1 at 0x804835c: file gdb-sample.c, line 19.
;
在第26行、tempFunction函数开头各设置一个断点(分别使用命令“b 26”,“b tempFunction”):
(gdb) b 26
显示:Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26.
。
(gdb) b tempFunction
显示:Breakpoint 3 at 0x804832e: file gdb-sample.c, line 12.
++输入,r执行;再一次执行“c”命令(Continue);“s”命令(Step)执行下一行代码++
用“p”命令(Print)查看变量或者修改变量:
* 输入(gdb) p n
显示:$1 = 1
。
s, n
* (s 相当于其它调试器中的“Step Into (单步跟踪进入)”;n 相当于其它调试器中的“Step Over (单步跟踪)”。):在函数断点处输入s(小写),单步执行进入函数;输入n ,单步执行,不进入函数内部。
继续执行c,
* Continue的简写,继续执行被调试程序,直至下一个断点或程序结束。
q退出GDB ;
* Quit的简写,退出GDB调试环境。 (gdb) q。
Linux下gdb多文档
原文参考:Linux下gdb调试工具的使用
结构:
....../ |---include/ | |---add/ | | |--add.h | |---subtract/ | | |--subtract.h |---src/ | |---add/ | | |--add.cpp | |---subtract/ | | |--subtract.cpp |---demo/ | |---test/ | | |--test.cpp
关键命令:
进入到/demo/test/test.cpp文件夹
编译:
g++ -c -g test.cpp ../../src/add/add.cpp ../../src/subtract/subtract.cpp
g++ -o test test.o add.o subtract.o
断点:
break 22
break ../../src/add/add.cpp:13,break ../../subtract/subtract.cpp:5, break 26
,
查看断点信息:
info break
n单步调试
使用n
或next命令
若断点在函数内部,从函数内部退出,finish
输入finish
命令
显示某个变量的类型whatis命令
输入print ret查看变量值
执行到下一个断点设置处
使用c
和continue
命令
清除指定的断点
clear ../../src/add/add.cpp:13
重新调试程序 run
输入run 30 40
,之前设的断点继续有效
GDB 多线程调试基本命令 (转载)
原文: GDB 调试基本命令
info threads
显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。
前面有*的是当前调试的线程。
thread ID
切换当前调试的线程为指定ID的线程。
thread apply ID1 ID2 command
让一个或者多个线程执行GDB命令command。
thread apply all command
让所有被调试线程执行GDB命令command。
set scheduler-locking off|on|step
估计是实际使用过多线程调试的人都可以发现,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。
off 不锁定任何线程,也就是所有线程都执行,这是默认值。
on 只有当前被调试程序会执行。
step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。
GDB多线程调试的实现思路:
比较主要的代码是thread.c,前面介绍的几个命令等都是在其中实现。 thread_list这个表存储了当前可调试的所有线程的信息。
函数add_thread_silent或者add_thread(不同版本GDB不同)用来向thread_list列表增加一个线程的信息。
函数delete_thread用来向thread_list列表删除一个线程的信息。
上面提到的这2个函数会被有线程支持的target调用,用来增加和删除线程,不同的OS对线程的实现差异很大,这么实现比较好的保证了GDB多线程调试支持的扩展性。
函数info_threads_command是被命令info threads调用的,就是显示thread_list列表的信息。
函数thread_command是被命令thread调用,切换当前线程最终调用的函数是switch_to_thread,这个函数会先将当前调试线程变量inferior_ptid,然后对寄存器和frame缓冲进行刷新。
函数thread_apply_command被命令thread apply调用,这个函数的实际实现其实很简单,就是先切换当前线为指定线程,然后调用函数execute_command调用指定函数。
比较特别的是set scheduler-locking没有实现在thread.c中,而是实现在控制被调试程序执行的文件infrun.c中。
对其的设置会保存到变量scheduler_mode中,而实际使用这个变量的函数只有用来令被调试程序执行的函数resume。在默认情况下, 传递给target_resume的变量是resume_ptid,默认情况下其的值为RESUME_ALL,也就是告诉target程序执行的时候所有 被调试线程都要被执行。而当scheduler_mode设置为只让当前线程执行的时候,resume_ptid将被设置为inferior_ptid, 这就告诉target只有inferior_ptid的线程会被执行。
最后特别介绍一下Linux下多线程的支持,基本的调试功能在linux-nat.c中,这里有对Linux轻量级别进程本地调试的支持。但是其 在调试多线程程序的时候,还需要对pthread调试的支持,这个功能实现在linux-thread-db.c中。对pthread的调试要通过调用 libthread_db库来支持。
这里有一个单独的target”multi-thread”,这个target有2点很特别:
第一,一般target的装载是在调用相关to_open函数的时候调用push_target进行装载。而这个target则不同,在其初始化 的时候,就注册了函数thread_db_new_objfile到库文件attach事件中。这样当GDB为调试程序的动态加载库时候attach库文 件的时候,就会调用这个函数thread_db_new_objfile。这样当GDB装载libpthread库的时候,最终会装载 target”multi-thread”。
第二,这个target并没有像大部分target那样自己实现了全部调试功能,其配合linux-nat.c的代码的功能,这里有一个target多层结构的设计,要介绍的比较多,就不详细介绍了。
遇见的一个多线程调试和解决:
基本问题是在一个Linux环境中,调试多线程程序不正常,info threads看不到多线程的信息。
我先用命令maintenance print target-stack看了一下target的装载情况,发现target”multi-thread”没有被装载,用GDB对GDB进行调试,发现在 函数check_for_thread_db在调用libthread_db中的函数td_ta_new的时候,返回了TD_NOLIBTHREAD,所 以没有装载target”multi-thread”。
在时候我就怀疑是不是libpthread有问题,于是检查了一下发现了问题,这个环境中的libpthread是被strip过的,我想可能就 是以为这个影响了td_ta_new对libpthread符号信息的获取。当我换了一个没有strip过的libpthread的时候,问题果然解决 了。
解决办法是拷贝了一个.debug版本的libpthread到lib目录中,问题解决了。
写作参考
1.GDB十分钟教程
2.gdb 跟踪调试命令整理
3.用GDB调试程序(一)~(七)
4. GDB 调试基本命令
5.GDB: The GNU Project Debugger
说明:参考了以上参考文件特别是GDB十分钟教程和 GDB 调试基本命令的源码。时间原因。未项以上作者授权申请授权。故不公开,不发表,不转载。