0 前言
无论是PC还是嵌入式Linux应用开发,GDB是不可或缺的试工具,本文总结一些常用技巧。
1 本地调试
PC调试,就是调试PC上的程序。
1.1 程序
(1)编译参数
使用gcc编译程序,为了使用gdb进行调试,加上选项-g:
gcc -g test.c(2)加载程序
先启动gdb工具,然后使用file命令加载需要调试的程序:
gdb # 启动gdb工具 file a.out # 加载程序a.out # or gdb a.out(3)调试进程
如果想调试系统开机启动的进程(例如窗口管理器)怎办呢?可通过下面方法之一进行调试:
方法1:--pid
gdb --pid <PID> # 例如 gdb --pid `pidof ukwm` # 调试UbuntuKylin-22.04管理器,注:需SSH远程登录来执行该命令以避免界面卡住无法操作方法2:attach
gdb attach <PID> # 也可先启动gdb,再执行attach命令,相反命令为detach[31]注:
- 上述两种方法的效果都是一样的,都可通过detach命令取消调试;
- 尝试attach D状态(可通过“pid -p <PID>”确认)进程会卡死[25]!
1.2 运行
加载程序后,可以使用start或run命令运行:
start # 单步运行 # or run # 可用简称“r”代替,完整运行1.2.1 参数
方法一
set args <参数> run方法二
run <参数> # “run”可简化为“r”1.2.2 暂停
“Ctrl - C”默认会暂停被调试程序的执行。
1.2.3 继续
设置断点后,可以使用c命令继续从断点处开始执行程序(使用r命令会使得程序重新运行)
c # continue1.2.4 单步
(1)C/C++
可以使用n、s命令进行单步调试,其中后者为step in,会跳进调用的函数里面:
s # step, (step in) n # next, (step over)(2)汇编
汇编级别的单步可通过ni、si命令进行,其中后者会进入汇编和C函数内部[34]。
1.2.5 退出
quit # 退出gdb1.3 断点
(1)增加
增加端点的方式有多种,详见下面第1.3.1、1.3.2、1.3.2小节。
(2)禁止/使能
disable <id> enable <id>(3)删除
d <id> # 删除编号为<id>的断点 d # 删除所有断点 clear # 清除所有断点 clear <id> # 清除第18行处的断点 clear func1 # 清除函数func1处的断点(4)查看
info b # 查看当前设置的所有断点 # or i b结果中的“Enb”列的值表示该断点是否使能(y/n):
1.3.1 普通断点
# 函数名 b <func_name> # 在函数func_name处设置断点,C++还需指定类名,例如“RenderThread::main” # 代码行 b 18 # 在第18行处设置断点 b test.c:8 # 在src/main.cpp的第18行处设置断点[23]C++ new操作符也可以设置断点:
b operator new注:在指定该断点行代码之前停止执行,而不是执行完毕再停止,该特性直接影响后面热补丁,如果需要执行断点行代码,需要添加next指令。
1.3.2 条件断点
# 单条件 b test.c:8 if a==10 # 当i变量等于10时断点[23][24] # 双条件 b test.c:8 if (a==10 && b==10) # 当i变量等于10时断点[23][24]1.3.3 热补丁
遇到断点之后可以设置一些自动执行的命令以实现热补丁效果[46],用于临时解决bug或验证问题:
b <func_name> commands 1 # 该序号是变化的,上述“b”命令会提示 printf "parameter a = %d\n",a set var a=10 # 改变函数形参值,函数内局部变量貌似设置不了 printf "parameter a changed to %d\n",a jump 26 # 跳转到第26行执行 continue end在补丁中可以设置、查看变量,还可以改变执行流程(next、jumb、continue、return、finish)。
(1)设置
除了改变变量值,还可以设置跳转(jumb)等(详见参考资料[46]);
(2)查看
除了打印变量值,还可以查看函数地址对应的函数名(详见第1.10小节)。
1.3.4 动态打印
手动:程序到了断点位置暂停下来后,手动执行p或display命令将变量打印出来
自动:通过dprintf命令添加自动打印(打印完毕后程序不暂停,而是继续执行)[47],打印位置的指定方法同前面“b”命令(可通过行号、文件名+行号、函数名等方式指定打印位置),格式化打印类似printf()函数,例如:
(1)存粹打印函数名
dprintf QueueKeyboardEvents,"QueueKeyboardEvents\n"(2)打印参数或变量
# 将xf86PostButtonEventM(DeviceIntPtr device, int is_absolute, int button, int is_down, const ValuatorMask *mask)@xorg-1.20.13/hw/xfree86/common/xf86Xinput.c的前三个函数自动打印出来 dprintf xf86Xinput.c:1369,"is_absolute = %d, button = %d, is_down = %d\n",is