如何调试罕见的死锁?

How to debug a rare deadlock?

我正在尝试调试具有很少 死锁的自定义线程池实现。所以我不能使用像 gdb 这样的调试器,因为在出​​现死锁之前我已经点击了 100 次 "launch" 调试器。

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

如何高效调试程序,使程序反复重启直至死锁? (或者也许我应该用所有代码打开另一个问题以获得帮助?)

提前致谢!

奖金问题:如何使用 std::condition_variable 检查一切是否正常?您无法真正判断哪个线程处于休眠状态,或者是否在 wait 条件下发生竞争条件。

如果这是某种家庭作业 - 一次又一次地重新启动并进行更多调试将是一种合理的方法。

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

link中提到的软件居然支持Linux和多线程

您可以 运行 使用 中显示的命令在循环中使用 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打断进入调试器

有两种基本方法:

  1. 在调试器下自动执行 运行 程序。使用 gdb program -ex 'run <args>' -ex 'quit' 应该 运行 调试器下的程序然后退出。如果程序仍然以一种或另一种形式存在(段错误,或者您手动破坏了它),您将被要求确认。
  2. 重现死锁后附加调试器。例如,gdb 可以 运行 作为 gdb <program> <pid> 附加到 运行ning 程序 - 等待死锁然后附加。当连接的调试器导致时间改变并且您无法再重现错误时,这尤其有用。

通过这种方式,您可以 运行 循环并在喝咖啡的同时等待结果。顺便说一句 - 我发现第二个选项更容易。

查找死锁的简单快速调试方法是在要调试的位置修改一些全局变量,然后在信号处理程序中打印它。您可以使用 SIGINT(当您使用 ctrl+c 中断时发送)或 SIGTERM(当您终止程序时发送):

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 中断程序时,您只会看到所有调试变量的状态。

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

$> while [ $? -eq 0 ]
   do
   ./my_program
   done

这将永远 运行 你的程序,直到它失败($? 是你的程序的退出状态,你在你的信号处理程序中退出 EXIT_FAILURE)。

它对我来说效果很好,尤其是找出在什么锁之前和之后有多少线程通过。

它很土气,但你不需要任何额外的工具,而且实施起来很快。

Mozilla rr 开源基于回放的调试

https://github.com/mozilla/rr

Hans ,但有一个具体的开源实现值得一提:Mozilla rr.

首先你做一个记录 运行,然后你可以重播完全相同的 运行 任意多次,并在 GDB 中观察它,它会保留所有内容,包括输入 /输出和线程排序。

official website提到:

rr's original motivation was to make debugging of intermittent failures easie

此外,rr使reverse-next等GDB逆向调试命令能够跳转到上一行,更容易找到问题的根本原因。

这里是 rr 的一个最小例子:How to go to the previous line in GDB?