Skip to content

Commit 5a195f4

Browse files
committedApr 30, 2017
[tsan] Track external tags in thread traces
To make the TSan external API work with Swift and other use cases, we need to track "tags" for individual memory accesses. Since there is no space to store this information in shadow cells, let's use the thread traces for that. This patch stores the tag as an extra frame in the stack traces (by calling FuncEntry and FuncExit with the address of a registered tag), this extra frame is then stripped before printing the backtrace to stderr. Differential Revision: https://reviews.llvm.org/D32382 llvm-svn: 301777
1 parent 08989c7 commit 5a195f4

File tree

5 files changed

+62
-20
lines changed

5 files changed

+62
-20
lines changed
 

‎compiler-rt/lib/tsan/go/buildgo.sh

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set -e
55
SRCS="
66
tsan_go.cc
77
../rtl/tsan_clock.cc
8+
../rtl/tsan_external.cc
89
../rtl/tsan_flags.cc
910
../rtl/tsan_interface_atomic.cc
1011
../rtl/tsan_md5.cc

‎compiler-rt/lib/tsan/rtl/tsan_external.cc

+21-8
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@ namespace __tsan {
1717

1818
#define CALLERPC ((uptr)__builtin_return_address(0))
1919

20-
const uptr kMaxTag = 128; // Limited to 65,536, since MBlock only stores tags
21-
// as 16-bit values, see tsan_defs.h.
22-
23-
const char *registered_tags[kMaxTag];
24-
static atomic_uint32_t used_tags{1}; // Tag 0 means "no tag". NOLINT
20+
const char *registered_tags[kExternalTagMax];
21+
static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable}; // NOLINT.
2522

2623
const char *GetObjectTypeFromTag(uptr tag) {
2724
if (tag == 0) return nullptr;
@@ -30,25 +27,39 @@ const char *GetObjectTypeFromTag(uptr tag) {
3027
return registered_tags[tag];
3128
}
3229

30+
void InsertShadowStackFrameForTag(ThreadState *thr, uptr tag) {
31+
FuncEntry(thr, (uptr)&registered_tags[tag]);
32+
}
33+
34+
uptr TagFromShadowStackFrame(uptr pc) {
35+
uptr tag_count = atomic_load(&used_tags, memory_order_relaxed);
36+
void *pc_ptr = (void *)pc;
37+
if (pc_ptr < &registered_tags[0] || pc_ptr >= &registered_tags[tag_count])
38+
return 0;
39+
return (const char **)pc_ptr - &registered_tags[0];
40+
}
41+
42+
#if !SANITIZER_GO
43+
3344
typedef void(*AccessFunc)(ThreadState *, uptr, uptr, int);
3445
void ExternalAccess(void *addr, void *caller_pc, void *tag, AccessFunc access) {
3546
CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
3647
ThreadState *thr = cur_thread();
37-
thr->external_tag = (uptr)tag;
3848
if (caller_pc) FuncEntry(thr, (uptr)caller_pc);
49+
InsertShadowStackFrameForTag(thr, (uptr)tag);
3950
bool in_ignored_lib;
4051
if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) {
4152
access(thr, CALLERPC, (uptr)addr, kSizeLog1);
4253
}
54+
FuncExit(thr);
4355
if (caller_pc) FuncExit(thr);
44-
thr->external_tag = 0;
4556
}
4657

4758
extern "C" {
4859
SANITIZER_INTERFACE_ATTRIBUTE
4960
void *__tsan_external_register_tag(const char *object_type) {
5061
uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed);
51-
CHECK_LT(new_tag, kMaxTag);
62+
CHECK_LT(new_tag, kExternalTagMax);
5263
registered_tags[new_tag] = internal_strdup(object_type);
5364
return (void *)new_tag;
5465
}
@@ -78,4 +89,6 @@ void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
7889
}
7990
} // extern "C"
8091

92+
#endif // !SANITIZER_GO
93+
8194
} // namespace __tsan

