利用 gdb 调试线程

使用 gdb 调试线程

本文通过实践来说明 gdb 如何调试多线程。

如下的程序我们在主程序中先创建两个线程,这两个线程分别打印自己的线程 ID。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void* pthread_run1(void*) {
while (true) {
printf("I am thread1,ID: %lu\n", pthread_self());
sleep(1);
}
}

void* pthread_run2(void*) {
while (true) {
printf("I am thread2,ID: %lu\n", pthread_self());
sleep(1);
}
}

int main() {
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, pthread_run1, NULL);
pthread_create(&tid2, NULL, pthread_run2, NULL);
printf("I am main thread\n");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}

我们可以通过 ps -eLf | grep xxx 来查看进程 ID 和 线程 ID。或者通过 pstree -p 主线程id 查看主线程和子线程之间的关系。

使用 gdb 查看线程的信息。注意:gdb 链接到进程,需要 root 权限。

1
2
3
4
5
6
7
8
9
10
# sudo gdb attach 进程id
# sudo gdb attach 1129979

Attaching to process 1129979
[New LWP 1129980]
[New LWP 1129981]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
__pthread_clockjoin_ex (threadid=281473151635936, thread_return=0x0, clockid=0, abstime=<optimized out>, block=<optimized out>) at pthread_join_common.c:145
145 pthread_join_common.c: No such file or directory.

查看线程的一些信息

1
2
3
4
查看进程:info inferiors
查看线程:info threads
查看线程栈结构:bt
切换线程:thread n (n 代表第几个线程)

如下实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(gdb) info inferiors
Num Description Executable
* 1 process 1129979 /home/noahyzhang/code/cpp/test/test_01/main

(gdb) info threads
Id Target Id Frame
* 1 Thread 0xffff93541010 (LWP 1129979) "main" __pthread_clockjoin_ex (threadid=281473151635936, thread_return=0x0, clockid=0, abstime=<optimized out>, block=<optimized out>)
at pthread_join_common.c:145
2 Thread 0xffff933791e0 (LWP 1129980) "main" 0x0000ffff934185cc in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0, flags=flags@entry=0, req=req@entry=0xffff93378988,
rem=rem@entry=0xffff93378988) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
3 Thread 0xffff92b781e0 (LWP 1129981) "main" 0x0000ffff934185cc in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0, flags=flags@entry=0, req=req@entry=0xffff92b77988,
rem=rem@entry=0xffff92b77988) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78

(gdb) thread 2
[Switching to thread 2 (Thread 0xffff933791e0 (LWP 1129980))]
#0 0x0000ffff934185cc in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0, flags=flags@entry=0, req=req@entry=0xffff93378988, rem=rem@entry=0xffff93378988)
at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78 ../sysdeps/unix/sysv/linux/clock_nanosleep.c: No such file or directory.

当我们切换到某个线程的时候,就可以调试对应的线程

1
2
3
4
5
6
7
8
9
设置断点:break 行号/函数名
查看断点:info b
继续使某一线程运行:thread apply x(第几个线程) n

设置只运行当前线程:set scheduler-locking on
运行:n

设置所有线程并发执行:set scheduler-locking off
运行:n

想要获取所有线程的堆栈信息

1
2
sudo gdb attach pid
thread apply all bt