GDB 多线程环境调试

出处:http://www.ibm.com/developerworks/cn/linux/l-pow-debug/ 感谢原作者

多线程环境

在 GDB 中有一些特殊的命令可以用于多线程应用程序的调试。下面这个例子给出了一个死锁情况,并介绍了如何使用这些命令来检查多线程应用程序中的问题:


多线程例子
                
#include <stdio.h>
#include "pthread.h>
pthread_mutex_t AccountA_mutex;
pthread_mutex_t AccountB_mutex;
struct BankAccount {
     char account_name[1];
     int balance;
};
struct BankAccount  accountA = {"A", 10000 };
struct BankAccount  accountB = {"B", 20000 };
void * transferAB (void* amount_ptr) {
     int amount = *((int*)amount_ptr);
     pthread_mutex_lock(&AccountA_mutex);
     if (accountA.balance < amount)   {
             printf("There is not enough memory in Account A!\n");
             pthread_mutex_unlock(&AccountA_mutex);
             pthread_exit((void *)1);
     }
     accountA.balance -=amount;
     sleep(1);
     pthread_mutex_lock(&AccountB_mutex);
     accountB.balance +=amount;
     pthread_mutex_unlock(&AccountA_mutex); 
     pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
     int amount = *((int*)amount_ptr);
     pthread_mutex_lock(&AccountB_mutex);
     if (accountB.balance < amount)   {
             printf("There is not enough memory in Account B!\n");
             pthread_mutex_unlock(&AccountB_mutex);
             pthread_exit((void *)1);
     }
     accountB.balance -=amount;
     sleep(1);
     pthread_mutex_lock(&AccountA_mutex);
     accountA.balance +=amount;
     pthread_mutex_unlock(&AccountB_mutex);
     pthread_mutex_unlock(&AccountA_mutex);
}
int main(int argc, char* argv[]) {
     int             threadid[4];
     pthread_t       pthread[4];
     int             transfer_amount[4] = {100, 200, 300, 400};
     int             final_balanceA, final_balanceB;
     final_balanceA=accountA.balance-transfer_amount[0]-

transfer_amount[1]+transfer_amount[2]+transfer_amount[3];
     final_balanceB=accountB.balance+transfer_amount[0]

+transfer_amount[1]-transfer_amount[2]-transfer_amount[3];
     if (threadid[0] = pthread_create(&pthread[0], NULL, transferAB, 
     (void*)&transfer_amount[0]) " 0) {
             perror("Thread #0 creation failed.");
             exit (1);
        }
     if (threadid[1] = pthread_create(&pthread[1], NULL, transferAB, 
      (void*)&transfer_amount[1]) " 0) {
             perror("Thread #1 creation failed.");
             exit (1);
     }
     if (threadid[2] = pthread_create(&pthread[2], NULL, transferBA, 
      (void*)&transfer_amount[2]) < 0) {
             perror("Thread #2 creation failed.");
             exit (1);
     }
     if (threadid[3] = pthread_create(&pthread[3], NULL, transferBA, 
        (void*)&transfer_amount[3]) < 0) {
             perror("Thread #3 creation failed.");
             exit (1);
     }
     printf("Transitions are in progress..");
     while ((accountA.balance != final_balanceA) && (accountB.balance 
             != final_balanceB)) {
             printf("..");
     }
     printf("\nAll the  money is transferred !!\n");
}

使用 gcc 来编译这个程序,如下所示:

# gcc -g -o gdbtest2 gdbtest2.c -L/lib/tls -lpthread

程序 gdbtest2 会挂起,不会返回一条 All the money is transferred !! 消息。

将 gdb 附加到正在运行的进程上,从而了解这个进程内部正在发生什么。


 将 gdb 附加到正在运行的进程上
                
