From ab02680b5aee81fbcf564e30891e8e2089d513e1 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 14 Jul 2022 16:58:07 +0200 Subject: [PATCH] tsan: fix a bug in trace part switching Callers of TraceSwitchPart expect that TraceAcquire will always succeed after the call. It's possible that TryTraceFunc/TraceMutexLock in TraceSwitchPart that restore the current stack/mutexset filled the trace part exactly up to the TracePart::kAlignment gap and the next TraceAcquire won't succeed. Skip the alignment gap after writing initial stack/mutexset to avoid that. Reviewed By: melver Differential Revision: https://reviews.llvm.org/D129777 --- compiler-rt/lib/tsan/rtl/tsan_rtl.cpp | 9 +++++++++ compiler-rt/lib/tsan/tests/unit/tsan_trace_test.cpp | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp index 607f373871b4..ef073ca32e57 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp @@ -951,6 +951,15 @@ void TraceSwitchPartImpl(ThreadState* thr) { TraceMutexLock(thr, d.write ? EventType::kLock : EventType::kRLock, 0, d.addr, d.stack_id); } + // Callers of TraceSwitchPart expect that TraceAcquire will always succeed + // after the call. It's possible that TryTraceFunc/TraceMutexLock above + // filled the trace part exactly up to the TracePart::kAlignment gap + // and the next TraceAcquire won't succeed. Skip the gap to avoid that. + EventFunc *ev; + if (!TraceAcquire(thr, &ev)) { + CHECK(TraceSkipGap(thr)); + CHECK(TraceAcquire(thr, &ev)); + } { Lock lock(&ctx->slot_mtx); // There is a small chance that the slot may be not queued at this point. diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_trace_test.cpp b/compiler-rt/lib/tsan/tests/unit/tsan_trace_test.cpp index 9f006ab09833..0d354db548c4 100644 --- a/compiler-rt/lib/tsan/tests/unit/tsan_trace_test.cpp +++ b/compiler-rt/lib/tsan/tests/unit/tsan_trace_test.cpp @@ -243,6 +243,18 @@ TRACE_TEST(Trace, MultiPart) { CHECK_EQ(mset.Get(1).count, 1); } +TRACE_TEST(Trace, DeepSwitch) { + ThreadArray<1> thr; + for (int i = 0; i < 2000; i++) { + FuncEntry(thr, 0x1000); + const uptr kEvents = sizeof(TracePart) / sizeof(Event); + for (uptr i = 0; i < kEvents; i++) { + TraceMutexLock(thr, EventType::kLock, 0x4000, 0x5000, 0x6000); + TraceMutexUnlock(thr, 0x5000); + } + } +} + void CheckTraceState(uptr count, uptr finished, uptr excess, uptr recycle) { Lock l(&ctx->slot_mtx); Printf("CheckTraceState(%zu/%zu, %zu/%zu, %zu/%zu, %zu/%zu)\n",