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