Path: blob/main/contrib/llvm-project/compiler-rt/lib/tsan/rtl/tsan_report.cpp
35266 views
//===-- tsan_report.cpp ---------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of ThreadSanitizer (TSan), a race detector.9//10//===----------------------------------------------------------------------===//11#include "tsan_report.h"12#include "tsan_platform.h"13#include "tsan_rtl.h"14#include "sanitizer_common/sanitizer_file.h"15#include "sanitizer_common/sanitizer_placement_new.h"16#include "sanitizer_common/sanitizer_report_decorator.h"17#include "sanitizer_common/sanitizer_stacktrace_printer.h"1819namespace __tsan {2021class Decorator: public __sanitizer::SanitizerCommonDecorator {22public:23Decorator() : SanitizerCommonDecorator() { }24const char *Access() { return Blue(); }25const char *ThreadDescription() { return Cyan(); }26const char *Location() { return Green(); }27const char *Sleep() { return Yellow(); }28const char *Mutex() { return Magenta(); }29};3031ReportDesc::ReportDesc()32: tag(kExternalTagNone)33, stacks()34, mops()35, locs()36, mutexes()37, threads()38, unique_tids()39, sleep()40, count() {41}4243ReportMop::ReportMop()44: mset() {45}4647ReportDesc::~ReportDesc() {48// FIXME(dvyukov): it must be leaking a lot of memory.49}5051#if !SANITIZER_GO5253const int kThreadBufSize = 32;54const char *thread_name(char *buf, Tid tid) {55if (tid == kMainTid)56return "main thread";57internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);58return buf;59}6061static const char *ReportTypeString(ReportType typ, uptr tag) {62switch (typ) {63case ReportTypeRace:64return "data race";65case ReportTypeVptrRace:66return "data race on vptr (ctor/dtor vs virtual call)";67case ReportTypeUseAfterFree:68return "heap-use-after-free";69case ReportTypeVptrUseAfterFree:70return "heap-use-after-free (virtual call vs free)";71case ReportTypeExternalRace: {72const char *str = GetReportHeaderFromTag(tag);73return str ? str : "race on external object";74}75case ReportTypeThreadLeak:76return "thread leak";77case ReportTypeMutexDestroyLocked:78return "destroy of a locked mutex";79case ReportTypeMutexDoubleLock:80return "double lock of a mutex";81case ReportTypeMutexInvalidAccess:82return "use of an invalid mutex (e.g. uninitialized or destroyed)";83case ReportTypeMutexBadUnlock:84return "unlock of an unlocked mutex (or by a wrong thread)";85case ReportTypeMutexBadReadLock:86return "read lock of a write locked mutex";87case ReportTypeMutexBadReadUnlock:88return "read unlock of a write locked mutex";89case ReportTypeSignalUnsafe:90return "signal-unsafe call inside of a signal";91case ReportTypeErrnoInSignal:92return "signal handler spoils errno";93case ReportTypeDeadlock:94return "lock-order-inversion (potential deadlock)";95case ReportTypeMutexHeldWrongContext:96return "mutex held in the wrong context";97// No default case so compiler warns us if we miss one98}99UNREACHABLE("missing case");100}101102void PrintStack(const ReportStack *ent) {103if (ent == 0 || ent->frames == 0) {104Printf(" [failed to restore the stack]\n\n");105return;106}107SymbolizedStack *frame = ent->frames;108for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {109InternalScopedString res;110StackTracePrinter::GetOrInit()->RenderFrame(111&res, common_flags()->stack_trace_format, i, frame->info.address,112&frame->info, common_flags()->symbolize_vs_style,113common_flags()->strip_path_prefix);114Printf("%s\n", res.data());115}116Printf("\n");117}118119static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {120for (uptr i = 0; i < mset.Size(); i++) {121if (i == 0)122Printf(" (mutexes:");123const ReportMopMutex m = mset[i];124Printf(" %s M%u", m.write ? "write" : "read", m.id);125Printf(i == mset.Size() - 1 ? ")" : ",");126}127}128129static const char *MopDesc(bool first, bool write, bool atomic) {130return atomic ? (first ? (write ? "Atomic write" : "Atomic read")131: (write ? "Previous atomic write" : "Previous atomic read"))132: (first ? (write ? "Write" : "Read")133: (write ? "Previous write" : "Previous read"));134}135136static const char *ExternalMopDesc(bool first, bool write) {137return first ? (write ? "Modifying" : "Read-only")138: (write ? "Previous modifying" : "Previous read-only");139}140141static void PrintMop(const ReportMop *mop, bool first) {142Decorator d;143char thrbuf[kThreadBufSize];144Printf("%s", d.Access());145if (mop->external_tag == kExternalTagNone) {146Printf(" %s of size %d at %p by %s",147MopDesc(first, mop->write, mop->atomic), mop->size,148(void *)mop->addr, thread_name(thrbuf, mop->tid));149} else {150const char *object_type = GetObjectTypeFromTag(mop->external_tag);151if (object_type == nullptr)152object_type = "external object";153Printf(" %s access of %s at %p by %s",154ExternalMopDesc(first, mop->write), object_type,155(void *)mop->addr, thread_name(thrbuf, mop->tid));156}157PrintMutexSet(mop->mset);158Printf(":\n");159Printf("%s", d.Default());160PrintStack(mop->stack);161}162163static void PrintLocation(const ReportLocation *loc) {164Decorator d;165char thrbuf[kThreadBufSize];166bool print_stack = false;167Printf("%s", d.Location());168if (loc->type == ReportLocationGlobal) {169const DataInfo &global = loc->global;170if (global.size != 0)171Printf(" Location is global '%s' of size %zu at %p (%s+0x%zx)\n\n",172global.name, global.size, reinterpret_cast<void *>(global.start),173StripModuleName(global.module), global.module_offset);174else175Printf(" Location is global '%s' at %p (%s+0x%zx)\n\n", global.name,176reinterpret_cast<void *>(global.start),177StripModuleName(global.module), global.module_offset);178} else if (loc->type == ReportLocationHeap) {179char thrbuf[kThreadBufSize];180const char *object_type = GetObjectTypeFromTag(loc->external_tag);181if (!object_type) {182Printf(" Location is heap block of size %zu at %p allocated by %s:\n",183loc->heap_chunk_size,184reinterpret_cast<void *>(loc->heap_chunk_start),185thread_name(thrbuf, loc->tid));186} else {187Printf(" Location is %s of size %zu at %p allocated by %s:\n",188object_type, loc->heap_chunk_size,189reinterpret_cast<void *>(loc->heap_chunk_start),190thread_name(thrbuf, loc->tid));191}192print_stack = true;193} else if (loc->type == ReportLocationStack) {194Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));195} else if (loc->type == ReportLocationTLS) {196Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));197} else if (loc->type == ReportLocationFD) {198Printf(" Location is file descriptor %d %s by %s at:\n", loc->fd,199loc->fd_closed ? "destroyed" : "created",200thread_name(thrbuf, loc->tid));201print_stack = true;202}203Printf("%s", d.Default());204if (print_stack)205PrintStack(loc->stack);206}207208static void PrintMutexShort(const ReportMutex *rm, const char *after) {209Decorator d;210Printf("%sM%d%s%s", d.Mutex(), rm->id, d.Default(), after);211}212213static void PrintMutexShortWithAddress(const ReportMutex *rm,214const char *after) {215Decorator d;216Printf("%sM%d (%p)%s%s", d.Mutex(), rm->id,217reinterpret_cast<void *>(rm->addr), d.Default(), after);218}219220static void PrintMutex(const ReportMutex *rm) {221Decorator d;222Printf("%s", d.Mutex());223Printf(" Mutex M%u (%p) created at:\n", rm->id,224reinterpret_cast<void *>(rm->addr));225Printf("%s", d.Default());226PrintStack(rm->stack);227}228229static void PrintThread(const ReportThread *rt) {230Decorator d;231if (rt->id == kMainTid) // Little sense in describing the main thread.232return;233Printf("%s", d.ThreadDescription());234Printf(" Thread T%d", rt->id);235if (rt->name && rt->name[0] != '\0')236Printf(" '%s'", rt->name);237char thrbuf[kThreadBufSize];238const char *thread_status = rt->running ? "running" : "finished";239if (rt->thread_type == ThreadType::Worker) {240Printf(" (tid=%llu, %s) is a GCD worker thread\n", rt->os_id,241thread_status);242Printf("\n");243Printf("%s", d.Default());244return;245}246Printf(" (tid=%llu, %s) created by %s", rt->os_id, thread_status,247thread_name(thrbuf, rt->parent_tid));248if (rt->stack)249Printf(" at:");250Printf("\n");251Printf("%s", d.Default());252PrintStack(rt->stack);253}254255static void PrintSleep(const ReportStack *s) {256Decorator d;257Printf("%s", d.Sleep());258Printf(" As if synchronized via sleep:\n");259Printf("%s", d.Default());260PrintStack(s);261}262263static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {264if (rep->mops.Size())265return rep->mops[0]->stack;266if (rep->stacks.Size())267return rep->stacks[0];268if (rep->mutexes.Size())269return rep->mutexes[0]->stack;270if (rep->threads.Size())271return rep->threads[0]->stack;272return 0;273}274275static const SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {276if (const SymbolizedStack *f = SkipInternalFrames(frames))277return f;278return frames; // Fallback to the top frame.279}280281void PrintReport(const ReportDesc *rep) {282Decorator d;283Printf("==================\n");284const char *rep_typ_str = ReportTypeString(rep->typ, rep->tag);285Printf("%s", d.Warning());286Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str,287(int)internal_getpid());288Printf("%s", d.Default());289290if (rep->typ == ReportTypeErrnoInSignal)291Printf(" Signal %u handler invoked at:\n", rep->signum);292293if (rep->typ == ReportTypeDeadlock) {294char thrbuf[kThreadBufSize];295Printf(" Cycle in lock order graph: ");296for (uptr i = 0; i < rep->mutexes.Size(); i++)297PrintMutexShortWithAddress(rep->mutexes[i], " => ");298PrintMutexShort(rep->mutexes[0], "\n\n");299CHECK_GT(rep->mutexes.Size(), 0U);300CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),301rep->stacks.Size());302for (uptr i = 0; i < rep->mutexes.Size(); i++) {303Printf(" Mutex ");304PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],305" acquired here while holding mutex ");306PrintMutexShort(rep->mutexes[i], " in ");307Printf("%s", d.ThreadDescription());308Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));309Printf("%s", d.Default());310if (flags()->second_deadlock_stack) {311PrintStack(rep->stacks[2*i]);312Printf(" Mutex ");313PrintMutexShort(rep->mutexes[i],314" previously acquired by the same thread here:\n");315PrintStack(rep->stacks[2*i+1]);316} else {317PrintStack(rep->stacks[i]);318if (i == 0)319Printf(" Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "320"to get more informative warning message\n\n");321}322}323} else {324for (uptr i = 0; i < rep->stacks.Size(); i++) {325if (i)326Printf(" and:\n");327PrintStack(rep->stacks[i]);328}329}330331for (uptr i = 0; i < rep->mops.Size(); i++)332PrintMop(rep->mops[i], i == 0);333334if (rep->sleep)335PrintSleep(rep->sleep);336337for (uptr i = 0; i < rep->locs.Size(); i++)338PrintLocation(rep->locs[i]);339340if (rep->typ != ReportTypeDeadlock) {341for (uptr i = 0; i < rep->mutexes.Size(); i++)342PrintMutex(rep->mutexes[i]);343}344345for (uptr i = 0; i < rep->threads.Size(); i++)346PrintThread(rep->threads[i]);347348if (rep->typ == ReportTypeThreadLeak && rep->count > 1)349Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);350351if (ReportStack *stack = ChooseSummaryStack(rep)) {352if (const SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))353ReportErrorSummary(rep_typ_str, frame->info);354}355356if (common_flags()->print_module_map == 2)357DumpProcessMap();358359Printf("==================\n");360}361362#else // #if !SANITIZER_GO363364const Tid kMainGoroutineId = 1;365366void PrintStack(const ReportStack *ent) {367if (ent == 0 || ent->frames == 0) {368Printf(" [failed to restore the stack]\n");369return;370}371SymbolizedStack *frame = ent->frames;372for (int i = 0; frame; frame = frame->next, i++) {373const AddressInfo &info = frame->info;374Printf(" %s()\n %s:%d +0x%zx\n", info.function,375StripPathPrefix(info.file, common_flags()->strip_path_prefix),376info.line, info.module_offset);377}378}379380static void PrintMop(const ReportMop *mop, bool first) {381Printf("\n");382Printf("%s at %p by ",383(first ? (mop->write ? "Write" : "Read")384: (mop->write ? "Previous write" : "Previous read")),385reinterpret_cast<void *>(mop->addr));386if (mop->tid == kMainGoroutineId)387Printf("main goroutine:\n");388else389Printf("goroutine %d:\n", mop->tid);390PrintStack(mop->stack);391}392393static void PrintLocation(const ReportLocation *loc) {394switch (loc->type) {395case ReportLocationHeap: {396Printf("\n");397Printf("Heap block of size %zu at %p allocated by ", loc->heap_chunk_size,398reinterpret_cast<void *>(loc->heap_chunk_start));399if (loc->tid == kMainGoroutineId)400Printf("main goroutine:\n");401else402Printf("goroutine %d:\n", loc->tid);403PrintStack(loc->stack);404break;405}406case ReportLocationGlobal: {407Printf("\n");408Printf("Global var %s of size %zu at %p declared at %s:%zu\n",409loc->global.name, loc->global.size,410reinterpret_cast<void *>(loc->global.start), loc->global.file,411loc->global.line);412break;413}414default:415break;416}417}418419static void PrintThread(const ReportThread *rt) {420if (rt->id == kMainGoroutineId)421return;422Printf("\n");423Printf("Goroutine %d (%s) created at:\n",424rt->id, rt->running ? "running" : "finished");425PrintStack(rt->stack);426}427428void PrintReport(const ReportDesc *rep) {429Printf("==================\n");430if (rep->typ == ReportTypeRace) {431Printf("WARNING: DATA RACE");432for (uptr i = 0; i < rep->mops.Size(); i++)433PrintMop(rep->mops[i], i == 0);434for (uptr i = 0; i < rep->locs.Size(); i++)435PrintLocation(rep->locs[i]);436for (uptr i = 0; i < rep->threads.Size(); i++)437PrintThread(rep->threads[i]);438} else if (rep->typ == ReportTypeDeadlock) {439Printf("WARNING: DEADLOCK\n");440for (uptr i = 0; i < rep->mutexes.Size(); i++) {441Printf("Goroutine %d lock mutex %u while holding mutex %u:\n", 999,442rep->mutexes[i]->id,443rep->mutexes[(i + 1) % rep->mutexes.Size()]->id);444PrintStack(rep->stacks[2*i]);445Printf("\n");446Printf("Mutex %u was previously locked here:\n",447rep->mutexes[(i + 1) % rep->mutexes.Size()]->id);448PrintStack(rep->stacks[2*i + 1]);449Printf("\n");450}451}452Printf("==================\n");453}454455#endif456457} // namespace __tsan458459460