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(), 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 | |
54 | MachThread::~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 | |
60 | void 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 | |
72 | void 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 | |
80 | bool 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 | |
118 | bool 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 | |
155 | const 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. |
194 | thread_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 | |
230 | bool 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 | |
248 | struct 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 | |
254 | bool 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 | |
267 | struct 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 | |
273 | bool 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 | |
287 | bool MachThread::ThreadIDIsValid(uint64_t thread) { return thread != 0; } |
288 | |
289 | bool MachThread::MachPortNumberIsValid(thread_t thread) { |
290 | return thread != THREAD_NULL; |
291 | } |
292 | |
293 | bool MachThread::GetRegisterState(int flavor, bool force) { |
294 | return m_arch_up->GetRegisterState(flavor, force) == KERN_SUCCESS; |
295 | } |
296 | |
297 | bool MachThread::SetRegisterState(int flavor) { |
298 | return m_arch_up->SetRegisterState(flavor) == KERN_SUCCESS; |
299 | } |
300 | |
301 | uint64_t MachThread::GetPC(uint64_t failValue) { |
302 | // Get program counter |
303 | return m_arch_up->GetPC(failValue); |
304 | } |
305 | |
306 | bool MachThread::SetPC(uint64_t value) { |
307 | // Set program counter |
308 | return m_arch_up->SetPC(value); |
309 | } |
310 | |
311 | uint64_t MachThread::GetSP(uint64_t failValue) { |
312 | // Get stack pointer |
313 | return m_arch_up->GetSP(failValue); |
314 | } |
315 | |
316 | nub_process_t MachThread::ProcessID() const { |
317 | if (m_process) |
318 | return m_process->ProcessID(); |
319 | return INVALID_NUB_PROCESS; |
320 | } |
321 | |
322 | void 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 | |
361 | void 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 | |
385 | DNBBreakpoint *MachThread::CurrentBreakpoint() { |
386 | return m_process->Breakpoints().FindByAddress(GetPC()); |
387 | } |
388 | |
389 | bool 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 | } |
420 | bool MachThread::IsStepping() { return GetState() == eStateStepping; } |
421 | |
422 | bool 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 | |
451 | bool 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 | |
471 | nub_state_t MachThread::GetState() { |
472 | std::lock_guard<std::recursive_mutex> guard(m_state_mutex); |
473 | return m_state; |
474 | } |
475 | |
476 | void 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 | |
484 | nub_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 | |
490 | const 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 | |
496 | const 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 | } |
503 | void 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 | |
526 | const DNBRegisterSetInfo * |
527 | MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets) const { |
528 | *num_reg_sets = m_num_reg_sets; |
529 | return &m_reg_sets[0]; |
530 | } |
531 | |
532 | bool MachThread::GetRegisterValue(uint32_t set, uint32_t reg, |
533 | DNBRegisterValue *value) { |
534 | return m_arch_up->GetRegisterValue(set, reg, value); |
535 | } |
536 | |
537 | bool MachThread::SetRegisterValue(uint32_t set, uint32_t reg, |
538 | const DNBRegisterValue *value) { |
539 | return m_arch_up->SetRegisterValue(set, reg, value); |
540 | } |
541 | |
542 | nub_size_t MachThread::GetRegisterContext(void *buf, nub_size_t buf_len) { |
543 | return m_arch_up->GetRegisterContext(buf, buf_len); |
544 | } |
545 | |
546 | nub_size_t MachThread::SetRegisterContext(const void *buf, nub_size_t buf_len) { |
547 | return m_arch_up->SetRegisterContext(buf, buf_len); |
548 | } |
549 | |
550 | uint32_t MachThread::SaveRegisterState() { |
551 | return m_arch_up->SaveRegisterState(); |
552 | } |
553 | bool MachThread::RestoreRegisterState(uint32_t save_id) { |
554 | return m_arch_up->RestoreRegisterState(save_id); |
555 | } |
556 | |
557 | uint32_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 | |
566 | uint32_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 | |
575 | bool MachThread::RollbackTransForHWP() { |
576 | return m_arch_up->RollbackTransForHWP(); |
577 | } |
578 | |
579 | bool MachThread::FinishTransForHWP() { return m_arch_up->FinishTransForHWP(); } |
580 | |
581 | bool 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 | |
590 | bool 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 | |
598 | uint32_t MachThread::NumSupportedHardwareWatchpoints() const { |
599 | return m_arch_up->NumSupportedHardwareWatchpoints(); |
600 | } |
601 | |
602 | const 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 | |
612 | uint64_t |
613 | MachThread::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 | |
625 | nub_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 | |
671 | nub_addr_t |
672 | MachThread::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 | |
705 | nub_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 | |
735 | ThreadInfo::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 |
Definitions
- GetSequenceID
- MachThread
- ~MachThread
- Suspend
- Resume
- SetSuspendCountBeforeResume
- RestoreSuspendCountAfterStop
- GetBasicInfoAsString
- InferiorThreadID
- IsUserReady
- GetBasicInfo
- GetBasicInfo
- GetExtendedInfo
- GetExtendedInfo
- ThreadIDIsValid
- MachPortNumberIsValid
- GetRegisterState
- SetRegisterState
- GetPC
- SetPC
- GetSP
- ProcessID
- Dump
- ThreadWillResume
- CurrentBreakpoint
- ShouldStop
- IsStepping
- ThreadDidStop
- NotifyException
- GetState
- SetState
- GetNumRegistersInSet
- GetRegisterSetName
- GetRegisterInfo
- DumpRegisterState
- GetRegisterSetInfo
- GetRegisterValue
- SetRegisterValue
- GetRegisterContext
- SetRegisterContext
- SaveRegisterState
- RestoreRegisterState
- EnableHardwareBreakpoint
- EnableHardwareWatchpoint
- RollbackTransForHWP
- FinishTransForHWP
- DisableHardwareBreakpoint
- DisableHardwareWatchpoint
- NumSupportedHardwareWatchpoints
- GetName
- GetGloballyUniqueThreadIDForMachPortID
- GetPThreadT
- GetTSDAddressForThread
- GetDispatchQueueT
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more