brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.9 KiB · 22ba428 Raw
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