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