tsan_report.cc revision 7c9150579ed0278492f51cc8434b1d63a44b9bd1
1603c4be006d8c53905d736bf1f19a49f5ce98276Alexey Samsonov//===-- tsan_report.cc ----------------------------------------------------===//
27ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//
37ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//                     The LLVM Compiler Infrastructure
47ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//
57ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// This file is distributed under the University of Illinois Open Source
67ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// License. See LICENSE.TXT for details.
77ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//
87ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//===----------------------------------------------------------------------===//
97ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//
107ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany// This file is a part of ThreadSanitizer (TSan), a race detector.
117ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//
127ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany//===----------------------------------------------------------------------===//
137ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_report.h"
147ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_platform.h"
157ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany#include "tsan_rtl.h"
166d1862363c88c183b0ed7740fca876342cf0474bStephen Hines#include "sanitizer_common/sanitizer_placement_new.h"
17a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany#include "sanitizer_common/sanitizer_report_decorator.h"
186d1862363c88c183b0ed7740fca876342cf0474bStephen Hines#include "sanitizer_common/sanitizer_stacktrace_printer.h"
197ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
207ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanynamespace __tsan {
217ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
2286277eb844c4983c81de62d7c050e92fe7155788Stephen HinesReportStack::ReportStack() : frames(nullptr), suppressable(false) {}
236d1862363c88c183b0ed7740fca876342cf0474bStephen Hines
2486277eb844c4983c81de62d7c050e92fe7155788Stephen HinesReportStack *ReportStack::New() {
256d1862363c88c183b0ed7740fca876342cf0474bStephen Hines  void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
2686277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  return new(mem) ReportStack();
276d1862363c88c183b0ed7740fca876342cf0474bStephen Hines}
286d1862363c88c183b0ed7740fca876342cf0474bStephen Hines
296d1862363c88c183b0ed7740fca876342cf0474bStephen HinesReportLocation::ReportLocation(ReportLocationType type)
306d1862363c88c183b0ed7740fca876342cf0474bStephen Hines    : type(type), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
316d1862363c88c183b0ed7740fca876342cf0474bStephen Hines      fd(0), suppressable(false), stack(nullptr) {}
326d1862363c88c183b0ed7740fca876342cf0474bStephen Hines
336d1862363c88c183b0ed7740fca876342cf0474bStephen HinesReportLocation *ReportLocation::New(ReportLocationType type) {
346d1862363c88c183b0ed7740fca876342cf0474bStephen Hines  void *mem = internal_alloc(MBlockReportStack, sizeof(ReportLocation));
356d1862363c88c183b0ed7740fca876342cf0474bStephen Hines  return new(mem) ReportLocation(type);
366d1862363c88c183b0ed7740fca876342cf0474bStephen Hines}
376d1862363c88c183b0ed7740fca876342cf0474bStephen Hines
386a211c5814e25d6745a5058cc0e499e5235d3821Stephen Hinesclass Decorator: public __sanitizer::SanitizerCommonDecorator {
39a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany public:
406a211c5814e25d6745a5058cc0e499e5235d3821Stephen Hines  Decorator() : SanitizerCommonDecorator() { }
41a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *Warning()    { return Red(); }
42a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *EndWarning() { return Default(); }
43a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *Access()     { return Blue(); }
44a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *EndAccess()  { return Default(); }
45a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *ThreadDescription()    { return Cyan(); }
46a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *EndThreadDescription() { return Default(); }
47a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *Location()   { return Green(); }
48a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *EndLocation() { return Default(); }
49a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *Sleep()   { return Yellow(); }
50a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *EndSleep() { return Default(); }
51a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *Mutex()   { return Magenta(); }
52a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  const char *EndMutex() { return Default(); }
53a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany};
54a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany
557ac41484ea322e0ea5774df681660269f5dc321eKostya SerebryanyReportDesc::ReportDesc()
567ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    : stacks(MBlockReportStack)
577ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    , mops(MBlockReportMop)
587ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    , locs(MBlockReportLoc)
597ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    , mutexes(MBlockReportMutex)
60848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukov    , threads(MBlockReportThread)
612d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    , unique_tids(MBlockReportThread)
624536cb1fa7734133f404acb413589d7a6d314f4aDmitry Vyukov    , sleep()
634536cb1fa7734133f404acb413589d7a6d314f4aDmitry Vyukov    , count() {
647ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
657ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
66ad9da372f962495b3487685232d09390be841b1cDmitry VyukovReportMop::ReportMop()
67ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov    : mset(MBlockReportMutex) {
68ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov}
69ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov
707ac41484ea322e0ea5774df681660269f5dc321eKostya SerebryanyReportDesc::~ReportDesc() {
71aecf2e5756c6a0de7c146bef67a6e338c7017d55Dmitry Vyukov  // FIXME(dvyukov): it must be leaking a lot of memory.
727ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
737ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
7486277eb844c4983c81de62d7c050e92fe7155788Stephen Hines#ifndef SANITIZER_GO
75c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov
76da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukovconst int kThreadBufSize = 32;
77da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukovconst char *thread_name(char *buf, int tid) {
78da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov  if (tid == 0)
79da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov    return "main thread";
80da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov  internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
81da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov  return buf;
82da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov}
83da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov
842f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryanystatic const char *ReportTypeString(ReportType typ) {
857ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  if (typ == ReportTypeRace)
862f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return "data race";
870dc47b652dfbe0d61d153eded02bae9487a7b539Dmitry Vyukov  if (typ == ReportTypeVptrRace)
880dc47b652dfbe0d61d153eded02bae9487a7b539Dmitry Vyukov    return "data race on vptr (ctor/dtor vs virtual call)";
892f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (typ == ReportTypeUseAfterFree)
902f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return "heap-use-after-free";
916d1862363c88c183b0ed7740fca876342cf0474bStephen Hines  if (typ == ReportTypeVptrUseAfterFree)
926d1862363c88c183b0ed7740fca876342cf0474bStephen Hines    return "heap-use-after-free (virtual call vs free)";
932f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (typ == ReportTypeThreadLeak)
942f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return "thread leak";
952f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (typ == ReportTypeMutexDestroyLocked)
962f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return "destroy of a locked mutex";
972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (typ == ReportTypeMutexDoubleLock)
982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    return "double lock of a mutex";
992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (typ == ReportTypeMutexBadUnlock)
1002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    return "unlock of an unlocked mutex (or by a wrong thread)";
1012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (typ == ReportTypeMutexBadReadLock)
1022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    return "read lock of a write locked mutex";
1032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (typ == ReportTypeMutexBadReadUnlock)
1042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    return "read unlock of a write locked mutex";
1052f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (typ == ReportTypeSignalUnsafe)
1062f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return "signal-unsafe call inside of a signal";
1072f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (typ == ReportTypeErrnoInSignal)
1082f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return "signal handler spoils errno";
1092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (typ == ReportTypeDeadlock)
1102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    return "lock-order-inversion (potential deadlock)";
1112f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  return "";
1127ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
1137ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
1141da1056127d1dbcacdd035eb4149257848f7c4dfDmitry Vyukovvoid PrintStack(const ReportStack *ent) {
11586277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  if (ent == 0 || ent->frames == 0) {
11695f50b126cebecc0165fbf6e4c8209c640836271Dmitry Vyukov    Printf("    [failed to restore the stack]\n\n");
117dc2a27ecd65079f1916f5ab53ec646f9a9e61349Dmitry Vyukov    return;
118dc2a27ecd65079f1916f5ab53ec646f9a9e61349Dmitry Vyukov  }
11986277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  SymbolizedStack *frame = ent->frames;
12086277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
1216d1862363c88c183b0ed7740fca876342cf0474bStephen Hines    InternalScopedString res(2 * GetPageSizeCached());
12286277eb844c4983c81de62d7c050e92fe7155788Stephen Hines    RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
1236d1862363c88c183b0ed7740fca876342cf0474bStephen Hines                common_flags()->strip_path_prefix, "__interceptor_");
1246d1862363c88c183b0ed7740fca876342cf0474bStephen Hines    Printf("%s\n", res.data());
1257ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  }
126b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("\n");
1277ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
1287ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
129ad9da372f962495b3487685232d09390be841b1cDmitry Vyukovstatic void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
130ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  for (uptr i = 0; i < mset.Size(); i++) {
131ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov    if (i == 0)
132ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov      Printf(" (mutexes:");
133ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov    const ReportMopMutex m = mset[i];
134ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov    Printf(" %s M%llu", m.write ? "write" : "read", m.id);
135ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov    Printf(i == mset.Size() - 1 ? ")" : ",");
136ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  }
137ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov}
138ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov
1390a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukovstatic const char *MopDesc(bool first, bool write, bool atomic) {
1400a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukov  return atomic ? (first ? (write ? "Atomic write" : "Atomic read")
1410a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukov                : (write ? "Previous atomic write" : "Previous atomic read"))
1420a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukov                : (first ? (write ? "Write" : "Read")
1430a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukov                : (write ? "Previous write" : "Previous read"));
1440a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukov}
1450a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukov
1467ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic void PrintMop(const ReportMop *mop, bool first) {
147a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Decorator d;
148da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov  char thrbuf[kThreadBufSize];
149a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.Access());
150da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov  Printf("  %s of size %d at %p by %s",
1510a07b354fe95d50911c620b42fc031868ef15cc1Dmitry Vyukov      MopDesc(first, mop->write, mop->atomic),
152da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov      mop->size, (void*)mop->addr,
153da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov      thread_name(thrbuf, mop->tid));
154ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  PrintMutexSet(mop->mset);
155ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  Printf(":\n");
156a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.EndAccess());
1577ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  PrintStack(mop->stack);
1587ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
1597ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
1607ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic void PrintLocation(const ReportLocation *loc) {
161a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Decorator d;
162c2234cd922bbd94e276e0bebb08004d63cbc5cf2Dmitry Vyukov  char thrbuf[kThreadBufSize];
163a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  bool print_stack = false;
164a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.Location());
1657ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  if (loc->type == ReportLocationGlobal) {
1666d1862363c88c183b0ed7740fca876342cf0474bStephen Hines    const DataInfo &global = loc->global;
167c842dabb53cc2101cc5449f24921a972d812d7c6Alexey Samsonov    Printf("  Location is global '%s' of size %zu at %p (%s+%p)\n\n",
1686d1862363c88c183b0ed7740fca876342cf0474bStephen Hines           global.name, global.size, global.start,
1696d1862363c88c183b0ed7740fca876342cf0474bStephen Hines           StripModuleName(global.module), global.module_offset);
1707ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  } else if (loc->type == ReportLocationHeap) {
171da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov    char thrbuf[kThreadBufSize];
172da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov    Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
1736d1862363c88c183b0ed7740fca876342cf0474bStephen Hines           loc->heap_chunk_size, loc->heap_chunk_start,
1746d1862363c88c183b0ed7740fca876342cf0474bStephen Hines           thread_name(thrbuf, loc->tid));
175a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany    print_stack = true;
1767ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  } else if (loc->type == ReportLocationStack) {
177fb917e9069ea44f7103f50c658be84a8f66de56cDmitry Vyukov    Printf("  Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
178fb917e9069ea44f7103f50c658be84a8f66de56cDmitry Vyukov  } else if (loc->type == ReportLocationTLS) {
179fb917e9069ea44f7103f50c658be84a8f66de56cDmitry Vyukov    Printf("  Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
180c2234cd922bbd94e276e0bebb08004d63cbc5cf2Dmitry Vyukov  } else if (loc->type == ReportLocationFD) {
181c2234cd922bbd94e276e0bebb08004d63cbc5cf2Dmitry Vyukov    Printf("  Location is file descriptor %d created by %s at:\n",
182c2234cd922bbd94e276e0bebb08004d63cbc5cf2Dmitry Vyukov        loc->fd, thread_name(thrbuf, loc->tid));
183a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany    print_stack = true;
1847ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  }
185a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.EndLocation());
186a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  if (print_stack)
187a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany    PrintStack(loc->stack);
1887ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
1897ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
1902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic void PrintMutexShort(const ReportMutex *rm, const char *after) {
1912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  Decorator d;
1922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  Printf("%sM%zd%s%s", d.Mutex(), rm->id, d.EndMutex(), after);
1932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines}
1942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines
1952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic void PrintMutexShortWithAddress(const ReportMutex *rm,
1962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines                                       const char *after) {
1972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  Decorator d;
1982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  Printf("%sM%zd (%p)%s%s", d.Mutex(), rm->id, rm->addr, d.EndMutex(), after);
1992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines}
2002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines
2017ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic void PrintMutex(const ReportMutex *rm) {
202a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Decorator d;
203ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  if (rm->destroyed) {
204a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany    Printf("%s", d.Mutex());
205ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov    Printf("  Mutex M%llu is already destroyed.\n\n", rm->id);
206a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany    Printf("%s", d.EndMutex());
207ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  } else {
208a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany    Printf("%s", d.Mutex());
2092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    Printf("  Mutex M%llu (%p) created at:\n", rm->id, rm->addr);
210a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany    Printf("%s", d.EndMutex());
211ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov    PrintStack(rm->stack);
212ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  }
2137ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
2147ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
2157ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanystatic void PrintThread(const ReportThread *rt) {
216a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Decorator d;
2177ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  if (rt->id == 0)  // Little sense in describing the main thread.
2187ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    return;
219a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.ThreadDescription());
220ad9da372f962495b3487685232d09390be841b1cDmitry Vyukov  Printf("  Thread T%d", rt->id);
2212bbd8bec77c2fdb41c5f5b6cb0d83d22bc576650Alexey Samsonov  if (rt->name && rt->name[0] != '\0')
222b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov    Printf(" '%s'", rt->name);
223da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov  char thrbuf[kThreadBufSize];
224da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov  Printf(" (tid=%zu, %s) created by %s",
225da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov    rt->pid, rt->running ? "running" : "finished",
226da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov    thread_name(thrbuf, rt->parent_tid));
2277ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  if (rt->stack)
228da3503782901d30bd6e48885055b51b38cf5126cDmitry Vyukov    Printf(" at:");
229b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("\n");
230a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.EndThreadDescription());
2317ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  PrintStack(rt->stack);
2327ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
2337ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
234848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukovstatic void PrintSleep(const ReportStack *s) {
235a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Decorator d;
236a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.Sleep());
237b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("  As if synchronized via sleep:\n");
238a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.EndSleep());
239848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukov  PrintStack(s);
240848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukov}
241848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukov
2422f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryanystatic ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
2432f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (rep->mops.Size())
2442f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return rep->mops[0]->stack;
2452f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (rep->stacks.Size())
2462f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return rep->stacks[0];
2472f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (rep->mutexes.Size())
2482f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return rep->mutexes[0]->stack;
2492f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  if (rep->threads.Size())
2502f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany    return rep->threads[0]->stack;
2512f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  return 0;
2522f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany}
2532f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany
25486277eb844c4983c81de62d7c050e92fe7155788Stephen Hinesstatic bool FrameIsInternal(const SymbolizedStack *frame) {
25586277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  if (frame == 0)
25686277eb844c4983c81de62d7c050e92fe7155788Stephen Hines    return false;
25786277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  const char *file = frame->info.file;
25886277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  return file != 0 &&
25986277eb844c4983c81de62d7c050e92fe7155788Stephen Hines         (internal_strstr(file, "tsan_interceptors.cc") ||
26086277eb844c4983c81de62d7c050e92fe7155788Stephen Hines          internal_strstr(file, "sanitizer_common_interceptors.inc") ||
26186277eb844c4983c81de62d7c050e92fe7155788Stephen Hines          internal_strstr(file, "tsan_interface_"));
26286277eb844c4983c81de62d7c050e92fe7155788Stephen Hines}
26386277eb844c4983c81de62d7c050e92fe7155788Stephen Hines
26486277eb844c4983c81de62d7c050e92fe7155788Stephen Hinesstatic SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
26586277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  while (FrameIsInternal(frames) && frames->next)
26686277eb844c4983c81de62d7c050e92fe7155788Stephen Hines    frames = frames->next;
26786277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  return frames;
2682f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany}
2692f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany
2707ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryanyvoid PrintReport(const ReportDesc *rep) {
271a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Decorator d;
272b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("==================\n");
2732f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany  const char *rep_typ_str = ReportTypeString(rep->typ);
274a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.Warning());
2750b694fcab9b2f33bdd6691cbea4e80a5c27191b1Peter Collingbourne  Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str,
2760b694fcab9b2f33bdd6691cbea4e80a5c27191b1Peter Collingbourne         (int)internal_getpid());
277a27512c4e115df4f260501a94b8d343f9ed955afKostya Serebryany  Printf("%s", d.EndWarning());
2787ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
2792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (rep->typ == ReportTypeDeadlock) {
2802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    char thrbuf[kThreadBufSize];
2812d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    Printf("  Cycle in lock order graph: ");
2822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    for (uptr i = 0; i < rep->mutexes.Size(); i++)
2832d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintMutexShortWithAddress(rep->mutexes[i], " => ");
2842d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    PrintMutexShort(rep->mutexes[0], "\n\n");
2852d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    CHECK_GT(rep->mutexes.Size(), 0U);
2862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),
2872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines             rep->stacks.Size());
2882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    for (uptr i = 0; i < rep->mutexes.Size(); i++) {
2892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("  Mutex ");
2902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],
2912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines                      " acquired here while holding mutex ");
2922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintMutexShort(rep->mutexes[i], " in ");
2932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("%s", d.ThreadDescription());
2942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));
2952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("%s", d.EndThreadDescription());
2962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      if (flags()->second_deadlock_stack) {
2972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines        PrintStack(rep->stacks[2*i]);
2982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines        Printf("  Mutex ");
2992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines        PrintMutexShort(rep->mutexes[i],
3002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines                        " previously acquired by the same thread here:\n");
3012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines        PrintStack(rep->stacks[2*i+1]);
3022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      } else {
3032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines        PrintStack(rep->stacks[i]);
3042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines        if (i == 0)
3052d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines          Printf("    Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
3062d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines                 "to get more informative warning message\n\n");
3072d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      }
3082d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    }
3092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  } else {
3102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    for (uptr i = 0; i < rep->stacks.Size(); i++) {
3112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      if (i)
3122d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines        Printf("  and:\n");
3132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintStack(rep->stacks[i]);
3142d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    }
315332c62b52b3603be872b28bd3ea5e739aa28cd05Dmitry Vyukov  }
3167ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
3177ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  for (uptr i = 0; i < rep->mops.Size(); i++)
3187ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    PrintMop(rep->mops[i], i == 0);
3197ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
320848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukov  if (rep->sleep)
321848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukov    PrintSleep(rep->sleep);
322848531192777acecf79747dc7c1ffeedf5c1da9fDmitry Vyukov
3237ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  for (uptr i = 0; i < rep->locs.Size(); i++)
3247ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    PrintLocation(rep->locs[i]);
3257ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
3262d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (rep->typ != ReportTypeDeadlock) {
3272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    for (uptr i = 0; i < rep->mutexes.Size(); i++)
3282d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintMutex(rep->mutexes[i]);
3292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  }
3307ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
3317ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany  for (uptr i = 0; i < rep->threads.Size(); i++)
3327ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany    PrintThread(rep->threads[i]);
3337ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
3344536cb1fa7734133f404acb413589d7a6d314f4aDmitry Vyukov  if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
3354536cb1fa7734133f404acb413589d7a6d314f4aDmitry Vyukov    Printf("  And %d more similar thread leaks.\n\n", rep->count - 1);
3364536cb1fa7734133f404acb413589d7a6d314f4aDmitry Vyukov
33786277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  if (ReportStack *stack = ChooseSummaryStack(rep)) {
3387c9150579ed0278492f51cc8434b1d63a44b9bd1Pirama Arumuga Nainar    if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
3397c9150579ed0278492f51cc8434b1d63a44b9bd1Pirama Arumuga Nainar      ReportErrorSummary(rep_typ_str, frame->info);
3406d1862363c88c183b0ed7740fca876342cf0474bStephen Hines  }
3412f588f9d3417aa107ebbbd8830f97501023d3f40Kostya Serebryany
342b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("==================\n");
3437ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}
3447ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany
34586277eb844c4983c81de62d7c050e92fe7155788Stephen Hines#else  // #ifndef SANITIZER_GO
3469d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov
3479d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukovconst int kMainThreadId = 1;
348c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov
3490ab628c61594eb80612e5389d9c33da0e0d70c66Dmitry Vyukovvoid PrintStack(const ReportStack *ent) {
35086277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  if (ent == 0 || ent->frames == 0) {
3519d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov    Printf("  [failed to restore the stack]\n");
3526c0fbcae2f03eb78f82969bea61ccdbd2fb58e27Dmitry Vyukov    return;
3536c0fbcae2f03eb78f82969bea61ccdbd2fb58e27Dmitry Vyukov  }
35486277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  SymbolizedStack *frame = ent->frames;
35586277eb844c4983c81de62d7c050e92fe7155788Stephen Hines  for (int i = 0; frame; frame = frame->next, i++) {
35686277eb844c4983c81de62d7c050e92fe7155788Stephen Hines    const AddressInfo &info = frame->info;
35786277eb844c4983c81de62d7c050e92fe7155788Stephen Hines    Printf("  %s()\n      %s:%d +0x%zx\n", info.function,
35886277eb844c4983c81de62d7c050e92fe7155788Stephen Hines        StripPathPrefix(info.file, common_flags()->strip_path_prefix),
35986277eb844c4983c81de62d7c050e92fe7155788Stephen Hines        info.line, (void *)info.module_offset);
360c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov  }
361c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov}
362c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov
363c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukovstatic void PrintMop(const ReportMop *mop, bool first) {
3649d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov  Printf("\n");
3659d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov  Printf("%s by ",
366c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov      (first ? (mop->write ? "Write" : "Read")
3679d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov             : (mop->write ? "Previous write" : "Previous read")));
3689d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov  if (mop->tid == kMainThreadId)
3699d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov    Printf("main goroutine:\n");
3709d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov  else
3719d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov    Printf("goroutine %d:\n", mop->tid);
372c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov  PrintStack(mop->stack);
373c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov}
374c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov
37543046e0b628df7eaaec266a4b6c9e9a998b02120Dmitry Vyukovstatic void PrintThread(const ReportThread *rt) {
3769d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov  if (rt->id == kMainThreadId)
37743046e0b628df7eaaec266a4b6c9e9a998b02120Dmitry Vyukov    return;
3789d95475f8adb1e08e45484feb03570ae2be2d0f3Dmitry Vyukov  Printf("\n");
379b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("Goroutine %d (%s) created at:\n",
38043046e0b628df7eaaec266a4b6c9e9a998b02120Dmitry Vyukov    rt->id, rt->running ? "running" : "finished");
38143046e0b628df7eaaec266a4b6c9e9a998b02120Dmitry Vyukov  PrintStack(rt->stack);
38243046e0b628df7eaaec266a4b6c9e9a998b02120Dmitry Vyukov}
38343046e0b628df7eaaec266a4b6c9e9a998b02120Dmitry Vyukov
384c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukovvoid PrintReport(const ReportDesc *rep) {
385b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("==================\n");
3862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  if (rep->typ == ReportTypeRace) {
3872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    Printf("WARNING: DATA RACE");
3882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    for (uptr i = 0; i < rep->mops.Size(); i++)
3892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintMop(rep->mops[i], i == 0);
3902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    for (uptr i = 0; i < rep->threads.Size(); i++)
3912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintThread(rep->threads[i]);
3922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  } else if (rep->typ == ReportTypeDeadlock) {
3932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    Printf("WARNING: DEADLOCK\n");
3942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    for (uptr i = 0; i < rep->mutexes.Size(); i++) {
3952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
3962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines          999, rep->mutexes[i]->id,
3972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines          rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
3982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintStack(rep->stacks[2*i]);
3992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("\n");
4002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("Mutex %d was previously locked here:\n",
4012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines          rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
4022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      PrintStack(rep->stacks[2*i + 1]);
4032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines      Printf("\n");
4042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines    }
4052d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines  }
406b1fe3021eca0843e37878d224ee7f32e32f40d99Alexey Samsonov  Printf("==================\n");
407c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov}
408c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov
409c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov#endif
410c510a2f264a22ff60333fc48e5fa12d41cefba3cDmitry Vyukov
4117ac41484ea322e0ea5774df681660269f5dc321eKostya Serebryany}  // namespace __tsan
412