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