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 | |
22 | static uint32_t GetSequenceID() { |
23 | static uint32_t g_nextID = 0; |
24 | return ++g_nextID; |
25 | } |
26 | |
27 | MachThread::MachThread(MachProcess *process, bool is_64_bit, |
28 | uint64_t unique_thread_id, thread_t mach_port_num) |
29 | : m_process(process), m_unique_id(unique_thread_id), |
30 | m_mach_port_number(mach_port_num), m_seq_id(GetSequenceID()), |
31 | m_state(eStateUnloaded), m_state_mutex(PTHREAD_MUTEX_RECURSIVE), |
32 | m_suspend_count(0), m_stop_exception(), |
33 | m_arch_up(DNBArchProtocol::Create(this)), m_reg_sets(NULL), |
34 | m_num_reg_sets(0), m_ident_info(), m_proc_threadinfo(), |
35 | m_dispatch_queue_name(), m_is_64_bit(is_64_bit), |
36 | m_pthread_qos_class_decode(nullptr) { |
37 | nub_size_t num_reg_sets = 0; |
38 | m_reg_sets = m_arch_up->GetRegisterSetInfo(&num_reg_sets); |
39 | m_num_reg_sets = num_reg_sets; |
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 | |
55 | MachThread::~MachThread() { |
56 | DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, |
57 | "MachThread::~MachThread() for tid = 0x%8.8" PRIx64 " (%u)" , |
58 | m_unique_id, m_seq_id); |
59 | } |
60 | |
61 | void 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 | |
73 | void 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 | |
81 | bool 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 | |
119 | bool 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 | |
156 | const 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. |
195 | thread_t MachThread::InferiorThreadID() const { |
196 | mach_msg_type_number_t i; |
197 | mach_port_name_array_t names; |
198 | mach_port_type_array_t types; |
199 | mach_msg_type_number_t ncount, tcount; |
200 | thread_t inferior_tid = INVALID_NUB_THREAD; |
201 | task_t my_task = ::mach_task_self(); |
202 | task_t task = m_process->Task().TaskPort(); |
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 | |
231 | bool 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 | |
249 | struct 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 | |
255 | bool 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 | |
268 | bool MachThread::ThreadIDIsValid(uint64_t thread) { return thread != 0; } |
269 | |
270 | bool MachThread::MachPortNumberIsValid(thread_t thread) { |
271 | return thread != THREAD_NULL; |
272 | } |
273 | |
274 | bool MachThread::GetRegisterState(int flavor, bool force) { |
275 | return m_arch_up->GetRegisterState(flavor, force) == KERN_SUCCESS; |
276 | } |
277 | |
278 | bool MachThread::SetRegisterState(int flavor) { |
279 | return m_arch_up->SetRegisterState(flavor) == KERN_SUCCESS; |
280 | } |
281 | |
282 | uint64_t MachThread::GetPC(uint64_t failValue) { |
283 | // Get program counter |
284 | return m_arch_up->GetPC(failValue); |
285 | } |
286 | |
287 | bool MachThread::SetPC(uint64_t value) { |
288 | // Set program counter |
289 | return m_arch_up->SetPC(value); |
290 | } |
291 | |
292 | uint64_t MachThread::GetSP(uint64_t failValue) { |
293 | // Get stack pointer |
294 | return m_arch_up->GetSP(failValue); |
295 | } |
296 | |
297 | nub_process_t MachThread::ProcessID() const { |
298 | if (m_process) |
299 | return m_process->ProcessID(); |
300 | return INVALID_NUB_PROCESS; |
301 | } |
302 | |
303 | void 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 | |
342 | void MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action, |
343 | bool others_stopped) { |
344 | if (thread_action->addr != INVALID_NUB_ADDRESS) |
345 | SetPC(thread_action->addr); |
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 | |
366 | DNBBreakpoint *MachThread::CurrentBreakpoint() { |
367 | return m_process->Breakpoints().FindByAddress(GetPC()); |
368 | } |
369 | |
370 | bool 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 | } |
401 | bool MachThread::IsStepping() { return GetState() == eStateStepping; } |
402 | |
403 | bool MachThread::ThreadDidStop() { |
404 | // This thread has existed prior to resuming under debug nub control, |
405 | // and has just been stopped. Do any cleanup that needs to be done |
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 | |
432 | bool MachThread::NotifyException(MachException::Data &exc) { |
433 | // Allow the arch specific protocol to process (MachException::Data &)exc |
434 | // first before possible reassignment of m_stop_exception with exc. |
435 | // See also MachThread::GetStopException(). |
436 | bool handled = m_arch_up->NotifyException(exc); |
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 | |
452 | nub_state_t MachThread::GetState() { |
453 | // If any other threads access this we will need a mutex for it |
454 | PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); |
455 | return m_state; |
456 | } |
457 | |
458 | void 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 | |
466 | nub_size_t MachThread::GetNumRegistersInSet(nub_size_t regSet) const { |
467 | if (regSet < m_num_reg_sets) |
468 | return m_reg_sets[regSet].num_registers; |
469 | return 0; |
470 | } |
471 | |
472 | const 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 | |
478 | const DNBRegisterInfo *MachThread::GetRegisterInfo(nub_size_t regSet, |
479 | nub_size_t regIndex) const { |
480 | if (regSet < m_num_reg_sets) |
481 | if (regIndex < m_reg_sets[regSet].num_registers) |
482 | return &m_reg_sets[regSet].registers[regIndex]; |
483 | return NULL; |
484 | } |
485 | void MachThread::DumpRegisterState(nub_size_t regSet) { |
486 | if (regSet == REGISTER_SET_ALL) { |
487 | for (regSet = 1; regSet < m_num_reg_sets; regSet++) |
488 | DumpRegisterState(regSet); |
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, ®)) { |
496 | reg.Dump(NULL, NULL); |
497 | } |
498 | } |
499 | } else { |
500 | DNBLog("%s: registers are not currently valid." , |
501 | GetRegisterSetName(regSet)); |
502 | } |
503 | } |
504 | } |
505 | |
506 | const DNBRegisterSetInfo * |
507 | MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets) const { |
508 | *num_reg_sets = m_num_reg_sets; |
509 | return &m_reg_sets[0]; |
510 | } |
511 | |
512 | bool MachThread::GetRegisterValue(uint32_t set, uint32_t reg, |
513 | DNBRegisterValue *value) { |
514 | return m_arch_up->GetRegisterValue(set, reg, value); |
515 | } |
516 | |
517 | bool MachThread::SetRegisterValue(uint32_t set, uint32_t reg, |
518 | const DNBRegisterValue *value) { |
519 | return m_arch_up->SetRegisterValue(set, reg, value); |
520 | } |
521 | |
522 | nub_size_t MachThread::GetRegisterContext(void *buf, nub_size_t buf_len) { |
523 | return m_arch_up->GetRegisterContext(buf, buf_len); |
524 | } |
525 | |
526 | nub_size_t MachThread::SetRegisterContext(const void *buf, nub_size_t buf_len) { |
527 | return m_arch_up->SetRegisterContext(buf, buf_len); |
528 | } |
529 | |
530 | uint32_t MachThread::SaveRegisterState() { |
531 | return m_arch_up->SaveRegisterState(); |
532 | } |
533 | bool MachThread::RestoreRegisterState(uint32_t save_id) { |
534 | return m_arch_up->RestoreRegisterState(save_id); |
535 | } |
536 | |
537 | uint32_t MachThread::EnableHardwareBreakpoint(const DNBBreakpoint *bp, |
538 | bool also_set_on_task) { |
539 | if (bp != NULL && bp->IsBreakpoint()) { |
540 | return m_arch_up->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize(), |
541 | also_set_on_task); |
542 | } |
543 | return INVALID_NUB_HW_INDEX; |
544 | } |
545 | |
546 | uint32_t MachThread::EnableHardwareWatchpoint(const DNBBreakpoint *wp, |
547 | bool also_set_on_task) { |
548 | if (wp != NULL && wp->IsWatchpoint()) |
549 | return m_arch_up->EnableHardwareWatchpoint( |
550 | wp->Address(), wp->ByteSize(), wp->WatchpointRead(), |
551 | wp->WatchpointWrite(), also_set_on_task); |
552 | return INVALID_NUB_HW_INDEX; |
553 | } |
554 | |
555 | bool MachThread::RollbackTransForHWP() { |
556 | return m_arch_up->RollbackTransForHWP(); |
557 | } |
558 | |
559 | bool MachThread::FinishTransForHWP() { return m_arch_up->FinishTransForHWP(); } |
560 | |
561 | bool MachThread::DisableHardwareBreakpoint(const DNBBreakpoint *bp, |
562 | bool also_set_on_task) { |
563 | if (bp != NULL && bp->IsHardware()) { |
564 | return m_arch_up->DisableHardwareBreakpoint(bp->GetHardwareIndex(), |
565 | also_set_on_task); |
566 | } |
567 | return false; |
568 | } |
569 | |
570 | bool MachThread::DisableHardwareWatchpoint(const DNBBreakpoint *wp, |
571 | bool also_set_on_task) { |
572 | if (wp != NULL && wp->IsHardware()) |
573 | return m_arch_up->DisableHardwareWatchpoint(wp->GetHardwareIndex(), |
574 | also_set_on_task); |
575 | return false; |
576 | } |
577 | |
578 | uint32_t MachThread::NumSupportedHardwareWatchpoints() const { |
579 | return m_arch_up->NumSupportedHardwareWatchpoints(); |
580 | } |
581 | |
582 | bool MachThread::GetIdentifierInfo() { |
583 | // Don't try to get the thread info once and cache it for the life of the |
584 | // thread. It changes over time, for instance |
585 | // if the thread name changes, then the thread_handle also changes... So you |
586 | // have to refetch it every time. |
587 | mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; |
588 | kern_return_t kret = ::thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO, |
589 | (thread_info_t)&m_ident_info, &count); |
590 | return kret == KERN_SUCCESS; |
591 | |
592 | return false; |
593 | } |
594 | |
595 | const char *MachThread::GetName() { |
596 | if (GetIdentifierInfo()) { |
597 | int len = ::proc_pidinfo(m_process->ProcessID(), PROC_PIDTHREADINFO, |
598 | m_ident_info.thread_handle, &m_proc_threadinfo, |
599 | sizeof(m_proc_threadinfo)); |
600 | |
601 | if (len && m_proc_threadinfo.pth_name[0]) |
602 | return m_proc_threadinfo.pth_name; |
603 | } |
604 | return NULL; |
605 | } |
606 | |
607 | uint64_t |
608 | MachThread::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 | |
620 | nub_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 | |
666 | nub_addr_t |
667 | MachThread::GetTSDAddressForThread(uint64_t plo_pthread_tsd_base_address_offset, |
668 | uint64_t plo_pthread_tsd_base_offset, |
669 | uint64_t plo_pthread_tsd_entry_size) { |
670 | nub_addr_t tsd_addr = INVALID_NUB_ADDRESS; |
671 | nub_addr_t pthread_t_value = GetPThreadT(); |
672 | if (plo_pthread_tsd_base_offset != 0 && |
673 | plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS) { |
674 | tsd_addr = pthread_t_value + plo_pthread_tsd_base_offset; |
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 | |
700 | nub_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 | |
730 | ThreadInfo::QoS MachThread::GetRequestedQoS(nub_addr_t tsd, |
731 | uint64_t dti_qos_class_index) { |
732 | ThreadInfo::QoS qos_value; |
733 | if (MachPortNumberIsValid(m_mach_port_number) && |
734 | m_pthread_qos_class_decode != nullptr) { |
735 | uint64_t pthread_priority_value = 0; |
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 | |