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
OnCreated(void * arg)273d4bba30STeresa Johnson void MemprofThreadContext::OnCreated(void *arg) {
283d4bba30STeresa Johnson CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg);
293d4bba30STeresa Johnson if (args->stack)
303d4bba30STeresa Johnson stack_id = StackDepotPut(*args->stack);
313d4bba30STeresa Johnson thread = args->thread;
323d4bba30STeresa Johnson thread->set_context(this);
333d4bba30STeresa Johnson }
343d4bba30STeresa Johnson
OnFinished()353d4bba30STeresa Johnson void MemprofThreadContext::OnFinished() {
363d4bba30STeresa Johnson // Drop the link to the MemprofThread object.
373d4bba30STeresa Johnson thread = nullptr;
383d4bba30STeresa Johnson }
393d4bba30STeresa Johnson
403d4bba30STeresa Johnson static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)];
413d4bba30STeresa Johnson static ThreadRegistry *memprof_thread_registry;
423d4bba30STeresa Johnson
4356debbf5SDmitry Vyukov static Mutex mu_for_thread_context;
443d4bba30STeresa Johnson static LowLevelAllocator allocator_for_thread_context;
453d4bba30STeresa Johnson
GetMemprofThreadContext(u32 tid)463d4bba30STeresa Johnson static ThreadContextBase *GetMemprofThreadContext(u32 tid) {
4756debbf5SDmitry Vyukov Lock lock(&mu_for_thread_context);
483d4bba30STeresa Johnson return new (allocator_for_thread_context) MemprofThreadContext(tid);
493d4bba30STeresa Johnson }
503d4bba30STeresa Johnson
memprofThreadRegistry()513d4bba30STeresa Johnson ThreadRegistry &memprofThreadRegistry() {
523d4bba30STeresa Johnson static bool initialized;
533d4bba30STeresa Johnson // Don't worry about thread_safety - this should be called when there is
543d4bba30STeresa Johnson // a single thread.
553d4bba30STeresa Johnson if (!initialized) {
563d4bba30STeresa Johnson // Never reuse MemProf threads: we store pointer to MemprofThreadContext
573d4bba30STeresa Johnson // in TSD and can't reliably tell when no more TSD destructors will
583d4bba30STeresa Johnson // be called. It would be wrong to reuse MemprofThreadContext for another
593d4bba30STeresa Johnson // thread before all TSD destructors will be called for it.
60dfd9808bSDmitry Vyukov memprof_thread_registry = new (thread_registry_placeholder)
61dfd9808bSDmitry Vyukov ThreadRegistry(GetMemprofThreadContext);
623d4bba30STeresa Johnson initialized = true;
633d4bba30STeresa Johnson }
643d4bba30STeresa Johnson return *memprof_thread_registry;
653d4bba30STeresa Johnson }
663d4bba30STeresa Johnson
GetThreadContextByTidLocked(u32 tid)673d4bba30STeresa Johnson MemprofThreadContext *GetThreadContextByTidLocked(u32 tid) {
683d4bba30STeresa Johnson return static_cast<MemprofThreadContext *>(
693d4bba30STeresa Johnson memprofThreadRegistry().GetThreadLocked(tid));
703d4bba30STeresa Johnson }
713d4bba30STeresa Johnson
723d4bba30STeresa Johnson // MemprofThread implementation.
733d4bba30STeresa Johnson
Create(thread_callback_t start_routine,void * arg,u32 parent_tid,StackTrace * stack,bool detached)743d4bba30STeresa Johnson MemprofThread *MemprofThread::Create(thread_callback_t start_routine, void *arg,
753d4bba30STeresa Johnson u32 parent_tid, StackTrace *stack,
763d4bba30STeresa Johnson bool detached) {
773d4bba30STeresa Johnson uptr PageSize = GetPageSizeCached();
783d4bba30STeresa Johnson uptr size = RoundUpTo(sizeof(MemprofThread), PageSize);
793d4bba30STeresa Johnson MemprofThread *thread = (MemprofThread *)MmapOrDie(size, __func__);
803d4bba30STeresa Johnson thread->start_routine_ = start_routine;
813d4bba30STeresa Johnson thread->arg_ = arg;
823d4bba30STeresa Johnson MemprofThreadContext::CreateThreadContextArgs args = {thread, stack};
83*d1f72f02SDmitry Vyukov memprofThreadRegistry().CreateThread(0, detached, parent_tid, &args);
843d4bba30STeresa Johnson
853d4bba30STeresa Johnson return thread;
863d4bba30STeresa Johnson }
873d4bba30STeresa Johnson
TSDDtor(void * tsd)883d4bba30STeresa Johnson void MemprofThread::TSDDtor(void *tsd) {
893d4bba30STeresa Johnson MemprofThreadContext *context = (MemprofThreadContext *)tsd;
903d4bba30STeresa Johnson VReport(1, "T%d TSDDtor\n", context->tid);
913d4bba30STeresa Johnson if (context->thread)
923d4bba30STeresa Johnson context->thread->Destroy();
933d4bba30STeresa Johnson }
943d4bba30STeresa Johnson
Destroy()953d4bba30STeresa Johnson void MemprofThread::Destroy() {
963d4bba30STeresa Johnson int tid = this->tid();
973d4bba30STeresa Johnson VReport(1, "T%d exited\n", tid);
983d4bba30STeresa Johnson
993d4bba30STeresa Johnson malloc_storage().CommitBack();
1003d4bba30STeresa Johnson memprofThreadRegistry().FinishThread(tid);
1013d4bba30STeresa Johnson FlushToDeadThreadStats(&stats_);
1023d4bba30STeresa Johnson uptr size = RoundUpTo(sizeof(MemprofThread), GetPageSizeCached());
1033d4bba30STeresa Johnson UnmapOrDie(this, size);
1043d4bba30STeresa Johnson DTLS_Destroy();
1053d4bba30STeresa Johnson }
1063d4bba30STeresa Johnson
GetStackBounds() const1073d4bba30STeresa Johnson inline MemprofThread::StackBounds MemprofThread::GetStackBounds() const {
1083d4bba30STeresa Johnson if (stack_bottom_ >= stack_top_)
1093d4bba30STeresa Johnson return {0, 0};
1103d4bba30STeresa Johnson return {stack_bottom_, stack_top_};
1113d4bba30STeresa Johnson }
1123d4bba30STeresa Johnson
stack_top()1133d4bba30STeresa Johnson uptr MemprofThread::stack_top() { return GetStackBounds().top; }
1143d4bba30STeresa Johnson
stack_bottom()1153d4bba30STeresa Johnson uptr MemprofThread::stack_bottom() { return GetStackBounds().bottom; }
1163d4bba30STeresa Johnson
stack_size()1173d4bba30STeresa Johnson uptr MemprofThread::stack_size() {
1183d4bba30STeresa Johnson const auto bounds = GetStackBounds();
1193d4bba30STeresa Johnson return bounds.top - bounds.bottom;
1203d4bba30STeresa Johnson }
1213d4bba30STeresa Johnson
Init(const InitOptions * options)1223d4bba30STeresa Johnson void MemprofThread::Init(const InitOptions *options) {
1233d4bba30STeresa Johnson CHECK_EQ(this->stack_size(), 0U);
1243d4bba30STeresa Johnson SetThreadStackAndTls(options);
1253d4bba30STeresa Johnson if (stack_top_ != stack_bottom_) {
1263d4bba30STeresa Johnson CHECK_GT(this->stack_size(), 0U);
1273d4bba30STeresa Johnson CHECK(AddrIsInMem(stack_bottom_));
1283d4bba30STeresa Johnson CHECK(AddrIsInMem(stack_top_ - 1));
1293d4bba30STeresa Johnson }
1303d4bba30STeresa Johnson int local = 0;
1313d4bba30STeresa Johnson VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
1323d4bba30STeresa Johnson (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
13313a442caSMartin Liska (void *)&local);
1343d4bba30STeresa Johnson }
1353d4bba30STeresa Johnson
1363d4bba30STeresa Johnson thread_return_t
ThreadStart(tid_t os_id,atomic_uintptr_t * signal_thread_is_registered)1373d4bba30STeresa Johnson MemprofThread::ThreadStart(tid_t os_id,
1383d4bba30STeresa Johnson atomic_uintptr_t *signal_thread_is_registered) {
1393d4bba30STeresa Johnson Init();
1403d4bba30STeresa Johnson memprofThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular,
1413d4bba30STeresa Johnson nullptr);
1423d4bba30STeresa Johnson if (signal_thread_is_registered)
1433d4bba30STeresa Johnson atomic_store(signal_thread_is_registered, 1, memory_order_release);
1443d4bba30STeresa Johnson
1453d4bba30STeresa Johnson if (!start_routine_) {
1463d4bba30STeresa Johnson // start_routine_ == 0 if we're on the main thread or on one of the
1473d4bba30STeresa Johnson // OS X libdispatch worker threads. But nobody is supposed to call
1483d4bba30STeresa Johnson // ThreadStart() for the worker threads.
1493d4bba30STeresa Johnson CHECK_EQ(tid(), 0);
1503d4bba30STeresa Johnson return 0;
1513d4bba30STeresa Johnson }
1523d4bba30STeresa Johnson
1533d4bba30STeresa Johnson return start_routine_(arg_);
1543d4bba30STeresa Johnson }
1553d4bba30STeresa Johnson
CreateMainThread()1563d4bba30STeresa Johnson MemprofThread *CreateMainThread() {
1573d4bba30STeresa Johnson MemprofThread *main_thread = MemprofThread::Create(
15892a3a2dcSDmitry Vyukov /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ kMainTid,
1593d4bba30STeresa Johnson /* stack */ nullptr, /* detached */ true);
1603d4bba30STeresa Johnson SetCurrentThread(main_thread);
1613d4bba30STeresa Johnson main_thread->ThreadStart(internal_getpid(),
1623d4bba30STeresa Johnson /* signal_thread_is_registered */ nullptr);
1633d4bba30STeresa Johnson return main_thread;
1643d4bba30STeresa Johnson }
1653d4bba30STeresa Johnson
1663d4bba30STeresa Johnson // This implementation doesn't use the argument, which is just passed down
1673d4bba30STeresa Johnson // from the caller of Init (which see, above). It's only there to support
1683d4bba30STeresa Johnson // OS-specific implementations that need more information passed through.
SetThreadStackAndTls(const InitOptions * options)1693d4bba30STeresa Johnson void MemprofThread::SetThreadStackAndTls(const InitOptions *options) {
1703d4bba30STeresa Johnson DCHECK_EQ(options, nullptr);
1713d4bba30STeresa Johnson uptr tls_size = 0;
1723d4bba30STeresa Johnson uptr stack_size = 0;
17392a3a2dcSDmitry Vyukov GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_size,
17492a3a2dcSDmitry Vyukov &tls_begin_, &tls_size);
1753d4bba30STeresa Johnson stack_top_ = stack_bottom_ + stack_size;
1763d4bba30STeresa Johnson tls_end_ = tls_begin_ + tls_size;
1773d4bba30STeresa Johnson dtls_ = DTLS_Get();
1783d4bba30STeresa Johnson
1793d4bba30STeresa Johnson if (stack_top_ != stack_bottom_) {
1803d4bba30STeresa Johnson int local;
1813d4bba30STeresa Johnson CHECK(AddrIsInStack((uptr)&local));
1823d4bba30STeresa Johnson }
1833d4bba30STeresa Johnson }
1843d4bba30STeresa Johnson
AddrIsInStack(uptr addr)1853d4bba30STeresa Johnson bool MemprofThread::AddrIsInStack(uptr addr) {
1863d4bba30STeresa Johnson const auto bounds = GetStackBounds();
1873d4bba30STeresa Johnson return addr >= bounds.bottom && addr < bounds.top;
1883d4bba30STeresa Johnson }
1893d4bba30STeresa Johnson
GetCurrentThread()1903d4bba30STeresa Johnson MemprofThread *GetCurrentThread() {
1913d4bba30STeresa Johnson MemprofThreadContext *context =
1923d4bba30STeresa Johnson reinterpret_cast<MemprofThreadContext *>(TSDGet());
1933d4bba30STeresa Johnson if (!context)
1943d4bba30STeresa Johnson return nullptr;
1953d4bba30STeresa Johnson return context->thread;
1963d4bba30STeresa Johnson }
1973d4bba30STeresa Johnson
SetCurrentThread(MemprofThread * t)1983d4bba30STeresa Johnson void SetCurrentThread(MemprofThread *t) {
1993d4bba30STeresa Johnson CHECK(t->context());
20013a442caSMartin Liska VReport(2, "SetCurrentThread: %p for thread %p\n", (void *)t->context(),
2013d4bba30STeresa Johnson (void *)GetThreadSelf());
2023d4bba30STeresa Johnson // Make sure we do not reset the current MemprofThread.
2033d4bba30STeresa Johnson CHECK_EQ(0, TSDGet());
2043d4bba30STeresa Johnson TSDSet(t->context());
2053d4bba30STeresa Johnson CHECK_EQ(t->context(), TSDGet());
2063d4bba30STeresa Johnson }
2073d4bba30STeresa Johnson
GetCurrentTidOrInvalid()2083d4bba30STeresa Johnson u32 GetCurrentTidOrInvalid() {
2093d4bba30STeresa Johnson MemprofThread *t = GetCurrentThread();
2103d4bba30STeresa Johnson return t ? t->tid() : kInvalidTid;
2113d4bba30STeresa Johnson }
2123d4bba30STeresa Johnson
EnsureMainThreadIDIsCorrect()2133d4bba30STeresa Johnson void EnsureMainThreadIDIsCorrect() {
2143d4bba30STeresa Johnson MemprofThreadContext *context =
2153d4bba30STeresa Johnson reinterpret_cast<MemprofThreadContext *>(TSDGet());
21692a3a2dcSDmitry Vyukov if (context && (context->tid == kMainTid))
2173d4bba30STeresa Johnson context->os_id = GetTid();
2183d4bba30STeresa Johnson }
2193d4bba30STeresa Johnson } // namespace __memprof
220