话说谁能生巧。以前由于经常被拉去定位疑难杂症,gdb用的还算熟练。最近年把因工作内容的调整,gdb很少用,前些日子定位问题时发现曾经很熟悉的东西都有点陌生了。因此决定把以前整理的一些小经验、技巧再回顾一下,并分享给大家。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
debug 版本的应用程序发生死锁,可以将pthread_mutex_t打印出来,查看其中的owner字段即可知道锁被哪个线程持有。
release版本的程序,由于进行了优化,可能无法直接打出锁变量。 这里介绍一个简单方法,可以查看release版(当然也支持debug)的锁状态,以便快速定位死锁问题。
操作步骤
1) gdb attach 到死锁的进程.
例如 gdb –p 2456
2) thread 命令切换到等待锁(这里主要指Mutex,暂不考虑读写锁)的某个线程。 可以结合 info thr、thr apply all bt 等命令确定哪些线程在等锁。
3) bt 查看该线程堆栈
(gdb) bt
#0 0x00110424 in __kernel_vsyscall ()
#1 0x0062d019 in __lll_lock_wait () from /lib/libpthread.so.0
#2 0x00628430 in _L_lock_677 () from /lib/libpthread.so.0
#3 0x00628301 in pthread_mutex_lock () from /lib/libpthread.so.0
……
4) frame 1 切换到 __lll_lock_wait () 帧
5) 执行 p (pthread_mutex_t) ebx 1 = {__data = {__lock = 2, _count = 0, __owner = 20644, __kind = 0, __nusers = 1, {_spins = 0, __list = {
__next = 0x0}}},
__size = “\002\000\000\000\000\000\000\000\244P\000\000\000\000\000\000\001\000\000\000\000\000\000”, __align = 2}
Owner 字段表示是哪个线程持有这把锁,它是线程的 LWP 号,可以通过 info thr 查看。
6) 根据info thr 的信息,thr 命令切换到 owner对应的线程,结合代码排查。 一般来说,这个线程应该也在等锁,重复执行 4、5 步骤,可以看到它锁等待的锁被谁持有。 再结合代码分析,基本上可以定位出死锁位置。
注意事项
1)上述方法只适用于32bit系统上使用 pthread_mutex_t 造成的死锁。
X86_64 位系统上,对应的寄存器貌似是 rdi ,也就是说命令应该改成
p *(pthread_mutex_t)$rdi
2) 读写锁造成的死锁,需要结合glibc源码和汇编,找出pthread_rwlock_t的地址(具体是啥还有待分析 _),将其打印出来应该即可。
3)如果owner对应的线程已经退出,就无法通过gdb查看该线程的堆栈。 因此建议在日志中打印线程的LWP号(可通过getttid打印),找到该线程的日志信息,分析是哪个线程造成的死锁。
#define gettid() syscall(__NR_gettid)
4)如果提示 pthread_mutex_t 符号找不到,说明gdb没有为 pthreadtypes.h 加载符号表,这种情况下仍有方法查看锁状态
(gdb) print ((int)($ebx)) // lock 字段
$4 = 2
(gdb) print ((int)($ebx)+1) // count 字段
$5 = 0
print *((int)($ebx)+2) // owner字段*
$6 = 12275
附:pthread_mutex_t 定义
typedef union {
struct __pthread_mutex_s {
int lock;
unsigned int __count;
int __owner;
/* KIND must stay at this position in the structure to maintain binary compatibility. */
int __kind;
unsigned int __nusers;
__extension union {
int _spins;
__pthread_slist_t __list;
};
} __data;
char __size[_SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t;