NativeThreadListDarwin.cpp revision 353358
1//===-- NativeThreadListDarwin.cpp ------------------------------------*- C++ 2//-*-===// 3// 4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5// See https://llvm.org/LICENSE.txt for license information. 6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7// 8//===----------------------------------------------------------------------===// 9// 10// Created by Greg Clayton on 6/19/07. 11// 12//===----------------------------------------------------------------------===// 13 14#include "NativeThreadListDarwin.h" 15 16// C includes 17#include <inttypes.h> 18#include <mach/vm_map.h> 19#include <sys/sysctl.h> 20 21// LLDB includes 22#include "lldb/Utility/Log.h" 23#include "lldb/Utility/Status.h" 24#include "lldb/Utility/Stream.h" 25#include "lldb/lldb-enumerations.h" 26 27#include "NativeProcessDarwin.h" 28#include "NativeThreadDarwin.h" 29 30using namespace lldb; 31using namespace lldb_private; 32using namespace lldb_private::process_darwin; 33 34NativeThreadListDarwin::NativeThreadListDarwin() 35 : m_threads(), m_threads_mutex(), m_is_64_bit(false) {} 36 37NativeThreadListDarwin::~NativeThreadListDarwin() {} 38 39// These methods will be accessed directly from NativeThreadDarwin 40#if 0 41nub_state_t 42NativeThreadListDarwin::GetState(nub_thread_t tid) 43{ 44 MachThreadSP thread_sp (GetThreadByID (tid)); 45 if (thread_sp) 46 return thread_sp->GetState(); 47 return eStateInvalid; 48} 49 50const char * 51NativeThreadListDarwin::GetName (nub_thread_t tid) 52{ 53 MachThreadSP thread_sp (GetThreadByID (tid)); 54 if (thread_sp) 55 return thread_sp->GetName(); 56 return NULL; 57} 58#endif 59 60// TODO: figure out if we need to add this to NativeThreadDarwin yet. 61#if 0 62ThreadInfo::QoS 63NativeThreadListDarwin::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) 64{ 65 MachThreadSP thread_sp (GetThreadByID (tid)); 66 if (thread_sp) 67 return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index); 68 return ThreadInfo::QoS(); 69} 70 71nub_addr_t 72NativeThreadListDarwin::GetPThreadT (nub_thread_t tid) 73{ 74 MachThreadSP thread_sp (GetThreadByID (tid)); 75 if (thread_sp) 76 return thread_sp->GetPThreadT(); 77 return INVALID_NUB_ADDRESS; 78} 79 80nub_addr_t 81NativeThreadListDarwin::GetDispatchQueueT (nub_thread_t tid) 82{ 83 MachThreadSP thread_sp (GetThreadByID (tid)); 84 if (thread_sp) 85 return thread_sp->GetDispatchQueueT(); 86 return INVALID_NUB_ADDRESS; 87} 88 89nub_addr_t 90NativeThreadListDarwin::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) 91{ 92 MachThreadSP thread_sp (GetThreadByID (tid)); 93 if (thread_sp) 94 return thread_sp->GetTSDAddressForThread(plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); 95 return INVALID_NUB_ADDRESS; 96} 97#endif 98 99// TODO implement these 100#if 0 101nub_thread_t 102NativeThreadListDarwin::SetCurrentThread(nub_thread_t tid) 103{ 104 MachThreadSP thread_sp (GetThreadByID (tid)); 105 if (thread_sp) 106 { 107 m_current_thread = thread_sp; 108 return tid; 109 } 110 return INVALID_NUB_THREAD; 111} 112 113 114bool 115NativeThreadListDarwin::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const 116{ 117 MachThreadSP thread_sp (GetThreadByID (tid)); 118 if (thread_sp) 119 return thread_sp->GetStopException().GetStopInfo(stop_info); 120 return false; 121} 122 123bool 124NativeThreadListDarwin::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info) 125{ 126 thread_t mach_port_number = GetMachPortNumberByThreadID (tid); 127 128 mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; 129 return ::thread_info (mach_port_number, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS; 130} 131 132void 133NativeThreadListDarwin::DumpThreadStoppedReason (nub_thread_t tid) const 134{ 135 MachThreadSP thread_sp (GetThreadByID (tid)); 136 if (thread_sp) 137 thread_sp->GetStopException().DumpStopReason(); 138} 139 140const char * 141NativeThreadListDarwin::GetThreadInfo (nub_thread_t tid) const 142{ 143 MachThreadSP thread_sp (GetThreadByID (tid)); 144 if (thread_sp) 145 return thread_sp->GetBasicInfoAsString(); 146 return NULL; 147} 148 149#endif 150 151NativeThreadDarwinSP 152NativeThreadListDarwin::GetThreadByID(lldb::tid_t tid) const { 153 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 154 for (auto thread_sp : m_threads) { 155 if (thread_sp && (thread_sp->GetID() == tid)) 156 return thread_sp; 157 } 158 return NativeThreadDarwinSP(); 159} 160 161NativeThreadDarwinSP NativeThreadListDarwin::GetThreadByMachPortNumber( 162 ::thread_t mach_port_number) const { 163 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 164 for (auto thread_sp : m_threads) { 165 if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number)) 166 return thread_sp; 167 } 168 return NativeThreadDarwinSP(); 169} 170 171lldb::tid_t NativeThreadListDarwin::GetThreadIDByMachPortNumber( 172 ::thread_t mach_port_number) const { 173 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 174 for (auto thread_sp : m_threads) { 175 if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number)) 176 return thread_sp->GetID(); 177 } 178 return LLDB_INVALID_THREAD_ID; 179} 180 181// TODO implement 182#if 0 183thread_t 184NativeThreadListDarwin::GetMachPortNumberByThreadID (nub_thread_t globally_unique_id) const 185{ 186 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 187 MachThreadSP thread_sp; 188 const size_t num_threads = m_threads.size(); 189 for (size_t idx = 0; idx < num_threads; ++idx) 190 { 191 if (m_threads[idx]->ThreadID() == globally_unique_id) 192 { 193 return m_threads[idx]->MachPortNumber(); 194 } 195 } 196 return 0; 197} 198 199bool 200NativeThreadListDarwin::GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value ) const 201{ 202 MachThreadSP thread_sp (GetThreadByID (tid)); 203 if (thread_sp) 204 return thread_sp->GetRegisterValue(set, reg, reg_value); 205 206 return false; 207} 208 209bool 210NativeThreadListDarwin::SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value ) const 211{ 212 MachThreadSP thread_sp (GetThreadByID (tid)); 213 if (thread_sp) 214 return thread_sp->SetRegisterValue(set, reg, reg_value); 215 216 return false; 217} 218 219nub_size_t 220NativeThreadListDarwin::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len) 221{ 222 MachThreadSP thread_sp (GetThreadByID (tid)); 223 if (thread_sp) 224 return thread_sp->GetRegisterContext (buf, buf_len); 225 return 0; 226} 227 228nub_size_t 229NativeThreadListDarwin::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len) 230{ 231 MachThreadSP thread_sp (GetThreadByID (tid)); 232 if (thread_sp) 233 return thread_sp->SetRegisterContext (buf, buf_len); 234 return 0; 235} 236 237uint32_t 238NativeThreadListDarwin::SaveRegisterState (nub_thread_t tid) 239{ 240 MachThreadSP thread_sp (GetThreadByID (tid)); 241 if (thread_sp) 242 return thread_sp->SaveRegisterState (); 243 return 0; 244} 245 246bool 247NativeThreadListDarwin::RestoreRegisterState (nub_thread_t tid, uint32_t save_id) 248{ 249 MachThreadSP thread_sp (GetThreadByID (tid)); 250 if (thread_sp) 251 return thread_sp->RestoreRegisterState (save_id); 252 return 0; 253} 254#endif 255 256size_t NativeThreadListDarwin::GetNumberOfThreads() const { 257 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 258 return static_cast<size_t>(m_threads.size()); 259} 260 261// TODO implement 262#if 0 263nub_thread_t 264NativeThreadListDarwin::ThreadIDAtIndex (nub_size_t idx) const 265{ 266 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 267 if (idx < m_threads.size()) 268 return m_threads[idx]->ThreadID(); 269 return INVALID_NUB_THREAD; 270} 271 272nub_thread_t 273NativeThreadListDarwin::CurrentThreadID ( ) 274{ 275 MachThreadSP thread_sp; 276 CurrentThread(thread_sp); 277 if (thread_sp.get()) 278 return thread_sp->ThreadID(); 279 return INVALID_NUB_THREAD; 280} 281 282#endif 283 284bool NativeThreadListDarwin::NotifyException(MachException::Data &exc) { 285 auto thread_sp = GetThreadByMachPortNumber(exc.thread_port); 286 if (thread_sp) { 287 thread_sp->NotifyException(exc); 288 return true; 289 } 290 return false; 291} 292 293void NativeThreadListDarwin::Clear() { 294 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 295 m_threads.clear(); 296} 297 298uint32_t NativeThreadListDarwin::UpdateThreadList(NativeProcessDarwin &process, 299 bool update, 300 collection *new_threads) { 301 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); 302 303 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 304 if (log) 305 log->Printf("NativeThreadListDarwin::%s() (pid = %" PRIu64 ", update = " 306 "%u) process stop count = %u", 307 __FUNCTION__, process.GetID(), update, process.GetStopID()); 308 309 if (process.GetStopID() == 0) { 310 // On our first stop, we'll record details like 32/64 bitness and select 311 // the proper architecture implementation. 312 // 313 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)process.GetID()}; 314 315 struct kinfo_proc processInfo; 316 size_t bufsize = sizeof(processInfo); 317 if ((sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, 318 &bufsize, NULL, 0) == 0) && 319 (bufsize > 0)) { 320 if (processInfo.kp_proc.p_flag & P_LP64) 321 m_is_64_bit = true; 322 } 323 324// TODO implement architecture selection and abstraction. 325#if 0 326#if defined(__i386__) || defined(__x86_64__) 327 if (m_is_64_bit) 328 DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64); 329 else 330 DNBArchProtocol::SetArchitecture(CPU_TYPE_I386); 331#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) 332 if (m_is_64_bit) 333 DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64); 334 else 335 DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM); 336#endif 337#endif 338 } 339 340 if (m_threads.empty() || update) { 341 thread_array_t thread_list = nullptr; 342 mach_msg_type_number_t thread_list_count = 0; 343 task_t task = process.GetTask(); 344 345 Status error; 346 auto mach_err = ::task_threads(task, &thread_list, &thread_list_count); 347 error.SetError(mach_err, eErrorTypeMachKernel); 348 if (error.Fail()) { 349 if (log) 350 log->Printf("::task_threads(task = 0x%4.4x, thread_list => %p, " 351 "thread_list_count => %u) failed: %u (%s)", 352 task, thread_list, thread_list_count, error.GetError(), 353 error.AsCString()); 354 return 0; 355 } 356 357 if (thread_list_count > 0) { 358 collection currThreads; 359 size_t idx; 360 // Iterator through the current thread list and see which threads we 361 // already have in our list (keep them), which ones we don't (add them), 362 // and which ones are not around anymore (remove them). 363 for (idx = 0; idx < thread_list_count; ++idx) { 364 // Get the Mach thread port. 365 const ::thread_t mach_port_num = thread_list[idx]; 366 367 // Get the unique thread id for the mach port number. 368 uint64_t unique_thread_id = 369 NativeThreadDarwin::GetGloballyUniqueThreadIDForMachPortID( 370 mach_port_num); 371 372 // Retrieve the thread if it exists. 373 auto thread_sp = GetThreadByID(unique_thread_id); 374 if (thread_sp) { 375 // We are already tracking it. Keep the existing native thread 376 // instance. 377 currThreads.push_back(thread_sp); 378 } else { 379 // We don't have a native thread instance for this thread. Create it 380 // now. 381 thread_sp.reset(new NativeThreadDarwin( 382 &process, m_is_64_bit, unique_thread_id, mach_port_num)); 383 384 // Add the new thread regardless of its is user ready state. Make 385 // sure the thread is ready to be displayed and shown to users before 386 // we add this thread to our list... 387 if (thread_sp->IsUserReady()) { 388 if (new_threads) 389 new_threads->push_back(thread_sp); 390 391 currThreads.push_back(thread_sp); 392 } 393 } 394 } 395 396 m_threads.swap(currThreads); 397 m_current_thread.reset(); 398 399 // Free the vm memory given to us by ::task_threads() 400 vm_size_t thread_list_size = 401 (vm_size_t)(thread_list_count * sizeof(::thread_t)); 402 ::vm_deallocate(::mach_task_self(), (vm_address_t)thread_list, 403 thread_list_size); 404 } 405 } 406 return static_cast<uint32_t>(m_threads.size()); 407} 408 409// TODO implement 410#if 0 411 412void 413NativeThreadListDarwin::CurrentThread (MachThreadSP& thread_sp) 414{ 415 // locker will keep a mutex locked until it goes out of scope 416 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 417 if (m_current_thread.get() == NULL) 418 { 419 // Figure out which thread is going to be our current thread. This is 420 // currently done by finding the first thread in the list that has a 421 // valid exception. 422 const size_t num_threads = m_threads.size(); 423 for (uint32_t idx = 0; idx < num_threads; ++idx) 424 { 425 if (m_threads[idx]->GetStopException().IsValid()) 426 { 427 m_current_thread = m_threads[idx]; 428 break; 429 } 430 } 431 } 432 thread_sp = m_current_thread; 433} 434 435#endif 436 437void NativeThreadListDarwin::Dump(Stream &stream) const { 438 bool first = true; 439 440 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 441 for (auto thread_sp : m_threads) { 442 if (thread_sp) { 443 // Handle newlines between thread entries. 444 if (first) 445 first = false; 446 else 447 stream.PutChar('\n'); 448 thread_sp->Dump(stream); 449 } 450 } 451} 452 453void NativeThreadListDarwin::ProcessWillResume( 454 NativeProcessDarwin &process, const ResumeActionList &thread_actions) { 455 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 456 457 // Update our thread list, because sometimes libdispatch or the kernel will 458 // spawn threads while a task is suspended. 459 NativeThreadListDarwin::collection new_threads; 460 461// TODO implement this. 462#if 0 463 // First figure out if we were planning on running only one thread, and if 464 // so, force that thread to resume. 465 bool run_one_thread; 466 thread_t solo_thread = THREAD_NULL; 467 if ((thread_actions.GetSize() > 0) && 468 (thread_actions.NumActionsWithState(eStateStepping) + 469 thread_actions.NumActionsWithState (eStateRunning) == 1)) 470 { 471 run_one_thread = true; 472 const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst(); 473 size_t num_actions = thread_actions.GetSize(); 474 for (size_t i = 0; i < num_actions; i++, action_ptr++) 475 { 476 if (action_ptr->state == eStateStepping || action_ptr->state == eStateRunning) 477 { 478 solo_thread = action_ptr->tid; 479 break; 480 } 481 } 482 } 483 else 484 run_one_thread = false; 485#endif 486 487 UpdateThreadList(process, true, &new_threads); 488 489#if 0 490 DNBThreadResumeAction resume_new_threads = { -1U, eStateRunning, 0, INVALID_NUB_ADDRESS }; 491 // If we are planning to run only one thread, any new threads should be 492 // suspended. 493 if (run_one_thread) 494 resume_new_threads.state = eStateSuspended; 495 496 const size_t num_new_threads = new_threads.size(); 497 const size_t num_threads = m_threads.size(); 498 for (uint32_t idx = 0; idx < num_threads; ++idx) 499 { 500 MachThread *thread = m_threads[idx].get(); 501 bool handled = false; 502 for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx) 503 { 504 if (thread == new_threads[new_idx].get()) 505 { 506 thread->ThreadWillResume(&resume_new_threads); 507 handled = true; 508 break; 509 } 510 } 511 512 if (!handled) 513 { 514 const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true); 515 // There must always be a thread action for every thread. 516 assert (thread_action); 517 bool others_stopped = false; 518 if (solo_thread == thread->ThreadID()) 519 others_stopped = true; 520 thread->ThreadWillResume (thread_action, others_stopped); 521 } 522 } 523 524 if (new_threads.size()) 525 { 526 for (uint32_t idx = 0; idx < num_new_threads; ++idx) 527 { 528 DNBLogThreadedIf (LOG_THREAD, "NativeThreadListDarwin::ProcessWillResume (pid = %4.4x) stop-id=%u, resuming newly discovered thread: 0x%8.8" PRIx64 ", thread-is-user-ready=%i)", 529 process->ProcessID(), 530 process->StopCount(), 531 new_threads[idx]->ThreadID(), 532 new_threads[idx]->IsUserReady()); 533 } 534 } 535#endif 536} 537 538uint32_t NativeThreadListDarwin::ProcessDidStop(NativeProcessDarwin &process) { 539 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 540 541 // Update our thread list. 542 UpdateThreadList(process, true); 543 544 for (auto thread_sp : m_threads) { 545 if (thread_sp) 546 thread_sp->ThreadDidStop(); 547 } 548 return (uint32_t)m_threads.size(); 549} 550 551// Check each thread in our thread list to see if we should notify our client 552// of the current halt in execution. 553// 554// Breakpoints can have callback functions associated with them than can return 555// true to stop, or false to continue executing the inferior. 556// 557// RETURNS 558// true if we should stop and notify our clients 559// false if we should resume our child process and skip notification 560bool NativeThreadListDarwin::ShouldStop(bool &step_more) { 561 std::lock_guard<std::recursive_mutex> locker(m_threads_mutex); 562 for (auto thread_sp : m_threads) { 563 if (thread_sp && thread_sp->ShouldStop(step_more)) 564 return true; 565 } 566 return false; 567} 568 569// Implement. 570#if 0 571 572void 573NativeThreadListDarwin::NotifyBreakpointChanged (const DNBBreakpoint *bp) 574{ 575 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 576 const size_t num_threads = m_threads.size(); 577 for (uint32_t idx = 0; idx < num_threads; ++idx) 578 { 579 m_threads[idx]->NotifyBreakpointChanged(bp); 580 } 581} 582 583 584uint32_t 585NativeThreadListDarwin::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const 586{ 587 if (bp != NULL) 588 { 589 const size_t num_threads = m_threads.size(); 590 for (uint32_t idx = 0; idx < num_threads; ++idx) 591 m_threads[idx]->EnableHardwareBreakpoint(bp); 592 } 593 return INVALID_NUB_HW_INDEX; 594} 595 596bool 597NativeThreadListDarwin::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const 598{ 599 if (bp != NULL) 600 { 601 const size_t num_threads = m_threads.size(); 602 for (uint32_t idx = 0; idx < num_threads; ++idx) 603 m_threads[idx]->DisableHardwareBreakpoint(bp); 604 } 605 return false; 606} 607 608// DNBWatchpointSet() -> MachProcess::CreateWatchpoint() -> 609// MachProcess::EnableWatchpoint() -> 610// NativeThreadListDarwin::EnableHardwareWatchpoint(). 611uint32_t 612NativeThreadListDarwin::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const 613{ 614 uint32_t hw_index = INVALID_NUB_HW_INDEX; 615 if (wp != NULL) 616 { 617 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 618 const size_t num_threads = m_threads.size(); 619 // On Mac OS X we have to prime the control registers for new threads. 620 // We do this using the control register data for the first thread, for 621 // lack of a better way of choosing. 622 bool also_set_on_task = true; 623 for (uint32_t idx = 0; idx < num_threads; ++idx) 624 { 625 if ((hw_index = m_threads[idx]->EnableHardwareWatchpoint(wp, also_set_on_task)) == INVALID_NUB_HW_INDEX) 626 { 627 // We know that idx failed for some reason. Let's rollback the 628 // transaction for [0, idx). 629 for (uint32_t i = 0; i < idx; ++i) 630 m_threads[i]->RollbackTransForHWP(); 631 return INVALID_NUB_HW_INDEX; 632 } 633 also_set_on_task = false; 634 } 635 // Notify each thread to commit the pending transaction. 636 for (uint32_t idx = 0; idx < num_threads; ++idx) 637 m_threads[idx]->FinishTransForHWP(); 638 639 } 640 return hw_index; 641} 642 643bool 644NativeThreadListDarwin::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const 645{ 646 if (wp != NULL) 647 { 648 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 649 const size_t num_threads = m_threads.size(); 650 651 // On Mac OS X we have to prime the control registers for new threads. 652 // We do this using the control register data for the first thread, for 653 // lack of a better way of choosing. 654 bool also_set_on_task = true; 655 for (uint32_t idx = 0; idx < num_threads; ++idx) 656 { 657 if (!m_threads[idx]->DisableHardwareWatchpoint(wp, also_set_on_task)) 658 { 659 // We know that idx failed for some reason. Let's rollback the 660 // transaction for [0, idx). 661 for (uint32_t i = 0; i < idx; ++i) 662 m_threads[i]->RollbackTransForHWP(); 663 return false; 664 } 665 also_set_on_task = false; 666 } 667 // Notify each thread to commit the pending transaction. 668 for (uint32_t idx = 0; idx < num_threads; ++idx) 669 m_threads[idx]->FinishTransForHWP(); 670 671 return true; 672 } 673 return false; 674} 675 676uint32_t 677NativeThreadListDarwin::NumSupportedHardwareWatchpoints () const 678{ 679 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 680 const size_t num_threads = m_threads.size(); 681 // Use an arbitrary thread to retrieve the number of supported hardware 682 // watchpoints. 683 if (num_threads) 684 return m_threads[0]->NumSupportedHardwareWatchpoints(); 685 return 0; 686} 687 688uint32_t 689NativeThreadListDarwin::GetThreadIndexForThreadStoppedWithSignal (const int signo) const 690{ 691 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 692 uint32_t should_stop = false; 693 const size_t num_threads = m_threads.size(); 694 for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) 695 { 696 if (m_threads[idx]->GetStopException().SoftSignal () == signo) 697 return idx; 698 } 699 return UINT32_MAX; 700} 701 702#endif 703