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