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