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