brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.9 KiB · 11cc481 Raw
213 lines · cpp
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-exception6//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 23namespace __memprof {24 25// MemprofThreadContext implementation.26 27void MemprofThreadContext::OnCreated(void *arg) {28  thread = static_cast<MemprofThread *>(arg);29  thread->set_context(this);30}31 32void MemprofThreadContext::OnFinished() {33  // Drop the link to the MemprofThread object.34  thread = nullptr;35}36 37alignas(16) static char thread_registry_placeholder[sizeof(ThreadRegistry)];38static ThreadRegistry *memprof_thread_registry;39 40static Mutex mu_for_thread_context;41static LowLevelAllocator allocator_for_thread_context;42 43static ThreadContextBase *GetMemprofThreadContext(u32 tid) {44  Lock lock(&mu_for_thread_context);45  return new (allocator_for_thread_context) MemprofThreadContext(tid);46}47 48ThreadRegistry &memprofThreadRegistry() {49  static bool initialized;50  // Don't worry about thread_safety - this should be called when there is51  // a single thread.52  if (!initialized) {53    // Never reuse MemProf threads: we store pointer to MemprofThreadContext54    // in TSD and can't reliably tell when no more TSD destructors will55    // be called. It would be wrong to reuse MemprofThreadContext for another56    // thread before all TSD destructors will be called for it.57    memprof_thread_registry = new (thread_registry_placeholder)58        ThreadRegistry(GetMemprofThreadContext);59    initialized = true;60  }61  return *memprof_thread_registry;62}63 64MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) {65  return static_cast<MemprofThreadContext *>(66      memprofThreadRegistry().GetThreadLocked(tid));67}68 69// MemprofThread implementation.70 71MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg,72                                     u32 parent_tid, StackTrace *stack,73                                     bool detached) {74  uptr PageSize = GetPageSizeCached();75  uptr size = RoundUpTo(sizeof(MemprofThread), PageSize);76  MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__);77  thread->start_routine_ = start_routine;78  thread->arg_ = arg;79  memprofThreadRegistry().CreateThread(80      0, detached, parent_tid, stack ? StackDepotPut(*stack) : 0, thread);81 82  return thread;83}84 85void MemprofThread::TSDDtor(void *tsd) {86  MemprofThreadContext *context = (MemprofThreadContext *)tsd;87  VReport(1, "T%d TSDDtor\n", context->tid);88  if (context->thread)89    context->thread->Destroy();90}91 92void MemprofThread::Destroy() {93  int tid = this->tid();94  VReport(1, "T%d exited\n", tid);95 96  malloc_storage().CommitBack();97  memprofThreadRegistry().FinishThread(tid);98  FlushToDeadThreadStats(&stats_);99  uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached());100  UnmapOrDie(this, size);101  DTLS_Destroy();102}103 104inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const {105  if (stack_bottom_ >= stack_top_)106    return {0, 0};107  return {stack_bottom_, stack_top_};108}109 110uptr MemprofThread::stack_top() { return GetStackBounds().top; }111 112uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; }113 114uptr MemprofThread::stack_size() {115  const auto bounds = GetStackBounds();116  return bounds.top - bounds.bottom;117}118 119void MemprofThread::Init(const InitOptions *options) {120  CHECK_EQ(this->stack_size(), 0U);121  SetThreadStackAndTls(options);122  if (stack_top_ != stack_bottom_) {123    CHECK_GT(this->stack_size(), 0U);124    CHECK(AddrIsInMem(stack_bottom_));125    CHECK(AddrIsInMem(stack_top_ - 1));126  }127  int local = 0;128  VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),129          (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,130          (void *)&local);131}132 133thread_return_t134MemprofThread::ThreadStart(ThreadID os_id,135                           atomic_uintptr_t *signal_thread_is_registered) {136  Init();137  memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular,138                                      nullptr);139  if (signal_thread_is_registered)140    atomic_store(signal_thread_is_registered, 1, memory_order_release);141 142  if (!start_routine_) {143    // start_routine_ == 0 if we're on the main thread or on one of the144    // OS X libdispatch worker threads. But nobody is supposed to call145    // ThreadStart() for the worker threads.146    CHECK_EQ(tid(), 0);147    return 0;148  }149 150  return start_routine_(arg_);151}152 153MemprofThread *CreateMainThread() {154  MemprofThread *main_thread = MemprofThread::Create(155      /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ kMainTid,156      /* stack */ nullptr, /* detached */ true);157  SetCurrentThread(main_thread);158  main_thread->ThreadStart(internal_getpid(),159                           /* signal_thread_is_registered */ nullptr);160  return main_thread;161}162 163// This implementation doesn't use the argument, which is just passed down164// from the caller of Init (which see, above).  It's only there to support165// OS-specific implementations that need more information passed through.166void MemprofThread::SetThreadStackAndTls(const InitOptions *options) {167  DCHECK_EQ(options, nullptr);168  GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_top_,169                       &tls_begin_, &tls_end_);170  dtls_ = DTLS_Get();171 172  if (stack_top_ != stack_bottom_) {173    int local;174    CHECK(AddrIsInStack((uptr)&local));175  }176}177 178bool MemprofThread::AddrIsInStack(uptr addr) {179  const auto bounds = GetStackBounds();180  return addr >= bounds.bottom && addr < bounds.top;181}182 183MemprofThread *GetCurrentThread() {184  MemprofThreadContext *context =185      reinterpret_cast<MemprofThreadContext *>(TSDGet());186  if (!context)187    return nullptr;188  return context->thread;189}190 191void SetCurrentThread(MemprofThread *t) {192  CHECK(t->context());193  VReport(2, "SetCurrentThread: %p for thread %p\n", (void *)t->context(),194          (void *)GetThreadSelf());195  // Make sure we do not reset the current MemprofThread.196  CHECK_EQ(0, TSDGet());197  TSDSet(t->context());198  CHECK_EQ(t->context(), TSDGet());199}200 201u32 GetCurrentTidOrInvalid() {202  MemprofThread *t = GetCurrentThread();203  return t ? t->tid() : kInvalidTid;204}205 206void EnsureMainThreadIDIsCorrect() {207  MemprofThreadContext *context =208      reinterpret_cast<MemprofThreadContext *>(TSDGet());209  if (context && (context->tid == kMainTid))210    context->os_id = GetTid();211}212} // namespace __memprof213