Skip to content

Commit 7358a11

Browse files
committed
[TSan] Optimize handling of racy address
This patch splits the handling of racy address and racy stack into separate functions. If a race was already reported for the address, we can avoid the cost for collecting the involved stacks. This patch also removes the race condition in storing the racy address / racy stack. This race condition allowed all threads to report the race. This patch changes the transitive suppression of reports. Previously suppression could transitively chain memory location and racy stacks. Now racy memory and racy stack are separate suppressions. Commit again, now with fixed tests. Reviewed by: dvyukov Differential Revision: https://reviews.llvm.org/D83625
1 parent fc2317f commit 7358a11

File tree

2 files changed

+87
-67
lines changed

2 files changed

+87
-67
lines changed

compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp

Lines changed: 50 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -439,65 +439,61 @@ void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
439439
ExtractTagFromStack(stk, tag);
440440
}
441441

442-
static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
443-
uptr addr_min, uptr addr_max) {
444-
bool equal_stack = false;
445-
RacyStacks hash;
446-
bool equal_address = false;
447-
RacyAddress ra0 = {addr_min, addr_max};
448-
{
449-
ReadLock lock(&ctx->racy_mtx);
450-
if (flags()->suppress_equal_stacks) {
451-
hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
452-
hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
453-
for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
454-
if (hash == ctx->racy_stacks[i]) {
455-
VPrintf(2,
456-
"ThreadSanitizer: suppressing report as doubled (stack)\n");
457-
equal_stack = true;
458-
break;
459-
}
460-
}
461-
}
462-
if (flags()->suppress_equal_addresses) {
463-
for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
464-
RacyAddress ra2 = ctx->racy_addresses[i];
465-
uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
466-
uptr minend = min(ra0.addr_max, ra2.addr_max);
467-
if (maxbeg < minend) {
468-
VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n");
469-
equal_address = true;
470-
break;
471-
}
472-
}
442+
static bool FindRacyStacks(const RacyStacks &hash) {
443+
for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
444+
if (hash == ctx->racy_stacks[i]) {
445+
VPrintf(2, "ThreadSanitizer: suppressing report as doubled (stack)\n");
446+
return true;
473447
}
474448
}
475-
if (!equal_stack && !equal_address)
449+
return false;
450+
}
451+
452+
static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2]) {
453+
if (!flags()->suppress_equal_stacks)
476454
return false;
477-
if (!equal_stack) {
478-
Lock lock(&ctx->racy_mtx);
479-
ctx->racy_stacks.PushBack(hash);
480-
}
481-
if (!equal_address) {
482-
Lock lock(&ctx->racy_mtx);
483-
ctx->racy_addresses.PushBack(ra0);
455+
RacyStacks hash;
456+
hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
457+
hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
458+
{
459+
ReadLock lock(&ctx->racy_mtx);
460+
if (FindRacyStacks(hash))
461+
return true;
484462
}
485-
return true;
463+
Lock lock(&ctx->racy_mtx);
464+
if (FindRacyStacks(hash))
465+
return true;
466+
ctx->racy_stacks.PushBack(hash);
467+
return false;
486468
}
487469

488-
static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
489-
uptr addr_min, uptr addr_max) {
490-
Lock lock(&ctx->racy_mtx);
491-
if (flags()->suppress_equal_stacks) {
492-
RacyStacks hash;
493-
hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
494-
hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
495-
ctx->racy_stacks.PushBack(hash);
470+
static bool FindRacyAddress(const RacyAddress &ra0) {
471+
for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
472+
RacyAddress ra2 = ctx->racy_addresses[i];
473+
uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
474+
uptr minend = min(ra0.addr_max, ra2.addr_max);
475+
if (maxbeg < minend) {
476+
VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n");
477+
return true;
478+
}
496479
}
497-
if (flags()->suppress_equal_addresses) {
498-
RacyAddress ra0 = {addr_min, addr_max};
499-
ctx->racy_addresses.PushBack(ra0);
480+
return false;
481+
}
482+
483+
static bool HandleRacyAddress(ThreadState *thr, uptr addr_min, uptr addr_max) {
484+
if (!flags()->suppress_equal_addresses)
485+
return false;
486+
RacyAddress ra0 = {addr_min, addr_max};
487+
{
488+
ReadLock lock(&ctx->racy_mtx);
489+
if (FindRacyAddress(ra0))
490+
return true;
500491
}
492+
Lock lock(&ctx->racy_mtx);
493+
if (FindRacyAddress(ra0))
494+
return true;
495+
ctx->racy_addresses.PushBack(ra0);
496+
return false;
501497
}
502498

503499
bool OutputReport(ThreadState *thr, const ScopedReport &srep) {
@@ -618,6 +614,8 @@ void ReportRace(ThreadState *thr) {
618614
if (IsExpectedReport(addr_min, addr_max - addr_min))
619615
return;
620616
}
617+
if (HandleRacyAddress(thr, addr_min, addr_max))
618+
return;
621619

622620
ReportType typ = ReportTypeRace;
623621
if (thr->is_vptr_access && freed)
@@ -668,7 +666,7 @@ void ReportRace(ThreadState *thr) {
668666
if (IsFiredSuppression(ctx, typ, traces[1]))
669667
return;
670668

671-
if (HandleRacyStacks(thr, traces, addr_min, addr_max))
669+
if (HandleRacyStacks(thr, traces))
672670
return;
673671

674672
// If any of the accesses has a tag, treat this as an "external" race.
@@ -711,7 +709,6 @@ void ReportRace(ThreadState *thr) {
711709
if (!OutputReport(thr, rep))
712710
return;
713711

714-
AddRacyStacks(thr, traces, addr_min, addr_max);
715712
}
716713

717714
void PrintCurrentStack(ThreadState *thr, uptr pc) {

compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cpp

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <unistd.h>
2828
#include <errno.h>
2929

30+
#define CALLERPC (__builtin_return_address(0))
31+
3032
using namespace __tsan;
3133

3234
static __thread bool expect_report;
@@ -249,22 +251,42 @@ void ScopedThread::Impl::HandleEvent(Event *ev) {
249251
switch (ev->type) {
250252
case Event::READ:
251253
case Event::WRITE: {
252-
void (*tsan_mop)(void *addr) = 0;
254+
void (*tsan_mop)(void *addr, void *pc) = 0;
253255
if (ev->type == Event::READ) {
254256
switch (ev->arg /*size*/) {
255-
case 1: tsan_mop = __tsan_read1; break;
256-
case 2: tsan_mop = __tsan_read2; break;
257-
case 4: tsan_mop = __tsan_read4; break;
258-
case 8: tsan_mop = __tsan_read8; break;
259-
case 16: tsan_mop = __tsan_read16; break;
257+
case 1:
258+
tsan_mop = __tsan_read1_pc;
259+
break;
260+
case 2:
261+
tsan_mop = __tsan_read2_pc;
262+
break;
263+
case 4:
264+
tsan_mop = __tsan_read4_pc;
265+
break;
266+
case 8:
267+
tsan_mop = __tsan_read8_pc;
268+
break;
269+
case 16:
270+
tsan_mop = __tsan_read16_pc;
271+
break;
260272
}
261273
} else {
262274
switch (ev->arg /*size*/) {
263-
case 1: tsan_mop = __tsan_write1; break;
264-
case 2: tsan_mop = __tsan_write2; break;
265-
case 4: tsan_mop = __tsan_write4; break;
266-
case 8: tsan_mop = __tsan_write8; break;
267-
case 16: tsan_mop = __tsan_write16; break;
275+
case 1:
276+
tsan_mop = __tsan_write1_pc;
277+
break;
278+
case 2:
279+
tsan_mop = __tsan_write2_pc;
280+
break;
281+
case 4:
282+
tsan_mop = __tsan_write4_pc;
283+
break;
284+
case 8:
285+
tsan_mop = __tsan_write8_pc;
286+
break;
287+
case 16:
288+
tsan_mop = __tsan_write16_pc;
289+
break;
268290
}
269291
}
270292
CHECK_NE(tsan_mop, 0);
@@ -274,7 +296,7 @@ void ScopedThread::Impl::HandleEvent(Event *ev) {
274296
const int ErrCode = ECHRNG;
275297
#endif
276298
errno = ErrCode;
277-
tsan_mop(ev->ptr);
299+
tsan_mop(ev->ptr, (void *)ev->arg2);
278300
CHECK_EQ(ErrCode, errno); // In no case must errno be changed.
279301
break;
280302
}
@@ -327,7 +349,7 @@ void ScopedThread::Impl::HandleEvent(Event *ev) {
327349
}
328350

329351
void *ScopedThread::Impl::ScopedThreadCallback(void *arg) {
330-
__tsan_func_entry(__builtin_return_address(0));
352+
__tsan_func_entry(CALLERPC);
331353
Impl *impl = (Impl*)arg;
332354
for (;;) {
333355
Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire);
@@ -392,7 +414,8 @@ void ScopedThread::Detach() {
392414

393415
void ScopedThread::Access(void *addr, bool is_write,
394416
int size, bool expect_race) {
395-
Event event(is_write ? Event::WRITE : Event::READ, addr, size);
417+
Event event(is_write ? Event::WRITE : Event::READ, addr, size,
418+
(uptr)CALLERPC);
396419
if (expect_race)
397420
event.ExpectReport(ReportTypeRace);
398421
impl_->send(&event);

0 commit comments

Comments
 (0)