Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/compiler-rt/lib/memprof/memprof_thread.cpp
35236 views
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
CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg);
29
if (args->stack)
30
stack_id = StackDepotPut(*args->stack);
31
thread = args->thread;
32
thread->set_context(this);
33
}
34
35
void MemprofThreadContext::OnFinished() {
36
// Drop the link to the MemprofThread object.
37
thread = nullptr;
38
}
39
40
alignas(16) static char thread_registry_placeholder[sizeof(ThreadRegistry)];
41
static ThreadRegistry *memprof_thread_registry;
42
43
static Mutex mu_for_thread_context;
44
static LowLevelAllocator allocator_for_thread_context;
45
46
static ThreadContextBase *GetMemprofThreadContext(u32 tid) {
47
Lock lock(&mu_for_thread_context);
48
return new (allocator_for_thread_context) MemprofThreadContext(tid);
49
}
50
51
ThreadRegistry &memprofThreadRegistry() {
52
static bool initialized;
53
// Don't worry about thread_safety - this should be called when there is
54
// a single thread.
55
if (!initialized) {
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);
62
initialized = true;
63
}
64
return *memprof_thread_registry;
65
}
66
67
MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) {
68
return static_cast<MemprofThreadContext *>(
69
memprofThreadRegistry().GetThreadLocked(tid));
70
}
71
72
// MemprofThread implementation.
73
74
MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg,
75
u32 parent_tid, StackTrace *stack,
76
bool detached) {
77
uptr PageSize = GetPageSizeCached();
78
uptr size = RoundUpTo(sizeof(MemprofThread), PageSize);
79
MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__);
80
thread->start_routine_ = start_routine;
81
thread->arg_ = arg;
82
MemprofThreadContext::CreateThreadContextArgs args = {thread, stack};
83
memprofThreadRegistry().CreateThread(0, detached, parent_tid, &args);
84
85
return thread;
86
}
87
88
void MemprofThread::TSDDtor(void *tsd) {
89
MemprofThreadContext *context = (MemprofThreadContext *)tsd;
90
VReport(1, "T%d TSDDtor\n", context->tid);
91
if (context->thread)
92
context->thread->Destroy();
93
}
94
95
void MemprofThread::Destroy() {
96
int tid = this->tid();
97
VReport(1, "T%d exited\n", tid);
98
99
malloc_storage().CommitBack();
100
memprofThreadRegistry().FinishThread(tid);
101
FlushToDeadThreadStats(&stats_);
102
uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached());
103
UnmapOrDie(this, size);
104
DTLS_Destroy();
105
}
106
107
inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const {
108
if (stack_bottom_ >= stack_top_)
109
return {0, 0};
110
return {stack_bottom_, stack_top_};
111
}
112
113
uptr MemprofThread::stack_top() { return GetStackBounds().top; }
114
115
uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; }
116
117
uptr MemprofThread::stack_size() {
118
const auto bounds = GetStackBounds();
119
return bounds.top - bounds.bottom;
120
}
121
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));
129
}
130
int local = 0;
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_,
133
(void *)&local);
134
}
135
136
thread_return_t
137
MemprofThread::ThreadStart(tid_t os_id,
138
atomic_uintptr_t *signal_thread_is_registered) {
139
Init();
140
memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular,
141
nullptr);
142
if (signal_thread_is_registered)
143
atomic_store(signal_thread_is_registered, 1, memory_order_release);
144
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.
149
CHECK_EQ(tid(), 0);
150
return 0;
151
}
152
153
return start_routine_(arg_);
154
}
155
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);
163
return main_thread;
164
}
165
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);
171
uptr tls_size = 0;
172
uptr stack_size = 0;
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;
177
dtls_ = DTLS_Get();
178
179
if (stack_top_ != stack_bottom_) {
180
int local;
181
CHECK(AddrIsInStack((uptr)&local));
182
}
183
}
184
185
bool MemprofThread::AddrIsInStack(uptr addr) {
186
const auto bounds = GetStackBounds();
187
return addr >= bounds.bottom && addr < bounds.top;
188
}
189
190
MemprofThread *GetCurrentThread() {
191
MemprofThreadContext *context =
192
reinterpret_cast<MemprofThreadContext *>(TSDGet());
193
if (!context)
194
return nullptr;
195
return context->thread;
196
}
197
198
void SetCurrentThread(MemprofThread *t) {
199
CHECK(t->context());
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());
206
}
207
208
u32 GetCurrentTidOrInvalid() {
209
MemprofThread *t = GetCurrentThread();
210
return t ? t->tid() : kInvalidTid;
211
}
212
213
void EnsureMainThreadIDIsCorrect() {
214
MemprofThreadContext *context =
215
reinterpret_cast<MemprofThreadContext *>(TSDGet());
216
if (context && (context->tid == kMainTid))
217
context->os_id = GetTid();
218
}
219
} // namespace __memprof
220
221