MachThreadList.cpp revision 24943d2ee8bfaa7cf5893e4709143924157a5c1e
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                    currThreads.push_back(threadSP);
241                }
242            }
243
244            m_threads.swap(currThreads);
245            m_current_thread.reset();
246
247            // Free the vm memory given to us by ::task_threads()
248            vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t));
249            ::vm_deallocate (::mach_task_self(),
250                             (vm_address_t)thread_list,
251                             thread_list_size);
252        }
253    }
254    return m_threads.size();
255}
256
257
258void
259MachThreadList::CurrentThread(MachThreadSP& threadSP)
260{
261    // locker will keep a mutex locked until it goes out of scope
262    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
263    if (m_current_thread.get() == NULL)
264    {
265        // Figure out which thread is going to be our current thread.
266        // This is currently done by finding the first thread in the list
267        // that has a valid exception.
268        const size_t num_threads = m_threads.size();
269        size_t idx;
270        for (idx = 0; idx < num_threads; ++idx)
271        {
272            MachThread *thread = m_threads[idx].get();
273            if (thread->GetStopException().IsValid())
274            {
275                m_current_thread = m_threads[idx];
276                break;
277            }
278        }
279    }
280    threadSP = m_current_thread;
281}
282
283void
284MachThreadList::GetRegisterState(int flavor, bool force)
285{
286    uint32_t idx = 0;
287    const uint32_t num_threads = m_threads.size();
288    for (idx = 0; idx < num_threads; ++idx)
289    {
290        m_threads[idx]->GetRegisterState(flavor, force);
291    }
292}
293
294void
295MachThreadList::SetRegisterState(int flavor)
296{
297    uint32_t idx = 0;
298    const uint32_t num_threads = m_threads.size();
299    for (idx = 0; idx < num_threads; ++idx)
300    {
301        m_threads[idx]->SetRegisterState(flavor);
302    }
303}
304
305void
306MachThreadList::Dump() const
307{
308    uint32_t idx = 0;
309    const uint32_t num_threads = m_threads.size();
310    for (idx = 0; idx < num_threads; ++idx)
311    {
312        m_threads[idx]->Dump(idx);
313    }
314}
315
316
317void
318MachThreadList::ProcessWillResume(MachProcess *process, const DNBThreadResumeActions &thread_actions)
319{
320    uint32_t idx = 0;
321    const uint32_t num_threads = m_threads.size();
322
323    for (idx = 0; idx < num_threads; ++idx)
324    {
325        MachThread *thread = m_threads[idx].get();
326
327        const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true);
328        // There must always be a thread action for every thread.
329        assert (thread_action);
330        thread->ThreadWillResume (thread_action);
331    }
332}
333
334uint32_t
335MachThreadList::ProcessDidStop(MachProcess *process)
336{
337    // Update our thread list
338    const uint32_t num_threads = UpdateThreadList(process, true);
339    uint32_t idx = 0;
340    for (idx = 0; idx < num_threads; ++idx)
341    {
342        m_threads[idx]->ThreadDidStop();
343    }
344    return num_threads;
345}
346
347//----------------------------------------------------------------------
348// Check each thread in our thread list to see if we should notify our
349// client of the current halt in execution.
350//
351// Breakpoints can have callback functions associated with them than
352// can return true to stop, or false to continue executing the inferior.
353//
354// RETURNS
355//    true if we should stop and notify our clients
356//    false if we should resume our child process and skip notification
357//----------------------------------------------------------------------
358bool
359MachThreadList::ShouldStop(bool &step_more)
360{
361    uint32_t should_stop = false;
362    const uint32_t num_threads = m_threads.size();
363    uint32_t idx = 0;
364    for (idx = 0; !should_stop && idx < num_threads; ++idx)
365    {
366        should_stop = m_threads[idx]->ShouldStop(step_more);
367    }
368    return should_stop;
369}
370
371
372void
373MachThreadList::NotifyBreakpointChanged (const DNBBreakpoint *bp)
374{
375    uint32_t idx = 0;
376    const uint32_t num_threads = m_threads.size();
377    for (idx = 0; idx < num_threads; ++idx)
378    {
379        m_threads[idx]->NotifyBreakpointChanged(bp);
380    }
381}
382
383
384uint32_t
385MachThreadList::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const
386{
387    if (bp != NULL)
388    {
389        uint32_t idx = GetThreadIndexByID(bp->ThreadID());
390        if (idx < m_threads.size())
391            return m_threads[idx]->EnableHardwareBreakpoint(bp);
392    }
393    return INVALID_NUB_HW_INDEX;
394}
395
396bool
397MachThreadList::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const
398{
399    if (bp != NULL)
400    {
401        uint32_t idx = GetThreadIndexByID(bp->ThreadID());
402        if (idx < m_threads.size())
403            return m_threads[idx]->DisableHardwareBreakpoint(bp);
404    }
405    return false;
406}
407
408uint32_t
409MachThreadList::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const
410{
411    if (wp != NULL)
412    {
413        uint32_t idx = GetThreadIndexByID(wp->ThreadID());
414        if (idx < m_threads.size())
415            return m_threads[idx]->EnableHardwareWatchpoint(wp);
416    }
417    return INVALID_NUB_HW_INDEX;
418}
419
420bool
421MachThreadList::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const
422{
423    if (wp != NULL)
424    {
425        uint32_t idx = GetThreadIndexByID(wp->ThreadID());
426        if (idx < m_threads.size())
427            return m_threads[idx]->DisableHardwareWatchpoint(wp);
428    }
429    return false;
430}
431
432
433