Index: lib/tsan/rtl/tsan_debugging.cc =================================================================== --- lib/tsan/rtl/tsan_debugging.cc +++ lib/tsan/rtl/tsan_debugging.cc @@ -25,6 +25,7 @@ if (typ == ReportTypeUseAfterFree) return "heap-use-after-free"; if (typ == ReportTypeVptrUseAfterFree) return "heap-use-after-free-vptr"; if (typ == ReportTypeExternalRace) return "external-race"; + if (typ == ReportTypeSwiftAccessRace) return "swift-access-race"; if (typ == ReportTypeThreadLeak) return "thread-leak"; if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy"; if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock"; Index: lib/tsan/rtl/tsan_external.cc =================================================================== --- lib/tsan/rtl/tsan_external.cc +++ lib/tsan/rtl/tsan_external.cc @@ -16,11 +16,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}; const char *GetObjectTypeFromTag(uptr tag) { if (tag == 0) return nullptr; @@ -33,7 +30,7 @@ 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; } @@ -57,9 +54,9 @@ CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); ThreadState *thr = cur_thread(); thr->external_tag = (uptr)tag; - FuncEntry(thr, (uptr)caller_pc); + if (caller_pc) FuncEntry(thr, (uptr)caller_pc); MemoryRead(thr, CALLERPC, (uptr)addr, kSizeLog8); - FuncExit(thr); + if (caller_pc) FuncExit(thr); thr->external_tag = 0; } @@ -68,9 +65,9 @@ CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); ThreadState *thr = cur_thread(); thr->external_tag = (uptr)tag; - FuncEntry(thr, (uptr)caller_pc); + if (caller_pc) FuncEntry(thr, (uptr)caller_pc); MemoryWrite(thr, CALLERPC, (uptr)addr, kSizeLog8); - FuncExit(thr); + if (caller_pc) FuncExit(thr); thr->external_tag = 0; } } // extern "C" Index: lib/tsan/rtl/tsan_report.h =================================================================== --- lib/tsan/rtl/tsan_report.h +++ lib/tsan/rtl/tsan_report.h @@ -25,6 +25,7 @@ ReportTypeUseAfterFree, ReportTypeVptrUseAfterFree, ReportTypeExternalRace, + ReportTypeSwiftAccessRace, ReportTypeThreadLeak, ReportTypeMutexDestroyLocked, ReportTypeMutexDoubleLock, Index: lib/tsan/rtl/tsan_report.cc =================================================================== --- lib/tsan/rtl/tsan_report.cc +++ lib/tsan/rtl/tsan_report.cc @@ -92,6 +92,8 @@ return "heap-use-after-free (virtual call vs free)"; if (typ == ReportTypeExternalRace) return "race on a library object"; + if (typ == ReportTypeSwiftAccessRace) + return "Swift access race"; if (typ == ReportTypeThreadLeak) return "thread leak"; if (typ == ReportTypeMutexDestroyLocked) @@ -158,13 +160,21 @@ return first ? (write ? "Mutating" : "Read-only") : (write ? "Previous mutating" : "Previous read-only"); } + +static const char *SwiftMopDesc(bool first) { + return first ? "Modifying access" : "Previous modifying access"; +} 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 (!object_type) { + if (mop->external_tag == kExternalTagSwiftModifyingAccess) { + Printf(" %s access at %p by %s", + SwiftMopDesc(first), (void *)mop->addr, + thread_name(thrbuf, mop->tid)); + } else 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: lib/tsan/rtl/tsan_rtl.h =================================================================== --- lib/tsan/rtl/tsan_rtl.h +++ lib/tsan/rtl/tsan_rtl.h @@ -645,6 +645,14 @@ bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); +enum ExternalTag : uptr { + kExternalTagNone = 0, + kExternalTagSwiftModifyingAccess = 1, + kExternalTagFirstUserAvailable = 128, + 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); #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 Index: lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- lib/tsan/rtl/tsan_rtl_report.cc +++ lib/tsan/rtl/tsan_rtl_report.cc @@ -625,6 +625,8 @@ typ = ReportTypeVptrRace; else if (freed) typ = ReportTypeUseAfterFree; + else if (thr->external_tag == kExternalTagSwiftModifyingAccess) + typ = ReportTypeSwiftAccessRace; else if (thr->external_tag > 0) typ = ReportTypeExternalRace; Index: lib/tsan/rtl/tsan_suppressions.cc =================================================================== --- lib/tsan/rtl/tsan_suppressions.cc +++ lib/tsan/rtl/tsan_suppressions.cc @@ -76,6 +76,8 @@ return kSuppressionRace; else if (typ == ReportTypeExternalRace) return kSuppressionRace; + else if (typ == ReportTypeSwiftAccessRace) + return kSuppressionRace; else if (typ == ReportTypeThreadLeak) return kSuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) Index: test/tsan/Darwin/external-swift.cc =================================================================== --- test/tsan/Darwin/external-swift.cc +++ test/tsan/Darwin/external-swift.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#include + +static void *tag = (void *)0x1; +extern "C" { +void *__tsan_external_register_tag(const char *object_type); +void *__tsan_external_assign_tag(void *addr, void *tag); +void __tsan_external_read(void *addr, void *caller_pc, void *tag); +void __tsan_external_write(void *addr, void *caller_pc, void *tag); +} + +int main(int argc, char *argv[]) { + fprintf(stderr, "Start.\n"); + void *opaque_object = malloc(16); + + std::thread t1([opaque_object] { + __tsan_external_write(opaque_object, nullptr, tag); + }); + std::thread t2([opaque_object] { + __tsan_external_write(opaque_object, nullptr, tag); + }); + t1.join(); + t2.join(); + + fprintf(stderr, "Stop.\n"); +} + +// CHECK: Start. +// CHECK: WARNING: ThreadSanitizer: Swift access race +// CHECK: Modifying access at {{.*}} by thread {{.*}} +// CHECK: Previous modifying access at {{.*}} by thread {{.*}} +// CHECK: Stop.