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