• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ReportDesc()19 ReportDesc::ReportDesc()
20     : stacks(MBlockReportStack)
21     , mops(MBlockReportMop)
22     , locs(MBlockReportLoc)
23     , mutexes(MBlockReportMutex)
24     , threads(MBlockReportThread)
25     , sleep() {
26 }
27 
ReportMop()28 ReportMop::ReportMop()
29     : mset(MBlockReportMutex) {
30 }
31 
~ReportDesc()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;
thread_name(char * buf,int tid)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 
ReportTypeString(ReportType typ)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 
PrintStack(const ReportStack * ent)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 
PrintMutexSet(Vector<ReportMopMutex> const & mset)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 
MopDesc(bool first,bool write,bool atomic)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 
PrintMop(const ReportMop * mop,bool first)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 
PrintLocation(const ReportLocation * loc)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 
PrintMutex(const ReportMutex * rm)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 
PrintThread(const ReportThread * rt)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 
PrintSleep(const ReportStack * s)153 static void PrintSleep(const ReportStack *s) {
154   Printf("  As if synchronized via sleep:\n");
155   PrintStack(s);
156 }
157 
ChooseSummaryStack(const ReportDesc * rep)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 
SkipTsanInternalFrames(ReportStack * ent)170 ReportStack *SkipTsanInternalFrames(ReportStack *ent) {
171   while (FrameIsInternal(ent) && ent->next)
172     ent = ent->next;
173   return ent;
174 }
175 
PrintReport(const ReportDesc * rep)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 
PrintStack(const ReportStack * ent)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 
PrintMop(const ReportMop * mop,bool first)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 
PrintThread(const ReportThread * rt)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 
PrintReport(const ReportDesc * rep)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