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