diff --git a/compiler-rt/lib/tsan/rtl/tsan_flags.inc b/compiler-rt/lib/tsan/rtl/tsan_flags.inc index b1691452d022..32cf3bbf152d 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_flags.inc +++ b/compiler-rt/lib/tsan/rtl/tsan_flags.inc @@ -81,3 +81,6 @@ TSAN_FLAG(bool, ignore_noninstrumented_modules, SANITIZER_MAC ? true : false, "modules.") TSAN_FLAG(bool, shared_ptr_interceptor, true, "Track atomic reference counting in libc++ shared_ptr and weak_ptr.") +TSAN_FLAG(bool, print_full_thread_history, false, + "If set, prints thread creation stacks for the threads involved in " + "the report and their ancestors up to the main thread.") diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp index 8f28b75f8f37..4cf8816489df 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp @@ -823,6 +823,18 @@ void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old, rep.AddLocation(addr_min, addr_max - addr_min); + if (flags()->print_full_thread_history) { + const ReportDesc *rep_desc = rep.GetReport(); + for (uptr i = 0; i < rep_desc->threads.Size(); i++) { + Tid parent_tid = rep_desc->threads[i]->parent_tid; + if (parent_tid == kMainTid || parent_tid == kInvalidTid) + continue; + ThreadContext *parent_tctx = static_cast( + ctx->thread_registry.GetThreadLocked(parent_tid)); + rep.AddThread(parent_tctx); + } + } + #if !SANITIZER_GO if (!((typ0 | typ1) & kAccessFree) && s[1].epoch() <= thr->last_sleep_clock.Get(s[1].sid())) diff --git a/compiler-rt/test/tsan/print_full_thread_history.cpp b/compiler-rt/test/tsan/print_full_thread_history.cpp new file mode 100644 index 000000000000..ff341f6bfae8 --- /dev/null +++ b/compiler-rt/test/tsan/print_full_thread_history.cpp @@ -0,0 +1,40 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=print_full_thread_history=true %deflake %run %t 2>&1 | FileCheck %s + +#include "test.h" + +int Global; + +void *Thread2(void *x) { + barrier_wait(&barrier); + Global++; + return NULL; +} + +void *Thread3(void *x) { + Global--; + barrier_wait(&barrier); + return NULL; +} + +void *Thread1(void *x) { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread2, NULL); + pthread_create(&t[1], NULL, Thread3, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return NULL; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + pthread_join(t, NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T2 {{.*}} created by thread T1 at +// CHECK: Thread T3 {{.*}} created by thread T1 at: +// CHECK: Thread T1 {{.*}} created by main thread at: +// CHECK: SUMMARY: ThreadSanitizer: data race{{.*}}