xref: /NextBSD/contrib/compiler-rt/lib/tsan/rtl/tsan_report.cc (revision 84d351007654069f9643c8e4b4802a7f5f08ee42)
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 #include "sanitizer_common/sanitizer_placement_new.h"
17 #include "sanitizer_common/sanitizer_report_decorator.h"
18 #include "sanitizer_common/sanitizer_stacktrace_printer.h"
19 
20 namespace __tsan {
21 
ReportStack()22 ReportStack::ReportStack() : frames(nullptr), suppressable(false) {}
23 
New()24 ReportStack *ReportStack::New() {
25   void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
26   return new(mem) ReportStack();
27 }
28 
ReportLocation(ReportLocationType type)29 ReportLocation::ReportLocation(ReportLocationType type)
30     : type(type), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
31       fd(0), suppressable(false), stack(nullptr) {}
32 
New(ReportLocationType type)33 ReportLocation *ReportLocation::New(ReportLocationType type) {
34   void *mem = internal_alloc(MBlockReportStack, sizeof(ReportLocation));
35   return new(mem) ReportLocation(type);
36 }
37 
38 class Decorator: public __sanitizer::SanitizerCommonDecorator {
39  public:
Decorator()40   Decorator() : SanitizerCommonDecorator() { }
Warning()41   const char *Warning()    { return Red(); }
EndWarning()42   const char *EndWarning() { return Default(); }
Access()43   const char *Access()     { return Blue(); }
EndAccess()44   const char *EndAccess()  { return Default(); }
ThreadDescription()45   const char *ThreadDescription()    { return Cyan(); }
EndThreadDescription()46   const char *EndThreadDescription() { return Default(); }
Location()47   const char *Location()   { return Green(); }
EndLocation()48   const char *EndLocation() { return Default(); }
Sleep()49   const char *Sleep()   { return Yellow(); }
EndSleep()50   const char *EndSleep() { return Default(); }
Mutex()51   const char *Mutex()   { return Magenta(); }
EndMutex()52   const char *EndMutex() { return Default(); }
53 };
54 
ReportDesc()55 ReportDesc::ReportDesc()
56     : stacks(MBlockReportStack)
57     , mops(MBlockReportMop)
58     , locs(MBlockReportLoc)
59     , mutexes(MBlockReportMutex)
60     , threads(MBlockReportThread)
61     , unique_tids(MBlockReportThread)
62     , sleep()
63     , count() {
64 }
65 
ReportMop()66 ReportMop::ReportMop()
67     : mset(MBlockReportMutex) {
68 }
69 
~ReportDesc()70 ReportDesc::~ReportDesc() {
71   // FIXME(dvyukov): it must be leaking a lot of memory.
72 }
73 
74 #ifndef SANITIZER_GO
75 
76 const int kThreadBufSize = 32;
thread_name(char * buf,int tid)77 const char *thread_name(char *buf, int tid) {
78   if (tid == 0)
79     return "main thread";
80   internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
81   return buf;
82 }
83 
ReportTypeString(ReportType typ)84 static const char *ReportTypeString(ReportType typ) {
85   if (typ == ReportTypeRace)
86     return "data race";
87   if (typ == ReportTypeVptrRace)
88     return "data race on vptr (ctor/dtor vs virtual call)";
89   if (typ == ReportTypeUseAfterFree)
90     return "heap-use-after-free";
91   if (typ == ReportTypeVptrUseAfterFree)
92     return "heap-use-after-free (virtual call vs free)";
93   if (typ == ReportTypeThreadLeak)
94     return "thread leak";
95   if (typ == ReportTypeMutexDestroyLocked)
96     return "destroy of a locked mutex";
97   if (typ == ReportTypeMutexDoubleLock)
98     return "double lock of a mutex";
99   if (typ == ReportTypeMutexBadUnlock)
100     return "unlock of an unlocked mutex (or by a wrong thread)";
101   if (typ == ReportTypeMutexBadReadLock)
102     return "read lock of a write locked mutex";
103   if (typ == ReportTypeMutexBadReadUnlock)
104     return "read unlock of a write locked mutex";
105   if (typ == ReportTypeSignalUnsafe)
106     return "signal-unsafe call inside of a signal";
107   if (typ == ReportTypeErrnoInSignal)
108     return "signal handler spoils errno";
109   if (typ == ReportTypeDeadlock)
110     return "lock-order-inversion (potential deadlock)";
111   return "";
112 }
113 
PrintStack(const ReportStack * ent)114 void PrintStack(const ReportStack *ent) {
115   if (ent == 0 || ent->frames == 0) {
116     Printf("    [failed to restore the stack]\n\n");
117     return;
118   }
119   SymbolizedStack *frame = ent->frames;
120   for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
121     InternalScopedString res(2 * GetPageSizeCached());
122     RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
123                 common_flags()->symbolize_vs_style,
124                 common_flags()->strip_path_prefix, "__interceptor_");
125     Printf("%s\n", res.data());
126   }
127   Printf("\n");
128 }
129 
PrintMutexSet(Vector<ReportMopMutex> const & mset)130 static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
131   for (uptr i = 0; i < mset.Size(); i++) {
132     if (i == 0)
133       Printf(" (mutexes:");
134     const ReportMopMutex m = mset[i];
135     Printf(" %s M%llu", m.write ? "write" : "read", m.id);
136     Printf(i == mset.Size() - 1 ? ")" : ",");
137   }
138 }
139 
MopDesc(bool first,bool write,bool atomic)140 static const char *MopDesc(bool first, bool write, bool atomic) {
141   return atomic ? (first ? (write ? "Atomic write" : "Atomic read")
142                 : (write ? "Previous atomic write" : "Previous atomic read"))
143                 : (first ? (write ? "Write" : "Read")
144                 : (write ? "Previous write" : "Previous read"));
145 }
146 
PrintMop(const ReportMop * mop,bool first)147 static void PrintMop(const ReportMop *mop, bool first) {
148   Decorator d;
149   char thrbuf[kThreadBufSize];
150   Printf("%s", d.Access());
151   Printf("  %s of size %d at %p by %s",
152       MopDesc(first, mop->write, mop->atomic),
153       mop->size, (void*)mop->addr,
154       thread_name(thrbuf, mop->tid));
155   PrintMutexSet(mop->mset);
156   Printf(":\n");
157   Printf("%s", d.EndAccess());
158   PrintStack(mop->stack);
159 }
160 
PrintLocation(const ReportLocation * loc)161 static void PrintLocation(const ReportLocation *loc) {
162   Decorator d;
163   char thrbuf[kThreadBufSize];
164   bool print_stack = false;
165   Printf("%s", d.Location());
166   if (loc->type == ReportLocationGlobal) {
167     const DataInfo &global = loc->global;
168     Printf("  Location is global '%s' of size %zu at %p (%s+%p)\n\n",
169            global.name, global.size, global.start,
170            StripModuleName(global.module), global.module_offset);
171   } else if (loc->type == ReportLocationHeap) {
172     char thrbuf[kThreadBufSize];
173     Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
174            loc->heap_chunk_size, loc->heap_chunk_start,
175            thread_name(thrbuf, loc->tid));
176     print_stack = true;
177   } else if (loc->type == ReportLocationStack) {
178     Printf("  Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
179   } else if (loc->type == ReportLocationTLS) {
180     Printf("  Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
181   } else if (loc->type == ReportLocationFD) {
182     Printf("  Location is file descriptor %d created by %s at:\n",
183         loc->fd, thread_name(thrbuf, loc->tid));
184     print_stack = true;
185   }
186   Printf("%s", d.EndLocation());
187   if (print_stack)
188     PrintStack(loc->stack);
189 }
190 
PrintMutexShort(const ReportMutex * rm,const char * after)191 static void PrintMutexShort(const ReportMutex *rm, const char *after) {
192   Decorator d;
193   Printf("%sM%zd%s%s", d.Mutex(), rm->id, d.EndMutex(), after);
194 }
195 
PrintMutexShortWithAddress(const ReportMutex * rm,const char * after)196 static void PrintMutexShortWithAddress(const ReportMutex *rm,
197                                        const char *after) {
198   Decorator d;
199   Printf("%sM%zd (%p)%s%s", d.Mutex(), rm->id, rm->addr, d.EndMutex(), after);
200 }
201 
PrintMutex(const ReportMutex * rm)202 static void PrintMutex(const ReportMutex *rm) {
203   Decorator d;
204   if (rm->destroyed) {
205     Printf("%s", d.Mutex());
206     Printf("  Mutex M%llu is already destroyed.\n\n", rm->id);
207     Printf("%s", d.EndMutex());
208   } else {
209     Printf("%s", d.Mutex());
210     Printf("  Mutex M%llu (%p) created at:\n", rm->id, rm->addr);
211     Printf("%s", d.EndMutex());
212     PrintStack(rm->stack);
213   }
214 }
215 
PrintThread(const ReportThread * rt)216 static void PrintThread(const ReportThread *rt) {
217   Decorator d;
218   if (rt->id == 0)  // Little sense in describing the main thread.
219     return;
220   Printf("%s", d.ThreadDescription());
221   Printf("  Thread T%d", rt->id);
222   if (rt->name && rt->name[0] != '\0')
223     Printf(" '%s'", rt->name);
224   char thrbuf[kThreadBufSize];
225   Printf(" (tid=%zu, %s) created by %s",
226     rt->pid, rt->running ? "running" : "finished",
227     thread_name(thrbuf, rt->parent_tid));
228   if (rt->stack)
229     Printf(" at:");
230   Printf("\n");
231   Printf("%s", d.EndThreadDescription());
232   PrintStack(rt->stack);
233 }
234 
PrintSleep(const ReportStack * s)235 static void PrintSleep(const ReportStack *s) {
236   Decorator d;
237   Printf("%s", d.Sleep());
238   Printf("  As if synchronized via sleep:\n");
239   Printf("%s", d.EndSleep());
240   PrintStack(s);
241 }
242 
ChooseSummaryStack(const ReportDesc * rep)243 static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
244   if (rep->mops.Size())
245     return rep->mops[0]->stack;
246   if (rep->stacks.Size())
247     return rep->stacks[0];
248   if (rep->mutexes.Size())
249     return rep->mutexes[0]->stack;
250   if (rep->threads.Size())
251     return rep->threads[0]->stack;
252   return 0;
253 }
254 
FrameIsInternal(const SymbolizedStack * frame)255 static bool FrameIsInternal(const SymbolizedStack *frame) {
256   if (frame == 0)
257     return false;
258   const char *file = frame->info.file;
259   return file != 0 &&
260          (internal_strstr(file, "tsan_interceptors.cc") ||
261           internal_strstr(file, "sanitizer_common_interceptors.inc") ||
262           internal_strstr(file, "tsan_interface_"));
263 }
264 
SkipTsanInternalFrames(SymbolizedStack * frames)265 static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
266   while (FrameIsInternal(frames) && frames->next)
267     frames = frames->next;
268   return frames;
269 }
270 
PrintReport(const ReportDesc * rep)271 void PrintReport(const ReportDesc *rep) {
272   Decorator d;
273   Printf("==================\n");
274   const char *rep_typ_str = ReportTypeString(rep->typ);
275   Printf("%s", d.Warning());
276   Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str,
277          (int)internal_getpid());
278   Printf("%s", d.EndWarning());
279 
280   if (rep->typ == ReportTypeDeadlock) {
281     char thrbuf[kThreadBufSize];
282     Printf("  Cycle in lock order graph: ");
283     for (uptr i = 0; i < rep->mutexes.Size(); i++)
284       PrintMutexShortWithAddress(rep->mutexes[i], " => ");
285     PrintMutexShort(rep->mutexes[0], "\n\n");
286     CHECK_GT(rep->mutexes.Size(), 0U);
287     CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),
288              rep->stacks.Size());
289     for (uptr i = 0; i < rep->mutexes.Size(); i++) {
290       Printf("  Mutex ");
291       PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],
292                       " acquired here while holding mutex ");
293       PrintMutexShort(rep->mutexes[i], " in ");
294       Printf("%s", d.ThreadDescription());
295       Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));
296       Printf("%s", d.EndThreadDescription());
297       if (flags()->second_deadlock_stack) {
298         PrintStack(rep->stacks[2*i]);
299         Printf("  Mutex ");
300         PrintMutexShort(rep->mutexes[i],
301                         " previously acquired by the same thread here:\n");
302         PrintStack(rep->stacks[2*i+1]);
303       } else {
304         PrintStack(rep->stacks[i]);
305         if (i == 0)
306           Printf("    Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
307                  "to get more informative warning message\n\n");
308       }
309     }
310   } else {
311     for (uptr i = 0; i < rep->stacks.Size(); i++) {
312       if (i)
313         Printf("  and:\n");
314       PrintStack(rep->stacks[i]);
315     }
316   }
317 
318   for (uptr i = 0; i < rep->mops.Size(); i++)
319     PrintMop(rep->mops[i], i == 0);
320 
321   if (rep->sleep)
322     PrintSleep(rep->sleep);
323 
324   for (uptr i = 0; i < rep->locs.Size(); i++)
325     PrintLocation(rep->locs[i]);
326 
327   if (rep->typ != ReportTypeDeadlock) {
328     for (uptr i = 0; i < rep->mutexes.Size(); i++)
329       PrintMutex(rep->mutexes[i]);
330   }
331 
332   for (uptr i = 0; i < rep->threads.Size(); i++)
333     PrintThread(rep->threads[i]);
334 
335   if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
336     Printf("  And %d more similar thread leaks.\n\n", rep->count - 1);
337 
338   if (ReportStack *stack = ChooseSummaryStack(rep)) {
339     if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
340       ReportErrorSummary(rep_typ_str, frame->info);
341   }
342 
343   Printf("==================\n");
344 }
345 
346 #else  // #ifndef SANITIZER_GO
347 
348 const int kMainThreadId = 1;
349 
PrintStack(const ReportStack * ent)350 void PrintStack(const ReportStack *ent) {
351   if (ent == 0 || ent->frames == 0) {
352     Printf("  [failed to restore the stack]\n");
353     return;
354   }
355   SymbolizedStack *frame = ent->frames;
356   for (int i = 0; frame; frame = frame->next, i++) {
357     const AddressInfo &info = frame->info;
358     Printf("  %s()\n      %s:%d +0x%zx\n", info.function,
359         StripPathPrefix(info.file, common_flags()->strip_path_prefix),
360         info.line, (void *)info.module_offset);
361   }
362 }
363 
PrintMop(const ReportMop * mop,bool first)364 static void PrintMop(const ReportMop *mop, bool first) {
365   Printf("\n");
366   Printf("%s by ",
367       (first ? (mop->write ? "Write" : "Read")
368              : (mop->write ? "Previous write" : "Previous read")));
369   if (mop->tid == kMainThreadId)
370     Printf("main goroutine:\n");
371   else
372     Printf("goroutine %d:\n", mop->tid);
373   PrintStack(mop->stack);
374 }
375 
PrintThread(const ReportThread * rt)376 static void PrintThread(const ReportThread *rt) {
377   if (rt->id == kMainThreadId)
378     return;
379   Printf("\n");
380   Printf("Goroutine %d (%s) created at:\n",
381     rt->id, rt->running ? "running" : "finished");
382   PrintStack(rt->stack);
383 }
384 
PrintReport(const ReportDesc * rep)385 void PrintReport(const ReportDesc *rep) {
386   Printf("==================\n");
387   if (rep->typ == ReportTypeRace) {
388     Printf("WARNING: DATA RACE");
389     for (uptr i = 0; i < rep->mops.Size(); i++)
390       PrintMop(rep->mops[i], i == 0);
391     for (uptr i = 0; i < rep->threads.Size(); i++)
392       PrintThread(rep->threads[i]);
393   } else if (rep->typ == ReportTypeDeadlock) {
394     Printf("WARNING: DEADLOCK\n");
395     for (uptr i = 0; i < rep->mutexes.Size(); i++) {
396       Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
397           999, rep->mutexes[i]->id,
398           rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
399       PrintStack(rep->stacks[2*i]);
400       Printf("\n");
401       Printf("Mutex %d was previously locked here:\n",
402           rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
403       PrintStack(rep->stacks[2*i + 1]);
404       Printf("\n");
405     }
406   }
407   Printf("==================\n");
408 }
409 
410 #endif
411 
412 }  // namespace __tsan
413