‎compiler-rt/lib/tsan/rtl/tsan_report.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ static void PrintMop(const ReportMop *mop, bool first) {
164164
char thrbuf[kThreadBufSize];
165165
Printf("%s", d.Access());
166166
const char *object_type = GetObjectTypeFromTag(mop->external_tag);
167-
if (!object_type) {
167+
if (mop->external_tag == kExternalTagNone || !object_type) {
168168
Printf(" %s of size %d at %p by %s",
169169
MopDesc(first, mop->write, mop->atomic), mop->size,
170170
(void *)mop->addr, thread_name(thrbuf, mop->tid));

‎compiler-rt/lib/tsan/rtl/tsan_rtl.h

+29-5
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ struct ThreadState {
411411
bool is_dead;
412412
bool is_freeing;
413413
bool is_vptr_access;
414-
uptr external_tag;
415414
const uptr stk_addr;
416415
const uptr stk_size;
417416
const uptr tls_addr;
@@ -565,6 +564,16 @@ struct ScopedIgnoreInterceptors {
565564
}
566565
};
567566

567+
enum ExternalTag : uptr {
568+
kExternalTagNone = 0,
569+
kExternalTagFirstUserAvailable = 1,
570+
kExternalTagMax = 1024,
571+
// Don't set kExternalTagMax over 65,536, since MBlock only stores tags
572+
// as 16-bit values, see tsan_defs.h.
573+
};
574+
const char *GetObjectTypeFromTag(uptr tag);
575+
uptr TagFromShadowStackFrame(uptr pc);
576+
568577
class ScopedReport {
569578
public:
570579
explicit ScopedReport(ReportType typ);
@@ -598,17 +607,34 @@ class ScopedReport {
598607

599608
ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack);
600609
void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
601-
MutexSet *mset);
610+
MutexSet *mset, uptr *tag = nullptr);
611+
612+
// The stack could look like:
613+
// <start> | <main> | <foo> | tag | <bar>
614+
// This will extract the tag and keep:
615+
// <start> | <main> | <foo> | <bar>
616+
template<typename StackTraceTy>
617+
void ExtractTagFromStack(StackTraceTy *stack, uptr *tag = nullptr) {
618+
if (stack->size < 2) return;
619+
uptr possible_tag_pc = stack->trace[stack->size - 2];
620+
uptr possible_tag = TagFromShadowStackFrame(possible_tag_pc);
621+
if (possible_tag == kExternalTagNone) return;
622+
stack->trace_buffer[stack->size - 2] = stack->trace_buffer[stack->size - 1];
623+
stack->size -= 1;
624+
if (tag) *tag = possible_tag;
625+
}
602626

603627
template<typename StackTraceTy>
604-
void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) {
628+
void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack,
629+
uptr *tag = nullptr) {
605630
uptr size = thr->shadow_stack_pos - thr->shadow_stack;
606631
uptr start = 0;
607632
if (size + !!toppc > kStackTraceMax) {
608633
start = size + !!toppc - kStackTraceMax;
609634
size = kStackTraceMax - !!toppc;
610635
}
611636
stack->Init(&thr->shadow_stack[start], size, toppc);
637+
ExtractTagFromStack(stack, tag);
612638
}
613639

614640

@@ -646,8 +672,6 @@ bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace);
646672
bool IsExpectedReport(uptr addr, uptr size);
647673
void PrintMatchedBenignRaces();
648674

649-
const char *GetObjectTypeFromTag(uptr tag);
650-
651675
#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1
652676
# define DPrintf Printf
653677
#else

‎compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc

+10-6
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ const ReportDesc *ScopedReport::GetReport() const {
377377
}
378378

379379
void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
380-
MutexSet *mset) {
380+
MutexSet *mset, uptr *tag) {
381381
// This function restores stack trace and mutex set for the thread/epoch.
382382
// It does so by getting stack trace and mutex set at the beginning of
383383
// trace part, and then replaying the trace till the given epoch.
@@ -436,6 +436,7 @@ void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
436436
return;
437437
pos++;
438438
stk->Init(&stack[0], pos);
439+
ExtractTagFromStack(stk, tag);
439440
}
440441

441442
static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
@@ -625,16 +626,15 @@ void ReportRace(ThreadState *thr) {
625626
typ = ReportTypeVptrRace;
626627
else if (freed)
627628
typ = ReportTypeUseAfterFree;
628-
else if (thr->external_tag > 0)
629-
typ = ReportTypeExternalRace;
630629

631630
if (IsFiredSuppression(ctx, typ, addr))
632631
return;
633632

634633
const uptr kMop = 2;
635634
VarSizeStackTrace traces[kMop];
635+
uptr tags[kMop] = {kExternalTagNone};
636636
const uptr toppc = TraceTopPC(thr);
637-
ObtainCurrentStack(thr, toppc, &traces[0]);
637+
ObtainCurrentStack(thr, toppc, &traces[0], &tags[0]);
638638
if (IsFiredSuppression(ctx, typ, traces[0]))
639639
return;
640640

@@ -644,18 +644,22 @@ void ReportRace(ThreadState *thr) {
644644
MutexSet *mset2 = new(&mset_buffer[0]) MutexSet();
645645

646646
Shadow s2(thr->racy_state[1]);
647-
RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2);
647+
RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2, &tags[1]);
648648
if (IsFiredSuppression(ctx, typ, traces[1]))
649649
return;
650650

651651
if (HandleRacyStacks(thr, traces, addr_min, addr_max))
652652
return;
653653

654+
// If any of the two accesses has a tag, treat this as an "external" race.
655+
if (tags[0] != kExternalTagNone || tags[1] != kExternalTagNone)
656+
typ = ReportTypeExternalRace;
657+
654658
ThreadRegistryLock l0(ctx->thread_registry);
655659
ScopedReport rep(typ);
656660
for (uptr i = 0; i < kMop; i++) {
657661
Shadow s(thr->racy_state[i]);
658-
rep.AddMemoryAccess(addr, thr->external_tag, s, traces[i],
662+
rep.AddMemoryAccess(addr, tags[i], s, traces[i],
659663
i == 0 ? &thr->mset : mset2);
660664
}
661665

0 commit comments

Comments
 (0)
Please sign in to comment.