# ps -ef |grep gdbtest2
root      9510  8065  1 06:30 pts/1    00:00:00 ./gdbtest2
root      9516  9400  0 06:30 pts/4    00:00:00 grep gdbtest2
# gdb -pid 9510
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you 
are welcome to change it and/or distribute copies of it under certain 
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for 
details.
This GDB was configured as "ppc-suse-linux".
Attaching to process 9510
Reading symbols from /root/test/gdbtest2...done.
Using host libthread_db library "/lib/tls/libthread_db.so.1".
Reading symbols from /lib/tls/libpthread.so.0...done.
[Thread debugging using libthread_db enabled]
[New Thread 1073991712 (LWP 9510)]
[New Thread 1090771744 (LWP 9514)]
[New Thread 1086577440 (LWP 9513)]
[New Thread 1082383136 (LWP 9512)]
[New Thread 1078188832 (LWP 9511)]
Loaded symbols for /lib/tls/libpthread.so.0
Reading symbols from /lib/tls/libc.so.6...done.
Loaded symbols for /lib/tls/libc.so.6
Reading symbols from /lib/ld.so.1...done.
Loaded symbols for /lib/ld.so.1
0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) info thread
  5 Thread 1078188832 (LWP 9511)  0x0ffe94ec in __lll_lock_wait () 
   from /lib/tls/libpthread.so.0
  4 Thread 1082383136 (LWP 9512)  0x0ffe94ec in __lll_lock_wait () 
   from /lib/tls/libpthread.so.0
  3 Thread 1086577440 (LWP 9513)  0x0ffe94ec in __lll_lock_wait () 
   from /lib/tls/libpthread.so.0
  2 Thread 1090771744 (LWP 9514)  0x0ffe94ec in __lll_lock_wait () 
        from /lib/tls/libpthread.so.0
  1 Thread 1073991712 (LWP 9510)  0x0ff4ac40 in __write_nocancel () 
     from /lib/tls/libc.so.6
(gdb)

info thread 命令中,我们可以了解到除了主线程(thread #1)之外的所有线程都在等待函数 __lll_lock_wait () 完成。

使用 thread apply threadno where 命令来查看每个线程到底运行到了什么地方:


查看每个线程运行到了什么地方
           
(gdb) thread apply 1 where
Thread 1 (Thread 1073991712 (LWP 9510)):
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
#1  0x0ff4ac28 in __write_nocancel () from /lib/tls/libc.so.6
Previous frame identical to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 2 where
Thread 2 (Thread 1090771744 (LWP 9514)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 3 where
Thread 3 (Thread 1086577440 (LWP 9513)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 4 where
Thread 4 (Thread 1082383136 (LWP 9512)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
 (gdb) thread apply 5 where
Thread 5 (Thread 1078188832 (LWP 9511)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6

每个线程都试图对一个互斥体进行加锁,但是这个互斥体却是不可用的,可能是因为有另外一个线程已经对其进行加锁了。从上面的证据我们可以判断程序中一定存在死锁。您还可以看到哪个线程现在拥有这个互斥体。


查看哪个线程拥有互斥体
                
(gdb) print AccountA_mutex
$1 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2527, 

__m_kind = 0, __m_lock  = {__status = 1, __spinlock = 0}}
(gdb) print 0x2527
$2 = 9511
(gdb) print AccountB_mutex
$3 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2529, 

__m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}}
(gdb) print 0x2529
$4 = 9513
(gdb)  

从上面的命令中,我们可以看出 AccontA_mutex 是被线程 5(LWP 9511)加锁(拥有)的,而 AccontB_mutex 是被线程 3(LWP 9513)加锁(拥有)的。

为了解决上面的死锁情况,可以按照相同的顺序对互斥体进行加锁,如下所示:


按照相同的顺序对互斥体进行加锁
                    
.
.
void * transferAB (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountA.balance < amount)   {
                printf("There is not enough memory in Account A!\n");
                pthread_mutex_unlock(&AccountA_mutex);
                pthread_exit((void *)1);
        }
        accountA.balance -=amount;
        sleep(1);
        accountB.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountB.balance < amount)   {
                printf("There is not enough memory in Account B!\n");
                pthread_mutex_unlock(&AccountB_mutex);
                pthread_exit((void *)1);
        }
        accountB.balance -=amount;
        sleep(1);
        accountA.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_unlock(&AccountB_mutex);
}
.
.

或者对每个帐号单独进行加锁,如下所示:


对每个帐号单独进行加锁
                    
.
.
void * transferAB (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        if (accountA.balance < amount)   {
                printf("There is not enough memory in Account A!\n");
                pthread_mutex_unlock(&AccountA_mutex);
                pthread_exit((void *)1);
        }
        accountA.balance -=amount;
        sleep(1);
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex); 
        accountB.balance +=amount;
        pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountB.balance < amount)   {
                printf("There is not enough memory in Account B!\n");
                pthread_mutex_unlock(&AccountB_mutex);
                pthread_exit((void *)1);
        }
        accountB.balance -=amount;
        sleep(1);
        pthread_mutex_unlock(&AccountB_mutex);
        pthread_mutex_lock(&AccountA_mutex); 
        accountA.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
}
.
.
.

要调试 64 位的应用程序(使用 GCC 的 –m64 选项或 IBM XL C/C++ 编译器的 –q64 选项编译),应该使用一个特别的 gdb 版本 gdb64。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值