// 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/thread_delegate_mac.h" #include #include #include #include "base/logging.h" #include "base/mac/mach_logging.h" #include "base/profiler/profile_builder.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 { // Fills |state| with |target_thread|'s context. NO HEAP ALLOCATIONS. bool GetThreadState(thread_act_t target_thread, x86_thread_state64_t* state) { auto count = static_cast(x86_THREAD_STATE64_COUNT); return thread_get_state(target_thread, x86_THREAD_STATE64, reinterpret_cast(state), &count) == KERN_SUCCESS; } } // namespace // ScopedSuspendThread -------------------------------------------------------- // NO HEAP ALLOCATIONS after thread_suspend. ThreadDelegateMac::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. ThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() { if (!WasSuccessful()) return; kern_return_t kr = thread_resume(thread_port_); MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_resume"; } bool ThreadDelegateMac::ScopedSuspendThread::WasSuccessful() const { return thread_port_ != MACH_PORT_NULL; } // ThreadDelegateMac ---------------------------------------------------------- ThreadDelegateMac::ThreadDelegateMac(mach_port_t thread_port) : thread_port_(thread_port), thread_stack_base_address_(reinterpret_cast( pthread_get_stackaddr_np(pthread_from_mach_thread_np(thread_port)))) { // 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. x86_thread_state64_t thread_state; GetThreadState(thread_port_, &thread_state); } ThreadDelegateMac::~ThreadDelegateMac() = default; std::unique_ptr ThreadDelegateMac::CreateScopedSuspendThread() { return std::make_unique(thread_port_); } // NO HEAP ALLOCATIONS. bool ThreadDelegateMac::GetThreadContext(x86_thread_state64_t* thread_context) { return GetThreadState(thread_port_, thread_context); } // NO HEAP ALLOCATIONS. uintptr_t ThreadDelegateMac::GetStackBaseAddress() const { return thread_stack_base_address_; } // NO HEAP ALLOCATIONS. bool ThreadDelegateMac::CanCopyStack(uintptr_t stack_pointer) { return true; } std::vector ThreadDelegateMac::GetRegistersToRewrite( x86_thread_state64_t* thread_context) { 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)}; } } // namespace base