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