MachThread.cpp revision 679c77b1a1956ddff405fdb6e3c40035f0cc20a7
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    }
338    m_arch_ap->ThreadWillResume();
339    m_stop_exception.Clear();
340}
341
342nub_break_t
343MachThread::CurrentBreakpoint()
344{
345    return m_process->Breakpoints().FindIDByAddress(GetPC());
346}
347
348bool
349MachThread::ShouldStop(bool &step_more)
350{
351    // See if this thread is at a breakpoint?
352    nub_break_t breakID = CurrentBreakpoint();
353
354    if (NUB_BREAK_ID_IS_VALID(breakID))
355    {
356        // This thread is sitting at a breakpoint, ask the breakpoint
357        // if we should be stopping here.
358        if (Process()->Breakpoints().ShouldStop(ProcessID(), ThreadID(), breakID))
359            return true;
360        else
361        {
362            // The breakpoint said we shouldn't stop, but we may have gotten
363            // a signal or the user may have requested to stop in some other
364            // way. Stop if we have a valid exception (this thread won't if
365            // another thread was the reason this process stopped) and that
366            // exception, is NOT a breakpoint exception (a common case would
367            // be a SIGINT signal).
368            if (GetStopException().IsValid() && !GetStopException().IsBreakpoint())
369                return true;
370        }
371    }
372    else
373    {
374        if (m_arch_ap->StepNotComplete())
375        {
376            step_more = true;
377            return false;
378        }
379        // The thread state is used to let us know what the thread was
380        // trying to do. MachThread::ThreadWillResume() will set the
381        // thread state to various values depending if the thread was
382        // the current thread and if it was to be single stepped, or
383        // resumed.
384        if (GetState() == eStateRunning)
385        {
386            // If our state is running, then we should continue as we are in
387            // the process of stepping over a breakpoint.
388            return false;
389        }
390        else
391        {
392            // Stop if we have any kind of valid exception for this
393            // thread.
394            if (GetStopException().IsValid())
395                return true;
396        }
397    }
398    return false;
399}
400bool
401MachThread::IsStepping()
402{
403#if ENABLE_AUTO_STEPPING_OVER_BP
404    // Return true if this thread is currently being stepped.
405    // MachThread::ThreadWillResume currently determines this by looking if we
406    // have been asked to single step, or if we are at a breakpoint instruction
407    // and have been asked to resume. In the latter case we need to disable the
408    // breakpoint we are at, single step, re-enable and continue.
409    nub_state_t state = GetState();
410    return ((state == eStateStepping) ||
411            (state == eStateRunning && NUB_BREAK_ID_IS_VALID(CurrentBreakpoint())));
412#else
413    return GetState() == eStateStepping;
414#endif
415}
416
417
418bool
419MachThread::ThreadDidStop()
420{
421    // This thread has existed prior to resuming under debug nub control,
422    // and has just been stopped. Do any cleanup that needs to be done
423    // after running.
424
425    // The thread state and breakpoint will still have the same values
426    // as they had prior to resuming the thread, so it makes it easy to check
427    // if we were trying to step a thread, or we tried to resume while being
428    // at a breakpoint.
429
430    // When this method gets called, the process state is still in the
431    // state it was in while running so we can act accordingly.
432    m_arch_ap->ThreadDidStop();
433
434
435    // We may have suspended this thread so the primary thread could step
436    // without worrying about race conditions, so lets restore our suspend
437    // count.
438    RestoreSuspendCount();
439
440    // Update the basic information for a thread
441    MachThread::GetBasicInfo(m_tid, &m_basicInfo);
442
443#if ENABLE_AUTO_STEPPING_OVER_BP
444    // See if we were at a breakpoint when we last resumed that we disabled,
445    // re-enable it.
446    nub_break_t breakID = CurrentBreakpoint();
447
448    if (NUB_BREAK_ID_IS_VALID(breakID))
449    {
450        m_process->EnableBreakpoint(breakID);
451        if (m_basicInfo.suspend_count > 0)
452        {
453            SetState(eStateSuspended);
454        }
455        else
456        {
457            // If we last were at a breakpoint and we single stepped, our state
458            // will be "running" to indicate we need to continue after stepping
459            // over the breakpoint instruction. If we step over a breakpoint
460            // instruction, we need to stop.
461            if (GetState() == eStateRunning)
462            {
463                // Leave state set to running so we will continue automatically
464                // from this breakpoint
465            }
466            else
467            {
468                SetState(eStateStopped);
469            }
470        }
471    }
472    else
473    {
474        if (m_basicInfo.suspend_count > 0)
475        {
476            SetState(eStateSuspended);
477        }
478        else
479        {
480            SetState(eStateStopped);
481        }
482    }
483#else
484    if (m_basicInfo.suspend_count > 0)
485        SetState(eStateSuspended);
486    else
487        SetState(eStateStopped);
488#endif
489    return true;
490}
491
492bool
493MachThread::NotifyException(MachException::Data& exc)
494{
495    if (m_stop_exception.IsValid())
496    {
497        // We may have more than one exception for a thread, but we need to
498        // only remember the one that we will say is the reason we stopped.
499        // We may have been single stepping and also gotten a signal exception,
500        // so just remember the most pertinent one.
501        if (m_stop_exception.IsBreakpoint())
502            m_stop_exception = exc;
503    }
504    else
505    {
506        m_stop_exception = exc;
507    }
508    bool handled = m_arch_ap->NotifyException(exc);
509    if (!handled)
510    {
511        handled = true;
512//        switch (exc.exc_type)
513//        {
514//        case EXC_BAD_ACCESS:
515//            break;
516//        case EXC_BAD_INSTRUCTION:
517//            break;
518//        case EXC_ARITHMETIC:
519//            break;
520//        case EXC_EMULATION:
521//            break;
522//        case EXC_SOFTWARE:
523//            break;
524//        case EXC_BREAKPOINT:
525//            break;
526//        case EXC_SYSCALL:
527//            break;
528//        case EXC_MACH_SYSCALL:
529//            break;
530//        case EXC_RPC_ALERT:
531//            break;
532//        }
533    }
534    return handled;
535}
536
537
538nub_state_t
539MachThread::GetState()
540{
541    // If any other threads access this we will need a mutex for it
542    PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
543    return m_state;
544}
545
546void
547MachThread::SetState(nub_state_t state)
548{
549    PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
550    m_state = state;
551    DNBLogThreadedIf(LOG_THREAD, "MachThread::SetState ( %s ) for tid = 0x%4.4x", DNBStateAsString(state), m_tid);
552}
553
554uint32_t
555MachThread::GetNumRegistersInSet(int regSet) const
556{
557    if (regSet < n_num_reg_sets)
558        return m_reg_sets[regSet].num_registers;
559    return 0;
560}
561
562const char *
563MachThread::GetRegisterSetName(int regSet) const
564{
565    if (regSet < n_num_reg_sets)
566        return m_reg_sets[regSet].name;
567    return NULL;
568}
569
570const DNBRegisterInfo *
571MachThread::GetRegisterInfo(int regSet, int regIndex) const
572{
573    if (regSet < n_num_reg_sets)
574        if (regIndex < m_reg_sets[regSet].num_registers)
575            return &m_reg_sets[regSet].registers[regIndex];
576    return NULL;
577}
578void
579MachThread::DumpRegisterState(int regSet)
580{
581    if (regSet == REGISTER_SET_ALL)
582    {
583        for (regSet = 1; regSet < n_num_reg_sets; regSet++)
584            DumpRegisterState(regSet);
585    }
586    else
587    {
588        if (m_arch_ap->RegisterSetStateIsValid(regSet))
589        {
590            const size_t numRegisters = GetNumRegistersInSet(regSet);
591            size_t regIndex = 0;
592            DNBRegisterValueClass reg;
593            for (regIndex = 0; regIndex < numRegisters; ++regIndex)
594            {
595                if (m_arch_ap->GetRegisterValue(regSet, regIndex, &reg))
596                {
597                    reg.Dump(NULL, NULL);
598                }
599            }
600        }
601        else
602        {
603            DNBLog("%s: registers are not currently valid.", GetRegisterSetName(regSet));
604        }
605    }
606}
607
608const DNBRegisterSetInfo *
609MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets ) const
610{
611    *num_reg_sets = n_num_reg_sets;
612    return &m_reg_sets[0];
613}
614
615bool
616MachThread::GetRegisterValue ( uint32_t set, uint32_t reg, DNBRegisterValue *value )
617{
618    return m_arch_ap->GetRegisterValue(set, reg, value);
619}
620
621bool
622MachThread::SetRegisterValue ( uint32_t set, uint32_t reg, const DNBRegisterValue *value )
623{
624    return m_arch_ap->SetRegisterValue(set, reg, value);
625}
626
627nub_size_t
628MachThread::GetRegisterContext (void *buf, nub_size_t buf_len)
629{
630    return m_arch_ap->GetRegisterContext(buf, buf_len);
631}
632
633nub_size_t
634MachThread::SetRegisterContext (const void *buf, nub_size_t buf_len)
635{
636    return m_arch_ap->SetRegisterContext(buf, buf_len);
637}
638
639uint32_t
640MachThread::EnableHardwareBreakpoint (const DNBBreakpoint *bp)
641{
642    if (bp != NULL && bp->IsBreakpoint())
643        return m_arch_ap->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize());
644    return INVALID_NUB_HW_INDEX;
645}
646
647uint32_t
648MachThread::EnableHardwareWatchpoint (const DNBBreakpoint *wp)
649{
650    if (wp != NULL && wp->IsWatchpoint())
651        return m_arch_ap->EnableHardwareWatchpoint(wp->Address(), wp->ByteSize(), wp->WatchpointRead(), wp->WatchpointWrite());
652    return INVALID_NUB_HW_INDEX;
653}
654
655bool
656MachThread::DisableHardwareBreakpoint (const DNBBreakpoint *bp)
657{
658    if (bp != NULL && bp->IsHardware())
659        return m_arch_ap->DisableHardwareBreakpoint(bp->GetHardwareIndex());
660    return false;
661}
662
663bool
664MachThread::DisableHardwareWatchpoint (const DNBBreakpoint *wp)
665{
666    if (wp != NULL && wp->IsHardware())
667        return m_arch_ap->DisableHardwareWatchpoint(wp->GetHardwareIndex());
668    return false;
669}
670
671bool
672MachThread::GetIdentifierInfo ()
673{
674#ifdef THREAD_IDENTIFIER_INFO_COUNT
675        // Don't try to get the thread info once and cache it for the life of the thread.  It changes over time, for instance
676        // if the thread name changes, then the thread_handle also changes...  So you have to refetch it every time.
677        mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
678        kern_return_t kret = ::thread_info (ThreadID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count);
679        return kret == KERN_SUCCESS;
680#endif
681
682    return false;
683}
684
685
686const char *
687MachThread::GetName ()
688{
689    if (GetIdentifierInfo ())
690    {
691        int len = ::proc_pidinfo (m_process->ProcessID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo));
692
693        if (len && m_proc_threadinfo.pth_name[0])
694            return m_proc_threadinfo.pth_name;
695    }
696    return NULL;
697}
698
699
700//
701//const char *
702//MachThread::GetDispatchQueueName()
703//{
704//    if (GetIdentifierInfo ())
705//    {
706//        if (m_ident_info.dispatch_qaddr == 0)
707//            return NULL;
708//
709//        uint8_t memory_buffer[8];
710//        DNBDataRef data(memory_buffer, sizeof(memory_buffer), false);
711//        ModuleSP module_sp(GetProcess()->GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib")));
712//        if (module_sp.get() == NULL)
713//            return NULL;
714//
715//        lldb::addr_t dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS;
716//        const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData);
717//        if (dispatch_queue_offsets_symbol)
718//            dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(GetProcess());
719//
720//        if (dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
721//            return NULL;
722//
723//        // Excerpt from src/queue_private.h
724//        struct dispatch_queue_offsets_s
725//        {
726//            uint16_t dqo_version;
727//            uint16_t dqo_label;
728//            uint16_t dqo_label_size;
729//        } dispatch_queue_offsets;
730//
731//
732//        if (GetProcess()->ReadMemory (dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets)) == sizeof(dispatch_queue_offsets))
733//        {
734//            uint32_t data_offset = 0;
735//            if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t)))
736//            {
737//                if (GetProcess()->ReadMemory (m_ident_info.dispatch_qaddr, &memory_buffer, data.GetAddressByteSize()) == data.GetAddressByteSize())
738//                {
739//                    data_offset = 0;
740//                    lldb::addr_t queue_addr = data.GetAddress(&data_offset);
741//                    lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label;
742//                    const size_t chunk_size = 32;
743//                    uint32_t label_pos = 0;
744//                    m_dispatch_queue_name.resize(chunk_size, '\0');
745//                    while (1)
746//                    {
747//                        size_t bytes_read = GetProcess()->ReadMemory (label_addr + label_pos, &m_dispatch_queue_name[label_pos], chunk_size);
748//
749//                        if (bytes_read <= 0)
750//                            break;
751//
752//                        if (m_dispatch_queue_name.find('\0', label_pos) != std::string::npos)
753//                            break;
754//                        label_pos += bytes_read;
755//                    }
756//                    m_dispatch_queue_name.erase(m_dispatch_queue_name.find('\0'));
757//                }
758//            }
759//        }
760//    }
761//
762//    if (m_dispatch_queue_name.empty())
763//        return NULL;
764//    return m_dispatch_queue_name.c_str();
765//}
766