1 | //===-- MachThreadList.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 "MachThreadList.h" |
14 | |
15 | #include "DNB.h" |
16 | #include "DNBLog.h" |
17 | #include "DNBThreadResumeActions.h" |
18 | #include "MachProcess.h" |
19 | |
20 | #include <cinttypes> |
21 | #include <sys/sysctl.h> |
22 | |
23 | #include <memory> |
24 | |
25 | MachThreadList::MachThreadList() |
26 | : m_threads(), m_threads_mutex(), m_is_64_bit(false) {} |
27 | |
28 | MachThreadList::~MachThreadList() = default; |
29 | |
30 | nub_state_t MachThreadList::GetState(nub_thread_t tid) { |
31 | MachThreadSP thread_sp(GetThreadByID(tid)); |
32 | if (thread_sp) |
33 | return thread_sp->GetState(); |
34 | return eStateInvalid; |
35 | } |
36 | |
37 | const char *MachThreadList::GetName(nub_thread_t tid) { |
38 | MachThreadSP thread_sp(GetThreadByID(tid)); |
39 | if (thread_sp) |
40 | return thread_sp->GetName(); |
41 | return NULL; |
42 | } |
43 | |
44 | ThreadInfo::QoS MachThreadList::GetRequestedQoS(nub_thread_t tid, |
45 | nub_addr_t tsd, |
46 | uint64_t dti_qos_class_index) { |
47 | MachThreadSP thread_sp(GetThreadByID(tid)); |
48 | if (thread_sp) |
49 | return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index); |
50 | return ThreadInfo::QoS(); |
51 | } |
52 | |
53 | nub_addr_t MachThreadList::GetPThreadT(nub_thread_t tid) { |
54 | MachThreadSP thread_sp(GetThreadByID(tid)); |
55 | if (thread_sp) |
56 | return thread_sp->GetPThreadT(); |
57 | return INVALID_NUB_ADDRESS; |
58 | } |
59 | |
60 | nub_addr_t MachThreadList::GetDispatchQueueT(nub_thread_t tid) { |
61 | MachThreadSP thread_sp(GetThreadByID(tid)); |
62 | if (thread_sp) |
63 | return thread_sp->GetDispatchQueueT(); |
64 | return INVALID_NUB_ADDRESS; |
65 | } |
66 | |
67 | nub_addr_t MachThreadList::GetTSDAddressForThread( |
68 | nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, |
69 | uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) { |
70 | MachThreadSP thread_sp(GetThreadByID(tid)); |
71 | if (thread_sp) |
72 | return thread_sp->GetTSDAddressForThread( |
73 | plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, |
74 | plo_pthread_tsd_entry_size); |
75 | return INVALID_NUB_ADDRESS; |
76 | } |
77 | |
78 | nub_thread_t MachThreadList::SetCurrentThread(nub_thread_t tid) { |
79 | MachThreadSP thread_sp(GetThreadByID(tid)); |
80 | if (thread_sp) { |
81 | m_current_thread = thread_sp; |
82 | return tid; |
83 | } |
84 | return INVALID_NUB_THREAD; |
85 | } |
86 | |
87 | bool MachThreadList::GetThreadStoppedReason( |
88 | nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const { |
89 | MachThreadSP thread_sp(GetThreadByID(tid)); |
90 | if (thread_sp) |
91 | return thread_sp->GetStopException().GetStopInfo(stop_info); |
92 | return false; |
93 | } |
94 | |
95 | bool MachThreadList::GetIdentifierInfo( |
96 | nub_thread_t tid, thread_identifier_info_data_t *ident_info) { |
97 | thread_t mach_port_number = GetMachPortNumberByThreadID(tid); |
98 | |
99 | mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; |
100 | return ::thread_info(mach_port_number, THREAD_IDENTIFIER_INFO, |
101 | (thread_info_t)ident_info, &count) == KERN_SUCCESS; |
102 | } |
103 | |
104 | void MachThreadList::DumpThreadStoppedReason(nub_thread_t tid) const { |
105 | MachThreadSP thread_sp(GetThreadByID(tid)); |
106 | if (thread_sp) |
107 | thread_sp->GetStopException().DumpStopReason(); |
108 | } |
109 | |
110 | const char *MachThreadList::GetThreadInfo(nub_thread_t tid) const { |
111 | MachThreadSP thread_sp(GetThreadByID(tid)); |
112 | if (thread_sp) |
113 | return thread_sp->GetBasicInfoAsString(); |
114 | return NULL; |
115 | } |
116 | |
117 | MachThreadSP MachThreadList::GetThreadByID(nub_thread_t tid) const { |
118 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
119 | MachThreadSP thread_sp; |
120 | const size_t num_threads = m_threads.size(); |
121 | for (size_t idx = 0; idx < num_threads; ++idx) { |
122 | if (m_threads[idx]->ThreadID() == tid) { |
123 | thread_sp = m_threads[idx]; |
124 | break; |
125 | } |
126 | } |
127 | return thread_sp; |
128 | } |
129 | |
130 | MachThreadSP |
131 | MachThreadList::GetThreadByMachPortNumber(thread_t mach_port_number) const { |
132 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
133 | MachThreadSP thread_sp; |
134 | const size_t num_threads = m_threads.size(); |
135 | for (size_t idx = 0; idx < num_threads; ++idx) { |
136 | if (m_threads[idx]->MachPortNumber() == mach_port_number) { |
137 | thread_sp = m_threads[idx]; |
138 | break; |
139 | } |
140 | } |
141 | return thread_sp; |
142 | } |
143 | |
144 | nub_thread_t |
145 | MachThreadList::GetThreadIDByMachPortNumber(thread_t mach_port_number) const { |
146 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
147 | MachThreadSP thread_sp; |
148 | const size_t num_threads = m_threads.size(); |
149 | for (size_t idx = 0; idx < num_threads; ++idx) { |
150 | if (m_threads[idx]->MachPortNumber() == mach_port_number) { |
151 | return m_threads[idx]->ThreadID(); |
152 | } |
153 | } |
154 | return INVALID_NUB_THREAD; |
155 | } |
156 | |
157 | thread_t MachThreadList::GetMachPortNumberByThreadID( |
158 | nub_thread_t globally_unique_id) const { |
159 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
160 | MachThreadSP thread_sp; |
161 | const size_t num_threads = m_threads.size(); |
162 | for (size_t idx = 0; idx < num_threads; ++idx) { |
163 | if (m_threads[idx]->ThreadID() == globally_unique_id) { |
164 | return m_threads[idx]->MachPortNumber(); |
165 | } |
166 | } |
167 | return 0; |
168 | } |
169 | |
170 | bool MachThreadList::GetRegisterValue(nub_thread_t tid, uint32_t set, |
171 | uint32_t reg, |
172 | DNBRegisterValue *reg_value) const { |
173 | MachThreadSP thread_sp(GetThreadByID(tid)); |
174 | if (thread_sp) |
175 | return thread_sp->GetRegisterValue(set, reg, reg_value); |
176 | |
177 | return false; |
178 | } |
179 | |
180 | bool MachThreadList::SetRegisterValue(nub_thread_t tid, uint32_t set, |
181 | uint32_t reg, |
182 | const DNBRegisterValue *reg_value) const { |
183 | MachThreadSP thread_sp(GetThreadByID(tid)); |
184 | if (thread_sp) |
185 | return thread_sp->SetRegisterValue(set, reg, reg_value); |
186 | |
187 | return false; |
188 | } |
189 | |
190 | nub_size_t MachThreadList::GetRegisterContext(nub_thread_t tid, void *buf, |
191 | size_t buf_len) { |
192 | MachThreadSP thread_sp(GetThreadByID(tid)); |
193 | if (thread_sp) |
194 | return thread_sp->GetRegisterContext(buf, buf_len); |
195 | return 0; |
196 | } |
197 | |
198 | nub_size_t MachThreadList::SetRegisterContext(nub_thread_t tid, const void *buf, |
199 | size_t buf_len) { |
200 | MachThreadSP thread_sp(GetThreadByID(tid)); |
201 | if (thread_sp) |
202 | return thread_sp->SetRegisterContext(buf, buf_len); |
203 | return 0; |
204 | } |
205 | |
206 | uint32_t MachThreadList::SaveRegisterState(nub_thread_t tid) { |
207 | MachThreadSP thread_sp(GetThreadByID(tid)); |
208 | if (thread_sp) |
209 | return thread_sp->SaveRegisterState(); |
210 | return 0; |
211 | } |
212 | |
213 | bool MachThreadList::RestoreRegisterState(nub_thread_t tid, uint32_t save_id) { |
214 | MachThreadSP thread_sp(GetThreadByID(tid)); |
215 | if (thread_sp) |
216 | return thread_sp->RestoreRegisterState(save_id); |
217 | return false; |
218 | } |
219 | |
220 | nub_size_t MachThreadList::NumThreads() const { |
221 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
222 | return m_threads.size(); |
223 | } |
224 | |
225 | nub_thread_t MachThreadList::ThreadIDAtIndex(nub_size_t idx) const { |
226 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
227 | if (idx < m_threads.size()) |
228 | return m_threads[idx]->ThreadID(); |
229 | return INVALID_NUB_THREAD; |
230 | } |
231 | |
232 | nub_thread_t MachThreadList::CurrentThreadID() { |
233 | MachThreadSP thread_sp; |
234 | CurrentThread(threadSP&: thread_sp); |
235 | if (thread_sp.get()) |
236 | return thread_sp->ThreadID(); |
237 | return INVALID_NUB_THREAD; |
238 | } |
239 | |
240 | bool MachThreadList::NotifyException(MachException::Data &exc) { |
241 | MachThreadSP thread_sp(GetThreadByMachPortNumber(exc.thread_port)); |
242 | if (thread_sp) { |
243 | thread_sp->NotifyException(exc); |
244 | return true; |
245 | } |
246 | return false; |
247 | } |
248 | |
249 | void MachThreadList::Clear() { |
250 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
251 | m_threads.clear(); |
252 | } |
253 | |
254 | uint32_t |
255 | MachThreadList::UpdateThreadList(MachProcess *process, bool update, |
256 | MachThreadList::collection *new_threads) { |
257 | // locker will keep a mutex locked until it goes out of scope |
258 | DNBLogThreadedIf(LOG_THREAD, "MachThreadList::UpdateThreadList (pid = %4.4x, " |
259 | "update = %u) process stop count = %u", |
260 | process->ProcessID(), update, process->StopCount()); |
261 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
262 | |
263 | if (process->StopCount() == 0) { |
264 | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, process->ProcessID()}; |
265 | struct kinfo_proc processInfo; |
266 | size_t bufsize = sizeof(processInfo); |
267 | if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, |
268 | &bufsize, NULL, 0) == 0 && |
269 | bufsize > 0) { |
270 | if (processInfo.kp_proc.p_flag & P_LP64) |
271 | m_is_64_bit = true; |
272 | } |
273 | #if defined(__i386__) || defined(__x86_64__) |
274 | if (m_is_64_bit) |
275 | DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64); |
276 | else |
277 | DNBArchProtocol::SetArchitecture(CPU_TYPE_I386); |
278 | #elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) |
279 | if (m_is_64_bit) |
280 | DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64); |
281 | else { |
282 | if (process->GetCPUType() == CPU_TYPE_ARM64_32) |
283 | DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32); |
284 | else |
285 | DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM); |
286 | } |
287 | #endif |
288 | } |
289 | |
290 | if (m_threads.empty() || update) { |
291 | thread_array_t thread_list = NULL; |
292 | mach_msg_type_number_t thread_list_count = 0; |
293 | task_t task = process->Task().TaskPort(); |
294 | DNBError err(::task_threads(task, &thread_list, &thread_list_count), |
295 | DNBError::MachKernel); |
296 | |
297 | if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) |
298 | err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, " |
299 | "thread_list_count => %u )", |
300 | task, thread_list, thread_list_count); |
301 | |
302 | if (err.Status() == KERN_SUCCESS && thread_list_count > 0) { |
303 | MachThreadList::collection currThreads; |
304 | size_t idx; |
305 | // Iterator through the current thread list and see which threads |
306 | // we already have in our list (keep them), which ones we don't |
307 | // (add them), and which ones are not around anymore (remove them). |
308 | for (idx = 0; idx < thread_list_count; ++idx) { |
309 | const thread_t mach_port_num = thread_list[idx]; |
310 | |
311 | uint64_t unique_thread_id = |
312 | MachThread::GetGloballyUniqueThreadIDForMachPortID(mach_port_num); |
313 | MachThreadSP thread_sp(GetThreadByID(unique_thread_id)); |
314 | if (thread_sp) { |
315 | // Keep the existing thread class |
316 | currThreads.push_back(x: thread_sp); |
317 | } else { |
318 | // We don't have this thread, lets add it. |
319 | thread_sp = std::make_shared<MachThread>( |
320 | process, m_is_64_bit, unique_thread_id, mach_port_num); |
321 | |
322 | // Add the new thread regardless of its is user ready state... |
323 | // Make sure the thread is ready to be displayed and shown to users |
324 | // before we add this thread to our list... |
325 | if (thread_sp->IsUserReady()) { |
326 | if (new_threads) |
327 | new_threads->push_back(x: thread_sp); |
328 | |
329 | currThreads.push_back(x: thread_sp); |
330 | } |
331 | } |
332 | } |
333 | |
334 | m_threads.swap(x&: currThreads); |
335 | m_current_thread.reset(); |
336 | |
337 | // Free the vm memory given to us by ::task_threads() |
338 | vm_size_t thread_list_size = |
339 | (vm_size_t)(thread_list_count * sizeof(thread_t)); |
340 | ::vm_deallocate(::mach_task_self(), (vm_address_t)thread_list, |
341 | thread_list_size); |
342 | } |
343 | } |
344 | return static_cast<uint32_t>(m_threads.size()); |
345 | } |
346 | |
347 | void MachThreadList::CurrentThread(MachThreadSP &thread_sp) { |
348 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
349 | if (m_current_thread.get() == NULL) { |
350 | // Figure out which thread is going to be our current thread. |
351 | // This is currently done by finding the first thread in the list |
352 | // that has a valid exception. |
353 | const size_t num_threads = m_threads.size(); |
354 | for (uint32_t idx = 0; idx < num_threads; ++idx) { |
355 | if (m_threads[idx]->GetStopException().IsValid()) { |
356 | m_current_thread = m_threads[idx]; |
357 | break; |
358 | } |
359 | } |
360 | } |
361 | thread_sp = m_current_thread; |
362 | } |
363 | |
364 | void MachThreadList::Dump() const { |
365 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
366 | const size_t num_threads = m_threads.size(); |
367 | for (uint32_t idx = 0; idx < num_threads; ++idx) { |
368 | m_threads[idx]->Dump(idx); |
369 | } |
370 | } |
371 | |
372 | void MachThreadList::ProcessWillResume( |
373 | MachProcess *process, const DNBThreadResumeActions &thread_actions) { |
374 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
375 | |
376 | // Update our thread list, because sometimes libdispatch or the kernel |
377 | // will spawn threads while a task is suspended. |
378 | MachThreadList::collection new_threads; |
379 | |
380 | // First figure out if we were planning on running only one thread, and if so |
381 | // force that thread to resume. |
382 | bool run_one_thread; |
383 | nub_thread_t solo_thread = INVALID_NUB_THREAD; |
384 | if (thread_actions.GetSize() > 0 && |
385 | thread_actions.NumActionsWithState(eStateStepping) + |
386 | thread_actions.NumActionsWithState(eStateRunning) == |
387 | 1) { |
388 | run_one_thread = true; |
389 | const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst(); |
390 | size_t num_actions = thread_actions.GetSize(); |
391 | for (size_t i = 0; i < num_actions; i++, action_ptr++) { |
392 | if (action_ptr->state == eStateStepping || |
393 | action_ptr->state == eStateRunning) { |
394 | solo_thread = action_ptr->tid; |
395 | break; |
396 | } |
397 | } |
398 | } else |
399 | run_one_thread = false; |
400 | |
401 | UpdateThreadList(process, update: true, new_threads: &new_threads); |
402 | |
403 | DNBThreadResumeAction resume_new_threads = {-1U, eStateRunning, 0, |
404 | INVALID_NUB_ADDRESS}; |
405 | // If we are planning to run only one thread, any new threads should be |
406 | // suspended. |
407 | if (run_one_thread) |
408 | resume_new_threads.state = eStateSuspended; |
409 | |
410 | const size_t num_new_threads = new_threads.size(); |
411 | const size_t num_threads = m_threads.size(); |
412 | for (uint32_t idx = 0; idx < num_threads; ++idx) { |
413 | MachThread *thread = m_threads[idx].get(); |
414 | bool handled = false; |
415 | for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx) { |
416 | if (thread == new_threads[new_idx].get()) { |
417 | thread->ThreadWillResume(&resume_new_threads); |
418 | handled = true; |
419 | break; |
420 | } |
421 | } |
422 | |
423 | if (!handled) { |
424 | const DNBThreadResumeAction *thread_action = |
425 | thread_actions.GetActionForThread(thread->ThreadID(), true); |
426 | // There must always be a thread action for every thread. |
427 | assert(thread_action); |
428 | bool others_stopped = false; |
429 | if (solo_thread == thread->ThreadID()) |
430 | others_stopped = true; |
431 | thread->ThreadWillResume(thread_action, others_stopped); |
432 | } |
433 | } |
434 | |
435 | if (new_threads.size()) { |
436 | for (uint32_t idx = 0; idx < num_new_threads; ++idx) { |
437 | DNBLogThreadedIf( |
438 | LOG_THREAD, "MachThreadList::ProcessWillResume (pid = %4.4x) " |
439 | "stop-id=%u, resuming newly discovered thread: " |
440 | "0x%8.8"PRIx64 ", thread-is-user-ready=%i)", |
441 | process->ProcessID(), process->StopCount(), |
442 | new_threads[idx]->ThreadID(), new_threads[idx]->IsUserReady()); |
443 | } |
444 | } |
445 | } |
446 | |
447 | uint32_t MachThreadList::ProcessDidStop(MachProcess *process) { |
448 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
449 | // Update our thread list |
450 | const uint32_t num_threads = UpdateThreadList(process, update: true); |
451 | for (uint32_t idx = 0; idx < num_threads; ++idx) { |
452 | m_threads[idx]->ThreadDidStop(); |
453 | } |
454 | return num_threads; |
455 | } |
456 | |
457 | // Check each thread in our thread list to see if we should notify our |
458 | // client of the current halt in execution. |
459 | // |
460 | // Breakpoints can have callback functions associated with them than |
461 | // can return true to stop, or false to continue executing the inferior. |
462 | // |
463 | // RETURNS |
464 | // true if we should stop and notify our clients |
465 | // false if we should resume our child process and skip notification |
466 | bool MachThreadList::ShouldStop(bool &step_more) { |
467 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
468 | uint32_t should_stop = false; |
469 | const size_t num_threads = m_threads.size(); |
470 | for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) { |
471 | should_stop = m_threads[idx]->ShouldStop(step_more); |
472 | } |
473 | return should_stop; |
474 | } |
475 | |
476 | void MachThreadList::NotifyBreakpointChanged(const DNBBreakpoint *bp) { |
477 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
478 | const size_t num_threads = m_threads.size(); |
479 | for (uint32_t idx = 0; idx < num_threads; ++idx) { |
480 | m_threads[idx]->NotifyBreakpointChanged(bp); |
481 | } |
482 | } |
483 | |
484 | uint32_t MachThreadList::DoHardwareBreakpointAction( |
485 | const DNBBreakpoint *bp, HardwareBreakpointAction action) const { |
486 | if (bp == NULL) |
487 | return INVALID_NUB_HW_INDEX; |
488 | |
489 | uint32_t hw_index = INVALID_NUB_HW_INDEX; |
490 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
491 | const size_t num_threads = m_threads.size(); |
492 | // On Mac OS X we have to prime the control registers for new threads. We do |
493 | // this using the control register data for the first thread, for lack of a |
494 | // better way of choosing. |
495 | bool also_set_on_task = true; |
496 | for (uint32_t idx = 0; idx < num_threads; ++idx) { |
497 | switch (action) { |
498 | case HardwareBreakpointAction::EnableWatchpoint: |
499 | hw_index = m_threads[idx]->EnableHardwareWatchpoint(bp, also_set_on_task); |
500 | break; |
501 | case HardwareBreakpointAction::DisableWatchpoint: |
502 | hw_index = |
503 | m_threads[idx]->DisableHardwareWatchpoint(bp, also_set_on_task); |
504 | break; |
505 | case HardwareBreakpointAction::EnableBreakpoint: |
506 | hw_index = m_threads[idx]->EnableHardwareBreakpoint(bp, also_set_on_task); |
507 | break; |
508 | case HardwareBreakpointAction::DisableBreakpoint: |
509 | hw_index = |
510 | m_threads[idx]->DisableHardwareBreakpoint(bp, also_set_on_task); |
511 | break; |
512 | } |
513 | if (hw_index == INVALID_NUB_HW_INDEX) { |
514 | // We know that idx failed for some reason. Let's rollback the |
515 | // transaction for [0, idx). |
516 | for (uint32_t i = 0; i < idx; ++i) |
517 | m_threads[i]->RollbackTransForHWP(); |
518 | return INVALID_NUB_HW_INDEX; |
519 | } |
520 | also_set_on_task = false; |
521 | } |
522 | // Notify each thread to commit the pending transaction. |
523 | for (uint32_t idx = 0; idx < num_threads; ++idx) |
524 | m_threads[idx]->FinishTransForHWP(); |
525 | return hw_index; |
526 | } |
527 | |
528 | // DNBWatchpointSet() -> MachProcess::CreateWatchpoint() -> |
529 | // MachProcess::EnableWatchpoint() |
530 | // -> MachThreadList::EnableHardwareWatchpoint(). |
531 | uint32_t |
532 | MachThreadList::EnableHardwareWatchpoint(const DNBBreakpoint *wp) const { |
533 | return DoHardwareBreakpointAction(bp: wp, |
534 | action: HardwareBreakpointAction::EnableWatchpoint); |
535 | } |
536 | |
537 | bool MachThreadList::DisableHardwareWatchpoint(const DNBBreakpoint *wp) const { |
538 | return DoHardwareBreakpointAction( |
539 | wp, HardwareBreakpointAction::DisableWatchpoint) != |
540 | INVALID_NUB_HW_INDEX; |
541 | } |
542 | |
543 | uint32_t |
544 | MachThreadList::EnableHardwareBreakpoint(const DNBBreakpoint *bp) const { |
545 | return DoHardwareBreakpointAction(bp, |
546 | action: HardwareBreakpointAction::EnableBreakpoint); |
547 | } |
548 | |
549 | bool MachThreadList::DisableHardwareBreakpoint(const DNBBreakpoint *bp) const { |
550 | return DoHardwareBreakpointAction( |
551 | bp, HardwareBreakpointAction::DisableBreakpoint) != |
552 | INVALID_NUB_HW_INDEX; |
553 | } |
554 | |
555 | uint32_t MachThreadList::NumSupportedHardwareWatchpoints() const { |
556 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
557 | const size_t num_threads = m_threads.size(); |
558 | // Use an arbitrary thread to retrieve the number of supported hardware |
559 | // watchpoints. |
560 | if (num_threads) |
561 | return m_threads[0]->NumSupportedHardwareWatchpoints(); |
562 | return 0; |
563 | } |
564 | |
565 | uint32_t MachThreadList::GetThreadIndexForThreadStoppedWithSignal( |
566 | const int signo) const { |
567 | std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); |
568 | uint32_t should_stop = false; |
569 | const size_t num_threads = m_threads.size(); |
570 | for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) { |
571 | if (m_threads[idx]->GetStopException().SoftSignal() == signo) |
572 | return idx; |
573 | } |
574 | return UINT32_MAX; |
575 | } |
576 |
Definitions
- MachThreadList
- ~MachThreadList
- GetState
- GetName
- GetRequestedQoS
- GetPThreadT
- GetDispatchQueueT
- GetTSDAddressForThread
- SetCurrentThread
- GetThreadStoppedReason
- GetIdentifierInfo
- DumpThreadStoppedReason
- GetThreadInfo
- GetThreadByID
- GetThreadByMachPortNumber
- GetThreadIDByMachPortNumber
- GetMachPortNumberByThreadID
- GetRegisterValue
- SetRegisterValue
- GetRegisterContext
- SetRegisterContext
- SaveRegisterState
- RestoreRegisterState
- NumThreads
- ThreadIDAtIndex
- CurrentThreadID
- NotifyException
- Clear
- UpdateThreadList
- CurrentThread
- Dump
- ProcessWillResume
- ProcessDidStop
- ShouldStop
- NotifyBreakpointChanged
- DoHardwareBreakpointAction
- EnableHardwareWatchpoint
- DisableHardwareWatchpoint
- EnableHardwareBreakpoint
- DisableHardwareBreakpoint
- NumSupportedHardwareWatchpoints
Improve your Profiling and Debugging skills
Find out more