MachThread.cpp revision e1f50b9df1299f6b9181b5ac2699ed4a3ad38a59
1//===-- MachThread.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 "MachThread.h"
15#include "MachProcess.h"
16#include "DNBLog.h"
17#include "DNB.h"
18
19static uint32_t
20GetSequenceID()
21{
22    static uint32_t g_nextID = 0;
23    return ++g_nextID;
24}
25
26MachThread::MachThread (MachProcess *process, thread_t thread) :
27    m_process (process),
28    m_tid (thread),
29    m_seq_id (GetSequenceID()),
30    m_state (eStateUnloaded),
31    m_state_mutex (PTHREAD_MUTEX_RECURSIVE),
32    m_breakID (INVALID_NUB_BREAK_ID),
33    m_suspendCount (0),
34    m_arch_ap (DNBArchProtocol::Create (this)),
35    m_reg_sets (m_arch_ap->GetRegisterSetInfo (&n_num_reg_sets))
36{
37    // Get the thread state so we know if a thread is in a state where we can't
38    // muck with it and also so we get the suspend count correct in case it was
39    // already suspended
40    GetBasicInfo();
41    DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::MachThread ( process = %p, tid = 0x%4.4x, seq_id = %u )", &m_process, m_tid, m_seq_id);
42}
43
44MachThread::~MachThread()
45{
46    DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::~MachThread() for tid = 0x%4.4x (%u)", m_tid, m_seq_id);
47}
48
49
50
51uint32_t
52MachThread::Suspend()
53{
54    DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
55    if (ThreadIDIsValid(m_tid))
56    {
57        DNBError err(::thread_suspend (m_tid), DNBError::MachKernel);
58        if (err.Success())
59            m_suspendCount++;
60        if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
61            err.LogThreaded("::thread_suspend (%4.4x)", m_tid);
62    }
63    return SuspendCount();
64}
65
66uint32_t
67MachThread::Resume()
68{
69    DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
70    if (ThreadIDIsValid(m_tid))
71    {
72        RestoreSuspendCount();
73    }
74    return SuspendCount();
75}
76
77bool
78MachThread::RestoreSuspendCount()
79{
80    DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
81    DNBError err;
82    if (ThreadIDIsValid(m_tid) == false)
83        return false;
84    if (m_suspendCount > 0)
85    {
86        while (m_suspendCount > 0)
87        {
88            err = ::thread_resume (m_tid);
89            if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
90                err.LogThreaded("::thread_resume (%4.4x)", m_tid);
91            if (err.Success())
92                --m_suspendCount;
93            else
94            {
95                if (GetBasicInfo())
96                    m_suspendCount = m_basicInfo.suspend_count;
97                else
98                    m_suspendCount = 0;
99                return false; // ???
100            }
101        }
102    }
103    // We don't currently really support resuming a thread that was externally
104    // suspended. If/when we do, we will need to make the code below work and
105    // m_suspendCount will need to become signed instead of unsigned.
106//    else if (m_suspendCount < 0)
107//    {
108//        while (m_suspendCount < 0)
109//        {
110//            err = ::thread_suspend (m_tid);
111//            if (err.Success())
112//                ++m_suspendCount;
113//            if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
114//                err.LogThreaded("::thread_suspend (%4.4x)", m_tid);
115//        }
116//    }
117    return true;
118}
119
120
121const char *
122MachThread::GetBasicInfoAsString () const
123{
124    static char g_basic_info_string[1024];
125    struct thread_basic_info basicInfo;
126
127    if (GetBasicInfo(m_tid, &basicInfo))
128    {
129
130//        char run_state_str[32];
131//        size_t run_state_str_size = sizeof(run_state_str);
132//        switch (basicInfo.run_state)
133//        {
134//        case TH_STATE_RUNNING:          strncpy(run_state_str, "running", run_state_str_size); break;
135//        case TH_STATE_STOPPED:          strncpy(run_state_str, "stopped", run_state_str_size); break;
136//        case TH_STATE_WAITING:          strncpy(run_state_str, "waiting", run_state_str_size); break;
137//        case TH_STATE_UNINTERRUPTIBLE:  strncpy(run_state_str, "uninterruptible", run_state_str_size); break;
138//        case TH_STATE_HALTED:           strncpy(run_state_str, "halted", run_state_str_size); break;
139//        default:                        snprintf(run_state_str, run_state_str_size, "%d", basicInfo.run_state); break;    // ???
140//        }
141        float user = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
142        float system = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
143        snprintf(g_basic_info_string, sizeof(g_basic_info_string), "Thread 0x%4.4x: user=%f system=%f cpu=%d sleep_time=%d",
144            InferiorThreadID(),
145            user,
146            system,
147            basicInfo.cpu_usage,
148            basicInfo.sleep_time);
149
150        return g_basic_info_string;
151    }
152    return NULL;
153}
154
155thread_t
156MachThread::InferiorThreadID() const
157{
158    mach_msg_type_number_t i;
159    mach_port_name_array_t names;
160    mach_port_type_array_t types;
161    mach_msg_type_number_t ncount, tcount;
162    thread_t inferior_tid = INVALID_NUB_THREAD;
163    task_t my_task = ::mach_task_self();
164    task_t task = m_process->Task().TaskPort();
165
166    kern_return_t kret = ::mach_port_names (task, &names, &ncount, &types, &tcount);
167    if (kret == KERN_SUCCESS)
168    {
169
170        for (i = 0; i < ncount; i++)
171        {
172            mach_port_t my_name;
173            mach_msg_type_name_t my_type;
174
175            kret = ::mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &my_name, &my_type);
176            if (kret == KERN_SUCCESS)
177            {
178                ::mach_port_deallocate (my_task, my_name);
179                if (my_name == m_tid)
180                {
181                    inferior_tid = names[i];
182                    break;
183                }
184            }
185        }
186        // Free up the names and types
187        ::vm_deallocate (my_task, (vm_address_t) names, ncount * sizeof (mach_port_name_t));
188        ::vm_deallocate (my_task, (vm_address_t) types, tcount * sizeof (mach_port_type_t));
189    }
190    return inferior_tid;
191}
192
193bool
194MachThread::IsUserReady()
195{
196    if (m_basicInfo.run_state == 0)
197        GetBasicInfo ();
198
199    switch (m_basicInfo.run_state)
200    {
201    default:
202    case TH_STATE_UNINTERRUPTIBLE:
203        break;
204
205    case TH_STATE_RUNNING:
206    case TH_STATE_STOPPED:
207    case TH_STATE_WAITING:
208    case TH_STATE_HALTED:
209        return true;
210    }
211    return false;
212}
213
214struct thread_basic_info *
215MachThread::GetBasicInfo ()
216{
217    if (MachThread::GetBasicInfo(m_tid, &m_basicInfo))
218        return &m_basicInfo;
219    return NULL;
220}
221
222
223bool
224MachThread::GetBasicInfo(thread_t thread, struct thread_basic_info *basicInfoPtr)
225{
226    if (ThreadIDIsValid(thread))
227    {
228        unsigned int info_count = THREAD_BASIC_INFO_COUNT;
229        kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count);
230        if (err == KERN_SUCCESS)
231            return true;
232    }
233    ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info));
234    return false;
235}
236
237
238bool
239MachThread::ThreadIDIsValid(thread_t thread)
240{
241    return thread != THREAD_NULL;
242}
243
244bool
245MachThread::GetRegisterState(int flavor, bool force)
246{
247    return m_arch_ap->GetRegisterState(flavor, force) == KERN_SUCCESS;
248}
249
250bool
251MachThread::SetRegisterState(int flavor)
252{
253    return m_arch_ap->SetRegisterState(flavor) == KERN_SUCCESS;
254}
255
256uint64_t
257MachThread::GetPC(uint64_t failValue)
258{
259    // Get program counter
260    return m_arch_ap->GetPC(failValue);
261}
262
263bool
264MachThread::SetPC(uint64_t value)
265{
266    // Set program counter
267    return m_arch_ap->SetPC(value);
268}
269
270uint64_t
271MachThread::GetSP(uint64_t failValue)
272{
273    // Get stack pointer
274    return m_arch_ap->GetSP(failValue);
275}
276
277nub_process_t
278MachThread::ProcessID() const
279{
280    if (m_process)
281        return m_process->ProcessID();
282    return INVALID_NUB_PROCESS;
283}
284
285void
286MachThread::Dump(uint32_t index)
287{
288    const char * thread_run_state = NULL;
289
290    switch (m_basicInfo.run_state)
291    {
292    case TH_STATE_RUNNING:          thread_run_state = "running"; break;    // 1 thread is running normally
293    case TH_STATE_STOPPED:          thread_run_state = "stopped"; break;    // 2 thread is stopped
294    case TH_STATE_WAITING:          thread_run_state = "waiting"; break;    // 3 thread is waiting normally
295    case TH_STATE_UNINTERRUPTIBLE:  thread_run_state = "uninter"; break;    // 4 thread is in an uninterruptible wait
296    case TH_STATE_HALTED:           thread_run_state = "halted "; break;     // 5 thread is halted at a
297    default:                        thread_run_state = "???"; break;
298    }
299
300    DNBLogThreaded("[%3u] #%3u tid: 0x%4.4x, pc: 0x%16.16llx, sp: 0x%16.16llx, breakID: %3d, user: %d.%06.6d, system: %d.%06.6d, cpu: %2d, policy: %2d, run_state: %2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
301        index,
302        m_seq_id,
303        m_tid,
304        GetPC(INVALID_NUB_ADDRESS),
305        GetSP(INVALID_NUB_ADDRESS),
306        m_breakID,
307        m_basicInfo.user_time.seconds,      m_basicInfo.user_time.microseconds,
308        m_basicInfo.system_time.seconds,    m_basicInfo.system_time.microseconds,
309        m_basicInfo.cpu_usage,
310        m_basicInfo.policy,
311        m_basicInfo.run_state,
312        thread_run_state,
313        m_basicInfo.flags,
314        m_basicInfo.suspend_count, m_suspendCount,
315        m_basicInfo.sleep_time);
316    //DumpRegisterState(0);
317}
318
319void
320MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action)
321{
322    if (thread_action->addr != INVALID_NUB_ADDRESS)
323        SetPC (thread_action->addr);
324
325    SetState (thread_action->state);
326    switch (thread_action->state)
327    {
328    case eStateStopped:
329    case eStateSuspended:
330        Suspend();
331        break;
332
333    case eStateRunning:
334    case eStateStepping:
335        Resume();
336        break;
337    default:
338        break;
339    }
340    m_arch_ap->ThreadWillResume();
341    m_stop_exception.Clear();
342}
343
344nub_break_t
345MachThread::CurrentBreakpoint()
346{
347    return m_process->Breakpoints().FindIDByAddress(GetPC());
348}
349
350bool
351MachThread::ShouldStop(bool &step_more)
352{
353    // See if this thread is at a breakpoint?
354    nub_break_t breakID = CurrentBreakpoint();
355
356    if (NUB_BREAK_ID_IS_VALID(breakID))
357    {
358        // This thread is sitting at a breakpoint, ask the breakpoint
359        // if we should be stopping here.
360        if (Process()->Breakpoints().ShouldStop(ProcessID(), ThreadID(), breakID))
361            return true;
362        else
363        {
364            // The breakpoint said we shouldn't stop, but we may have gotten
365            // a signal or the user may have requested to stop in some other
366            // way. Stop if we have a valid exception (this thread won't if
367            // another thread was the reason this process stopped) and that
368            // exception, is NOT a breakpoint exception (a common case would
369            // be a SIGINT signal).
370            if (GetStopException().IsValid() && !GetStopException().IsBreakpoint())
371                return true;
372        }
373    }
374    else
375    {
376        if (m_arch_ap->StepNotComplete())
377        {
378            step_more = true;
379            return false;
380        }
381        // The thread state is used to let us know what the thread was
382        // trying to do. MachThread::ThreadWillResume() will set the
383        // thread state to various values depending if the thread was
384        // the current thread and if it was to be single stepped, or
385        // resumed.
386        if (GetState() == eStateRunning)
387        {
388            // If our state is running, then we should continue as we are in
389            // the process of stepping over a breakpoint.
390            return false;
391        }
392        else
393        {
394            // Stop if we have any kind of valid exception for this
395            // thread.
396            if (GetStopException().IsValid())
397                return true;
398        }
399    }
400    return false;
401}
402bool
403MachThread::IsStepping()
404{
405#if ENABLE_AUTO_STEPPING_OVER_BP
406    // Return true if this thread is currently being stepped.
407    // MachThread::ThreadWillResume currently determines this by looking if we
408    // have been asked to single step, or if we are at a breakpoint instruction
409    // and have been asked to resume. In the latter case we need to disable the
410    // breakpoint we are at, single step, re-enable and continue.
411    nub_state_t state = GetState();
412    return ((state == eStateStepping) ||
413            (state == eStateRunning && NUB_BREAK_ID_IS_VALID(CurrentBreakpoint())));
414#else
415    return GetState() == eStateStepping;
416#endif
417}
418
419
420bool
421MachThread::ThreadDidStop()
422{
423    // This thread has existed prior to resuming under debug nub control,
424    // and has just been stopped. Do any cleanup that needs to be done
425    // after running.
426
427    // The thread state and breakpoint will still have the same values
428    // as they had prior to resuming the thread, so it makes it easy to check
429    // if we were trying to step a thread, or we tried to resume while being
430    // at a breakpoint.
431
432    // When this method gets called, the process state is still in the
433    // state it was in while running so we can act accordingly.
434    m_arch_ap->ThreadDidStop();
435
436
437    // We may have suspended this thread so the primary thread could step
438    // without worrying about race conditions, so lets restore our suspend
439    // count.
440    RestoreSuspendCount();
441
442    // Update the basic information for a thread
443    MachThread::GetBasicInfo(m_tid, &m_basicInfo);
444
445#if ENABLE_AUTO_STEPPING_OVER_BP
446    // See if we were at a breakpoint when we last resumed that we disabled,
447    // re-enable it.
448    nub_break_t breakID = CurrentBreakpoint();
449
450    if (NUB_BREAK_ID_IS_VALID(breakID))
451    {
452        m_process->EnableBreakpoint(breakID);
453        if (m_basicInfo.suspend_count > 0)
454        {
455            SetState(eStateSuspended);
456        }
457        else
458        {
459            // If we last were at a breakpoint and we single stepped, our state
460            // will be "running" to indicate we need to continue after stepping
461            // over the breakpoint instruction. If we step over a breakpoint
462            // instruction, we need to stop.
463            if (GetState() == eStateRunning)
464            {
465                // Leave state set to running so we will continue automatically
466                // from this breakpoint
467            }
468            else
469            {
470                SetState(eStateStopped);
471            }
472        }
473    }
474    else
475    {
476        if (m_basicInfo.suspend_count > 0)
477        {
478            SetState(eStateSuspended);
479        }
480        else
481        {
482            SetState(eStateStopped);
483        }
484    }
485#else
486    if (m_basicInfo.suspend_count > 0)
487        SetState(eStateSuspended);
488    else
489        SetState(eStateStopped);
490#endif
491    return true;
492}
493
494bool
495MachThread::NotifyException(MachException::Data& exc)
496{
497    if (m_stop_exception.IsValid())
498    {
499        // We may have more than one exception for a thread, but we need to
500        // only remember the one that we will say is the reason we stopped.
501        // We may have been single stepping and also gotten a signal exception,
502        // so just remember the most pertinent one.
503        if (m_stop_exception.IsBreakpoint())
504            m_stop_exception = exc;
505    }
506    else
507    {
508        m_stop_exception = exc;
509    }
510    bool handled = m_arch_ap->NotifyException(exc);
511    if (!handled)
512    {
513        handled = true;
514//        switch (exc.exc_type)
515//        {
516//        case EXC_BAD_ACCESS:
517//            break;
518//        case EXC_BAD_INSTRUCTION:
519//            break;
520//        case EXC_ARITHMETIC:
521//            break;
522//        case EXC_EMULATION:
523//            break;
524//        case EXC_SOFTWARE:
525//            break;
526//        case EXC_BREAKPOINT:
527//            break;
528//        case EXC_SYSCALL:
529//            break;
530//        case EXC_MACH_SYSCALL:
531//            break;
532//        case EXC_RPC_ALERT:
533//            break;
534//        }
535    }
536    return handled;
537}
538
539
540nub_state_t
541MachThread::GetState()
542{
543    // If any other threads access this we will need a mutex for it
544    PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
545    return m_state;
546}
547
548void
549MachThread::SetState(nub_state_t state)
550{
551    PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
552    m_state = state;
553    DNBLogThreadedIf(LOG_THREAD, "MachThread::SetState ( %s ) for tid = 0x%4.4x", DNBStateAsString(state), m_tid);
554}
555
556uint32_t
557MachThread::GetNumRegistersInSet(int regSet) const
558{
559    if (regSet < n_num_reg_sets)
560        return m_reg_sets[regSet].num_registers;
561    return 0;
562}
563
564const char *
565MachThread::GetRegisterSetName(int regSet) const
566{
567    if (regSet < n_num_reg_sets)
568        return m_reg_sets[regSet].name;
569    return NULL;
570}
571
572const DNBRegisterInfo *
573MachThread::GetRegisterInfo(int regSet, int regIndex) const
574{
575    if (regSet < n_num_reg_sets)
576        if (regIndex < m_reg_sets[regSet].num_registers)
577            return &m_reg_sets[regSet].registers[regIndex];
578    return NULL;
579}
580void
581MachThread::DumpRegisterState(int regSet)
582{
583    if (regSet == REGISTER_SET_ALL)
584    {
585        for (regSet = 1; regSet < n_num_reg_sets; regSet++)
586            DumpRegisterState(regSet);
587    }
588    else
589    {
590        if (m_arch_ap->RegisterSetStateIsValid(regSet))
591        {
592            const size_t numRegisters = GetNumRegistersInSet(regSet);
593            size_t regIndex = 0;
594            DNBRegisterValueClass reg;
595            for (regIndex = 0; regIndex < numRegisters; ++regIndex)
596            {
597                if (m_arch_ap->GetRegisterValue(regSet, regIndex, &reg))
598                {
599                    reg.Dump(NULL, NULL);
600                }
601            }
602        }
603        else
604        {
605            DNBLog("%s: registers are not currently valid.", GetRegisterSetName(regSet));
606        }
607    }
608}
609
610const DNBRegisterSetInfo *
611MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets ) const
612{
613    *num_reg_sets = n_num_reg_sets;
614    return &m_reg_sets[0];
615}
616
617bool
618MachThread::GetRegisterValue ( uint32_t set, uint32_t reg, DNBRegisterValue *value )
619{
620    return m_arch_ap->GetRegisterValue(set, reg, value);
621}
622
623bool
624MachThread::SetRegisterValue ( uint32_t set, uint32_t reg, const DNBRegisterValue *value )
625{
626    return m_arch_ap->SetRegisterValue(set, reg, value);
627}
628
629nub_size_t
630MachThread::GetRegisterContext (void *buf, nub_size_t buf_len)
631{
632    return m_arch_ap->GetRegisterContext(buf, buf_len);
633}
634
635nub_size_t
636MachThread::SetRegisterContext (const void *buf, nub_size_t buf_len)
637{
638    return m_arch_ap->SetRegisterContext(buf, buf_len);
639}
640
641uint32_t
642MachThread::EnableHardwareBreakpoint (const DNBBreakpoint *bp)
643{
644    if (bp != NULL && bp->IsBreakpoint())
645        return m_arch_ap->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize());
646    return INVALID_NUB_HW_INDEX;
647}
648
649uint32_t
650MachThread::EnableHardwareWatchpoint (const DNBBreakpoint *wp)
651{
652    if (wp != NULL && wp->IsWatchpoint())
653        return m_arch_ap->EnableHardwareWatchpoint(wp->Address(), wp->ByteSize(), wp->WatchpointRead(), wp->WatchpointWrite());
654    return INVALID_NUB_HW_INDEX;
655}
656
657bool
658MachThread::DisableHardwareBreakpoint (const DNBBreakpoint *bp)
659{
660    if (bp != NULL && bp->IsHardware())
661        return m_arch_ap->DisableHardwareBreakpoint(bp->GetHardwareIndex());
662    return false;
663}
664
665bool
666MachThread::DisableHardwareWatchpoint (const DNBBreakpoint *wp)
667{
668    if (wp != NULL && wp->IsHardware())
669        return m_arch_ap->DisableHardwareWatchpoint(wp->GetHardwareIndex());
670    return false;
671}
672
673bool
674MachThread::GetIdentifierInfo ()
675{
676#ifdef THREAD_IDENTIFIER_INFO_COUNT
677        // Don't try to get the thread info once and cache it for the life of the thread.  It changes over time, for instance
678        // if the thread name changes, then the thread_handle also changes...  So you have to refetch it every time.
679        mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
680        kern_return_t kret = ::thread_info (ThreadID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count);
681        return kret == KERN_SUCCESS;
682#endif
683
684    return false;
685}
686
687
688const char *
689MachThread::GetName ()
690{
691    if (GetIdentifierInfo ())
692    {
693        int len = ::proc_pidinfo (m_process->ProcessID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo));
694
695        if (len && m_proc_threadinfo.pth_name[0])
696            return m_proc_threadinfo.pth_name;
697    }
698    return NULL;
699}
700
701
702//
703//const char *
704//MachThread::GetDispatchQueueName()
705//{
706//    if (GetIdentifierInfo ())
707//    {
708//        if (m_ident_info.dispatch_qaddr == 0)
709//            return NULL;
710//
711//        uint8_t memory_buffer[8];
712//        DNBDataRef data(memory_buffer, sizeof(memory_buffer), false);
713//        ModuleSP module_sp(GetProcess()->GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib")));
714//        if (module_sp.get() == NULL)
715//            return NULL;
716//
717//        lldb::addr_t dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS;
718//        const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData);
719//        if (dispatch_queue_offsets_symbol)
720//            dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(GetProcess());
721//
722//        if (dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
723//            return NULL;
724//
725//        // Excerpt from src/queue_private.h
726//        struct dispatch_queue_offsets_s
727//        {
728//            uint16_t dqo_version;
729//            uint16_t dqo_label;
730//            uint16_t dqo_label_size;
731//        } dispatch_queue_offsets;
732//
733//
734//        if (GetProcess()->ReadMemory (dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets)) == sizeof(dispatch_queue_offsets))
735//        {
736//            uint32_t data_offset = 0;
737//            if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t)))
738//            {
739//                if (GetProcess()->ReadMemory (m_ident_info.dispatch_qaddr, &memory_buffer, data.GetAddressByteSize()) == data.GetAddressByteSize())
740//                {
741//                    data_offset = 0;
742//                    lldb::addr_t queue_addr = data.GetAddress(&data_offset);
743//                    lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label;
744//                    const size_t chunk_size = 32;
745//                    uint32_t label_pos = 0;
746//                    m_dispatch_queue_name.resize(chunk_size, '\0');
747//                    while (1)
748//                    {
749//                        size_t bytes_read = GetProcess()->ReadMemory (label_addr + label_pos, &m_dispatch_queue_name[label_pos], chunk_size);
750//
751//                        if (bytes_read <= 0)
752//                            break;
753//
754//                        if (m_dispatch_queue_name.find('\0', label_pos) != std::string::npos)
755//                            break;
756//                        label_pos += bytes_read;
757//                    }
758//                    m_dispatch_queue_name.erase(m_dispatch_queue_name.find('\0'));
759//                }
760//            }
761//        }
762//    }
763//
764//    if (m_dispatch_queue_name.empty())
765//        return NULL;
766//    return m_dispatch_queue_name.c_str();
767//}
768