gdb调试

基本调试

编译时加上 -g,添加调试信息,同时需要关闭优化(O2,O3,…)

带参数调试

  • gdb --args exe_demo arg1 arg2
  • 进入gdb界面,然后设置参数:gdb exe_demo; set args arg1 arg2
  • 在run指令后面直接跟参数就行: gdb exe_demo; r arg1 arg2
  • 查看传递的参数: show args

断点操作

设置断点

  • b function:在函数function的入口处设置断点,C++中可以使用class::functionfunction(type,type)来指定函数名
  • b line_number:在当前活动源代码文件的line_number处设置断点
  • b filename:line_number:在源代码文件filename的line_number处设置断点,文件可以使用相对或者绝对路径
  • b filename:function:在文件filename中的function入口处设置断点。会在所有重载函数或同名静态函数上设置断点
  • b *address在程序运行的内存地址处停住。
  • 条件断点:break break-args if (condition )
    • break-args是可以指定断点位置的任何参数,如上所示
    • condition括号可选,condition表达式具有布尔值就行可以
    • 条件可以使用:和有效的C语句差不多:
      • 相等/不相等/逻辑:<, <=, ==, !=, >, >=, &&, ||
      • 按位和移位运算符:&,|, ^, >>, <<
      • 算术运算符:+, -,x, /,%
      • 自己的函数,只要它们被链接到程序中
      • 库函数,只要该库被链接到程序中——库函数还在哪种情况下可以使用,printf可以使用
  • 对正常断点设置条件以将他们转变为条件断点,使用cond【ition】,如cond 3 i==3,如果要删除条件但保持该断点,只要输入cond 3即可(这个3是断点的序号,可以用info b查看),在程序运行到断点处设置
  • 临时断点tb【reak】的命令设置和break相同,有效性只到第一次到达指定行为止
  • 如果是虚函数,如下打断点会出现多个:
    在这里插入图片描述

删除断点

  • d breakpoint_list:删除断点使用数值标识符,如delete 2删除第二个断点,delete 2 4删除第2,4个断点
  • d:删除所有断点
  • clear:清除GDB将执行的下一个指令处的断点——实测为清除执行所在行的断点
  • clear functionclear fliename:functionclear line_numberclear filename:line_number,这几个命令的工作方式和break对应的命令相似

禁用断点

  • enable/disable breakpoint-list:禁用断点:其中breakpoint_list是使用空格分隔的列表,如enable 15,disable可以简写为dis
  • enable/disable不带任何参数将启用/禁用现有所有断点
  • enable once breakpoint-list:在断点下次引起GDB暂停执行后被禁用

查看断点

info b【reakpoints】,各属性如下:

  • Num:断点的唯一标识符
  • type:指出该断点是断点,监视点还是捕获点
  • Disp(部署):指示断点下次引起GDB暂停程序执行后该断点上会发生什么事情,可能的部署有三种:
    • keep(保持):默认部署,下次断点到达后不改变断点
    • del(删除):下次到达后删除断点,使用tbreak创建的任何断点都是这样的断点
    • dis(禁用):下次到达断点时会禁用该断点,这是使用enable once命令设置的断点
  • Enb(启用状态):这个字段说明断点当前是启用还是禁用
  • Address(地址):内存中设置断点的位置
  • What(位置):显示断点所在文件和行号

执行

  • stepnext都可以单步调试,都有一个可选的参数,如next 3效果和三次next相同;next(单步越过函数)会执行下一行,step(单步进入函数)类似,但在函数调用时,step会进入函数,而next导致程序执行的暂停出现在下次调用函数时
  • c:程序执行到下一个断点处或程序结束;可以接受一个可选整数参数n,表示要忽略下面n个断点
  • fin:恢复程序执行,直到恰好在当前栈帧完成之后为止,即退出当前函数,进入调用他的函数
  • u:跳到某个位置执行,通常用在跳出循环或直接跳到某一行的情况;可以接受源代码中的位置,函数名等作为参数, 会执行到该处
    • u 17 跳到第17行
    • u function
    • u filename:line_number
    • u filename:function

