寻找竞争条件的方法

Posted

技术标签:

【中文标题】寻找竞争条件的方法【英文标题】:Ways to Find a Race Condition 【发布时间】:2011-03-09 06:55:24 【问题描述】:

我有一段代码,其中包含竞态条件...我知道这是一个竞态条件,因为它不会始终如一地发生,而且它似乎在双核机器上更频繁地发生。

当我追踪时,它永远不会发生。虽然,它也有可能是一个僵局。通过分析发生和未发生这种情况的日志完成阶段,我已经能够将此错误定位到单个函数。但是,我不知道这是在函数范围内发生的。它不在顶层。

如果是竞态条件,添加日志语句或断点会改变时间,并防止这种情况发生。

除了获得一个可以让我查明发生这种情况的竞争条件分析器之外,我还可以使用什么技术吗?

这是在 Visual Studio 9 中,使用 C++(非托管品种)。

【问题讨论】:

上次我遇到了严重的比赛状况,我在当地知道它发生在哪里。我以“老式方式”进行操作,并采用图形调用树并手动突出显示每个调用的锁定持续时间。在我的例子中,它被归为 2 个源文件和一些函数,但它被证明是无价的。 【参考方案1】:

CLang 和 gcc 4.8+ 中包含一个名为 ThreadSanitizer 的工具。

您使用-fsanitize=thread 标志编译您的代码

例子:

$ cat simple_race.cc
#include <pthread.h>
#include <stdio.h>

int Global;

void *Thread1(void *x) 
  Global++;
  return NULL;


void *Thread2(void *x) 
  Global--;
  return NULL;


int main() 
  pthread_t t[2];
  pthread_create(&t[0], NULL, Thread1, NULL);
  pthread_create(&t[1], NULL, Thread2, NULL);
  pthread_join(t[0], NULL);
  pthread_join(t[1], NULL);

还有输出

$ clang++ simple_race.cc -fsanitize=thread -fPIE -pie -g
$ ./a.out 
==================
WARNING: ThreadSanitizer: data race (pid=26327)
  Write of size 4 at 0x7f89554701d0 by thread T1:
    #0 Thread1(void*) simple_race.cc:8 (exe+0x000000006e66)

  Previous write of size 4 at 0x7f89554701d0 by thread T2:
    #0 Thread2(void*) simple_race.cc:13 (exe+0x000000006ed6)

  Thread T1 (tid=26328, running) created at:
    #0 pthread_create tsan_interceptors.cc:683 (exe+0x00000001108b)
    #1 main simple_race.cc:19 (exe+0x000000006f39)

  Thread T2 (tid=26329, running) created at:
    #0 pthread_create tsan_interceptors.cc:683 (exe+0x00000001108b)
    #1 main simple_race.cc:20 (exe+0x000000006f63)
==================
ThreadSanitizer: reported 1 warnings

【讨论】:

Thread Sanitizer 检测竞争条件。它检测不一定相同的数据竞争。您的示例显示了标准所说的数据竞争,而不是竞争条件。请参阅 this 以了解它未能检测到竞态条件的位置 不过,它在 Windows 上不受支持。【参考方案2】:

在代码的各个部分放置睡眠。即使它(或异步代码)休眠几秒钟,线程安全的东西也将是线程安全的。

【讨论】:

我知道现在评论这个答案已经很晚了,但是有没有任何例子可以说明如何使用“put sleeps”来检测竞争条件? @james:这是一种非常“白盒”的竞态条件分析方法,只是为了指出,如果你的竞态条件只有 1% 的时间“获胜”,那么在其中一个竞争线程中的一些睡眠可以使其“赢”〜100%的时间,从而更容易诊断。因为它必然要求你对代码有很多了解(和修改),所以这里很难举例。【参考方案3】:

确实有一些尝试自动查找竞争条件。

Lockset-Based Race Checker Happens-Before Race Detection Hybrid Race Detection

我读到的另一个与竞争条件检测相关的术语是 RaceFuzzer,但我找不到真正有用的信息。

我认为这是一个相对年轻的研究领域,所以据我所知,主要是关于这个主题的理论论文。不过,尝试用谷歌搜索上述关键字之一,也许你会发现一些有用的信息。

【讨论】:

【参考方案4】:

我所知道的追踪这些的最好方法是在 Visual Studio 中使用 CHESS。这不是一个简单易用的工具,可能需要逐步测试应用程序的子部分。祝你好运。

【讨论】:

【参考方案5】:

我在使用 Visual Studio 的跟踪点来查找竞争条件方面很幸运。当然,它仍然会影响计时,但至少在我使用它的情况下,它还不足以完全防止竞争条件的发生。至少,它看起来不像专用日志记录那样具有破坏性。

除此之外,请尝试发布代码以供其他人查看。仅仅详细研究代码并不是找到竞争条件的好方法。

【讨论】:

【参考方案6】:

所以,我的大锤方法如下,这需要很大的耐心,并且在最好的情况下可以让你走上正轨。我用它来弄清楚这个特定问题发生了什么。 我一直在使用tracepoints,一个在怀疑高级函数的开头,一个在结尾。向下移动跟踪点。如果在函数开头添加跟踪点导致您的错误停止发生,请向下移动跟踪点,直到您可以再次重现该条件。这个想法是,如果您将跟踪点放在最终触发不安全代码的调用之后,则跟踪点不会影响时间,但如果您将它放在之前,则会影响时间。另外,请注意您的输出窗口。您的错误发生在哪些消息之间?您也可以使用跟踪点来缩小此范围。

一旦您将错误缩小到可管理的代码区域,您就可以设置断点并查看此时其他线程在做什么。

【讨论】:

【参考方案7】:

它也可以是不受保护的资源,这可以解释不一致的行为(特别是如果在单核上它工作正常而不是在双核上)。无论如何,代码审查(针对竞争条件和非线程安全的源代码)可能是解决问题的最短路径。

【讨论】:

【参考方案8】:

您可以使用Intel Inspector 之类的工具来检查某些类型的竞争条件。

【讨论】:

以上是关于寻找竞争条件的方法的主要内容,如果未能解决你的问题,请参考以下文章

多线程系列Race Condition的产生和解决

Angular - 竞争条件 - 一次多个订阅

使用 __block 的竞争条件

在 model.save() 中处理竞争条件

数据竞争

moctf 没时间解释了 条件竞争漏洞