1 //===-- tsan_rtl_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 //===----------------------------------------------------------------------===//
14 #include "sanitizer_common/sanitizer_libc.h"
15 #include "sanitizer_common/sanitizer_placement_new.h"
16 #include "sanitizer_common/sanitizer_stackdepot.h"
17 #include "tsan_platform.h"
19 #include "tsan_suppressions.h"
20 #include "tsan_symbolize.h"
21 #include "tsan_report.h"
22 #include "tsan_sync.h"
23 #include "tsan_mman.h"
24 #include "tsan_flags.h"
28 void TsanCheckFailed(const char *file
, int line
, const char *cond
,
31 Printf("FATAL: ThreadSanitizer CHECK failed: "
32 "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
33 file
, line
, cond
, (uptr
)v1
, (uptr
)v2
);
37 // Can be overriden by an application/test to intercept reports.
38 #ifdef TSAN_EXTERNAL_HOOKS
39 bool OnReport(const ReportDesc
*rep
, bool suppressed
);
41 SANITIZER_INTERFACE_ATTRIBUTE
42 bool WEAK
OnReport(const ReportDesc
*rep
, bool suppressed
) {
48 static void StackStripMain(ReportStack
*stack
) {
49 ReportStack
*last_frame
= 0;
50 ReportStack
*last_frame2
= 0;
51 const char *prefix
= "__interceptor_";
52 uptr prefix_len
= internal_strlen(prefix
);
53 const char *path_prefix
= flags()->strip_path_prefix
;
54 uptr path_prefix_len
= internal_strlen(path_prefix
);
56 for (ReportStack
*ent
= stack
; ent
; ent
= ent
->next
) {
57 if (ent
->func
&& 0 == internal_strncmp(ent
->func
, prefix
, prefix_len
))
58 ent
->func
+= prefix_len
;
59 if (ent
->file
&& (pos
= internal_strstr(ent
->file
, path_prefix
)))
60 ent
->file
= pos
+ path_prefix_len
;
61 if (ent
->file
&& ent
->file
[0] == '.' && ent
->file
[1] == '/')
63 last_frame2
= last_frame
;
69 const char *last
= last_frame
->func
;
71 const char *last2
= last_frame2
->func
;
72 // Strip frame above 'main'
73 if (last2
&& 0 == internal_strcmp(last2
, "main")) {
74 last_frame2
->next
= 0;
75 // Strip our internal thread start routine.
76 } else if (last
&& 0 == internal_strcmp(last
, "__tsan_thread_start_func")) {
77 last_frame2
->next
= 0;
78 // Strip global ctors init.
79 } else if (last
&& 0 == internal_strcmp(last
, "__do_global_ctors_aux")) {
80 last_frame2
->next
= 0;
81 // If both are 0, then we probably just failed to symbolize.
82 } else if (last
|| last2
) {
83 // Ensure that we recovered stack completely. Trimmed stack
84 // can actually happen if we do not instrument some code,
85 // so it's only a debug print. However we must try hard to not miss it
87 DPrintf("Bottom stack frame of stack %zx is missed\n", stack
->pc
);
90 if (last
&& 0 == internal_strcmp(last
, "schedunlock"))
91 last_frame2
->next
= 0;
95 static ReportStack
*SymbolizeStack(const StackTrace
& trace
) {
98 ReportStack
*stack
= 0;
99 for (uptr si
= 0; si
< trace
.Size(); si
++) {
100 // We obtain the return address, that is, address of the next instruction,
101 // so offset it by 1 byte.
102 bool is_last
= (si
== trace
.Size() - 1);
103 ReportStack
*ent
= SymbolizeCode(trace
.Get(si
) - !is_last
);
105 ReportStack
*last
= ent
;
107 last
->pc
+= !is_last
;
110 last
->pc
+= !is_last
;
114 StackStripMain(stack
);
118 ScopedReport::ScopedReport(ReportType typ
) {
120 void *mem
= internal_alloc(MBlockReport
, sizeof(ReportDesc
));
121 rep_
= new(mem
) ReportDesc
;
123 ctx_
->report_mtx
.Lock();
126 ScopedReport::~ScopedReport() {
127 ctx_
->report_mtx
.Unlock();
132 void ScopedReport::AddStack(const StackTrace
*stack
) {
133 ReportStack
**rs
= rep_
->stacks
.PushBack();
134 *rs
= SymbolizeStack(*stack
);
137 void ScopedReport::AddMemoryAccess(uptr addr
, Shadow s
,
138 const StackTrace
*stack
) {
139 void *mem
= internal_alloc(MBlockReportMop
, sizeof(ReportMop
));
140 ReportMop
*mop
= new(mem
) ReportMop
;
141 rep_
->mops
.PushBack(mop
);
143 mop
->addr
= addr
+ s
.addr0();
144 mop
->size
= s
.size();
145 mop
->write
= s
.is_write();
147 mop
->stack
= SymbolizeStack(*stack
);
150 void ScopedReport::AddThread(const ThreadContext
*tctx
) {
151 for (uptr i
= 0; i
< rep_
->threads
.Size(); i
++) {
152 if (rep_
->threads
[i
]->id
== tctx
->tid
)
155 void *mem
= internal_alloc(MBlockReportThread
, sizeof(ReportThread
));
156 ReportThread
*rt
= new(mem
) ReportThread();
157 rep_
->threads
.PushBack(rt
);
159 rt
->pid
= tctx
->os_id
;
160 rt
->running
= (tctx
->status
== ThreadStatusRunning
);
161 rt
->stack
= SymbolizeStack(tctx
->creation_stack
);
165 static ThreadContext
*FindThread(int unique_id
) {
166 CTX()->thread_mtx
.CheckLocked();
167 for (unsigned i
= 0; i
< kMaxTid
; i
++) {
168 ThreadContext
*tctx
= CTX()->threads
[i
];
169 if (tctx
&& tctx
->unique_id
== unique_id
) {
177 void ScopedReport::AddMutex(const SyncVar
*s
) {
178 void *mem
= internal_alloc(MBlockReportMutex
, sizeof(ReportMutex
));
179 ReportMutex
*rm
= new(mem
) ReportMutex();
180 rep_
->mutexes
.PushBack(rm
);
182 rm
->stack
= SymbolizeStack(s
->creation_stack
);
185 void ScopedReport::AddLocation(uptr addr
, uptr size
) {
189 if (allocator()->PointerIsMine((void*)addr
)) {
190 MBlock
*b
= user_mblock(0, (void*)addr
);
191 ThreadContext
*tctx
= FindThread(b
->alloc_tid
);
192 void *mem
= internal_alloc(MBlockReportLoc
, sizeof(ReportLocation
));
193 ReportLocation
*loc
= new(mem
) ReportLocation();
194 rep_
->locs
.PushBack(loc
);
195 loc
->type
= ReportLocationHeap
;
196 loc
->addr
= (uptr
)allocator()->GetBlockBegin((void*)addr
);
198 loc
->tid
= tctx
? tctx
->tid
: b
->alloc_tid
;
204 const uptr
*stack
= StackDepotGet(b
->alloc_stack_id
, &ssz
);
207 trace
.Init(stack
, ssz
);
208 loc
->stack
= SymbolizeStack(trace
);
215 ReportStack
*symb
= SymbolizeData(addr
);
217 void *mem
= internal_alloc(MBlockReportLoc
, sizeof(ReportLocation
));
218 ReportLocation
*loc
= new(mem
) ReportLocation();
219 rep_
->locs
.PushBack(loc
);
220 loc
->type
= ReportLocationGlobal
;
224 loc
->name
= symb
->func
;
225 loc
->file
= symb
->file
;
226 loc
->line
= symb
->line
;
234 void ScopedReport::AddSleep(u32 stack_id
) {
236 const uptr
*stack
= StackDepotGet(stack_id
, &ssz
);
239 trace
.Init(stack
, ssz
);
240 rep_
->sleep
= SymbolizeStack(trace
);
245 const ReportDesc
*ScopedReport::GetReport() const {
249 void RestoreStack(int tid
, const u64 epoch
, StackTrace
*stk
) {
250 ThreadContext
*tctx
= CTX()->threads
[tid
];
254 if (tctx
->status
== ThreadStatusRunning
) {
256 trace
= &tctx
->thr
->trace
;
257 } else if (tctx
->status
== ThreadStatusFinished
258 || tctx
->status
== ThreadStatusDead
) {
259 if (tctx
->dead_info
== 0)
261 trace
= &tctx
->dead_info
->trace
;
266 const int partidx
= (epoch
/ (TraceSize() / kTraceParts
)) % kTraceParts
;
267 TraceHeader
* hdr
= &trace
->headers
[partidx
];
268 if (epoch
< hdr
->epoch0
)
270 const u64 eend
= epoch
% TraceSize();
271 const u64 ebegin
= eend
/ kTracePartSize
* kTracePartSize
;
272 DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
273 tid
, (uptr
)epoch
, (uptr
)ebegin
, (uptr
)eend
, partidx
);
274 InternalScopedBuffer
<uptr
> stack(1024); // FIXME: de-hardcode 1024
275 for (uptr i
= 0; i
< hdr
->stack0
.Size(); i
++) {
276 stack
[i
] = hdr
->stack0
.Get(i
);
277 DPrintf2(" #%02lu: pc=%zx\n", i
, stack
[i
]);
279 uptr pos
= hdr
->stack0
.Size();
280 Event
*events
= (Event
*)GetThreadTrace(tid
);
281 for (uptr i
= ebegin
; i
<= eend
; i
++) {
282 Event ev
= events
[i
];
283 EventType typ
= (EventType
)(ev
>> 61);
284 uptr pc
= (uptr
)(ev
& 0xffffffffffffull
);
285 DPrintf2(" %zu typ=%d pc=%zx\n", i
, typ
, pc
);
286 if (typ
== EventTypeMop
) {
288 } else if (typ
== EventTypeFuncEnter
) {
290 } else if (typ
== EventTypeFuncExit
) {
294 for (uptr j
= 0; j
<= pos
; j
++)
295 DPrintf2(" #%zu: %zx\n", j
, stack
[j
]);
297 if (pos
== 0 && stack
[0] == 0)
300 stk
->Init(stack
.data(), pos
);
303 static bool HandleRacyStacks(ThreadState
*thr
, const StackTrace (&traces
)[2],
304 uptr addr_min
, uptr addr_max
) {
305 Context
*ctx
= CTX();
306 bool equal_stack
= false;
307 RacyStacks hash
= {};
308 if (flags()->suppress_equal_stacks
) {
309 hash
.hash
[0] = md5_hash(traces
[0].Begin(), traces
[0].Size() * sizeof(uptr
));
310 hash
.hash
[1] = md5_hash(traces
[1].Begin(), traces
[1].Size() * sizeof(uptr
));
311 for (uptr i
= 0; i
< ctx
->racy_stacks
.Size(); i
++) {
312 if (hash
== ctx
->racy_stacks
[i
]) {
313 DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n");
319 bool equal_address
= false;
320 RacyAddress ra0
= {addr_min
, addr_max
};
321 if (flags()->suppress_equal_addresses
) {
322 for (uptr i
= 0; i
< ctx
->racy_addresses
.Size(); i
++) {
323 RacyAddress ra2
= ctx
->racy_addresses
[i
];
324 uptr maxbeg
= max(ra0
.addr_min
, ra2
.addr_min
);
325 uptr minend
= min(ra0
.addr_max
, ra2
.addr_max
);
326 if (maxbeg
< minend
) {
327 DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n");
328 equal_address
= true;
333 if (equal_stack
|| equal_address
) {
335 ctx
->racy_stacks
.PushBack(hash
);
337 ctx
->racy_addresses
.PushBack(ra0
);
343 static void AddRacyStacks(ThreadState
*thr
, const StackTrace (&traces
)[2],
344 uptr addr_min
, uptr addr_max
) {
345 Context
*ctx
= CTX();
346 if (flags()->suppress_equal_stacks
) {
348 hash
.hash
[0] = md5_hash(traces
[0].Begin(), traces
[0].Size() * sizeof(uptr
));
349 hash
.hash
[1] = md5_hash(traces
[1].Begin(), traces
[1].Size() * sizeof(uptr
));
350 ctx
->racy_stacks
.PushBack(hash
);
352 if (flags()->suppress_equal_addresses
) {
353 RacyAddress ra0
= {addr_min
, addr_max
};
354 ctx
->racy_addresses
.PushBack(ra0
);
358 bool OutputReport(Context
*ctx
,
359 const ScopedReport
&srep
,
360 const ReportStack
*suppress_stack
) {
361 const ReportDesc
*rep
= srep
.GetReport();
362 const uptr suppress_pc
= IsSuppressed(rep
->typ
, suppress_stack
);
363 if (suppress_pc
!= 0) {
364 FiredSuppression supp
= {srep
.GetReport()->typ
, suppress_pc
};
365 ctx
->fired_suppressions
.PushBack(supp
);
367 if (OnReport(rep
, suppress_pc
!= 0))
374 bool IsFiredSuppression(Context
*ctx
,
375 const ScopedReport
&srep
,
376 const StackTrace
&trace
) {
377 for (uptr k
= 0; k
< ctx
->fired_suppressions
.Size(); k
++) {
378 if (ctx
->fired_suppressions
[k
].type
!= srep
.GetReport()->typ
)
380 for (uptr j
= 0; j
< trace
.Size(); j
++) {
381 if (trace
.Get(j
) == ctx
->fired_suppressions
[k
].pc
)
388 // On programs that use Java we see weird reports like:
389 // WARNING: ThreadSanitizer: data race (pid=22512)
390 // Read of size 8 at 0x7d2b00084318 by thread 100:
391 // #0 memcpy tsan_interceptors.cc:406 (foo+0x00000d8dfae3)
392 // #1 <null> <null>:0 (0x7f7ad9b40193)
393 // Previous write of size 8 at 0x7d2b00084318 by thread 105:
394 // #0 strncpy tsan_interceptors.cc:501 (foo+0x00000d8e0919)
395 // #1 <null> <null>:0 (0x7f7ad9b42707)
396 static bool IsJavaNonsense(const ReportDesc
*rep
) {
397 for (uptr i
= 0; i
< rep
->mops
.Size(); i
++) {
398 ReportMop
*mop
= rep
->mops
[i
];
399 ReportStack
*frame
= mop
->stack
;
400 if (frame
!= 0 && frame
->func
!= 0
401 && (internal_strcmp(frame
->func
, "memset") == 0
402 || internal_strcmp(frame
->func
, "memcpy") == 0
403 || internal_strcmp(frame
->func
, "strcmp") == 0
404 || internal_strcmp(frame
->func
, "strncpy") == 0
405 || internal_strcmp(frame
->func
, "pthread_mutex_lock") == 0)) {
408 || (frame
->func
== 0 && frame
->file
== 0 && frame
->line
== 0
409 && frame
->module
== 0)) {
411 FiredSuppression supp
= {rep
->typ
, frame
->pc
};
412 CTX()->fired_suppressions
.PushBack(supp
);
421 void ReportRace(ThreadState
*thr
) {
422 if (!flags()->report_bugs
)
428 Shadow
s(thr
->racy_state
[1]);
429 freed
= s
.GetFreedAndReset();
430 thr
->racy_state
[1] = s
.raw();
433 uptr addr
= ShadowToMem((uptr
)thr
->racy_shadow_addr
);
437 uptr a0
= addr
+ Shadow(thr
->racy_state
[0]).addr0();
438 uptr a1
= addr
+ Shadow(thr
->racy_state
[1]).addr0();
439 uptr e0
= a0
+ Shadow(thr
->racy_state
[0]).size();
440 uptr e1
= a1
+ Shadow(thr
->racy_state
[1]).size();
441 addr_min
= min(a0
, a1
);
442 addr_max
= max(e0
, e1
);
443 if (IsExpectedReport(addr_min
, addr_max
- addr_min
))
447 Context
*ctx
= CTX();
448 Lock
l0(&ctx
->thread_mtx
);
450 ScopedReport
rep(freed
? ReportTypeUseAfterFree
: ReportTypeRace
);
452 StackTrace traces
[kMop
];
453 const uptr toppc
= TraceTopPC(thr
);
454 traces
[0].ObtainCurrent(thr
, toppc
);
455 if (IsFiredSuppression(ctx
, rep
, traces
[0]))
457 Shadow
s2(thr
->racy_state
[1]);
458 RestoreStack(s2
.tid(), s2
.epoch(), &traces
[1]);
460 if (HandleRacyStacks(thr
, traces
, addr_min
, addr_max
))
463 for (uptr i
= 0; i
< kMop
; i
++) {
464 Shadow
s(thr
->racy_state
[i
]);
465 rep
.AddMemoryAccess(addr
, s
, &traces
[i
]);
468 if (flags()->suppress_java
&& IsJavaNonsense(rep
.GetReport()))
471 for (uptr i
= 0; i
< kMop
; i
++) {
472 FastState
s(thr
->racy_state
[i
]);
473 ThreadContext
*tctx
= ctx
->threads
[s
.tid()];
474 if (s
.epoch() < tctx
->epoch0
|| s
.epoch() > tctx
->epoch1
)
479 rep
.AddLocation(addr_min
, addr_max
- addr_min
);
483 Shadow
s(thr
->racy_state
[1]);
484 if (s
.epoch() <= thr
->last_sleep_clock
.get(s
.tid()))
485 rep
.AddSleep(thr
->last_sleep_stack_id
);
489 if (!OutputReport(ctx
, rep
, rep
.GetReport()->mops
[0]->stack
))
492 AddRacyStacks(thr
, traces
, addr_min
, addr_max
);
495 void PrintCurrentStack(ThreadState
*thr
, uptr pc
) {
497 trace
.ObtainCurrent(thr
, pc
);
498 PrintStack(SymbolizeStack(trace
));
501 } // namespace __tsan