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: 2468d75effSDimitry Andric Decorator() : SanitizerCommonDecorator() { } 2568d75effSDimitry Andric const char *Access() { return Blue(); } 2668d75effSDimitry Andric const char *ThreadDescription() { return Cyan(); } 2768d75effSDimitry Andric const char *Location() { return Green(); } 2868d75effSDimitry Andric const char *Sleep() { return Yellow(); } 2968d75effSDimitry Andric const char *Mutex() { return Magenta(); } 3068d75effSDimitry Andric }; 3168d75effSDimitry Andric 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 4468d75effSDimitry Andric ReportMop::ReportMop() 4568d75effSDimitry Andric : mset() { 4668d75effSDimitry Andric } 4768d75effSDimitry Andric 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; 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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