GDB 常用命令总结

时间:2022-12-10 08:23:47

GDB用过一段时间了,确实很强大,总结一下平时经常用到的命令。

针对 Linux 下 C/C++ 程序。编译的时候请加上 -g 选项(默认-g2, -g3可以调试宏),关掉优化或者改成 -O0, 保证行号一致和局部变量不被优化掉。

断点、观察点

b func          # b : break
b file:line
b +offset   #在当前断点行号,后 offset 偏移行再加个断点
b -offset   #与上面相反,前 offset 偏移行加个断点
b *addr     #在地址 addr 上打断点, 返汇编函数可以看代码段,指令的地址,无源码或者行对不上时有用
b file:line thread all
b [where] if [condition] # 条件断点,满足condition时停住
i b    # info breakpoints
del n  # delete 断点n
dis n  # disable 断点n
en n   # enable 断点n
watch expr    # 观察点,expr 变化时断住, expr当前上下文可见
     #eg: watch *(unsigned int *)0xbffff400==2
rwatch expr   # 观察点,expr 被读时断住
awatch expr   # 观察点,expr 被读或写时断住
info watchpoints  # 查看观察点

运行

c          # continue 恢复程序运行
r          # run 重新运行
fin        # finish 执行完毕此函数,退出函数
ret val    # 强制函数return val值,不执行完毕
l          # list source code
until      # 走出当前循环
until loc  # 往下走,直到loc位置
s          # step 单步,遇到函数进入
n          # next 单步,遇到函数不进入
set disassemble-next-line auto    ## 汇编级单步时显示行号
si              # step instruction, 汇编级单步,遇函数进入
ni              # next instruction, 汇编级单步,遇函数不进
p my_var=5        # 改变变量的值
set var my_var=5  #
ret val           # return ,强制结束函数 ,并且返回 val
jump <line>       # 不要跨栈帧,一个函数内可以用用
call func         # 强制调用函数
set $epc = 0x10 #修改寄存器
set *(unsigned int*)0x8048a54=6a #修改内存的值

调用栈

bt         # backtrace
t a a bt   # thread apply all backtrace, 查看所有线程调用栈
f n        # frame 切换栈帧, 栈 高地址 -> 低地址
up n       # 上移n个栈帧函数,缺省1
down n     # 下移n个栈帧函数,缺省1
i r        # info registers, 可以看函数入参
                # %rdi 1
                # %rsi 2
                # %rdx 3
                # %rcx 4
                # %r8 5
                # %r9 6
           #eg: p $rdi 就可以看第一个参数了
i r a      # 查看所有寄存器
i args     # 当前栈帧函数入参
i loc      # locals 查看栈帧内所有局部变量
pt var     # ptype 显示变量的类型
whatis var # 查看类型
disass     # 当前栈帧函数的反汇编
disass func     #看func反汇编
disass /m func  #反汇编与源代码行号对应
p $pc      #看当前汇编指令地址

多线程

i threads  # 查看线程
thread n   # 切换到线程n
set scheduler-locking [off|on|step] # 设置多线程
    # off 不锁定任何线程,也就是所有线程都执行,这是默认值
    # on 只有当前被调试程序会执行
    # step 在单步的时候,除了next过一个函数的情况(是一个设置断点然后continue的行为)以外,只有当前线程会执行

attach 进程

gdb - pid   # 先用 ps 命令查 pid
gdb -p pid
gdb att pid
gdb attach pid
i prog     # program 查看程序是否在运行,进程号,被暂停的原因

打印

set pr pr on    # set print pretty on, 打开printf pretty这个选项,那么当GDB显示结构体时会比较漂亮!
p/[format] var  # 打印变量值 /x 16进制, /d 10进制
x/[n][format][size]  address  # 显示内存
    # [n] n为要打印的对象数目
    # [format] 格式字符,o(octal), x(hex), d(decimal), u(unsigned decimal), 
               #t(binary), f(float), a(address), i(instruction), c(char) and s(string)
    # [size] size指示符,b(byte), h(halfword), w(word), g(giant, 8 bytes)
p *array@len # 查看动态数组,malloc的数组,array里包含了数组的类型了,len是数组元素个数
display/<fmt> <expr>  # 自动显示,程序被断住或停住是,自动显示这个值,非常有用!!!
display/<fmt> <addr>
display/i $pc         # $pc是GDB的环境变量,表示着指令的地址,/i则表示输出汇编

自动化脚本

command n # n表示断点号
命令
命令
...
end

GDB 环境变量

set $my_var = exp
e.g.
set $i = 0
print bar[$i++]->contents  # 回车,下次会自动递增

调试宏

macro expand 带参宏(自己随便定一个入参)  # 测试宏展开, 也可以 gcc -E 看预编译的源码

信号Signals

信号是一种软中断,是一种处理异步事件的方法。比如:SIGINT表示中断字符信号,也就是Ctrl+C的信号。
handle

info signals
info handle     # 查看有哪些信号在被GDB检测中
pass noignore   # 当被调试的程序收到信号时,GDB不处理信号。这表示,GDB会把这个信号交给被调试程序会处理
                # 有些信号处理是必要的,如果给忽略了,程序可能由于自身或其他保护机制退出