Linux 用户线程卡死调试
在嵌入式开发中,有时会遇到线程死锁,等待某些信号量卡死等问题。这类问题往往很少出现,一旦出现也比较难以调试。
一般来讲,可以通过 gdb 工具进行调试,打印此时卡死的线程栈帮助确认问题。下面将介绍一下步骤。
1.确认进程号
>>> ps | grep main
98 root 131m S {main} /bin/main
2061 root 1236 S grep main
可以找到,进程号为 98
2.确认线程号
一般来讲,我们是推测 xxxx 的线程名的线程可能卡死了,比如 record 线程卡死了。
那么在 Linux 下 main 进程的子线程就在 /proc/98/task/ 路径下,子线程名字为保存在 /proc/98/task/{子线程号}/comm 中
因此可以通过如下方法,把线程号和线程名全部都打印出来,从而确认线程号
>>> for tid in /proc/98/task/*; do [ -d "$tid" ] && echo -n "$(basename "$tid") " && cat "$tid/comm"; done
124 timer
125 uart
144 light
149 audio_play
162 video_recv
179 http
180 rtsp
181 osd
183 storage
189 record
……
因此,可以得到 record 线程号为 189
3.gdb打印内核堆栈
根据查找到的线程号,通过 gdb 调试工具进入附加线程,打印堆栈
>>> # 进入 GDB 附加线程, TID = 189
>>> gdb -p 189
>>> # 获取栈指针(SP)
>>> (gdb) info register sp
sp 0x75f64720 0x75f64720
>>> # 打印栈顶开始的 256 字节 (64个32为字)
>>> (gdb) x/64xw 0x75f64720
0x75f64720: 0x00b651d8 0x00b651f0 0x00000000 0x00000000
0x75f64730: 0x00000000 0x00000000 0x000000f0 0x75f64720
0x75f64740: 0x00000000 0x00000000 0x00000000 0x00000000
0x75f64750: 0x00000000 0x00b651f0 0x00b651d8 0x00000000
0x75f64760: 0x76efac1d 0x75f64750 0x00000000 0x00000000
0x75f64770: 0x00000000 0x00000000 0x00000000 0x00000000
0x75f64780: 0x00000000 0x00000001 0x00000001 0x00000001
0x75f64790: 0x00000001 0x00000000 0x00b64388 0x00b64388
0x75f647a0: 0x00000000 0x00000000 0x00000000 0x00000000
0x75f647b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x75f647c0: 0x00000001 0x00000001 0x00000001 0x00000001
0x75f647d0: 0x00000000 0x00b64388 0x00b64388 0x00000000
0x75f647e0: 0x00000000 0x00000000 0x00000001 0x00b64370
0x75f647f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x75f64800: 0x00b651f4 0x00000080 0x00001c33 0x00000000
0x75f64810: 0x00000001 0x00000001 0x00000001 0x00000001
把这些寄存器信息保存到本地 stack.txt 文件中
调试完毕后,输入 q 让 gdb 退出
4.栈回溯
将原始的 main 执行文件,和 stack.txt 放到同一个目录下。注意,main 执行文件必现得是没有 strip 过的,这样才能保留符号表。并且需要交叉编译链的 addr2line,不同芯片方案略有不同。
>>> grep -oE '0x[0-9a-fA-F]+' call.txt | xargs addr2line -e main -f | grep -v '??'
plan_process
record/plan.c:305
$d
record/plan.c:?
$d
storage/record.c:74
$d
storage/record.c:3696
$d
storage/record.c:74
record_body
storage/record.c:4321
$d
storage/record.c:74
$d
storage/record.c:186
$d
storage/record.c:173
$d
由此可以看到,阻塞在 record_body 函数,record.c 第 4321 行。另外,还可根据栈的先后关系,推算出线程的调用栈。(注意栈内可能有一些混杂数据,需要进行甄别)

浙公网安备 33010602011771号