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