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
17namespace __tsan {
18
19ReportDesc::ReportDesc()
20    : stacks(MBlockReportStack)
21    , mops(MBlockReportMop)
22    , locs(MBlockReportLoc)
23    , mutexes(MBlockReportMutex)
24    , threads(MBlockReportThread)
25    , sleep() {
26}
27
28ReportDesc::~ReportDesc() {
29}
30
31#ifndef TSAN_GO
32
33static void PrintHeader(ReportType typ) {
34  TsanPrintf("WARNING: ThreadSanitizer: ");
35
36  if (typ == ReportTypeRace)
37    TsanPrintf("data race");
38  else if (typ == ReportTypeUseAfterFree)
39    TsanPrintf("heap-use-after-free");
40  else if (typ == ReportTypeThreadLeak)
41    TsanPrintf("thread leak");
42  else if (typ == ReportTypeMutexDestroyLocked)
43    TsanPrintf("destroy of a locked mutex");
44  else if (typ == ReportTypeSignalUnsafe)
45    TsanPrintf("signal-unsafe call inside of a signal");
46  else if (typ == ReportTypeErrnoInSignal)
47    TsanPrintf("signal handler spoils errno");
48
49  TsanPrintf(" (pid=%d)\n", GetPid());
50}
51
52void PrintStack(const ReportStack *ent) {
53  for (int i = 0; ent; ent = ent->next, i++) {
54    TsanPrintf("    #%d %s %s:%d", i, ent->func, ent->file, ent->line);
55    if (ent->col)
56      TsanPrintf(":%d", ent->col);
57    if (ent->module && ent->offset)
58      TsanPrintf(" (%s+%p)\n", ent->module, (void*)ent->offset);
59    else
60      TsanPrintf(" (%p)\n", (void*)ent->pc);
61  }
62  TsanPrintf("\n");
63}
64
65static void PrintMop(const ReportMop *mop, bool first) {
66  TsanPrintf("  %s of size %d at %p",
67      (first ? (mop->write ? "Write" : "Read")
68             : (mop->write ? "Previous write" : "Previous read")),
69      mop->size, (void*)mop->addr);
70  if (mop->tid == 0)
71    TsanPrintf(" by main thread:\n");
72  else
73    TsanPrintf(" by thread %d:\n", mop->tid);
74  PrintStack(mop->stack);
75}
76
77static void PrintLocation(const ReportLocation *loc) {
78  if (loc->type == ReportLocationGlobal) {
79    TsanPrintf("  Location is global '%s' of size %zu at %zx %s:%d\n",
80               loc->name, loc->size, loc->addr, loc->file, loc->line);
81  } else if (loc->type == ReportLocationHeap) {
82    TsanPrintf("  Location is heap block of size %zu at %p allocated",
83        loc->size, loc->addr);
84    if (loc->tid == 0)
85      TsanPrintf(" by main thread:\n");
86    else
87      TsanPrintf(" by thread %d:\n", loc->tid);
88    PrintStack(loc->stack);
89  } else if (loc->type == ReportLocationStack) {
90    TsanPrintf("  Location is stack of thread %d:\n", loc->tid);
91  }
92}
93
94static void PrintMutex(const ReportMutex *rm) {
95  if (rm->stack == 0)
96    return;
97  TsanPrintf("  Mutex %d created at:\n", rm->id);
98  PrintStack(rm->stack);
99}
100
101static void PrintThread(const ReportThread *rt) {
102  if (rt->id == 0)  // Little sense in describing the main thread.
103    return;
104  TsanPrintf("  Thread %d", rt->id);
105  if (rt->name)
106    TsanPrintf(" '%s'", rt->name);
107  TsanPrintf(" (%s)", rt->running ? "running" : "finished");
108  if (rt->stack)
109    TsanPrintf(" created at:");
110  TsanPrintf("\n");
111  PrintStack(rt->stack);
112}
113
114static void PrintSleep(const ReportStack *s) {
115  TsanPrintf("  As if synchronized via sleep:\n");
116  PrintStack(s);
117}
118
119void PrintReport(const ReportDesc *rep) {
120  TsanPrintf("==================\n");
121  PrintHeader(rep->typ);
122
123  for (uptr i = 0; i < rep->stacks.Size(); i++) {
124    if (i)
125      TsanPrintf("  and:\n");
126    PrintStack(rep->stacks[i]);
127  }
128
129  for (uptr i = 0; i < rep->mops.Size(); i++)
130    PrintMop(rep->mops[i], i == 0);
131
132  if (rep->sleep)
133    PrintSleep(rep->sleep);
134
135  for (uptr i = 0; i < rep->locs.Size(); i++)
136    PrintLocation(rep->locs[i]);
137
138  for (uptr i = 0; i < rep->mutexes.Size(); i++)
139    PrintMutex(rep->mutexes[i]);
140
141  for (uptr i = 0; i < rep->threads.Size(); i++)
142    PrintThread(rep->threads[i]);
143
144  TsanPrintf("==================\n");
145}
146
147#else
148
149void PrintStack(const ReportStack *ent) {
150  for (int i = 0; ent; ent = ent->next, i++) {
151    TsanPrintf("  %s()\n      %s:%d +0x%zx\n",
152        ent->func, ent->file, ent->line, (void*)ent->offset);
153  }
154  TsanPrintf("\n");
155}
156
157static void PrintMop(const ReportMop *mop, bool first) {
158  TsanPrintf("%s by goroutine %d:\n",
159      (first ? (mop->write ? "Write" : "Read")
160             : (mop->write ? "Previous write" : "Previous read")),
161      mop->tid);
162  PrintStack(mop->stack);
163}
164
165static void PrintThread(const ReportThread *rt) {
166  if (rt->id == 0)  // Little sense in describing the main thread.
167    return;
168  TsanPrintf("Goroutine %d (%s) created at:\n",
169    rt->id, rt->running ? "running" : "finished");
170  PrintStack(rt->stack);
171}
172
173void PrintReport(const ReportDesc *rep) {
174  TsanPrintf("==================\n");
175  TsanPrintf("WARNING: DATA RACE\n");
176  for (uptr i = 0; i < rep->mops.Size(); i++)
177    PrintMop(rep->mops[i], i == 0);
178  for (uptr i = 0; i < rep->threads.Size(); i++)
179    PrintThread(rep->threads[i]);
180  TsanPrintf("==================\n");
181}
182
183#endif
184
185}  // namespace __tsan
186