GDB 常用命令学习

GDB 常用命令学习

GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。在这里并不是要和Windows做个什么比较,所谓“寸有所长,尺有所短”,图形化工具还是有不如命令行的地方。gdb可以调试C、C++、D、Go、python、pascal、assemble(ANSI 汇编标准)等等编程语言。课件gdb调试工具的强大。在此总结了gdb调试C语言常用的一些命令。

  1. 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
  2. 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)。
  3. 当程序被停住时,可以检查此时你的程序中所发生的事。
  4. 动态的改变你程序的执行环境。

从上面看来,GDB和一般的调试工具没有什么两样,基本上也是完成这些功能,不过在细节上,你会发现GDB这个调试工具的强大,大家可能比较习惯了图形化的调试工具,但有时候,命令行的调试工具却有着图形化工具不同的功能。

GDB基本命令

GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。GDB中的命令固然很多,但我们只需掌握其中常用的命令,就大致可以完成日常的基本的程序调试工作。

RE 字符意义与范例意义与范例
123

启动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) db 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查看变量值
执行到下一个断点设置处
使用ccontinue命令
清除指定的断点
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 调试基本命令的源码。时间原因。未项以上作者授权申请授权。故不公开,不发表,不转载。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值