检查和设置变量

  • p:打印,如果要打印指向结构体指针的各成员,使用p *tmp;p $1:打印已经打印过的变量;p/x var 以十六进制显示,其他常用的格式如c表示字符,s表示字符串,f表示浮点。在p的打印中,类似"\357\377\000\001"代表的是一个字符数组的内容,其中的每一个\元素都是一个八进制表示的ASCII码,如果希望看到更直观的字符形式,可以使用"x/s"命令来打印字符串,具体参考下面examine的用法
  • disp x:GDB在每次暂停时都会打印该变量x(在这个变量的作用域内打印,出了作用域就不打印了)
  • dis disp x:临时禁用某个显示项
  • enable disp x:重新启用
  • 完全删除显示的条目,使用 undisp【lay】 xxx
  • 查看变量类型:whatis X
  • ptype:查看类或结构的成员(不是查看值)
  • p/type 命令来查看一个对象的实际类型。例如,如果你有一个名为obj的父类对象,你可以使用以下命令来查看其实际类型:p/type obj,但是这种方法只能在程序编译时启用了RTTI的情况下使用。在g++中,你可以使用-frtti选项来启用RTTI。
  • info locals:得到当前栈帧(函数栈)中所有局部变量的值列表
  • examine(简写:x)查看内存:x/<n/f/u> <地址>
    • n是一个整数,表示显示内存的长度,从当前地址向后显示几个地址的内容;
    • f表示显示格式,可取值如下:
      • x:按十六进制显示变量
      • d:按十进制显示
      • o:按八进制显示
      • u:十进制格式显示无符号整型;t:二进制格式显示变量;a:十六进制格式显示变量;i:指令地址格式;c:按字符格式显示变量;f:浮点数格式显示变量
    • u表示从当前地址往后请求的字节数,如果不指定的话GDB默认是4个bytes。可以用以下字符代替:
      • b:单字节
      • h:双字节
      • w:四字节
      • g:八字节
        举例:
(gdb) x/16xw 0x7FFFFFFFE0F8
0x7fffffffe0f8: 0x11f71b00      0x5f731990      0x00000000      0x00000000
0x7fffffffe108: 0xf7de30b3      0x00007fff      0xf7ffc620      0x00007fff
0x7fffffffe118: 0xffffe1f8      0x00007fff      0x00000000      0x00000001
0x7fffffffe128: 0x55555198      0x00005555      0x55555210      0x00005555

(gdb) x/16xd 0x7FFFFFFFE0F8
0x7fffffffe0f8: 6877869162492271360     0
0x7fffffffe108: 140737351921843 140737354122784
0x7fffffffe118: 140737488347640 4294967296
0x7fffffffe128: 93824992235928  93824992236048
0x7fffffffe138: -18285573919020457      93824992235648
0x7fffffffe148: 140737488347632 0
0x7fffffffe158: 0       18285573285014103
0x7fffffffe168: 18268258704878167       0
  • p和disp允许指定可选的格式:

监视

值改变时就暂停执行,只能监视存在且在作用域内的变量,一旦变量不存在于调用栈的任何帧中,gdb会自动删除监视点

  • watch X:每当X值发生变化gdb都会暂停执行——会自动删除监视点,那怎么监视
  • 基于条件表达式监视:如 watch (z>28),表达式为true时gdb暂停执行
  • 查看和删除监视点同断点

上下移动调用栈

  • frame命令:当前执行的函数的栈帧编号为0,其父帧被编号为1,以此类推
    • up:进入上一个父帧(怎样查看上一个栈中的某些变量的信息?)
    • down:反方向
  • bt:会显示整个栈,即当前存在的所有帧的集合

显示源码

文件名可以忽略,表示当前文件

  • layout的用法:
    • layout:用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:
    • layout src:显示源代码窗口
    • layout asm:显示汇编窗口
    • layout regs:显示源代码/汇编和寄存器窗口
    • layout split:显示源代码和汇编窗口
    • layout next:显示下一个layout
    • layout prev:显示上一个layout
  • 窗口显示:
    • Ctrl + L:刷新窗口
    • Ctrl + x,再按1:单窗口模式,显示一个窗口
    • Ctrl + x,再按2:双窗口模式,显示两个窗口
    • Ctrl + x,再按a:回到传统模式,即退出layout,回到执行layout之前的调试窗口。
  • list的用法:
    • l【ist】 main.c:main:显示main.c文件中main函数附近的源码(具体是多附近?)
    • l【ist】 main.c:2,20:显示main.c中第2到20行的源码
    • 单独l显示当前代码,显示10行

其他调试信息

  • 重新开始调试:r

断点命令列表

使用commands命令设置命令列表,在断点执行到这里的时候会自动执行commands。格式如下:

  • 这样进行输出的时候会打印断点信息如cpp文件,行号等,使用silent避免打印这些,只输出commands中指定输出的内容
  • 如果命令列表中的最后一个命令(end之前)是continue,GDB在完成命令列表中的命令后继续自动执行程序
  • 怎样取消这个命令?重新定义一个空集合来取消
  • commands命令中可以使用if/else之类的逻辑控制,命令列表和条件中断合并使用威力巨大
