13d4bba30STeresa Johnson //===-- memprof_thread.cpp -----------------------------------------------===// 23d4bba30STeresa Johnson // 33d4bba30STeresa Johnson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 43d4bba30STeresa Johnson // See https://llvm.org/LICENSE.txt for license information. 53d4bba30STeresa Johnson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 63d4bba30STeresa Johnson // 73d4bba30STeresa Johnson //===----------------------------------------------------------------------===// 83d4bba30STeresa Johnson // 93d4bba30STeresa Johnson // This file is a part of MemProfiler, a memory profiler. 103d4bba30STeresa Johnson // 113d4bba30STeresa Johnson // Thread-related code. 123d4bba30STeresa Johnson //===----------------------------------------------------------------------===// 133d4bba30STeresa Johnson #include "memprof_thread.h" 143d4bba30STeresa Johnson #include "memprof_allocator.h" 153d4bba30STeresa Johnson #include "memprof_interceptors.h" 163d4bba30STeresa Johnson #include "memprof_mapping.h" 173d4bba30STeresa Johnson #include "memprof_stack.h" 183d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_common.h" 193d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_placement_new.h" 203d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_stackdepot.h" 213d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_tls_get_addr.h" 223d4bba30STeresa Johnson 233d4bba30STeresa Johnson namespace __memprof { 243d4bba30STeresa Johnson 253d4bba30STeresa Johnson // MemprofThreadContext implementation. 263d4bba30STeresa Johnson 273d4bba30STeresa Johnson void MemprofThreadContext::OnCreated(void *arg) { 28*abe148a0SVitaly Buka thread = static_cast<MemprofThread *>(arg); 293d4bba30STeresa Johnson thread->set_context(this); 303d4bba30STeresa Johnson } 313d4bba30STeresa Johnson 323d4bba30STeresa Johnson void MemprofThreadContext::OnFinished() { 333d4bba30STeresa Johnson // Drop the link to the MemprofThread object. 343d4bba30STeresa Johnson thread = nullptr; 353d4bba30STeresa Johnson } 363d4bba30STeresa Johnson 37ba66d60bSFangrui Song alignas(16) static char thread_registry_placeholder[sizeof(ThreadRegistry)]; 383d4bba30STeresa Johnson static ThreadRegistry *memprof_thread_registry; 393d4bba30STeresa Johnson 4056debbf5SDmitry Vyukov static Mutex mu_for_thread_context; 413d4bba30STeresa Johnson static LowLevelAllocator allocator_for_thread_context; 423d4bba30STeresa Johnson 433d4bba30STeresa Johnson static ThreadContextBase *GetMemprofThreadContext(u32 tid) { 4456debbf5SDmitry Vyukov Lock lock(&mu_for_thread_context); 453d4bba30STeresa Johnson return new (allocator_for_thread_context) MemprofThreadContext(tid); 463d4bba30STeresa Johnson } 473d4bba30STeresa Johnson 483d4bba30STeresa Johnson ThreadRegistry &memprofThreadRegistry() { 493d4bba30STeresa Johnson static bool initialized; 503d4bba30STeresa Johnson // Don't worry about thread_safety - this should be called when there is 513d4bba30STeresa Johnson // a single thread. 523d4bba30STeresa Johnson if (!initialized) { 533d4bba30STeresa Johnson // Never reuse MemProf threads: we store pointer to MemprofThreadContext 543d4bba30STeresa Johnson // in TSD and can't reliably tell when no more TSD destructors will 553d4bba30STeresa Johnson // be called. It would be wrong to reuse MemprofThreadContext for another 563d4bba30STeresa Johnson // thread before all TSD destructors will be called for it. 57dfd9808bSDmitry Vyukov memprof_thread_registry = new (thread_registry_placeholder) 58dfd9808bSDmitry Vyukov ThreadRegistry(GetMemprofThreadContext); 593d4bba30STeresa Johnson initialized = true; 603d4bba30STeresa Johnson } 613d4bba30STeresa Johnson return *memprof_thread_registry; 623d4bba30STeresa Johnson } 633d4bba30STeresa Johnson 643d4bba30STeresa Johnson MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) { 653d4bba30STeresa Johnson return static_cast<MemprofThreadContext *>( 663d4bba30STeresa Johnson memprofThreadRegistry().GetThreadLocked(tid)); 673d4bba30STeresa Johnson } 683d4bba30STeresa Johnson 693d4bba30STeresa Johnson // MemprofThread implementation. 703d4bba30STeresa Johnson 713d4bba30STeresa Johnson MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg, 723d4bba30STeresa Johnson u32 parent_tid, StackTrace *stack, 733d4bba30STeresa Johnson bool detached) { 743d4bba30STeresa Johnson uptr PageSize = GetPageSizeCached(); 753d4bba30STeresa Johnson uptr size = RoundUpTo(sizeof(MemprofThread), PageSize); 763d4bba30STeresa Johnson MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__); 773d4bba30STeresa Johnson thread->start_routine_ = start_routine; 783d4bba30STeresa Johnson thread->arg_ = arg; 79*abe148a0SVitaly Buka memprofThreadRegistry().CreateThread( 80*abe148a0SVitaly Buka 0, detached, parent_tid, stack ? StackDepotPut(*stack) : 0, thread); 813d4bba30STeresa Johnson 823d4bba30STeresa Johnson return thread; 833d4bba30STeresa Johnson } 843d4bba30STeresa Johnson 853d4bba30STeresa Johnson void MemprofThread::TSDDtor(void *tsd) { 863d4bba30STeresa Johnson MemprofThreadContext *context = (MemprofThreadContext *)tsd; 873d4bba30STeresa Johnson VReport(1, "T%d TSDDtor\n", context->tid); 883d4bba30STeresa Johnson if (context->thread) 893d4bba30STeresa Johnson context->thread->Destroy(); 903d4bba30STeresa Johnson } 913d4bba30STeresa Johnson 923d4bba30STeresa Johnson void MemprofThread::Destroy() { 933d4bba30STeresa Johnson int tid = this->tid(); 943d4bba30STeresa Johnson VReport(1, "T%d exited\n", tid); 953d4bba30STeresa Johnson 963d4bba30STeresa Johnson malloc_storage().CommitBack(); 973d4bba30STeresa Johnson memprofThreadRegistry().FinishThread(tid); 983d4bba30STeresa Johnson FlushToDeadThreadStats(&stats_); 993d4bba30STeresa Johnson uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached()); 1003d4bba30STeresa Johnson UnmapOrDie(this, size); 1013d4bba30STeresa Johnson DTLS_Destroy(); 1023d4bba30STeresa Johnson } 1033d4bba30STeresa Johnson 1043d4bba30STeresa Johnson inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const { 1053d4bba30STeresa Johnson if (stack_bottom_ >= stack_top_) 1063d4bba30STeresa Johnson return {0, 0}; 1073d4bba30STeresa Johnson return {stack_bottom_, stack_top_}; 1083d4bba30STeresa Johnson } 1093d4bba30STeresa Johnson 1103d4bba30STeresa Johnson uptr MemprofThread::stack_top() { return GetStackBounds().top; } 1113d4bba30STeresa Johnson 1123d4bba30STeresa Johnson uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; } 1133d4bba30STeresa Johnson 1143d4bba30STeresa Johnson uptr MemprofThread::stack_size() { 1153d4bba30STeresa Johnson const auto bounds = GetStackBounds(); 1163d4bba30STeresa Johnson return bounds.top - bounds.bottom; 1173d4bba30STeresa Johnson } 1183d4bba30STeresa Johnson 1193d4bba30STeresa Johnson void MemprofThread::Init(const InitOptions *options) { 1203d4bba30STeresa Johnson CHECK_EQ(this->stack_size(), 0U); 1213d4bba30STeresa Johnson SetThreadStackAndTls(options); 1223d4bba30STeresa Johnson if (stack_top_ != stack_bottom_) { 1233d4bba30STeresa Johnson CHECK_GT(this->stack_size(), 0U); 1243d4bba30STeresa Johnson CHECK(AddrIsInMem(stack_bottom_)); 1253d4bba30STeresa Johnson CHECK(AddrIsInMem(stack_top_ - 1)); 1263d4bba30STeresa Johnson } 1273d4bba30STeresa Johnson int local = 0; 1283d4bba30STeresa Johnson VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), 1293d4bba30STeresa Johnson (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, 13013a442caSMartin Liska (void *)&local); 1313d4bba30STeresa Johnson } 1323d4bba30STeresa Johnson 1333d4bba30STeresa Johnson thread_return_t 1343d4bba30STeresa Johnson MemprofThread::ThreadStart(tid_t os_id, 1353d4bba30STeresa Johnson atomic_uintptr_t *signal_thread_is_registered) { 1363d4bba30STeresa Johnson Init(); 1373d4bba30STeresa Johnson memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular, 1383d4bba30STeresa Johnson nullptr); 1393d4bba30STeresa Johnson if (signal_thread_is_registered) 1403d4bba30STeresa Johnson atomic_store(signal_thread_is_registered, 1, memory_order_release); 1413d4bba30STeresa Johnson 1423d4bba30STeresa Johnson if (!start_routine_) { 1433d4bba30STeresa Johnson // start_routine_ == 0 if we're on the main thread or on one of the 1443d4bba30STeresa Johnson // OS X libdispatch worker threads. But nobody is supposed to call 1453d4bba30STeresa Johnson // ThreadStart() for the worker threads. 1463d4bba30STeresa Johnson CHECK_EQ(tid(), 0); 1473d4bba30STeresa Johnson return 0; 1483d4bba30STeresa Johnson } 1493d4bba30STeresa Johnson 1503d4bba30STeresa Johnson return start_routine_(arg_); 1513d4bba30STeresa Johnson } 1523d4bba30STeresa Johnson 1533d4bba30STeresa Johnson MemprofThread *CreateMainThread() { 1543d4bba30STeresa Johnson MemprofThread *main_thread = MemprofThread::Create( 15592a3a2dcSDmitry Vyukov /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ kMainTid, 1563d4bba30STeresa Johnson /* stack */ nullptr, /* detached */ true); 1573d4bba30STeresa Johnson SetCurrentThread(main_thread); 1583d4bba30STeresa Johnson main_thread->ThreadStart(internal_getpid(), 1593d4bba30STeresa Johnson /* signal_thread_is_registered */ nullptr); 1603d4bba30STeresa Johnson return main_thread; 1613d4bba30STeresa Johnson } 1623d4bba30STeresa Johnson 1633d4bba30STeresa Johnson // This implementation doesn't use the argument, which is just passed down 1643d4bba30STeresa Johnson // from the caller of Init (which see, above). It's only there to support 1653d4bba30STeresa Johnson // OS-specific implementations that need more information passed through. 1663d4bba30STeresa Johnson void MemprofThread::SetThreadStackAndTls(const InitOptions *options) { 1673d4bba30STeresa Johnson DCHECK_EQ(options, nullptr); 168f13b7d0bSVitaly Buka GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_top_, 169f13b7d0bSVitaly Buka &tls_begin_, &tls_end_); 1703d4bba30STeresa Johnson dtls_ = DTLS_Get(); 1713d4bba30STeresa Johnson 1723d4bba30STeresa Johnson if (stack_top_ != stack_bottom_) { 1733d4bba30STeresa Johnson int local; 1743d4bba30STeresa Johnson CHECK(AddrIsInStack((uptr)&local)); 1753d4bba30STeresa Johnson } 1763d4bba30STeresa Johnson } 1773d4bba30STeresa Johnson 1783d4bba30STeresa Johnson bool MemprofThread::AddrIsInStack(uptr addr) { 1793d4bba30STeresa Johnson const auto bounds = GetStackBounds(); 1803d4bba30STeresa Johnson return addr >= bounds.bottom && addr < bounds.top; 1813d4bba30STeresa Johnson } 1823d4bba30STeresa Johnson 1833d4bba30STeresa Johnson MemprofThread *GetCurrentThread() { 1843d4bba30STeresa Johnson MemprofThreadContext *context = 1853d4bba30STeresa Johnson reinterpret_cast<MemprofThreadContext *>(TSDGet()); 1863d4bba30STeresa Johnson if (!context) 1873d4bba30STeresa Johnson return nullptr; 1883d4bba30STeresa Johnson return context->thread; 1893d4bba30STeresa Johnson } 1903d4bba30STeresa Johnson 1913d4bba30STeresa Johnson void SetCurrentThread(MemprofThread *t) { 1923d4bba30STeresa Johnson CHECK(t->context()); 19313a442caSMartin Liska VReport(2, "SetCurrentThread: %p for thread %p\n", (void *)t->context(), 1943d4bba30STeresa Johnson (void *)GetThreadSelf()); 1953d4bba30STeresa Johnson // Make sure we do not reset the current MemprofThread. 1963d4bba30STeresa Johnson CHECK_EQ(0, TSDGet()); 1973d4bba30STeresa Johnson TSDSet(t->context()); 1983d4bba30STeresa Johnson CHECK_EQ(t->context(), TSDGet()); 1993d4bba30STeresa Johnson } 2003d4bba30STeresa Johnson 2013d4bba30STeresa Johnson u32 GetCurrentTidOrInvalid() { 2023d4bba30STeresa Johnson MemprofThread *t = GetCurrentThread(); 2033d4bba30STeresa Johnson return t ? t->tid() : kInvalidTid; 2043d4bba30STeresa Johnson } 2053d4bba30STeresa Johnson 2063d4bba30STeresa Johnson void EnsureMainThreadIDIsCorrect() { 2073d4bba30STeresa Johnson MemprofThreadContext *context = 2083d4bba30STeresa Johnson reinterpret_cast<MemprofThreadContext *>(TSDGet()); 20992a3a2dcSDmitry Vyukov if (context && (context->tid == kMainTid)) 2103d4bba30STeresa Johnson context->os_id = GetTid(); 2113d4bba30STeresa Johnson } 2123d4bba30STeresa Johnson } // namespace __memprof 213