1//===-- tsan_report.cc ----------------------------------------------------===//
2//
3// This file is distributed under the University of Illinois Open Source
4// License. See LICENSE.TXT for details.
5//
6//===----------------------------------------------------------------------===//
7//
8// This file is a part of ThreadSanitizer (TSan), a race detector.
9//
10//===----------------------------------------------------------------------===//
11#include "tsan_report.h"
12#include "tsan_platform.h"
13#include "tsan_rtl.h"
14#include "sanitizer_common/sanitizer_placement_new.h"
15#include "sanitizer_common/sanitizer_report_decorator.h"
16#include "sanitizer_common/sanitizer_stacktrace_printer.h"
17
18namespace __tsan {
19
20ReportStack::ReportStack() : next(nullptr), info(), suppressable(false) {}
21
22ReportStack *ReportStack::New(uptr addr) {
23  void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
24  ReportStack *res = new(mem) ReportStack();
25  res->info.address = addr;
26  return res;
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 TSAN_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
114void PrintStack(const ReportStack *ent) {
115  if (ent == 0) {
116    Printf("    [failed to restore the stack]\n\n");
117    return;
118  }
119  for (int i = 0; ent && ent->info.address; ent = ent->next, i++) {
120    InternalScopedString res(2 * GetPageSizeCached());
121    RenderFrame(&res, common_flags()->stack_trace_format, i, ent->info,
122                common_flags()->strip_path_prefix, "__interceptor_");
123    Printf("%s\n", res.data());
124  }
125  Printf("\n");
126}
127
128static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
129  for (uptr i = 0; i < mset.Size(); i++) {
130    if (i == 0)
131      Printf(" (mutexes:");
132    const ReportMopMutex m = mset[i];
133    Printf(" %s M%llu", m.write ? "write" : "read", m.id);
134    Printf(i == mset.Size() - 1 ? ")" : ",");
135  }
136}
137
138static const char *MopDesc(bool first, bool write, bool atomic) {
139  return atomic ? (first ? (write ? "Atomic write" : "Atomic read")
140                : (write ? "Previous atomic write" : "Previous atomic read"))
141                : (first ? (write ? "Write" : "Read")
142                : (write ? "Previous write" : "Previous read"));
143}
144
145static void PrintMop(const ReportMop *mop, bool first) {
146  Decorator d;
147  char thrbuf[kThreadBufSize];
148  Printf("%s", d.Access());
149  Printf("  %s of size %d at %p by %s",
150      MopDesc(first, mop->write, mop->atomic),
151      mop->size, (void*)mop->addr,
152      thread_name(thrbuf, mop->tid));
153  PrintMutexSet(mop->mset);
154  Printf(":\n");
155  Printf("%s", d.EndAccess());
156  PrintStack(mop->stack);
157}
158
159static void PrintLocation(const ReportLocation *loc) {
160  Decorator d;
161  char thrbuf[kThreadBufSize];
162  bool print_stack = false;
163  Printf("%s", d.Location());
164  if (loc->type == ReportLocationGlobal) {
165    const DataInfo &global = loc->global;
166    Printf("  Location is global '%s' of size %zu at %p (%s+%p)\n\n",
167           global.name, global.size, global.start,
168           StripModuleName(global.module), global.module_offset);
169  } else if (loc->type == ReportLocationHeap) {
170    char thrbuf[kThreadBufSize];
171    Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
172           loc->heap_chunk_size, loc->heap_chunk_start,
173           thread_name(thrbuf, loc->tid));
174    print_stack = true;
175  } else if (loc->type == ReportLocationStack) {
176    Printf("  Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
177  } else if (loc->type == ReportLocationTLS) {
178    Printf("  Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
179  } else if (loc->type == ReportLocationFD) {
180    Printf("  Location is file descriptor %d created by %s at:\n",
181        loc->fd, thread_name(thrbuf, loc->tid));
182    print_stack = true;
183  }
184  Printf("%s", d.EndLocation());
185  if (print_stack)
186    PrintStack(loc->stack);
187}
188
189static void PrintMutexShort(const ReportMutex *rm, const char *after) {
190  Decorator d;
191  Printf("%sM%zd%s%s", d.Mutex(), rm->id, d.EndMutex(), after);
192}
193
194static void PrintMutexShortWithAddress(const ReportMutex *rm,
195                                       const char *after) {
196  Decorator d;
197  Printf("%sM%zd (%p)%s%s", d.Mutex(), rm->id, rm->addr, d.EndMutex(), after);
198}
199
200static void PrintMutex(const ReportMutex *rm) {
201  Decorator d;
202  if (rm->destroyed) {
203    Printf("%s", d.Mutex());
204    Printf("  Mutex M%llu is already destroyed.\n\n", rm->id);
205    Printf("%s", d.EndMutex());
206  } else {
207    Printf("%s", d.Mutex());
208    Printf("  Mutex M%llu (%p) created at:\n", rm->id, rm->addr);
209    Printf("%s", d.EndMutex());
210    PrintStack(rm->stack);
211  }
212}
213
214static void PrintThread(const ReportThread *rt) {
215  Decorator d;
216  if (rt->id == 0)  // Little sense in describing the main thread.
217    return;
218  Printf("%s", d.ThreadDescription());
219  Printf("  Thread T%d", rt->id);
220  if (rt->name && rt->name[0] != '\0')
221    Printf(" '%s'", rt->name);
222  char thrbuf[kThreadBufSize];
223  Printf(" (tid=%zu, %s) created by %s",
224    rt->pid, rt->running ? "running" : "finished",
225    thread_name(thrbuf, rt->parent_tid));
226  if (rt->stack)
227    Printf(" at:");
228  Printf("\n");
229  Printf("%s", d.EndThreadDescription());
230  PrintStack(rt->stack);
231}
232
233static void PrintSleep(const ReportStack *s) {
234  Decorator d;
235  Printf("%s", d.Sleep());
236  Printf("  As if synchronized via sleep:\n");
237  Printf("%s", d.EndSleep());
238  PrintStack(s);
239}
240
241static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
242  if (rep->mops.Size())
243    return rep->mops[0]->stack;
244  if (rep->stacks.Size())
245    return rep->stacks[0];
246  if (rep->mutexes.Size())
247    return rep->mutexes[0]->stack;
248  if (rep->threads.Size())
249    return rep->threads[0]->stack;
250  return 0;
251}
252
253ReportStack *SkipTsanInternalFrames(ReportStack *ent) {
254  while (FrameIsInternal(ent) && ent->next)
255    ent = ent->next;
256  return ent;
257}
258
259void PrintReport(const ReportDesc *rep) {
260  Decorator d;
261  Printf("==================\n");
262  const char *rep_typ_str = ReportTypeString(rep->typ);
263  Printf("%s", d.Warning());
264  Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str,
265         (int)internal_getpid());
266  Printf("%s", d.EndWarning());
267
268  if (rep->typ == ReportTypeDeadlock) {
269    char thrbuf[kThreadBufSize];
270    Printf("  Cycle in lock order graph: ");
271    for (uptr i = 0; i < rep->mutexes.Size(); i++)
272      PrintMutexShortWithAddress(rep->mutexes[i], " => ");
273    PrintMutexShort(rep->mutexes[0], "\n\n");
274    CHECK_GT(rep->mutexes.Size(), 0U);
275    CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),
276             rep->stacks.Size());
277    for (uptr i = 0; i < rep->mutexes.Size(); i++) {
278      Printf("  Mutex ");
279      PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],
280                      " acquired here while holding mutex ");
281      PrintMutexShort(rep->mutexes[i], " in ");
282      Printf("%s", d.ThreadDescription());
283      Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));
284      Printf("%s", d.EndThreadDescription());
285      if (flags()->second_deadlock_stack) {
286        PrintStack(rep->stacks[2*i]);
287        Printf("  Mutex ");
288        PrintMutexShort(rep->mutexes[i],
289                        " previously acquired by the same thread here:\n");
290        PrintStack(rep->stacks[2*i+1]);
291      } else {
292        PrintStack(rep->stacks[i]);
293        if (i == 0)
294          Printf("    Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
295                 "to get more informative warning message\n\n");
296      }
297    }
298  } else {
299    for (uptr i = 0; i < rep->stacks.Size(); i++) {
300      if (i)
301        Printf("  and:\n");
302      PrintStack(rep->stacks[i]);
303    }
304  }
305
306  for (uptr i = 0; i < rep->mops.Size(); i++)
307    PrintMop(rep->mops[i], i == 0);
308
309  if (rep->sleep)
310    PrintSleep(rep->sleep);
311
312  for (uptr i = 0; i < rep->locs.Size(); i++)
313    PrintLocation(rep->locs[i]);
314
315  if (rep->typ != ReportTypeDeadlock) {
316    for (uptr i = 0; i < rep->mutexes.Size(); i++)
317      PrintMutex(rep->mutexes[i]);
318  }
319
320  for (uptr i = 0; i < rep->threads.Size(); i++)
321    PrintThread(rep->threads[i]);
322
323  if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
324    Printf("  And %d more similar thread leaks.\n\n", rep->count - 1);
325
326  if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) {
327    const AddressInfo &info = ent->info;
328    ReportErrorSummary(rep_typ_str, info.file, info.line, info.function);
329  }
330
331  Printf("==================\n");
332}
333
334#else  // #ifndef TSAN_GO
335
336const int kMainThreadId = 1;
337
338void PrintStack(const ReportStack *ent) {
339  if (ent == 0) {
340    Printf("  [failed to restore the stack]\n");
341    return;
342  }
343  for (int i = 0; ent; ent = ent->next, i++) {
344    const AddressInfo &info = ent->info;
345    Printf("  %s()\n      %s:%d +0x%zx\n", info.function, info.file, info.line,
346           (void *)info.module_offset);
347  }
348}
349
350static void PrintMop(const ReportMop *mop, bool first) {
351  Printf("\n");
352  Printf("%s by ",
353      (first ? (mop->write ? "Write" : "Read")
354             : (mop->write ? "Previous write" : "Previous read")));
355  if (mop->tid == kMainThreadId)
356    Printf("main goroutine:\n");
357  else
358    Printf("goroutine %d:\n", mop->tid);
359  PrintStack(mop->stack);
360}
361
362static void PrintThread(const ReportThread *rt) {
363  if (rt->id == kMainThreadId)
364    return;
365  Printf("\n");
366  Printf("Goroutine %d (%s) created at:\n",
367    rt->id, rt->running ? "running" : "finished");
368  PrintStack(rt->stack);
369}
370
371void PrintReport(const ReportDesc *rep) {
372  Printf("==================\n");
373  if (rep->typ == ReportTypeRace) {
374    Printf("WARNING: DATA RACE");
375    for (uptr i = 0; i < rep->mops.Size(); i++)
376      PrintMop(rep->mops[i], i == 0);
377    for (uptr i = 0; i < rep->threads.Size(); i++)
378      PrintThread(rep->threads[i]);
379  } else if (rep->typ == ReportTypeDeadlock) {
380    Printf("WARNING: DEADLOCK\n");
381    for (uptr i = 0; i < rep->mutexes.Size(); i++) {
382      Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
383          999, rep->mutexes[i]->id,
384          rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
385      PrintStack(rep->stacks[2*i]);
386      Printf("\n");
387      Printf("Mutex %d was previously locked here:\n",
388          rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
389      PrintStack(rep->stacks[2*i + 1]);
390      Printf("\n");
391    }
392  }
393  Printf("==================\n");
394}
395
396#endif
397
398}  // namespace __tsan
399