commands breakpoint-number
silent
commands  // 用新行分隔的任何有效GDB命令
. . .
end
  • define命令创建宏:可以作为commands中的一条命令
    • 宏可以使用参数
    • 使用show user可以看到所有的宏列表
define CMD
/*command*/
end

检查和设置变量

其他命令

  • printf:类似C中的printf,格式控制输出
  • call:调用程序中的函数

检查动态数组

如果是字符串类型还是可以打印出来的,如果是其他类型直接用p x打印不了,一般形式为 *pointer@number_of_elememts;表示打印这个指针的前number_of_elememts个元素

int *a = new int[32];
a[3] = 10;
gdb: p *a@3
$1={0,0,10}  # 显示
  • GDB还允许适当的时候使用强制类型转换

GDB中的表达式

  • GDB变量
  • 程序中任何在作用域内的变量
  • 任何种类的字符串,数值或字符常量
  • 预处理宏
  • 条件,函数调用,类型强制转换和所用语言的运算符

在GDB中设置变量

有什么用还没探索出来

  • set args 命令设置命令行参数
  • info args:检查当前函数参数

GDB中的变量

  • 值历史:诸如 $1, $2
  • 方便变量——?也没看懂
    handle命令告诉gdb不要停止或发出警告信息

程序崩溃处理

core文件

  • 使用ulimit命令控制核心文件的创建
    • ulimit -a:可以查看core文件的生成开关和大小限制所有信息,输出内容中core file size,若为0表示不生成core文件
    • ulimit -c
      • ulimit c:可查看core文件的生成开关,如结果为0,则表示关闭了功能,不会生成core文件
      • ulimit -c n:n是核心文件的最大大小,以kb为单位;
      • ulimit -c unlimited 不限大小;
    • ulimit只在当前目录下生效, 经实测
  • core文件的生成路径:
    • 默认和可执行文件在同一目录
  • 没有生成core:
    • ulimit -c确认不为0
    • cat /proc/sys/kernel/core_pattern,如果内容为 |/usr/share/apport/apport %p %s %c %d %P %E,需要修改:echo "core-%e-%p-%t" > /proc/sys/kernel/core_pattern,注意,使用vim可能会提示没有权限。如果上面命令提示没权限修改,使用:echo "core-%e-%p-%t" | sudo tee /proc/sys/kernel/core_pattern,产生的文件名为core-命令名-pid-时间戳
      以下是参数列表:
      %p - insert pid into filename 添加pid(进程id)
      %u - insert current uid into filename 添加当前uid(用户id)
      %g - insert current gid into filename 添加当前gid(用户组id)
      %s - insert signal that caused the coredump into the filename 添加导致产生core的信号
      %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
      %h - insert hostname where the coredump happened into filename 添加主机名
      %e - insert coredumping executable name into filename 添加导致产生core的命令名
    • 还有一种方法(未实测):vim etc/default/apport文件,enabled 设置为0
  • 默认名字:core
  • frame n:移动到第n个栈帧中
  • 调试流程:
    • gdb 【应用程序名称】 core
    • 查看崩溃的堆栈:bt(backtrace)
    • frame x:进入x栈帧
  • 调试会话期间不退出GDB
    • vim窗口中直接make可以调用同目录下的makefile
    • vim中的autowrite

多线程调试

  • info threads:给出当前所有线程的信息,"*"表示当前位于哪个线程;
  • bt:查看堆栈
  • thread n,n为info threads中线程前面的编号,切换到n线程
  • break m thread n:当线程n到达源码第88行时停止
  • break xx.cpp:123 thread all:在所有线程中相应的行上设置断点
  • break m thread n if x==y:当钱程n到达源码第m行,并且变量x和y相等时停止执行
  • thread apply ID1 ID2 command:让线程ID1和ID2执行GDB命令command
  • thread apply all command:让所有被调试线程执行GDB命令command
  • set scheduler-locking off|on|step:使用step或continue命令调试当前被调试线程的时候其他线程也是同时执行的。
    • off:不锁定任何线程,也就是所有线程都执行,默认值
    • on:只有被调试线程执行
    • step:在单步的时候,除了next过一个函数之外,只有当前线程会被执行
  • 怎样知道其他线程在干什么?thread x切换到其他线程然后执行bt查看该线程的栈

不阻塞进程运行

  • 背景:一般业务会有心跳机制,使用gdb调试需要先关闭心跳功能,避免长时间断住进程导致复位
  • 两种方法可以做到gdb执行命令后快速退出:
    • gdb -batch -ex <command> -p <pid>。如gdb -batch -ex “call test_show()” -p 23456
    • 使用command命令

