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 文件中
调试完毕后,输入 qgdb 退出

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 行。另外,还可根据栈的先后关系,推算出线程的调用栈。(注意栈内可能有一些混杂数据,需要进行甄别)

posted @ 2026-01-20 09:48  infinite_ryvius  阅读(1)  评论(0)    收藏  举报