1 //===-- MachThread.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 "MachThread.h"
16 #include "MachProcess.h"
17 #include "ThreadInfo.h"
20 #include <mach/thread_policy.h>
22 static uint32_t GetSequenceID() {
23 static uint32_t g_nextID
= 0;
27 MachThread::MachThread(MachProcess
*process
, bool is_64_bit
,
28 uint64_t unique_thread_id
, thread_t mach_port_num
)
29 : m_process(process
), m_unique_id(unique_thread_id
),
30 m_mach_port_number(mach_port_num
), m_seq_id(GetSequenceID()),
31 m_state(eStateUnloaded
), m_state_mutex(PTHREAD_MUTEX_RECURSIVE
),
32 m_suspend_count(0), m_stop_exception(),
33 m_arch_up(DNBArchProtocol::Create(this)), m_reg_sets(NULL
),
34 m_num_reg_sets(0), m_ident_info(), m_proc_threadinfo(),
35 m_dispatch_queue_name(), m_is_64_bit(is_64_bit
),
36 m_pthread_qos_class_decode(nullptr) {
37 nub_size_t num_reg_sets
= 0;
38 m_reg_sets
= m_arch_up
->GetRegisterSetInfo(&num_reg_sets
);
39 m_num_reg_sets
= num_reg_sets
;
41 m_pthread_qos_class_decode
=
42 (unsigned int (*)(unsigned long, int *, unsigned long *))dlsym(
43 RTLD_DEFAULT
, "_pthread_qos_class_decode");
45 // Get the thread state so we know if a thread is in a state where we can't
46 // muck with it and also so we get the suspend count correct in case it was
49 DNBLogThreadedIf(LOG_THREAD
| LOG_VERBOSE
,
50 "MachThread::MachThread ( process = %p, tid = 0x%8.8" PRIx64
52 static_cast<void *>(&m_process
), m_unique_id
, m_seq_id
);
55 MachThread::~MachThread() {
56 DNBLogThreadedIf(LOG_THREAD
| LOG_VERBOSE
,
57 "MachThread::~MachThread() for tid = 0x%8.8" PRIx64
" (%u)",
58 m_unique_id
, m_seq_id
);
61 void MachThread::Suspend() {
62 DNBLogThreadedIf(LOG_THREAD
| LOG_VERBOSE
, "MachThread::%s ( )",
64 if (MachPortNumberIsValid(m_mach_port_number
)) {
65 DNBError
err(::thread_suspend(m_mach_port_number
), DNBError::MachKernel
);
68 if (DNBLogCheckLogBit(LOG_THREAD
) || err
.Fail())
69 err
.LogThreaded("::thread_suspend (%4.4" PRIx32
")", m_mach_port_number
);
73 void MachThread::Resume(bool others_stopped
) {
74 DNBLogThreadedIf(LOG_THREAD
| LOG_VERBOSE
, "MachThread::%s ( )",
76 if (MachPortNumberIsValid(m_mach_port_number
)) {
77 SetSuspendCountBeforeResume(others_stopped
);
81 bool MachThread::SetSuspendCountBeforeResume(bool others_stopped
) {
82 DNBLogThreadedIf(LOG_THREAD
| LOG_VERBOSE
, "MachThread::%s ( )",
85 if (!MachPortNumberIsValid(m_mach_port_number
))
88 integer_t times_to_resume
;
92 times_to_resume
= m_basic_info
.suspend_count
;
93 m_suspend_count
= -(times_to_resume
- m_suspend_count
);
97 times_to_resume
= m_suspend_count
;
101 if (times_to_resume
> 0) {
102 while (times_to_resume
> 0) {
103 err
= ::thread_resume(m_mach_port_number
);
104 if (DNBLogCheckLogBit(LOG_THREAD
) || err
.Fail())
105 err
.LogThreaded("::thread_resume (%4.4" PRIx32
")", m_mach_port_number
);
110 times_to_resume
= m_basic_info
.suspend_count
;
119 bool MachThread::RestoreSuspendCountAfterStop() {
120 DNBLogThreadedIf(LOG_THREAD
| LOG_VERBOSE
, "MachThread::%s ( )",
123 if (!MachPortNumberIsValid(m_mach_port_number
))
126 if (m_suspend_count
> 0) {
127 while (m_suspend_count
> 0) {
128 err
= ::thread_resume(m_mach_port_number
);
129 if (DNBLogCheckLogBit(LOG_THREAD
) || err
.Fail())
130 err
.LogThreaded("::thread_resume (%4.4" PRIx32
")", m_mach_port_number
);
135 m_suspend_count
= m_basic_info
.suspend_count
;
141 } else if (m_suspend_count
< 0) {
142 while (m_suspend_count
< 0) {
143 err
= ::thread_suspend(m_mach_port_number
);
146 if (DNBLogCheckLogBit(LOG_THREAD
) || err
.Fail()) {
147 err
.LogThreaded("::thread_suspend (%4.4" PRIx32
")",
156 const char *MachThread::GetBasicInfoAsString() const {
157 static char g_basic_info_string
[1024];
158 struct thread_basic_info basicInfo
;
160 if (GetBasicInfo(m_mach_port_number
, &basicInfo
)) {
162 // char run_state_str[32];
163 // size_t run_state_str_size = sizeof(run_state_str);
164 // switch (basicInfo.run_state)
166 // case TH_STATE_RUNNING: strlcpy(run_state_str, "running",
167 // run_state_str_size); break;
168 // case TH_STATE_STOPPED: strlcpy(run_state_str, "stopped",
169 // run_state_str_size); break;
170 // case TH_STATE_WAITING: strlcpy(run_state_str, "waiting",
171 // run_state_str_size); break;
172 // case TH_STATE_UNINTERRUPTIBLE: strlcpy(run_state_str,
173 // "uninterruptible", run_state_str_size); break;
174 // case TH_STATE_HALTED: strlcpy(run_state_str, "halted",
175 // run_state_str_size); break;
176 // default: snprintf(run_state_str,
177 // run_state_str_size, "%d", basicInfo.run_state); break; // ???
179 float user
= (float)basicInfo
.user_time
.seconds
+
180 (float)basicInfo
.user_time
.microseconds
/ 1000000.0f
;
181 float system
= (float)basicInfo
.user_time
.seconds
+
182 (float)basicInfo
.user_time
.microseconds
/ 1000000.0f
;
183 snprintf(g_basic_info_string
, sizeof(g_basic_info_string
),
184 "Thread 0x%8.8" PRIx64
": user=%f system=%f cpu=%d sleep_time=%d",
185 m_unique_id
, user
, system
, basicInfo
.cpu_usage
,
186 basicInfo
.sleep_time
);
188 return g_basic_info_string
;
193 // Finds the Mach port number for a given thread in the inferior process' port
195 thread_t
MachThread::InferiorThreadID() const {
196 mach_msg_type_number_t i
;
197 mach_port_name_array_t names
;
198 mach_port_type_array_t types
;
199 mach_msg_type_number_t ncount
, tcount
;
200 thread_t inferior_tid
= INVALID_NUB_THREAD
;
201 task_t my_task
= ::mach_task_self();
202 task_t task
= m_process
->Task().TaskPort();
205 ::mach_port_names(task
, &names
, &ncount
, &types
, &tcount
);
206 if (kret
== KERN_SUCCESS
) {
208 for (i
= 0; i
< ncount
; i
++) {
210 mach_msg_type_name_t my_type
;
212 kret
= ::mach_port_extract_right(task
, names
[i
], MACH_MSG_TYPE_COPY_SEND
,
214 if (kret
== KERN_SUCCESS
) {
215 ::mach_port_deallocate(my_task
, my_name
);
216 if (my_name
== m_mach_port_number
) {
217 inferior_tid
= names
[i
];
222 // Free up the names and types
223 ::vm_deallocate(my_task
, (vm_address_t
)names
,
224 ncount
* sizeof(mach_port_name_t
));
225 ::vm_deallocate(my_task
, (vm_address_t
)types
,
226 tcount
* sizeof(mach_port_type_t
));
231 bool MachThread::IsUserReady() {
232 if (m_basic_info
.run_state
== 0)
235 switch (m_basic_info
.run_state
) {
237 case TH_STATE_UNINTERRUPTIBLE
:
240 case TH_STATE_RUNNING
:
241 case TH_STATE_STOPPED
:
242 case TH_STATE_WAITING
:
243 case TH_STATE_HALTED
:
246 return GetPC(0) != 0;
249 struct thread_basic_info
*MachThread::GetBasicInfo() {
250 if (MachThread::GetBasicInfo(m_mach_port_number
, &m_basic_info
))
251 return &m_basic_info
;
255 bool MachThread::GetBasicInfo(thread_t thread
,
256 struct thread_basic_info
*basicInfoPtr
) {
257 if (MachPortNumberIsValid(thread
)) {
258 unsigned int info_count
= THREAD_BASIC_INFO_COUNT
;
259 kern_return_t err
= ::thread_info(thread
, THREAD_BASIC_INFO
,
260 (thread_info_t
)basicInfoPtr
, &info_count
);
261 if (err
== KERN_SUCCESS
)
264 ::memset(basicInfoPtr
, 0, sizeof(struct thread_basic_info
));
268 bool MachThread::ThreadIDIsValid(uint64_t thread
) { return thread
!= 0; }
270 bool MachThread::MachPortNumberIsValid(thread_t thread
) {
271 return thread
!= THREAD_NULL
;
274 bool MachThread::GetRegisterState(int flavor
, bool force
) {
275 return m_arch_up
->GetRegisterState(flavor
, force
) == KERN_SUCCESS
;
278 bool MachThread::SetRegisterState(int flavor
) {
279 return m_arch_up
->SetRegisterState(flavor
) == KERN_SUCCESS
;
282 uint64_t MachThread::GetPC(uint64_t failValue
) {
283 // Get program counter
284 return m_arch_up
->GetPC(failValue
);
287 bool MachThread::SetPC(uint64_t value
) {
288 // Set program counter
289 return m_arch_up
->SetPC(value
);
292 uint64_t MachThread::GetSP(uint64_t failValue
) {
294 return m_arch_up
->GetSP(failValue
);
297 nub_process_t
MachThread::ProcessID() const {
299 return m_process
->ProcessID();
300 return INVALID_NUB_PROCESS
;
303 void MachThread::Dump(uint32_t index
) {
304 const char *thread_run_state
= NULL
;
306 switch (m_basic_info
.run_state
) {
307 case TH_STATE_RUNNING
:
308 thread_run_state
= "running";
309 break; // 1 thread is running normally
310 case TH_STATE_STOPPED
:
311 thread_run_state
= "stopped";
312 break; // 2 thread is stopped
313 case TH_STATE_WAITING
:
314 thread_run_state
= "waiting";
315 break; // 3 thread is waiting normally
316 case TH_STATE_UNINTERRUPTIBLE
:
317 thread_run_state
= "uninter";
318 break; // 4 thread is in an uninterruptible wait
319 case TH_STATE_HALTED
:
320 thread_run_state
= "halted ";
321 break; // 5 thread is halted at a
323 thread_run_state
= "???";
328 "[%3u] #%3u tid: 0x%8.8" PRIx64
", pc: 0x%16.16" PRIx64
329 ", sp: 0x%16.16" PRIx64
330 ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: "
331 "%2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
332 index
, m_seq_id
, m_unique_id
, GetPC(INVALID_NUB_ADDRESS
),
333 GetSP(INVALID_NUB_ADDRESS
), m_basic_info
.user_time
.seconds
,
334 m_basic_info
.user_time
.microseconds
, m_basic_info
.system_time
.seconds
,
335 m_basic_info
.system_time
.microseconds
, m_basic_info
.cpu_usage
,
336 m_basic_info
.policy
, m_basic_info
.run_state
, thread_run_state
,
337 m_basic_info
.flags
, m_basic_info
.suspend_count
, m_suspend_count
,
338 m_basic_info
.sleep_time
);
339 // DumpRegisterState(0);
342 void MachThread::ThreadWillResume(const DNBThreadResumeAction
*thread_action
,
343 bool others_stopped
) {
344 if (thread_action
->addr
!= INVALID_NUB_ADDRESS
)
345 SetPC(thread_action
->addr
);
347 SetState(thread_action
->state
);
348 switch (thread_action
->state
) {
350 case eStateSuspended
:
351 assert(others_stopped
== false);
357 Resume(others_stopped
);
362 m_arch_up
->ThreadWillResume();
363 m_stop_exception
.Clear();
366 DNBBreakpoint
*MachThread::CurrentBreakpoint() {
367 return m_process
->Breakpoints().FindByAddress(GetPC());
370 bool MachThread::ShouldStop(bool &step_more
) {
371 // See if this thread is at a breakpoint?
372 DNBBreakpoint
*bp
= CurrentBreakpoint();
375 // This thread is sitting at a breakpoint, ask the breakpoint
376 // if we should be stopping here.
379 if (m_arch_up
->StepNotComplete()) {
383 // The thread state is used to let us know what the thread was
384 // trying to do. MachThread::ThreadWillResume() will set the
385 // thread state to various values depending if the thread was
386 // the current thread and if it was to be single stepped, or
388 if (GetState() == eStateRunning
) {
389 // If our state is running, then we should continue as we are in
390 // the process of stepping over a breakpoint.
393 // Stop if we have any kind of valid exception for this
395 if (GetStopException().IsValid())
401 bool MachThread::IsStepping() { return GetState() == eStateStepping
; }
403 bool MachThread::ThreadDidStop() {
404 // This thread has existed prior to resuming under debug nub control,
405 // and has just been stopped. Do any cleanup that needs to be done
408 // The thread state and breakpoint will still have the same values
409 // as they had prior to resuming the thread, so it makes it easy to check
410 // if we were trying to step a thread, or we tried to resume while being
413 // When this method gets called, the process state is still in the
414 // state it was in while running so we can act accordingly.
415 m_arch_up
->ThreadDidStop();
417 // We may have suspended this thread so the primary thread could step
418 // without worrying about race conditions, so lets restore our suspend
420 RestoreSuspendCountAfterStop();
422 // Update the basic information for a thread
423 MachThread::GetBasicInfo(m_mach_port_number
, &m_basic_info
);
425 if (m_basic_info
.suspend_count
> 0)
426 SetState(eStateSuspended
);
428 SetState(eStateStopped
);
432 bool MachThread::NotifyException(MachException::Data
&exc
) {
433 // Allow the arch specific protocol to process (MachException::Data &)exc
434 // first before possible reassignment of m_stop_exception with exc.
435 // See also MachThread::GetStopException().
436 bool handled
= m_arch_up
->NotifyException(exc
);
438 if (m_stop_exception
.IsValid()) {
439 // We may have more than one exception for a thread, but we need to
440 // only remember the one that we will say is the reason we stopped.
441 // We may have been single stepping and also gotten a signal exception,
442 // so just remember the most pertinent one.
443 if (m_stop_exception
.IsBreakpoint())
444 m_stop_exception
= exc
;
446 m_stop_exception
= exc
;
452 nub_state_t
MachThread::GetState() {
453 // If any other threads access this we will need a mutex for it
454 PTHREAD_MUTEX_LOCKER(locker
, m_state_mutex
);
458 void MachThread::SetState(nub_state_t state
) {
459 PTHREAD_MUTEX_LOCKER(locker
, m_state_mutex
);
461 DNBLogThreadedIf(LOG_THREAD
,
462 "MachThread::SetState ( %s ) for tid = 0x%8.8" PRIx64
"",
463 DNBStateAsString(state
), m_unique_id
);
466 nub_size_t
MachThread::GetNumRegistersInSet(nub_size_t regSet
) const {
467 if (regSet
< m_num_reg_sets
)
468 return m_reg_sets
[regSet
].num_registers
;
472 const char *MachThread::GetRegisterSetName(nub_size_t regSet
) const {
473 if (regSet
< m_num_reg_sets
)
474 return m_reg_sets
[regSet
].name
;
478 const DNBRegisterInfo
*MachThread::GetRegisterInfo(nub_size_t regSet
,
479 nub_size_t regIndex
) const {
480 if (regSet
< m_num_reg_sets
)
481 if (regIndex
< m_reg_sets
[regSet
].num_registers
)
482 return &m_reg_sets
[regSet
].registers
[regIndex
];
485 void MachThread::DumpRegisterState(nub_size_t regSet
) {
486 if (regSet
== REGISTER_SET_ALL
) {
487 for (regSet
= 1; regSet
< m_num_reg_sets
; regSet
++)
488 DumpRegisterState(regSet
);
490 if (m_arch_up
->RegisterSetStateIsValid((int)regSet
)) {
491 const size_t numRegisters
= GetNumRegistersInSet(regSet
);
492 uint32_t regIndex
= 0;
493 DNBRegisterValueClass reg
;
494 for (regIndex
= 0; regIndex
< numRegisters
; ++regIndex
) {
495 if (m_arch_up
->GetRegisterValue((uint32_t)regSet
, regIndex
, ®
)) {
496 reg
.Dump(NULL
, NULL
);
500 DNBLog("%s: registers are not currently valid.",
501 GetRegisterSetName(regSet
));
506 const DNBRegisterSetInfo
*
507 MachThread::GetRegisterSetInfo(nub_size_t
*num_reg_sets
) const {
508 *num_reg_sets
= m_num_reg_sets
;
509 return &m_reg_sets
[0];
512 bool MachThread::GetRegisterValue(uint32_t set
, uint32_t reg
,
513 DNBRegisterValue
*value
) {
514 return m_arch_up
->GetRegisterValue(set
, reg
, value
);
517 bool MachThread::SetRegisterValue(uint32_t set
, uint32_t reg
,
518 const DNBRegisterValue
*value
) {
519 return m_arch_up
->SetRegisterValue(set
, reg
, value
);
522 nub_size_t
MachThread::GetRegisterContext(void *buf
, nub_size_t buf_len
) {
523 return m_arch_up
->GetRegisterContext(buf
, buf_len
);
526 nub_size_t
MachThread::SetRegisterContext(const void *buf
, nub_size_t buf_len
) {
527 return m_arch_up
->SetRegisterContext(buf
, buf_len
);
530 uint32_t MachThread::SaveRegisterState() {
531 return m_arch_up
->SaveRegisterState();
533 bool MachThread::RestoreRegisterState(uint32_t save_id
) {
534 return m_arch_up
->RestoreRegisterState(save_id
);
537 uint32_t MachThread::EnableHardwareBreakpoint(const DNBBreakpoint
*bp
,
538 bool also_set_on_task
) {
539 if (bp
!= NULL
&& bp
->IsBreakpoint()) {
540 return m_arch_up
->EnableHardwareBreakpoint(bp
->Address(), bp
->ByteSize(),
543 return INVALID_NUB_HW_INDEX
;
546 uint32_t MachThread::EnableHardwareWatchpoint(const DNBBreakpoint
*wp
,
547 bool also_set_on_task
) {
548 if (wp
!= NULL
&& wp
->IsWatchpoint())
549 return m_arch_up
->EnableHardwareWatchpoint(
550 wp
->Address(), wp
->ByteSize(), wp
->WatchpointRead(),
551 wp
->WatchpointWrite(), also_set_on_task
);
552 return INVALID_NUB_HW_INDEX
;
555 bool MachThread::RollbackTransForHWP() {
556 return m_arch_up
->RollbackTransForHWP();
559 bool MachThread::FinishTransForHWP() { return m_arch_up
->FinishTransForHWP(); }
561 bool MachThread::DisableHardwareBreakpoint(const DNBBreakpoint
*bp
,
562 bool also_set_on_task
) {
563 if (bp
!= NULL
&& bp
->IsHardware()) {
564 return m_arch_up
->DisableHardwareBreakpoint(bp
->GetHardwareIndex(),
570 bool MachThread::DisableHardwareWatchpoint(const DNBBreakpoint
*wp
,
571 bool also_set_on_task
) {
572 if (wp
!= NULL
&& wp
->IsHardware())
573 return m_arch_up
->DisableHardwareWatchpoint(wp
->GetHardwareIndex(),
578 uint32_t MachThread::NumSupportedHardwareWatchpoints() const {
579 return m_arch_up
->NumSupportedHardwareWatchpoints();
582 bool MachThread::GetIdentifierInfo() {
583 // Don't try to get the thread info once and cache it for the life of the
584 // thread. It changes over time, for instance
585 // if the thread name changes, then the thread_handle also changes... So you
586 // have to refetch it every time.
587 mach_msg_type_number_t count
= THREAD_IDENTIFIER_INFO_COUNT
;
588 kern_return_t kret
= ::thread_info(m_mach_port_number
, THREAD_IDENTIFIER_INFO
,
589 (thread_info_t
)&m_ident_info
, &count
);
590 return kret
== KERN_SUCCESS
;
595 const char *MachThread::GetName() {
596 if (GetIdentifierInfo()) {
597 int len
= ::proc_pidinfo(m_process
->ProcessID(), PROC_PIDTHREADINFO
,
598 m_ident_info
.thread_handle
, &m_proc_threadinfo
,
599 sizeof(m_proc_threadinfo
));
601 if (len
&& m_proc_threadinfo
.pth_name
[0])
602 return m_proc_threadinfo
.pth_name
;
608 MachThread::GetGloballyUniqueThreadIDForMachPortID(thread_t mach_port_id
) {
610 thread_identifier_info_data_t tident
;
611 mach_msg_type_number_t tident_count
= THREAD_IDENTIFIER_INFO_COUNT
;
612 kr
= thread_info(mach_port_id
, THREAD_IDENTIFIER_INFO
, (thread_info_t
)&tident
,
614 if (kr
!= KERN_SUCCESS
) {
617 return tident
.thread_id
;
620 nub_addr_t
MachThread::GetPThreadT() {
621 nub_addr_t pthread_t_value
= INVALID_NUB_ADDRESS
;
622 if (MachPortNumberIsValid(m_mach_port_number
)) {
624 thread_identifier_info_data_t tident
;
625 mach_msg_type_number_t tident_count
= THREAD_IDENTIFIER_INFO_COUNT
;
626 kr
= thread_info(m_mach_port_number
, THREAD_IDENTIFIER_INFO
,
627 (thread_info_t
)&tident
, &tident_count
);
628 if (kr
== KERN_SUCCESS
) {
629 // Dereference thread_handle to get the pthread_t value for this thread.
632 if (m_process
->ReadMemory(tident
.thread_handle
, 8, &addr
) == 8) {
634 pthread_t_value
= addr
;
639 if (m_process
->ReadMemory(tident
.thread_handle
, 4, &addr
) == 4) {
641 pthread_t_value
= addr
;
647 return pthread_t_value
;
650 // Return this thread's TSD (Thread Specific Data) address.
651 // This is computed based on this thread's pthread_t value.
653 // We compute the TSD from the pthread_t by one of two methods.
655 // If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we
657 // the pthread_t to get the TSD base address.
659 // Else we read a pointer from memory at pthread_t +
660 // plo_pthread_tsd_base_address_offset and
661 // that gives us the TSD address.
663 // These plo_pthread_tsd_base values must be read out of libpthread by lldb &
664 // provided to debugserver.
667 MachThread::GetTSDAddressForThread(uint64_t plo_pthread_tsd_base_address_offset
,
668 uint64_t plo_pthread_tsd_base_offset
,
669 uint64_t plo_pthread_tsd_entry_size
) {
670 nub_addr_t tsd_addr
= INVALID_NUB_ADDRESS
;
671 nub_addr_t pthread_t_value
= GetPThreadT();
672 if (plo_pthread_tsd_base_offset
!= 0 &&
673 plo_pthread_tsd_base_offset
!= INVALID_NUB_ADDRESS
) {
674 tsd_addr
= pthread_t_value
+ plo_pthread_tsd_base_offset
;
676 if (plo_pthread_tsd_entry_size
== 4) {
678 if (m_process
->ReadMemory(pthread_t_value
+
679 plo_pthread_tsd_base_address_offset
,
686 if (plo_pthread_tsd_entry_size
== 4) {
688 if (m_process
->ReadMemory(pthread_t_value
+
689 plo_pthread_tsd_base_address_offset
,
700 nub_addr_t
MachThread::GetDispatchQueueT() {
701 nub_addr_t dispatch_queue_t_value
= INVALID_NUB_ADDRESS
;
702 if (MachPortNumberIsValid(m_mach_port_number
)) {
704 thread_identifier_info_data_t tident
;
705 mach_msg_type_number_t tident_count
= THREAD_IDENTIFIER_INFO_COUNT
;
706 kr
= thread_info(m_mach_port_number
, THREAD_IDENTIFIER_INFO
,
707 (thread_info_t
)&tident
, &tident_count
);
708 if (kr
== KERN_SUCCESS
&& tident
.dispatch_qaddr
!= 0 &&
709 tident
.dispatch_qaddr
!= INVALID_NUB_ADDRESS
) {
710 // Dereference dispatch_qaddr to get the dispatch_queue_t value for this
711 // thread's queue, if any.
714 if (m_process
->ReadMemory(tident
.dispatch_qaddr
, 8, &addr
) == 8) {
716 dispatch_queue_t_value
= addr
;
720 if (m_process
->ReadMemory(tident
.dispatch_qaddr
, 4, &addr
) == 4) {
722 dispatch_queue_t_value
= addr
;
727 return dispatch_queue_t_value
;
730 ThreadInfo::QoS
MachThread::GetRequestedQoS(nub_addr_t tsd
,
731 uint64_t dti_qos_class_index
) {
732 ThreadInfo::QoS qos_value
;
733 if (MachPortNumberIsValid(m_mach_port_number
) &&
734 m_pthread_qos_class_decode
!= nullptr) {
735 uint64_t pthread_priority_value
= 0;
738 if (m_process
->ReadMemory(tsd
+ (dti_qos_class_index
* 8), 8, &pri
) ==
740 pthread_priority_value
= pri
;
744 if (m_process
->ReadMemory(tsd
+ (dti_qos_class_index
* 4), 4, &pri
) ==
746 pthread_priority_value
= pri
;
750 uint32_t requested_qos
=
751 m_pthread_qos_class_decode(pthread_priority_value
, NULL
, NULL
);
753 switch (requested_qos
) {
754 // These constants from <pthread/qos.h>
756 qos_value
.enum_value
= requested_qos
;
757 qos_value
.constant_name
= "QOS_CLASS_USER_INTERACTIVE";
758 qos_value
.printable_name
= "User Interactive";
761 qos_value
.enum_value
= requested_qos
;
762 qos_value
.constant_name
= "QOS_CLASS_USER_INITIATED";
763 qos_value
.printable_name
= "User Initiated";
766 qos_value
.enum_value
= requested_qos
;
767 qos_value
.constant_name
= "QOS_CLASS_DEFAULT";
768 qos_value
.printable_name
= "Default";
771 qos_value
.enum_value
= requested_qos
;
772 qos_value
.constant_name
= "QOS_CLASS_UTILITY";
773 qos_value
.printable_name
= "Utility";
776 qos_value
.enum_value
= requested_qos
;
777 qos_value
.constant_name
= "QOS_CLASS_BACKGROUND";
778 qos_value
.printable_name
= "Background";
781 qos_value
.enum_value
= requested_qos
;
782 qos_value
.constant_name
= "QOS_CLASS_UNSPECIFIED";
783 qos_value
.printable_name
= "Unspecified";