如何在挂起的C ++中调试多线程应用程序(死锁)?

时间:2009-10-01 20:03:16

标签: c++ multithreading debugging

在java调试中,挂起的应用程序很容易。您可以获取应用程序的内存转储并使用和使用eclipse jvm dump analyzer来查看线程的状态以及每个线程被阻塞的位置?

C ++是否存在类似的内容?

9 个答案:

答案 0 :(得分:4)

gdb中的魔术调用是:

  

主题全部应用bt

为所有线程运行bt(backtrace)命令。除非您完全剥离了程序,否则您应该能够看到每个函数的名称。

这适用于实时和事后(即运行gdb对核心)调试。

答案 1 :(得分:3)

你可以用C ++做同样的事情;强制核心转储并在之后查看。

或者,如果您使用的是MSVC,则只需在运行时将调试器附加到应用程序即可。点击“打破所有”并通过线程进行搜索。

答案 2 :(得分:2)

在Windows原生应用程序中,Windbg是我的首选工具。如果可能的话,我将调试一个死锁的进程,但是没有完整的进程内存转储通常会让你在那里。

我的方法是绘制一个wait graph来记录线程和资源之间的关系。我通常首先运行命令!locks来确定哪些线程在死锁进程中持有任何关键部分。

然后我通过选择具有最高争用计数的关键部分开始绘制等待图(如果存在死锁,则图中将存在一个循环,因此在您开始的位置并不重要)。找到拥有的线程并在调试器中选择它(命令允许您将线程ID与调试器使用的线程号相关联,使用〜*** threadnumber *** s < / strong>选择线程, kbn 显示其堆栈。如果进程死锁,则可能会执行某种阻塞操作,例如查找对RtlEnterCriticalSection或WaitForSingleObject等的调用。在死锁的情况下,这些调用通常会使您识别正在等待的另一个资源。将此信息添加到等待图中并继续,直到您返回到开始的位置。

如果你的等待图穿过进程边界,你可能会发现你需要找到谁在另一个进程中拥有一个内核对象(这就是我可以调试直播的原因)。 sysinternals Process Explorer工具可用于此目的。

一旦你确定参与者陷入僵局,那么你需要把你的思维上限放在下一步找出去的地方。这可能意味着改变资源获取的顺序(正如有人指出的那样)但实际上没有一般方法需要关于应用程序设计的额外信息来理解如何在等待图中删除循环依赖。 / p>

在某些情况下,循环可能不是导致问题的原因,例如您的系统可能正在等待永远不会出现的用户输入(请将任何看过调用MessageBox的人作为服务运行的进程)。

当然还有更多的东西,但我希望这可能会让你朝着正确的方向前进。

答案 3 :(得分:1)

某些平台支持pstack

答案 4 :(得分:0)

我还没有这样做,但我认为您可以使用gdb在挂起时生成应用程序的核心。

您可以尝试使用gdb本身调试此核心,并亲自查看哪些线程被阻塞在哪里?

以上在Linux平台上是可行的。不确定,如果windows上的cygwin可以用于相同的目的。

答案 5 :(得分:0)

当然,策略性地放置cout语句(或其他输出替代方案)总是一种选择,但往往远非理想。

如果使用g ++进行编译,请使用-g进行编译并使用gdb。您可以附加到正在运行的进程(和源代码),或者只需在调试器中运行该程序即可。然后看看堆栈。

在Windows中,只需暂停程序的执行并查看堆栈。

答案 6 :(得分:0)

您可以在Linux系统上使用gdb来查看线程状态

答案 7 :(得分:0)

我们可以使用下面的gdb命令来调试死锁

  • 使用以下命令附加到处于挂起/死锁状态的正在运行的进程

    gdb -p <PID>

  • 完成该过程后,您可以使用以下命令查看所有LWP

(gdb) info threads

Id   Target Id         Frame 

16   Thread 0xfff06111f0 (LWP 2791) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

15   Thread 0xffefdf01f0 (LWP 2792) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

14   Thread 0xffef5bb1f0 (LWP 2793) "abc.d" 0x000000fff26feb4c in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0

13   Thread 0xffeed351f0 (LWP 2794) "abc.d" 0x000000fff2703924 in nanosleep () from /lib64/libpthread.so.0

12   Thread 0xffee5351f0 (LWP 2795) "abc.d" 0x000000fff26fe76c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0

11   Thread 0xffec8a71f0 (LWP 2796) "abc.d" 0x000000fff26fe76c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0

10   Thread 0xffd7cd11f0 (LWP 2797) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

9    Thread 0xffd74d11f0 (LWP 2798) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

8    Thread 0xffd6cd11f0 (LWP 2801) "abc.d" 0x000000fff27022f4 in __lll_lock_wait () from /lib64/libpthread.so.0

7    Thread 0xffd64d11f0 (LWP 2802) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

6    Thread 0xffd5cd11f0 (LWP 2803) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

5    Thread 0xffd54d11f0 (LWP 2804) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

4    Thread 0xffd4cd11f0 (LWP 2805) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

3    Thread 0xffc7fff1f0 (LWP 2928) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

2    Thread 0xffc77ff1f0 (LWP 2929) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6

1    Thread 0xfff0a62000 (LWP 2744 for) "abc.d" 0x000000fff0f19b9c in __lll_lock_wait_private () from /lib64/libc.so.6
  • 我们可以看到线程1和线程8处于等待状态,我们可以按如下所示进入每个线程

    (gdb) thread 1

    (gdb) bt

以上命令的输出如下:

(gdb) thread 1 

[Switching to thread 1 (Thread 0xfff0a62000 (LWP 2744))]

0  0x000000fff0f19b9c in __lll_lock_wait_private () from /lib64/libc.so.6

(gdb) bt 

0  0x000000fff0f19b9c in __lll_lock_wait_private () from
/lib64/libc.so.6

1  0x000000fff0ea3238 in malloc () from /lib64/libc.so.6

2  0x000000fff115df0c in operator new(unsigned long) ()    from
/lib64/libstdc++.so.6

3  0x000000fff11ceddc in std::string::_Rep::_S_create(unsigned long,
unsigned long, std::allocator<char> const&) () from
/lib64/libstdc++.so.6

4  0x000000fff11d165c in char* std::string::_S_construct<char
const*>(char const*, char const*, std::allocator<char> const&,
std::forward_iterator_tag) ()    from /lib64/libstdc++.so.6

5  0x000000fff11d1760 in std::basic_string<char,
std::char_traits<char>, std::allocator<char> >::basic_string(char
const*, std::allocator<char> const&) ()    from /lib64/libstdc++.so.6

6  0x000000fff1eeac1c in getTime() ()    from
/usr/sbin/tejas/sharedobj/liblibLite.so

7  0x000000fff1eeb18c in Logging::logBegin() ()    from
/usr/sbin/tejas/sharedobj/liblibLite.so

8  0x000000fff1f324f8 in sigsegv_handler(int, siginfo_t*, void*) ()   
from /usr/sbin/tejas/sharedobj/liblibLite.so

9  signal handler called

10 0x000000fff0e9f530 in malloc_consolidate () from /lib64/libc.so.6

11 0x000000fff0ea0160 in _int_free () from /lib64/libc.so.6

12 0x000000fff115b184 in operator delete(void*) () from
 /lib64/libstdc++.so.6

13 0x000000fff115b1f4 in operator delete[](void*) ()    from
 /lib64/libstdc++.so.6

14 0x000000fff20cfd60 in pstream::~pstream() ()    from
 /usr/sbin/tejas/sharedobj/libconnV2.so

15 0x000000fff208ffd8 in ifaceSocket::dispatchMsg(pstream&) ()    from
 /usr/sbin/tejas/sharedobj/libsockIf.so

16 0x000000fff207d5a4 in
 socketInterface::socket_callback(ConnectionEvent, char*, int) () from
 /usr/sbin/tejas/sharedobj/libsockIf.so

17 0x000000fff208f43c in ifaceSocket::Callback(ConnectionEvent, char*,
 int)
 () from /usr/sbin/tejas/sharedobj/libsockIf.so

18 0x000000fff20c4674 in ConnectionOS::ProcessReadEvent() ()    from
 /usr/sbin/tejas/sharedobj/libconnV2.so

19 0x000000fff20cc808 in ConnectionOSManager::ProcessConns(fd_set*,
 fd_set*)
 () from /usr/sbin/tejas/sharedobj/libconnV2.so

20 0x000000fff20cf3bc in SocketsManager::ProcessFds(bool) ()    from 
/usr/sbin/tejas/sharedobj/libconnV2.so

21 0x000000fff1e54aa8 in EventReactorBase::IO() ()    from 
 /usr/sbin/tejas/sharedobj/libthreadlib.so

22 0x000000fff1e5406c in EventReactorBase::React() ()    from 
/usr/sbin/tejas/sharedobj/libthreadlib.so

23 0x000000fff1e50508 in Task::Run() ()    from 
/usr/sbin/tejas/sharedobj/libthreadlib.so

24 0x000000fff1e50584 in startTask(void*) ()    from 
/usr/sbin/tejas/sharedobj/libthreadlib.so

25 0x00000000104a421c in TaskMgr::Start() ()

26 0x00000000100ddddc in main ()
  • 我们可以检查pthread_mutex_t结构并获取该线程正在等待的所有者的详细信息。

    (gdb)信息注册表 从r8字段获取第一个地址

    (gdb)打印((int )( 0x0000000019ff3d30 ))  $ 1 = 2 //锁
     (gdb)打印((int )( 0x0000000019ff3d30 )+ 1)  $ 2 = 0 //计数
     (gdb)打印((int (0x0000000019ff3d30 )+ 2)  $ 3 = 2744 //所有者PID

答案 8 :(得分:-1)

  1. 找出卡住线程#1
  2. 所拥有的关键部分
  3. 找出卡住线程#2
  4. 所拥有的关键部分
  5. 确定所述关键部分获取的正确顺序