130fdc8d8SChris Lattner //===-- MachThread.cpp ------------------------------------------*- C++ -*-===//
230fdc8d8SChris Lattner //
330fdc8d8SChris Lattner //                     The LLVM Compiler Infrastructure
430fdc8d8SChris Lattner //
530fdc8d8SChris Lattner // This file is distributed under the University of Illinois Open Source
630fdc8d8SChris Lattner // License. See LICENSE.TXT for details.
730fdc8d8SChris Lattner //
830fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
930fdc8d8SChris Lattner //
1030fdc8d8SChris Lattner //  Created by Greg Clayton on 6/19/07.
1130fdc8d8SChris Lattner //
1230fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
1330fdc8d8SChris Lattner 
14b9c1b51eSKate Stone #include "MachThread.h"
15b9c1b51eSKate Stone #include "DNB.h"
16b9c1b51eSKate Stone #include "DNBLog.h"
17b9c1b51eSKate Stone #include "MachProcess.h"
18b9c1b51eSKate Stone #include "ThreadInfo.h"
19b9c1b51eSKate Stone #include <dlfcn.h>
201c73911dSJason Molenda #include <inttypes.h>
21705b1809SJason Molenda #include <mach/thread_policy.h>
2230fdc8d8SChris Lattner 
23b9c1b51eSKate Stone static uint32_t GetSequenceID() {
2430fdc8d8SChris Lattner   static uint32_t g_nextID = 0;
2530fdc8d8SChris Lattner   return ++g_nextID;
2630fdc8d8SChris Lattner }
2730fdc8d8SChris Lattner 
28b9c1b51eSKate Stone MachThread::MachThread(MachProcess *process, bool is_64_bit,
29b9c1b51eSKate Stone                        uint64_t unique_thread_id, thread_t mach_port_num)
30b9c1b51eSKate Stone     : m_process(process), m_unique_id(unique_thread_id),
31b9c1b51eSKate Stone       m_mach_port_number(mach_port_num), m_seq_id(GetSequenceID()),
32b9c1b51eSKate Stone       m_state(eStateUnloaded), m_state_mutex(PTHREAD_MUTEX_RECURSIVE),
33b9c1b51eSKate Stone       m_suspend_count(0), m_stop_exception(),
34b9c1b51eSKate Stone       m_arch_ap(DNBArchProtocol::Create(this)), m_reg_sets(NULL),
35b9c1b51eSKate Stone       m_num_reg_sets(0), m_ident_info(), m_proc_threadinfo(),
36b9c1b51eSKate Stone       m_dispatch_queue_name(), m_is_64_bit(is_64_bit),
37b9c1b51eSKate Stone       m_pthread_qos_class_decode(nullptr) {
38c235ac76SGreg Clayton   nub_size_t num_reg_sets = 0;
39c235ac76SGreg Clayton   m_reg_sets = m_arch_ap->GetRegisterSetInfo(&num_reg_sets);
40c235ac76SGreg Clayton   m_num_reg_sets = num_reg_sets;
41c235ac76SGreg Clayton 
42b9c1b51eSKate Stone   m_pthread_qos_class_decode =
43b9c1b51eSKate Stone       (unsigned int (*)(unsigned long, int *, unsigned long *))dlsym(
44b9c1b51eSKate Stone           RTLD_DEFAULT, "_pthread_qos_class_decode");
45705b1809SJason Molenda 
461b946bf6SGreg Clayton   // Get the thread state so we know if a thread is in a state where we can't
471b946bf6SGreg Clayton   // muck with it and also so we get the suspend count correct in case it was
481b946bf6SGreg Clayton   // already suspended
49cdc7322bSGreg Clayton   GetBasicInfo();
50b9c1b51eSKate Stone   DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
51b9c1b51eSKate Stone                    "MachThread::MachThread ( process = %p, tid = 0x%8.8" PRIx64
52b9c1b51eSKate Stone                    ", seq_id = %u )",
53a64fafc7STim Hammerquist                    reinterpret_cast<void *>(&m_process), m_unique_id, m_seq_id);
5430fdc8d8SChris Lattner }
5530fdc8d8SChris Lattner 
56b9c1b51eSKate Stone MachThread::~MachThread() {
57b9c1b51eSKate Stone   DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
58b9c1b51eSKate Stone                    "MachThread::~MachThread() for tid = 0x%8.8" PRIx64 " (%u)",
59b9c1b51eSKate Stone                    m_unique_id, m_seq_id);
6030fdc8d8SChris Lattner }
6130fdc8d8SChris Lattner 
62b9c1b51eSKate Stone void MachThread::Suspend() {
63b9c1b51eSKate Stone   DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
64b9c1b51eSKate Stone                    __FUNCTION__);
65b9c1b51eSKate Stone   if (MachPortNumberIsValid(m_mach_port_number)) {
661c73911dSJason Molenda     DNBError err(::thread_suspend(m_mach_port_number), DNBError::MachKernel);
6730fdc8d8SChris Lattner     if (err.Success())
68c235ac76SGreg Clayton       m_suspend_count++;
6930fdc8d8SChris Lattner     if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
701c73911dSJason Molenda       err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number);
7130fdc8d8SChris Lattner   }
7230fdc8d8SChris Lattner }
7330fdc8d8SChris Lattner 
74b9c1b51eSKate Stone void MachThread::Resume(bool others_stopped) {
75b9c1b51eSKate Stone   DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
76b9c1b51eSKate Stone                    __FUNCTION__);
77b9c1b51eSKate Stone   if (MachPortNumberIsValid(m_mach_port_number)) {
789411ddb6SJim Ingham     SetSuspendCountBeforeResume(others_stopped);
7930fdc8d8SChris Lattner   }
8030fdc8d8SChris Lattner }
8130fdc8d8SChris Lattner 
82b9c1b51eSKate Stone bool MachThread::SetSuspendCountBeforeResume(bool others_stopped) {
83b9c1b51eSKate Stone   DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
84b9c1b51eSKate Stone                    __FUNCTION__);
8530fdc8d8SChris Lattner   DNBError err;
861c73911dSJason Molenda   if (MachPortNumberIsValid(m_mach_port_number) == false)
8730fdc8d8SChris Lattner     return false;
889411ddb6SJim Ingham 
89ee2ed525SGreg Clayton   integer_t times_to_resume;
909411ddb6SJim Ingham 
91b9c1b51eSKate Stone   if (others_stopped) {
92b9c1b51eSKate Stone     if (GetBasicInfo()) {
9382283e8eSJim Ingham       times_to_resume = m_basic_info.suspend_count;
94c235ac76SGreg Clayton       m_suspend_count = -(times_to_resume - m_suspend_count);
95b9c1b51eSKate Stone     } else
9682283e8eSJim Ingham       times_to_resume = 0;
97b9c1b51eSKate Stone   } else {
98c235ac76SGreg Clayton     times_to_resume = m_suspend_count;
99c235ac76SGreg Clayton     m_suspend_count = 0;
1009411ddb6SJim Ingham   }
1019411ddb6SJim Ingham 
102b9c1b51eSKate Stone   if (times_to_resume > 0) {
103b9c1b51eSKate Stone     while (times_to_resume > 0) {
1041c73911dSJason Molenda       err = ::thread_resume(m_mach_port_number);
1059411ddb6SJim Ingham       if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
1061c73911dSJason Molenda         err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
1079411ddb6SJim Ingham       if (err.Success())
1089411ddb6SJim Ingham         --times_to_resume;
109b9c1b51eSKate Stone       else {
1109411ddb6SJim Ingham         if (GetBasicInfo())
111c235ac76SGreg Clayton           times_to_resume = m_basic_info.suspend_count;
1129411ddb6SJim Ingham         else
1139411ddb6SJim Ingham           times_to_resume = 0;
1149411ddb6SJim Ingham       }
1159411ddb6SJim Ingham     }
1169411ddb6SJim Ingham   }
1179411ddb6SJim Ingham   return true;
1189411ddb6SJim Ingham }
1199411ddb6SJim Ingham 
120b9c1b51eSKate Stone bool MachThread::RestoreSuspendCountAfterStop() {
121b9c1b51eSKate Stone   DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
122b9c1b51eSKate Stone                    __FUNCTION__);
1239411ddb6SJim Ingham   DNBError err;
1241c73911dSJason Molenda   if (MachPortNumberIsValid(m_mach_port_number) == false)
1259411ddb6SJim Ingham     return false;
1269411ddb6SJim Ingham 
127b9c1b51eSKate Stone   if (m_suspend_count > 0) {
128b9c1b51eSKate Stone     while (m_suspend_count > 0) {
1291c73911dSJason Molenda       err = ::thread_resume(m_mach_port_number);
13030fdc8d8SChris Lattner       if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
1311c73911dSJason Molenda         err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
13230fdc8d8SChris Lattner       if (err.Success())
133c235ac76SGreg Clayton         --m_suspend_count;
134b9c1b51eSKate Stone       else {
135cdc7322bSGreg Clayton         if (GetBasicInfo())
136c235ac76SGreg Clayton           m_suspend_count = m_basic_info.suspend_count;
1370dd2c627SGreg Clayton         else
138c235ac76SGreg Clayton           m_suspend_count = 0;
1390dd2c627SGreg Clayton         return false; // ???
14030fdc8d8SChris Lattner       }
14130fdc8d8SChris Lattner     }
142b9c1b51eSKate Stone   } else if (m_suspend_count < 0) {
143b9c1b51eSKate Stone     while (m_suspend_count < 0) {
1441c73911dSJason Molenda       err = ::thread_suspend(m_mach_port_number);
1459411ddb6SJim Ingham       if (err.Success())
146c235ac76SGreg Clayton         ++m_suspend_count;
147b9c1b51eSKate Stone       if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) {
148b9c1b51eSKate Stone         err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")",
149b9c1b51eSKate Stone                         m_mach_port_number);
150478235d8SJim Ingham         return false;
151478235d8SJim Ingham       }
1529411ddb6SJim Ingham     }
1539411ddb6SJim Ingham   }
1540dd2c627SGreg Clayton   return true;
15530fdc8d8SChris Lattner }
15630fdc8d8SChris Lattner 
157b9c1b51eSKate Stone const char *MachThread::GetBasicInfoAsString() const {
15830fdc8d8SChris Lattner   static char g_basic_info_string[1024];
15930fdc8d8SChris Lattner   struct thread_basic_info basicInfo;
16030fdc8d8SChris Lattner 
161b9c1b51eSKate Stone   if (GetBasicInfo(m_mach_port_number, &basicInfo)) {
16230fdc8d8SChris Lattner 
16330fdc8d8SChris Lattner     //        char run_state_str[32];
16430fdc8d8SChris Lattner     //        size_t run_state_str_size = sizeof(run_state_str);
16530fdc8d8SChris Lattner     //        switch (basicInfo.run_state)
16630fdc8d8SChris Lattner     //        {
167*aae5b690SJason Molenda     //        case TH_STATE_RUNNING:          strlcpy(run_state_str, "running",
168b9c1b51eSKate Stone     //        run_state_str_size); break;
169*aae5b690SJason Molenda     //        case TH_STATE_STOPPED:          strlcpy(run_state_str, "stopped",
170b9c1b51eSKate Stone     //        run_state_str_size); break;
171*aae5b690SJason Molenda     //        case TH_STATE_WAITING:          strlcpy(run_state_str, "waiting",
172b9c1b51eSKate Stone     //        run_state_str_size); break;
173*aae5b690SJason Molenda     //        case TH_STATE_UNINTERRUPTIBLE:  strlcpy(run_state_str,
174b9c1b51eSKate Stone     //        "uninterruptible", run_state_str_size); break;
175*aae5b690SJason Molenda     //        case TH_STATE_HALTED:           strlcpy(run_state_str, "halted",
176b9c1b51eSKate Stone     //        run_state_str_size); break;
177b9c1b51eSKate Stone     //        default:                        snprintf(run_state_str,
178b9c1b51eSKate Stone     //        run_state_str_size, "%d", basicInfo.run_state); break;    // ???
17930fdc8d8SChris Lattner     //        }
180b9c1b51eSKate Stone     float user = (float)basicInfo.user_time.seconds +
181b9c1b51eSKate Stone                  (float)basicInfo.user_time.microseconds / 1000000.0f;
182b9c1b51eSKate Stone     float system = (float)basicInfo.user_time.seconds +
183b9c1b51eSKate Stone                    (float)basicInfo.user_time.microseconds / 1000000.0f;
184b9c1b51eSKate Stone     snprintf(g_basic_info_string, sizeof(g_basic_info_string),
185b9c1b51eSKate Stone              "Thread 0x%8.8" PRIx64 ": user=%f system=%f cpu=%d sleep_time=%d",
186b9c1b51eSKate Stone              m_unique_id, user, system, basicInfo.cpu_usage,
18730fdc8d8SChris Lattner              basicInfo.sleep_time);
18830fdc8d8SChris Lattner 
18930fdc8d8SChris Lattner     return g_basic_info_string;
19030fdc8d8SChris Lattner   }
19130fdc8d8SChris Lattner   return NULL;
19230fdc8d8SChris Lattner }
19330fdc8d8SChris Lattner 
194b9c1b51eSKate Stone // Finds the Mach port number for a given thread in the inferior process' port
195b9c1b51eSKate Stone // namespace.
196b9c1b51eSKate Stone thread_t MachThread::InferiorThreadID() const {
19730fdc8d8SChris Lattner   mach_msg_type_number_t i;
19830fdc8d8SChris Lattner   mach_port_name_array_t names;
19930fdc8d8SChris Lattner   mach_port_type_array_t types;
20030fdc8d8SChris Lattner   mach_msg_type_number_t ncount, tcount;
20130fdc8d8SChris Lattner   thread_t inferior_tid = INVALID_NUB_THREAD;
20230fdc8d8SChris Lattner   task_t my_task = ::mach_task_self();
20330fdc8d8SChris Lattner   task_t task = m_process->Task().TaskPort();
20430fdc8d8SChris Lattner 
205b9c1b51eSKate Stone   kern_return_t kret =
206b9c1b51eSKate Stone       ::mach_port_names(task, &names, &ncount, &types, &tcount);
207b9c1b51eSKate Stone   if (kret == KERN_SUCCESS) {
20830fdc8d8SChris Lattner 
209b9c1b51eSKate Stone     for (i = 0; i < ncount; i++) {
21030fdc8d8SChris Lattner       mach_port_t my_name;
21130fdc8d8SChris Lattner       mach_msg_type_name_t my_type;
21230fdc8d8SChris Lattner 
213b9c1b51eSKate Stone       kret = ::mach_port_extract_right(task, names[i], MACH_MSG_TYPE_COPY_SEND,
214b9c1b51eSKate Stone                                        &my_name, &my_type);
215b9c1b51eSKate Stone       if (kret == KERN_SUCCESS) {
21630fdc8d8SChris Lattner         ::mach_port_deallocate(my_task, my_name);
217b9c1b51eSKate Stone         if (my_name == m_mach_port_number) {
21830fdc8d8SChris Lattner           inferior_tid = names[i];
21930fdc8d8SChris Lattner           break;
22030fdc8d8SChris Lattner         }
22130fdc8d8SChris Lattner       }
22230fdc8d8SChris Lattner     }
22330fdc8d8SChris Lattner     // Free up the names and types
224b9c1b51eSKate Stone     ::vm_deallocate(my_task, (vm_address_t)names,
225b9c1b51eSKate Stone                     ncount * sizeof(mach_port_name_t));
226b9c1b51eSKate Stone     ::vm_deallocate(my_task, (vm_address_t)types,
227b9c1b51eSKate Stone                     tcount * sizeof(mach_port_type_t));
22830fdc8d8SChris Lattner   }
22930fdc8d8SChris Lattner   return inferior_tid;
23030fdc8d8SChris Lattner }
23130fdc8d8SChris Lattner 
232b9c1b51eSKate Stone bool MachThread::IsUserReady() {
233c235ac76SGreg Clayton   if (m_basic_info.run_state == 0)
234cdc7322bSGreg Clayton     GetBasicInfo();
2351b946bf6SGreg Clayton 
236b9c1b51eSKate Stone   switch (m_basic_info.run_state) {
2371b946bf6SGreg Clayton   default:
2381b946bf6SGreg Clayton   case TH_STATE_UNINTERRUPTIBLE:
2391b946bf6SGreg Clayton     break;
2401b946bf6SGreg Clayton 
2411b946bf6SGreg Clayton   case TH_STATE_RUNNING:
2421b946bf6SGreg Clayton   case TH_STATE_STOPPED:
2431b946bf6SGreg Clayton   case TH_STATE_WAITING:
2441b946bf6SGreg Clayton   case TH_STATE_HALTED:
2451b946bf6SGreg Clayton     return true;
2461b946bf6SGreg Clayton   }
2471b946bf6SGreg Clayton   return false;
2481b946bf6SGreg Clayton }
2491b946bf6SGreg Clayton 
250b9c1b51eSKate Stone struct thread_basic_info *MachThread::GetBasicInfo() {
2511c73911dSJason Molenda   if (MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info))
252c235ac76SGreg Clayton     return &m_basic_info;
2531b946bf6SGreg Clayton   return NULL;
2541b946bf6SGreg Clayton }
2551b946bf6SGreg Clayton 
256b9c1b51eSKate Stone bool MachThread::GetBasicInfo(thread_t thread,
257b9c1b51eSKate Stone                               struct thread_basic_info *basicInfoPtr) {
258b9c1b51eSKate Stone   if (MachPortNumberIsValid(thread)) {
25930fdc8d8SChris Lattner     unsigned int info_count = THREAD_BASIC_INFO_COUNT;
260b9c1b51eSKate Stone     kern_return_t err = ::thread_info(thread, THREAD_BASIC_INFO,
261b9c1b51eSKate Stone                                       (thread_info_t)basicInfoPtr, &info_count);
26230fdc8d8SChris Lattner     if (err == KERN_SUCCESS)
26330fdc8d8SChris Lattner       return true;
26430fdc8d8SChris Lattner   }
26530fdc8d8SChris Lattner   ::memset(basicInfoPtr, 0, sizeof(struct thread_basic_info));
26630fdc8d8SChris Lattner   return false;
26730fdc8d8SChris Lattner }
26830fdc8d8SChris Lattner 
269b9c1b51eSKate Stone bool MachThread::ThreadIDIsValid(uint64_t thread) { return thread != 0; }
27030fdc8d8SChris Lattner 
271b9c1b51eSKate Stone bool MachThread::MachPortNumberIsValid(thread_t thread) {
27230fdc8d8SChris Lattner   return thread != THREAD_NULL;
27330fdc8d8SChris Lattner }
27430fdc8d8SChris Lattner 
275b9c1b51eSKate Stone bool MachThread::GetRegisterState(int flavor, bool force) {
2763af9ea56SGreg Clayton   return m_arch_ap->GetRegisterState(flavor, force) == KERN_SUCCESS;
27730fdc8d8SChris Lattner }
27830fdc8d8SChris Lattner 
279b9c1b51eSKate Stone bool MachThread::SetRegisterState(int flavor) {
2803af9ea56SGreg Clayton   return m_arch_ap->SetRegisterState(flavor) == KERN_SUCCESS;
28130fdc8d8SChris Lattner }
28230fdc8d8SChris Lattner 
283b9c1b51eSKate Stone uint64_t MachThread::GetPC(uint64_t failValue) {
28430fdc8d8SChris Lattner   // Get program counter
2853af9ea56SGreg Clayton   return m_arch_ap->GetPC(failValue);
28630fdc8d8SChris Lattner }
28730fdc8d8SChris Lattner 
288b9c1b51eSKate Stone bool MachThread::SetPC(uint64_t value) {
28930fdc8d8SChris Lattner   // Set program counter
2903af9ea56SGreg Clayton   return m_arch_ap->SetPC(value);
29130fdc8d8SChris Lattner }
29230fdc8d8SChris Lattner 
293b9c1b51eSKate Stone uint64_t MachThread::GetSP(uint64_t failValue) {
29430fdc8d8SChris Lattner   // Get stack pointer
2953af9ea56SGreg Clayton   return m_arch_ap->GetSP(failValue);
29630fdc8d8SChris Lattner }
29730fdc8d8SChris Lattner 
298b9c1b51eSKate Stone nub_process_t MachThread::ProcessID() const {
29930fdc8d8SChris Lattner   if (m_process)
30030fdc8d8SChris Lattner     return m_process->ProcessID();
30130fdc8d8SChris Lattner   return INVALID_NUB_PROCESS;
30230fdc8d8SChris Lattner }
30330fdc8d8SChris Lattner 
304b9c1b51eSKate Stone void MachThread::Dump(uint32_t index) {
30530fdc8d8SChris Lattner   const char *thread_run_state = NULL;
30630fdc8d8SChris Lattner 
307b9c1b51eSKate Stone   switch (m_basic_info.run_state) {
308b9c1b51eSKate Stone   case TH_STATE_RUNNING:
309b9c1b51eSKate Stone     thread_run_state = "running";
310b9c1b51eSKate Stone     break; // 1 thread is running normally
311b9c1b51eSKate Stone   case TH_STATE_STOPPED:
312b9c1b51eSKate Stone     thread_run_state = "stopped";
313b9c1b51eSKate Stone     break; // 2 thread is stopped
314b9c1b51eSKate Stone   case TH_STATE_WAITING:
315b9c1b51eSKate Stone     thread_run_state = "waiting";
316b9c1b51eSKate Stone     break; // 3 thread is waiting normally
317b9c1b51eSKate Stone   case TH_STATE_UNINTERRUPTIBLE:
318b9c1b51eSKate Stone     thread_run_state = "uninter";
319b9c1b51eSKate Stone     break; // 4 thread is in an uninterruptible wait
320b9c1b51eSKate Stone   case TH_STATE_HALTED:
321b9c1b51eSKate Stone     thread_run_state = "halted ";
322b9c1b51eSKate Stone     break; // 5 thread is halted at a
323b9c1b51eSKate Stone   default:
324b9c1b51eSKate Stone     thread_run_state = "???";
325b9c1b51eSKate Stone     break;
32630fdc8d8SChris Lattner   }
32730fdc8d8SChris Lattner 
328b9c1b51eSKate Stone   DNBLogThreaded(
329b9c1b51eSKate Stone       "[%3u] #%3u tid: 0x%8.8" PRIx64 ", pc: 0x%16.16" PRIx64
330b9c1b51eSKate Stone       ", sp: 0x%16.16" PRIx64
331b9c1b51eSKate Stone       ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: "
332b9c1b51eSKate Stone       "%2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
333b9c1b51eSKate Stone       index, m_seq_id, m_unique_id, GetPC(INVALID_NUB_ADDRESS),
334b9c1b51eSKate Stone       GetSP(INVALID_NUB_ADDRESS), m_basic_info.user_time.seconds,
335b9c1b51eSKate Stone       m_basic_info.user_time.microseconds, m_basic_info.system_time.seconds,
336b9c1b51eSKate Stone       m_basic_info.system_time.microseconds, m_basic_info.cpu_usage,
337b9c1b51eSKate Stone       m_basic_info.policy, m_basic_info.run_state, thread_run_state,
338b9c1b51eSKate Stone       m_basic_info.flags, m_basic_info.suspend_count, m_suspend_count,
339c235ac76SGreg Clayton       m_basic_info.sleep_time);
34030fdc8d8SChris Lattner   // DumpRegisterState(0);
34130fdc8d8SChris Lattner }
34230fdc8d8SChris Lattner 
343b9c1b51eSKate Stone void MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action,
344b9c1b51eSKate Stone                                   bool others_stopped) {
34530fdc8d8SChris Lattner   if (thread_action->addr != INVALID_NUB_ADDRESS)
34630fdc8d8SChris Lattner     SetPC(thread_action->addr);
347cdc7322bSGreg Clayton 
34830fdc8d8SChris Lattner   SetState(thread_action->state);
349b9c1b51eSKate Stone   switch (thread_action->state) {
35030fdc8d8SChris Lattner   case eStateStopped:
35130fdc8d8SChris Lattner   case eStateSuspended:
3529411ddb6SJim Ingham     assert(others_stopped == false);
35330fdc8d8SChris Lattner     Suspend();
35430fdc8d8SChris Lattner     break;
35530fdc8d8SChris Lattner 
35630fdc8d8SChris Lattner   case eStateRunning:
357b1e11121SJim Ingham   case eStateStepping:
3589411ddb6SJim Ingham     Resume(others_stopped);
359b1e11121SJim Ingham     break;
360effe5c95SGreg Clayton   default:
361effe5c95SGreg Clayton     break;
36230fdc8d8SChris Lattner   }
3633af9ea56SGreg Clayton   m_arch_ap->ThreadWillResume();
36430fdc8d8SChris Lattner   m_stop_exception.Clear();
36530fdc8d8SChris Lattner }
36630fdc8d8SChris Lattner 
367b9c1b51eSKate Stone DNBBreakpoint *MachThread::CurrentBreakpoint() {
368d8cf1a11SGreg Clayton   return m_process->Breakpoints().FindByAddress(GetPC());
369c4e411ffSGreg Clayton }
370c4e411ffSGreg Clayton 
371b9c1b51eSKate Stone bool MachThread::ShouldStop(bool &step_more) {
37230fdc8d8SChris Lattner   // See if this thread is at a breakpoint?
373d8cf1a11SGreg Clayton   DNBBreakpoint *bp = CurrentBreakpoint();
37430fdc8d8SChris Lattner 
375b9c1b51eSKate Stone   if (bp) {
37630fdc8d8SChris Lattner     // This thread is sitting at a breakpoint, ask the breakpoint
37730fdc8d8SChris Lattner     // if we should be stopping here.
37830fdc8d8SChris Lattner     return true;
379b9c1b51eSKate Stone   } else {
380b9c1b51eSKate Stone     if (m_arch_ap->StepNotComplete()) {
38130fdc8d8SChris Lattner       step_more = true;
38230fdc8d8SChris Lattner       return false;
38330fdc8d8SChris Lattner     }
38430fdc8d8SChris Lattner     // The thread state is used to let us know what the thread was
38530fdc8d8SChris Lattner     // trying to do. MachThread::ThreadWillResume() will set the
38630fdc8d8SChris Lattner     // thread state to various values depending if the thread was
38730fdc8d8SChris Lattner     // the current thread and if it was to be single stepped, or
38830fdc8d8SChris Lattner     // resumed.
389b9c1b51eSKate Stone     if (GetState() == eStateRunning) {
39030fdc8d8SChris Lattner       // If our state is running, then we should continue as we are in
39130fdc8d8SChris Lattner       // the process of stepping over a breakpoint.
39230fdc8d8SChris Lattner       return false;
393b9c1b51eSKate Stone     } else {
39430fdc8d8SChris Lattner       // Stop if we have any kind of valid exception for this
39530fdc8d8SChris Lattner       // thread.
39630fdc8d8SChris Lattner       if (GetStopException().IsValid())
39730fdc8d8SChris Lattner         return true;
39830fdc8d8SChris Lattner     }
39930fdc8d8SChris Lattner   }
40030fdc8d8SChris Lattner   return false;
40130fdc8d8SChris Lattner }
402b9c1b51eSKate Stone bool MachThread::IsStepping() { return GetState() == eStateStepping; }
40330fdc8d8SChris Lattner 
404b9c1b51eSKate Stone bool MachThread::ThreadDidStop() {
40530fdc8d8SChris Lattner   // This thread has existed prior to resuming under debug nub control,
40630fdc8d8SChris Lattner   // and has just been stopped. Do any cleanup that needs to be done
40730fdc8d8SChris Lattner   // after running.
40830fdc8d8SChris Lattner 
40930fdc8d8SChris Lattner   // The thread state and breakpoint will still have the same values
41030fdc8d8SChris Lattner   // as they had prior to resuming the thread, so it makes it easy to check
41130fdc8d8SChris Lattner   // if we were trying to step a thread, or we tried to resume while being
41230fdc8d8SChris Lattner   // at a breakpoint.
41330fdc8d8SChris Lattner 
41430fdc8d8SChris Lattner   // When this method gets called, the process state is still in the
41530fdc8d8SChris Lattner   // state it was in while running so we can act accordingly.
4163af9ea56SGreg Clayton   m_arch_ap->ThreadDidStop();
41730fdc8d8SChris Lattner 
41830fdc8d8SChris Lattner   // We may have suspended this thread so the primary thread could step
41930fdc8d8SChris Lattner   // without worrying about race conditions, so lets restore our suspend
42030fdc8d8SChris Lattner   // count.
4219411ddb6SJim Ingham   RestoreSuspendCountAfterStop();
42230fdc8d8SChris Lattner 
42330fdc8d8SChris Lattner   // Update the basic information for a thread
4241c73911dSJason Molenda   MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info);
42530fdc8d8SChris Lattner 
426c235ac76SGreg Clayton   if (m_basic_info.suspend_count > 0)
427c4e411ffSGreg Clayton     SetState(eStateSuspended);
428c4e411ffSGreg Clayton   else
429c4e411ffSGreg Clayton     SetState(eStateStopped);
43030fdc8d8SChris Lattner   return true;
43130fdc8d8SChris Lattner }
43230fdc8d8SChris Lattner 
433b9c1b51eSKate Stone bool MachThread::NotifyException(MachException::Data &exc) {
43488c1b77fSJohnny Chen   // Allow the arch specific protocol to process (MachException::Data &)exc
43588c1b77fSJohnny Chen   // first before possible reassignment of m_stop_exception with exc.
43676abb3b5SJohnny Chen   // See also MachThread::GetStopException().
43788c1b77fSJohnny Chen   bool handled = m_arch_ap->NotifyException(exc);
43888c1b77fSJohnny Chen 
439b9c1b51eSKate Stone   if (m_stop_exception.IsValid()) {
44030fdc8d8SChris Lattner     // We may have more than one exception for a thread, but we need to
44130fdc8d8SChris Lattner     // only remember the one that we will say is the reason we stopped.
44230fdc8d8SChris Lattner     // We may have been single stepping and also gotten a signal exception,
44330fdc8d8SChris Lattner     // so just remember the most pertinent one.
44430fdc8d8SChris Lattner     if (m_stop_exception.IsBreakpoint())
44530fdc8d8SChris Lattner       m_stop_exception = exc;
446b9c1b51eSKate Stone   } else {
44730fdc8d8SChris Lattner     m_stop_exception = exc;
44830fdc8d8SChris Lattner   }
44988c1b77fSJohnny Chen 
45030fdc8d8SChris Lattner   return handled;
45130fdc8d8SChris Lattner }
45230fdc8d8SChris Lattner 
453b9c1b51eSKate Stone nub_state_t MachThread::GetState() {
45430fdc8d8SChris Lattner   // If any other threads access this we will need a mutex for it
45530fdc8d8SChris Lattner   PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
45630fdc8d8SChris Lattner   return m_state;
45730fdc8d8SChris Lattner }
45830fdc8d8SChris Lattner 
459b9c1b51eSKate Stone void MachThread::SetState(nub_state_t state) {
46030fdc8d8SChris Lattner   PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
46130fdc8d8SChris Lattner   m_state = state;
462b9c1b51eSKate Stone   DNBLogThreadedIf(LOG_THREAD,
463b9c1b51eSKate Stone                    "MachThread::SetState ( %s ) for tid = 0x%8.8" PRIx64 "",
464b9c1b51eSKate Stone                    DNBStateAsString(state), m_unique_id);
46530fdc8d8SChris Lattner }
46630fdc8d8SChris Lattner 
467b9c1b51eSKate Stone nub_size_t MachThread::GetNumRegistersInSet(nub_size_t regSet) const {
468c235ac76SGreg Clayton   if (regSet < m_num_reg_sets)
4693af9ea56SGreg Clayton     return m_reg_sets[regSet].num_registers;
47030fdc8d8SChris Lattner   return 0;
47130fdc8d8SChris Lattner }
47230fdc8d8SChris Lattner 
473b9c1b51eSKate Stone const char *MachThread::GetRegisterSetName(nub_size_t regSet) const {
474c235ac76SGreg Clayton   if (regSet < m_num_reg_sets)
4753af9ea56SGreg Clayton     return m_reg_sets[regSet].name;
47630fdc8d8SChris Lattner   return NULL;
47730fdc8d8SChris Lattner }
47830fdc8d8SChris Lattner 
479b9c1b51eSKate Stone const DNBRegisterInfo *MachThread::GetRegisterInfo(nub_size_t regSet,
480b9c1b51eSKate Stone                                                    nub_size_t regIndex) const {
481c235ac76SGreg Clayton   if (regSet < m_num_reg_sets)
4823af9ea56SGreg Clayton     if (regIndex < m_reg_sets[regSet].num_registers)
4833af9ea56SGreg Clayton       return &m_reg_sets[regSet].registers[regIndex];
48430fdc8d8SChris Lattner   return NULL;
48530fdc8d8SChris Lattner }
486b9c1b51eSKate Stone void MachThread::DumpRegisterState(nub_size_t regSet) {
487b9c1b51eSKate Stone   if (regSet == REGISTER_SET_ALL) {
488c235ac76SGreg Clayton     for (regSet = 1; regSet < m_num_reg_sets; regSet++)
48930fdc8d8SChris Lattner       DumpRegisterState(regSet);
490b9c1b51eSKate Stone   } else {
491b9c1b51eSKate Stone     if (m_arch_ap->RegisterSetStateIsValid((int)regSet)) {
49230fdc8d8SChris Lattner       const size_t numRegisters = GetNumRegistersInSet(regSet);
493ee2ed525SGreg Clayton       uint32_t regIndex = 0;
49430fdc8d8SChris Lattner       DNBRegisterValueClass reg;
495b9c1b51eSKate Stone       for (regIndex = 0; regIndex < numRegisters; ++regIndex) {
496b9c1b51eSKate Stone         if (m_arch_ap->GetRegisterValue((uint32_t)regSet, regIndex, &reg)) {
49730fdc8d8SChris Lattner           reg.Dump(NULL, NULL);
49830fdc8d8SChris Lattner         }
49930fdc8d8SChris Lattner       }
500b9c1b51eSKate Stone     } else {
501b9c1b51eSKate Stone       DNBLog("%s: registers are not currently valid.",
502b9c1b51eSKate Stone              GetRegisterSetName(regSet));
50330fdc8d8SChris Lattner     }
50430fdc8d8SChris Lattner   }
50530fdc8d8SChris Lattner }
50630fdc8d8SChris Lattner 
50730fdc8d8SChris Lattner const DNBRegisterSetInfo *
508b9c1b51eSKate Stone MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets) const {
509c235ac76SGreg Clayton   *num_reg_sets = m_num_reg_sets;
5103af9ea56SGreg Clayton   return &m_reg_sets[0];
51130fdc8d8SChris Lattner }
51230fdc8d8SChris Lattner 
513b9c1b51eSKate Stone bool MachThread::GetRegisterValue(uint32_t set, uint32_t reg,
514b9c1b51eSKate Stone                                   DNBRegisterValue *value) {
5153af9ea56SGreg Clayton   return m_arch_ap->GetRegisterValue(set, reg, value);
51630fdc8d8SChris Lattner }
51730fdc8d8SChris Lattner 
518b9c1b51eSKate Stone bool MachThread::SetRegisterValue(uint32_t set, uint32_t reg,
519b9c1b51eSKate Stone                                   const DNBRegisterValue *value) {
5203af9ea56SGreg Clayton   return m_arch_ap->SetRegisterValue(set, reg, value);
52130fdc8d8SChris Lattner }
52230fdc8d8SChris Lattner 
523b9c1b51eSKate Stone nub_size_t MachThread::GetRegisterContext(void *buf, nub_size_t buf_len) {
5243af9ea56SGreg Clayton   return m_arch_ap->GetRegisterContext(buf, buf_len);
52530fdc8d8SChris Lattner }
52630fdc8d8SChris Lattner 
527b9c1b51eSKate Stone nub_size_t MachThread::SetRegisterContext(const void *buf, nub_size_t buf_len) {
5283af9ea56SGreg Clayton   return m_arch_ap->SetRegisterContext(buf, buf_len);
52930fdc8d8SChris Lattner }
53030fdc8d8SChris Lattner 
531b9c1b51eSKate Stone uint32_t MachThread::SaveRegisterState() {
532f74cf86bSGreg Clayton   return m_arch_ap->SaveRegisterState();
533f74cf86bSGreg Clayton }
534b9c1b51eSKate Stone bool MachThread::RestoreRegisterState(uint32_t save_id) {
535f74cf86bSGreg Clayton   return m_arch_ap->RestoreRegisterState(save_id);
536f74cf86bSGreg Clayton }
537f74cf86bSGreg Clayton 
538b9c1b51eSKate Stone uint32_t MachThread::EnableHardwareBreakpoint(const DNBBreakpoint *bp) {
53930fdc8d8SChris Lattner   if (bp != NULL && bp->IsBreakpoint())
5403af9ea56SGreg Clayton     return m_arch_ap->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize());
54130fdc8d8SChris Lattner   return INVALID_NUB_HW_INDEX;
54230fdc8d8SChris Lattner }
54330fdc8d8SChris Lattner 
544b9c1b51eSKate Stone uint32_t MachThread::EnableHardwareWatchpoint(const DNBBreakpoint *wp,
545b9c1b51eSKate Stone                                               bool also_set_on_task) {
54630fdc8d8SChris Lattner   if (wp != NULL && wp->IsWatchpoint())
547b9c1b51eSKate Stone     return m_arch_ap->EnableHardwareWatchpoint(
548b9c1b51eSKate Stone         wp->Address(), wp->ByteSize(), wp->WatchpointRead(),
549b9c1b51eSKate Stone         wp->WatchpointWrite(), also_set_on_task);
55030fdc8d8SChris Lattner   return INVALID_NUB_HW_INDEX;
55130fdc8d8SChris Lattner }
55230fdc8d8SChris Lattner 
553b9c1b51eSKate Stone bool MachThread::RollbackTransForHWP() {
55484707560SJohnny Chen   return m_arch_ap->RollbackTransForHWP();
55584707560SJohnny Chen }
55684707560SJohnny Chen 
557b9c1b51eSKate Stone bool MachThread::FinishTransForHWP() { return m_arch_ap->FinishTransForHWP(); }
55884707560SJohnny Chen 
559b9c1b51eSKate Stone bool MachThread::DisableHardwareBreakpoint(const DNBBreakpoint *bp) {
56030fdc8d8SChris Lattner   if (bp != NULL && bp->IsHardware())
5613af9ea56SGreg Clayton     return m_arch_ap->DisableHardwareBreakpoint(bp->GetHardwareIndex());
56230fdc8d8SChris Lattner   return false;
56330fdc8d8SChris Lattner }
56430fdc8d8SChris Lattner 
565b9c1b51eSKate Stone bool MachThread::DisableHardwareWatchpoint(const DNBBreakpoint *wp,
566b9c1b51eSKate Stone                                            bool also_set_on_task) {
56730fdc8d8SChris Lattner   if (wp != NULL && wp->IsHardware())
568b9c1b51eSKate Stone     return m_arch_ap->DisableHardwareWatchpoint(wp->GetHardwareIndex(),
569b9c1b51eSKate Stone                                                 also_set_on_task);
57030fdc8d8SChris Lattner   return false;
57130fdc8d8SChris Lattner }
57230fdc8d8SChris Lattner 
573b9c1b51eSKate Stone uint32_t MachThread::NumSupportedHardwareWatchpoints() const {
57464637205SJohnny Chen   return m_arch_ap->NumSupportedHardwareWatchpoints();
57564637205SJohnny Chen }
57664637205SJohnny Chen 
577b9c1b51eSKate Stone bool MachThread::GetIdentifierInfo() {
578b9c1b51eSKate Stone   // Don't try to get the thread info once and cache it for the life of the
579b9c1b51eSKate Stone   // thread.  It changes over time, for instance
580b9c1b51eSKate Stone   // if the thread name changes, then the thread_handle also changes...  So you
581b9c1b51eSKate Stone   // have to refetch it every time.
58230fdc8d8SChris Lattner   mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
583b9c1b51eSKate Stone   kern_return_t kret = ::thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
584b9c1b51eSKate Stone                                      (thread_info_t)&m_ident_info, &count);
585f2c6ccf0SJim Ingham   return kret == KERN_SUCCESS;
58630fdc8d8SChris Lattner 
58730fdc8d8SChris Lattner   return false;
58830fdc8d8SChris Lattner }
58930fdc8d8SChris Lattner 
590b9c1b51eSKate Stone const char *MachThread::GetName() {
591b9c1b51eSKate Stone   if (GetIdentifierInfo()) {
592b9c1b51eSKate Stone     int len = ::proc_pidinfo(m_process->ProcessID(), PROC_PIDTHREADINFO,
593b9c1b51eSKate Stone                              m_ident_info.thread_handle, &m_proc_threadinfo,
594b9c1b51eSKate Stone                              sizeof(m_proc_threadinfo));
59530fdc8d8SChris Lattner 
59630fdc8d8SChris Lattner     if (len && m_proc_threadinfo.pth_name[0])
59730fdc8d8SChris Lattner       return m_proc_threadinfo.pth_name;
59830fdc8d8SChris Lattner   }
59930fdc8d8SChris Lattner   return NULL;
60030fdc8d8SChris Lattner }
60130fdc8d8SChris Lattner 
6021c73911dSJason Molenda uint64_t
603b9c1b51eSKate Stone MachThread::GetGloballyUniqueThreadIDForMachPortID(thread_t mach_port_id) {
6041c73911dSJason Molenda   kern_return_t kr;
6051c73911dSJason Molenda   thread_identifier_info_data_t tident;
6061c73911dSJason Molenda   mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
607b9c1b51eSKate Stone   kr = thread_info(mach_port_id, THREAD_IDENTIFIER_INFO, (thread_info_t)&tident,
608b9c1b51eSKate Stone                    &tident_count);
609b9c1b51eSKate Stone   if (kr != KERN_SUCCESS) {
6101c73911dSJason Molenda     return mach_port_id;
6111c73911dSJason Molenda   }
6121c73911dSJason Molenda   return tident.thread_id;
6131c73911dSJason Molenda }
614705b1809SJason Molenda 
615b9c1b51eSKate Stone nub_addr_t MachThread::GetPThreadT() {
616705b1809SJason Molenda   nub_addr_t pthread_t_value = INVALID_NUB_ADDRESS;
617b9c1b51eSKate Stone   if (MachPortNumberIsValid(m_mach_port_number)) {
618705b1809SJason Molenda     kern_return_t kr;
619705b1809SJason Molenda     thread_identifier_info_data_t tident;
620705b1809SJason Molenda     mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
621705b1809SJason Molenda     kr = thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
622705b1809SJason Molenda                      (thread_info_t)&tident, &tident_count);
623b9c1b51eSKate Stone     if (kr == KERN_SUCCESS) {
624705b1809SJason Molenda       // Dereference thread_handle to get the pthread_t value for this thread.
625b9c1b51eSKate Stone       if (m_is_64_bit) {
626705b1809SJason Molenda         uint64_t addr;
627b9c1b51eSKate Stone         if (m_process->ReadMemory(tident.thread_handle, 8, &addr) == 8) {
628b9c1b51eSKate Stone           if (addr != 0) {
629705b1809SJason Molenda             pthread_t_value = addr;
630705b1809SJason Molenda           }
631705b1809SJason Molenda         }
632b9c1b51eSKate Stone       } else {
633705b1809SJason Molenda         uint32_t addr;
634b9c1b51eSKate Stone         if (m_process->ReadMemory(tident.thread_handle, 4, &addr) == 4) {
635b9c1b51eSKate Stone           if (addr != 0) {
636705b1809SJason Molenda             pthread_t_value = addr;
637705b1809SJason Molenda           }
638705b1809SJason Molenda         }
639705b1809SJason Molenda       }
640705b1809SJason Molenda     }
641705b1809SJason Molenda   }
642705b1809SJason Molenda   return pthread_t_value;
643705b1809SJason Molenda }
644705b1809SJason Molenda 
645705b1809SJason Molenda // Return this thread's TSD (Thread Specific Data) address.
646705b1809SJason Molenda // This is computed based on this thread's pthread_t value.
647705b1809SJason Molenda //
648705b1809SJason Molenda // We compute the TSD from the pthread_t by one of two methods.
649705b1809SJason Molenda //
650b9c1b51eSKate Stone // If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we
651b9c1b51eSKate Stone // add to
652705b1809SJason Molenda // the pthread_t to get the TSD base address.
653705b1809SJason Molenda //
654b9c1b51eSKate Stone // Else we read a pointer from memory at pthread_t +
655b9c1b51eSKate Stone // plo_pthread_tsd_base_address_offset and
656705b1809SJason Molenda // that gives us the TSD address.
657705b1809SJason Molenda //
658b9c1b51eSKate Stone // These plo_pthread_tsd_base values must be read out of libpthread by lldb &
659b9c1b51eSKate Stone // provided to debugserver.
660705b1809SJason Molenda 
661705b1809SJason Molenda nub_addr_t
662b9c1b51eSKate Stone MachThread::GetTSDAddressForThread(uint64_t plo_pthread_tsd_base_address_offset,
663b9c1b51eSKate Stone                                    uint64_t plo_pthread_tsd_base_offset,
664b9c1b51eSKate Stone                                    uint64_t plo_pthread_tsd_entry_size) {
665705b1809SJason Molenda   nub_addr_t tsd_addr = INVALID_NUB_ADDRESS;
666705b1809SJason Molenda   nub_addr_t pthread_t_value = GetPThreadT();
667b9c1b51eSKate Stone   if (plo_pthread_tsd_base_offset != 0 &&
668b9c1b51eSKate Stone       plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS) {
669705b1809SJason Molenda     tsd_addr = pthread_t_value + plo_pthread_tsd_base_offset;
670b9c1b51eSKate Stone   } else {
671b9c1b51eSKate Stone     if (plo_pthread_tsd_entry_size == 4) {
672705b1809SJason Molenda       uint32_t addr = 0;
673b9c1b51eSKate Stone       if (m_process->ReadMemory(pthread_t_value +
674b9c1b51eSKate Stone                                     plo_pthread_tsd_base_address_offset,
675b9c1b51eSKate Stone                                 4, &addr) == 4) {
676b9c1b51eSKate Stone         if (addr != 0) {
677705b1809SJason Molenda           tsd_addr = addr;
678705b1809SJason Molenda         }
679705b1809SJason Molenda       }
680705b1809SJason Molenda     }
681b9c1b51eSKate Stone     if (plo_pthread_tsd_entry_size == 4) {
682705b1809SJason Molenda       uint64_t addr = 0;
683b9c1b51eSKate Stone       if (m_process->ReadMemory(pthread_t_value +
684b9c1b51eSKate Stone                                     plo_pthread_tsd_base_address_offset,
685b9c1b51eSKate Stone                                 8, &addr) == 8) {
686b9c1b51eSKate Stone         if (addr != 0) {
687705b1809SJason Molenda           tsd_addr = addr;
688705b1809SJason Molenda         }
689705b1809SJason Molenda       }
690705b1809SJason Molenda     }
691705b1809SJason Molenda   }
692705b1809SJason Molenda   return tsd_addr;
693705b1809SJason Molenda }
694705b1809SJason Molenda 
695b9c1b51eSKate Stone nub_addr_t MachThread::GetDispatchQueueT() {
696705b1809SJason Molenda   nub_addr_t dispatch_queue_t_value = INVALID_NUB_ADDRESS;
697b9c1b51eSKate Stone   if (MachPortNumberIsValid(m_mach_port_number)) {
698705b1809SJason Molenda     kern_return_t kr;
699705b1809SJason Molenda     thread_identifier_info_data_t tident;
700705b1809SJason Molenda     mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
701705b1809SJason Molenda     kr = thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
702705b1809SJason Molenda                      (thread_info_t)&tident, &tident_count);
703b9c1b51eSKate Stone     if (kr == KERN_SUCCESS && tident.dispatch_qaddr != 0 &&
704b9c1b51eSKate Stone         tident.dispatch_qaddr != INVALID_NUB_ADDRESS) {
705b9c1b51eSKate Stone       // Dereference dispatch_qaddr to get the dispatch_queue_t value for this
706b9c1b51eSKate Stone       // thread's queue, if any.
707b9c1b51eSKate Stone       if (m_is_64_bit) {
708705b1809SJason Molenda         uint64_t addr;
709b9c1b51eSKate Stone         if (m_process->ReadMemory(tident.dispatch_qaddr, 8, &addr) == 8) {
710705b1809SJason Molenda           if (addr != 0)
711705b1809SJason Molenda             dispatch_queue_t_value = addr;
712705b1809SJason Molenda         }
713b9c1b51eSKate Stone       } else {
714705b1809SJason Molenda         uint32_t addr;
715b9c1b51eSKate Stone         if (m_process->ReadMemory(tident.dispatch_qaddr, 4, &addr) == 4) {
716705b1809SJason Molenda           if (addr != 0)
717705b1809SJason Molenda             dispatch_queue_t_value = addr;
718705b1809SJason Molenda         }
719705b1809SJason Molenda       }
720705b1809SJason Molenda     }
721705b1809SJason Molenda   }
722705b1809SJason Molenda   return dispatch_queue_t_value;
723705b1809SJason Molenda }
724705b1809SJason Molenda 
725b9c1b51eSKate Stone ThreadInfo::QoS MachThread::GetRequestedQoS(nub_addr_t tsd,
726b9c1b51eSKate Stone                                             uint64_t dti_qos_class_index) {
727705b1809SJason Molenda   ThreadInfo::QoS qos_value;
728b9c1b51eSKate Stone   if (MachPortNumberIsValid(m_mach_port_number) &&
729b9c1b51eSKate Stone       m_pthread_qos_class_decode != nullptr) {
730705b1809SJason Molenda     uint64_t pthread_priority_value = 0;
731b9c1b51eSKate Stone     if (m_is_64_bit) {
732705b1809SJason Molenda       uint64_t pri;
733b9c1b51eSKate Stone       if (m_process->ReadMemory(tsd + (dti_qos_class_index * 8), 8, &pri) ==
734b9c1b51eSKate Stone           8) {
735705b1809SJason Molenda         pthread_priority_value = pri;
736705b1809SJason Molenda       }
737b9c1b51eSKate Stone     } else {
738705b1809SJason Molenda       uint32_t pri;
739b9c1b51eSKate Stone       if (m_process->ReadMemory(tsd + (dti_qos_class_index * 4), 4, &pri) ==
740b9c1b51eSKate Stone           4) {
741705b1809SJason Molenda         pthread_priority_value = pri;
742705b1809SJason Molenda       }
743705b1809SJason Molenda     }
744705b1809SJason Molenda 
745b9c1b51eSKate Stone     uint32_t requested_qos =
746b9c1b51eSKate Stone         m_pthread_qos_class_decode(pthread_priority_value, NULL, NULL);
747705b1809SJason Molenda 
748b9c1b51eSKate Stone     switch (requested_qos) {
749705b1809SJason Molenda     // These constants from <pthread/qos.h>
750705b1809SJason Molenda     case 0x21:
751705b1809SJason Molenda       qos_value.enum_value = requested_qos;
752705b1809SJason Molenda       qos_value.constant_name = "QOS_CLASS_USER_INTERACTIVE";
753705b1809SJason Molenda       qos_value.printable_name = "User Interactive";
754705b1809SJason Molenda       break;
755705b1809SJason Molenda     case 0x19:
756705b1809SJason Molenda       qos_value.enum_value = requested_qos;
757705b1809SJason Molenda       qos_value.constant_name = "QOS_CLASS_USER_INITIATED";
758705b1809SJason Molenda       qos_value.printable_name = "User Initiated";
759705b1809SJason Molenda       break;
760705b1809SJason Molenda     case 0x15:
761705b1809SJason Molenda       qos_value.enum_value = requested_qos;
762705b1809SJason Molenda       qos_value.constant_name = "QOS_CLASS_DEFAULT";
763705b1809SJason Molenda       qos_value.printable_name = "Default";
764705b1809SJason Molenda       break;
765705b1809SJason Molenda     case 0x11:
766705b1809SJason Molenda       qos_value.enum_value = requested_qos;
767705b1809SJason Molenda       qos_value.constant_name = "QOS_CLASS_UTILITY";
768705b1809SJason Molenda       qos_value.printable_name = "Utility";
769705b1809SJason Molenda       break;
770705b1809SJason Molenda     case 0x09:
771705b1809SJason Molenda       qos_value.enum_value = requested_qos;
772705b1809SJason Molenda       qos_value.constant_name = "QOS_CLASS_BACKGROUND";
773705b1809SJason Molenda       qos_value.printable_name = "Background";
774705b1809SJason Molenda       break;
775705b1809SJason Molenda     case 0x00:
776705b1809SJason Molenda       qos_value.enum_value = requested_qos;
777705b1809SJason Molenda       qos_value.constant_name = "QOS_CLASS_UNSPECIFIED";
778705b1809SJason Molenda       qos_value.printable_name = "Unspecified";
779705b1809SJason Molenda       break;
780705b1809SJason Molenda     }
781705b1809SJason Molenda   }
782705b1809SJason Molenda   return qos_value;
783705b1809SJason Molenda }
784