1 //===-- tsan_rtl_thread.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of ThreadSanitizer (TSan), a race detector.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_placement_new.h"
15 #include "tsan_mman.h"
16 #include "tsan_platform.h"
17 #include "tsan_report.h"
18 #include "tsan_sync.h"
22 // ThreadContext implementation.
24 ThreadContext::ThreadContext(Tid tid
) : ThreadContextBase(tid
), thr(), sync() {}
27 ThreadContext::~ThreadContext() {
31 void ThreadContext::OnReset() { CHECK(!sync
); }
39 static void CollectThreadLeaks(ThreadContextBase
*tctx_base
, void *arg
) {
40 auto &leaks
= *static_cast<Vector
<ThreadLeak
> *>(arg
);
41 auto *tctx
= static_cast<ThreadContext
*>(tctx_base
);
42 if (tctx
->detached
|| tctx
->status
!= ThreadStatusFinished
)
44 for (uptr i
= 0; i
< leaks
.Size(); i
++) {
45 if (leaks
[i
].tctx
->creation_stack_id
== tctx
->creation_stack_id
) {
50 leaks
.PushBack({tctx
, 1});
54 // Disabled on Mac because lldb test TestTsanBasic fails:
55 // https://reviews.llvm.org/D112603#3163158
56 #if !SANITIZER_GO && !SANITIZER_APPLE
57 static void ReportIgnoresEnabled(ThreadContext
*tctx
, IgnoreSet
*set
) {
58 if (tctx
->tid
== kMainTid
) {
59 Printf("ThreadSanitizer: main thread finished with ignores enabled\n");
61 Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled,"
62 " created at:\n", tctx
->tid
, tctx
->name
);
63 PrintStack(SymbolizeStackId(tctx
->creation_stack_id
));
65 Printf(" One of the following ignores was not ended"
66 " (in order of probability)\n");
67 for (uptr i
= 0; i
< set
->Size(); i
++) {
68 Printf(" Ignore was enabled at:\n");
69 PrintStack(SymbolizeStackId(set
->At(i
)));
74 static void ThreadCheckIgnore(ThreadState
*thr
) {
75 if (ctx
->after_multithreaded_fork
)
77 if (thr
->ignore_reads_and_writes
)
78 ReportIgnoresEnabled(thr
->tctx
, &thr
->mop_ignore_set
);
80 ReportIgnoresEnabled(thr
->tctx
, &thr
->sync_ignore_set
);
83 static void ThreadCheckIgnore(ThreadState
*thr
) {}
86 void ThreadFinalize(ThreadState
*thr
) {
87 ThreadCheckIgnore(thr
);
89 if (!ShouldReport(thr
, ReportTypeThreadLeak
))
91 ThreadRegistryLock
l(&ctx
->thread_registry
);
92 Vector
<ThreadLeak
> leaks
;
93 ctx
->thread_registry
.RunCallbackForEachThreadLocked(CollectThreadLeaks
,
95 for (uptr i
= 0; i
< leaks
.Size(); i
++) {
96 ScopedReport
rep(ReportTypeThreadLeak
);
97 rep
.AddThread(leaks
[i
].tctx
, true);
98 rep
.SetCount(leaks
[i
].count
);
99 OutputReport(thr
, rep
);
104 int ThreadCount(ThreadState
*thr
) {
106 ctx
->thread_registry
.GetNumberOfThreads(0, 0, &result
);
110 struct OnCreatedArgs
{
116 Tid
ThreadCreate(ThreadState
*thr
, uptr pc
, uptr uid
, bool detached
) {
117 // The main thread and GCD workers don't have a parent thread.
118 Tid parent
= kInvalidTid
;
119 OnCreatedArgs arg
= {nullptr, 0, kInvalidStackID
};
122 arg
.stack
= CurrentStackId(thr
, pc
);
123 if (!thr
->ignore_sync
) {
124 SlotLocker
locker(thr
);
125 thr
->clock
.ReleaseStore(&arg
.sync
);
126 arg
.sync_epoch
= ctx
->global_epoch
;
130 Tid tid
= ctx
->thread_registry
.CreateThread(uid
, detached
, parent
, &arg
);
131 DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent
, tid
, uid
);
135 void ThreadContext::OnCreated(void *arg
) {
136 OnCreatedArgs
*args
= static_cast<OnCreatedArgs
*>(arg
);
138 sync_epoch
= args
->sync_epoch
;
139 creation_stack_id
= args
->stack
;
142 extern "C" void __tsan_stack_initialization() {}
144 struct OnStartedArgs
{
152 void ThreadStart(ThreadState
*thr
, Tid tid
, tid_t os_id
,
153 ThreadType thread_type
) {
154 ctx
->thread_registry
.StartThread(tid
, os_id
, thread_type
, thr
);
155 if (!thr
->ignore_sync
) {
156 SlotAttachAndLock(thr
);
157 if (thr
->tctx
->sync_epoch
== ctx
->global_epoch
)
158 thr
->clock
.Acquire(thr
->tctx
->sync
);
161 Free(thr
->tctx
->sync
);
168 if (thread_type
!= ThreadType::Fiber
)
169 GetThreadStackAndTls(tid
== kMainTid
, &stk_addr
, &stk_size
, &tls_addr
,
172 thr
->stk_addr
= stk_addr
;
173 thr
->stk_size
= stk_size
;
174 thr
->tls_addr
= tls_addr
;
175 thr
->tls_size
= tls_size
;
178 if (ctx
->after_multithreaded_fork
) {
179 thr
->ignore_interceptors
++;
180 ThreadIgnoreBegin(thr
, 0);
181 ThreadIgnoreSyncBegin(thr
, 0);
186 // Don't imitate stack/TLS writes for the main thread,
187 // because its initialization is synchronized with all
188 // subsequent threads anyway.
189 if (tid
!= kMainTid
) {
190 if (stk_addr
&& stk_size
) {
191 const uptr pc
= StackTrace::GetNextInstructionPc(
192 reinterpret_cast<uptr
>(__tsan_stack_initialization
));
193 MemoryRangeImitateWrite(thr
, pc
, stk_addr
, stk_size
);
196 if (tls_addr
&& tls_size
)
197 ImitateTlsWrite(thr
, tls_addr
, tls_size
);
202 void ThreadContext::OnStarted(void *arg
) {
203 thr
= static_cast<ThreadState
*>(arg
);
204 DPrintf("#%d: ThreadStart\n", tid
);
205 new (thr
) ThreadState(tid
);
206 if (common_flags()->detect_deadlocks
)
207 thr
->dd_lt
= ctx
->dd
->CreateLogicalThread(tid
);
210 thr
->is_inited
= true;
214 void ThreadFinish(ThreadState
*thr
) {
215 DPrintf("#%d: ThreadFinish\n", thr
->tid
);
216 ThreadCheckIgnore(thr
);
217 if (thr
->stk_addr
&& thr
->stk_size
)
218 DontNeedShadowFor(thr
->stk_addr
, thr
->stk_size
);
219 if (thr
->tls_addr
&& thr
->tls_size
)
220 DontNeedShadowFor(thr
->tls_addr
, thr
->tls_size
);
223 thr
->is_inited
= false;
224 thr
->ignore_interceptors
++;
225 PlatformCleanUpThreadState(thr
);
227 if (!thr
->ignore_sync
) {
228 SlotLocker
locker(thr
);
229 ThreadRegistryLock
lock(&ctx
->thread_registry
);
230 // Note: detached is protected by the thread registry mutex,
231 // the thread may be detaching concurrently in another thread.
232 if (!thr
->tctx
->detached
) {
233 thr
->clock
.ReleaseStore(&thr
->tctx
->sync
);
234 thr
->tctx
->sync_epoch
= ctx
->global_epoch
;
239 UnmapOrDie(thr
->shadow_stack
, kShadowStackSize
* sizeof(uptr
));
241 Free(thr
->shadow_stack
);
243 thr
->shadow_stack
= nullptr;
244 thr
->shadow_stack_pos
= nullptr;
245 thr
->shadow_stack_end
= nullptr;
246 if (common_flags()->detect_deadlocks
)
247 ctx
->dd
->DestroyLogicalThread(thr
->dd_lt
);
249 ctx
->thread_registry
.FinishThread(thr
->tid
);
253 void ThreadContext::OnFinished() {
254 Lock
lock(&ctx
->slot_mtx
);
255 Lock
lock1(&trace
.mtx
);
256 // Queue all trace parts into the global recycle queue.
257 auto parts
= &trace
.parts
;
258 while (trace
.local_head
) {
259 CHECK(parts
->Queued(trace
.local_head
));
260 ctx
->trace_part_recycle
.PushBack(trace
.local_head
);
261 trace
.local_head
= parts
->Next(trace
.local_head
);
263 ctx
->trace_part_recycle_finished
+= parts
->Size();
264 if (ctx
->trace_part_recycle_finished
> Trace::kFinishedThreadHi
) {
265 ctx
->trace_part_finished_excess
+= parts
->Size();
266 trace
.parts_allocated
= 0;
267 } else if (ctx
->trace_part_recycle_finished
> Trace::kFinishedThreadLo
&&
269 ctx
->trace_part_finished_excess
+= parts
->Size() - 1;
270 trace
.parts_allocated
= 1;
272 // From now on replay will use trace->final_pos.
273 trace
.final_pos
= (Event
*)atomic_load_relaxed(&thr
->trace_pos
);
274 atomic_store_relaxed(&thr
->trace_pos
, 0);
279 struct ConsumeThreadContext
{
281 ThreadContextBase
*tctx
;
284 Tid
ThreadConsumeTid(ThreadState
*thr
, uptr pc
, uptr uid
) {
285 return ctx
->thread_registry
.ConsumeThreadUserId(uid
);
293 void ThreadJoin(ThreadState
*thr
, uptr pc
, Tid tid
) {
295 DPrintf("#%d: ThreadJoin tid=%d\n", thr
->tid
, tid
);
297 ctx
->thread_registry
.JoinThread(tid
, &arg
);
298 if (!thr
->ignore_sync
) {
299 SlotLocker
locker(thr
);
300 if (arg
.sync_epoch
== ctx
->global_epoch
)
301 thr
->clock
.Acquire(arg
.sync
);
306 void ThreadContext::OnJoined(void *ptr
) {
307 auto arg
= static_cast<JoinArg
*>(ptr
);
309 arg
->sync_epoch
= sync_epoch
;
314 void ThreadContext::OnDead() { CHECK_EQ(sync
, nullptr); }
316 void ThreadDetach(ThreadState
*thr
, uptr pc
, Tid tid
) {
318 ctx
->thread_registry
.DetachThread(tid
, thr
);
321 void ThreadContext::OnDetached(void *arg
) { Free(sync
); }
323 void ThreadNotJoined(ThreadState
*thr
, uptr pc
, Tid tid
, uptr uid
) {
325 ctx
->thread_registry
.SetThreadUserId(tid
, uid
);
328 void ThreadSetName(ThreadState
*thr
, const char *name
) {
329 ctx
->thread_registry
.SetThreadName(thr
->tid
, name
);
333 void FiberSwitchImpl(ThreadState
*from
, ThreadState
*to
) {
334 Processor
*proc
= from
->proc();
335 ProcUnwire(proc
, from
);
340 ThreadState
*FiberCreate(ThreadState
*thr
, uptr pc
, unsigned flags
) {
341 void *mem
= Alloc(sizeof(ThreadState
));
342 ThreadState
*fiber
= static_cast<ThreadState
*>(mem
);
343 internal_memset(fiber
, 0, sizeof(*fiber
));
344 Tid tid
= ThreadCreate(thr
, pc
, 0, true);
345 FiberSwitchImpl(thr
, fiber
);
346 ThreadStart(fiber
, tid
, 0, ThreadType::Fiber
);
347 FiberSwitchImpl(fiber
, thr
);
351 void FiberDestroy(ThreadState
*thr
, uptr pc
, ThreadState
*fiber
) {
352 FiberSwitchImpl(thr
, fiber
);
354 FiberSwitchImpl(fiber
, thr
);
358 void FiberSwitch(ThreadState
*thr
, uptr pc
,
359 ThreadState
*fiber
, unsigned flags
) {
360 if (!(flags
& FiberSwitchFlagNoSync
))
361 Release(thr
, pc
, (uptr
)fiber
);
362 FiberSwitchImpl(thr
, fiber
);
363 if (!(flags
& FiberSwitchFlagNoSync
))
364 Acquire(fiber
, pc
, (uptr
)fiber
);
368 } // namespace __tsan