Home | History | Annotate | Download | only in rtl
      1 //===-- tsan_report.cc ----------------------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of ThreadSanitizer (TSan), a race detector.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 #include "tsan_report.h"
     14 #include "tsan_platform.h"
     15 #include "tsan_rtl.h"
     16 
     17 namespace __tsan {
     18 
     19 ReportDesc::ReportDesc()
     20     : stacks(MBlockReportStack)
     21     , mops(MBlockReportMop)
     22     , locs(MBlockReportLoc)
     23     , mutexes(MBlockReportMutex)
     24     , threads(MBlockReportThread)
     25     , sleep() {
     26 }
     27 
     28 ReportMop::ReportMop()
     29     : mset(MBlockReportMutex) {
     30 }
     31 
     32 ReportDesc::~ReportDesc() {
     33   // FIXME(dvyukov): it must be leaking a lot of memory.
     34 }
     35 
     36 #ifndef TSAN_GO
     37 
     38 const int kThreadBufSize = 32;
     39 const char *thread_name(char *buf, int tid) {
     40   if (tid == 0)
     41     return "main thread";
     42   internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
     43   return buf;
     44 }
     45 
     46 static const char *ReportTypeString(ReportType typ) {
     47   if (typ == ReportTypeRace)
     48     return "data race";
     49   if (typ == ReportTypeUseAfterFree)
     50     return "heap-use-after-free";
     51   if (typ == ReportTypeThreadLeak)
     52     return "thread leak";
     53   if (typ == ReportTypeMutexDestroyLocked)
     54     return "destroy of a locked mutex";
     55   if (typ == ReportTypeSignalUnsafe)
     56     return "signal-unsafe call inside of a signal";
     57   if (typ == ReportTypeErrnoInSignal)
     58     return "signal handler spoils errno";
     59   return "";
     60 }
     61 
     62 void PrintStack(const ReportStack *ent) {
     63   if (ent == 0) {
     64     Printf("    [failed to restore the stack]\n\n");
     65     return;
     66   }
     67   for (int i = 0; ent; ent = ent->next, i++) {
     68     Printf("    #%d %s %s:%d", i, ent->func, ent->file, ent->line);
     69     if (ent->col)
     70       Printf(":%d", ent->col);
     71     if (ent->module && ent->offset)
     72       Printf(" (%s+%p)\n", ent->module, (void*)ent->offset);
     73     else
     74       Printf(" (%p)\n", (void*)ent->pc);
     75   }
     76   Printf("\n");
     77 }
     78 
     79 static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
     80   for (uptr i = 0; i < mset.Size(); i++) {
     81     if (i == 0)
     82       Printf(" (mutexes:");
     83     const ReportMopMutex m = mset[i];
     84     Printf(" %s M%llu", m.write ? "write" : "read", m.id);
     85     Printf(i == mset.Size() - 1 ? ")" : ",");
     86   }
     87 }
     88 
     89 static const char *MopDesc(bool first, bool write, bool atomic) {
     90   return atomic ? (first ? (write ? "Atomic write" : "Atomic read")
     91                 : (write ? "Previous atomic write" : "Previous atomic read"))
     92                 : (first ? (write ? "Write" : "Read")
     93                 : (write ? "Previous write" : "Previous read"));
     94 }
     95 
     96 static void PrintMop(const ReportMop *mop, bool first) {
     97   char thrbuf[kThreadBufSize];
     98   Printf("  %s of size %d at %p by %s",
     99       MopDesc(first, mop->write, mop->atomic),
    100       mop->size, (void*)mop->addr,
    101       thread_name(thrbuf, mop->tid));
    102   PrintMutexSet(mop->mset);
    103   Printf(":\n");
    104   PrintStack(mop->stack);
    105 }
    106 
    107 static void PrintLocation(const ReportLocation *loc) {
    108   char thrbuf[kThreadBufSize];
    109   if (loc->type == ReportLocationGlobal) {
    110     Printf("  Location is global '%s' of size %zu at %zx (%s+%p)\n\n",
    111                loc->name, loc->size, loc->addr, loc->module, loc->offset);
    112   } else if (loc->type == ReportLocationHeap) {
    113     char thrbuf[kThreadBufSize];
    114     Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
    115         loc->size, loc->addr, thread_name(thrbuf, loc->tid));
    116     PrintStack(loc->stack);
    117   } else if (loc->type == ReportLocationStack) {
    118     Printf("  Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
    119   } else if (loc->type == ReportLocationTLS) {
    120     Printf("  Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
    121   } else if (loc->type == ReportLocationFD) {
    122     Printf("  Location is file descriptor %d created by %s at:\n",
    123         loc->fd, thread_name(thrbuf, loc->tid));
    124     PrintStack(loc->stack);
    125   }
    126 }
    127 
    128 static void PrintMutex(const ReportMutex *rm) {
    129   if (rm->destroyed) {
    130     Printf("  Mutex M%llu is already destroyed.\n\n", rm->id);
    131   } else {
    132     Printf("  Mutex M%llu created at:\n", rm->id);
    133     PrintStack(rm->stack);
    134   }
    135 }
    136 
    137 static void PrintThread(const ReportThread *rt) {
    138   if (rt->id == 0)  // Little sense in describing the main thread.
    139     return;
    140   Printf("  Thread T%d", rt->id);
    141   if (rt->name && rt->name[0] != '\0')
    142     Printf(" '%s'", rt->name);
    143   char thrbuf[kThreadBufSize];
    144   Printf(" (tid=%zu, %s) created by %s",
    145     rt->pid, rt->running ? "running" : "finished",
    146     thread_name(thrbuf, rt->parent_tid));
    147   if (rt->stack)
    148     Printf(" at:");
    149   Printf("\n");
    150   PrintStack(rt->stack);
    151 }
    152 
    153 static void PrintSleep(const ReportStack *s) {
    154   Printf("  As if synchronized via sleep:\n");
    155   PrintStack(s);
    156 }
    157 
    158 static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
    159   if (rep->mops.Size())
    160     return rep->mops[0]->stack;
    161   if (rep->stacks.Size())
    162     return rep->stacks[0];
    163   if (rep->mutexes.Size())
    164     return rep->mutexes[0]->stack;
    165   if (rep->threads.Size())
    166     return rep->threads[0]->stack;
    167   return 0;
    168 }
    169 
    170 ReportStack *SkipTsanInternalFrames(ReportStack *ent) {
    171   while (FrameIsInternal(ent) && ent->next)
    172     ent = ent->next;
    173   return ent;
    174 }
    175 
    176 void PrintReport(const ReportDesc *rep) {
    177   Printf("==================\n");
    178   const char *rep_typ_str = ReportTypeString(rep->typ);
    179   Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, GetPid());
    180 
    181   for (uptr i = 0; i < rep->stacks.Size(); i++) {
    182     if (i)
    183       Printf("  and:\n");
    184     PrintStack(rep->stacks[i]);
    185   }
    186 
    187   for (uptr i = 0; i < rep->mops.Size(); i++)
    188     PrintMop(rep->mops[i], i == 0);
    189 
    190   if (rep->sleep)
    191     PrintSleep(rep->sleep);
    192 
    193   for (uptr i = 0; i < rep->locs.Size(); i++)
    194     PrintLocation(rep->locs[i]);
    195 
    196   for (uptr i = 0; i < rep->mutexes.Size(); i++)
    197     PrintMutex(rep->mutexes[i]);
    198 
    199   for (uptr i = 0; i < rep->threads.Size(); i++)
    200     PrintThread(rep->threads[i]);
    201 
    202   if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep)))
    203     ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func);
    204 
    205   Printf("==================\n");
    206 }
    207 
    208 #else
    209 
    210 void PrintStack(const ReportStack *ent) {
    211   if (ent == 0) {
    212     Printf("  [failed to restore the stack]\n\n");
    213     return;
    214   }
    215   for (int i = 0; ent; ent = ent->next, i++) {
    216     Printf("  %s()\n      %s:%d +0x%zx\n",
    217         ent->func, ent->file, ent->line, (void*)ent->offset);
    218   }
    219   Printf("\n");
    220 }
    221 
    222 static void PrintMop(const ReportMop *mop, bool first) {
    223   Printf("%s by goroutine %d:\n",
    224       (first ? (mop->write ? "Write" : "Read")
    225              : (mop->write ? "Previous write" : "Previous read")),
    226       mop->tid);
    227   PrintStack(mop->stack);
    228 }
    229 
    230 static void PrintThread(const ReportThread *rt) {
    231   if (rt->id == 0)  // Little sense in describing the main thread.
    232     return;
    233   Printf("Goroutine %d (%s) created at:\n",
    234     rt->id, rt->running ? "running" : "finished");
    235   PrintStack(rt->stack);
    236 }
    237 
    238 void PrintReport(const ReportDesc *rep) {
    239   Printf("==================\n");
    240   Printf("WARNING: DATA RACE\n");
    241   for (uptr i = 0; i < rep->mops.Size(); i++)
    242     PrintMop(rep->mops[i], i == 0);
    243   for (uptr i = 0; i < rep->threads.Size(); i++)
    244     PrintThread(rep->threads[i]);
    245   Printf("==================\n");
    246 }
    247 
    248 #endif
    249 
    250 }  // namespace __tsan
    251