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("[%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 
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 nub_break_t
343 MachThread::CurrentBreakpoint()
344 {
345     return m_process->Breakpoints().FindIDByAddress(GetPC());
346 }
347 
348 bool
349 MachThread::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 }
400 bool
401 MachThread::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 
418 bool
419 MachThread::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 
492 bool
493 MachThread::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 
538 nub_state_t
539 MachThread::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 
546 void
547 MachThread::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 
554 uint32_t
555 MachThread::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 
562 const char *
563 MachThread::GetRegisterSetName(int regSet) const
564 {
565     if (regSet < n_num_reg_sets)
566         return m_reg_sets[regSet].name;
567     return NULL;
568 }
569 
570 const DNBRegisterInfo *
571 MachThread::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 }
578 void
579 MachThread::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 
608 const DNBRegisterSetInfo *
609 MachThread::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 
615 bool
616 MachThread::GetRegisterValue ( uint32_t set, uint32_t reg, DNBRegisterValue *value )
617 {
618     return m_arch_ap->GetRegisterValue(set, reg, value);
619 }
620 
621 bool
622 MachThread::SetRegisterValue ( uint32_t set, uint32_t reg, const DNBRegisterValue *value )
623 {
624     return m_arch_ap->SetRegisterValue(set, reg, value);
625 }
626 
627 nub_size_t
628 MachThread::GetRegisterContext (void *buf, nub_size_t buf_len)
629 {
630     return m_arch_ap->GetRegisterContext(buf, buf_len);
631 }
632 
633 nub_size_t
634 MachThread::SetRegisterContext (const void *buf, nub_size_t buf_len)
635 {
636     return m_arch_ap->SetRegisterContext(buf, buf_len);
637 }
638 
639 uint32_t
640 MachThread::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 
647 uint32_t
648 MachThread::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 
655 bool
656 MachThread::DisableHardwareBreakpoint (const DNBBreakpoint *bp)
657 {
658     if (bp != NULL && bp->IsHardware())
659         return m_arch_ap->DisableHardwareBreakpoint(bp->GetHardwareIndex());
660     return false;
661 }
662 
663 bool
664 MachThread::DisableHardwareWatchpoint (const DNBBreakpoint *wp)
665 {
666     if (wp != NULL && wp->IsHardware())
667         return m_arch_ap->DisableHardwareWatchpoint(wp->GetHardwareIndex());
668     return false;
669 }
670 
671 bool
672 MachThread::GetIdentifierInfo ()
673 {
674 #ifdef THREAD_IDENTIFIER_INFO_COUNT
675     if (m_ident_info.thread_id == 0)
676     {
677         mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
678         return ::thread_info (ThreadID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count) == KERN_SUCCESS;
679     }
680 #endif
681 
682     return false;
683 }
684 
685 
686 const char *
687 MachThread::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