// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/profiler/suspendable_thread_delegate_mac.h" #include #include #include #include #include "base/check.h" #include "base/mac/mach_logging.h" #include "base/profiler/profile_builder.h" #include "build/build_config.h" // IMPORTANT NOTE: Some functions within this implementation are invoked while // the target thread is suspended so it must not do any allocation from the // heap, including indirectly via use of DCHECK/CHECK or other logging // statements. Otherwise this code can deadlock on heap locks acquired by the // target thread before it was suspended. These functions are commented with "NO // HEAP ALLOCATIONS". namespace base { namespace { #if defined(ARCH_CPU_X86_64) constexpr mach_msg_type_number_t kThreadStateCount = x86_THREAD_STATE64_COUNT; constexpr thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64; #elif defined(ARCH_CPU_ARM64) constexpr mach_msg_type_number_t kThreadStateCount = ARM_THREAD_STATE64_COUNT; constexpr thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64; #endif // Fills |state| with |target_thread|'s context. NO HEAP ALLOCATIONS. bool GetThreadContextImpl(thread_act_t target_thread, RegisterContext* state) { auto count = kThreadStateCount; return thread_get_state(target_thread, kThreadStateFlavor, reinterpret_cast(state), &count) == KERN_SUCCESS; } } // namespace // ScopedSuspendThread -------------------------------------------------------- // NO HEAP ALLOCATIONS after thread_suspend. SuspendableThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread( mach_port_t thread_port) : thread_port_(thread_suspend(thread_port) == KERN_SUCCESS ? thread_port : MACH_PORT_NULL) {} // NO HEAP ALLOCATIONS. The MACH_CHECK is OK because it provides a more noisy // failure mode than deadlocking. SuspendableThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() { if (!WasSuccessful()) return; kern_return_t kr = thread_resume(thread_port_); MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_resume"; } bool SuspendableThreadDelegateMac::ScopedSuspendThread::WasSuccessful() const { return thread_port_ != MACH_PORT_NULL; } // SuspendableThreadDelegateMac ----------------------------------------------- SuspendableThreadDelegateMac::SuspendableThreadDelegateMac( SamplingProfilerThreadToken thread_token) : thread_port_(thread_token.id), thread_stack_base_address_( reinterpret_cast(pthread_get_stackaddr_np( pthread_from_mach_thread_np(thread_token.id)))) { // This class suspends threads, and those threads might be suspended in dyld. // Therefore, for all the system functions that might be linked in dynamically // that are used while threads are suspended, make calls to them to make sure // that they are linked up. RegisterContext thread_context; GetThreadContextImpl(thread_port_, &thread_context); } SuspendableThreadDelegateMac::~SuspendableThreadDelegateMac() = default; std::unique_ptr SuspendableThreadDelegateMac::CreateScopedSuspendThread() { return std::make_unique(thread_port_); } PlatformThreadId SuspendableThreadDelegateMac::GetThreadId() const { return thread_port_; } // NO HEAP ALLOCATIONS. bool SuspendableThreadDelegateMac::GetThreadContext( RegisterContext* thread_context) { return GetThreadContextImpl(thread_port_, thread_context); } // NO HEAP ALLOCATIONS. uintptr_t SuspendableThreadDelegateMac::GetStackBaseAddress() const { return thread_stack_base_address_; } // NO HEAP ALLOCATIONS. bool SuspendableThreadDelegateMac::CanCopyStack(uintptr_t stack_pointer) { return true; } std::vector SuspendableThreadDelegateMac::GetRegistersToRewrite( RegisterContext* thread_context) { #if defined(ARCH_CPU_X86_64) return { &AsUintPtr(&thread_context->__rbx), &AsUintPtr(&thread_context->__rbp), &AsUintPtr(&thread_context->__rsp), &AsUintPtr(&thread_context->__r12), &AsUintPtr(&thread_context->__r13), &AsUintPtr(&thread_context->__r14), &AsUintPtr(&thread_context->__r15)}; #elif defined(ARCH_CPU_ARM64) // defined(ARCH_CPU_X86_64) return { &AsUintPtr(&thread_context->__fp), &AsUintPtr(&thread_context->__sp), &AsUintPtr(&thread_context->__x[19]), &AsUintPtr(&thread_context->__x[20]), &AsUintPtr(&thread_context->__x[21]), &AsUintPtr(&thread_context->__x[22]), &AsUintPtr(&thread_context->__x[23]), &AsUintPtr(&thread_context->__x[24]), &AsUintPtr(&thread_context->__x[25]), &AsUintPtr(&thread_context->__x[26]), &AsUintPtr(&thread_context->__x[27]), &AsUintPtr(&thread_context->__x[28]), }; #endif // defined(ARCH_CPU_ARM64) } } // namespace base