首先介绍下GDB常用命令
流程
start <args>或run <args>:bin程序开始执行
c(continue):从断点开始继续执行,直到下一断点或结束
n(next):执行下一行(跳过函数)
s(step):执行下一行(进入函数)
finish:运行到当前函数结束退出
运行状态下ctrl-c:暂停
q(quit),或暂停状态下ctrl-c:退出
断点
b <func-name>:在函数上打断点
b * <address>:在代码段地址上打断点
i b:查看断点
d b <N>:禁用指定编号的断点
e b <N>:启用指定编号的断点
b ... if <expr>:条件断点:若<expr>为真(或非NULL),在断点处暂停
临时进入shell模式
shell(进入)、exit(退出)
帮助:help或?
查看信息
bt(backtrace):查看调用栈
i r:查看寄存器信息
x/Ns:以字符串形式查看指定变量/地址
x/Nd:以十进制数字形式查看指定变量/地址
x/Nx:以十六进制形式查看指定变量/地址
x/Ni:查看指定函数/地址的汇编指令
p <expr>:查看<expr>的值
x/b &str:查看全局变量的字符串,str是一个全局字符串
其它
暂停状态下回车:执行上一条命令
支持上翻、下翻
设置打印字符的个数为不限制数量,设置为unlimited或者zero。
(gdb) set print elements 0
(gdb) show print elements
Limit on string chars or arry elemtns to print is unlimited
CPU寄存器信息
mips:
zero:永远为0
v0/v1:临时寄存器;函数返回时存返回值
a0 ~ a3:临时寄存器;调用函数时存参数
t0 ~ t9:临时寄存器。t9常用于临时存要调用的子函数入口地址
s0 ~ s8:非易失性寄存器
gp:全局指针
sp:栈顶指针
ra:当前函数返回地址
pc:当前指令
x86:
r15 = 0x0000000000000007 r14 = 0x0000000000000000
r13 = 0x0000000000000000 r12 = 0xffffc9005dd6b2b0
bp = 0xffff8800462375e8 bx = 0xffff8800463780a0 (这里的bp寄存器和上面提到的%rbp寄存器是等价的)
r11 = 0x0000000000000000 r10 = 0xffffc9005dd735b0
r9 = 0x0000000000000002 r8 = 0xffffc9005dd67130
ax = 0x0000000004230423 cx = 0xffffc90061f9b6d3
dx = 0x0000000000000095 si = 0x0000000000000000
di = 0xffffc9005dd67008 orig_ax = 0xffffffffffffffff
ip = 0xffffffffc224156c cs = 0x0000000000000010
flags = 0x0000000000010246 sp = 0xffff880046237570
ss = 0x0000000000000018 ®s = 0xffff8800462374d8
2) SP寄存器指向当前调用栈的栈顶,通过md命令可查看调用栈的内容,而IP寄存器里存储的是正在执行的函数地址
0xffff880046237580 ffff880046237696 ffff880046378355 .v#F....U.7F....
0xffff880046237590 0000000000000000 ffffc9005dd7772e .........w.]....
0xffff8800462375a0 ffff8800462376e4 ffff880046378356 .v#F....V.7F....
0xffff8800462375b0 ffff880046378010 0000000042dd1653 ..7F....S..B....
0xffff8800462375c0 0000000000000001 0000000000000000 ................
0xffff8800462375d0 0000000000000000 0000000000000346 ........F.......
0xffff8800462375e0 0000000000000000 // 3)BP寄存器指向的地址(0xffff8800462375e8),存储了前一个函数栈的BP和返回地址。ffff880046237638 是前一个函数栈的BP值, ffffffffc22427a9是函数返回地址。
0xffff8800462375f0 ffffffffc22427a9
fa_traverse+0x69 // 4)通过函数返回地址得到前一个调用函数名称fa_traverse。
0xffff880046237600 ffff880046237688 0000000000000000 .v#F............
0xffff880046237610 0000000000000000 0000000000000000 ................
0xffff880046237620 0000000000000000 ffffffffc31ff918 ................
0xffff880046237630 ffffc9005dd67000 // 5)同时,我们得到了fa_traverse函数的BP寄存器的值,它指向的地址(ffff880046237638)同样存储了前一个函数的BP和返回地址。
0xffff880046237640 ffffffffc22428ed // 6)以此类推, 接着向下推栈即可。
regex_traverse+0x7d
0000000000000000 .($.............
0xffff880046237650 ffff880046237680 ffff880046237690 .v#F.....v#F....
0xffff880046237660 ffff8800462376e4 0000000000000000 .v#F............
[12]kdb>
0xffff880046237670 ffff880046237688 0000000000000000 .v#F............
0xffff880046237680 0000000000000000 0000000000000001 ................
0xffff880046237690 0000000000000000 0000000000000000 ................
0xffff8800462376a0 ffffffffc31ff660 ffff880029099c00 `..........)....
0xffff8800462376b0 0000000000000000 0000000000000010 ................
0xffff8800462376c0 0000000000000000
ffff8800462376f8 .........v#F....
0xffff8800462376d0 ffffffffc223f533
pcre_engine_regex_check+0x43
ffffffffc210f000 3.#.............
0xffff8800462376e0 00003a98c31ff660 ffff880029099c00 `....:.....)....
0xffff8800462376f0 ffffffffc31ff660
ffff880046237778 `.......xw#F....
0xffff880046237700 ffffffffc237dea1
dcdm_pcre_new_single_rule_proc+0xf1
0000020000000200 ..7.............
0xffff880046237710 0000000000000000 ffff880029099c30 ........0..)....
0xffff880046237720 ffffe90142dd16f0 ffffe90142dd16d8 ...B.......B....
0xffff880046237730 ffffffffc31ff918 0000000000000000 ................
0xffff880046237740 ffff880029099c1c ffff8800462377a8 ...).....w#F....
0xffff880046237750 0000000000000000 ffff880046370000 ..........7F....
0xffff880046237760 ffffffffc31ff660 0000000000000000 `...............
0xffff880046237770 ffff880029099c00
ffff8800462377d8 ...).....w#F....
0xffff880046237780 ffffffffc237e34b
dcdm_pcre_new_flow_proc+0x1eb
ffff880065a04880 K.7......H.e....
0xffff880046237790 0000000165a1e0f6 ffffe90142dd16c0 ...e.......B....
0xffff8800462377a0 ffff880065a04880 ffff8800462377d8 .H.e.....w#F....
0xffff8800462377b0 ffff880065a04880 ffff880046370000 .H.e......7F....
0xffff8800462377c0 ffffe90142dd16a8 000000000002d2a0 ...B............
0xffff8800462377d0 ffffe90142dd1518 ffff8800462377f8 ...B.....w#F....
0xffff8800462377e0 ffffffffc237e3cb ffff880065a04880 ..7......H.e....
0xffff8800462377f0 ffff880046370000 ffff880046237898 ..7F.....x#F....
0xffff880046237800 ffffffffc237d311 0000000000000000 ..7.............
0xffff880046237810 0000000000000000 ffff880046237868 ........hx#F....
0xffff880046237820 ffffffffc21eb9ff 471d0201ba190101 ...............G
0xffff880046237830 000000061606db27 ffff880046237858 '.......Xx#F....
0xffff880046237840 ffff880065a04880 ffffffffc5ed6860 .H.e....`h......
0xffff880046237850 42000004c2380ca3 ffff880046237898 ..8....B.x#F....
0xffff880046237860 00000001c23785f4 ffff8800462378b8 ..7......x#F....
调试进程
使用方法
gdb -p <pid>
适用场景
某进程运行过程中会出故障,需要跟踪执行过程
例:页面下发配置时发生500错误,需分析web_main代码流程
某进程已经出故障但未崩溃,需要查看当前正在哪个流程中
例:串口卡住,但远程诊断能用,需要析vtysh卡在何处
操作流程:
进入gdb模式;
打断点;
c;
进行页面/命令行等操作,使进程运行至断点处;
分析寄存器、内存信息。
反复进行3~5步操作,直到分析明白。
q;
调试bin
使用方法
gdb <bin-file-path>
适用场景
某程序执行时不会转变成后台进程,运行过程中会出故障,需要跟踪执行过程
例:配置恢复有启动项失败,需要从startup程序跟踪其执行流程
某程序执行时会转变成后台进程,但进入自身循环之前(相当于进程启动阶段)会出故障,需要分析该过程执行流程
例:eventd进程进入死循环前出故障,需要分析该执行过程
set follow-fork-mode (parent|child)命令用法
用于指定跟随子进程还是父进程(默认为parent)
当前进程切换成后台进程时,执行set follow-fork-mode child
调用生成子进程的函数(如system、popen、exec系列函数等)前,执行set follow-fork-mode parent
操作流程:
与后台进程类似,但在1~3之间执行set follow-fork-mode child,确定已切换到后台后set follow-fork-mode parent切回来。
调试启动项:
先找到要调用的启动项所在文件startup_N_*、所在行M
对/usr/bin/startup挂gdb
先打断点、执行set follow-fork-mode child
运行:r -p N M