1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/profiler/suspendable_thread_delegate_mac.h"
11
12 #include <mach/mach.h>
13 #include <mach/thread_act.h>
14 #include <pthread.h>
15
16 #include <vector>
17
18 #include "base/apple/mach_logging.h"
19 #include "base/check.h"
20 #include "base/profiler/profile_builder.h"
21 #include "build/build_config.h"
22
23 // IMPORTANT NOTE: Some functions within this implementation are invoked while
24 // the target thread is suspended so it must not do any allocation from the
25 // heap, including indirectly via use of DCHECK/CHECK or other logging
26 // statements. Otherwise this code can deadlock on heap locks acquired by the
27 // target thread before it was suspended. These functions are commented with "NO
28 // HEAP ALLOCATIONS".
29
30 namespace base {
31
32 namespace {
33
34 #if defined(ARCH_CPU_X86_64)
35 constexpr mach_msg_type_number_t kThreadStateCount = x86_THREAD_STATE64_COUNT;
36 constexpr thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64;
37 #elif defined(ARCH_CPU_ARM64)
38 constexpr mach_msg_type_number_t kThreadStateCount = ARM_THREAD_STATE64_COUNT;
39 constexpr thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
40 #endif
41
42 // Fills |state| with |target_thread|'s context. NO HEAP ALLOCATIONS.
GetThreadContextImpl(thread_act_t target_thread,RegisterContext * state)43 bool GetThreadContextImpl(thread_act_t target_thread, RegisterContext* state) {
44 auto count = kThreadStateCount;
45 return thread_get_state(target_thread, kThreadStateFlavor,
46 reinterpret_cast<thread_state_t>(state),
47 &count) == KERN_SUCCESS;
48 }
49
50 } // namespace
51
52 // ScopedSuspendThread --------------------------------------------------------
53
54 // NO HEAP ALLOCATIONS after thread_suspend.
ScopedSuspendThread(mach_port_t thread_port)55 SuspendableThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread(
56 mach_port_t thread_port)
57 : thread_port_(thread_suspend(thread_port) == KERN_SUCCESS
58 ? thread_port
59 : MACH_PORT_NULL) {}
60
61 // NO HEAP ALLOCATIONS. The MACH_CHECK is OK because it provides a more noisy
62 // failure mode than deadlocking.
~ScopedSuspendThread()63 SuspendableThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() {
64 if (!WasSuccessful())
65 return;
66
67 kern_return_t kr = thread_resume(thread_port_);
68 MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_resume";
69 }
70
WasSuccessful() const71 bool SuspendableThreadDelegateMac::ScopedSuspendThread::WasSuccessful() const {
72 return thread_port_ != MACH_PORT_NULL;
73 }
74
75 // SuspendableThreadDelegateMac -----------------------------------------------
76
SuspendableThreadDelegateMac(SamplingProfilerThreadToken thread_token)77 SuspendableThreadDelegateMac::SuspendableThreadDelegateMac(
78 SamplingProfilerThreadToken thread_token)
79 : thread_port_(thread_token.id),
80 thread_stack_base_address_(
81 reinterpret_cast<uintptr_t>(pthread_get_stackaddr_np(
82 pthread_from_mach_thread_np(thread_token.id)))) {
83 // This class suspends threads, and those threads might be suspended in dyld.
84 // Therefore, for all the system functions that might be linked in dynamically
85 // that are used while threads are suspended, make calls to them to make sure
86 // that they are linked up.
87 RegisterContext thread_context;
88 GetThreadContextImpl(thread_port_, &thread_context);
89 }
90
91 SuspendableThreadDelegateMac::~SuspendableThreadDelegateMac() = default;
92
93 std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
CreateScopedSuspendThread()94 SuspendableThreadDelegateMac::CreateScopedSuspendThread() {
95 return std::make_unique<ScopedSuspendThread>(thread_port_);
96 }
97
GetThreadId() const98 PlatformThreadId SuspendableThreadDelegateMac::GetThreadId() const {
99 return thread_port_;
100 }
101
102 // NO HEAP ALLOCATIONS.
GetThreadContext(RegisterContext * thread_context)103 bool SuspendableThreadDelegateMac::GetThreadContext(
104 RegisterContext* thread_context) {
105 return GetThreadContextImpl(thread_port_, thread_context);
106 }
107
108 // NO HEAP ALLOCATIONS.
GetStackBaseAddress() const109 uintptr_t SuspendableThreadDelegateMac::GetStackBaseAddress() const {
110 return thread_stack_base_address_;
111 }
112
113 // NO HEAP ALLOCATIONS.
CanCopyStack(uintptr_t stack_pointer)114 bool SuspendableThreadDelegateMac::CanCopyStack(uintptr_t stack_pointer) {
115 return true;
116 }
117
GetRegistersToRewrite(RegisterContext * thread_context)118 std::vector<uintptr_t*> SuspendableThreadDelegateMac::GetRegistersToRewrite(
119 RegisterContext* thread_context) {
120 #if defined(ARCH_CPU_X86_64)
121 return {
122 &AsUintPtr(&thread_context->__rbx), &AsUintPtr(&thread_context->__rbp),
123 &AsUintPtr(&thread_context->__rsp), &AsUintPtr(&thread_context->__r12),
124 &AsUintPtr(&thread_context->__r13), &AsUintPtr(&thread_context->__r14),
125 &AsUintPtr(&thread_context->__r15)};
126 #elif defined(ARCH_CPU_ARM64) // defined(ARCH_CPU_X86_64)
127 return {
128 &AsUintPtr(&thread_context->__fp),
129 &AsUintPtr(&thread_context->__sp),
130 &AsUintPtr(&thread_context->__x[19]),
131 &AsUintPtr(&thread_context->__x[20]),
132 &AsUintPtr(&thread_context->__x[21]),
133 &AsUintPtr(&thread_context->__x[22]),
134 &AsUintPtr(&thread_context->__x[23]),
135 &AsUintPtr(&thread_context->__x[24]),
136 &AsUintPtr(&thread_context->__x[25]),
137 &AsUintPtr(&thread_context->__x[26]),
138 &AsUintPtr(&thread_context->__x[27]),
139 &AsUintPtr(&thread_context->__x[28]),
140 };
141 #endif // defined(ARCH_CPU_ARM64)
142 }
143
144 } // namespace base
145