123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- //===-- 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 Mutex mu_for_thread_context;
- static LowLevelAllocator allocator_for_thread_context;
- static ThreadContextBase *GetMemprofThreadContext(u32 tid) {
- Lock 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(0, 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_,
- (void *)&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", (void *)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
|