gdb调试技巧
线程切换控制
set scheduler-locking on|off|step
- on:启用线程切换
- off:禁止线程切换
- step:单步调试时禁止线程切换
处理信号
handle SIGCONT nostop noprint
core文件调试
当进程崩溃时,通过系统dump出来的core文件来排查程序崩溃原因,如果不确定是否开启,可以通过ulimit -c
命令查看,如果输出为0,则表示未开启,可以通过ulimit -c unlimited
命令开启。
dump的core文件放置目录的格式,由/proc4/sys/kernel/core_pattern设置
echo "/corefile/core-%p-%t" > /proc/sys/kernel/core_pattern
通过gdb 进程文件路径 core文件路径
来调试,然后输入bt打印调用堆栈,查看程序崩溃时的代码位置。
gdb调试
gdb链接正在运行的进程以进行调试
gdb -p `pgrep 进程名`
单步调试
- step
就是单步执行,遇到子函数就进入并且继续单步执行;在其他调试其中相当于step-into命令,作用是移动到下一个可执行的代码行。如果当前行是一个函数调用,则调试器进入函数并停止在函数体的第一行。step可以帮助初步揭开代码位置的谜团,例如:函数调用和函数本身可能在不同的文件中。 - next
是在单步执行时,在函数内遇到子函数时不会进入子函数内单步执行,而是将子函数整个执行完再停止,也就是把子函数整个作为一步。在其他调试器中相当于step-over,作用是在同一个调用栈层中移动到下一个可执行的代码行。调试器不会进入函数体。如果当前行是函数的最后一行,则,next将进入下一个栈层,并在调用函数的下一行停止。 - finish
就是但单步执行到子函数内时,用step out就可以执行完子函数余下部分,并返回到上一层函数。在其他调试器中相当于step-out,作用是在栈中前进到到下一层,并在调用函数的下一行停止。 - ni/si
汇编级别的next/step
断点
-
break <断点表达式>设置断点,支持
- 类名::成员函数 b class_a::function
- 源码文件:行数 b file_1:12
- 函数名 b function
- 指定地址 b *0x00000000004004ee
- 源码文件:函数 b file_1:function
-
info break 查看断点
-
delete <断点号>删除指定断点
-
clear 删除所有断点
-
watch <变量名> 监视变量,变量改变时程序暂停
条件断点
break 断点 if 条件
条件支持c语法,可以调用程序中的函数,例如
- break func if a == 10
- break func if strcmp(a, b) == 0
- condition 断点号 [条件表达式]: 用来修改一个断点的条件,可增删改
打印变量p、x
支持c++语法打印变量,如
- p/fu 实例名.成员
- p/fu *(指针)
- p/fu $rsp 打印寄存器或gdb定义的变量
- x/nfu 打印内存
n、f、u格式见后表格
代号 | 意义 | 取值范围 |
---|---|---|
n | 打印的数据单元数 | 整数 |
f | 打印的数据单元长度 | b(byte), h(halfword), w(word), g(giant, 8 bytes) |
u | 打印的格式 | o(octal), x(hex), d(decimal), u(unsigned decimal),t(binary), f(float), a(address), i(instruction), c(char) and s(string) |
上表信息可以通过 help x 获取
打印字符串时,字符串过长会省略显示,可以通过以下命令解决
(gdb) show print elements
Limit on string chars or array elements to print is 200.
(gdb) set print elements 0
(gdb) show print elements
Limit on string chars or array elements to print is unlimited.
变量的格式特征
- 如果value为指针,打印出来的是0x开头
- 如果是对象(结构体或类),打印出来是以{}包裹起来的
有时候程序中存在同名变量,打印出来的值很可能不是你想要的,这时可以首先通过gdb打印变量的格式来判断下是否打的是其他同名变量
一个程序可能链接多个动态库,这些库中可能使用了同名变量,这时打印出来的值很可能出乎你意料,解决办法,利用工具修改库中的同名符号(未验证)
变量自动打印
- display 变量名 程序暂停时自动打印该变量
- undisplay 变量编号 取消变量的自动打印,通过display可以查看编号
命令脚本
把需要的序列化的命令操作直接一行行写到一个文件,然后直接
gdb 程序 --command=命令文件
环境变量
- show paths: 展示当前程序系统PATH路径
- show environment [varname]:展示当前程序指定或全部环境变量的值
- set environment varname[=varlue]: 设置环境变量
- environment可以缩写为env
多线程调试
- thread aplly all bt:查看所有线程的调用栈
- info threads [id]:查看所有(或指定)线程的信息,id是指序号,不是指线程id,多个id用空格隔开
查看栈信息
- backtrace(bt): 打印函数调用栈
- frame <n>: 打印当前栈帧信息或跳转到第n号调用栈帧
- up/down <n>:向上/下移动一个或n个栈帧
- info frame: 这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等
- info args: 打印出当前函数的参数名及其值。
- info locals: 打印出当前函数中所有局部变量及其值。
- info catch: 打印出当前的函数中的异常处理信息。
查看CPU寄存器
- info all-reg:查看所有CPU寄存器
- info float:查看浮点寄存器
- info registers:查看CPU通用和特殊功能寄存器
修改寄存器或内存值
- set $eax=0
- set {unsigned int}0x8048481=50
查看源程序
- list <linenum>: 显示程序第linenum行的周围的源程序。
- list <function>: 显示函数名为function的函数的源程序。
- list: 显示当前行后面的源程序。
- list -: 显示当前行前面的源程序。
- list <first>, <last>: 显示从first行到last行之间的源代码。
- list , <last>: 显示从当前行到last行之间的源代码。
- list +: 往后显示源代码。
指定源文件的路径
某些时候,用-g编译过后的执行程序中只是包括了源文件的名字,没有路径名。GDB提供了可以让你指定源文件的路径的命令,以便GDB进行搜索。
directory <dirname … >: 加一个源文件路径到当前路径的前面。如果你要指定多个路径,UNIX下你可以使用“:”,Windows下你可以使用“;”。
directory: 清除所有的自定义的源文件搜索路径信息。
show directories: 显示定义了的源文件搜索路径。
源代码的内存
你可以使用info line命令来查看源代码在内存中的地址。info line后面可以跟“行号”,“函数名”,“文件名:行号”,“文件名:函数名”,这个命令会打印出所指定的源码在运行时的内存地址,如:
info line tst.c:func