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