做毕设的时候,我曾经遇到一个多线程的BUG。这个BUG表现得较为诡异,会导致数据随机出错。由于找不出什么规律,一开始我还是挺头疼的。查了半天后我发现,相关的日志有多线程下共享数据访问问题的迹象(即所谓的data race),所以很快确诊是多线程部分代码存在逻辑错误。这个问题的解决办法很简单,就是把相关的代码review下,找出data race的部分并加以修正。虽然BUG是搞定了,不过我还是想找到一个自动化工具,能够检测出代码中潜在的线程安全问题。这样就能把BUG消灭在萌芽之中,而不是等到事后才睁大眼睛揪它出来。
搜索了下,发现了两个适合做这个的工具,Valgrind和ThreadSanitizer。今天就来介绍下这两个工具。
Valgrind
Valgrind一般用做内存泄露和访存越界检测,除此之外,其实它也支持对data race及一些简单的多线程问题的检查。Valgrind工具集里面,helgrind和drd都能用来完成这种检测。你可以用valgrind --tool=helgrind
或valgrind --tool=drd
来启用它。只要应用使用的线程模型是POSIX thread(pthread),这两个工具就能进行检测。这两个工具间差别不大,下面我就基于helgrind
来介绍下用法:
先上一段有问题的示例代码:
// raceCondition.cpp
#include <pthread.h>
void *write_buffer(void *args)
{
pthread_t *buffer = static_cast<pthread_t *>(args);
*buffer = pthread_self();
pthread_exit(0);
return NULL;
}
int main()
{
pthread_t *buffer = new pthread_t[2];
pthread_t a, b;
pthread_create(&a, NULL, write_buffer, buffer);
pthread_create(&b, NULL, write_buffer, buffer);
pthread_join(a, NULL);
pthread_join(b, NULL);
delete []buffer;
return 0;
}
这段代码有一个刻意为之的问题,线程a和线程b写入了同一个缓冲区。
用Valgrind可以检测出问题:
==5697== ---Thread-Announcement------------------------------------------
==5697==
==5697== Thread #3 was created
==5697== at 0x545943E: clone (clone.S:74)
==5697== by 0x5148199: do_clone.constprop.3 (createthread.c:75)
==5697== by 0x51498BA: create_thread (createthread.c:245)
==5697== by 0x51498BA: pthread_create@@GLIBC_2.2.5 (pthread_create.c:611)
==5697== by 0x4C30E0D: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5697== by 0x400928: main (in /home/lzx/C/thread_error/a.out)
==5697==
==5697== ---Thread-Announcemen