1 //===-- tsan_report.cc ----------------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of ThreadSanitizer (TSan), a race detector.
12 //===----------------------------------------------------------------------===//
13 #include "tsan_report.h"
14 #include "tsan_platform.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"
22 ReportStack::ReportStack() : frames(nullptr), suppressable(false) {}
24 ReportStack
*ReportStack::New() {
25 void *mem
= internal_alloc(MBlockReportStack
, sizeof(ReportStack
));
26 return new(mem
) ReportStack();
29 ReportLocation::ReportLocation(ReportLocationType type
)
30 : type(type
), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
31 fd(0), suppressable(false), stack(nullptr) {}
33 ReportLocation
*ReportLocation::New(ReportLocationType type
) {
34 void *mem
= internal_alloc(MBlockReportStack
, sizeof(ReportLocation
));
35 return new(mem
) ReportLocation(type
);
38 class Decorator
: public __sanitizer::SanitizerCommonDecorator
{
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(); }
55 ReportDesc::ReportDesc()
56 : stacks(MBlockReportStack
)
57 , mops(MBlockReportMop
)
58 , locs(MBlockReportLoc
)
59 , mutexes(MBlockReportMutex
)
60 , threads(MBlockReportThread
)
61 , unique_tids(MBlockReportThread
)
66 ReportMop::ReportMop()
67 : mset(MBlockReportMutex
) {
70 ReportDesc::~ReportDesc() {
71 // FIXME(dvyukov): it must be leaking a lot of memory.
76 const int kThreadBufSize
= 32;
77 const char *thread_name(char *buf
, int tid
) {
80 internal_snprintf(buf
, kThreadBufSize
, "thread T%d", tid
);
84 static const char *ReportTypeString(ReportType typ
) {
85 if (typ
== ReportTypeRace
)
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
)
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
== ReportTypeMutexInvalidAccess
)
100 return "use of an invalid mutex (e.g. uninitialized or destroyed)";
101 if (typ
== ReportTypeMutexBadUnlock
)
102 return "unlock of an unlocked mutex (or by a wrong thread)";
103 if (typ
== ReportTypeMutexBadReadLock
)
104 return "read lock of a write locked mutex";
105 if (typ
== ReportTypeMutexBadReadUnlock
)
106 return "read unlock of a write locked mutex";
107 if (typ
== ReportTypeSignalUnsafe
)
108 return "signal-unsafe call inside of a signal";
109 if (typ
== ReportTypeErrnoInSignal
)
110 return "signal handler spoils errno";
111 if (typ
== ReportTypeDeadlock
)
112 return "lock-order-inversion (potential deadlock)";
117 static const char *const kInterposedFunctionPrefix
= "wrap_";
119 static const char *const kInterposedFunctionPrefix
= "__interceptor_";
122 void PrintStack(const ReportStack
*ent
) {
123 if (ent
== 0 || ent
->frames
== 0) {
124 Printf(" [failed to restore the stack]\n\n");
127 SymbolizedStack
*frame
= ent
->frames
;
128 for (int i
= 0; frame
&& frame
->info
.address
; frame
= frame
->next
, i
++) {
129 InternalScopedString
res(2 * GetPageSizeCached());
130 RenderFrame(&res
, common_flags()->stack_trace_format
, i
, frame
->info
,
131 common_flags()->symbolize_vs_style
,
132 common_flags()->strip_path_prefix
, kInterposedFunctionPrefix
);
133 Printf("%s\n", res
.data());
138 static void PrintMutexSet(Vector
<ReportMopMutex
> const& mset
) {
139 for (uptr i
= 0; i
< mset
.Size(); i
++) {
141 Printf(" (mutexes:");
142 const ReportMopMutex m
= mset
[i
];
143 Printf(" %s M%llu", m
.write
? "write" : "read", m
.id
);
144 Printf(i
== mset
.Size() - 1 ? ")" : ",");
148 static const char *MopDesc(bool first
, bool write
, bool atomic
) {
149 return atomic
? (first
? (write
? "Atomic write" : "Atomic read")
150 : (write
? "Previous atomic write" : "Previous atomic read"))
151 : (first
? (write
? "Write" : "Read")
152 : (write
? "Previous write" : "Previous read"));
155 static void PrintMop(const ReportMop
*mop
, bool first
) {
157 char thrbuf
[kThreadBufSize
];
158 Printf("%s", d
.Access());
159 Printf(" %s of size %d at %p by %s",
160 MopDesc(first
, mop
->write
, mop
->atomic
),
161 mop
->size
, (void*)mop
->addr
,
162 thread_name(thrbuf
, mop
->tid
));
163 PrintMutexSet(mop
->mset
);
165 Printf("%s", d
.EndAccess());
166 PrintStack(mop
->stack
);
169 static void PrintLocation(const ReportLocation
*loc
) {
171 char thrbuf
[kThreadBufSize
];
172 bool print_stack
= false;
173 Printf("%s", d
.Location());
174 if (loc
->type
== ReportLocationGlobal
) {
175 const DataInfo
&global
= loc
->global
;
176 if (global
.size
!= 0)
177 Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
178 global
.name
, global
.size
, global
.start
,
179 StripModuleName(global
.module
), global
.module_offset
);
181 Printf(" Location is global '%s' at %p (%s+%p)\n\n", global
.name
,
182 global
.start
, StripModuleName(global
.module
),
183 global
.module_offset
);
184 } else if (loc
->type
== ReportLocationHeap
) {
185 char thrbuf
[kThreadBufSize
];
186 Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
187 loc
->heap_chunk_size
, loc
->heap_chunk_start
,
188 thread_name(thrbuf
, loc
->tid
));
190 } else if (loc
->type
== ReportLocationStack
) {
191 Printf(" Location is stack of %s.\n\n", thread_name(thrbuf
, loc
->tid
));
192 } else if (loc
->type
== ReportLocationTLS
) {
193 Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf
, loc
->tid
));
194 } else if (loc
->type
== ReportLocationFD
) {
195 Printf(" Location is file descriptor %d created by %s at:\n",
196 loc
->fd
, thread_name(thrbuf
, loc
->tid
));
199 Printf("%s", d
.EndLocation());
201 PrintStack(loc
->stack
);
204 static void PrintMutexShort(const ReportMutex
*rm
, const char *after
) {
206 Printf("%sM%zd%s%s", d
.Mutex(), rm
->id
, d
.EndMutex(), after
);
209 static void PrintMutexShortWithAddress(const ReportMutex
*rm
,
212 Printf("%sM%zd (%p)%s%s", d
.Mutex(), rm
->id
, rm
->addr
, d
.EndMutex(), after
);
215 static void PrintMutex(const ReportMutex
*rm
) {
218 Printf("%s", d
.Mutex());
219 Printf(" Mutex M%llu is already destroyed.\n\n", rm
->id
);
220 Printf("%s", d
.EndMutex());
222 Printf("%s", d
.Mutex());
223 Printf(" Mutex M%llu (%p) created at:\n", rm
->id
, rm
->addr
);
224 Printf("%s", d
.EndMutex());
225 PrintStack(rm
->stack
);
229 static void PrintThread(const ReportThread
*rt
) {
231 if (rt
->id
== 0) // Little sense in describing the main thread.
233 Printf("%s", d
.ThreadDescription());
234 Printf(" Thread T%d", rt
->id
);
235 if (rt
->name
&& rt
->name
[0] != '\0')
236 Printf(" '%s'", rt
->name
);
237 char thrbuf
[kThreadBufSize
];
238 Printf(" (tid=%zu, %s) created by %s",
239 rt
->os_id
, rt
->running
? "running" : "finished",
240 thread_name(thrbuf
, rt
->parent_tid
));
244 Printf("%s", d
.EndThreadDescription());
245 PrintStack(rt
->stack
);
248 static void PrintSleep(const ReportStack
*s
) {
250 Printf("%s", d
.Sleep());
251 Printf(" As if synchronized via sleep:\n");
252 Printf("%s", d
.EndSleep());
256 static ReportStack
*ChooseSummaryStack(const ReportDesc
*rep
) {
257 if (rep
->mops
.Size())
258 return rep
->mops
[0]->stack
;
259 if (rep
->stacks
.Size())
260 return rep
->stacks
[0];
261 if (rep
->mutexes
.Size())
262 return rep
->mutexes
[0]->stack
;
263 if (rep
->threads
.Size())
264 return rep
->threads
[0]->stack
;
268 static bool FrameIsInternal(const SymbolizedStack
*frame
) {
271 const char *file
= frame
->info
.file
;
272 const char *module
= frame
->info
.module
;
274 (internal_strstr(file
, "tsan_interceptors.cc") ||
275 internal_strstr(file
, "sanitizer_common_interceptors.inc") ||
276 internal_strstr(file
, "tsan_interface_")))
278 if (module
!= 0 && (internal_strstr(module
, "libclang_rt.tsan_")))
283 static SymbolizedStack
*SkipTsanInternalFrames(SymbolizedStack
*frames
) {
284 while (FrameIsInternal(frames
) && frames
->next
)
285 frames
= frames
->next
;
289 void PrintReport(const ReportDesc
*rep
) {
291 Printf("==================\n");
292 const char *rep_typ_str
= ReportTypeString(rep
->typ
);
293 Printf("%s", d
.Warning());
294 Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str
,
295 (int)internal_getpid());
296 Printf("%s", d
.EndWarning());
298 if (rep
->typ
== ReportTypeDeadlock
) {
299 char thrbuf
[kThreadBufSize
];
300 Printf(" Cycle in lock order graph: ");
301 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++)
302 PrintMutexShortWithAddress(rep
->mutexes
[i
], " => ");
303 PrintMutexShort(rep
->mutexes
[0], "\n\n");
304 CHECK_GT(rep
->mutexes
.Size(), 0U);
305 CHECK_EQ(rep
->mutexes
.Size() * (flags()->second_deadlock_stack
? 2 : 1),
307 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++) {
309 PrintMutexShort(rep
->mutexes
[(i
+ 1) % rep
->mutexes
.Size()],
310 " acquired here while holding mutex ");
311 PrintMutexShort(rep
->mutexes
[i
], " in ");
312 Printf("%s", d
.ThreadDescription());
313 Printf("%s:\n", thread_name(thrbuf
, rep
->unique_tids
[i
]));
314 Printf("%s", d
.EndThreadDescription());
315 if (flags()->second_deadlock_stack
) {
316 PrintStack(rep
->stacks
[2*i
]);
318 PrintMutexShort(rep
->mutexes
[i
],
319 " previously acquired by the same thread here:\n");
320 PrintStack(rep
->stacks
[2*i
+1]);
322 PrintStack(rep
->stacks
[i
]);
324 Printf(" Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
325 "to get more informative warning message\n\n");
329 for (uptr i
= 0; i
< rep
->stacks
.Size(); i
++) {
332 PrintStack(rep
->stacks
[i
]);
336 for (uptr i
= 0; i
< rep
->mops
.Size(); i
++)
337 PrintMop(rep
->mops
[i
], i
== 0);
340 PrintSleep(rep
->sleep
);
342 for (uptr i
= 0; i
< rep
->locs
.Size(); i
++)
343 PrintLocation(rep
->locs
[i
]);
345 if (rep
->typ
!= ReportTypeDeadlock
) {
346 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++)
347 PrintMutex(rep
->mutexes
[i
]);
350 for (uptr i
= 0; i
< rep
->threads
.Size(); i
++)
351 PrintThread(rep
->threads
[i
]);
353 if (rep
->typ
== ReportTypeThreadLeak
&& rep
->count
> 1)
354 Printf(" And %d more similar thread leaks.\n\n", rep
->count
- 1);
356 if (ReportStack
*stack
= ChooseSummaryStack(rep
)) {
357 if (SymbolizedStack
*frame
= SkipTsanInternalFrames(stack
->frames
))
358 ReportErrorSummary(rep_typ_str
, frame
->info
);
361 if (common_flags()->print_module_map
== 2) PrintModuleMap();
363 Printf("==================\n");
366 #else // #if !SANITIZER_GO
368 const int kMainThreadId
= 1;
370 void PrintStack(const ReportStack
*ent
) {
371 if (ent
== 0 || ent
->frames
== 0) {
372 Printf(" [failed to restore the stack]\n");
375 SymbolizedStack
*frame
= ent
->frames
;
376 for (int i
= 0; frame
; frame
= frame
->next
, i
++) {
377 const AddressInfo
&info
= frame
->info
;
378 Printf(" %s()\n %s:%d +0x%zx\n", info
.function
,
379 StripPathPrefix(info
.file
, common_flags()->strip_path_prefix
),
380 info
.line
, (void *)info
.module_offset
);
384 static void PrintMop(const ReportMop
*mop
, bool first
) {
386 Printf("%s at %p by ",
387 (first
? (mop
->write
? "Write" : "Read")
388 : (mop
->write
? "Previous write" : "Previous read")), mop
->addr
);
389 if (mop
->tid
== kMainThreadId
)
390 Printf("main goroutine:\n");
392 Printf("goroutine %d:\n", mop
->tid
);
393 PrintStack(mop
->stack
);
396 static void PrintLocation(const ReportLocation
*loc
) {
398 case ReportLocationHeap
: {
400 Printf("Heap block of size %zu at %p allocated by ",
401 loc
->heap_chunk_size
, loc
->heap_chunk_start
);
402 if (loc
->tid
== kMainThreadId
)
403 Printf("main goroutine:\n");
405 Printf("goroutine %d:\n", loc
->tid
);
406 PrintStack(loc
->stack
);
409 case ReportLocationGlobal
: {
411 Printf("Global var %s of size %zu at %p declared at %s:%zu\n",
412 loc
->global
.name
, loc
->global
.size
, loc
->global
.start
,
413 loc
->global
.file
, loc
->global
.line
);
421 static void PrintThread(const ReportThread
*rt
) {
422 if (rt
->id
== kMainThreadId
)
425 Printf("Goroutine %d (%s) created at:\n",
426 rt
->id
, rt
->running
? "running" : "finished");
427 PrintStack(rt
->stack
);
430 void PrintReport(const ReportDesc
*rep
) {
431 Printf("==================\n");
432 if (rep
->typ
== ReportTypeRace
) {
433 Printf("WARNING: DATA RACE");
434 for (uptr i
= 0; i
< rep
->mops
.Size(); i
++)
435 PrintMop(rep
->mops
[i
], i
== 0);
436 for (uptr i
= 0; i
< rep
->locs
.Size(); i
++)
437 PrintLocation(rep
->locs
[i
]);
438 for (uptr i
= 0; i
< rep
->threads
.Size(); i
++)
439 PrintThread(rep
->threads
[i
]);
440 } else if (rep
->typ
== ReportTypeDeadlock
) {
441 Printf("WARNING: DEADLOCK\n");
442 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++) {
443 Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
444 999, rep
->mutexes
[i
]->id
,
445 rep
->mutexes
[(i
+1) % rep
->mutexes
.Size()]->id
);
446 PrintStack(rep
->stacks
[2*i
]);
448 Printf("Mutex %d was previously locked here:\n",
449 rep
->mutexes
[(i
+1) % rep
->mutexes
.Size()]->id
);
450 PrintStack(rep
->stacks
[2*i
+ 1]);
454 Printf("==================\n");
459 } // namespace __tsan