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