168d75effSDimitry Andric //===-- tsan_report.cpp ---------------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of ThreadSanitizer (TSan), a race detector.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric //===----------------------------------------------------------------------===//
1268d75effSDimitry Andric #include "tsan_report.h"
1368d75effSDimitry Andric #include "tsan_platform.h"
1468d75effSDimitry Andric #include "tsan_rtl.h"
1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_file.h"
1668d75effSDimitry Andric #include "sanitizer_common/sanitizer_placement_new.h"
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_report_decorator.h"
1868d75effSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace_printer.h"
1968d75effSDimitry Andric 
2068d75effSDimitry Andric namespace __tsan {
2168d75effSDimitry Andric 
2268d75effSDimitry Andric class Decorator: public __sanitizer::SanitizerCommonDecorator {
2368d75effSDimitry Andric  public:
Decorator()2468d75effSDimitry Andric   Decorator() : SanitizerCommonDecorator() { }
Access()2568d75effSDimitry Andric   const char *Access()     { return Blue(); }
ThreadDescription()2668d75effSDimitry Andric   const char *ThreadDescription()    { return Cyan(); }
Location()2768d75effSDimitry Andric   const char *Location()   { return Green(); }
Sleep()2868d75effSDimitry Andric   const char *Sleep()   { return Yellow(); }
Mutex()2968d75effSDimitry Andric   const char *Mutex()   { return Magenta(); }
3068d75effSDimitry Andric };
3168d75effSDimitry Andric 
ReportDesc()3268d75effSDimitry Andric ReportDesc::ReportDesc()
3368d75effSDimitry Andric     : tag(kExternalTagNone)
3468d75effSDimitry Andric     , stacks()
3568d75effSDimitry Andric     , mops()
3668d75effSDimitry Andric     , locs()
3768d75effSDimitry Andric     , mutexes()
3868d75effSDimitry Andric     , threads()
3968d75effSDimitry Andric     , unique_tids()
4068d75effSDimitry Andric     , sleep()
4168d75effSDimitry Andric     , count() {
4268d75effSDimitry Andric }
4368d75effSDimitry Andric 
ReportMop()4468d75effSDimitry Andric ReportMop::ReportMop()
4568d75effSDimitry Andric     : mset() {
4668d75effSDimitry Andric }
4768d75effSDimitry Andric 
~ReportDesc()4868d75effSDimitry Andric ReportDesc::~ReportDesc() {
4968d75effSDimitry Andric   // FIXME(dvyukov): it must be leaking a lot of memory.
5068d75effSDimitry Andric }
5168d75effSDimitry Andric 
5268d75effSDimitry Andric #if !SANITIZER_GO
5368d75effSDimitry Andric 
5468d75effSDimitry Andric const int kThreadBufSize = 32;
thread_name(char * buf,Tid tid)55349cc55cSDimitry Andric const char *thread_name(char *buf, Tid tid) {
56fe6060f1SDimitry Andric   if (tid == kMainTid)
5768d75effSDimitry Andric     return "main thread";
5868d75effSDimitry Andric   internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
5968d75effSDimitry Andric   return buf;
6068d75effSDimitry Andric }
6168d75effSDimitry Andric 
ReportTypeString(ReportType typ,uptr tag)6268d75effSDimitry Andric static const char *ReportTypeString(ReportType typ, uptr tag) {
6368d75effSDimitry Andric   switch (typ) {
6468d75effSDimitry Andric     case ReportTypeRace:
6568d75effSDimitry Andric       return "data race";
6668d75effSDimitry Andric     case ReportTypeVptrRace:
6768d75effSDimitry Andric       return "data race on vptr (ctor/dtor vs virtual call)";
6868d75effSDimitry Andric     case ReportTypeUseAfterFree:
6968d75effSDimitry Andric       return "heap-use-after-free";
7068d75effSDimitry Andric     case ReportTypeVptrUseAfterFree:
7168d75effSDimitry Andric       return "heap-use-after-free (virtual call vs free)";
7268d75effSDimitry Andric     case ReportTypeExternalRace: {
7368d75effSDimitry Andric       const char *str = GetReportHeaderFromTag(tag);
7468d75effSDimitry Andric       return str ? str : "race on external object";
7568d75effSDimitry Andric     }
7668d75effSDimitry Andric     case ReportTypeThreadLeak:
7768d75effSDimitry Andric       return "thread leak";
7868d75effSDimitry Andric     case ReportTypeMutexDestroyLocked:
7968d75effSDimitry Andric       return "destroy of a locked mutex";
8068d75effSDimitry Andric     case ReportTypeMutexDoubleLock:
8168d75effSDimitry Andric       return "double lock of a mutex";
8268d75effSDimitry Andric     case ReportTypeMutexInvalidAccess:
8368d75effSDimitry Andric       return "use of an invalid mutex (e.g. uninitialized or destroyed)";
8468d75effSDimitry Andric     case ReportTypeMutexBadUnlock:
8568d75effSDimitry Andric       return "unlock of an unlocked mutex (or by a wrong thread)";
8668d75effSDimitry Andric     case ReportTypeMutexBadReadLock:
8768d75effSDimitry Andric       return "read lock of a write locked mutex";
8868d75effSDimitry Andric     case ReportTypeMutexBadReadUnlock:
8968d75effSDimitry Andric       return "read unlock of a write locked mutex";
9068d75effSDimitry Andric     case ReportTypeSignalUnsafe:
9168d75effSDimitry Andric       return "signal-unsafe call inside of a signal";
9268d75effSDimitry Andric     case ReportTypeErrnoInSignal:
9368d75effSDimitry Andric       return "signal handler spoils errno";
9468d75effSDimitry Andric     case ReportTypeDeadlock:
9568d75effSDimitry Andric       return "lock-order-inversion (potential deadlock)";
965f757f3fSDimitry Andric     case ReportTypeMutexHeldWrongContext:
975f757f3fSDimitry Andric       return "mutex held in the wrong context";
9868d75effSDimitry Andric       // No default case so compiler warns us if we miss one
9968d75effSDimitry Andric   }
10068d75effSDimitry Andric   UNREACHABLE("missing case");
10168d75effSDimitry Andric }
10268d75effSDimitry Andric 
PrintStack(const ReportStack * ent)10368d75effSDimitry Andric void PrintStack(const ReportStack *ent) {
10468d75effSDimitry Andric   if (ent == 0 || ent->frames == 0) {
10568d75effSDimitry Andric     Printf("    [failed to restore the stack]\n\n");
10668d75effSDimitry Andric     return;
10768d75effSDimitry Andric   }
10868d75effSDimitry Andric   SymbolizedStack *frame = ent->frames;
10968d75effSDimitry Andric   for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
110fe6060f1SDimitry Andric     InternalScopedString res;
1115f757f3fSDimitry Andric     StackTracePrinter::GetOrInit()->RenderFrame(
1125f757f3fSDimitry Andric         &res, common_flags()->stack_trace_format, i, frame->info.address,
1135f757f3fSDimitry Andric         &frame->info, common_flags()->symbolize_vs_style,
11406c3fb27SDimitry Andric         common_flags()->strip_path_prefix);
11568d75effSDimitry Andric     Printf("%s\n", res.data());
11668d75effSDimitry Andric   }
11768d75effSDimitry Andric   Printf("\n");
11868d75effSDimitry Andric }
11968d75effSDimitry Andric 
PrintMutexSet(Vector<ReportMopMutex> const & mset)12068d75effSDimitry Andric static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
12168d75effSDimitry Andric   for (uptr i = 0; i < mset.Size(); i++) {
12268d75effSDimitry Andric     if (i == 0)
12368d75effSDimitry Andric       Printf(" (mutexes:");
12468d75effSDimitry Andric     const ReportMopMutex m = mset[i];
1250eae32dcSDimitry Andric     Printf(" %s M%u", m.write ? "write" : "read", m.id);
12668d75effSDimitry Andric     Printf(i == mset.Size() - 1 ? ")" : ",");
12768d75effSDimitry Andric   }
12868d75effSDimitry Andric }
12968d75effSDimitry Andric 
MopDesc(bool first,bool write,bool atomic)13068d75effSDimitry Andric static const char *MopDesc(bool first, bool write, bool atomic) {
13168d75effSDimitry Andric   return atomic ? (first ? (write ? "Atomic write" : "Atomic read")
13268d75effSDimitry Andric                 : (write ? "Previous atomic write" : "Previous atomic read"))
13368d75effSDimitry Andric                 : (first ? (write ? "Write" : "Read")
13468d75effSDimitry Andric                 : (write ? "Previous write" : "Previous read"));
13568d75effSDimitry Andric }
13668d75effSDimitry Andric 
ExternalMopDesc(bool first,bool write)13768d75effSDimitry Andric static const char *ExternalMopDesc(bool first, bool write) {
13868d75effSDimitry Andric   return first ? (write ? "Modifying" : "Read-only")
13968d75effSDimitry Andric                : (write ? "Previous modifying" : "Previous read-only");
14068d75effSDimitry Andric }
14168d75effSDimitry Andric 
PrintMop(const ReportMop * mop,bool first)14268d75effSDimitry Andric static void PrintMop(const ReportMop *mop, bool first) {
14368d75effSDimitry Andric   Decorator d;
14468d75effSDimitry Andric   char thrbuf[kThreadBufSize];
14568d75effSDimitry Andric   Printf("%s", d.Access());
14668d75effSDimitry Andric   if (mop->external_tag == kExternalTagNone) {
14768d75effSDimitry Andric     Printf("  %s of size %d at %p by %s",
14868d75effSDimitry Andric            MopDesc(first, mop->write, mop->atomic), mop->size,
14968d75effSDimitry Andric            (void *)mop->addr, thread_name(thrbuf, mop->tid));
15068d75effSDimitry Andric   } else {
15168d75effSDimitry Andric     const char *object_type = GetObjectTypeFromTag(mop->external_tag);
15268d75effSDimitry Andric     if (object_type == nullptr)
15368d75effSDimitry Andric         object_type = "external object";
15468d75effSDimitry Andric     Printf("  %s access of %s at %p by %s",
15568d75effSDimitry Andric            ExternalMopDesc(first, mop->write), object_type,
15668d75effSDimitry Andric            (void *)mop->addr, thread_name(thrbuf, mop->tid));
15768d75effSDimitry Andric   }
15868d75effSDimitry Andric   PrintMutexSet(mop->mset);
15968d75effSDimitry Andric   Printf(":\n");
16068d75effSDimitry Andric   Printf("%s", d.Default());
16168d75effSDimitry Andric   PrintStack(mop->stack);
16268d75effSDimitry Andric }
16368d75effSDimitry Andric 
PrintLocation(const ReportLocation * loc)16468d75effSDimitry Andric static void PrintLocation(const ReportLocation *loc) {
16568d75effSDimitry Andric   Decorator d;
16668d75effSDimitry Andric   char thrbuf[kThreadBufSize];
16768d75effSDimitry Andric   bool print_stack = false;
16868d75effSDimitry Andric   Printf("%s", d.Location());
16968d75effSDimitry Andric   if (loc->type == ReportLocationGlobal) {
17068d75effSDimitry Andric     const DataInfo &global = loc->global;
17168d75effSDimitry Andric     if (global.size != 0)
172349cc55cSDimitry Andric       Printf("  Location is global '%s' of size %zu at %p (%s+0x%zx)\n\n",
173349cc55cSDimitry Andric              global.name, global.size, reinterpret_cast<void *>(global.start),
17468d75effSDimitry Andric              StripModuleName(global.module), global.module_offset);
17568d75effSDimitry Andric     else
176349cc55cSDimitry Andric       Printf("  Location is global '%s' at %p (%s+0x%zx)\n\n", global.name,
177349cc55cSDimitry Andric              reinterpret_cast<void *>(global.start),
178349cc55cSDimitry Andric              StripModuleName(global.module), global.module_offset);
17968d75effSDimitry Andric   } else if (loc->type == ReportLocationHeap) {
18068d75effSDimitry Andric     char thrbuf[kThreadBufSize];
18168d75effSDimitry Andric     const char *object_type = GetObjectTypeFromTag(loc->external_tag);
18268d75effSDimitry Andric     if (!object_type) {
18368d75effSDimitry Andric       Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
184349cc55cSDimitry Andric              loc->heap_chunk_size,
185349cc55cSDimitry Andric              reinterpret_cast<void *>(loc->heap_chunk_start),
18668d75effSDimitry Andric              thread_name(thrbuf, loc->tid));
18768d75effSDimitry Andric     } else {
18868d75effSDimitry Andric       Printf("  Location is %s of size %zu at %p allocated by %s:\n",
189349cc55cSDimitry Andric              object_type, loc->heap_chunk_size,
190349cc55cSDimitry Andric              reinterpret_cast<void *>(loc->heap_chunk_start),
19168d75effSDimitry Andric              thread_name(thrbuf, loc->tid));
19268d75effSDimitry Andric     }
19368d75effSDimitry Andric     print_stack = true;
19468d75effSDimitry Andric   } else if (loc->type == ReportLocationStack) {
19568d75effSDimitry Andric     Printf("  Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
19668d75effSDimitry Andric   } else if (loc->type == ReportLocationTLS) {
19768d75effSDimitry Andric     Printf("  Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
19868d75effSDimitry Andric   } else if (loc->type == ReportLocationFD) {
199bdd1243dSDimitry Andric     Printf("  Location is file descriptor %d %s by %s at:\n", loc->fd,
200bdd1243dSDimitry Andric            loc->fd_closed ? "destroyed" : "created",
201bdd1243dSDimitry Andric            thread_name(thrbuf, loc->tid));
20268d75effSDimitry Andric     print_stack = true;
20368d75effSDimitry Andric   }
20468d75effSDimitry Andric   Printf("%s", d.Default());
20568d75effSDimitry Andric   if (print_stack)
20668d75effSDimitry Andric     PrintStack(loc->stack);
20768d75effSDimitry Andric }
20868d75effSDimitry Andric 
PrintMutexShort(const ReportMutex * rm,const char * after)20968d75effSDimitry Andric static void PrintMutexShort(const ReportMutex *rm, const char *after) {
21068d75effSDimitry Andric   Decorator d;
2110eae32dcSDimitry Andric   Printf("%sM%d%s%s", d.Mutex(), rm->id, d.Default(), after);
21268d75effSDimitry Andric }
21368d75effSDimitry Andric 
PrintMutexShortWithAddress(const ReportMutex * rm,const char * after)21468d75effSDimitry Andric static void PrintMutexShortWithAddress(const ReportMutex *rm,
21568d75effSDimitry Andric                                        const char *after) {
21668d75effSDimitry Andric   Decorator d;
2170eae32dcSDimitry Andric   Printf("%sM%d (%p)%s%s", d.Mutex(), rm->id,
218349cc55cSDimitry Andric          reinterpret_cast<void *>(rm->addr), d.Default(), after);
21968d75effSDimitry Andric }
22068d75effSDimitry Andric 
PrintMutex(const ReportMutex * rm)22168d75effSDimitry Andric static void PrintMutex(const ReportMutex *rm) {
22268d75effSDimitry Andric   Decorator d;
22368d75effSDimitry Andric   Printf("%s", d.Mutex());
2240eae32dcSDimitry Andric   Printf("  Mutex M%u (%p) created at:\n", rm->id,
225349cc55cSDimitry Andric          reinterpret_cast<void *>(rm->addr));
22668d75effSDimitry Andric   Printf("%s", d.Default());
22768d75effSDimitry Andric   PrintStack(rm->stack);
22868d75effSDimitry Andric }
22968d75effSDimitry Andric 
PrintThread(const ReportThread * rt)23068d75effSDimitry Andric static void PrintThread(const ReportThread *rt) {
23168d75effSDimitry Andric   Decorator d;
232fe6060f1SDimitry Andric   if (rt->id == kMainTid)  // Little sense in describing the main thread.
23368d75effSDimitry Andric     return;
23468d75effSDimitry Andric   Printf("%s", d.ThreadDescription());
23568d75effSDimitry Andric   Printf("  Thread T%d", rt->id);
23668d75effSDimitry Andric   if (rt->name && rt->name[0] != '\0')
23768d75effSDimitry Andric     Printf(" '%s'", rt->name);
23868d75effSDimitry Andric   char thrbuf[kThreadBufSize];
23968d75effSDimitry Andric   const char *thread_status = rt->running ? "running" : "finished";
24068d75effSDimitry Andric   if (rt->thread_type == ThreadType::Worker) {
241349cc55cSDimitry Andric     Printf(" (tid=%llu, %s) is a GCD worker thread\n", rt->os_id,
242349cc55cSDimitry Andric            thread_status);
24368d75effSDimitry Andric     Printf("\n");
24468d75effSDimitry Andric     Printf("%s", d.Default());
24568d75effSDimitry Andric     return;
24668d75effSDimitry Andric   }
247349cc55cSDimitry Andric   Printf(" (tid=%llu, %s) created by %s", rt->os_id, thread_status,
24868d75effSDimitry Andric          thread_name(thrbuf, rt->parent_tid));
24968d75effSDimitry Andric   if (rt->stack)
25068d75effSDimitry Andric     Printf(" at:");
25168d75effSDimitry Andric   Printf("\n");
25268d75effSDimitry Andric   Printf("%s", d.Default());
25368d75effSDimitry Andric   PrintStack(rt->stack);
25468d75effSDimitry Andric }
25568d75effSDimitry Andric 
PrintSleep(const ReportStack * s)25668d75effSDimitry Andric static void PrintSleep(const ReportStack *s) {
25768d75effSDimitry Andric   Decorator d;
25868d75effSDimitry Andric   Printf("%s", d.Sleep());
25968d75effSDimitry Andric   Printf("  As if synchronized via sleep:\n");
26068d75effSDimitry Andric   Printf("%s", d.Default());
26168d75effSDimitry Andric   PrintStack(s);
26268d75effSDimitry Andric }
26368d75effSDimitry Andric 
ChooseSummaryStack(const ReportDesc * rep)26468d75effSDimitry Andric static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
26568d75effSDimitry Andric   if (rep->mops.Size())
26668d75effSDimitry Andric     return rep->mops[0]->stack;
26768d75effSDimitry Andric   if (rep->stacks.Size())
26868d75effSDimitry Andric     return rep->stacks[0];
26968d75effSDimitry Andric   if (rep->mutexes.Size())
27068d75effSDimitry Andric     return rep->mutexes[0]->stack;
27168d75effSDimitry Andric   if (rep->threads.Size())
27268d75effSDimitry Andric     return rep->threads[0]->stack;
27368d75effSDimitry Andric   return 0;
27468d75effSDimitry Andric }
27568d75effSDimitry Andric 
SkipTsanInternalFrames(SymbolizedStack * frames)276*1db9f3b2SDimitry Andric static const SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
277*1db9f3b2SDimitry Andric   if (const SymbolizedStack *f = SkipInternalFrames(frames))
278*1db9f3b2SDimitry Andric     return f;
279*1db9f3b2SDimitry Andric   return frames;  // Fallback to the top frame.
28068d75effSDimitry Andric }
28168d75effSDimitry Andric 
PrintReport(const ReportDesc * rep)28268d75effSDimitry Andric void PrintReport(const ReportDesc *rep) {
28368d75effSDimitry Andric   Decorator d;
28468d75effSDimitry Andric   Printf("==================\n");
28568d75effSDimitry Andric   const char *rep_typ_str = ReportTypeString(rep->typ, rep->tag);
28668d75effSDimitry Andric   Printf("%s", d.Warning());
28768d75effSDimitry Andric   Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str,
28868d75effSDimitry Andric          (int)internal_getpid());
28968d75effSDimitry Andric   Printf("%s", d.Default());
29068d75effSDimitry Andric 
29181ad6265SDimitry Andric   if (rep->typ == ReportTypeErrnoInSignal)
29281ad6265SDimitry Andric     Printf("  Signal %u handler invoked at:\n", rep->signum);
29381ad6265SDimitry Andric 
29468d75effSDimitry Andric   if (rep->typ == ReportTypeDeadlock) {
29568d75effSDimitry Andric     char thrbuf[kThreadBufSize];
29668d75effSDimitry Andric     Printf("  Cycle in lock order graph: ");
29768d75effSDimitry Andric     for (uptr i = 0; i < rep->mutexes.Size(); i++)
29868d75effSDimitry Andric       PrintMutexShortWithAddress(rep->mutexes[i], " => ");
29968d75effSDimitry Andric     PrintMutexShort(rep->mutexes[0], "\n\n");
30068d75effSDimitry Andric     CHECK_GT(rep->mutexes.Size(), 0U);
30168d75effSDimitry Andric     CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),
30268d75effSDimitry Andric              rep->stacks.Size());
30368d75effSDimitry Andric     for (uptr i = 0; i < rep->mutexes.Size(); i++) {
30468d75effSDimitry Andric       Printf("  Mutex ");
30568d75effSDimitry Andric       PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],
30668d75effSDimitry Andric                       " acquired here while holding mutex ");
30768d75effSDimitry Andric       PrintMutexShort(rep->mutexes[i], " in ");
30868d75effSDimitry Andric       Printf("%s", d.ThreadDescription());
30968d75effSDimitry Andric       Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));
31068d75effSDimitry Andric       Printf("%s", d.Default());
31168d75effSDimitry Andric       if (flags()->second_deadlock_stack) {
31268d75effSDimitry Andric         PrintStack(rep->stacks[2*i]);
31368d75effSDimitry Andric         Printf("  Mutex ");
31468d75effSDimitry Andric         PrintMutexShort(rep->mutexes[i],
31568d75effSDimitry Andric                         " previously acquired by the same thread here:\n");
31668d75effSDimitry Andric         PrintStack(rep->stacks[2*i+1]);
31768d75effSDimitry Andric       } else {
31868d75effSDimitry Andric         PrintStack(rep->stacks[i]);
31968d75effSDimitry Andric         if (i == 0)
32068d75effSDimitry Andric           Printf("    Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
32168d75effSDimitry Andric                  "to get more informative warning message\n\n");
32268d75effSDimitry Andric       }
32368d75effSDimitry Andric     }
32468d75effSDimitry Andric   } else {
32568d75effSDimitry Andric     for (uptr i = 0; i < rep->stacks.Size(); i++) {
32668d75effSDimitry Andric       if (i)
32768d75effSDimitry Andric         Printf("  and:\n");
32868d75effSDimitry Andric       PrintStack(rep->stacks[i]);
32968d75effSDimitry Andric     }
33068d75effSDimitry Andric   }
33168d75effSDimitry Andric 
33268d75effSDimitry Andric   for (uptr i = 0; i < rep->mops.Size(); i++)
33368d75effSDimitry Andric     PrintMop(rep->mops[i], i == 0);
33468d75effSDimitry Andric 
33568d75effSDimitry Andric   if (rep->sleep)
33668d75effSDimitry Andric     PrintSleep(rep->sleep);
33768d75effSDimitry Andric 
33868d75effSDimitry Andric   for (uptr i = 0; i < rep->locs.Size(); i++)
33968d75effSDimitry Andric     PrintLocation(rep->locs[i]);
34068d75effSDimitry Andric 
34168d75effSDimitry Andric   if (rep->typ != ReportTypeDeadlock) {
34268d75effSDimitry Andric     for (uptr i = 0; i < rep->mutexes.Size(); i++)
34368d75effSDimitry Andric       PrintMutex(rep->mutexes[i]);
34468d75effSDimitry Andric   }
34568d75effSDimitry Andric 
34668d75effSDimitry Andric   for (uptr i = 0; i < rep->threads.Size(); i++)
34768d75effSDimitry Andric     PrintThread(rep->threads[i]);
34868d75effSDimitry Andric 
34968d75effSDimitry Andric   if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
35068d75effSDimitry Andric     Printf("  And %d more similar thread leaks.\n\n", rep->count - 1);
35168d75effSDimitry Andric 
35268d75effSDimitry Andric   if (ReportStack *stack = ChooseSummaryStack(rep)) {
353*1db9f3b2SDimitry Andric     if (const SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
35468d75effSDimitry Andric       ReportErrorSummary(rep_typ_str, frame->info);
35568d75effSDimitry Andric   }
35668d75effSDimitry Andric 
357e8d8bef9SDimitry Andric   if (common_flags()->print_module_map == 2)
358e8d8bef9SDimitry Andric     DumpProcessMap();
35968d75effSDimitry Andric 
36068d75effSDimitry Andric   Printf("==================\n");
36168d75effSDimitry Andric }
36268d75effSDimitry Andric 
36368d75effSDimitry Andric #else  // #if !SANITIZER_GO
36468d75effSDimitry Andric 
365349cc55cSDimitry Andric const Tid kMainGoroutineId = 1;
36668d75effSDimitry Andric 
PrintStack(const ReportStack * ent)36768d75effSDimitry Andric void PrintStack(const ReportStack *ent) {
36868d75effSDimitry Andric   if (ent == 0 || ent->frames == 0) {
36968d75effSDimitry Andric     Printf("  [failed to restore the stack]\n");
37068d75effSDimitry Andric     return;
37168d75effSDimitry Andric   }
37268d75effSDimitry Andric   SymbolizedStack *frame = ent->frames;
37368d75effSDimitry Andric   for (int i = 0; frame; frame = frame->next, i++) {
37468d75effSDimitry Andric     const AddressInfo &info = frame->info;
37568d75effSDimitry Andric     Printf("  %s()\n      %s:%d +0x%zx\n", info.function,
37668d75effSDimitry Andric            StripPathPrefix(info.file, common_flags()->strip_path_prefix),
377349cc55cSDimitry Andric            info.line, info.module_offset);
37868d75effSDimitry Andric   }
37968d75effSDimitry Andric }
38068d75effSDimitry Andric 
PrintMop(const ReportMop * mop,bool first)38168d75effSDimitry Andric static void PrintMop(const ReportMop *mop, bool first) {
38268d75effSDimitry Andric   Printf("\n");
38368d75effSDimitry Andric   Printf("%s at %p by ",
38468d75effSDimitry Andric          (first ? (mop->write ? "Write" : "Read")
385349cc55cSDimitry Andric                 : (mop->write ? "Previous write" : "Previous read")),
386349cc55cSDimitry Andric          reinterpret_cast<void *>(mop->addr));
387fe6060f1SDimitry Andric   if (mop->tid == kMainGoroutineId)
38868d75effSDimitry Andric     Printf("main goroutine:\n");
38968d75effSDimitry Andric   else
39068d75effSDimitry Andric     Printf("goroutine %d:\n", mop->tid);
39168d75effSDimitry Andric   PrintStack(mop->stack);
39268d75effSDimitry Andric }
39368d75effSDimitry Andric 
PrintLocation(const ReportLocation * loc)39468d75effSDimitry Andric static void PrintLocation(const ReportLocation *loc) {
39568d75effSDimitry Andric   switch (loc->type) {
39668d75effSDimitry Andric   case ReportLocationHeap: {
39768d75effSDimitry Andric     Printf("\n");
398349cc55cSDimitry Andric     Printf("Heap block of size %zu at %p allocated by ", loc->heap_chunk_size,
399349cc55cSDimitry Andric            reinterpret_cast<void *>(loc->heap_chunk_start));
400fe6060f1SDimitry Andric     if (loc->tid == kMainGoroutineId)
40168d75effSDimitry Andric       Printf("main goroutine:\n");
40268d75effSDimitry Andric     else
40368d75effSDimitry Andric       Printf("goroutine %d:\n", loc->tid);
40468d75effSDimitry Andric     PrintStack(loc->stack);
40568d75effSDimitry Andric     break;
40668d75effSDimitry Andric   }
40768d75effSDimitry Andric   case ReportLocationGlobal: {
40868d75effSDimitry Andric     Printf("\n");
40968d75effSDimitry Andric     Printf("Global var %s of size %zu at %p declared at %s:%zu\n",
410349cc55cSDimitry Andric            loc->global.name, loc->global.size,
411349cc55cSDimitry Andric            reinterpret_cast<void *>(loc->global.start), loc->global.file,
412349cc55cSDimitry Andric            loc->global.line);
41368d75effSDimitry Andric     break;
41468d75effSDimitry Andric   }
41568d75effSDimitry Andric   default:
41668d75effSDimitry Andric     break;
41768d75effSDimitry Andric   }
41868d75effSDimitry Andric }
41968d75effSDimitry Andric 
PrintThread(const ReportThread * rt)42068d75effSDimitry Andric static void PrintThread(const ReportThread *rt) {
421fe6060f1SDimitry Andric   if (rt->id == kMainGoroutineId)
42268d75effSDimitry Andric     return;
42368d75effSDimitry Andric   Printf("\n");
42468d75effSDimitry Andric   Printf("Goroutine %d (%s) created at:\n",
42568d75effSDimitry Andric     rt->id, rt->running ? "running" : "finished");
42668d75effSDimitry Andric   PrintStack(rt->stack);
42768d75effSDimitry Andric }
42868d75effSDimitry Andric 
PrintReport(const ReportDesc * rep)42968d75effSDimitry Andric void PrintReport(const ReportDesc *rep) {
43068d75effSDimitry Andric   Printf("==================\n");
43168d75effSDimitry Andric   if (rep->typ == ReportTypeRace) {
43268d75effSDimitry Andric     Printf("WARNING: DATA RACE");
43368d75effSDimitry Andric     for (uptr i = 0; i < rep->mops.Size(); i++)
43468d75effSDimitry Andric       PrintMop(rep->mops[i], i == 0);
43568d75effSDimitry Andric     for (uptr i = 0; i < rep->locs.Size(); i++)
43668d75effSDimitry Andric       PrintLocation(rep->locs[i]);
43768d75effSDimitry Andric     for (uptr i = 0; i < rep->threads.Size(); i++)
43868d75effSDimitry Andric       PrintThread(rep->threads[i]);
43968d75effSDimitry Andric   } else if (rep->typ == ReportTypeDeadlock) {
44068d75effSDimitry Andric     Printf("WARNING: DEADLOCK\n");
44168d75effSDimitry Andric     for (uptr i = 0; i < rep->mutexes.Size(); i++) {
4420eae32dcSDimitry Andric       Printf("Goroutine %d lock mutex %u while holding mutex %u:\n", 999,
443349cc55cSDimitry Andric              rep->mutexes[i]->id,
44468d75effSDimitry Andric              rep->mutexes[(i + 1) % rep->mutexes.Size()]->id);
44568d75effSDimitry Andric       PrintStack(rep->stacks[2*i]);
44668d75effSDimitry Andric       Printf("\n");
4470eae32dcSDimitry Andric       Printf("Mutex %u was previously locked here:\n",
44868d75effSDimitry Andric              rep->mutexes[(i + 1) % rep->mutexes.Size()]->id);
44968d75effSDimitry Andric       PrintStack(rep->stacks[2*i + 1]);
45068d75effSDimitry Andric       Printf("\n");
45168d75effSDimitry Andric     }
45268d75effSDimitry Andric   }
45368d75effSDimitry Andric   Printf("==================\n");
45468d75effSDimitry Andric }
45568d75effSDimitry Andric 
45668d75effSDimitry Andric #endif
45768d75effSDimitry Andric 
45868d75effSDimitry Andric }  // namespace __tsan
459