1//===-- tsan_rtl_thread.cpp -----------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This file is a part of ThreadSanitizer (TSan), a race detector.
10//
11//===----------------------------------------------------------------------===//
12
13#include "sanitizer_common/sanitizer_placement_new.h"
14#include "tsan_rtl.h"
15#include "tsan_mman.h"
16#include "tsan_platform.h"
17#include "tsan_report.h"
18#include "tsan_sync.h"
19
20namespace __tsan {
21
22// ThreadContext implementation.
23
24ThreadContext::ThreadContext(Tid tid) : ThreadContextBase(tid), thr(), sync() {}
25
26#if !SANITIZER_GO
27ThreadContext::~ThreadContext() {
28}
29#endif
30
31void ThreadContext::OnReset() { CHECK(!sync); }
32
33#if !SANITIZER_GO
34struct ThreadLeak {
35 ThreadContext *tctx;
36 int count;
37};
38
39static 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)
43 return;
44 for (uptr i = 0; i < leaks.Size(); i++) {
45 if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) {
46 leaks[i].count++;
47 return;
48 }
49 }
50 leaks.PushBack(v: {.tctx: tctx, .count: 1});
51}
52#endif
53
54// Disabled on Mac because lldb test TestTsanBasic fails:
55// https://reviews.llvm.org/D112603#3163158
56#if !SANITIZER_GO && !SANITIZER_APPLE
57static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) {
58 if (tctx->tid == kMainTid) {
59 Printf(format: "ThreadSanitizer: main thread finished with ignores enabled\n");
60 } else {
61 Printf(format: "ThreadSanitizer: thread T%d %s finished with ignores enabled,"
62 " created at:\n", tctx->tid, tctx->name);
63 PrintStack(stack: SymbolizeStackId(stack_id: tctx->creation_stack_id));
64 }
65 Printf(format: " 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(format: " Ignore was enabled at:\n");
69 PrintStack(stack: SymbolizeStackId(stack_id: set->At(i)));
70 }
71 Die();
72}
73
74static void ThreadCheckIgnore(ThreadState *thr) {
75 if (ctx->after_multithreaded_fork)
76 return;
77 if (thr->ignore_reads_and_writes)
78 ReportIgnoresEnabled(tctx: thr->tctx, set: &thr->mop_ignore_set);
79 if (thr->ignore_sync)
80 ReportIgnoresEnabled(tctx: thr->tctx, set: &thr->sync_ignore_set);
81}
82#else
83static void ThreadCheckIgnore(ThreadState *thr) {}
84#endif
85
86void ThreadFinalize(ThreadState *thr) {
87 ThreadCheckIgnore(thr);
88#if !SANITIZER_GO
89 if (!ShouldReport(thr, typ: ReportTypeThreadLeak))
90 return;
91 Vector<ThreadLeak> leaks;
92 {
93 ThreadRegistryLock l(&ctx->thread_registry);
94 ctx->thread_registry.RunCallbackForEachThreadLocked(cb: CollectThreadLeaks,
95 arg: &leaks);
96 }
97
98 for (uptr i = 0; i < leaks.Size(); i++) {
99 ScopedReport rep(ReportTypeThreadLeak);
100 rep.AddThread(tctx: leaks[i].tctx, suppressable: true);
101 rep.SetCount(leaks[i].count);
102 OutputReport(thr, srep&: rep);
103 }
104#endif
105}
106
107int ThreadCount(ThreadState *thr) {
108 uptr result;
109 ctx->thread_registry.GetNumberOfThreads(total: 0, running: 0, alive: &result);
110 return (int)result;
111}
112
113struct OnCreatedArgs {
114 VectorClock *sync;
115 uptr sync_epoch;
116 StackID stack;
117};
118
119Tid ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
120 // The main thread and GCD workers don't have a parent thread.
121 Tid parent = kInvalidTid;
122 OnCreatedArgs arg = {.sync: nullptr, .sync_epoch: 0, .stack: kInvalidStackID};
123 if (thr) {
124 parent = thr->tid;
125 arg.stack = CurrentStackId(thr, pc);
126 if (!thr->ignore_sync) {
127 SlotLocker locker(thr);
128 thr->clock.ReleaseStore(dstp: &arg.sync);
129 arg.sync_epoch = ctx->global_epoch;
130 IncrementEpoch(thr);
131 }
132 }
133 Tid tid = ctx->thread_registry.CreateThread(user_id: uid, detached, parent_tid: parent, arg: &arg);
134 DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent, tid, uid);
135 return tid;
136}
137
138void ThreadContext::OnCreated(void *arg) {
139 OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg);
140 sync = args->sync;
141 sync_epoch = args->sync_epoch;
142 creation_stack_id = args->stack;
143}
144
145extern "C" void __tsan_stack_initialization() {}
146
147struct OnStartedArgs {
148 ThreadState *thr;
149 uptr stk_addr;
150 uptr stk_size;
151 uptr tls_addr;
152 uptr tls_size;
153};
154
155void ThreadStart(ThreadState *thr, Tid tid, ThreadID os_id,
156 ThreadType thread_type) {
157 ctx->thread_registry.StartThread(tid, os_id, thread_type, arg: thr);
158 if (!thr->ignore_sync) {
159 SlotAttachAndLock(thr);
160 if (thr->tctx->sync_epoch == ctx->global_epoch)
161 thr->clock.Acquire(src: thr->tctx->sync);
162 SlotUnlock(thr);
163 }
164 Free(p&: thr->tctx->sync);
165
166#if !SANITIZER_GO
167 thr->is_inited = true;
168#endif
169
170 uptr stk_addr = 0;
171 uptr stk_end = 0;
172 uptr tls_addr = 0;
173 uptr tls_end = 0;
174#if !SANITIZER_GO
175 if (thread_type != ThreadType::Fiber)
176 GetThreadStackAndTls(main: tid == kMainTid, stk_begin: &stk_addr, stk_end: &stk_end, tls_begin: &tls_addr,
177 tls_end: &tls_end);
178#endif
179 uptr stk_size = stk_end - stk_addr;
180 uptr tls_size = tls_end - tls_addr;
181 thr->stk_addr = stk_addr;
182 thr->stk_size = stk_size;
183 thr->tls_addr = tls_addr;
184 thr->tls_size = tls_size;
185
186#if !SANITIZER_GO
187 if (ctx->after_multithreaded_fork) {
188 thr->ignore_interceptors++;
189 ThreadIgnoreBegin(thr, pc: 0);
190 ThreadIgnoreSyncBegin(thr, pc: 0);
191 }
192#endif
193
194#if !SANITIZER_GO && !SANITIZER_ANDROID
195 // Don't imitate stack/TLS writes for the main thread,
196 // because its initialization is synchronized with all
197 // subsequent threads anyway.
198 // Because thr is created by MmapOrDie, the thr object
199 // is not in tls, the pointer to the thr object is in
200 // TLS_SLOT_SANITIZER slot. So skip this check on
201 // Android platform.
202 if (tid != kMainTid) {
203 if (stk_addr && stk_size) {
204 const uptr pc = StackTrace::GetNextInstructionPc(
205 pc: reinterpret_cast<uptr>(__tsan_stack_initialization));
206 MemoryRangeImitateWrite(thr, pc, addr: stk_addr, size: stk_size);
207 }
208
209 if (tls_addr && tls_size)
210 ImitateTlsWrite(thr, tls_addr, tls_size);
211 }
212#endif
213}
214
215void ThreadContext::OnStarted(void *arg) {
216 DPrintf("#%d: ThreadStart\n", tid);
217 thr = new (arg) ThreadState(tid);
218 if (common_flags()->detect_deadlocks)
219 thr->dd_lt = ctx->dd->CreateLogicalThread(ctx: tid);
220 thr->tctx = this;
221}
222
223void ThreadFinish(ThreadState *thr) {
224 DPrintf("#%d: ThreadFinish\n", thr->tid);
225 ThreadCheckIgnore(thr);
226 if (thr->stk_addr && thr->stk_size)
227 DontNeedShadowFor(addr: thr->stk_addr, size: thr->stk_size);
228 if (thr->tls_addr && thr->tls_size)
229 DontNeedShadowFor(addr: thr->tls_addr, size: thr->tls_size);
230 thr->is_dead = true;
231#if !SANITIZER_GO
232 thr->is_inited = false;
233 thr->ignore_interceptors++;
234 PlatformCleanUpThreadState(thr);
235#endif
236 if (!thr->ignore_sync) {
237 SlotLocker locker(thr);
238 ThreadRegistryLock lock(&ctx->thread_registry);
239 // Note: detached is protected by the thread registry mutex,
240 // the thread may be detaching concurrently in another thread.
241 if (!thr->tctx->detached) {
242 thr->clock.ReleaseStore(dstp: &thr->tctx->sync);
243 thr->tctx->sync_epoch = ctx->global_epoch;
244 IncrementEpoch(thr);
245 }
246 }
247#if !SANITIZER_GO
248 UnmapOrDie(addr: thr->shadow_stack, size: kShadowStackSize * sizeof(uptr));
249#else
250 Free(thr->shadow_stack);
251#endif
252 thr->shadow_stack = nullptr;
253 thr->shadow_stack_pos = nullptr;
254 thr->shadow_stack_end = nullptr;
255 if (common_flags()->detect_deadlocks)
256 ctx->dd->DestroyLogicalThread(lt: thr->dd_lt);
257 SlotDetach(thr);
258 ctx->thread_registry.FinishThread(tid: thr->tid);
259 thr->~ThreadState();
260}
261
262void ThreadContext::OnFinished() {
263 Lock lock(&ctx->slot_mtx);
264 Lock lock1(&trace.mtx);
265 // Queue all trace parts into the global recycle queue.
266 auto parts = &trace.parts;
267 while (trace.local_head) {
268 CHECK(parts->Queued(trace.local_head));
269 ctx->trace_part_recycle.PushBack(e: trace.local_head);
270 trace.local_head = parts->Next(e: trace.local_head);
271 }
272 ctx->trace_part_recycle_finished += parts->Size();
273 if (ctx->trace_part_recycle_finished > Trace::kFinishedThreadHi) {
274 ctx->trace_part_finished_excess += parts->Size();
275 trace.parts_allocated = 0;
276 } else if (ctx->trace_part_recycle_finished > Trace::kFinishedThreadLo &&
277 parts->Size() > 1) {
278 ctx->trace_part_finished_excess += parts->Size() - 1;
279 trace.parts_allocated = 1;
280 }
281 // From now on replay will use trace->final_pos.
282 trace.final_pos = (Event *)atomic_load_relaxed(a: &thr->trace_pos);
283 atomic_store_relaxed(a: &thr->trace_pos, v: 0);
284 thr->tctx = nullptr;
285 thr = nullptr;
286}
287
288struct ConsumeThreadContext {
289 uptr uid;
290 ThreadContextBase *tctx;
291};
292
293Tid ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid) {
294 return ctx->thread_registry.ConsumeThreadUserId(user_id: uid);
295}
296
297struct JoinArg {
298 VectorClock *sync;
299 uptr sync_epoch;
300};
301
302void ThreadJoin(ThreadState *thr, uptr pc, Tid tid) {
303 CHECK_GT(tid, 0);
304 DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid);
305 JoinArg arg = {};
306 ctx->thread_registry.JoinThread(tid, arg: &arg);
307 if (!thr->ignore_sync) {
308 SlotLocker locker(thr);
309 if (arg.sync_epoch == ctx->global_epoch)
310 thr->clock.Acquire(src: arg.sync);
311 }
312 Free(p&: arg.sync);
313}
314
315void ThreadContext::OnJoined(void *ptr) {
316 auto arg = static_cast<JoinArg *>(ptr);
317 arg->sync = sync;
318 arg->sync_epoch = sync_epoch;
319 sync = nullptr;
320 sync_epoch = 0;
321}
322
323void ThreadContext::OnDead() { CHECK_EQ(sync, nullptr); }
324
325void ThreadDetach(ThreadState *thr, uptr pc, Tid tid) {
326 CHECK_GT(tid, 0);
327 ctx->thread_registry.DetachThread(tid, arg: thr);
328}
329
330void ThreadContext::OnDetached(void *arg) { Free(p&: sync); }
331
332void ThreadNotJoined(ThreadState *thr, uptr pc, Tid tid, uptr uid) {
333 CHECK_GT(tid, 0);
334 ctx->thread_registry.SetThreadUserId(tid, user_id: uid);
335}
336
337void ThreadSetName(ThreadState *thr, const char *name) {
338 ctx->thread_registry.SetThreadName(tid: thr->tid, name);
339}
340
341#if !SANITIZER_GO
342void FiberSwitchImpl(ThreadState *from, ThreadState *to) {
343 Processor *proc = from->proc();
344 ProcUnwire(proc, thr: from);
345 ProcWire(proc, thr: to);
346 set_cur_thread(to);
347}
348
349ThreadState *FiberCreate(ThreadState *thr, uptr pc, unsigned flags) {
350 void *mem = Alloc(sz: sizeof(ThreadState));
351 ThreadState *fiber = static_cast<ThreadState *>(mem);
352 internal_memset(s: fiber, c: 0, n: sizeof(*fiber));
353 Tid tid = ThreadCreate(thr, pc, uid: 0, detached: true);
354 FiberSwitchImpl(from: thr, to: fiber);
355 ThreadStart(thr: fiber, tid, os_id: 0, thread_type: ThreadType::Fiber);
356 FiberSwitchImpl(from: fiber, to: thr);
357 return fiber;
358}
359
360void FiberDestroy(ThreadState *thr, uptr pc, ThreadState *fiber) {
361 FiberSwitchImpl(from: thr, to: fiber);
362 ThreadFinish(thr: fiber);
363 FiberSwitchImpl(from: fiber, to: thr);
364 Free(p&: fiber);
365}
366
367void FiberSwitch(ThreadState *thr, uptr pc,
368 ThreadState *fiber, unsigned flags) {
369 if (!(flags & FiberSwitchFlagNoSync))
370 Release(thr, pc, addr: (uptr)fiber);
371 FiberSwitchImpl(from: thr, to: fiber);
372 if (!(flags & FiberSwitchFlagNoSync))
373 Acquire(thr: fiber, pc, addr: (uptr)fiber);
374}
375#endif
376
377} // namespace __tsan
378