Site Tools


wiki:valgrind-helgrind

Table of Contents

Valgrind helgrind

helgrind detects data races and lock-ordering problems in multithreaded programs (pthreads, OpenMP). A data race occurs when two threads access the same memory location without synchronization—at least one is a write. Even if the race doesn't cause a visible crash, it's undefined behavior and a correctness bug.

valgrind --tool=helgrind ./threaded_program

helgrind instruments all memory accesses and tracks which thread touched which location. When it sees two threads access the same location without a happens-before relationship (established by locks or synchronization primitives), it reports a race.

Example race: Two threads writing to the same counter without a lock:

int counter = 0;
 
void* increment(void* arg) {
    for (int i = 0; i < 1000; i++)
        counter++;          // data race: no lock
}

Both threads access counter without synchronization. On a multicore CPU, the increments can interleave, causing lost updates. helgrind reports:

Thread #2: Possible data race during write of size 4
  at 0x...: increment (program.c:7)
Conflicting load by thread #1
  at 0x...: increment (program.c:7)

Lock-ordering problems: helgrind also detects deadlocks caused by acquiring locks in different orders:

// Thread 1: acquire lock_a, then lock_b
// Thread 2: acquire lock_b, then lock_a

This is a potential deadlock. helgrind reports “Lock order violated” when it observes inconsistent ordering across threads.

False positives: helgrind can report races in code that's actually thread-safe due to subtle synchronization (memory barriers, atomic operations, careful locking). Use annotations to tell helgrind about custom synchronization:

#include <valgrind/helgrind.h>
 
// Tell helgrind this variable is protected by a lock
ANNOTATE_CONDVAR_LOCK_CREATE(lock);
ANNOTATE_BENIGN_RACE(&global_var, "protected by my_lock");

Suppressions are more common for helgrind than memcheck because library code sometimes has benign races or uses synchronization Valgrind doesn't understand.

Reporting: helgrind's output includes the conflicting accesses with stack traces for both threads. The pattern is the same as memcheck's format—function names, source files, line numbers.

Usage notes: helgrind is slower than memcheck but not as slow as memcheck with --track-origins. For multithreaded programs, run helgrind periodically (especially during development) to catch races. In large MPI or OpenMP codes, run helgrind on the single-threaded version first to catch memory bugs before debugging races.

wiki/valgrind-helgrind.md · Last modified: by 127.0.0.1