其他调试

  • 调试运行进程:
    1. 启动gdb时链接目标进程:gdb -p <PID>指定目标进程,就可以进入调试状态;c命令继续,不一定是r
    2. gdb中链接目标进程:(gdb) attach <PID>
    3. 断开链接:detach
  • 源文件在多个目录下,dir的用法:
    • dir:清除所有自定义源文件搜索路径信息
    • dir xxx:添加一个源文件路径,如果要添加多个,使用":";gdb调试时有时会出现找不到源代码文件只有行号的情况,用dir指定源码路径,如果指定到具体文件是不行的(亲测)
    • show dir:显示定义了的源文件的搜索路径
  • 文件焦点:GDB相关操作都是在具有焦点的文件上进行操作,这个文件默认是包含main函数的文件,当如下动作发生时,焦点会转移:
    • 向不同的源文件应用list命令——list swap就进入到swapper.c文件中(swap是swapper.c中的函数)
    • 进入位于不同的源代码文件中的代码
    • 当在不同的源代码文件中执行代码时GDB遇到断点——?
  • 调试宏:在gcc编译时加上-ggdb3就可以调试宏了
    • info macro:查看这个宏在哪些文件中被引用了,已经宏定义是什么样的
    • macro:查看宏展开的样子

.gdbinit文件

放在用户主目录下,GDB启动时自动加载

  • 参考:https://www.cnblogs.com/jiu0821/p/6244324.html,格式遵守:
define <command>        ## 调用该宏命令执行
<command>
end
document <command>      ## help command可以显示出来
<help text>
end
  • show user:可以查看自定义的命令

其他工具

  • strace
  • ltrace
  • pstack:linux下打印输出此进程的堆栈信息,可以输出所有线程的调用关系栈

调试总结

  • 某个线程停止(可能是死锁或者消息接收超时):使用gcore pid(调试进程的pid号)手动生成core文件,再使用pstack查看堆栈
  • 清理屏幕:shell clear
  • 问题总结:
    1. 如果想一直监视而不是每次进入堆栈就watch一次怎么操作?
    2. GDB不退出重新编译后run报段错误,是什么原因?
    3. 怎样用b一次打多个断点?
  • 简写汇总:
    break:b;delete:d;continue:c;finish:fin;until:u;print:p;backtrace:bt
    GDB调试参考博客:https://blog.csdn.net/gongmin856/article/details/79192259
  • Linux中共提供了三个函数用于打印调用堆栈:
/*
* 函数说明: 取得当前函数的调用堆栈
* 参数:buffer:用于存储函数地址的数组;size:buffer数组的长度
* 返回值:存储到数组中的函数个数
*/
int backtrace(void **buffer, int size);

/*
* 函数说明:将一组函数地址转换为字符串
* 参数: buffer: 经由backtrace得到的函数地址;size: buffer数组的长度
* 返回值: 函数在系统中对应用字符串
*/
char **backtrace_symbols(void *const *buffer, int size);

/*
* 函数说明:将一组函数地址转换为字符串
* 参数: buffer: 经由backtrace得到的函数地址;size: buffer数组的长度;fd: 输出结果文件描述符
*/
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS是一个开源的实时操作系统,它提供了一套用嵌入式系统的任务调度和管理机制。GDB(GNU Debugger)是一个功能强大的调试工具,可以用于调试C/C++程序。在使用FreeRTOS进行开发时,可以结合GDB进行调试。 要在FreeRTOS中使用GDB进行调试,需要进行以下几个步骤: 1. 配置编译器:首先,需要确保你的编译器支持GDB调试功能。常用的编译器如GCC和Keil都支持GDB调试。 2. 编译选项:在编译FreeRTOS应用程序时,需要添加一些编译选项以支持GDB调试。例如,在GCC中,可以使用"-g"选项来生成调试信息。 3. 连接器脚本:在链接应用程序时,需要使用连接器脚本来指定调试信息的位置。连接器脚本可以告诉GDB在哪里找到符号表和调试信息。 4. 启动GDB调试:在编译和链接完成后,可以使用GDB启动调试会话。可以通过命令行输入"gdb"命令来启动GDB,并使用"target remote"命令连接到目标设备。 5. 设置断点:在GDB中,可以使用"break"命令设置断点。可以设置函数断点、行号断点或地址断点等。 6. 执行调试:一旦设置好断点,可以使用GDB的调试命令来执行程序。可以使用"run"命令来运行程序,使用"step"命令逐行执行,使用"next"命令执行下一行,使用"continue"命令继续执行等。 7. 查看变量:在调试过程中,可以使用GDB的"print"命令来查看变量的值。可以使用"info locals"命令查看局部变量,使用"info global"命令查看全局变量等。 8. 结束调试:当调试完成后,可以使用GDB的"quit"命令退出调试会话。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值