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