1 //===-- tsan_report.cc ----------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
10 //===----------------------------------------------------------------------===//
11 #include "tsan_report.h"
12 #include "tsan_platform.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"
20 ReportStack::ReportStack() : frames(nullptr), suppressable(false) {}
22 ReportStack
*ReportStack::New() {
23 void *mem
= internal_alloc(MBlockReportStack
, sizeof(ReportStack
));
24 return new(mem
) ReportStack();
27 ReportLocation::ReportLocation(ReportLocationType type
)
28 : type(type
), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
29 fd(0), suppressable(false), stack(nullptr) {}
31 ReportLocation
*ReportLocation::New(ReportLocationType type
) {
32 void *mem
= internal_alloc(MBlockReportStack
, sizeof(ReportLocation
));
33 return new(mem
) ReportLocation(type
);
36 class Decorator
: public __sanitizer::SanitizerCommonDecorator
{
38 Decorator() : SanitizerCommonDecorator() { }
39 const char *Warning() { return Red(); }
40 const char *EndWarning() { return Default(); }
41 const char *Access() { return Blue(); }
42 const char *EndAccess() { return Default(); }
43 const char *ThreadDescription() { return Cyan(); }
44 const char *EndThreadDescription() { return Default(); }
45 const char *Location() { return Green(); }
46 const char *EndLocation() { return Default(); }
47 const char *Sleep() { return Yellow(); }
48 const char *EndSleep() { return Default(); }
49 const char *Mutex() { return Magenta(); }
50 const char *EndMutex() { return Default(); }
53 ReportDesc::ReportDesc()
54 : stacks(MBlockReportStack
)
55 , mops(MBlockReportMop
)
56 , locs(MBlockReportLoc
)
57 , mutexes(MBlockReportMutex
)
58 , threads(MBlockReportThread
)
59 , unique_tids(MBlockReportThread
)
64 ReportMop::ReportMop()
65 : mset(MBlockReportMutex
) {
68 ReportDesc::~ReportDesc() {
69 // FIXME(dvyukov): it must be leaking a lot of memory.
74 const int kThreadBufSize
= 32;
75 const char *thread_name(char *buf
, int tid
) {
78 internal_snprintf(buf
, kThreadBufSize
, "thread T%d", tid
);
82 static const char *ReportTypeString(ReportType typ
) {
83 if (typ
== ReportTypeRace
)
85 if (typ
== ReportTypeVptrRace
)
86 return "data race on vptr (ctor/dtor vs virtual call)";
87 if (typ
== ReportTypeUseAfterFree
)
88 return "heap-use-after-free";
89 if (typ
== ReportTypeVptrUseAfterFree
)
90 return "heap-use-after-free (virtual call vs free)";
91 if (typ
== ReportTypeThreadLeak
)
93 if (typ
== ReportTypeMutexDestroyLocked
)
94 return "destroy of a locked mutex";
95 if (typ
== ReportTypeMutexDoubleLock
)
96 return "double lock of a mutex";
97 if (typ
== ReportTypeMutexInvalidAccess
)
98 return "use of an invalid mutex (e.g. uninitialized or destroyed)";
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)";
115 static const char *const kInterposedFunctionPrefix
= "wrap_";
117 static const char *const kInterposedFunctionPrefix
= "__interceptor_";
120 void PrintStack(const ReportStack
*ent
) {
121 if (ent
== 0 || ent
->frames
== 0) {
122 Printf(" [failed to restore the stack]\n\n");
125 SymbolizedStack
*frame
= ent
->frames
;
126 for (int i
= 0; frame
&& frame
->info
.address
; frame
= frame
->next
, i
++) {
127 InternalScopedString
res(2 * GetPageSizeCached());
128 RenderFrame(&res
, common_flags()->stack_trace_format
, i
, frame
->info
,
129 common_flags()->symbolize_vs_style
,
130 common_flags()->strip_path_prefix
, kInterposedFunctionPrefix
);
131 Printf("%s\n", res
.data());
136 static void PrintMutexSet(Vector
<ReportMopMutex
> const& mset
) {
137 for (uptr i
= 0; i
< mset
.Size(); i
++) {
139 Printf(" (mutexes:");
140 const ReportMopMutex m
= mset
[i
];
141 Printf(" %s M%llu", m
.write
? "write" : "read", m
.id
);
142 Printf(i
== mset
.Size() - 1 ? ")" : ",");
146 static const char *MopDesc(bool first
, bool write
, bool atomic
) {
147 return atomic
? (first
? (write
? "Atomic write" : "Atomic read")
148 : (write
? "Previous atomic write" : "Previous atomic read"))
149 : (first
? (write
? "Write" : "Read")
150 : (write
? "Previous write" : "Previous read"));
153 static void PrintMop(const ReportMop
*mop
, bool first
) {
155 char thrbuf
[kThreadBufSize
];
156 Printf("%s", d
.Access());
157 Printf(" %s of size %d at %p by %s",
158 MopDesc(first
, mop
->write
, mop
->atomic
),
159 mop
->size
, (void*)mop
->addr
,
160 thread_name(thrbuf
, mop
->tid
));
161 PrintMutexSet(mop
->mset
);
163 Printf("%s", d
.EndAccess());
164 PrintStack(mop
->stack
);
167 static void PrintLocation(const ReportLocation
*loc
) {
169 char thrbuf
[kThreadBufSize
];
170 bool print_stack
= false;
171 Printf("%s", d
.Location());
172 if (loc
->type
== ReportLocationGlobal
) {
173 const DataInfo
&global
= loc
->global
;
174 if (global
.size
!= 0)
175 Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
176 global
.name
, global
.size
, global
.start
,
177 StripModuleName(global
.module
), global
.module_offset
);
179 Printf(" Location is global '%s' at %p (%s+%p)\n\n", global
.name
,
180 global
.start
, StripModuleName(global
.module
),
181 global
.module_offset
);
182 } else if (loc
->type
== ReportLocationHeap
) {
183 char thrbuf
[kThreadBufSize
];
184 Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
185 loc
->heap_chunk_size
, loc
->heap_chunk_start
,
186 thread_name(thrbuf
, loc
->tid
));
188 } else if (loc
->type
== ReportLocationStack
) {
189 Printf(" Location is stack of %s.\n\n", thread_name(thrbuf
, loc
->tid
));
190 } else if (loc
->type
== ReportLocationTLS
) {
191 Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf
, loc
->tid
));
192 } else if (loc
->type
== ReportLocationFD
) {
193 Printf(" Location is file descriptor %d created by %s at:\n",
194 loc
->fd
, thread_name(thrbuf
, loc
->tid
));
197 Printf("%s", d
.EndLocation());
199 PrintStack(loc
->stack
);
202 static void PrintMutexShort(const ReportMutex
*rm
, const char *after
) {
204 Printf("%sM%zd%s%s", d
.Mutex(), rm
->id
, d
.EndMutex(), after
);
207 static void PrintMutexShortWithAddress(const ReportMutex
*rm
,
210 Printf("%sM%zd (%p)%s%s", d
.Mutex(), rm
->id
, rm
->addr
, d
.EndMutex(), after
);
213 static void PrintMutex(const ReportMutex
*rm
) {
216 Printf("%s", d
.Mutex());
217 Printf(" Mutex M%llu is already destroyed.\n\n", rm
->id
);
218 Printf("%s", d
.EndMutex());
220 Printf("%s", d
.Mutex());
221 Printf(" Mutex M%llu (%p) created at:\n", rm
->id
, rm
->addr
);
222 Printf("%s", d
.EndMutex());
223 PrintStack(rm
->stack
);
227 static void PrintThread(const ReportThread
*rt
) {
229 if (rt
->id
== 0) // Little sense in describing the main thread.
231 Printf("%s", d
.ThreadDescription());
232 Printf(" Thread T%d", rt
->id
);
233 if (rt
->name
&& rt
->name
[0] != '\0')
234 Printf(" '%s'", rt
->name
);
235 char thrbuf
[kThreadBufSize
];
236 Printf(" (tid=%zu, %s) created by %s",
237 rt
->os_id
, rt
->running
? "running" : "finished",
238 thread_name(thrbuf
, rt
->parent_tid
));
242 Printf("%s", d
.EndThreadDescription());
243 PrintStack(rt
->stack
);
246 static void PrintSleep(const ReportStack
*s
) {
248 Printf("%s", d
.Sleep());
249 Printf(" As if synchronized via sleep:\n");
250 Printf("%s", d
.EndSleep());
254 static ReportStack
*ChooseSummaryStack(const ReportDesc
*rep
) {
255 if (rep
->mops
.Size())
256 return rep
->mops
[0]->stack
;
257 if (rep
->stacks
.Size())
258 return rep
->stacks
[0];
259 if (rep
->mutexes
.Size())
260 return rep
->mutexes
[0]->stack
;
261 if (rep
->threads
.Size())
262 return rep
->threads
[0]->stack
;
266 static bool FrameIsInternal(const SymbolizedStack
*frame
) {
269 const char *file
= frame
->info
.file
;
270 const char *module
= frame
->info
.module
;
272 (internal_strstr(file
, "tsan_interceptors.cc") ||
273 internal_strstr(file
, "sanitizer_common_interceptors.inc") ||
274 internal_strstr(file
, "tsan_interface_")))
276 if (module
!= 0 && (internal_strstr(module
, "libclang_rt.tsan_")))
281 static SymbolizedStack
*SkipTsanInternalFrames(SymbolizedStack
*frames
) {
282 while (FrameIsInternal(frames
) && frames
->next
)
283 frames
= frames
->next
;
287 void PrintReport(const ReportDesc
*rep
) {
289 Printf("==================\n");
290 const char *rep_typ_str
= ReportTypeString(rep
->typ
);
291 Printf("%s", d
.Warning());
292 Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str
,
293 (int)internal_getpid());
294 Printf("%s", d
.EndWarning());
296 if (rep
->typ
== ReportTypeDeadlock
) {
297 char thrbuf
[kThreadBufSize
];
298 Printf(" Cycle in lock order graph: ");
299 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++)
300 PrintMutexShortWithAddress(rep
->mutexes
[i
], " => ");
301 PrintMutexShort(rep
->mutexes
[0], "\n\n");
302 CHECK_GT(rep
->mutexes
.Size(), 0U);
303 CHECK_EQ(rep
->mutexes
.Size() * (flags()->second_deadlock_stack
? 2 : 1),
305 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++) {
307 PrintMutexShort(rep
->mutexes
[(i
+ 1) % rep
->mutexes
.Size()],
308 " acquired here while holding mutex ");
309 PrintMutexShort(rep
->mutexes
[i
], " in ");
310 Printf("%s", d
.ThreadDescription());
311 Printf("%s:\n", thread_name(thrbuf
, rep
->unique_tids
[i
]));
312 Printf("%s", d
.EndThreadDescription());
313 if (flags()->second_deadlock_stack
) {
314 PrintStack(rep
->stacks
[2*i
]);
316 PrintMutexShort(rep
->mutexes
[i
],
317 " previously acquired by the same thread here:\n");
318 PrintStack(rep
->stacks
[2*i
+1]);
320 PrintStack(rep
->stacks
[i
]);
322 Printf(" Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
323 "to get more informative warning message\n\n");
327 for (uptr i
= 0; i
< rep
->stacks
.Size(); i
++) {
330 PrintStack(rep
->stacks
[i
]);
334 for (uptr i
= 0; i
< rep
->mops
.Size(); i
++)
335 PrintMop(rep
->mops
[i
], i
== 0);
338 PrintSleep(rep
->sleep
);
340 for (uptr i
= 0; i
< rep
->locs
.Size(); i
++)
341 PrintLocation(rep
->locs
[i
]);
343 if (rep
->typ
!= ReportTypeDeadlock
) {
344 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++)
345 PrintMutex(rep
->mutexes
[i
]);
348 for (uptr i
= 0; i
< rep
->threads
.Size(); i
++)
349 PrintThread(rep
->threads
[i
]);
351 if (rep
->typ
== ReportTypeThreadLeak
&& rep
->count
> 1)
352 Printf(" And %d more similar thread leaks.\n\n", rep
->count
- 1);
354 if (ReportStack
*stack
= ChooseSummaryStack(rep
)) {
355 if (SymbolizedStack
*frame
= SkipTsanInternalFrames(stack
->frames
))
356 ReportErrorSummary(rep_typ_str
, frame
->info
);
359 Printf("==================\n");
362 #else // #if !SANITIZER_GO
364 const int kMainThreadId
= 1;
366 void PrintStack(const ReportStack
*ent
) {
367 if (ent
== 0 || ent
->frames
== 0) {
368 Printf(" [failed to restore the stack]\n");
371 SymbolizedStack
*frame
= ent
->frames
;
372 for (int i
= 0; frame
; frame
= frame
->next
, i
++) {
373 const AddressInfo
&info
= frame
->info
;
374 Printf(" %s()\n %s:%d +0x%zx\n", info
.function
,
375 StripPathPrefix(info
.file
, common_flags()->strip_path_prefix
),
376 info
.line
, (void *)info
.module_offset
);
380 static void PrintMop(const ReportMop
*mop
, bool first
) {
382 Printf("%s at %p by ",
383 (first
? (mop
->write
? "Write" : "Read")
384 : (mop
->write
? "Previous write" : "Previous read")), mop
->addr
);
385 if (mop
->tid
== kMainThreadId
)
386 Printf("main goroutine:\n");
388 Printf("goroutine %d:\n", mop
->tid
);
389 PrintStack(mop
->stack
);
392 static void PrintLocation(const ReportLocation
*loc
) {
394 case ReportLocationHeap
: {
396 Printf("Heap block of size %zu at %p allocated by ",
397 loc
->heap_chunk_size
, loc
->heap_chunk_start
);
398 if (loc
->tid
== kMainThreadId
)
399 Printf("main goroutine:\n");
401 Printf("goroutine %d:\n", loc
->tid
);
402 PrintStack(loc
->stack
);
405 case ReportLocationGlobal
: {
407 Printf("Global var %s of size %zu at %p declared at %s:%zu\n",
408 loc
->global
.name
, loc
->global
.size
, loc
->global
.start
,
409 loc
->global
.file
, loc
->global
.line
);
417 static void PrintThread(const ReportThread
*rt
) {
418 if (rt
->id
== kMainThreadId
)
421 Printf("Goroutine %d (%s) created at:\n",
422 rt
->id
, rt
->running
? "running" : "finished");
423 PrintStack(rt
->stack
);
426 void PrintReport(const ReportDesc
*rep
) {
427 Printf("==================\n");
428 if (rep
->typ
== ReportTypeRace
) {
429 Printf("WARNING: DATA RACE");
430 for (uptr i
= 0; i
< rep
->mops
.Size(); i
++)
431 PrintMop(rep
->mops
[i
], i
== 0);
432 for (uptr i
= 0; i
< rep
->locs
.Size(); i
++)
433 PrintLocation(rep
->locs
[i
]);
434 for (uptr i
= 0; i
< rep
->threads
.Size(); i
++)
435 PrintThread(rep
->threads
[i
]);
436 } else if (rep
->typ
== ReportTypeDeadlock
) {
437 Printf("WARNING: DEADLOCK\n");
438 for (uptr i
= 0; i
< rep
->mutexes
.Size(); i
++) {
439 Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
440 999, rep
->mutexes
[i
]->id
,
441 rep
->mutexes
[(i
+1) % rep
->mutexes
.Size()]->id
);
442 PrintStack(rep
->stacks
[2*i
]);
444 Printf("Mutex %d was previously locked here:\n",
445 rep
->mutexes
[(i
+1) % rep
->mutexes
.Size()]->id
);
446 PrintStack(rep
->stacks
[2*i
+ 1]);
450 Printf("==================\n");
455 } // namespace __tsan