数据竞争(data race)问题分析的利器——valgrind的Helgrind

时间:2022-06-17
本文章向大家介绍数据竞争(data race)问题分析的利器——valgrind的Helgrind,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

        数据竞争(data race)是指在非线程安全的情况下,多线程对同一个地址空间进行写操作。一般来说,我们都会通过线程同步方法来保证数据的安全,比如采用互斥量或者读写锁。但是由于某些笔误或者设计的缺陷,还是存在data race的可能性的。(转载请指明出于breaksoftware的csdn博客)

        比如下面这段代码

#define _GNU_SOURCE 1

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

static pthread_rwlock_t s_rwlock;
static int s_racy;

static void sleep_ms(const int ms) {
  struct timespec delay = { ms / 1000, (ms % 1000) * 1000 * 1000 };
  nanosleep(&delay, 0);
}

static void* thread_func(void* arg) {
  pthread_rwlock_rdlock(&s_rwlock);
  s_racy++;
  pthread_rwlock_unlock(&s_rwlock);
  sleep_ms(100);
  return 0;
}

int main(int argc, char** argv) {
  pthread_t thread1;
  pthread_t thread2;

  pthread_rwlock_init(&s_rwlock, 0);
  pthread_create(&thread1, 0, thread_func, 0);
  pthread_create(&thread2, 0, thread_func, 0);
  pthread_join(thread1, 0);
  pthread_join(thread2, 0);
  pthread_rwlock_destroy(&s_rwlock);

  fprintf(stderr, "Result: %dn", s_racy);

  return 0;
}

        线程函数thread_fun主要工作就是对全局变量s_racy进行自增。由于自增是写操作,所以需要加锁。但是第16行加的是读锁——共享锁,这就意味着其他线程也会获得该读锁。这个错误就将导致两个线程同时无协调的修改s_racy变量。

        对这个问题,我们可以使用如下指令分析

valgrind --tool=helgrind ./rdlock_race 

        最后我们会得到如下的结果显示

==5457== Possible data race during write of size 4 at 0x309078 by thread #3
==5457== Locks held: none
==5457==    at 0x108A19: thread_func (rwlock_race.c:25)
==5457==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5457==    by 0x4E496DA: start_thread (pthread_create.c:463)
==5457==    by 0x518288E: clone (clone.S:95)
==5457== 
==5457== This conflicts with a previous write of size 4 by thread #2
==5457== Locks held: none
==5457==    at 0x108A19: thread_func (rwlock_race.c:25)
==5457==    by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5457==    by 0x4E496DA: start_thread (pthread_create.c:463)
==5457==    by 0x518288E: clone (clone.S:95)
==5457==  Address 0x309078 is 0 bytes inside data symbol "s_racy"
==5457== 

        第1行显示线程3访问的0x309078地址有4个字节空间可能存在数据竞争。

        第2行显示线程3操作这个空间时没有持有独占锁。其具体操作的调用堆栈在第3到第6行体现。

        第8行显示线程2也操作了这个空间。

        第9行显示线程2操作这个空间是也没有持有独占锁。

        第14行则直接指出被操作的空间的变量名称为s_racy。

        如此我们便找到存在数据竞争的变量了。

        我们将读锁改成写锁——即将pthread_rwlock_rdlock改成pthread_rwlock_wrlock,就不会出现上述分析结果了。