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