Path: blob/main/contrib/llvm-project/compiler-rt/lib/memprof/memprof_thread.cpp
35236 views
//===-- memprof_thread.cpp -----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of MemProfiler, a memory profiler.9//10// Thread-related code.11//===----------------------------------------------------------------------===//12#include "memprof_thread.h"13#include "memprof_allocator.h"14#include "memprof_interceptors.h"15#include "memprof_mapping.h"16#include "memprof_stack.h"17#include "sanitizer_common/sanitizer_common.h"18#include "sanitizer_common/sanitizer_placement_new.h"19#include "sanitizer_common/sanitizer_stackdepot.h"20#include "sanitizer_common/sanitizer_tls_get_addr.h"2122namespace __memprof {2324// MemprofThreadContext implementation.2526void MemprofThreadContext::OnCreated(void *arg) {27CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg);28if (args->stack)29stack_id = StackDepotPut(*args->stack);30thread = args->thread;31thread->set_context(this);32}3334void MemprofThreadContext::OnFinished() {35// Drop the link to the MemprofThread object.36thread = nullptr;37}3839alignas(16) static char thread_registry_placeholder[sizeof(ThreadRegistry)];40static ThreadRegistry *memprof_thread_registry;4142static Mutex mu_for_thread_context;43static LowLevelAllocator allocator_for_thread_context;4445static ThreadContextBase *GetMemprofThreadContext(u32 tid) {46Lock lock(&mu_for_thread_context);47return new (allocator_for_thread_context) MemprofThreadContext(tid);48}4950ThreadRegistry &memprofThreadRegistry() {51static bool initialized;52// Don't worry about thread_safety - this should be called when there is53// a single thread.54if (!initialized) {55// Never reuse MemProf threads: we store pointer to MemprofThreadContext56// in TSD and can't reliably tell when no more TSD destructors will57// be called. It would be wrong to reuse MemprofThreadContext for another58// thread before all TSD destructors will be called for it.59memprof_thread_registry = new (thread_registry_placeholder)60ThreadRegistry(GetMemprofThreadContext);61initialized = true;62}63return *memprof_thread_registry;64}6566MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) {67return static_cast<MemprofThreadContext *>(68memprofThreadRegistry().GetThreadLocked(tid));69}7071// MemprofThread implementation.7273MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg,74u32 parent_tid, StackTrace *stack,75bool detached) {76uptr PageSize = GetPageSizeCached();77uptr size = RoundUpTo(sizeof(MemprofThread), PageSize);78MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__);79thread->start_routine_ = start_routine;80thread->arg_ = arg;81MemprofThreadContext::CreateThreadContextArgs args = {thread, stack};82memprofThreadRegistry().CreateThread(0, detached, parent_tid, &args);8384return thread;85}8687void MemprofThread::TSDDtor(void *tsd) {88MemprofThreadContext *context = (MemprofThreadContext *)tsd;89VReport(1, "T%d TSDDtor\n", context->tid);90if (context->thread)91context->thread->Destroy();92}9394void MemprofThread::Destroy() {95int tid = this->tid();96VReport(1, "T%d exited\n", tid);9798malloc_storage().CommitBack();99memprofThreadRegistry().FinishThread(tid);100FlushToDeadThreadStats(&stats_);101uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached());102UnmapOrDie(this, size);103DTLS_Destroy();104}105106inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const {107if (stack_bottom_ >= stack_top_)108return {0, 0};109return {stack_bottom_, stack_top_};110}111112uptr MemprofThread::stack_top() { return GetStackBounds().top; }113114uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; }115116uptr MemprofThread::stack_size() {117const auto bounds = GetStackBounds();118return bounds.top - bounds.bottom;119}120121void MemprofThread::Init(const InitOptions *options) {122CHECK_EQ(this->stack_size(), 0U);123SetThreadStackAndTls(options);124if (stack_top_ != stack_bottom_) {125CHECK_GT(this->stack_size(), 0U);126CHECK(AddrIsInMem(stack_bottom_));127CHECK(AddrIsInMem(stack_top_ - 1));128}129int local = 0;130VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),131(void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,132(void *)&local);133}134135thread_return_t136MemprofThread::ThreadStart(tid_t os_id,137atomic_uintptr_t *signal_thread_is_registered) {138Init();139memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular,140nullptr);141if (signal_thread_is_registered)142atomic_store(signal_thread_is_registered, 1, memory_order_release);143144if (!start_routine_) {145// start_routine_ == 0 if we're on the main thread or on one of the146// OS X libdispatch worker threads. But nobody is supposed to call147// ThreadStart() for the worker threads.148CHECK_EQ(tid(), 0);149return 0;150}151152return start_routine_(arg_);153}154155MemprofThread *CreateMainThread() {156MemprofThread *main_thread = MemprofThread::Create(157/* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ kMainTid,158/* stack */ nullptr, /* detached */ true);159SetCurrentThread(main_thread);160main_thread->ThreadStart(internal_getpid(),161/* signal_thread_is_registered */ nullptr);162return main_thread;163}164165// This implementation doesn't use the argument, which is just passed down166// from the caller of Init (which see, above). It's only there to support167// OS-specific implementations that need more information passed through.168void MemprofThread::SetThreadStackAndTls(const InitOptions *options) {169DCHECK_EQ(options, nullptr);170uptr tls_size = 0;171uptr stack_size = 0;172GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_size,173&tls_begin_, &tls_size);174stack_top_ = stack_bottom_ + stack_size;175tls_end_ = tls_begin_ + tls_size;176dtls_ = DTLS_Get();177178if (stack_top_ != stack_bottom_) {179int local;180CHECK(AddrIsInStack((uptr)&local));181}182}183184bool MemprofThread::AddrIsInStack(uptr addr) {185const auto bounds = GetStackBounds();186return addr >= bounds.bottom && addr < bounds.top;187}188189MemprofThread *GetCurrentThread() {190MemprofThreadContext *context =191reinterpret_cast<MemprofThreadContext *>(TSDGet());192if (!context)193return nullptr;194return context->thread;195}196197void SetCurrentThread(MemprofThread *t) {198CHECK(t->context());199VReport(2, "SetCurrentThread: %p for thread %p\n", (void *)t->context(),200(void *)GetThreadSelf());201// Make sure we do not reset the current MemprofThread.202CHECK_EQ(0, TSDGet());203TSDSet(t->context());204CHECK_EQ(t->context(), TSDGet());205}206207u32 GetCurrentTidOrInvalid() {208MemprofThread *t = GetCurrentThread();209return t ? t->tid() : kInvalidTid;210}211212void EnsureMainThreadIDIsCorrect() {213MemprofThreadContext *context =214reinterpret_cast<MemprofThreadContext *>(TSDGet());215if (context && (context->tid == kMainTid))216context->os_id = GetTid();217}218} // namespace __memprof219220221