MachThreadList.cpp revision 02274ed47ccff35a64870331f6e2c88c711ca0f9
1//===-- MachThreadList.cpp --------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  Created by Greg Clayton on 6/19/07.
11//
12//===----------------------------------------------------------------------===//
13
14#include "MachThreadList.h"
15#include "DNBLog.h"
16#include "DNBThreadResumeActions.h"
17#include "MachProcess.h"
18
19MachThreadList::MachThreadList() :
20    m_threads(),
21    m_threads_mutex(PTHREAD_MUTEX_RECURSIVE)
22{
23}
24
25MachThreadList::~MachThreadList()
26{
27}
28
29// Not thread safe, must lock m_threads_mutex prior to using this function.
30uint32_t
31MachThreadList::GetThreadIndexByID(thread_t tid) const
32{
33    uint32_t idx = 0;
34    const uint32_t num_threads = m_threads.size();
35    for (idx = 0; idx < num_threads; ++idx)
36    {
37        if (m_threads[idx]->ThreadID() == tid)
38            return idx;
39    }
40    return ~((uint32_t)0);
41}
42
43nub_state_t
44MachThreadList::GetState(thread_t tid)
45{
46    uint32_t idx = GetThreadIndexByID(tid);
47    if (idx < m_threads.size())
48        return m_threads[idx]->GetState();
49    return eStateInvalid;
50}
51
52const char *
53MachThreadList::GetName (thread_t tid)
54{
55    uint32_t idx = GetThreadIndexByID(tid);
56    if (idx < m_threads.size())
57        return m_threads[idx]->GetName();
58    return NULL;
59}
60
61nub_thread_t
62MachThreadList::SetCurrentThread(thread_t tid)
63{
64    uint32_t idx = GetThreadIndexByID(tid);
65    if (idx < m_threads.size())
66        m_current_thread = m_threads[idx];
67
68    if (m_current_thread.get())
69        return m_current_thread->ThreadID();
70    return INVALID_NUB_THREAD;
71}
72
73
74bool
75MachThreadList::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
76{
77    uint32_t idx = GetThreadIndexByID(tid);
78    if (idx < m_threads.size())
79        return m_threads[idx]->GetStopException().GetStopInfo(stop_info);
80    return false;
81}
82
83bool
84MachThreadList::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info)
85{
86    mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
87    return ::thread_info (tid, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS;
88}
89
90void
91MachThreadList::DumpThreadStoppedReason(nub_thread_t tid) const
92{
93    uint32_t idx = GetThreadIndexByID(tid);
94    if (idx < m_threads.size())
95        m_threads[idx]->GetStopException().DumpStopReason();
96}
97
98const char *
99MachThreadList::GetThreadInfo(nub_thread_t tid) const
100{
101    uint32_t idx = GetThreadIndexByID(tid);
102    if (idx < m_threads.size())
103        return m_threads[idx]->GetBasicInfoAsString();
104    return NULL;
105}
106
107bool
108MachThreadList::GetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value ) const
109{
110    uint32_t idx = GetThreadIndexByID(tid);
111    if (idx < m_threads.size())
112        return m_threads[idx]->GetRegisterValue(reg_set_idx, reg_idx, reg_value);
113
114    return false;
115}
116
117bool
118MachThreadList::SetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value ) const
119{
120    uint32_t idx = GetThreadIndexByID(tid);
121    if (idx < m_threads.size())
122        return m_threads[idx]->SetRegisterValue(reg_set_idx, reg_idx, reg_value);
123
124    return false;
125}
126
127nub_size_t
128MachThreadList::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len)
129{
130    uint32_t idx = GetThreadIndexByID(tid);
131    if (idx < m_threads.size())
132        return m_threads[idx]->GetRegisterContext (buf, buf_len);
133    return 0;
134}
135
136nub_size_t
137MachThreadList::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len)
138{
139    uint32_t idx = GetThreadIndexByID(tid);
140    if (idx < m_threads.size())
141        return m_threads[idx]->SetRegisterContext (buf, buf_len);
142    return 0;
143}
144
145nub_size_t
146MachThreadList::NumThreads() const
147{
148    return m_threads.size();
149}
150
151nub_thread_t
152MachThreadList::ThreadIDAtIndex(nub_size_t idx) const
153{
154    if (idx < m_threads.size())
155        return m_threads[idx]->ThreadID();
156    return INVALID_NUB_THREAD;
157}
158
159nub_thread_t
160MachThreadList::CurrentThreadID ( )
161{
162    MachThreadSP threadSP;
163    CurrentThread(threadSP);
164    if (threadSP.get())
165        return threadSP->ThreadID();
166    return INVALID_NUB_THREAD;
167}
168
169bool
170MachThreadList::NotifyException(MachException::Data& exc)
171{
172    uint32_t idx = GetThreadIndexByID(exc.thread_port);
173    if (idx < m_threads.size())
174    {
175        m_threads[idx]->NotifyException(exc);
176        return true;
177    }
178    return false;
179}
180
181/*
182MachThreadList::const_iterator
183MachThreadList::FindThreadByID(thread_t tid) const
184{
185    const_iterator pos;
186    const_iterator end = m_threads.end();
187    for (pos = m_threads.begin(); pos != end; ++pos)
188    {
189        if (pos->ThreadID() == tid)
190            return pos;
191    }
192    return NULL;
193}
194*/
195void
196MachThreadList::Clear()
197{
198    m_threads.clear();
199}
200
201uint32_t
202MachThreadList::UpdateThreadList(MachProcess *process, bool update)
203{
204    // locker will keep a mutex locked until it goes out of scope
205    DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThreadList::UpdateThreadList (pid = %4.4x, update = %u )", process->ProcessID(), update);
206    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
207
208    if (m_threads.empty() || update)
209    {
210        thread_array_t thread_list = NULL;
211        mach_msg_type_number_t thread_list_count = 0;
212        task_t task = process->Task().TaskPort();
213        DNBError err(::task_threads (task, &thread_list, &thread_list_count), DNBError::MachKernel);
214
215        if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
216            err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count);
217
218        if (err.Error() == KERN_SUCCESS && thread_list_count > 0)
219        {
220            MachThreadList::collection currThreads;
221            const size_t numOldThreads = m_threads.size();
222            size_t idx;
223            // Iterator through the current thread list and see which threads
224            // we already have in our list (keep them), which ones we don't
225            // (add them), and which ones are not around anymore (remove them).
226            for (idx = 0; idx < thread_list_count; ++idx)
227            {
228                uint32_t existing_idx = 0;
229                if (numOldThreads > 0)
230                    existing_idx = GetThreadIndexByID(thread_list[idx]);
231                if (existing_idx < numOldThreads)
232                {
233                    // Keep the existing thread class
234                    currThreads.push_back(m_threads[existing_idx]);
235                }
236                else
237                {
238                    // We don't have this thread, lets add it.
239                    MachThreadSP threadSP(new MachThread(process, thread_list[idx]));
240                    // Make sure the thread is ready to be displayed and shown to users
241                    // before we add this thread to our list...
242                    if (threadSP->IsUserReady())
243                        currThreads.push_back(threadSP);
244                }
245            }
246
247            m_threads.swap(currThreads);
248            m_current_thread.reset();
249
250            // Free the vm memory given to us by ::task_threads()
251            vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t));
252            ::vm_deallocate (::mach_task_self(),
253                             (vm_address_t)thread_list,
254                             thread_list_size);
255        }
256    }
257    return m_threads.size();
258}
259
260
261void
262MachThreadList::CurrentThread(MachThreadSP& threadSP)
263{
264    // locker will keep a mutex locked until it goes out of scope
265    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
266    if (m_current_thread.get() == NULL)
267    {
268        // Figure out which thread is going to be our current thread.
269        // This is currently done by finding the first thread in the list
270        // that has a valid exception.
271        const size_t num_threads = m_threads.size();
272        size_t idx;
273        for (idx = 0; idx < num_threads; ++idx)
274        {
275            MachThread *thread = m_threads[idx].get();
276            if (thread->GetStopException().IsValid())
277            {
278                m_current_thread = m_threads[idx];
279                break;
280            }
281        }
282    }
283    threadSP = m_current_thread;
284}
285
286void
287MachThreadList::GetRegisterState(int flavor, bool force)
288{
289    uint32_t idx = 0;
290    const uint32_t num_threads = m_threads.size();
291    for (idx = 0; idx < num_threads; ++idx)
292    {
293        m_threads[idx]->GetRegisterState(flavor, force);
294    }
295}
296
297void
298MachThreadList::SetRegisterState(int flavor)
299{
300    uint32_t idx = 0;
301    const uint32_t num_threads = m_threads.size();
302    for (idx = 0; idx < num_threads; ++idx)
303    {
304        m_threads[idx]->SetRegisterState(flavor);
305    }
306}
307
308void
309MachThreadList::Dump() const
310{
311    uint32_t idx = 0;
312    const uint32_t num_threads = m_threads.size();
313    for (idx = 0; idx < num_threads; ++idx)
314    {
315        m_threads[idx]->Dump(idx);
316    }
317}
318
319
320void
321MachThreadList::ProcessWillResume(MachProcess *process, const DNBThreadResumeActions &thread_actions)
322{
323    uint32_t idx = 0;
324    const uint32_t num_threads = m_threads.size();
325
326    for (idx = 0; idx < num_threads; ++idx)
327    {
328        MachThread *thread = m_threads[idx].get();
329
330        const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true);
331        // There must always be a thread action for every thread.
332        assert (thread_action);
333        thread->ThreadWillResume (thread_action);
334    }
335}
336
337uint32_t
338MachThreadList::ProcessDidStop(MachProcess *process)
339{
340    // Update our thread list
341    const uint32_t num_threads = UpdateThreadList(process, true);
342    uint32_t idx = 0;
343    for (idx = 0; idx < num_threads; ++idx)
344    {
345        m_threads[idx]->ThreadDidStop();
346    }
347    return num_threads;
348}
349
350//----------------------------------------------------------------------
351// Check each thread in our thread list to see if we should notify our
352// client of the current halt in execution.
353//
354// Breakpoints can have callback functions associated with them than
355// can return true to stop, or false to continue executing the inferior.
356//
357// RETURNS
358//    true if we should stop and notify our clients
359//    false if we should resume our child process and skip notification
360//----------------------------------------------------------------------
361bool
362MachThreadList::ShouldStop(bool &step_more)
363{
364    uint32_t should_stop = false;
365    const uint32_t num_threads = m_threads.size();
366    uint32_t idx = 0;
367    for (idx = 0; !should_stop && idx < num_threads; ++idx)
368    {
369        should_stop = m_threads[idx]->ShouldStop(step_more);
370    }
371    return should_stop;
372}
373
374
375void
376MachThreadList::NotifyBreakpointChanged (const DNBBreakpoint *bp)
377{
378    uint32_t idx = 0;
379    const uint32_t num_threads = m_threads.size();
380    for (idx = 0; idx < num_threads; ++idx)
381    {
382        m_threads[idx]->NotifyBreakpointChanged(bp);
383    }
384}
385
386
387uint32_t
388MachThreadList::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const
389{
390    if (bp != NULL)
391    {
392        uint32_t idx = GetThreadIndexByID(bp->ThreadID());
393        if (idx < m_threads.size())
394            return m_threads[idx]->EnableHardwareBreakpoint(bp);
395    }
396    return INVALID_NUB_HW_INDEX;
397}
398
399bool
400MachThreadList::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const
401{
402    if (bp != NULL)
403    {
404        uint32_t idx = GetThreadIndexByID(bp->ThreadID());
405        if (idx < m_threads.size())
406            return m_threads[idx]->DisableHardwareBreakpoint(bp);
407    }
408    return false;
409}
410
411uint32_t
412MachThreadList::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const
413{
414    if (wp != NULL)
415    {
416        uint32_t idx = GetThreadIndexByID(wp->ThreadID());
417        if (idx < m_threads.size())
418            return m_threads[idx]->EnableHardwareWatchpoint(wp);
419    }
420    return INVALID_NUB_HW_INDEX;
421}
422
423bool
424MachThreadList::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const
425{
426    if (wp != NULL)
427    {
428        uint32_t idx = GetThreadIndexByID(wp->ThreadID());
429        if (idx < m_threads.size())
430            return m_threads[idx]->DisableHardwareWatchpoint(wp);
431    }
432    return false;
433}
434
435uint32_t
436MachThreadList::GetThreadIndexForThreadStoppedWithSignal (const int signo) const
437{
438    uint32_t should_stop = false;
439    const uint32_t num_threads = m_threads.size();
440    uint32_t idx = 0;
441    for (idx = 0; !should_stop && idx < num_threads; ++idx)
442    {
443        if (m_threads[idx]->GetStopException().SoftSignal () == signo)
444            return idx;
445    }
446    return UINT32_MAX;
447}
448
449