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