Index: lib/tsan/rtl/tsan_defs.h ./lib/tsan/rtl/tsan_defs.h =================================================================== --- lib/tsan/rtl/tsan_defs.h ./lib/tsan/rtl/tsan_defs.h +++ lib/tsan/rtl/tsan_defs.h ./lib/tsan/rtl/tsan_defs.h @@ -157,6 +157,15 @@ COMPILER_CHECK(sizeof(MBlock) == 16); +enum ExternalTag : uptr { + kExternalTagNone = 0, + kExternalTagSwiftModifyingAccess = 1, + kExternalTagFirstUserAvailable = 2, + kExternalTagMax = 1024, + // Don't set kExternalTagMax over 65,536, since MBlock only stores tags + // as 16-bit values, see tsan_defs.h. +}; + } // namespace __tsan #endif // TSAN_DEFS_H Index: lib/tsan/rtl/tsan_external.cc ./lib/tsan/rtl/tsan_external.cc =================================================================== --- lib/tsan/rtl/tsan_external.cc ./lib/tsan/rtl/tsan_external.cc +++ lib/tsan/rtl/tsan_external.cc ./lib/tsan/rtl/tsan_external.cc @@ -17,7 +17,11 @@ #define CALLERPC ((uptr)__builtin_return_address(0)) -const char *registered_tags[kExternalTagMax]; +static const char *registered_tags[kExternalTagMax] { + nullptr, + "Swift variable", + nullptr +}; static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable}; // NOLINT. const char *GetObjectTypeFromTag(uptr tag) { Index: lib/tsan/rtl/tsan_report.h ./lib/tsan/rtl/tsan_report.h =================================================================== --- lib/tsan/rtl/tsan_report.h ./lib/tsan/rtl/tsan_report.h +++ lib/tsan/rtl/tsan_report.h ./lib/tsan/rtl/tsan_report.h @@ -108,6 +108,7 @@ class ReportDesc { public: ReportType typ; + ExternalTag tag; Vector stacks; Vector mops; Vector locs; Index: lib/tsan/rtl/tsan_report.cc ./lib/tsan/rtl/tsan_report.cc =================================================================== --- lib/tsan/rtl/tsan_report.cc ./lib/tsan/rtl/tsan_report.cc +++ lib/tsan/rtl/tsan_report.cc ./lib/tsan/rtl/tsan_report.cc @@ -53,7 +53,8 @@ }; ReportDesc::ReportDesc() - : stacks(MBlockReportStack) + : tag(kExternalTagNone) + , stacks(MBlockReportStack) , mops(MBlockReportMop) , locs(MBlockReportLoc) , mutexes(MBlockReportMutex) @@ -81,38 +82,40 @@ return buf; } -static const char *ReportTypeString(ReportType typ) { +void ReportTypeString(InternalScopedString *str, ReportType typ, ExternalTag tag) { if (typ == ReportTypeRace) - return "data race"; - if (typ == ReportTypeVptrRace) - return "data race on vptr (ctor/dtor vs virtual call)"; - if (typ == ReportTypeUseAfterFree) - return "heap-use-after-free"; - if (typ == ReportTypeVptrUseAfterFree) - return "heap-use-after-free (virtual call vs free)"; - if (typ == ReportTypeExternalRace) - return "race on a library object"; - if (typ == ReportTypeThreadLeak) - return "thread leak"; - if (typ == ReportTypeMutexDestroyLocked) - return "destroy of a locked mutex"; - if (typ == ReportTypeMutexDoubleLock) - return "double lock of a mutex"; - if (typ == ReportTypeMutexInvalidAccess) - return "use of an invalid mutex (e.g. uninitialized or destroyed)"; - if (typ == ReportTypeMutexBadUnlock) - return "unlock of an unlocked mutex (or by a wrong thread)"; - if (typ == ReportTypeMutexBadReadLock) - return "read lock of a write locked mutex"; - if (typ == ReportTypeMutexBadReadUnlock) - return "read unlock of a write locked mutex"; - if (typ == ReportTypeSignalUnsafe) - return "signal-unsafe call inside of a signal"; - if (typ == ReportTypeErrnoInSignal) - return "signal handler spoils errno"; - if (typ == ReportTypeDeadlock) - return "lock-order-inversion (potential deadlock)"; - return ""; + str->append("data race"); + else if (typ == ReportTypeVptrRace) + str->append("data race on vptr (ctor/dtor vs virtual call)"); + else if (typ == ReportTypeUseAfterFree) + str->append("heap-use-after-free"); + else if (typ == ReportTypeVptrUseAfterFree) + str->append("heap-use-after-free (virtual call vs free)"); + else if (typ == ReportTypeExternalRace) { + str->append("race on "); + const char *object_type = GetObjectTypeFromTag(tag); + str->append(object_type ?: "external object"); + } + else if (typ == ReportTypeThreadLeak) + str->append("thread leak"); + else if (typ == ReportTypeMutexDestroyLocked) + str->append("destroy of a locked mutex"); + else if (typ == ReportTypeMutexDoubleLock) + str->append("double lock of a mutex"); + else if (typ == ReportTypeMutexInvalidAccess) + str->append("use of an invalid mutex (e.g. uninitialized or destroyed)"); + else if (typ == ReportTypeMutexBadUnlock) + str->append("unlock of an unlocked mutex (or by a wrong thread)"); + else if (typ == ReportTypeMutexBadReadLock) + str->append("read lock of a write locked mutex"); + else if (typ == ReportTypeMutexBadReadUnlock) + str->append("read unlock of a write locked mutex"); + else if (typ == ReportTypeSignalUnsafe) + str->append("signal-unsafe call inside of a signal"); + else if (typ == ReportTypeErrnoInSignal) + str->append("signal handler spoils errno"); + else if (typ == ReportTypeDeadlock) + str->append("lock-order-inversion (potential deadlock)"); } #if SANITIZER_MAC @@ -155,20 +158,21 @@ } static const char *ExternalMopDesc(bool first, bool write) { - return first ? (write ? "Mutating" : "Read-only") - : (write ? "Previous mutating" : "Previous read-only"); + return first ? (write ? "Modifying" : "Read-only") + : (write ? "Previous modifying" : "Previous read-only"); } static void PrintMop(const ReportMop *mop, bool first) { Decorator d; char thrbuf[kThreadBufSize]; Printf("%s", d.Access()); - const char *object_type = GetObjectTypeFromTag(mop->external_tag); - if (mop->external_tag == kExternalTagNone || !object_type) { + if (mop->external_tag == kExternalTagNone) { 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)); } else { + const char *object_type = + GetObjectTypeFromTag(mop->external_tag) ?: "external object"; Printf(" %s access of %s at %p by %s", ExternalMopDesc(first, mop->write), object_type, (void *)mop->addr, thread_name(thrbuf, mop->tid)); @@ -315,9 +319,10 @@ void PrintReport(const ReportDesc *rep) { Decorator d; Printf("==================\n"); - const char *rep_typ_str = ReportTypeString(rep->typ); + InternalScopedString rep_typ_str(1024); + ReportTypeString(&rep_typ_str, rep->typ, rep->tag); Printf("%s", d.Warning()); - Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, + Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str.data(), (int)internal_getpid()); Printf("%s", d.EndWarning()); @@ -381,7 +386,7 @@ if (ReportStack *stack = ChooseSummaryStack(rep)) { if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames)) - ReportErrorSummary(rep_typ_str, frame->info); + ReportErrorSummary(rep_typ_str.data(), frame->info); } if (common_flags()->print_module_map == 2) PrintModuleMap(); Index: lib/tsan/rtl/tsan_rtl.h ./lib/tsan/rtl/tsan_rtl.h =================================================================== --- lib/tsan/rtl/tsan_rtl.h ./lib/tsan/rtl/tsan_rtl.h +++ lib/tsan/rtl/tsan_rtl.h ./lib/tsan/rtl/tsan_rtl.h @@ -581,6 +581,7 @@ void AddSleep(u32 stack_id); void SetCount(int count); void SetType(ReportType typ); + void SetTag(ExternalTag tag); const ReportDesc *GetReport() const; @@ -646,13 +647,6 @@ bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); -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); Index: lib/tsan/rtl/tsan_rtl_report.cc ./lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- lib/tsan/rtl/tsan_rtl_report.cc ./lib/tsan/rtl/tsan_rtl_report.cc +++ lib/tsan/rtl/tsan_rtl_report.cc ./lib/tsan/rtl/tsan_rtl_report.cc @@ -376,6 +376,10 @@ rep_->typ = typ; } +void ScopedReport::SetTag(ExternalTag tag) { + rep_->tag = tag; +} + const ReportDesc *ScopedReport::GetReport() const { return rep_; } @@ -670,6 +674,7 @@ uptr tag = RetrieveTagFromStackTrace(&trace); if (tag != kExternalTagNone) { rep.SetType(ReportTypeExternalRace); + rep.SetTag((ExternalTag)tag); } Shadow s(thr->racy_state[i]); Index: test/tsan/Darwin/external-dups.cc ./test/tsan/Darwin/external-dups.cc =================================================================== --- test/tsan/Darwin/external-dups.cc ./test/tsan/Darwin/external-dups.cc +++ test/tsan/Darwin/external-dups.cc ./test/tsan/Darwin/external-dups.cc @@ -28,7 +28,7 @@ barrier_wait(&barrier); ExternalWrite(opaque_object); }); - // CHECK: WARNING: ThreadSanitizer: race on a library object + // CHECK: WARNING: ThreadSanitizer: race on HelloWorld t1.join(); t2.join(); } @@ -46,7 +46,7 @@ barrier_wait(&barrier); ExternalWrite(opaque_object); }); - // CHECK: WARNING: ThreadSanitizer: race on a library object + // CHECK: WARNING: ThreadSanitizer: race on HelloWorld t1.join(); t2.join(); } Index: test/tsan/Darwin/external-swift.cc =================================================================== --- test/tsan/Darwin/external-swift.cc +++ test/tsan/Darwin/external-swift.cc @@ -0,0 +1,92 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#include + +#import "../test.h" + +extern "C" { +void __tsan_write8(void *addr); +} + +static void *tag = (void *)0x1; + +__attribute__((no_sanitize("thread"))) +void ExternalWrite(void *addr) { + __tsan_external_write(addr, nullptr, tag); +} + +__attribute__((no_sanitize("thread"))) +void RegularWrite(void *addr) { + __tsan_write8(addr); +} + +int main(int argc, char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "Start.\n"); + // CHECK: Start. + + { + void *opaque_object = malloc(16); + std::thread t1([opaque_object] { + ExternalWrite(opaque_object); + barrier_wait(&barrier); + }); + std::thread t2([opaque_object] { + barrier_wait(&barrier); + ExternalWrite(opaque_object); + }); + // CHECK: WARNING: ThreadSanitizer: race on Swift variable + // CHECK: Modifying access of Swift variable at {{.*}} by thread {{.*}} + // CHECK: Previous modifying access of Swift variable at {{.*}} by thread {{.*}} + // CHECK: SUMMARY: ThreadSanitizer: race on Swift variable + t1.join(); + t2.join(); + } + + fprintf(stderr, "external+external test done.\n"); + // CHECK: external+external test done. + + { + void *opaque_object = malloc(16); + std::thread t1([opaque_object] { + ExternalWrite(opaque_object); + barrier_wait(&barrier); + }); + std::thread t2([opaque_object] { + barrier_wait(&barrier); + RegularWrite(opaque_object); + }); + // CHECK: WARNING: ThreadSanitizer: race on Swift variable + // CHECK: Write of size 8 at {{.*}} by thread {{.*}} + // CHECK: Previous modifying access of Swift variable at {{.*}} by thread {{.*}} + // CHECK: SUMMARY: ThreadSanitizer: race on Swift variable + t1.join(); + t2.join(); + } + + fprintf(stderr, "external+regular test done.\n"); + // CHECK: external+regular test done. + + { + void *opaque_object = malloc(16); + std::thread t1([opaque_object] { + RegularWrite(opaque_object); + barrier_wait(&barrier); + }); + std::thread t2([opaque_object] { + barrier_wait(&barrier); + ExternalWrite(opaque_object); + }); + // CHECK: WARNING: ThreadSanitizer: race on Swift variable + // CHECK: Modifying access of Swift variable at {{.*}} by thread {{.*}} + // CHECK: Previous write of size 8 at {{.*}} by thread {{.*}} + // CHECK: SUMMARY: ThreadSanitizer: race on Swift variable + t1.join(); + t2.join(); + } + + fprintf(stderr, "regular+external test done.\n"); + // CHECK: regular+external test done. +} + Index: test/tsan/Darwin/external.cc ./test/tsan/Darwin/external.cc =================================================================== --- test/tsan/Darwin/external.cc ./test/tsan/Darwin/external.cc +++ test/tsan/Darwin/external.cc ./test/tsan/Darwin/external.cc @@ -67,13 +67,14 @@ // TEST2-NOT: WARNING: ThreadSanitizer - // TEST3: WARNING: ThreadSanitizer: race on a library object - // TEST3: {{Mutating|read-only}} access of MyLibrary::MyObject at + // TEST3: WARNING: ThreadSanitizer: race on MyLibrary::MyObject + // TEST3: {{Modifying|read-only}} access of MyLibrary::MyObject at // TEST3: {{ObjectWrite|ObjectRead}} - // TEST3: Previous {{mutating|read-only}} access of MyLibrary::MyObject at + // TEST3: Previous {{modifying|read-only}} access of MyLibrary::MyObject at // TEST3: {{ObjectWrite|ObjectRead}} // TEST3: Location is MyLibrary::MyObject of size 16 at // TEST3: {{ObjectCreate}} + // TEST3: SUMMARY: ThreadSanitizer: race on MyLibrary::MyObject {{.*}} in {{ObjectWrite|ObjectRead}} fprintf(stderr, "RW test done\n"); // CHECK: RW test done @@ -90,13 +91,14 @@ // TEST2-NOT: WARNING: ThreadSanitizer - // TEST3: WARNING: ThreadSanitizer: race on a library object - // TEST3: Mutating access of MyLibrary::MyObject at + // TEST3: WARNING: ThreadSanitizer: race on MyLibrary::MyObject + // TEST3: Modifying access of MyLibrary::MyObject at // TEST3: {{ObjectWrite|ObjectWriteAnother}} - // TEST3: Previous mutating access of MyLibrary::MyObject at + // TEST3: Previous modifying access of MyLibrary::MyObject at // TEST3: {{ObjectWrite|ObjectWriteAnother}} // TEST3: Location is MyLibrary::MyObject of size 16 at // TEST3: {{ObjectCreate}} + // TEST3: SUMMARY: ThreadSanitizer: race on MyLibrary::MyObject {{.*}} in {{ObjectWrite|ObjectWriteAnother}} fprintf(stderr, "WW test done\n"); // CHECK: WW test done