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_MAC && (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
: public SuspendedThreadsList
{
32 SuspendedThreadsListMac() : threads_(1024) {}
34 tid_t
GetThreadID(uptr index
) const;
35 thread_t
GetThread(uptr index
) const;
36 uptr
ThreadCount() const;
37 bool ContainsThread(thread_t thread
) const;
38 void Append(thread_t thread
);
40 PtraceRegistersStatus
GetRegistersAndSP(uptr index
, uptr
*buffer
,
42 uptr
RegisterCount() const;
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
));
81 void StopTheWorld(StopTheWorldCallback callback
, void *argument
) {
82 struct RunThreadArgs arg
= {callback
, argument
};
83 pthread_t run_thread
= (pthread_t
)internal_start_thread(RunThread
, &arg
);
84 internal_join_thread(run_thread
);
87 #if defined(__x86_64__)
88 typedef x86_thread_state64_t regs_struct
;
92 #elif defined(__aarch64__)
93 typedef arm_thread_state64_t regs_struct
;
101 #elif defined(__i386)
102 typedef x86_thread_state32_t regs_struct
;
107 #error "Unsupported architecture"
110 tid_t
SuspendedThreadsListMac::GetThreadID(uptr index
) const {
111 CHECK_LT(index
, threads_
.size());
112 return threads_
[index
].tid
;
115 thread_t
SuspendedThreadsListMac::GetThread(uptr index
) const {
116 CHECK_LT(index
, threads_
.size());
117 return threads_
[index
].thread
;
120 uptr
SuspendedThreadsListMac::ThreadCount() const {
121 return threads_
.size();
124 bool SuspendedThreadsListMac::ContainsThread(thread_t thread
) const {
125 for (uptr i
= 0; i
< threads_
.size(); i
++) {
126 if (threads_
[i
].thread
== thread
) return true;
131 void SuspendedThreadsListMac::Append(thread_t thread
) {
132 thread_identifier_info_data_t info
;
133 mach_msg_type_number_t info_count
= THREAD_IDENTIFIER_INFO_COUNT
;
134 kern_return_t err
= thread_info(thread
, THREAD_IDENTIFIER_INFO
,
135 (thread_info_t
)&info
, &info_count
);
136 if (err
!= KERN_SUCCESS
) {
137 VReport(1, "Error - unable to get thread ident for a thread\n");
140 threads_
.push_back({info
.thread_id
, thread
});
143 PtraceRegistersStatus
SuspendedThreadsListMac::GetRegistersAndSP(
144 uptr index
, uptr
*buffer
, uptr
*sp
) const {
145 thread_t thread
= GetThread(index
);
148 mach_msg_type_number_t reg_count
= MACHINE_THREAD_STATE_COUNT
;
149 err
= thread_get_state(thread
, MACHINE_THREAD_STATE
, (thread_state_t
)®s
,
151 if (err
!= KERN_SUCCESS
) {
152 VReport(1, "Error - unable to get registers for a thread\n");
153 // KERN_INVALID_ARGUMENT indicates that either the flavor is invalid,
154 // or the thread does not exist. The other possible error case,
155 // MIG_ARRAY_TOO_LARGE, means that the state is too large, but it's
156 // still safe to proceed.
157 return err
== KERN_INVALID_ARGUMENT
? REGISTERS_UNAVAILABLE_FATAL
158 : REGISTERS_UNAVAILABLE
;
161 internal_memcpy(buffer
, ®s
, sizeof(regs
));
164 // On x86_64 and aarch64, we must account for the stack redzone, which is 128
166 if (SANITIZER_WORDSIZE
== 64) *sp
-= 128;
168 return REGISTERS_AVAILABLE
;
171 uptr
SuspendedThreadsListMac::RegisterCount() const {
172 return MACHINE_THREAD_STATE_COUNT
;
174 } // namespace __sanitizer
176 #endif // SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) ||