Files
clang-p2996/compiler-rt/lib/memprof/memprof_thread.cpp
Dmitry Vyukov dfd9808b6c sanitizer_common: add simpler ThreadRegistry ctor
Currently ThreadRegistry is overcomplicated because of tsan,
it needs tid quarantine and reuse counters. Other sanitizers
don't need that. It also seems that no other sanitizer now
needs max number of threads. Asan used to need 2^24 limit,
but it does not seem to be needed now. Other sanitizers blindly
copy-pasted that without reasons. Lsan also uses quarantine,
but I don't see why that may be potentially needed.

Add a ThreadRegistry ctor that does not require any sizes
and use it in all sanitizers except for tsan.
In preparation for new tsan runtime, which won't need
any of these parameters as well.

Reviewed By: vitalybuka

Differential Revision: https://reviews.llvm.org/D105713
2021-07-13 22:52:25 -07:00

221 lines
7.3 KiB
C++

//===-- memprof_thread.cpp -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of MemProfiler, a memory profiler.
//
// Thread-related code.
//===----------------------------------------------------------------------===//
#include "memprof_thread.h"
#include "memprof_allocator.h"
#include "memprof_interceptors.h"
#include "memprof_mapping.h"
#include "memprof_stack.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"
namespace __memprof {
// MemprofThreadContext implementation.
void MemprofThreadContext::OnCreated(void *arg) {
CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg);
if (args->stack)
stack_id = StackDepotPut(*args->stack);
thread = args->thread;
thread->set_context(this);
}
void MemprofThreadContext::OnFinished() {
// Drop the link to the MemprofThread object.
thread = nullptr;
}
static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)];
static ThreadRegistry *memprof_thread_registry;
static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED);
static LowLevelAllocator allocator_for_thread_context;
static ThreadContextBase *GetMemprofThreadContext(u32 tid) {
BlockingMutexLock lock(&mu_for_thread_context);
return new (allocator_for_thread_context) MemprofThreadContext(tid);
}
ThreadRegistry &memprofThreadRegistry() {
static bool initialized;
// Don't worry about thread_safety - this should be called when there is
// a single thread.
if (!initialized) {
// Never reuse MemProf threads: we store pointer to MemprofThreadContext
// in TSD and can't reliably tell when no more TSD destructors will
// be called. It would be wrong to reuse MemprofThreadContext for another
// thread before all TSD destructors will be called for it.
memprof_thread_registry = new (thread_registry_placeholder)
ThreadRegistry(GetMemprofThreadContext);
initialized = true;
}
return *memprof_thread_registry;
}
MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) {
return static_cast<MemprofThreadContext *>(
memprofThreadRegistry().GetThreadLocked(tid));
}
// MemprofThread implementation.
MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg,
u32 parent_tid, StackTrace *stack,
bool detached) {
uptr PageSize = GetPageSizeCached();
uptr size = RoundUpTo(sizeof(MemprofThread), PageSize);
MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__);
thread->start_routine_ = start_routine;
thread->arg_ = arg;
MemprofThreadContext::CreateThreadContextArgs args = {thread, stack};
memprofThreadRegistry().CreateThread(*reinterpret_cast<uptr *>(thread),
detached, parent_tid, &args);
return thread;
}
void MemprofThread::TSDDtor(void *tsd) {
MemprofThreadContext *context = (MemprofThreadContext *)tsd;
VReport(1, "T%d TSDDtor\n", context->tid);
if (context->thread)
context->thread->Destroy();
}
void MemprofThread::Destroy() {
int tid = this->tid();
VReport(1, "T%d exited\n", tid);
malloc_storage().CommitBack();
memprofThreadRegistry().FinishThread(tid);
FlushToDeadThreadStats(&stats_);
uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached());
UnmapOrDie(this, size);
DTLS_Destroy();
}
inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const {
if (stack_bottom_ >= stack_top_)
return {0, 0};
return {stack_bottom_, stack_top_};
}
uptr MemprofThread::stack_top() { return GetStackBounds().top; }
uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; }
uptr MemprofThread::stack_size() {
const auto bounds = GetStackBounds();
return bounds.top - bounds.bottom;
}
void MemprofThread::Init(const InitOptions *options) {
CHECK_EQ(this->stack_size(), 0U);
SetThreadStackAndTls(options);
if (stack_top_ != stack_bottom_) {
CHECK_GT(this->stack_size(), 0U);
CHECK(AddrIsInMem(stack_bottom_));
CHECK(AddrIsInMem(stack_top_ - 1));
}
int local = 0;
VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
(void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
&local);
}
thread_return_t
MemprofThread::ThreadStart(tid_t os_id,
atomic_uintptr_t *signal_thread_is_registered) {
Init();
memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular,
nullptr);
if (signal_thread_is_registered)
atomic_store(signal_thread_is_registered, 1, memory_order_release);
if (!start_routine_) {
// start_routine_ == 0 if we're on the main thread or on one of the
// OS X libdispatch worker threads. But nobody is supposed to call
// ThreadStart() for the worker threads.
CHECK_EQ(tid(), 0);
return 0;
}
return start_routine_(arg_);
}
MemprofThread *CreateMainThread() {
MemprofThread *main_thread = MemprofThread::Create(
/* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ kMainTid,
/* stack */ nullptr, /* detached */ true);
SetCurrentThread(main_thread);
main_thread->ThreadStart(internal_getpid(),
/* signal_thread_is_registered */ nullptr);
return main_thread;
}
// This implementation doesn't use the argument, which is just passed down
// from the caller of Init (which see, above). It's only there to support
// OS-specific implementations that need more information passed through.
void MemprofThread::SetThreadStackAndTls(const InitOptions *options) {
DCHECK_EQ(options, nullptr);
uptr tls_size = 0;
uptr stack_size = 0;
GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_size,
&tls_begin_, &tls_size);
stack_top_ = stack_bottom_ + stack_size;
tls_end_ = tls_begin_ + tls_size;
dtls_ = DTLS_Get();
if (stack_top_ != stack_bottom_) {
int local;
CHECK(AddrIsInStack((uptr)&local));
}
}
bool MemprofThread::AddrIsInStack(uptr addr) {
const auto bounds = GetStackBounds();
return addr >= bounds.bottom && addr < bounds.top;
}
MemprofThread *GetCurrentThread() {
MemprofThreadContext *context =
reinterpret_cast<MemprofThreadContext *>(TSDGet());
if (!context)
return nullptr;
return context->thread;
}
void SetCurrentThread(MemprofThread *t) {
CHECK(t->context());
VReport(2, "SetCurrentThread: %p for thread %p\n", t->context(),
(void *)GetThreadSelf());
// Make sure we do not reset the current MemprofThread.
CHECK_EQ(0, TSDGet());
TSDSet(t->context());
CHECK_EQ(t->context(), TSDGet());
}
u32 GetCurrentTidOrInvalid() {
MemprofThread *t = GetCurrentThread();
return t ? t->tid() : kInvalidTid;
}
void EnsureMainThreadIDIsCorrect() {
MemprofThreadContext *context =
reinterpret_cast<MemprofThreadContext *>(TSDGet());
if (context && (context->tid == kMainTid))
context->os_id = GetTid();
}
} // namespace __memprof