1 //===-- memprof_thread.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of MemProfiler, a memory profiler.
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"
25 // MemprofThreadContext implementation.
27 void MemprofThreadContext::OnCreated(void *arg
) {
28 CreateThreadContextArgs
*args
= static_cast<CreateThreadContextArgs
*>(arg
);
30 stack_id
= StackDepotPut(*args
->stack
);
31 thread
= args
->thread
;
32 thread
->set_context(this);
35 void MemprofThreadContext::OnFinished() {
36 // Drop the link to the MemprofThread object.
40 static ALIGNED(16) char thread_registry_placeholder
[sizeof(ThreadRegistry
)];
41 static ThreadRegistry
*memprof_thread_registry
;
43 static Mutex mu_for_thread_context
;
44 static LowLevelAllocator allocator_for_thread_context
;
46 static ThreadContextBase
*GetMemprofThreadContext(u32 tid
) {
47 Lock
lock(&mu_for_thread_context
);
48 return new (allocator_for_thread_context
) MemprofThreadContext(tid
);
51 ThreadRegistry
&memprofThreadRegistry() {
52 static bool initialized
;
53 // Don't worry about thread_safety - this should be called when there is
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
);
64 return *memprof_thread_registry
;
67 MemprofThreadContext
*GetThreadContextByTidLocked(u32 tid
) {
68 return static_cast<MemprofThreadContext
*>(
69 memprofThreadRegistry().GetThreadLocked(tid
));
72 // MemprofThread implementation.
74 MemprofThread
*MemprofThread::Create(thread_callback_t start_routine
, void *arg
,
75 u32 parent_tid
, StackTrace
*stack
,
77 uptr PageSize
= GetPageSizeCached();
78 uptr size
= RoundUpTo(sizeof(MemprofThread
), PageSize
);
79 MemprofThread
*thread
= (MemprofThread
*)MmapOrDie(size
, __func__
);
80 thread
->start_routine_
= start_routine
;
82 MemprofThreadContext::CreateThreadContextArgs args
= {thread
, stack
};
83 memprofThreadRegistry().CreateThread(0, detached
, parent_tid
, &args
);
88 void MemprofThread::TSDDtor(void *tsd
) {
89 MemprofThreadContext
*context
= (MemprofThreadContext
*)tsd
;
90 VReport(1, "T%d TSDDtor\n", context
->tid
);
92 context
->thread
->Destroy();
95 void MemprofThread::Destroy() {
96 int tid
= this->tid();
97 VReport(1, "T%d exited\n", tid
);
99 malloc_storage().CommitBack();
100 memprofThreadRegistry().FinishThread(tid
);
101 FlushToDeadThreadStats(&stats_
);
102 uptr size
= RoundUpTo(sizeof(MemprofThread
), GetPageSizeCached());
103 UnmapOrDie(this, size
);
107 inline MemprofThread::StackBounds
MemprofThread::GetStackBounds() const {
108 if (stack_bottom_
>= stack_top_
)
110 return {stack_bottom_
, stack_top_
};
113 uptr
MemprofThread::stack_top() { return GetStackBounds().top
; }
115 uptr
MemprofThread::stack_bottom() { return GetStackBounds().bottom
; }
117 uptr
MemprofThread::stack_size() {
118 const auto bounds
= GetStackBounds();
119 return bounds
.top
- bounds
.bottom
;
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));
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_
,
137 MemprofThread::ThreadStart(tid_t os_id
,
138 atomic_uintptr_t
*signal_thread_is_registered
) {
140 memprofThreadRegistry().StartThread(tid(), os_id
, ThreadType::Regular
,
142 if (signal_thread_is_registered
)
143 atomic_store(signal_thread_is_registered
, 1, memory_order_release
);
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.
153 return start_routine_(arg_
);
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);
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.
169 void MemprofThread::SetThreadStackAndTls(const InitOptions
*options
) {
170 DCHECK_EQ(options
, nullptr);
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
;
179 if (stack_top_
!= stack_bottom_
) {
181 CHECK(AddrIsInStack((uptr
)&local
));
185 bool MemprofThread::AddrIsInStack(uptr addr
) {
186 const auto bounds
= GetStackBounds();
187 return addr
>= bounds
.bottom
&& addr
< bounds
.top
;
190 MemprofThread
*GetCurrentThread() {
191 MemprofThreadContext
*context
=
192 reinterpret_cast<MemprofThreadContext
*>(TSDGet());
195 return context
->thread
;
198 void SetCurrentThread(MemprofThread
*t
) {
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());
208 u32
GetCurrentTidOrInvalid() {
209 MemprofThread
*t
= GetCurrentThread();
210 return t
? t
->tid() : kInvalidTid
;
213 void EnsureMainThreadIDIsCorrect() {
214 MemprofThreadContext
*context
=
215 reinterpret_cast<MemprofThreadContext
*>(TSDGet());
216 if (context
&& (context
->tid
== kMainTid
))
217 context
->os_id
= GetTid();
219 } // namespace __memprof