xref: /llvm-project/compiler-rt/lib/memprof/memprof_thread.cpp (revision abe148a09f61fa341f80376c763ea4706cfca30e)
13d4bba30STeresa Johnson //===-- memprof_thread.cpp -----------------------------------------------===//
23d4bba30STeresa Johnson //
33d4bba30STeresa Johnson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43d4bba30STeresa Johnson // See https://llvm.org/LICENSE.txt for license information.
53d4bba30STeresa Johnson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63d4bba30STeresa Johnson //
73d4bba30STeresa Johnson //===----------------------------------------------------------------------===//
83d4bba30STeresa Johnson //
93d4bba30STeresa Johnson // This file is a part of MemProfiler, a memory profiler.
103d4bba30STeresa Johnson //
113d4bba30STeresa Johnson // Thread-related code.
123d4bba30STeresa Johnson //===----------------------------------------------------------------------===//
133d4bba30STeresa Johnson #include "memprof_thread.h"
143d4bba30STeresa Johnson #include "memprof_allocator.h"
153d4bba30STeresa Johnson #include "memprof_interceptors.h"
163d4bba30STeresa Johnson #include "memprof_mapping.h"
173d4bba30STeresa Johnson #include "memprof_stack.h"
183d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_common.h"
193d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_placement_new.h"
203d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_stackdepot.h"
213d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_tls_get_addr.h"
223d4bba30STeresa Johnson 
233d4bba30STeresa Johnson namespace __memprof {
243d4bba30STeresa Johnson 
253d4bba30STeresa Johnson // MemprofThreadContext implementation.
263d4bba30STeresa Johnson 
273d4bba30STeresa Johnson void MemprofThreadContext::OnCreated(void *arg) {
28*abe148a0SVitaly Buka   thread = static_cast<MemprofThread *>(arg);
293d4bba30STeresa Johnson   thread->set_context(this);
303d4bba30STeresa Johnson }
313d4bba30STeresa Johnson 
323d4bba30STeresa Johnson void MemprofThreadContext::OnFinished() {
333d4bba30STeresa Johnson   // Drop the link to the MemprofThread object.
343d4bba30STeresa Johnson   thread = nullptr;
353d4bba30STeresa Johnson }
363d4bba30STeresa Johnson 
37ba66d60bSFangrui Song alignas(16) static char thread_registry_placeholder[sizeof(ThreadRegistry)];
383d4bba30STeresa Johnson static ThreadRegistry *memprof_thread_registry;
393d4bba30STeresa Johnson 
4056debbf5SDmitry Vyukov static Mutex mu_for_thread_context;
413d4bba30STeresa Johnson static LowLevelAllocator allocator_for_thread_context;
423d4bba30STeresa Johnson 
433d4bba30STeresa Johnson static ThreadContextBase *GetMemprofThreadContext(u32 tid) {
4456debbf5SDmitry Vyukov   Lock lock(&mu_for_thread_context);
453d4bba30STeresa Johnson   return new (allocator_for_thread_context) MemprofThreadContext(tid);
463d4bba30STeresa Johnson }
473d4bba30STeresa Johnson 
483d4bba30STeresa Johnson ThreadRegistry &memprofThreadRegistry() {
493d4bba30STeresa Johnson   static bool initialized;
503d4bba30STeresa Johnson   // Don't worry about thread_safety - this should be called when there is
513d4bba30STeresa Johnson   // a single thread.
523d4bba30STeresa Johnson   if (!initialized) {
533d4bba30STeresa Johnson     // Never reuse MemProf threads: we store pointer to MemprofThreadContext
543d4bba30STeresa Johnson     // in TSD and can't reliably tell when no more TSD destructors will
553d4bba30STeresa Johnson     // be called. It would be wrong to reuse MemprofThreadContext for another
563d4bba30STeresa Johnson     // thread before all TSD destructors will be called for it.
57dfd9808bSDmitry Vyukov     memprof_thread_registry = new (thread_registry_placeholder)
58dfd9808bSDmitry Vyukov         ThreadRegistry(GetMemprofThreadContext);
593d4bba30STeresa Johnson     initialized = true;
603d4bba30STeresa Johnson   }
613d4bba30STeresa Johnson   return *memprof_thread_registry;
623d4bba30STeresa Johnson }
633d4bba30STeresa Johnson 
643d4bba30STeresa Johnson MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) {
653d4bba30STeresa Johnson   return static_cast<MemprofThreadContext *>(
663d4bba30STeresa Johnson       memprofThreadRegistry().GetThreadLocked(tid));
673d4bba30STeresa Johnson }
683d4bba30STeresa Johnson 
693d4bba30STeresa Johnson // MemprofThread implementation.
703d4bba30STeresa Johnson 
713d4bba30STeresa Johnson MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg,
723d4bba30STeresa Johnson                                      u32 parent_tid, StackTrace *stack,
733d4bba30STeresa Johnson                                      bool detached) {
743d4bba30STeresa Johnson   uptr PageSize = GetPageSizeCached();
753d4bba30STeresa Johnson   uptr size = RoundUpTo(sizeof(MemprofThread), PageSize);
763d4bba30STeresa Johnson   MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__);
773d4bba30STeresa Johnson   thread->start_routine_ = start_routine;
783d4bba30STeresa Johnson   thread->arg_ = arg;
79*abe148a0SVitaly Buka   memprofThreadRegistry().CreateThread(
80*abe148a0SVitaly Buka       0, detached, parent_tid, stack ? StackDepotPut(*stack) : 0, thread);
813d4bba30STeresa Johnson 
823d4bba30STeresa Johnson   return thread;
833d4bba30STeresa Johnson }
843d4bba30STeresa Johnson 
853d4bba30STeresa Johnson void MemprofThread::TSDDtor(void *tsd) {
863d4bba30STeresa Johnson   MemprofThreadContext *context = (MemprofThreadContext *)tsd;
873d4bba30STeresa Johnson   VReport(1, "T%d TSDDtor\n", context->tid);
883d4bba30STeresa Johnson   if (context->thread)
893d4bba30STeresa Johnson     context->thread->Destroy();
903d4bba30STeresa Johnson }
913d4bba30STeresa Johnson 
923d4bba30STeresa Johnson void MemprofThread::Destroy() {
933d4bba30STeresa Johnson   int tid = this->tid();
943d4bba30STeresa Johnson   VReport(1, "T%d exited\n", tid);
953d4bba30STeresa Johnson 
963d4bba30STeresa Johnson   malloc_storage().CommitBack();
973d4bba30STeresa Johnson   memprofThreadRegistry().FinishThread(tid);
983d4bba30STeresa Johnson   FlushToDeadThreadStats(&stats_);
993d4bba30STeresa Johnson   uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached());
1003d4bba30STeresa Johnson   UnmapOrDie(this, size);
1013d4bba30STeresa Johnson   DTLS_Destroy();
1023d4bba30STeresa Johnson }
1033d4bba30STeresa Johnson 
1043d4bba30STeresa Johnson inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const {
1053d4bba30STeresa Johnson   if (stack_bottom_ >= stack_top_)
1063d4bba30STeresa Johnson     return {0, 0};
1073d4bba30STeresa Johnson   return {stack_bottom_, stack_top_};
1083d4bba30STeresa Johnson }
1093d4bba30STeresa Johnson 
1103d4bba30STeresa Johnson uptr MemprofThread::stack_top() { return GetStackBounds().top; }
1113d4bba30STeresa Johnson 
1123d4bba30STeresa Johnson uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; }
1133d4bba30STeresa Johnson 
1143d4bba30STeresa Johnson uptr MemprofThread::stack_size() {
1153d4bba30STeresa Johnson   const auto bounds = GetStackBounds();
1163d4bba30STeresa Johnson   return bounds.top - bounds.bottom;
1173d4bba30STeresa Johnson }
1183d4bba30STeresa Johnson 
1193d4bba30STeresa Johnson void MemprofThread::Init(const InitOptions *options) {
1203d4bba30STeresa Johnson   CHECK_EQ(this->stack_size(), 0U);
1213d4bba30STeresa Johnson   SetThreadStackAndTls(options);
1223d4bba30STeresa Johnson   if (stack_top_ != stack_bottom_) {
1233d4bba30STeresa Johnson     CHECK_GT(this->stack_size(), 0U);
1243d4bba30STeresa Johnson     CHECK(AddrIsInMem(stack_bottom_));
1253d4bba30STeresa Johnson     CHECK(AddrIsInMem(stack_top_ - 1));
1263d4bba30STeresa Johnson   }
1273d4bba30STeresa Johnson   int local = 0;
1283d4bba30STeresa Johnson   VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
1293d4bba30STeresa Johnson           (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
13013a442caSMartin Liska           (void *)&local);
1313d4bba30STeresa Johnson }
1323d4bba30STeresa Johnson 
1333d4bba30STeresa Johnson thread_return_t
1343d4bba30STeresa Johnson MemprofThread::ThreadStart(tid_t os_id,
1353d4bba30STeresa Johnson                            atomic_uintptr_t *signal_thread_is_registered) {
1363d4bba30STeresa Johnson   Init();
1373d4bba30STeresa Johnson   memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular,
1383d4bba30STeresa Johnson                                       nullptr);
1393d4bba30STeresa Johnson   if (signal_thread_is_registered)
1403d4bba30STeresa Johnson     atomic_store(signal_thread_is_registered, 1, memory_order_release);
1413d4bba30STeresa Johnson 
1423d4bba30STeresa Johnson   if (!start_routine_) {
1433d4bba30STeresa Johnson     // start_routine_ == 0 if we're on the main thread or on one of the
1443d4bba30STeresa Johnson     // OS X libdispatch worker threads. But nobody is supposed to call
1453d4bba30STeresa Johnson     // ThreadStart() for the worker threads.
1463d4bba30STeresa Johnson     CHECK_EQ(tid(), 0);
1473d4bba30STeresa Johnson     return 0;
1483d4bba30STeresa Johnson   }
1493d4bba30STeresa Johnson 
1503d4bba30STeresa Johnson   return start_routine_(arg_);
1513d4bba30STeresa Johnson }
1523d4bba30STeresa Johnson 
1533d4bba30STeresa Johnson MemprofThread *CreateMainThread() {
1543d4bba30STeresa Johnson   MemprofThread *main_thread = MemprofThread::Create(
15592a3a2dcSDmitry Vyukov       /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ kMainTid,
1563d4bba30STeresa Johnson       /* stack */ nullptr, /* detached */ true);
1573d4bba30STeresa Johnson   SetCurrentThread(main_thread);
1583d4bba30STeresa Johnson   main_thread->ThreadStart(internal_getpid(),
1593d4bba30STeresa Johnson                            /* signal_thread_is_registered */ nullptr);
1603d4bba30STeresa Johnson   return main_thread;
1613d4bba30STeresa Johnson }
1623d4bba30STeresa Johnson 
1633d4bba30STeresa Johnson // This implementation doesn't use the argument, which is just passed down
1643d4bba30STeresa Johnson // from the caller of Init (which see, above).  It's only there to support
1653d4bba30STeresa Johnson // OS-specific implementations that need more information passed through.
1663d4bba30STeresa Johnson void MemprofThread::SetThreadStackAndTls(const InitOptions *options) {
1673d4bba30STeresa Johnson   DCHECK_EQ(options, nullptr);
168f13b7d0bSVitaly Buka   GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_top_,
169f13b7d0bSVitaly Buka                        &tls_begin_, &tls_end_);
1703d4bba30STeresa Johnson   dtls_ = DTLS_Get();
1713d4bba30STeresa Johnson 
1723d4bba30STeresa Johnson   if (stack_top_ != stack_bottom_) {
1733d4bba30STeresa Johnson     int local;
1743d4bba30STeresa Johnson     CHECK(AddrIsInStack((uptr)&local));
1753d4bba30STeresa Johnson   }
1763d4bba30STeresa Johnson }
1773d4bba30STeresa Johnson 
1783d4bba30STeresa Johnson bool MemprofThread::AddrIsInStack(uptr addr) {
1793d4bba30STeresa Johnson   const auto bounds = GetStackBounds();
1803d4bba30STeresa Johnson   return addr >= bounds.bottom && addr < bounds.top;
1813d4bba30STeresa Johnson }
1823d4bba30STeresa Johnson 
1833d4bba30STeresa Johnson MemprofThread *GetCurrentThread() {
1843d4bba30STeresa Johnson   MemprofThreadContext *context =
1853d4bba30STeresa Johnson       reinterpret_cast<MemprofThreadContext *>(TSDGet());
1863d4bba30STeresa Johnson   if (!context)
1873d4bba30STeresa Johnson     return nullptr;
1883d4bba30STeresa Johnson   return context->thread;
1893d4bba30STeresa Johnson }
1903d4bba30STeresa Johnson 
1913d4bba30STeresa Johnson void SetCurrentThread(MemprofThread *t) {
1923d4bba30STeresa Johnson   CHECK(t->context());
19313a442caSMartin Liska   VReport(2, "SetCurrentThread: %p for thread %p\n", (void *)t->context(),
1943d4bba30STeresa Johnson           (void *)GetThreadSelf());
1953d4bba30STeresa Johnson   // Make sure we do not reset the current MemprofThread.
1963d4bba30STeresa Johnson   CHECK_EQ(0, TSDGet());
1973d4bba30STeresa Johnson   TSDSet(t->context());
1983d4bba30STeresa Johnson   CHECK_EQ(t->context(), TSDGet());
1993d4bba30STeresa Johnson }
2003d4bba30STeresa Johnson 
2013d4bba30STeresa Johnson u32 GetCurrentTidOrInvalid() {
2023d4bba30STeresa Johnson   MemprofThread *t = GetCurrentThread();
2033d4bba30STeresa Johnson   return t ? t->tid() : kInvalidTid;
2043d4bba30STeresa Johnson }
2053d4bba30STeresa Johnson 
2063d4bba30STeresa Johnson void EnsureMainThreadIDIsCorrect() {
2073d4bba30STeresa Johnson   MemprofThreadContext *context =
2083d4bba30STeresa Johnson       reinterpret_cast<MemprofThreadContext *>(TSDGet());
20992a3a2dcSDmitry Vyukov   if (context && (context->tid == kMainTid))
2103d4bba30STeresa Johnson     context->os_id = GetTid();
2113d4bba30STeresa Johnson }
2123d4bba30STeresa Johnson } // namespace __memprof
213