如何调试罕见的死锁?

时间:2015-01-04 22:08:18

标签: c++ multithreading c++11 threadpool deadlock

我正在尝试调试很少死锁的自定义线程池实现。所以我不能使用像gdb这样的调试器,因为我在遇到死锁之前点击了100次“启动”调试器。

目前,我在shell脚本的无限循环中运行线程池测试,但这意味着我看不到变量等等。我正在尝试std::cout数据,但这会减慢线程并减少死锁的风险,这意味着我可以在获取消息之前等待1小时的无限。然后我没有得到错误,我需要更多的消息,这意味着再等一个小时......

如何有效地调试程序,以便它一遍又一遍地重启直到它死锁? (或者我应该用所有代码打开另一个问题以获得一些帮助?)

提前谢谢!

奖金问题:如何使用std::condition_variable检查一切正常?您无法确定哪个线程处于睡眠状态或wait条件下是否出现竞争条件。

5 个答案:

答案 0 :(得分:17)

有两种基本方法:

  1. 在调试器下自动运行程序。使用gdb program -ex 'run <args>' -ex 'quit'应该在调试器下运行程序,然后退出。如果程序仍然以某种形式存在(段错误,或者您手动将其打破),则会要求您进行确认。
  2. 在重现死锁后附加调试器。例如,gdb可以作为gdb <program> <pid>运行以附加到正在运行的程序 - 只需等待死锁然后附加即可。当附加的调试器导致更改时间并且您无法再重新编写错误时,这尤其有用。
  3. 通过这种方式,您可以在循环中运行它并在喝咖啡时等待结果。顺便说一句 - 我发现第二种选择更容易。

答案 1 :(得分:5)

如果这是某种功课 - 再次使用更多调试重新启动将是一种合理的方法。

如果有人为你等待的每一个小时付钱,他们可能更愿意投资支持replay-based debugging的软件,即一个记录程序所做的一切的软件,每条指令,并允许你重播它一次又一次,来回调试。因此,不是添加更多调试,而是记录发生死锁的会话,然后在发生死锁之前开始调试。你可以随心所欲地来回走动,直到你终于找到了罪魁祸首。

链接中提到的软件实际上支持Linux和多线程。

答案 2 :(得分:2)

您可以使用https://stackoverflow.com/a/8657833/341065中显示的命令循环在GDB下运行测试用例:gdb --eval-command=run --eval-command=quit --args ./a.out

我自己使用过这个:(while gdb --eval-command=run --eval-command=quit --args ./thread_testU ; do echo . ; done)

一旦死锁并且没有退出,你可以通过 CTRL + C 中断它以进入调试器。

答案 3 :(得分:1)

查找死锁的简单快速调试是将一些全局变量修改为您要调试的位置,然后将其打印在信号处理程序中。您可以使用SIGINT(使用nMoments打断时发送)或SIGTERM(杀死程序时发送):

int nMoments = 1000;
dataset = new DynamicTimeSeriesCollection(1, nMoments, new Second());
Calendar c = Calendar.getInstance();
c.setTime(new Date(0));
c.add(Calendar.SECOND, -nMoments);
dataset.setTimeBase(new Second(c.getTime()));

就像使用ctrl+c中断程序时,您只看到所有调试变量的状态。

此外,您可以在shell循环中运行它:

int dbg;

int multithreaded_function()
{
  signal(SIGINT, dbg_sighandler);
  ...
  dbg = someVar;
  ...  
}

void  dbg_sighandler(int)
{
  std::cout << dbg1 << std::endl;
  std::exit(EXIT_FAILURE);
}

将永远运行您的程序,直到它失败(ctrl+c是程序的退出状态,并在信号处理程序中退出EXIT_FAILURE。)

它对我来说效果很好,尤其是找出在锁定之前和之后传递了多少线程。

它非常质朴,但您不需要任何额外的工具,而且实施起来很快。

答案 4 :(得分:1)

基于Mozilla rr开源重播的调试

https://github.com/mozilla/rr

Hans mentioned replay based debugging,但有一个特定的开源实现值得一提:Mozilla rr

首先执行记录运行,然后您可以根据需要重复完全相同的运行,并在GDB中观察它,并保留所有内容,包括输入/​​输出和线程排序。

official website提及:

  

rr最初的动机是让间歇性故障的调试变得容易

此外,rr使GDB反向调试命令(如reverse-next)可以转到上一行,这样可以更容易地找到问题的根本原因。

以下是rr的最小示例:How to go to the previous line in GDB?