xref: /llvm-project/compiler-rt/lib/memprof/memprof_thread.cpp (revision abe148a09f61fa341f80376c763ea4706cfca30e)
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