1*e8d8bef9SDimitry Andric //===-- memprof_thread.cpp -----------------------------------------------===// 2*e8d8bef9SDimitry Andric // 3*e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*e8d8bef9SDimitry Andric // 7*e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8*e8d8bef9SDimitry Andric // 9*e8d8bef9SDimitry Andric // This file is a part of MemProfiler, a memory profiler. 10*e8d8bef9SDimitry Andric // 11*e8d8bef9SDimitry Andric // Thread-related code. 12*e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 13*e8d8bef9SDimitry Andric #include "memprof_thread.h" 14*e8d8bef9SDimitry Andric #include "memprof_allocator.h" 15*e8d8bef9SDimitry Andric #include "memprof_interceptors.h" 16*e8d8bef9SDimitry Andric #include "memprof_mapping.h" 17*e8d8bef9SDimitry Andric #include "memprof_stack.h" 18*e8d8bef9SDimitry Andric #include "sanitizer_common/sanitizer_common.h" 19*e8d8bef9SDimitry Andric #include "sanitizer_common/sanitizer_placement_new.h" 20*e8d8bef9SDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h" 21*e8d8bef9SDimitry Andric #include "sanitizer_common/sanitizer_tls_get_addr.h" 22*e8d8bef9SDimitry Andric 23*e8d8bef9SDimitry Andric namespace __memprof { 24*e8d8bef9SDimitry Andric 25*e8d8bef9SDimitry Andric // MemprofThreadContext implementation. 26*e8d8bef9SDimitry Andric 27*e8d8bef9SDimitry Andric void MemprofThreadContext::OnCreated(void *arg) { 28*e8d8bef9SDimitry Andric CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg); 29*e8d8bef9SDimitry Andric if (args->stack) 30*e8d8bef9SDimitry Andric stack_id = StackDepotPut(*args->stack); 31*e8d8bef9SDimitry Andric thread = args->thread; 32*e8d8bef9SDimitry Andric thread->set_context(this); 33*e8d8bef9SDimitry Andric } 34*e8d8bef9SDimitry Andric 35*e8d8bef9SDimitry Andric void MemprofThreadContext::OnFinished() { 36*e8d8bef9SDimitry Andric // Drop the link to the MemprofThread object. 37*e8d8bef9SDimitry Andric thread = nullptr; 38*e8d8bef9SDimitry Andric } 39*e8d8bef9SDimitry Andric 40*e8d8bef9SDimitry Andric static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; 41*e8d8bef9SDimitry Andric static ThreadRegistry *memprof_thread_registry; 42*e8d8bef9SDimitry Andric 43*e8d8bef9SDimitry Andric static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED); 44*e8d8bef9SDimitry Andric static LowLevelAllocator allocator_for_thread_context; 45*e8d8bef9SDimitry Andric 46*e8d8bef9SDimitry Andric static ThreadContextBase *GetMemprofThreadContext(u32 tid) { 47*e8d8bef9SDimitry Andric BlockingMutexLock lock(&mu_for_thread_context); 48*e8d8bef9SDimitry Andric return new (allocator_for_thread_context) MemprofThreadContext(tid); 49*e8d8bef9SDimitry Andric } 50*e8d8bef9SDimitry Andric 51*e8d8bef9SDimitry Andric ThreadRegistry &memprofThreadRegistry() { 52*e8d8bef9SDimitry Andric static bool initialized; 53*e8d8bef9SDimitry Andric // Don't worry about thread_safety - this should be called when there is 54*e8d8bef9SDimitry Andric // a single thread. 55*e8d8bef9SDimitry Andric if (!initialized) { 56*e8d8bef9SDimitry Andric // Never reuse MemProf threads: we store pointer to MemprofThreadContext 57*e8d8bef9SDimitry Andric // in TSD and can't reliably tell when no more TSD destructors will 58*e8d8bef9SDimitry Andric // be called. It would be wrong to reuse MemprofThreadContext for another 59*e8d8bef9SDimitry Andric // thread before all TSD destructors will be called for it. 60*e8d8bef9SDimitry Andric memprof_thread_registry = new (thread_registry_placeholder) ThreadRegistry( 61*e8d8bef9SDimitry Andric GetMemprofThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads); 62*e8d8bef9SDimitry Andric initialized = true; 63*e8d8bef9SDimitry Andric } 64*e8d8bef9SDimitry Andric return *memprof_thread_registry; 65*e8d8bef9SDimitry Andric } 66*e8d8bef9SDimitry Andric 67*e8d8bef9SDimitry Andric MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) { 68*e8d8bef9SDimitry Andric return static_cast<MemprofThreadContext *>( 69*e8d8bef9SDimitry Andric memprofThreadRegistry().GetThreadLocked(tid)); 70*e8d8bef9SDimitry Andric } 71*e8d8bef9SDimitry Andric 72*e8d8bef9SDimitry Andric // MemprofThread implementation. 73*e8d8bef9SDimitry Andric 74*e8d8bef9SDimitry Andric MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg, 75*e8d8bef9SDimitry Andric u32 parent_tid, StackTrace *stack, 76*e8d8bef9SDimitry Andric bool detached) { 77*e8d8bef9SDimitry Andric uptr PageSize = GetPageSizeCached(); 78*e8d8bef9SDimitry Andric uptr size = RoundUpTo(sizeof(MemprofThread), PageSize); 79*e8d8bef9SDimitry Andric MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__); 80*e8d8bef9SDimitry Andric thread->start_routine_ = start_routine; 81*e8d8bef9SDimitry Andric thread->arg_ = arg; 82*e8d8bef9SDimitry Andric MemprofThreadContext::CreateThreadContextArgs args = {thread, stack}; 83*e8d8bef9SDimitry Andric memprofThreadRegistry().CreateThread(*reinterpret_cast<uptr *>(thread), 84*e8d8bef9SDimitry Andric detached, parent_tid, &args); 85*e8d8bef9SDimitry Andric 86*e8d8bef9SDimitry Andric return thread; 87*e8d8bef9SDimitry Andric } 88*e8d8bef9SDimitry Andric 89*e8d8bef9SDimitry Andric void MemprofThread::TSDDtor(void *tsd) { 90*e8d8bef9SDimitry Andric MemprofThreadContext *context = (MemprofThreadContext *)tsd; 91*e8d8bef9SDimitry Andric VReport(1, "T%d TSDDtor\n", context->tid); 92*e8d8bef9SDimitry Andric if (context->thread) 93*e8d8bef9SDimitry Andric context->thread->Destroy(); 94*e8d8bef9SDimitry Andric } 95*e8d8bef9SDimitry Andric 96*e8d8bef9SDimitry Andric void MemprofThread::Destroy() { 97*e8d8bef9SDimitry Andric int tid = this->tid(); 98*e8d8bef9SDimitry Andric VReport(1, "T%d exited\n", tid); 99*e8d8bef9SDimitry Andric 100*e8d8bef9SDimitry Andric malloc_storage().CommitBack(); 101*e8d8bef9SDimitry Andric memprofThreadRegistry().FinishThread(tid); 102*e8d8bef9SDimitry Andric FlushToDeadThreadStats(&stats_); 103*e8d8bef9SDimitry Andric uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached()); 104*e8d8bef9SDimitry Andric UnmapOrDie(this, size); 105*e8d8bef9SDimitry Andric DTLS_Destroy(); 106*e8d8bef9SDimitry Andric } 107*e8d8bef9SDimitry Andric 108*e8d8bef9SDimitry Andric inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const { 109*e8d8bef9SDimitry Andric if (stack_bottom_ >= stack_top_) 110*e8d8bef9SDimitry Andric return {0, 0}; 111*e8d8bef9SDimitry Andric return {stack_bottom_, stack_top_}; 112*e8d8bef9SDimitry Andric } 113*e8d8bef9SDimitry Andric 114*e8d8bef9SDimitry Andric uptr MemprofThread::stack_top() { return GetStackBounds().top; } 115*e8d8bef9SDimitry Andric 116*e8d8bef9SDimitry Andric uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; } 117*e8d8bef9SDimitry Andric 118*e8d8bef9SDimitry Andric uptr MemprofThread::stack_size() { 119*e8d8bef9SDimitry Andric const auto bounds = GetStackBounds(); 120*e8d8bef9SDimitry Andric return bounds.top - bounds.bottom; 121*e8d8bef9SDimitry Andric } 122*e8d8bef9SDimitry Andric 123*e8d8bef9SDimitry Andric void MemprofThread::Init(const InitOptions *options) { 124*e8d8bef9SDimitry Andric CHECK_EQ(this->stack_size(), 0U); 125*e8d8bef9SDimitry Andric SetThreadStackAndTls(options); 126*e8d8bef9SDimitry Andric if (stack_top_ != stack_bottom_) { 127*e8d8bef9SDimitry Andric CHECK_GT(this->stack_size(), 0U); 128*e8d8bef9SDimitry Andric CHECK(AddrIsInMem(stack_bottom_)); 129*e8d8bef9SDimitry Andric CHECK(AddrIsInMem(stack_top_ - 1)); 130*e8d8bef9SDimitry Andric } 131*e8d8bef9SDimitry Andric int local = 0; 132*e8d8bef9SDimitry Andric VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), 133*e8d8bef9SDimitry Andric (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, 134*e8d8bef9SDimitry Andric &local); 135*e8d8bef9SDimitry Andric } 136*e8d8bef9SDimitry Andric 137*e8d8bef9SDimitry Andric thread_return_t 138*e8d8bef9SDimitry Andric MemprofThread::ThreadStart(tid_t os_id, 139*e8d8bef9SDimitry Andric atomic_uintptr_t *signal_thread_is_registered) { 140*e8d8bef9SDimitry Andric Init(); 141*e8d8bef9SDimitry Andric memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular, 142*e8d8bef9SDimitry Andric nullptr); 143*e8d8bef9SDimitry Andric if (signal_thread_is_registered) 144*e8d8bef9SDimitry Andric atomic_store(signal_thread_is_registered, 1, memory_order_release); 145*e8d8bef9SDimitry Andric 146*e8d8bef9SDimitry Andric if (!start_routine_) { 147*e8d8bef9SDimitry Andric // start_routine_ == 0 if we're on the main thread or on one of the 148*e8d8bef9SDimitry Andric // OS X libdispatch worker threads. But nobody is supposed to call 149*e8d8bef9SDimitry Andric // ThreadStart() for the worker threads. 150*e8d8bef9SDimitry Andric CHECK_EQ(tid(), 0); 151*e8d8bef9SDimitry Andric return 0; 152*e8d8bef9SDimitry Andric } 153*e8d8bef9SDimitry Andric 154*e8d8bef9SDimitry Andric return start_routine_(arg_); 155*e8d8bef9SDimitry Andric } 156*e8d8bef9SDimitry Andric 157*e8d8bef9SDimitry Andric MemprofThread *CreateMainThread() { 158*e8d8bef9SDimitry Andric MemprofThread *main_thread = MemprofThread::Create( 159*e8d8bef9SDimitry Andric /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0, 160*e8d8bef9SDimitry Andric /* stack */ nullptr, /* detached */ true); 161*e8d8bef9SDimitry Andric SetCurrentThread(main_thread); 162*e8d8bef9SDimitry Andric main_thread->ThreadStart(internal_getpid(), 163*e8d8bef9SDimitry Andric /* signal_thread_is_registered */ nullptr); 164*e8d8bef9SDimitry Andric return main_thread; 165*e8d8bef9SDimitry Andric } 166*e8d8bef9SDimitry Andric 167*e8d8bef9SDimitry Andric // This implementation doesn't use the argument, which is just passed down 168*e8d8bef9SDimitry Andric // from the caller of Init (which see, above). It's only there to support 169*e8d8bef9SDimitry Andric // OS-specific implementations that need more information passed through. 170*e8d8bef9SDimitry Andric void MemprofThread::SetThreadStackAndTls(const InitOptions *options) { 171*e8d8bef9SDimitry Andric DCHECK_EQ(options, nullptr); 172*e8d8bef9SDimitry Andric uptr tls_size = 0; 173*e8d8bef9SDimitry Andric uptr stack_size = 0; 174*e8d8bef9SDimitry Andric GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_, 175*e8d8bef9SDimitry Andric &tls_size); 176*e8d8bef9SDimitry Andric stack_top_ = stack_bottom_ + stack_size; 177*e8d8bef9SDimitry Andric tls_end_ = tls_begin_ + tls_size; 178*e8d8bef9SDimitry Andric dtls_ = DTLS_Get(); 179*e8d8bef9SDimitry Andric 180*e8d8bef9SDimitry Andric if (stack_top_ != stack_bottom_) { 181*e8d8bef9SDimitry Andric int local; 182*e8d8bef9SDimitry Andric CHECK(AddrIsInStack((uptr)&local)); 183*e8d8bef9SDimitry Andric } 184*e8d8bef9SDimitry Andric } 185*e8d8bef9SDimitry Andric 186*e8d8bef9SDimitry Andric bool MemprofThread::AddrIsInStack(uptr addr) { 187*e8d8bef9SDimitry Andric const auto bounds = GetStackBounds(); 188*e8d8bef9SDimitry Andric return addr >= bounds.bottom && addr < bounds.top; 189*e8d8bef9SDimitry Andric } 190*e8d8bef9SDimitry Andric 191*e8d8bef9SDimitry Andric MemprofThread *GetCurrentThread() { 192*e8d8bef9SDimitry Andric MemprofThreadContext *context = 193*e8d8bef9SDimitry Andric reinterpret_cast<MemprofThreadContext *>(TSDGet()); 194*e8d8bef9SDimitry Andric if (!context) 195*e8d8bef9SDimitry Andric return nullptr; 196*e8d8bef9SDimitry Andric return context->thread; 197*e8d8bef9SDimitry Andric } 198*e8d8bef9SDimitry Andric 199*e8d8bef9SDimitry Andric void SetCurrentThread(MemprofThread *t) { 200*e8d8bef9SDimitry Andric CHECK(t->context()); 201*e8d8bef9SDimitry Andric VReport(2, "SetCurrentThread: %p for thread %p\n", t->context(), 202*e8d8bef9SDimitry Andric (void *)GetThreadSelf()); 203*e8d8bef9SDimitry Andric // Make sure we do not reset the current MemprofThread. 204*e8d8bef9SDimitry Andric CHECK_EQ(0, TSDGet()); 205*e8d8bef9SDimitry Andric TSDSet(t->context()); 206*e8d8bef9SDimitry Andric CHECK_EQ(t->context(), TSDGet()); 207*e8d8bef9SDimitry Andric } 208*e8d8bef9SDimitry Andric 209*e8d8bef9SDimitry Andric u32 GetCurrentTidOrInvalid() { 210*e8d8bef9SDimitry Andric MemprofThread *t = GetCurrentThread(); 211*e8d8bef9SDimitry Andric return t ? t->tid() : kInvalidTid; 212*e8d8bef9SDimitry Andric } 213*e8d8bef9SDimitry Andric 214*e8d8bef9SDimitry Andric void EnsureMainThreadIDIsCorrect() { 215*e8d8bef9SDimitry Andric MemprofThreadContext *context = 216*e8d8bef9SDimitry Andric reinterpret_cast<MemprofThreadContext *>(TSDGet()); 217*e8d8bef9SDimitry Andric if (context && (context->tid == 0)) 218*e8d8bef9SDimitry Andric context->os_id = GetTid(); 219*e8d8bef9SDimitry Andric } 220*e8d8bef9SDimitry Andric } // namespace __memprof 221