Index: compiler-rt/trunk/lib/tsan/go/buildgo.sh =================================================================== --- compiler-rt/trunk/lib/tsan/go/buildgo.sh +++ compiler-rt/trunk/lib/tsan/go/buildgo.sh @@ -5,6 +5,7 @@ SRCS=" tsan_go.cc ../rtl/tsan_clock.cc + ../rtl/tsan_external.cc ../rtl/tsan_flags.cc ../rtl/tsan_interface_atomic.cc ../rtl/tsan_md5.cc Index: compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc @@ -17,11 +17,8 @@ #define CALLERPC ((uptr)__builtin_return_address(0)) -const uptr kMaxTag = 128; // Limited to 65,536, since MBlock only stores tags - // as 16-bit values, see tsan_defs.h. - -const char *registered_tags[kMaxTag]; -static atomic_uint32_t used_tags{1}; // Tag 0 means "no tag". NOLINT +const char *registered_tags[kExternalTagMax]; +static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable}; // NOLINT. const char *GetObjectTypeFromTag(uptr tag) { if (tag == 0) return nullptr; @@ -30,25 +27,39 @@ return registered_tags[tag]; } +void InsertShadowStackFrameForTag(ThreadState *thr, uptr tag) { + FuncEntry(thr, (uptr)®istered_tags[tag]); +} + +uptr TagFromShadowStackFrame(uptr pc) { + uptr tag_count = atomic_load(&used_tags, memory_order_relaxed); + void *pc_ptr = (void *)pc; + if (pc_ptr < ®istered_tags[0] || pc_ptr >= ®istered_tags[tag_count]) + return 0; + return (const char **)pc_ptr - ®istered_tags[0]; +} + +#if !SANITIZER_GO + typedef void(*AccessFunc)(ThreadState *, uptr, uptr, int); void ExternalAccess(void *addr, void *caller_pc, void *tag, AccessFunc access) { CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); ThreadState *thr = cur_thread(); - thr->external_tag = (uptr)tag; if (caller_pc) FuncEntry(thr, (uptr)caller_pc); + InsertShadowStackFrameForTag(thr, (uptr)tag); bool in_ignored_lib; if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) { access(thr, CALLERPC, (uptr)addr, kSizeLog1); } + FuncExit(thr); if (caller_pc) FuncExit(thr); - thr->external_tag = 0; } extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void *__tsan_external_register_tag(const char *object_type) { uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed); - CHECK_LT(new_tag, kMaxTag); + CHECK_LT(new_tag, kExternalTagMax); registered_tags[new_tag] = internal_strdup(object_type); return (void *)new_tag; } @@ -78,4 +89,6 @@ } } // extern "C" +#endif // !SANITIZER_GO + } // namespace __tsan Index: compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc @@ -164,7 +164,7 @@ char thrbuf[kThreadBufSize]; Printf("%s", d.Access()); const char *object_type = GetObjectTypeFromTag(mop->external_tag); - if (!object_type) { + if (mop->external_tag == kExternalTagNone || !object_type) { Printf(" %s of size %d at %p by %s", MopDesc(first, mop->write, mop->atomic), mop->size, (void *)mop->addr, thread_name(thrbuf, mop->tid)); Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h @@ -411,7 +411,6 @@ bool is_dead; bool is_freeing; bool is_vptr_access; - uptr external_tag; const uptr stk_addr; const uptr stk_size; const uptr tls_addr; @@ -565,6 +564,16 @@ } }; +enum ExternalTag : uptr { + kExternalTagNone = 0, + kExternalTagFirstUserAvailable = 1, + kExternalTagMax = 1024, + // Don't set kExternalTagMax over 65,536, since MBlock only stores tags + // as 16-bit values, see tsan_defs.h. +}; +const char *GetObjectTypeFromTag(uptr tag); +uptr TagFromShadowStackFrame(uptr pc); + class ScopedReport { public: explicit ScopedReport(ReportType typ); @@ -598,10 +607,26 @@ ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack); void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, - MutexSet *mset); + MutexSet *mset, uptr *tag = nullptr); + +// The stack could look like: +// |
| | tag | +// This will extract the tag and keep: +// |
| | +template +void ExtractTagFromStack(StackTraceTy *stack, uptr *tag = nullptr) { + if (stack->size < 2) return; + uptr possible_tag_pc = stack->trace[stack->size - 2]; + uptr possible_tag = TagFromShadowStackFrame(possible_tag_pc); + if (possible_tag == kExternalTagNone) return; + stack->trace_buffer[stack->size - 2] = stack->trace_buffer[stack->size - 1]; + stack->size -= 1; + if (tag) *tag = possible_tag; +} template -void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) { +void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack, + uptr *tag = nullptr) { uptr size = thr->shadow_stack_pos - thr->shadow_stack; uptr start = 0; if (size + !!toppc > kStackTraceMax) { @@ -609,6 +634,7 @@ size = kStackTraceMax - !!toppc; } stack->Init(&thr->shadow_stack[start], size, toppc); + ExtractTagFromStack(stack, tag); } @@ -646,8 +672,6 @@ bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); -const char *GetObjectTypeFromTag(uptr tag); - #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 # define DPrintf Printf #else Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc @@ -377,7 +377,7 @@ } void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, - MutexSet *mset) { + MutexSet *mset, uptr *tag) { // This function restores stack trace and mutex set for the thread/epoch. // It does so by getting stack trace and mutex set at the beginning of // trace part, and then replaying the trace till the given epoch. @@ -436,6 +436,7 @@ return; pos++; stk->Init(&stack[0], pos); + ExtractTagFromStack(stk, tag); } static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], @@ -625,16 +626,15 @@ typ = ReportTypeVptrRace; else if (freed) typ = ReportTypeUseAfterFree; - else if (thr->external_tag > 0) - typ = ReportTypeExternalRace; if (IsFiredSuppression(ctx, typ, addr)) return; const uptr kMop = 2; VarSizeStackTrace traces[kMop]; + uptr tags[kMop] = {kExternalTagNone}; const uptr toppc = TraceTopPC(thr); - ObtainCurrentStack(thr, toppc, &traces[0]); + ObtainCurrentStack(thr, toppc, &traces[0], &tags[0]); if (IsFiredSuppression(ctx, typ, traces[0])) return; @@ -644,18 +644,22 @@ MutexSet *mset2 = new(&mset_buffer[0]) MutexSet(); Shadow s2(thr->racy_state[1]); - RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2); + RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2, &tags[1]); if (IsFiredSuppression(ctx, typ, traces[1])) return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; + // If any of the two accesses has a tag, treat this as an "external" race. + if (tags[0] != kExternalTagNone || tags[1] != kExternalTagNone) + typ = ReportTypeExternalRace; + ThreadRegistryLock l0(ctx->thread_registry); ScopedReport rep(typ); for (uptr i = 0; i < kMop; i++) { Shadow s(thr->racy_state[i]); - rep.AddMemoryAccess(addr, thr->external_tag, s, traces[i], + rep.AddMemoryAccess(addr, tags[i], s, traces[i], i == 0 ? &thr->mset : mset2); }