1//===-- MachThread.cpp ------------------------------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// Created by Greg Clayton on 6/19/07.
10//
11//===----------------------------------------------------------------------===//
12
13#include "MachThread.h"
14#include "DNB.h"
15#include "DNBLog.h"
16#include "MachProcess.h"
17#include "ThreadInfo.h"
18#include <cinttypes>
19#include <dlfcn.h>
20#include <mach/thread_policy.h>
21
22static uint32_t GetSequenceID() {
23 static uint32_t g_nextID = 0;
24 return ++g_nextID;
25}
26
27MachThread::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;
40
41 m_pthread_qos_class_decode =
42 (unsigned int (*)(unsigned long, int *, unsigned long *))dlsym(
43 RTLD_DEFAULT, name: "_pthread_qos_class_decode");
44
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
47 // already suspended
48 GetBasicInfo();
49 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
50 "MachThread::MachThread ( process = %p, tid = 0x%8.8" PRIx64
51 ", seq_id = %u )",
52 static_cast<void *>(&m_process), m_unique_id, m_seq_id);
53}
54
55MachThread::~MachThread() {
56 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
57 "MachThread::~MachThread() for tid = 0x%8.8" PRIx64 " (%u)",
58 m_unique_id, m_seq_id);
59}
60
61void MachThread::Suspend() {
62 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
63 __FUNCTION__);
64 if (MachPortNumberIsValid(m_mach_port_number)) {
65 DNBError err(::thread_suspend(m_mach_port_number), DNBError::MachKernel);
66 if (err.Success())
67 m_suspend_count++;
68 if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
69 err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number);
70 }
71}
72
73void MachThread::Resume(bool others_stopped) {
74 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
75 __FUNCTION__);
76 if (MachPortNumberIsValid(m_mach_port_number)) {
77 SetSuspendCountBeforeResume(others_stopped);
78 }
79}
80
81bool MachThread::SetSuspendCountBeforeResume(bool others_stopped) {
82 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
83 __FUNCTION__);
84 DNBError err;
85 if (!MachPortNumberIsValid(m_mach_port_number))
86 return false;
87
88 integer_t times_to_resume;
89
90 if (others_stopped) {
91 if (GetBasicInfo()) {
92 times_to_resume = m_basic_info.suspend_count;
93 m_suspend_count = -(times_to_resume - m_suspend_count);
94 } else
95 times_to_resume = 0;
96 } else {
97 times_to_resume = m_suspend_count;
98 m_suspend_count = 0;
99 }
100
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);
106 if (err.Success())
107 --times_to_resume;
108 else {
109 if (GetBasicInfo())
110 times_to_resume = m_basic_info.suspend_count;
111 else
112 times_to_resume = 0;
113 }
114 }
115 }
116 return true;
117}
118
119bool MachThread::RestoreSuspendCountAfterStop() {
120 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
121 __FUNCTION__);
122 DNBError err;
123 if (!MachPortNumberIsValid(m_mach_port_number))
124 return false;
125
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);
131 if (err.Success())
132 --m_suspend_count;
133 else {
134 if (GetBasicInfo())
135 m_suspend_count = m_basic_info.suspend_count;
136 else
137 m_suspend_count = 0;
138 return false; // ???
139 }
140 }
141 } else if (m_suspend_count < 0) {
142 while (m_suspend_count < 0) {
143 err = ::thread_suspend(m_mach_port_number);
144 if (err.Success())
145 ++m_suspend_count;
146 if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) {
147 err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")",
148 m_mach_port_number);
149 return false;
150 }
151 }
152 }
153 return true;
154}
155
156const char *MachThread::GetBasicInfoAsString() const {
157 static char g_basic_info_string[1024];
158 struct thread_basic_info basicInfo;
159
160 if (GetBasicInfo(m_mach_port_number, &basicInfo)) {
161
162 // char run_state_str[32];
163 // size_t run_state_str_size = sizeof(run_state_str);
164 // switch (basicInfo.run_state)
165 // {
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; // ???
178 // }
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);
187
188 return g_basic_info_string;
189 }
190 return NULL;
191}
192
193// Finds the Mach port number for a given thread in the inferior process' port
194// namespace.
195thread_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();
203
204 kern_return_t kret =
205 ::mach_port_names(task, &names, &ncount, &types, &tcount);
206 if (kret == KERN_SUCCESS) {
207
208 for (i = 0; i < ncount; i++) {
209 mach_port_t my_name;
210 mach_msg_type_name_t my_type;
211
212 kret = ::mach_port_extract_right(task, names[i], MACH_MSG_TYPE_COPY_SEND,
213 &my_name, &my_type);
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];
218 break;
219 }
220 }
221 }
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));
227 }
228 return inferior_tid;
229}
230
231bool MachThread::IsUserReady() {
232 if (m_basic_info.run_state == 0)
233 GetBasicInfo();
234
235 switch (m_basic_info.run_state) {
236 default:
237 case TH_STATE_UNINTERRUPTIBLE:
238 break;
239
240 case TH_STATE_RUNNING:
241 case TH_STATE_STOPPED:
242 case TH_STATE_WAITING:
243 case TH_STATE_HALTED:
244 return true;
245 }
246 return GetPC(failValue: 0) != 0;
247}
248
249struct thread_basic_info *MachThread::GetBasicInfo() {
250 if (MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info))
251 return &m_basic_info;
252 return NULL;
253}
254
255bool MachThread::GetBasicInfo(thread_t thread,
256 struct thread_basic_info *basicInfoPtr) {
257 if (MachPortNumberIsValid(thread: 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)
262 return true;
263 }
264 ::memset(basicInfoPtr, 0, sizeof(struct thread_basic_info));
265 return false;
266}
267
268bool MachThread::ThreadIDIsValid(uint64_t thread) { return thread != 0; }
269
270bool MachThread::MachPortNumberIsValid(thread_t thread) {
271 return thread != THREAD_NULL;
272}
273
274bool MachThread::GetRegisterState(int flavor, bool force) {
275 return m_arch_up->GetRegisterState(flavor, force) == KERN_SUCCESS;
276}
277
278bool MachThread::SetRegisterState(int flavor) {
279 return m_arch_up->SetRegisterState(flavor) == KERN_SUCCESS;
280}
281
282uint64_t MachThread::GetPC(uint64_t failValue) {
283 // Get program counter
284 return m_arch_up->GetPC(failValue);
285}
286
287bool MachThread::SetPC(uint64_t value) {
288 // Set program counter
289 return m_arch_up->SetPC(value);
290}
291
292uint64_t MachThread::GetSP(uint64_t failValue) {
293 // Get stack pointer
294 return m_arch_up->GetSP(failValue);
295}
296
297nub_process_t MachThread::ProcessID() const {
298 if (m_process)
299 return m_process->ProcessID();
300 return INVALID_NUB_PROCESS;
301}
302
303void MachThread::Dump(uint32_t index) {
304 const char *thread_run_state = NULL;
305
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
322 default:
323 thread_run_state = "???";
324 break;
325 }
326
327 DNBLogThreaded(
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);
340}
341
342void MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action,
343 bool others_stopped) {
344 if (thread_action->addr != INVALID_NUB_ADDRESS)
345 SetPC(thread_action->addr);
346
347 SetState(thread_action->state);
348 switch (thread_action->state) {
349 case eStateStopped:
350 case eStateSuspended:
351 assert(others_stopped == false);
352 Suspend();
353 break;
354
355 case eStateRunning:
356 case eStateStepping:
357 Resume(others_stopped);
358 break;
359 default:
360 break;
361 }
362 m_arch_up->ThreadWillResume();
363 m_stop_exception.Clear();
364}
365
366DNBBreakpoint *MachThread::CurrentBreakpoint() {
367 return m_process->Breakpoints().FindByAddress(GetPC());
368}
369
370bool MachThread::ShouldStop(bool &step_more) {
371 // See if this thread is at a breakpoint?
372 DNBBreakpoint *bp = CurrentBreakpoint();
373
374 if (bp) {
375 // This thread is sitting at a breakpoint, ask the breakpoint
376 // if we should be stopping here.
377 return true;
378 } else {
379 if (m_arch_up->StepNotComplete()) {
380 step_more = true;
381 return false;
382 }
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
387 // resumed.
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.
391 return false;
392 } else {
393 // Stop if we have any kind of valid exception for this
394 // thread.
395 if (GetStopException().IsValid())
396 return true;
397 }
398 }
399 return false;
400}
401bool MachThread::IsStepping() { return GetState() == eStateStepping; }
402
403bool 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
406 // after running.
407
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
411 // at a breakpoint.
412
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();
416
417 // We may have suspended this thread so the primary thread could step
418 // without worrying about race conditions, so lets restore our suspend
419 // count.
420 RestoreSuspendCountAfterStop();
421
422 // Update the basic information for a thread
423 MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info);
424
425 if (m_basic_info.suspend_count > 0)
426 SetState(eStateSuspended);
427 else
428 SetState(eStateStopped);
429 return true;
430}
431
432bool 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);
437
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;
445 } else {
446 m_stop_exception = exc;
447 }
448
449 return handled;
450}
451
452nub_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);
455 return m_state;
456}
457
458void MachThread::SetState(nub_state_t state) {
459 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
460 m_state = state;
461 DNBLogThreadedIf(LOG_THREAD,
462 "MachThread::SetState ( %s ) for tid = 0x%8.8" PRIx64 "",
463 DNBStateAsString(state), m_unique_id);
464}
465
466nub_size_t MachThread::GetNumRegistersInSet(nub_size_t regSet) const {
467 if (regSet < m_num_reg_sets)
468 return m_reg_sets[regSet].num_registers;
469 return 0;
470}
471
472const char *MachThread::GetRegisterSetName(nub_size_t regSet) const {
473 if (regSet < m_num_reg_sets)
474 return m_reg_sets[regSet].name;
475 return NULL;
476}
477
478const 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];
483 return NULL;
484}
485void 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);
489 } else {
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, &reg)) {
496 reg.Dump(NULL, NULL);
497 }
498 }
499 } else {
500 DNBLog("%s: registers are not currently valid.",
501 GetRegisterSetName(regSet));
502 }
503 }
504}
505
506const DNBRegisterSetInfo *
507MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets) const {
508 *num_reg_sets = m_num_reg_sets;
509 return &m_reg_sets[0];
510}
511
512bool MachThread::GetRegisterValue(uint32_t set, uint32_t reg,
513 DNBRegisterValue *value) {
514 return m_arch_up->GetRegisterValue(set, reg, value);
515}
516
517bool MachThread::SetRegisterValue(uint32_t set, uint32_t reg,
518 const DNBRegisterValue *value) {
519 return m_arch_up->SetRegisterValue(set, reg, value);
520}
521
522nub_size_t MachThread::GetRegisterContext(void *buf, nub_size_t buf_len) {
523 return m_arch_up->GetRegisterContext(buf, buf_len);
524}
525
526nub_size_t MachThread::SetRegisterContext(const void *buf, nub_size_t buf_len) {
527 return m_arch_up->SetRegisterContext(buf, buf_len);
528}
529
530uint32_t MachThread::SaveRegisterState() {
531 return m_arch_up->SaveRegisterState();
532}
533bool MachThread::RestoreRegisterState(uint32_t save_id) {
534 return m_arch_up->RestoreRegisterState(save_id);
535}
536
537uint32_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(),
541 also_set_on_task);
542 }
543 return INVALID_NUB_HW_INDEX;
544}
545
546uint32_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;
553}
554
555bool MachThread::RollbackTransForHWP() {
556 return m_arch_up->RollbackTransForHWP();
557}
558
559bool MachThread::FinishTransForHWP() { return m_arch_up->FinishTransForHWP(); }
560
561bool 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(),
565 also_set_on_task);
566 }
567 return false;
568}
569
570bool 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(),
574 also_set_on_task);
575 return false;
576}
577
578uint32_t MachThread::NumSupportedHardwareWatchpoints() const {
579 return m_arch_up->NumSupportedHardwareWatchpoints();
580}
581
582bool 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;
591
592 return false;
593}
594
595const 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));
600
601 if (len && m_proc_threadinfo.pth_name[0])
602 return m_proc_threadinfo.pth_name;
603 }
604 return NULL;
605}
606
607uint64_t
608MachThread::GetGloballyUniqueThreadIDForMachPortID(thread_t mach_port_id) {
609 kern_return_t kr;
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,
613 &tident_count);
614 if (kr != KERN_SUCCESS) {
615 return mach_port_id;
616 }
617 return tident.thread_id;
618}
619
620nub_addr_t MachThread::GetPThreadT() {
621 nub_addr_t pthread_t_value = INVALID_NUB_ADDRESS;
622 if (MachPortNumberIsValid(m_mach_port_number)) {
623 kern_return_t kr;
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.
630 if (m_is_64_bit) {
631 uint64_t addr;
632 if (m_process->ReadMemory(tident.thread_handle, 8, &addr) == 8) {
633 if (addr != 0) {
634 pthread_t_value = addr;
635 }
636 }
637 } else {
638 uint32_t addr;
639 if (m_process->ReadMemory(tident.thread_handle, 4, &addr) == 4) {
640 if (addr != 0) {
641 pthread_t_value = addr;
642 }
643 }
644 }
645 }
646 }
647 return pthread_t_value;
648}
649
650// Return this thread's TSD (Thread Specific Data) address.
651// This is computed based on this thread's pthread_t value.
652//
653// We compute the TSD from the pthread_t by one of two methods.
654//
655// If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we
656// add to
657// the pthread_t to get the TSD base address.
658//
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.
662//
663// These plo_pthread_tsd_base values must be read out of libpthread by lldb &
664// provided to debugserver.
665
666nub_addr_t
667MachThread::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;
675 } else {
676 if (plo_pthread_tsd_entry_size == 4) {
677 uint32_t addr = 0;
678 if (m_process->ReadMemory(pthread_t_value +
679 plo_pthread_tsd_base_address_offset,
680 4, &addr) == 4) {
681 if (addr != 0) {
682 tsd_addr = addr;
683 }
684 }
685 }
686 if (plo_pthread_tsd_entry_size == 4) {
687 uint64_t addr = 0;
688 if (m_process->ReadMemory(pthread_t_value +
689 plo_pthread_tsd_base_address_offset,
690 8, &addr) == 8) {
691 if (addr != 0) {
692 tsd_addr = addr;
693 }
694 }
695 }
696 }
697 return tsd_addr;
698}
699
700nub_addr_t MachThread::GetDispatchQueueT() {
701 nub_addr_t dispatch_queue_t_value = INVALID_NUB_ADDRESS;
702 if (MachPortNumberIsValid(m_mach_port_number)) {
703 kern_return_t kr;
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.
712 if (m_is_64_bit) {
713 uint64_t addr;
714 if (m_process->ReadMemory(tident.dispatch_qaddr, 8, &addr) == 8) {
715 if (addr != 0)
716 dispatch_queue_t_value = addr;
717 }
718 } else {
719 uint32_t addr;
720 if (m_process->ReadMemory(tident.dispatch_qaddr, 4, &addr) == 4) {
721 if (addr != 0)
722 dispatch_queue_t_value = addr;
723 }
724 }
725 }
726 }
727 return dispatch_queue_t_value;
728}
729
730ThreadInfo::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;
736 if (m_is_64_bit) {
737 uint64_t pri;
738 if (m_process->ReadMemory(tsd + (dti_qos_class_index * 8), 8, &pri) ==
739 8) {
740 pthread_priority_value = pri;
741 }
742 } else {
743 uint32_t pri;
744 if (m_process->ReadMemory(tsd + (dti_qos_class_index * 4), 4, &pri) ==
745 4) {
746 pthread_priority_value = pri;
747 }
748 }
749
750 uint32_t requested_qos =
751 m_pthread_qos_class_decode(pthread_priority_value, NULL, NULL);
752
753 switch (requested_qos) {
754 // These constants from <pthread/qos.h>
755 case 0x21:
756 qos_value.enum_value = requested_qos;
757 qos_value.constant_name = "QOS_CLASS_USER_INTERACTIVE";
758 qos_value.printable_name = "User Interactive";
759 break;
760 case 0x19:
761 qos_value.enum_value = requested_qos;
762 qos_value.constant_name = "QOS_CLASS_USER_INITIATED";
763 qos_value.printable_name = "User Initiated";
764 break;
765 case 0x15:
766 qos_value.enum_value = requested_qos;
767 qos_value.constant_name = "QOS_CLASS_DEFAULT";
768 qos_value.printable_name = "Default";
769 break;
770 case 0x11:
771 qos_value.enum_value = requested_qos;
772 qos_value.constant_name = "QOS_CLASS_UTILITY";
773 qos_value.printable_name = "Utility";
774 break;
775 case 0x09:
776 qos_value.enum_value = requested_qos;
777 qos_value.constant_name = "QOS_CLASS_BACKGROUND";
778 qos_value.printable_name = "Background";
779 break;
780 case 0x00:
781 qos_value.enum_value = requested_qos;
782 qos_value.constant_name = "QOS_CLASS_UNSPECIFIED";
783 qos_value.printable_name = "Unspecified";
784 break;
785 }
786 }
787 return qos_value;
788}
789

source code of lldb/tools/debugserver/source/MacOSX/MachThread.cpp