编辑
2025-09-05
记录知识
0

目录

示例代码
让关心的线程运行,其他线程停止
让关心的线程停止,其他线程运行
设置观察点
总结

在实际的gdb调试经验中,更多是多线程的调试方式,因为系统很多的设计都是基于线程的,所以某个二进制存在问题,通常存在与某个线程中,此时gdb需要正确的切换到对应线程中,然后运行。

如果只是切换到某个线程,然后调试,这个其实按照之前的文章就能够达成调试的目的,但是通常而言,需要针对某个线程做一些特殊处理,例如暂停线程,观察某个线程,观察变量。本文主要介绍这些。

示例代码

为了介绍这些技巧,需要准备一个多线程示例程序,如下

#include <stdio.h> #include <unistd.h> #include <pthread.h> int a = 0; int b = 0; void *thread1_func(void *p_arg) { while (1) { a++; sleep(1); } } void *thread2_func(void *p_arg) { while (1) { b++; a=b; sleep(1); } } int main(void) { pthread_t t1, t2; pthread_create(&t1, NULL, thread1_func, "Thread 1"); pthread_create(&t2, NULL, thread2_func, "Thread 2"); pthread_join(t1, NULL); pthread_join(t2, NULL); }

编译即可

gcc threads.c -lpthread -g -o threads

让关心的线程运行,其他线程停止

在调试过程中,我们可以针对自己关心的线程来调试,停掉其他所有运行的线程,从而做一个排除无关因素来定位代码。主要方式如下

set scheduler-locking on b 9

此时根据断点在第9行a++,此时断点触发后,b的值是恒定为1的。如下

(gdb) p b $1 = 1

可以看到,这样的情况下,代码的两个线程并不会在continue的时候同时运行,而是停掉了thread2的运行,只运行thread1。

这种情况下,如果我们怀疑某些代码是某个线程导致的,那么可以根据此来进行排除法,从而能够很快的确定问题范围

让关心的线程停止,其他线程运行

和让关心的线程运行不一样的是,如果我们只是调试某个线程,但不想把所有线程停止,那么可以让其他线程运行,而自己关心的线程停止,从而调试查看变量。如下

(gdb) set non-stop on (gdb) r (gdb) set scheduler-locking off (gdb) b threads.c:16

此时我们查看线程状态,可以看到thread1是运行的,而断点的thread2是停止的,如下

(gdb) info threads Id Target Id Frame * 1 Thread 0xfffff7ff7010 (LWP 3262267) "threads" __pthread_clockjoin_ex (threadid=281474840293856, thread_return=0x0, clockid=0, abstime=<optimized out>, block=<optimized out>) at pthread_join_common.c:145 2 Thread 0xfffff7de71e0 (LWP 3262274) "threads" (running) 3 Thread 0xfffff75e61e0 (LWP 3262275) "threads" thread2_func (p_arg=0xaaaaaaaaaaf0) at threads.c:17

所以我们查看b的值是不变的,而a的值是不断更新的。如下

(gdb) p b $1 = 19 (gdb) p a $2 = 96 (gdb) p a $3 = 99 (gdb) p b $4 = 19

设置观察点

在调试时,经常需要为某个变量在其作用域内设置观察点,通常而言,观察点会导致整体程序运行过慢,甚至导致代码跑飞,所以需要打上硬件观察点awatch和rwatch,而读写观察点和读观察点需要根据业务来判断,通常读观察点性能稍高一点。

但是在使用观察点的时候,还需要注意作用域,离开变量作用域的观察点就会无效。所以局部变量的观察点需要单独讨论,这里先讨论全局变量的观察点。

在多线程中,全局变量是共享数据的,这种情况下,观察点会在每个线程生效,但是如果我只想观察某个线程的全局变量,可以为这个观察点设置仅在某个线程下生效。

(gdb) awatch a thread 3 Hardware access (read/write) watchpoint 2: a (gdb) c Continuing. [Switching to Thread 0xfffff75e61e0 (LWP 3349970)] Thread 3 "threads" hit Hardware access (read/write) watchpoint 2: a Value = 10 (gdb) c Continuing. Thread 3 "threads" hit Hardware access (read/write) watchpoint 2: a Value = 11

可以看到,如果指定了thread 3,那么a的读写观察点只会在thread 3 中被触发。这样就相当于设置了仅某线程生效的观察点。从而方便自己排查问题。

总结

通常很多大型程序都是多线程的,当存在性能问题,或者崩溃问题时,需要使用gdb排查,但是gdb在多线程排查的时候需要一些技巧,否则每次定位程序都比较复杂,本文介绍了能够提高多线程调试效率的办法,从而能够让gdb在多线程调试时,判断问题直接了当,不浪费时间。