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(), m_suspend_count(0),
32 m_stop_exception(), m_arch_up(DNBArchProtocol::Create(this)),
33 m_reg_sets(NULL), m_num_reg_sets(0), m_extended_info(),
34 m_dispatch_queue_name(), m_is_64_bit(is_64_bit),
35 m_pthread_qos_class_decode(nullptr) {
36 nub_size_t num_reg_sets = 0;
37 m_reg_sets = m_arch_up->GetRegisterSetInfo(&num_reg_sets);
38 m_num_reg_sets = num_reg_sets;
39
40 m_pthread_qos_class_decode =
41 (unsigned int (*)(unsigned long, int *, unsigned long *))dlsym(
42 RTLD_DEFAULT, name: "_pthread_qos_class_decode");
43
44 // Get the thread state so we know if a thread is in a state where we can't
45 // muck with it and also so we get the suspend count correct in case it was
46 // already suspended
47 GetBasicInfo();
48 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
49 "MachThread::MachThread ( process = %p, tid = 0x%8.8" PRIx64
50 ", seq_id = %u )",
51 static_cast<void *>(&m_process), m_unique_id, m_seq_id);
52}
53
54MachThread::~MachThread() {
55 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
56 "MachThread::~MachThread() for tid = 0x%8.8" PRIx64 " (%u)",
57 m_unique_id, m_seq_id);
58}
59
60void MachThread::Suspend() {
61 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
62 __FUNCTION__);
63 if (MachPortNumberIsValid(m_mach_port_number)) {
64 DNBError err(::thread_suspend(m_mach_port_number), DNBError::MachKernel);
65 if (err.Success())
66 m_suspend_count++;
67 if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
68 err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number);
69 }
70}
71
72void MachThread::Resume(bool others_stopped) {
73 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
74 __FUNCTION__);
75 if (MachPortNumberIsValid(m_mach_port_number)) {
76 SetSuspendCountBeforeResume(others_stopped);
77 }
78}
79
80bool MachThread::SetSuspendCountBeforeResume(bool others_stopped) {
81 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
82 __FUNCTION__);
83 DNBError err;
84 if (!MachPortNumberIsValid(m_mach_port_number))
85 return false;
86
87 integer_t times_to_resume;
88
89 if (others_stopped) {
90 if (GetBasicInfo()) {
91 times_to_resume = m_basic_info.suspend_count;
92 m_suspend_count = -(times_to_resume - m_suspend_count);
93 } else
94 times_to_resume = 0;
95 } else {
96 times_to_resume = m_suspend_count;
97 m_suspend_count = 0;
98 }
99
100 if (times_to_resume > 0) {
101 while (times_to_resume > 0) {
102 err = ::thread_resume(m_mach_port_number);
103 if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
104 err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
105 if (err.Success())
106 --times_to_resume;
107 else {
108 if (GetBasicInfo())
109 times_to_resume = m_basic_info.suspend_count;
110 else
111 times_to_resume = 0;
112 }
113 }
114 }
115 return true;
116}
117
118bool MachThread::RestoreSuspendCountAfterStop() {
119 DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
120 __FUNCTION__);
121 DNBError err;
122 if (!MachPortNumberIsValid(m_mach_port_number))
123 return false;
124
125 if (m_suspend_count > 0) {
126 while (m_suspend_count > 0) {
127 err = ::thread_resume(m_mach_port_number);
128 if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
129 err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
130 if (err.Success())
131 --m_suspend_count;
132 else {
133 if (GetBasicInfo())
134 m_suspend_count = m_basic_info.suspend_count;
135 else
136 m_suspend_count = 0;
137 return false; // ???
138 }
139 }
140 } else if (m_suspend_count < 0) {
141 while (m_suspend_count < 0) {
142 err = ::thread_suspend(m_mach_port_number);
143 if (err.Success())
144 ++m_suspend_count;
145 if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) {
146 err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")",
147 m_mach_port_number);
148 return false;
149 }
150 }
151 }
152 return true;
153}
154
155const char *MachThread::GetBasicInfoAsString() const {
156 static char g_basic_info_string[1024];
157 struct thread_basic_info basicInfo;
158
159 if (GetBasicInfo(m_mach_port_number, &basicInfo)) {
160
161 // char run_state_str[32];
162 // size_t run_state_str_size = sizeof(run_state_str);
163 // switch (basicInfo.run_state)
164 // {
165 // case TH_STATE_RUNNING: strlcpy(run_state_str, "running",
166 // run_state_str_size); break;
167 // case TH_STATE_STOPPED: strlcpy(run_state_str, "stopped",
168 // run_state_str_size); break;
169 // case TH_STATE_WAITING: strlcpy(run_state_str, "waiting",
170 // run_state_str_size); break;
171 // case TH_STATE_UNINTERRUPTIBLE: strlcpy(run_state_str,
172 // "uninterruptible", run_state_str_size); break;
173 // case TH_STATE_HALTED: strlcpy(run_state_str, "halted",
174 // run_state_str_size); break;
175 // default: snprintf(run_state_str,
176 // run_state_str_size, "%d", basicInfo.run_state); break; // ???
177 // }
178 float user = (float)basicInfo.user_time.seconds +
179 (float)basicInfo.user_time.microseconds / 1000000.0f;
180 float system = (float)basicInfo.user_time.seconds +
181 (float)basicInfo.user_time.microseconds / 1000000.0f;
182 snprintf(g_basic_info_string, sizeof(g_basic_info_string),
183 "Thread 0x%8.8" PRIx64 ": user=%f system=%f cpu=%d sleep_time=%d",
184 m_unique_id, user, system, basicInfo.cpu_usage,
185 basicInfo.sleep_time);
186
187 return g_basic_info_string;
188 }
189 return NULL;
190}
191
192// Finds the Mach port number for a given thread in the inferior process' port
193// namespace.
194thread_t MachThread::InferiorThreadID() const {
195 mach_msg_type_number_t i;
196 mach_port_name_array_t names;
197 mach_port_type_array_t types;
198 mach_msg_type_number_t ncount, tcount;
199 thread_t inferior_tid = INVALID_NUB_THREAD;
200 task_t my_task = ::mach_task_self();
201 task_t task = m_process->Task().TaskPort();
202
203 kern_return_t kret =
204 ::mach_port_names(task, &names, &ncount, &types, &tcount);
205 if (kret == KERN_SUCCESS) {
206
207 for (i = 0; i < ncount; i++) {
208 mach_port_t my_name;
209 mach_msg_type_name_t my_type;
210
211 kret = ::mach_port_extract_right(task, names[i], MACH_MSG_TYPE_COPY_SEND,
212 &my_name, &my_type);
213 if (kret == KERN_SUCCESS) {
214 ::mach_port_deallocate(my_task, my_name);
215 if (my_name == m_mach_port_number) {
216 inferior_tid = names[i];
217 break;
218 }
219 }
220 }
221 // Free up the names and types
222 ::vm_deallocate(my_task, (vm_address_t)names,
223 ncount * sizeof(mach_port_name_t));
224 ::vm_deallocate(my_task, (vm_address_t)types,
225 tcount * sizeof(mach_port_type_t));
226 }
227 return inferior_tid;
228}
229
230bool MachThread::IsUserReady() {
231 if (m_basic_info.run_state == 0)
232 GetBasicInfo();
233
234 switch (m_basic_info.run_state) {
235 default:
236 case TH_STATE_UNINTERRUPTIBLE:
237 break;
238
239 case TH_STATE_RUNNING:
240 case TH_STATE_STOPPED:
241 case TH_STATE_WAITING:
242 case TH_STATE_HALTED:
243 return true;
244 }
245 return GetPC(failValue: 0) != 0;
246}
247
248struct thread_basic_info *MachThread::GetBasicInfo() {
249 if (MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info))
250 return &m_basic_info;
251 return NULL;
252}
253
254bool MachThread::GetBasicInfo(thread_t thread,
255 struct thread_basic_info *basicInfoPtr) {
256 if (MachPortNumberIsValid(thread: thread)) {
257 mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;
258 kern_return_t err = ::thread_info(thread, THREAD_BASIC_INFO,
259 (thread_info_t)basicInfoPtr, &info_count);
260 if (err == KERN_SUCCESS)
261 return true;
262 }
263 ::memset(basicInfoPtr, 0, sizeof(struct thread_basic_info));
264 return false;
265}
266
267struct thread_extended_info *MachThread::GetExtendedInfo() {
268 if (MachThread::GetExtendedInfo(m_mach_port_number, &m_extended_info))
269 return &m_extended_info;
270 return NULL;
271}
272
273bool MachThread::GetExtendedInfo(thread_t thread,
274 struct thread_extended_info *extendedInfoPtr) {
275 if (MachPortNumberIsValid(thread: thread)) {
276 mach_msg_type_number_t info_count = THREAD_EXTENDED_INFO_COUNT;
277 kern_return_t err =
278 ::thread_info(thread, THREAD_EXTENDED_INFO,
279 (thread_info_t)extendedInfoPtr, &info_count);
280 if (err == KERN_SUCCESS)
281 return true;
282 }
283 ::memset(extendedInfoPtr, 0, sizeof(struct thread_extended_info));
284 return false;
285}
286
287bool MachThread::ThreadIDIsValid(uint64_t thread) { return thread != 0; }
288
289bool MachThread::MachPortNumberIsValid(thread_t thread) {
290 return thread != THREAD_NULL;
291}
292
293bool MachThread::GetRegisterState(int flavor, bool force) {
294 return m_arch_up->GetRegisterState(flavor, force) == KERN_SUCCESS;
295}
296
297bool MachThread::SetRegisterState(int flavor) {
298 return m_arch_up->SetRegisterState(flavor) == KERN_SUCCESS;
299}
300
301uint64_t MachThread::GetPC(uint64_t failValue) {
302 // Get program counter
303 return m_arch_up->GetPC(failValue);
304}
305
306bool MachThread::SetPC(uint64_t value) {
307 // Set program counter
308 return m_arch_up->SetPC(value);
309}
310
311uint64_t MachThread::GetSP(uint64_t failValue) {
312 // Get stack pointer
313 return m_arch_up->GetSP(failValue);
314}
315
316nub_process_t MachThread::ProcessID() const {
317 if (m_process)
318 return m_process->ProcessID();
319 return INVALID_NUB_PROCESS;
320}
321
322void MachThread::Dump(uint32_t index) {
323 const char *thread_run_state = NULL;
324
325 switch (m_basic_info.run_state) {
326 case TH_STATE_RUNNING:
327 thread_run_state = "running";
328 break; // 1 thread is running normally
329 case TH_STATE_STOPPED:
330 thread_run_state = "stopped";
331 break; // 2 thread is stopped
332 case TH_STATE_WAITING:
333 thread_run_state = "waiting";
334 break; // 3 thread is waiting normally
335 case TH_STATE_UNINTERRUPTIBLE:
336 thread_run_state = "uninter";
337 break; // 4 thread is in an uninterruptible wait
338 case TH_STATE_HALTED:
339 thread_run_state = "halted ";
340 break; // 5 thread is halted at a
341 default:
342 thread_run_state = "???";
343 break;
344 }
345
346 DNBLogThreaded(
347 "[%3u] #%3u tid: 0x%8.8" PRIx64 ", pc: 0x%16.16" PRIx64
348 ", sp: 0x%16.16" PRIx64
349 ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: "
350 "%2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
351 index, m_seq_id, m_unique_id, GetPC(INVALID_NUB_ADDRESS),
352 GetSP(INVALID_NUB_ADDRESS), m_basic_info.user_time.seconds,
353 m_basic_info.user_time.microseconds, m_basic_info.system_time.seconds,
354 m_basic_info.system_time.microseconds, m_basic_info.cpu_usage,
355 m_basic_info.policy, m_basic_info.run_state, thread_run_state,
356 m_basic_info.flags, m_basic_info.suspend_count, m_suspend_count,
357 m_basic_info.sleep_time);
358 // DumpRegisterState(0);
359}
360
361void MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action,
362 bool others_stopped) {
363 if (thread_action->addr != INVALID_NUB_ADDRESS)
364 SetPC(thread_action->addr);
365
366 SetState(thread_action->state);
367 switch (thread_action->state) {
368 case eStateStopped:
369 case eStateSuspended:
370 assert(others_stopped == false);
371 Suspend();
372 break;
373
374 case eStateRunning:
375 case eStateStepping:
376 Resume(others_stopped);
377 break;
378 default:
379 break;
380 }
381 m_arch_up->ThreadWillResume();
382 m_stop_exception.Clear();
383}
384
385DNBBreakpoint *MachThread::CurrentBreakpoint() {
386 return m_process->Breakpoints().FindByAddress(GetPC());
387}
388
389bool MachThread::ShouldStop(bool &step_more) {
390 // See if this thread is at a breakpoint?
391 DNBBreakpoint *bp = CurrentBreakpoint();
392
393 if (bp) {
394 // This thread is sitting at a breakpoint, ask the breakpoint
395 // if we should be stopping here.
396 return true;
397 } else {
398 if (m_arch_up->StepNotComplete()) {
399 step_more = true;
400 return false;
401 }
402 // The thread state is used to let us know what the thread was
403 // trying to do. MachThread::ThreadWillResume() will set the
404 // thread state to various values depending if the thread was
405 // the current thread and if it was to be single stepped, or
406 // resumed.
407 if (GetState() == eStateRunning) {
408 // If our state is running, then we should continue as we are in
409 // the process of stepping over a breakpoint.
410 return false;
411 } else {
412 // Stop if we have any kind of valid exception for this
413 // thread.
414 if (GetStopException().IsValid())
415 return true;
416 }
417 }
418 return false;
419}
420bool MachThread::IsStepping() { return GetState() == eStateStepping; }
421
422bool MachThread::ThreadDidStop() {
423 // This thread has existed prior to resuming under debug nub control,
424 // and has just been stopped. Do any cleanup that needs to be done
425 // after running.
426
427 // The thread state and breakpoint will still have the same values
428 // as they had prior to resuming the thread, so it makes it easy to check
429 // if we were trying to step a thread, or we tried to resume while being
430 // at a breakpoint.
431
432 // When this method gets called, the process state is still in the
433 // state it was in while running so we can act accordingly.
434 m_arch_up->ThreadDidStop();
435
436 // We may have suspended this thread so the primary thread could step
437 // without worrying about race conditions, so lets restore our suspend
438 // count.
439 RestoreSuspendCountAfterStop();
440
441 // Update the basic information for a thread
442 MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info);
443
444 if (m_basic_info.suspend_count > 0)
445 SetState(eStateSuspended);
446 else
447 SetState(eStateStopped);
448 return true;
449}
450
451bool MachThread::NotifyException(MachException::Data &exc) {
452 // Allow the arch specific protocol to process (MachException::Data &)exc
453 // first before possible reassignment of m_stop_exception with exc.
454 // See also MachThread::GetStopException().
455 bool handled = m_arch_up->NotifyException(exc);
456
457 if (m_stop_exception.IsValid()) {
458 // We may have more than one exception for a thread, but we need to
459 // only remember the one that we will say is the reason we stopped.
460 // We may have been single stepping and also gotten a signal exception,
461 // so just remember the most pertinent one.
462 if (m_stop_exception.IsBreakpoint())
463 m_stop_exception = exc;
464 } else {
465 m_stop_exception = exc;
466 }
467
468 return handled;
469}
470
471nub_state_t MachThread::GetState() {
472 std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
473 return m_state;
474}
475
476void MachThread::SetState(nub_state_t state) {
477 std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
478 m_state = state;
479 DNBLogThreadedIf(LOG_THREAD,
480 "MachThread::SetState ( %s ) for tid = 0x%8.8" PRIx64 "",
481 DNBStateAsString(state), m_unique_id);
482}
483
484nub_size_t MachThread::GetNumRegistersInSet(nub_size_t regSet) const {
485 if (regSet < m_num_reg_sets)
486 return m_reg_sets[regSet].num_registers;
487 return 0;
488}
489
490const char *MachThread::GetRegisterSetName(nub_size_t regSet) const {
491 if (regSet < m_num_reg_sets)
492 return m_reg_sets[regSet].name;
493 return NULL;
494}
495
496const DNBRegisterInfo *MachThread::GetRegisterInfo(nub_size_t regSet,
497 nub_size_t regIndex) const {
498 if (regSet < m_num_reg_sets)
499 if (regIndex < m_reg_sets[regSet].num_registers)
500 return &m_reg_sets[regSet].registers[regIndex];
501 return NULL;
502}
503void MachThread::DumpRegisterState(nub_size_t regSet) {
504 if (regSet == REGISTER_SET_ALL) {
505 for (regSet = 1; regSet < m_num_reg_sets; regSet++)
506 DumpRegisterState(regSet);
507 } else {
508 if (m_arch_up->RegisterSetStateIsValid((int)regSet)) {
509 const size_t numRegisters = GetNumRegistersInSet(regSet);
510 uint32_t regIndex = 0;
511 std::unique_ptr<DNBRegisterValueClass> reg =
512 std::make_unique<DNBRegisterValueClass>();
513 for (regIndex = 0; regIndex < numRegisters; ++regIndex) {
514 if (m_arch_up->GetRegisterValue((uint32_t)regSet, regIndex,
515 reg.get())) {
516 reg->Dump(NULL, NULL);
517 }
518 }
519 } else {
520 DNBLog("%s: registers are not currently valid.",
521 GetRegisterSetName(regSet));
522 }
523 }
524}
525
526const DNBRegisterSetInfo *
527MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets) const {
528 *num_reg_sets = m_num_reg_sets;
529 return &m_reg_sets[0];
530}
531
532bool MachThread::GetRegisterValue(uint32_t set, uint32_t reg,
533 DNBRegisterValue *value) {
534 return m_arch_up->GetRegisterValue(set, reg, value);
535}
536
537bool MachThread::SetRegisterValue(uint32_t set, uint32_t reg,
538 const DNBRegisterValue *value) {
539 return m_arch_up->SetRegisterValue(set, reg, value);
540}
541
542nub_size_t MachThread::GetRegisterContext(void *buf, nub_size_t buf_len) {
543 return m_arch_up->GetRegisterContext(buf, buf_len);
544}
545
546nub_size_t MachThread::SetRegisterContext(const void *buf, nub_size_t buf_len) {
547 return m_arch_up->SetRegisterContext(buf, buf_len);
548}
549
550uint32_t MachThread::SaveRegisterState() {
551 return m_arch_up->SaveRegisterState();
552}
553bool MachThread::RestoreRegisterState(uint32_t save_id) {
554 return m_arch_up->RestoreRegisterState(save_id);
555}
556
557uint32_t MachThread::EnableHardwareBreakpoint(const DNBBreakpoint *bp,
558 bool also_set_on_task) {
559 if (bp != NULL && bp->IsBreakpoint()) {
560 return m_arch_up->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize(),
561 also_set_on_task);
562 }
563 return INVALID_NUB_HW_INDEX;
564}
565
566uint32_t MachThread::EnableHardwareWatchpoint(const DNBBreakpoint *wp,
567 bool also_set_on_task) {
568 if (wp != NULL && wp->IsWatchpoint())
569 return m_arch_up->EnableHardwareWatchpoint(
570 wp->Address(), wp->ByteSize(), wp->WatchpointRead(),
571 wp->WatchpointWrite(), also_set_on_task);
572 return INVALID_NUB_HW_INDEX;
573}
574
575bool MachThread::RollbackTransForHWP() {
576 return m_arch_up->RollbackTransForHWP();
577}
578
579bool MachThread::FinishTransForHWP() { return m_arch_up->FinishTransForHWP(); }
580
581bool MachThread::DisableHardwareBreakpoint(const DNBBreakpoint *bp,
582 bool also_set_on_task) {
583 if (bp != NULL && bp->IsHardware()) {
584 return m_arch_up->DisableHardwareBreakpoint(bp->GetHardwareIndex(),
585 also_set_on_task);
586 }
587 return false;
588}
589
590bool MachThread::DisableHardwareWatchpoint(const DNBBreakpoint *wp,
591 bool also_set_on_task) {
592 if (wp != NULL && wp->IsHardware())
593 return m_arch_up->DisableHardwareWatchpoint(wp->GetHardwareIndex(),
594 also_set_on_task);
595 return false;
596}
597
598uint32_t MachThread::NumSupportedHardwareWatchpoints() const {
599 return m_arch_up->NumSupportedHardwareWatchpoints();
600}
601
602const char *MachThread::GetName() {
603 // Don't try to get the thread info once and cache it for the life of the
604 // thread. It changes over time, for instance
605 // if the thread name changes, then the thread_handle also changes... So you
606 // have to refetch it every time.
607 if (GetExtendedInfo() && m_extended_info.pth_name[0])
608 return m_extended_info.pth_name;
609 return NULL;
610}
611
612uint64_t
613MachThread::GetGloballyUniqueThreadIDForMachPortID(thread_t mach_port_id) {
614 kern_return_t kr;
615 thread_identifier_info_data_t tident;
616 mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
617 kr = thread_info(mach_port_id, THREAD_IDENTIFIER_INFO, (thread_info_t)&tident,
618 &tident_count);
619 if (kr != KERN_SUCCESS) {
620 return mach_port_id;
621 }
622 return tident.thread_id;
623}
624
625nub_addr_t MachThread::GetPThreadT() {
626 nub_addr_t pthread_t_value = INVALID_NUB_ADDRESS;
627 if (MachPortNumberIsValid(m_mach_port_number)) {
628 kern_return_t kr;
629 thread_identifier_info_data_t tident;
630 mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
631 kr = thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
632 (thread_info_t)&tident, &tident_count);
633 if (kr == KERN_SUCCESS) {
634 // Dereference thread_handle to get the pthread_t value for this thread.
635 if (m_is_64_bit) {
636 uint64_t addr;
637 if (m_process->ReadMemory(tident.thread_handle, 8, &addr) == 8) {
638 if (addr != 0) {
639 pthread_t_value = addr;
640 }
641 }
642 } else {
643 uint32_t addr;
644 if (m_process->ReadMemory(tident.thread_handle, 4, &addr) == 4) {
645 if (addr != 0) {
646 pthread_t_value = addr;
647 }
648 }
649 }
650 }
651 }
652 return pthread_t_value;
653}
654
655// Return this thread's TSD (Thread Specific Data) address.
656// This is computed based on this thread's pthread_t value.
657//
658// We compute the TSD from the pthread_t by one of two methods.
659//
660// If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we
661// add to
662// the pthread_t to get the TSD base address.
663//
664// Else we read a pointer from memory at pthread_t +
665// plo_pthread_tsd_base_address_offset and
666// that gives us the TSD address.
667//
668// These plo_pthread_tsd_base values must be read out of libpthread by lldb &
669// provided to debugserver.
670
671nub_addr_t
672MachThread::GetTSDAddressForThread(uint64_t plo_pthread_tsd_base_address_offset,
673 uint64_t plo_pthread_tsd_base_offset,
674 uint64_t plo_pthread_tsd_entry_size) {
675 nub_addr_t tsd_addr = INVALID_NUB_ADDRESS;
676 nub_addr_t pthread_t_value = GetPThreadT();
677 if (plo_pthread_tsd_base_offset != 0 &&
678 plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS) {
679 tsd_addr = pthread_t_value + plo_pthread_tsd_base_offset;
680 } else {
681 if (plo_pthread_tsd_entry_size == 4) {
682 uint32_t addr = 0;
683 if (m_process->ReadMemory(pthread_t_value +
684 plo_pthread_tsd_base_address_offset,
685 4, &addr) == 4) {
686 if (addr != 0) {
687 tsd_addr = addr;
688 }
689 }
690 }
691 if (plo_pthread_tsd_entry_size == 4) {
692 uint64_t addr = 0;
693 if (m_process->ReadMemory(pthread_t_value +
694 plo_pthread_tsd_base_address_offset,
695 8, &addr) == 8) {
696 if (addr != 0) {
697 tsd_addr = addr;
698 }
699 }
700 }
701 }
702 return tsd_addr;
703}
704
705nub_addr_t MachThread::GetDispatchQueueT() {
706 nub_addr_t dispatch_queue_t_value = INVALID_NUB_ADDRESS;
707 if (MachPortNumberIsValid(m_mach_port_number)) {
708 kern_return_t kr;
709 thread_identifier_info_data_t tident;
710 mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
711 kr = thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
712 (thread_info_t)&tident, &tident_count);
713 if (kr == KERN_SUCCESS && tident.dispatch_qaddr != 0 &&
714 tident.dispatch_qaddr != INVALID_NUB_ADDRESS) {
715 // Dereference dispatch_qaddr to get the dispatch_queue_t value for this
716 // thread's queue, if any.
717 if (m_is_64_bit) {
718 uint64_t addr;
719 if (m_process->ReadMemory(tident.dispatch_qaddr, 8, &addr) == 8) {
720 if (addr != 0)
721 dispatch_queue_t_value = addr;
722 }
723 } else {
724 uint32_t addr;
725 if (m_process->ReadMemory(tident.dispatch_qaddr, 4, &addr) == 4) {
726 if (addr != 0)
727 dispatch_queue_t_value = addr;
728 }
729 }
730 }
731 }
732 return dispatch_queue_t_value;
733}
734
735ThreadInfo::QoS MachThread::GetRequestedQoS(nub_addr_t tsd,
736 uint64_t dti_qos_class_index) {
737 ThreadInfo::QoS qos_value;
738 if (MachPortNumberIsValid(m_mach_port_number) &&
739 m_pthread_qos_class_decode != nullptr) {
740 uint64_t pthread_priority_value = 0;
741 if (m_is_64_bit) {
742 uint64_t pri;
743 if (m_process->ReadMemory(tsd + (dti_qos_class_index * 8), 8, &pri) ==
744 8) {
745 pthread_priority_value = pri;
746 }
747 } else {
748 uint32_t pri;
749 if (m_process->ReadMemory(tsd + (dti_qos_class_index * 4), 4, &pri) ==
750 4) {
751 pthread_priority_value = pri;
752 }
753 }
754
755 uint32_t requested_qos =
756 m_pthread_qos_class_decode(pthread_priority_value, NULL, NULL);
757
758 switch (requested_qos) {
759 // These constants from <pthread/qos.h>
760 case 0x21:
761 qos_value.enum_value = requested_qos;
762 qos_value.constant_name = "QOS_CLASS_USER_INTERACTIVE";
763 qos_value.printable_name = "User Interactive";
764 break;
765 case 0x19:
766 qos_value.enum_value = requested_qos;
767 qos_value.constant_name = "QOS_CLASS_USER_INITIATED";
768 qos_value.printable_name = "User Initiated";
769 break;
770 case 0x15:
771 qos_value.enum_value = requested_qos;
772 qos_value.constant_name = "QOS_CLASS_DEFAULT";
773 qos_value.printable_name = "Default";
774 break;
775 case 0x11:
776 qos_value.enum_value = requested_qos;
777 qos_value.constant_name = "QOS_CLASS_UTILITY";
778 qos_value.printable_name = "Utility";
779 break;
780 case 0x09:
781 qos_value.enum_value = requested_qos;
782 qos_value.constant_name = "QOS_CLASS_BACKGROUND";
783 qos_value.printable_name = "Background";
784 break;
785 case 0x00:
786 qos_value.enum_value = requested_qos;
787 qos_value.constant_name = "QOS_CLASS_UNSPECIFIED";
788 qos_value.printable_name = "Unspecified";
789 break;
790 }
791 }
792 return qos_value;
793}
794

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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