1 //===-- MachThreadList.cpp --------------------------------------*- C++ -*-===//
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 // Created by Greg Clayton on 6/19/07.
11 //===----------------------------------------------------------------------===//
13 #include "MachThreadList.h"
17 #include "DNBThreadResumeActions.h"
18 #include "MachProcess.h"
21 #include <sys/sysctl.h>
25 MachThreadList::MachThreadList()
26 : m_threads(), m_threads_mutex(PTHREAD_MUTEX_RECURSIVE
),
29 MachThreadList::~MachThreadList() {}
31 nub_state_t
MachThreadList::GetState(nub_thread_t tid
) {
32 MachThreadSP
thread_sp(GetThreadByID(tid
));
34 return thread_sp
->GetState();
38 const char *MachThreadList::GetName(nub_thread_t tid
) {
39 MachThreadSP
thread_sp(GetThreadByID(tid
));
41 return thread_sp
->GetName();
45 ThreadInfo::QoS
MachThreadList::GetRequestedQoS(nub_thread_t tid
,
47 uint64_t dti_qos_class_index
) {
48 MachThreadSP
thread_sp(GetThreadByID(tid
));
50 return thread_sp
->GetRequestedQoS(tsd
, dti_qos_class_index
);
51 return ThreadInfo::QoS();
54 nub_addr_t
MachThreadList::GetPThreadT(nub_thread_t tid
) {
55 MachThreadSP
thread_sp(GetThreadByID(tid
));
57 return thread_sp
->GetPThreadT();
58 return INVALID_NUB_ADDRESS
;
61 nub_addr_t
MachThreadList::GetDispatchQueueT(nub_thread_t tid
) {
62 MachThreadSP
thread_sp(GetThreadByID(tid
));
64 return thread_sp
->GetDispatchQueueT();
65 return INVALID_NUB_ADDRESS
;
68 nub_addr_t
MachThreadList::GetTSDAddressForThread(
69 nub_thread_t tid
, uint64_t plo_pthread_tsd_base_address_offset
,
70 uint64_t plo_pthread_tsd_base_offset
, uint64_t plo_pthread_tsd_entry_size
) {
71 MachThreadSP
thread_sp(GetThreadByID(tid
));
73 return thread_sp
->GetTSDAddressForThread(
74 plo_pthread_tsd_base_address_offset
, plo_pthread_tsd_base_offset
,
75 plo_pthread_tsd_entry_size
);
76 return INVALID_NUB_ADDRESS
;
79 nub_thread_t
MachThreadList::SetCurrentThread(nub_thread_t tid
) {
80 MachThreadSP
thread_sp(GetThreadByID(tid
));
82 m_current_thread
= thread_sp
;
85 return INVALID_NUB_THREAD
;
88 bool MachThreadList::GetThreadStoppedReason(
89 nub_thread_t tid
, struct DNBThreadStopInfo
*stop_info
) const {
90 MachThreadSP
thread_sp(GetThreadByID(tid
));
92 return thread_sp
->GetStopException().GetStopInfo(stop_info
);
96 bool MachThreadList::GetIdentifierInfo(
97 nub_thread_t tid
, thread_identifier_info_data_t
*ident_info
) {
98 thread_t mach_port_number
= GetMachPortNumberByThreadID(tid
);
100 mach_msg_type_number_t count
= THREAD_IDENTIFIER_INFO_COUNT
;
101 return ::thread_info(mach_port_number
, THREAD_IDENTIFIER_INFO
,
102 (thread_info_t
)ident_info
, &count
) == KERN_SUCCESS
;
105 void MachThreadList::DumpThreadStoppedReason(nub_thread_t tid
) const {
106 MachThreadSP
thread_sp(GetThreadByID(tid
));
108 thread_sp
->GetStopException().DumpStopReason();
111 const char *MachThreadList::GetThreadInfo(nub_thread_t tid
) const {
112 MachThreadSP
thread_sp(GetThreadByID(tid
));
114 return thread_sp
->GetBasicInfoAsString();
118 MachThreadSP
MachThreadList::GetThreadByID(nub_thread_t tid
) const {
119 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
120 MachThreadSP thread_sp
;
121 const size_t num_threads
= m_threads
.size();
122 for (size_t idx
= 0; idx
< num_threads
; ++idx
) {
123 if (m_threads
[idx
]->ThreadID() == tid
) {
124 thread_sp
= m_threads
[idx
];
132 MachThreadList::GetThreadByMachPortNumber(thread_t mach_port_number
) const {
133 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
134 MachThreadSP thread_sp
;
135 const size_t num_threads
= m_threads
.size();
136 for (size_t idx
= 0; idx
< num_threads
; ++idx
) {
137 if (m_threads
[idx
]->MachPortNumber() == mach_port_number
) {
138 thread_sp
= m_threads
[idx
];
146 MachThreadList::GetThreadIDByMachPortNumber(thread_t mach_port_number
) const {
147 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
148 MachThreadSP thread_sp
;
149 const size_t num_threads
= m_threads
.size();
150 for (size_t idx
= 0; idx
< num_threads
; ++idx
) {
151 if (m_threads
[idx
]->MachPortNumber() == mach_port_number
) {
152 return m_threads
[idx
]->ThreadID();
155 return INVALID_NUB_THREAD
;
158 thread_t
MachThreadList::GetMachPortNumberByThreadID(
159 nub_thread_t globally_unique_id
) const {
160 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
161 MachThreadSP thread_sp
;
162 const size_t num_threads
= m_threads
.size();
163 for (size_t idx
= 0; idx
< num_threads
; ++idx
) {
164 if (m_threads
[idx
]->ThreadID() == globally_unique_id
) {
165 return m_threads
[idx
]->MachPortNumber();
171 bool MachThreadList::GetRegisterValue(nub_thread_t tid
, uint32_t set
,
173 DNBRegisterValue
*reg_value
) const {
174 MachThreadSP
thread_sp(GetThreadByID(tid
));
176 return thread_sp
->GetRegisterValue(set
, reg
, reg_value
);
181 bool MachThreadList::SetRegisterValue(nub_thread_t tid
, uint32_t set
,
183 const DNBRegisterValue
*reg_value
) const {
184 MachThreadSP
thread_sp(GetThreadByID(tid
));
186 return thread_sp
->SetRegisterValue(set
, reg
, reg_value
);
191 nub_size_t
MachThreadList::GetRegisterContext(nub_thread_t tid
, void *buf
,
193 MachThreadSP
thread_sp(GetThreadByID(tid
));
195 return thread_sp
->GetRegisterContext(buf
, buf_len
);
199 nub_size_t
MachThreadList::SetRegisterContext(nub_thread_t tid
, const void *buf
,
201 MachThreadSP
thread_sp(GetThreadByID(tid
));
203 return thread_sp
->SetRegisterContext(buf
, buf_len
);
207 uint32_t MachThreadList::SaveRegisterState(nub_thread_t tid
) {
208 MachThreadSP
thread_sp(GetThreadByID(tid
));
210 return thread_sp
->SaveRegisterState();
214 bool MachThreadList::RestoreRegisterState(nub_thread_t tid
, uint32_t save_id
) {
215 MachThreadSP
thread_sp(GetThreadByID(tid
));
217 return thread_sp
->RestoreRegisterState(save_id
);
221 nub_size_t
MachThreadList::NumThreads() const {
222 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
223 return m_threads
.size();
226 nub_thread_t
MachThreadList::ThreadIDAtIndex(nub_size_t idx
) const {
227 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
228 if (idx
< m_threads
.size())
229 return m_threads
[idx
]->ThreadID();
230 return INVALID_NUB_THREAD
;
233 nub_thread_t
MachThreadList::CurrentThreadID() {
234 MachThreadSP thread_sp
;
235 CurrentThread(thread_sp
);
237 return thread_sp
->ThreadID();
238 return INVALID_NUB_THREAD
;
241 bool MachThreadList::NotifyException(MachException::Data
&exc
) {
242 MachThreadSP
thread_sp(GetThreadByMachPortNumber(exc
.thread_port
));
244 thread_sp
->NotifyException(exc
);
250 void MachThreadList::Clear() {
251 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
256 MachThreadList::UpdateThreadList(MachProcess
*process
, bool update
,
257 MachThreadList::collection
*new_threads
) {
258 // locker will keep a mutex locked until it goes out of scope
259 DNBLogThreadedIf(LOG_THREAD
, "MachThreadList::UpdateThreadList (pid = %4.4x, "
260 "update = %u) process stop count = %u",
261 process
->ProcessID(), update
, process
->StopCount());
262 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
264 if (process
->StopCount() == 0) {
265 int mib
[4] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process
->ProcessID()};
266 struct kinfo_proc processInfo
;
267 size_t bufsize
= sizeof(processInfo
);
268 if (sysctl(mib
, (unsigned)(sizeof(mib
) / sizeof(int)), &processInfo
,
269 &bufsize
, NULL
, 0) == 0 &&
271 if (processInfo
.kp_proc
.p_flag
& P_LP64
)
274 #if defined(__i386__) || defined(__x86_64__)
276 DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64
);
278 DNBArchProtocol::SetArchitecture(CPU_TYPE_I386
);
279 #elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
281 DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64
);
283 if (process
->GetCPUType() == CPU_TYPE_ARM64_32
)
284 DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32
);
286 DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM
);
291 if (m_threads
.empty() || update
) {
292 thread_array_t thread_list
= NULL
;
293 mach_msg_type_number_t thread_list_count
= 0;
294 task_t task
= process
->Task().TaskPort();
295 DNBError
err(::task_threads(task
, &thread_list
, &thread_list_count
),
296 DNBError::MachKernel
);
298 if (DNBLogCheckLogBit(LOG_THREAD
) || err
.Fail())
299 err
.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, "
300 "thread_list_count => %u )",
301 task
, thread_list
, thread_list_count
);
303 if (err
.Status() == KERN_SUCCESS
&& thread_list_count
> 0) {
304 MachThreadList::collection currThreads
;
306 // Iterator through the current thread list and see which threads
307 // we already have in our list (keep them), which ones we don't
308 // (add them), and which ones are not around anymore (remove them).
309 for (idx
= 0; idx
< thread_list_count
; ++idx
) {
310 const thread_t mach_port_num
= thread_list
[idx
];
312 uint64_t unique_thread_id
=
313 MachThread::GetGloballyUniqueThreadIDForMachPortID(mach_port_num
);
314 MachThreadSP
thread_sp(GetThreadByID(unique_thread_id
));
316 // Keep the existing thread class
317 currThreads
.push_back(thread_sp
);
319 // We don't have this thread, lets add it.
320 thread_sp
= std::make_shared
<MachThread
>(
321 process
, m_is_64_bit
, unique_thread_id
, mach_port_num
);
323 // Add the new thread regardless of its is user ready state...
324 // Make sure the thread is ready to be displayed and shown to users
325 // before we add this thread to our list...
326 if (thread_sp
->IsUserReady()) {
328 new_threads
->push_back(thread_sp
);
330 currThreads
.push_back(thread_sp
);
335 m_threads
.swap(currThreads
);
336 m_current_thread
.reset();
338 // Free the vm memory given to us by ::task_threads()
339 vm_size_t thread_list_size
=
340 (vm_size_t
)(thread_list_count
* sizeof(thread_t
));
341 ::vm_deallocate(::mach_task_self(), (vm_address_t
)thread_list
,
345 return static_cast<uint32_t>(m_threads
.size());
348 void MachThreadList::CurrentThread(MachThreadSP
&thread_sp
) {
349 // locker will keep a mutex locked until it goes out of scope
350 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
351 if (m_current_thread
.get() == NULL
) {
352 // Figure out which thread is going to be our current thread.
353 // This is currently done by finding the first thread in the list
354 // that has a valid exception.
355 const size_t num_threads
= m_threads
.size();
356 for (uint32_t idx
= 0; idx
< num_threads
; ++idx
) {
357 if (m_threads
[idx
]->GetStopException().IsValid()) {
358 m_current_thread
= m_threads
[idx
];
363 thread_sp
= m_current_thread
;
366 void MachThreadList::Dump() const {
367 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
368 const size_t num_threads
= m_threads
.size();
369 for (uint32_t idx
= 0; idx
< num_threads
; ++idx
) {
370 m_threads
[idx
]->Dump(idx
);
374 void MachThreadList::ProcessWillResume(
375 MachProcess
*process
, const DNBThreadResumeActions
&thread_actions
) {
376 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
378 // Update our thread list, because sometimes libdispatch or the kernel
379 // will spawn threads while a task is suspended.
380 MachThreadList::collection new_threads
;
382 // First figure out if we were planning on running only one thread, and if so
383 // force that thread to resume.
385 nub_thread_t solo_thread
= INVALID_NUB_THREAD
;
386 if (thread_actions
.GetSize() > 0 &&
387 thread_actions
.NumActionsWithState(eStateStepping
) +
388 thread_actions
.NumActionsWithState(eStateRunning
) ==
390 run_one_thread
= true;
391 const DNBThreadResumeAction
*action_ptr
= thread_actions
.GetFirst();
392 size_t num_actions
= thread_actions
.GetSize();
393 for (size_t i
= 0; i
< num_actions
; i
++, action_ptr
++) {
394 if (action_ptr
->state
== eStateStepping
||
395 action_ptr
->state
== eStateRunning
) {
396 solo_thread
= action_ptr
->tid
;
401 run_one_thread
= false;
403 UpdateThreadList(process
, true, &new_threads
);
405 DNBThreadResumeAction resume_new_threads
= {-1U, eStateRunning
, 0,
406 INVALID_NUB_ADDRESS
};
407 // If we are planning to run only one thread, any new threads should be
410 resume_new_threads
.state
= eStateSuspended
;
412 const size_t num_new_threads
= new_threads
.size();
413 const size_t num_threads
= m_threads
.size();
414 for (uint32_t idx
= 0; idx
< num_threads
; ++idx
) {
415 MachThread
*thread
= m_threads
[idx
].get();
416 bool handled
= false;
417 for (uint32_t new_idx
= 0; new_idx
< num_new_threads
; ++new_idx
) {
418 if (thread
== new_threads
[new_idx
].get()) {
419 thread
->ThreadWillResume(&resume_new_threads
);
426 const DNBThreadResumeAction
*thread_action
=
427 thread_actions
.GetActionForThread(thread
->ThreadID(), true);
428 // There must always be a thread action for every thread.
429 assert(thread_action
);
430 bool others_stopped
= false;
431 if (solo_thread
== thread
->ThreadID())
432 others_stopped
= true;
433 thread
->ThreadWillResume(thread_action
, others_stopped
);
437 if (new_threads
.size()) {
438 for (uint32_t idx
= 0; idx
< num_new_threads
; ++idx
) {
440 LOG_THREAD
, "MachThreadList::ProcessWillResume (pid = %4.4x) "
441 "stop-id=%u, resuming newly discovered thread: "
442 "0x%8.8" PRIx64
", thread-is-user-ready=%i)",
443 process
->ProcessID(), process
->StopCount(),
444 new_threads
[idx
]->ThreadID(), new_threads
[idx
]->IsUserReady());
449 uint32_t MachThreadList::ProcessDidStop(MachProcess
*process
) {
450 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
451 // Update our thread list
452 const uint32_t num_threads
= UpdateThreadList(process
, true);
453 for (uint32_t idx
= 0; idx
< num_threads
; ++idx
) {
454 m_threads
[idx
]->ThreadDidStop();
459 // Check each thread in our thread list to see if we should notify our
460 // client of the current halt in execution.
462 // Breakpoints can have callback functions associated with them than
463 // can return true to stop, or false to continue executing the inferior.
466 // true if we should stop and notify our clients
467 // false if we should resume our child process and skip notification
468 bool MachThreadList::ShouldStop(bool &step_more
) {
469 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
470 uint32_t should_stop
= false;
471 const size_t num_threads
= m_threads
.size();
472 for (uint32_t idx
= 0; !should_stop
&& idx
< num_threads
; ++idx
) {
473 should_stop
= m_threads
[idx
]->ShouldStop(step_more
);
478 void MachThreadList::NotifyBreakpointChanged(const DNBBreakpoint
*bp
) {
479 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
480 const size_t num_threads
= m_threads
.size();
481 for (uint32_t idx
= 0; idx
< num_threads
; ++idx
) {
482 m_threads
[idx
]->NotifyBreakpointChanged(bp
);
486 uint32_t MachThreadList::DoHardwareBreakpointAction(
487 const DNBBreakpoint
*bp
, HardwareBreakpointAction action
) const {
489 return INVALID_NUB_HW_INDEX
;
491 uint32_t hw_index
= INVALID_NUB_HW_INDEX
;
492 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
493 const size_t num_threads
= m_threads
.size();
494 // On Mac OS X we have to prime the control registers for new threads. We do
495 // this using the control register data for the first thread, for lack of a
496 // better way of choosing.
497 bool also_set_on_task
= true;
498 for (uint32_t idx
= 0; idx
< num_threads
; ++idx
) {
500 case HardwareBreakpointAction::EnableWatchpoint
:
501 hw_index
= m_threads
[idx
]->EnableHardwareWatchpoint(bp
, also_set_on_task
);
503 case HardwareBreakpointAction::DisableWatchpoint
:
505 m_threads
[idx
]->DisableHardwareWatchpoint(bp
, also_set_on_task
);
507 case HardwareBreakpointAction::EnableBreakpoint
:
508 hw_index
= m_threads
[idx
]->EnableHardwareBreakpoint(bp
, also_set_on_task
);
510 case HardwareBreakpointAction::DisableBreakpoint
:
512 m_threads
[idx
]->DisableHardwareBreakpoint(bp
, also_set_on_task
);
515 if (hw_index
== INVALID_NUB_HW_INDEX
) {
516 // We know that idx failed for some reason. Let's rollback the
517 // transaction for [0, idx).
518 for (uint32_t i
= 0; i
< idx
; ++i
)
519 m_threads
[i
]->RollbackTransForHWP();
520 return INVALID_NUB_HW_INDEX
;
522 also_set_on_task
= false;
524 // Notify each thread to commit the pending transaction.
525 for (uint32_t idx
= 0; idx
< num_threads
; ++idx
)
526 m_threads
[idx
]->FinishTransForHWP();
530 // DNBWatchpointSet() -> MachProcess::CreateWatchpoint() ->
531 // MachProcess::EnableWatchpoint()
532 // -> MachThreadList::EnableHardwareWatchpoint().
534 MachThreadList::EnableHardwareWatchpoint(const DNBBreakpoint
*wp
) const {
535 return DoHardwareBreakpointAction(wp
,
536 HardwareBreakpointAction::EnableWatchpoint
);
539 bool MachThreadList::DisableHardwareWatchpoint(const DNBBreakpoint
*wp
) const {
540 return DoHardwareBreakpointAction(
541 wp
, HardwareBreakpointAction::DisableWatchpoint
) !=
542 INVALID_NUB_HW_INDEX
;
546 MachThreadList::EnableHardwareBreakpoint(const DNBBreakpoint
*bp
) const {
547 return DoHardwareBreakpointAction(bp
,
548 HardwareBreakpointAction::EnableBreakpoint
);
551 bool MachThreadList::DisableHardwareBreakpoint(const DNBBreakpoint
*bp
) const {
552 return DoHardwareBreakpointAction(
553 bp
, HardwareBreakpointAction::DisableBreakpoint
) !=
554 INVALID_NUB_HW_INDEX
;
557 uint32_t MachThreadList::NumSupportedHardwareWatchpoints() const {
558 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
559 const size_t num_threads
= m_threads
.size();
560 // Use an arbitrary thread to retrieve the number of supported hardware
563 return m_threads
[0]->NumSupportedHardwareWatchpoints();
567 uint32_t MachThreadList::GetThreadIndexForThreadStoppedWithSignal(
568 const int signo
) const {
569 PTHREAD_MUTEX_LOCKER(locker
, m_threads_mutex
);
570 uint32_t should_stop
= false;
571 const size_t num_threads
= m_threads
.size();
572 for (uint32_t idx
= 0; !should_stop
&& idx
< num_threads
; ++idx
) {
573 if (m_threads
[idx
]->GetStopException().SoftSignal() == signo
)