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