如何调试罕见的死锁
How to debug a rare deadlock?
我正在尝试调试一个很少死锁的自定义线程池实现。 所以我不能使用像 gdb 这样的调试器,因为我在死锁之前点击了 100 次"启动"调试器。
目前,我正在 shell 脚本中的无限循环中运行线程池测试,但这意味着我看不到变量等。 我正在尝试std::cout
数据,但这会减慢线程速度并降低死锁的风险,这意味着我可以在收到消息之前无限等待 1 小时。 然后我没有收到错误,我需要更多消息,这意味着再等一个小时......
如何有效地调试程序,使其一遍又一遍地重新启动,直到死锁? (或者也许我应该用所有代码打开另一个问题以获得帮助?
提前谢谢你!
奖励问题:如何检查std::condition_variable
一切正常? 您无法真正判断哪个线程处于睡眠状态,或者wait
条件上是否发生了争用条件。
有两种基本方法:
- 在调试器下自动运行程序。使用
gdb program -ex 'run <args>' -ex 'quit'
应在调试器下运行程序,然后退出。如果程序以一种或另一种形式仍然处于活动状态(段错误,或者您手动破坏了它),您将被要求确认。 - 重现死锁后附加调试器。例如,gdb 可以作为
gdb <program> <pid>
运行以附加到正在运行的程序 - 只需等待死锁然后连接即可。当附加的调试器导致计时更改并且您无法再重现错误时,这尤其有用。
通过这种方式,您可以循环运行它并在喝咖啡时等待结果。顺便说一句 - 我发现第二种选择更容易。
如果这是某种家庭作业 - 一次又一次地重新启动并进行更多调试将是一种合理的方法。
如果有人为你等待的每个小时付费,他们可能更愿意投资一个支持基于重放的调试的软件,也就是说,一个记录程序所做的一切,每条指令的软件,并允许你一次又一次地重播它,来回调试。因此,您不会添加更多调试,而是记录发生死锁的会话,然后在死锁发生之前开始调试。你可以随心所欲地来回走动,直到你最终找到罪魁祸首。
链接中提到的软件实际上支持Linux和多线程。
Mozilla rr 开源重放 基于
调试https://github.com/mozilla/rr
Hans 提到了基于重放的调试,但有一个特定的开源实现值得一提:Mozilla rr
。
首先,您执行记录运行,然后您可以根据需要多次重播完全相同的运行,并在GDB中观察它,它保留了所有内容,包括输入/输出和线程排序。
官网提到:
RR 最初的动机是使间歇性故障的调试变得容易
此外,rr
使 GDB 反向调试命令(如 reverse-next
)能够转到上一行,从而更容易找到问题的根本原因。
下面是rr
操作的最小示例:如何转到 GDB 中的上一行?
https://stackoverflow.com/a/8657833/341065: gdb --eval-command=run --eval-command=quit --args ./a.out
中所示的命令在 GDB 下循环运行测试用例。
我自己用过这个:(while gdb --eval-command=run --eval-command=quit --args ./thread_testU ; do echo . ; done)
.
一旦它死锁并且没有退出,您只需通过 CTRL+C 中断它即可进入调试器。
查找死锁的一个简单快速调试是将一些全局变量修改到要调试的位置,然后将其打印在信号处理程序中。您可以使用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退出)。
它对我来说效果很好,特别是对于找出在锁定之前和之后传递了多少线程。
它非常质朴,但您不需要任何额外的工具,并且可以快速实现。
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 用C++中的std::condition_variable将线程置于死锁中会有风险吗
- 使用 std::async 时死锁,将来作为成员
- 如何调试读写器锁的死锁?
- 为什么在Visual Studio 2013上的std::this_thread::sleep_for上死锁
- localtime() 函数正在调用 ___lll_lock_wait_private(),这会使线程陷入死锁
- 如何重现 Boost 进程文档提示的死锁?
- 多线程Windows GUI应用程序中的死锁
- 为什么printf会导致与future.get的死锁,而cout则不会?
- C++中具有阻塞队列和障碍的死锁
- 死锁使用 std::mutex 来保护多个线程中的 cout
- 避免并发等待对象中的死锁
- 在VC++中从DLLMAIN内部调用D3D的CREATEDEVICE时,它会创建一个死锁(loaderlock?)。有没有办法克服这个问题?最终目标内
- 当用2个螺纹锁定时,将recursive_mutex死锁
- 程序在 C++11 中使用条件变量进入死锁
- 一个线程提升的死锁
- 如何调试罕见的死锁
- 如何在C++Qt程序中调试多线程死锁
- 如何在这个小的多线程程序中调试死锁