Linux开发调试经验整理(一)——Release版本死锁定位
2015-09-09 21:55
609 查看
话说谁能生巧。以前由于经常被拉去定位疑难杂症,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 查看该线程堆栈
4) frame 1 切换到 __lll_lock_wait () 帧
5) 执行 p (pthread_mutex_t)ebx1 = {__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打印),找到该线程的日志信息,分析是哪个线程造成的死锁。
4)如果提示 pthread_mutex_t 符号找不到,说明gdb没有为 pthreadtypes.h 加载符号表,这种情况下仍有方法查看锁状态
附:pthread_mutex_t 定义
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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)ebx1 = {__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;
相关文章推荐
- Linux 常用命令
- linux-C子进程退出信号
- linux下jsoncpp的编译和测试使用
- 系统调用原理
- linux 初级阶段整理笔记
- centos 下面安装lamp
- Linux(Ubuntu-GNOME)桌面常用快捷方式总结
- 浅谈Linux文件系统
- linux磁盘分区fdisk命令详解
- qrencode二维码生成在linux下的编译(转)
- Linux 文件查看常用命令
- linux文件查找命令
- 第一天:探索(2)
- Linux2.6.32内核笔记(2)内存管理子系统
- AIX和Linux如何确认磁盘是本地还是存储上的
- Linux运维之路第一部总结及扩展
- linux /sbin FHS翻译
- linux下.vimrc文件配置
- CentOS用DD镜像及恢复过程
- lesson2 -basic Linux tasks