1 //===-- sanitizer_stoptheworld_mac.cpp ------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // See sanitizer_stoptheworld.h for details.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_platform.h"
15 #if SANITIZER_APPLE && (defined(__x86_64__) || defined(__aarch64__) || \
18 #include <mach/mach.h>
19 #include <mach/thread_info.h>
22 #include "sanitizer_stoptheworld.h"
24 namespace __sanitizer
{
28 } SuspendedThreadInfo
;
30 class SuspendedThreadsListMac final
: public SuspendedThreadsList
{
32 SuspendedThreadsListMac() = default;
34 tid_t
GetThreadID(uptr index
) const override
;
35 thread_t
GetThread(uptr index
) const;
36 uptr
ThreadCount() const override
;
37 bool ContainsThread(thread_t thread
) const;
38 void Append(thread_t thread
);
40 PtraceRegistersStatus
GetRegistersAndSP(uptr index
,
41 InternalMmapVector
<uptr
> *buffer
,
42 uptr
*sp
) const override
;
45 InternalMmapVector
<SuspendedThreadInfo
> threads_
;
48 struct RunThreadArgs
{
49 StopTheWorldCallback callback
;
53 void *RunThread(void *arg
) {
54 struct RunThreadArgs
*run_args
= (struct RunThreadArgs
*)arg
;
55 SuspendedThreadsListMac suspended_threads_list
;
57 thread_array_t threads
;
58 mach_msg_type_number_t num_threads
;
59 kern_return_t err
= task_threads(mach_task_self(), &threads
, &num_threads
);
60 if (err
!= KERN_SUCCESS
) {
61 VReport(1, "Failed to get threads for task (errno %d).\n", err
);
65 thread_t thread_self
= mach_thread_self();
66 for (unsigned int i
= 0; i
< num_threads
; ++i
) {
67 if (threads
[i
] == thread_self
) continue;
69 thread_suspend(threads
[i
]);
70 suspended_threads_list
.Append(threads
[i
]);
73 run_args
->callback(suspended_threads_list
, run_args
->argument
);
75 uptr num_suspended
= suspended_threads_list
.ThreadCount();
76 for (unsigned int i
= 0; i
< num_suspended
; ++i
) {
77 thread_resume(suspended_threads_list
.GetThread(i
));
82 void StopTheWorld(StopTheWorldCallback callback
, void *argument
) {
83 struct RunThreadArgs arg
= {callback
, argument
};
84 pthread_t run_thread
= (pthread_t
)internal_start_thread(RunThread
, &arg
);
85 internal_join_thread(run_thread
);
88 #if defined(__x86_64__)
89 typedef x86_thread_state64_t regs_struct
;
90 #define regs_flavor x86_THREAD_STATE64
94 #elif defined(__aarch64__)
95 typedef arm_thread_state64_t regs_struct
;
96 #define regs_flavor ARM_THREAD_STATE64
104 #elif defined(__i386)
105 typedef x86_thread_state32_t regs_struct
;
106 #define regs_flavor x86_THREAD_STATE32
111 #error "Unsupported architecture"
114 tid_t
SuspendedThreadsListMac::GetThreadID(uptr index
) const {
115 CHECK_LT(index
, threads_
.size());
116 return threads_
[index
].tid
;
119 thread_t
SuspendedThreadsListMac::GetThread(uptr index
) const {
120 CHECK_LT(index
, threads_
.size());
121 return threads_
[index
].thread
;
124 uptr
SuspendedThreadsListMac::ThreadCount() const {
125 return threads_
.size();
128 bool SuspendedThreadsListMac::ContainsThread(thread_t thread
) const {
129 for (uptr i
= 0; i
< threads_
.size(); i
++) {
130 if (threads_
[i
].thread
== thread
) return true;
135 void SuspendedThreadsListMac::Append(thread_t thread
) {
136 thread_identifier_info_data_t info
;
137 mach_msg_type_number_t info_count
= THREAD_IDENTIFIER_INFO_COUNT
;
138 kern_return_t err
= thread_info(thread
, THREAD_IDENTIFIER_INFO
,
139 (thread_info_t
)&info
, &info_count
);
140 if (err
!= KERN_SUCCESS
) {
141 VReport(1, "Error - unable to get thread ident for a thread\n");
144 threads_
.push_back({info
.thread_id
, thread
});
147 PtraceRegistersStatus
SuspendedThreadsListMac::GetRegistersAndSP(
148 uptr index
, InternalMmapVector
<uptr
> *buffer
, uptr
*sp
) const {
149 thread_t thread
= GetThread(index
);
152 mach_msg_type_number_t reg_count
= sizeof(regs
) / sizeof(natural_t
);
153 err
= thread_get_state(thread
, regs_flavor
, (thread_state_t
)®s
,
155 if (err
!= KERN_SUCCESS
) {
156 VReport(1, "Error - unable to get registers for a thread\n");
157 // MIG_ARRAY_TOO_LARGE, means that the state is too large, but it's
158 // still safe to proceed.
159 return err
== MIG_ARRAY_TOO_LARGE
? REGISTERS_UNAVAILABLE
160 : REGISTERS_UNAVAILABLE_FATAL
;
163 buffer
->resize(RoundUpTo(sizeof(regs
), sizeof(uptr
)) / sizeof(uptr
));
164 internal_memcpy(buffer
->data(), ®s
, sizeof(regs
));
165 #if defined(__aarch64__) && defined(arm_thread_state64_get_sp)
166 *sp
= arm_thread_state64_get_sp(regs
);
171 // On x86_64 and aarch64, we must account for the stack redzone, which is 128
173 if (SANITIZER_WORDSIZE
== 64) *sp
-= 128;
175 return REGISTERS_AVAILABLE
;
178 } // namespace __sanitizer
180 #endif // SANITIZER_APPLE && (defined(__x86_64__) || defined(__aarch64__)) ||