MachProcess.cpp revision 91a1dabac7ef4a1c8a60dbbb8340d33b7d25f863
1//===-- MachProcess.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/15/07.
11//
12//===----------------------------------------------------------------------===//
13
14#include "DNB.h"
15#include <mach/mach.h>
16#include <spawn.h>
17#include <sys/fcntl.h>
18#include <sys/types.h>
19#include <sys/ptrace.h>
20#include <sys/stat.h>
21#include <unistd.h>
22#include "MacOSX/CFUtils.h"
23#include "SysSignal.h"
24
25#include <algorithm>
26#include <map>
27
28#include "DNBDataRef.h"
29#include "DNBLog.h"
30#include "DNBThreadResumeActions.h"
31#include "DNBTimer.h"
32#include "MachProcess.h"
33#include "PseudoTerminal.h"
34
35#include "CFBundle.h"
36#include "CFData.h"
37#include "CFString.h"
38
39static CFStringRef CopyBundleIDForPath (const char *app_buncle_path, DNBError &err_str);
40
41#if defined (__arm__)
42
43#include <CoreFoundation/CoreFoundation.h>
44#include <SpringBoardServices/SpringBoardServer.h>
45#include <SpringBoardServices/SBSWatchdogAssertion.h>
46
47
48static bool
49IsSBProcess (nub_process_t pid)
50{
51    bool opt_runningApps = true;
52    bool opt_debuggable = false;
53
54    CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable));
55    if (sbsAppIDs.get() != NULL)
56    {
57        CFIndex count = ::CFArrayGetCount (sbsAppIDs.get());
58        CFIndex i = 0;
59        for (i = 0; i < count; i++)
60        {
61            CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i);
62
63            // Get the process id for the app (if there is one)
64            pid_t sbs_pid = INVALID_NUB_PROCESS;
65            if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE)
66            {
67                if (sbs_pid == pid)
68                    return true;
69            }
70        }
71    }
72    return false;
73}
74
75
76#endif
77
78#if 0
79#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
80#else
81#define DEBUG_LOG(fmt, ...)
82#endif
83
84#ifndef MACH_PROCESS_USE_POSIX_SPAWN
85#define MACH_PROCESS_USE_POSIX_SPAWN 1
86#endif
87
88#ifndef _POSIX_SPAWN_DISABLE_ASLR
89#define _POSIX_SPAWN_DISABLE_ASLR       0x0100
90#endif
91
92MachProcess::MachProcess() :
93    m_pid               (0),
94    m_child_stdin       (-1),
95    m_child_stdout      (-1),
96    m_child_stderr      (-1),
97    m_path              (),
98    m_args              (),
99    m_task              (this),
100    m_flags             (eMachProcessFlagsNone),
101    m_stdio_thread      (0),
102    m_stdio_mutex       (PTHREAD_MUTEX_RECURSIVE),
103    m_stdout_data       (),
104    m_thread_list        (),
105    m_exception_messages (),
106    m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE),
107    m_state             (eStateUnloaded),
108    m_state_mutex       (PTHREAD_MUTEX_RECURSIVE),
109    m_events            (0, kAllEventsMask),
110    m_breakpoints       (),
111    m_watchpoints       (),
112    m_name_to_addr_callback(NULL),
113    m_name_to_addr_baton(NULL),
114    m_image_infos_callback(NULL),
115    m_image_infos_baton(NULL)
116{
117    DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
118}
119
120MachProcess::~MachProcess()
121{
122    DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
123    Clear();
124}
125
126pid_t
127MachProcess::SetProcessID(pid_t pid)
128{
129    // Free any previous process specific data or resources
130    Clear();
131    // Set the current PID appropriately
132    if (pid == 0)
133        m_pid = ::getpid ();
134    else
135        m_pid = pid;
136    return m_pid;    // Return actualy PID in case a zero pid was passed in
137}
138
139nub_state_t
140MachProcess::GetState()
141{
142    // If any other threads access this we will need a mutex for it
143    PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
144    return m_state;
145}
146
147const char *
148MachProcess::ThreadGetName(nub_thread_t tid)
149{
150    return m_thread_list.GetName(tid);
151}
152
153nub_state_t
154MachProcess::ThreadGetState(nub_thread_t tid)
155{
156    return m_thread_list.GetState(tid);
157}
158
159
160nub_size_t
161MachProcess::GetNumThreads () const
162{
163    return m_thread_list.NumThreads();
164}
165
166nub_thread_t
167MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const
168{
169    return m_thread_list.ThreadIDAtIndex(thread_idx);
170}
171
172uint32_t
173MachProcess::GetThreadIndexFromThreadID (nub_thread_t tid)
174{
175    return m_thread_list.GetThreadIndexByID(tid);
176}
177
178nub_thread_t
179MachProcess::GetCurrentThread ()
180{
181    return m_thread_list.CurrentThreadID();
182}
183
184nub_thread_t
185MachProcess::SetCurrentThread(nub_thread_t tid)
186{
187    return m_thread_list.SetCurrentThread(tid);
188}
189
190bool
191MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
192{
193    return m_thread_list.GetThreadStoppedReason(tid, stop_info);
194}
195
196void
197MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const
198{
199    return m_thread_list.DumpThreadStoppedReason(tid);
200}
201
202const char *
203MachProcess::GetThreadInfo(nub_thread_t tid) const
204{
205    return m_thread_list.GetThreadInfo(tid);
206}
207
208const DNBRegisterSetInfo *
209MachProcess::GetRegisterSetInfo(nub_thread_t tid, nub_size_t *num_reg_sets ) const
210{
211    return DNBArch::GetRegisterSetInfo (num_reg_sets);
212}
213
214bool
215MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const
216{
217    return m_thread_list.GetRegisterValue(tid, set, reg, value);
218}
219
220bool
221MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const
222{
223    return m_thread_list.SetRegisterValue(tid, set, reg, value);
224}
225
226void
227MachProcess::SetState(nub_state_t new_state)
228{
229    // If any other threads access this we will need a mutex for it
230    uint32_t event_mask = 0;
231
232    // Scope for mutex locker
233    {
234        PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
235        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( %s )", DNBStateAsString(new_state));
236
237        const nub_state_t old_state = m_state;
238
239        if (old_state != new_state)
240        {
241            if (NUB_STATE_IS_STOPPED(new_state))
242                event_mask = eEventProcessStoppedStateChanged;
243            else
244                event_mask = eEventProcessRunningStateChanged;
245
246            m_state = new_state;
247            if (new_state == eStateStopped)
248                m_stop_count++;
249        }
250    }
251
252    if (event_mask != 0)
253    {
254        m_events.SetEvents (event_mask);
255
256        // Wait for the event bit to reset if a reset ACK is requested
257        m_events.WaitForResetAck(event_mask);
258    }
259
260}
261
262void
263MachProcess::Clear()
264{
265    // Clear any cached thread list while the pid and task are still valid
266
267    m_task.Clear();
268    // Now clear out all member variables
269    m_pid = INVALID_NUB_PROCESS;
270    CloseChildFileDescriptors();
271    m_path.clear();
272    m_args.clear();
273    SetState(eStateUnloaded);
274    m_flags = eMachProcessFlagsNone;
275    m_stop_count = 0;
276    m_thread_list.Clear();
277    {
278        PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
279        m_exception_messages.clear();
280    }
281}
282
283
284bool
285MachProcess::StartSTDIOThread()
286{
287    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
288    // Create the thread that watches for the child STDIO
289    return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0;
290}
291
292
293nub_addr_t
294MachProcess::LookupSymbol(const char *name, const char *shlib)
295{
296    if (m_name_to_addr_callback != NULL && name && name[0])
297        return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton);
298    return INVALID_NUB_ADDRESS;
299}
300
301bool
302MachProcess::Resume (const DNBThreadResumeActions& thread_actions)
303{
304    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
305    nub_state_t state = GetState();
306
307    if (CanResume(state))
308    {
309        PrivateResume(thread_actions);
310        return true;
311    }
312    else if (state == eStateRunning)
313    {
314        DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort());
315        return true;
316    }
317    DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort());
318    return false;
319}
320
321bool
322MachProcess::Kill (const struct timespec *timeout_abstime)
323{
324    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()");
325    nub_state_t state = DoSIGSTOP(true);
326    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state));
327    errno = 0;
328    ::ptrace (PT_KILL, m_pid, 0, 0);
329    DNBError err;
330    err.SetErrorToErrno();
331    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", err.Error(), err.AsString());
332    PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
333    return true;
334}
335
336bool
337MachProcess::Signal (int signal, const struct timespec *timeout_abstime)
338{
339    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime);
340    nub_state_t state = GetState();
341    if (::kill (ProcessID(), signal) == 0)
342    {
343        // If we were running and we have a timeout, wait for the signal to stop
344        if (IsRunning(state) && timeout_abstime)
345        {
346            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime);
347            m_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime);
348            state = GetState();
349            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state));
350            return !IsRunning (state);
351        }
352        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime);
353        return true;
354    }
355    DNBError err(errno, DNBError::POSIX);
356    err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal);
357    return false;
358
359}
360
361nub_state_t
362MachProcess::DoSIGSTOP (bool clear_bps_and_wps, uint32_t *thread_idx_ptr)
363{
364    nub_state_t state = GetState();
365    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state));
366
367    if (!IsRunning(state))
368    {
369        if (clear_bps_and_wps)
370        {
371            DisableAllBreakpoints (true);
372            DisableAllWatchpoints (true);
373            clear_bps_and_wps = false;
374        }
375
376        // If we already have a thread stopped due to a SIGSTOP, we don't have
377        // to do anything...
378        uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP);
379        if (thread_idx_ptr)
380            *thread_idx_ptr = thread_idx;
381        if (thread_idx != UINT32_MAX)
382            return GetState();
383
384        // No threads were stopped with a SIGSTOP, we need to run and halt the
385        // process with a signal
386        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state));
387        PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
388
389        // Reset the event that says we were indeed running
390        m_events.ResetEvents(eEventProcessRunningStateChanged);
391        state = GetState();
392    }
393
394    // We need to be stopped in order to be able to detach, so we need
395    // to send ourselves a SIGSTOP
396
397    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state));
398    struct timespec sigstop_timeout;
399    DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0);
400    Signal (SIGSTOP, &sigstop_timeout);
401    if (clear_bps_and_wps)
402    {
403        DisableAllBreakpoints (true);
404        DisableAllWatchpoints (true);
405        clear_bps_and_wps = false;
406    }
407    uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP);
408    if (thread_idx_ptr)
409        *thread_idx_ptr = thread_idx;
410    return GetState();
411}
412
413bool
414MachProcess::Detach()
415{
416    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()");
417
418    uint32_t thread_idx = UINT32_MAX;
419    nub_state_t state = DoSIGSTOP(true, &thread_idx);
420    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state));
421
422    {
423        DNBThreadResumeActions thread_actions;
424        DNBThreadResumeAction thread_action;
425        thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx);
426        thread_action.state = eStateRunning;
427        thread_action.signal = -1;
428        thread_action.addr = INVALID_NUB_ADDRESS;
429
430        thread_actions.Append (thread_action);
431
432        thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0);
433
434        PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
435
436        ReplyToAllExceptions (thread_actions);
437
438    }
439
440    m_task.ShutDownExcecptionThread();
441
442    // Detach from our process
443    errno = 0;
444    nub_process_t pid = m_pid;
445    int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
446    DNBError err(errno, DNBError::POSIX);
447    if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0))
448        err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
449
450    // Resume our task
451    m_task.Resume();
452
453    // NULL our task out as we have already retored all exception ports
454    m_task.Clear();
455
456    // Clear out any notion of the process we once were
457    Clear();
458
459    SetState(eStateDetached);
460
461    return true;
462}
463
464
465nub_size_t
466MachProcess::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const
467{
468    nub_size_t bytes_removed = 0;
469    const DNBBreakpoint *bp;
470    nub_addr_t intersect_addr;
471    nub_size_t intersect_size;
472    nub_size_t opcode_offset;
473    nub_size_t idx;
474    for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx)
475    {
476        if (bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset))
477        {
478            assert(addr <= intersect_addr && intersect_addr < addr + size);
479            assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
480            assert(opcode_offset + intersect_size <= bp->ByteSize());
481            nub_size_t buf_offset = intersect_addr - addr;
482            ::memcpy(buf + buf_offset, bp->SavedOpcodeBytes() + opcode_offset, intersect_size);
483        }
484    }
485    return bytes_removed;
486}
487
488//----------------------------------------------------------------------
489// ReadMemory from the MachProcess level will always remove any software
490// breakpoints from the memory buffer before returning. If you wish to
491// read memory and see those traps, read from the MachTask
492// (m_task.ReadMemory()) as that version will give you what is actually
493// in inferior memory.
494//----------------------------------------------------------------------
495nub_size_t
496MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
497{
498    // We need to remove any current software traps (enabled software
499    // breakpoints) that we may have placed in our tasks memory.
500
501    // First just read the memory as is
502    nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf);
503
504    // Then place any opcodes that fall into this range back into the buffer
505    // before we return this to callers.
506    if (bytes_read > 0)
507        RemoveTrapsFromBuffer (addr, size, (uint8_t *)buf);
508    return bytes_read;
509}
510
511//----------------------------------------------------------------------
512// WriteMemory from the MachProcess level will always write memory around
513// any software breakpoints. Any software breakpoints will have their
514// opcodes modified if they are enabled. Any memory that doesn't overlap
515// with software breakpoints will be written to. If you wish to write to
516// inferior memory without this interference, then write to the MachTask
517// (m_task.WriteMemory()) as that version will always modify inferior
518// memory.
519//----------------------------------------------------------------------
520nub_size_t
521MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
522{
523    // We need to write any data that would go where any current software traps
524    // (enabled software breakpoints) any software traps (breakpoints) that we
525    // may have placed in our tasks memory.
526
527    std::map<nub_addr_t, DNBBreakpoint *> addr_to_bp_map;
528    DNBBreakpoint *bp;
529    nub_size_t idx;
530    for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx)
531    {
532        if (bp->IntersectsRange(addr, size, NULL, NULL, NULL))
533            addr_to_bp_map[bp->Address()] = bp;
534    }
535
536    // If we don't have any software breakpoints that are in this buffer, then
537    // we can just write memory and be done with it.
538    if (addr_to_bp_map.empty())
539        return m_task.WriteMemory(addr, size, buf);
540
541    // If we make it here, we have some breakpoints that overlap and we need
542    // to work around them.
543
544    nub_size_t bytes_written = 0;
545    nub_addr_t intersect_addr;
546    nub_size_t intersect_size;
547    nub_size_t opcode_offset;
548    const uint8_t *ubuf = (const uint8_t *)buf;
549    std::map<nub_addr_t, DNBBreakpoint *>::iterator pos, end = addr_to_bp_map.end();
550    for (pos = addr_to_bp_map.begin(); pos != end; ++pos)
551    {
552        bp = pos->second;
553
554        assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset));
555        assert(addr <= intersect_addr && intersect_addr < addr + size);
556        assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
557        assert(opcode_offset + intersect_size <= bp->ByteSize());
558
559        // Check for bytes before this breakpoint
560        const nub_addr_t curr_addr = addr + bytes_written;
561        if (intersect_addr > curr_addr)
562        {
563            // There are some bytes before this breakpoint that we need to
564            // just write to memory
565            nub_size_t curr_size = intersect_addr - curr_addr;
566            nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written);
567            bytes_written += curr_bytes_written;
568            if (curr_bytes_written != curr_size)
569            {
570                // We weren't able to write all of the requested bytes, we
571                // are done looping and will return the number of bytes that
572                // we have written so far.
573                break;
574            }
575        }
576
577        // Now write any bytes that would cover up any software breakpoints
578        // directly into the breakpoint opcode buffer
579        ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size);
580        bytes_written += intersect_size;
581    }
582
583    // Write any remaining bytes after the last breakpoint if we have any left
584    if (bytes_written < size)
585        bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written);
586
587    return bytes_written;
588}
589
590
591void
592MachProcess::ReplyToAllExceptions (const DNBThreadResumeActions& thread_actions)
593{
594    PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
595    if (m_exception_messages.empty() == false)
596    {
597        MachException::Message::iterator pos;
598        MachException::Message::iterator begin = m_exception_messages.begin();
599        MachException::Message::iterator end = m_exception_messages.end();
600        for (pos = begin; pos != end; ++pos)
601        {
602            DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %d...", std::distance(begin, pos));
603            int thread_reply_signal = 0;
604
605            const DNBThreadResumeAction *action = thread_actions.GetActionForThread (pos->state.thread_port, false);
606
607            if (action)
608            {
609                thread_reply_signal = action->signal;
610                if (thread_reply_signal)
611                    thread_actions.SetSignalHandledForThread (pos->state.thread_port);
612            }
613
614            DNBError err (pos->Reply(this, thread_reply_signal));
615            if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
616                err.LogThreadedIfError("Error replying to exception");
617        }
618
619        // Erase all exception message as we should have used and replied
620        // to them all already.
621        m_exception_messages.clear();
622    }
623}
624void
625MachProcess::PrivateResume (const DNBThreadResumeActions& thread_actions)
626{
627    PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
628
629    ReplyToAllExceptions (thread_actions);
630//    bool stepOverBreakInstruction = step;
631
632    // Let the thread prepare to resume and see if any threads want us to
633    // step over a breakpoint instruction (ProcessWillResume will modify
634    // the value of stepOverBreakInstruction).
635    m_thread_list.ProcessWillResume (this, thread_actions);
636
637    // Set our state accordingly
638    if (thread_actions.NumActionsWithState(eStateStepping))
639        SetState (eStateStepping);
640    else
641        SetState (eStateRunning);
642
643    // Now resume our task.
644    m_task.Resume();
645}
646
647nub_break_t
648MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware, thread_t tid)
649{
650    DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, hardware, tid);
651    if (hardware && tid == INVALID_NUB_THREAD)
652        tid = GetCurrentThread();
653
654    DNBBreakpoint bp(addr, length, tid, hardware);
655    nub_break_t breakID = m_breakpoints.Add(bp);
656    if (EnableBreakpoint(breakID))
657    {
658        DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%4.4x ) => %u", (uint64_t)addr, length, tid, breakID);
659        return breakID;
660    }
661    else
662    {
663        m_breakpoints.Remove(breakID);
664    }
665    // We failed to enable the breakpoint
666    return INVALID_NUB_BREAK_ID;
667}
668
669nub_watch_t
670MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware, thread_t tid)
671{
672    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, flags = 0x%8.8x, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, watch_flags, hardware, tid);
673    if (hardware && tid == INVALID_NUB_THREAD)
674        tid = GetCurrentThread();
675
676    DNBBreakpoint watch(addr, length, tid, hardware);
677    watch.SetIsWatchpoint(watch_flags);
678
679    nub_watch_t watchID = m_watchpoints.Add(watch);
680    if (EnableWatchpoint(watchID))
681    {
682        DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => %u", (uint64_t)addr, length, tid, watchID);
683        return watchID;
684    }
685    else
686    {
687        DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => FAILED (%u)", (uint64_t)addr, length, tid, watchID);
688        m_watchpoints.Remove(watchID);
689    }
690    // We failed to enable the watchpoint
691    return INVALID_NUB_BREAK_ID;
692}
693
694nub_size_t
695MachProcess::DisableAllBreakpoints(bool remove)
696{
697    DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
698    DNBBreakpoint *bp;
699    nub_size_t disabled_count = 0;
700    nub_size_t idx = 0;
701    while ((bp = m_breakpoints.GetByIndex(idx)) != NULL)
702    {
703        bool success = DisableBreakpoint(bp->GetID(), remove);
704
705        if (success)
706            disabled_count++;
707        // If we failed to disable the breakpoint or we aren't removing the breakpoint
708        // increment the breakpoint index. Otherwise DisableBreakpoint will have removed
709        // the breakpoint at this index and we don't need to change it.
710        if ((success == false) || (remove == false))
711            idx++;
712    }
713    return disabled_count;
714}
715
716nub_size_t
717MachProcess::DisableAllWatchpoints(bool remove)
718{
719    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
720    DNBBreakpoint *wp;
721    nub_size_t disabled_count = 0;
722    nub_size_t idx = 0;
723    while ((wp = m_watchpoints.GetByIndex(idx)) != NULL)
724    {
725        bool success = DisableWatchpoint(wp->GetID(), remove);
726
727        if (success)
728            disabled_count++;
729        // If we failed to disable the watchpoint or we aren't removing the watchpoint
730        // increment the watchpoint index. Otherwise DisableWatchpoint will have removed
731        // the watchpoint at this index and we don't need to change it.
732        if ((success == false) || (remove == false))
733            idx++;
734    }
735    return disabled_count;
736}
737
738bool
739MachProcess::DisableBreakpoint(nub_break_t breakID, bool remove)
740{
741    DNBBreakpoint *bp = m_breakpoints.FindByID (breakID);
742    if (bp)
743    {
744        nub_addr_t addr = bp->Address();
745        DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx", breakID, remove, (uint64_t)addr);
746
747        if (bp->IsHardware())
748        {
749            bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp);
750
751            if (hw_disable_result == true)
752            {
753                bp->SetEnabled(false);
754                // Let the thread list know that a breakpoint has been modified
755                if (remove)
756                {
757                    m_thread_list.NotifyBreakpointChanged(bp);
758                    m_breakpoints.Remove(breakID);
759                }
760                DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", breakID, remove, (uint64_t)addr);
761                return true;
762            }
763
764            return false;
765        }
766
767        const nub_size_t break_op_size = bp->ByteSize();
768        assert (break_op_size > 0);
769        const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(bp->ByteSize());
770        if (break_op_size > 0)
771        {
772            // Clear a software breakoint instruction
773            uint8_t curr_break_op[break_op_size];
774            bool break_op_found = false;
775
776            // Read the breakpoint opcode
777            if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size)
778            {
779                bool verify = false;
780                if (bp->IsEnabled())
781                {
782                    // Make sure we have the a breakpoint opcode exists at this address
783                    if (memcmp(curr_break_op, break_op, break_op_size) == 0)
784                    {
785                        break_op_found = true;
786                        // We found a valid breakpoint opcode at this address, now restore
787                        // the saved opcode.
788                        if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
789                        {
790                            verify = true;
791                        }
792                        else
793                        {
794                            DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx memory write failed when restoring original opcode", breakID, remove, (uint64_t)addr);
795                        }
796                    }
797                    else
798                    {
799                        DNBLogWarning("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx expected a breakpoint opcode but didn't find one.", breakID, remove, (uint64_t)addr);
800                        // Set verify to true and so we can check if the original opcode has already been restored
801                        verify = true;
802                    }
803                }
804                else
805                {
806                    DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x$8.8llx is not enabled", breakID, remove, (uint64_t)addr);
807                    // Set verify to true and so we can check if the original opcode is there
808                    verify = true;
809                }
810
811                if (verify)
812                {
813                    uint8_t verify_opcode[break_op_size];
814                    // Verify that our original opcode made it back to the inferior
815                    if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size)
816                    {
817                        // compare the memory we just read with the original opcode
818                        if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0)
819                        {
820                            // SUCCESS
821                            bp->SetEnabled(false);
822                            // Let the thread list know that a breakpoint has been modified
823                            if (remove)
824                            {
825                                m_thread_list.NotifyBreakpointChanged(bp);
826                                m_breakpoints.Remove(breakID);
827                            }
828                            DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx => success", breakID, remove, (uint64_t)addr);
829                            return true;
830                        }
831                        else
832                        {
833                            if (break_op_found)
834                                DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: failed to restore original opcode", breakID, remove, (uint64_t)addr);
835                            else
836                                DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: opcode changed", breakID, remove, (uint64_t)addr);
837                        }
838                    }
839                    else
840                    {
841                        DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr);
842                    }
843                }
844            }
845            else
846            {
847                DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr);
848            }
849        }
850    }
851    else
852    {
853        DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) invalid breakpoint ID", breakID, remove);
854    }
855    return false;
856}
857
858bool
859MachProcess::DisableWatchpoint(nub_watch_t watchID, bool remove)
860{
861    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(watchID = %d, remove = %d)", __FUNCTION__, watchID, remove);
862    DNBBreakpoint *wp = m_watchpoints.FindByID (watchID);
863    if (wp)
864    {
865        nub_addr_t addr = wp->Address();
866        DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx", watchID, remove, (uint64_t)addr);
867
868        if (wp->IsHardware())
869        {
870            bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp);
871
872            if (hw_disable_result == true)
873            {
874                wp->SetEnabled(false);
875                if (remove)
876                    m_watchpoints.Remove(watchID);
877                DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", watchID, remove, (uint64_t)addr);
878                return true;
879            }
880        }
881
882        // TODO: clear software watchpoints if we implement them
883    }
884    else
885    {
886        DNBLogError("MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) invalid watchpoint ID", watchID, remove);
887    }
888    return false;
889}
890
891
892void
893MachProcess::DumpBreakpoint(nub_break_t breakID) const
894{
895    DNBLogThreaded("MachProcess::DumpBreakpoint(breakID = %d)", breakID);
896
897    if (NUB_BREAK_ID_IS_VALID(breakID))
898    {
899        const DNBBreakpoint *bp = m_breakpoints.FindByID(breakID);
900        if (bp)
901            bp->Dump();
902        else
903            DNBLog("MachProcess::DumpBreakpoint(breakID = %d): invalid breakID", breakID);
904    }
905    else
906    {
907        m_breakpoints.Dump();
908    }
909}
910
911void
912MachProcess::DumpWatchpoint(nub_watch_t watchID) const
913{
914    DNBLogThreaded("MachProcess::DumpWatchpoint(watchID = %d)", watchID);
915
916    if (NUB_BREAK_ID_IS_VALID(watchID))
917    {
918        const DNBBreakpoint *wp = m_watchpoints.FindByID(watchID);
919        if (wp)
920            wp->Dump();
921        else
922            DNBLog("MachProcess::DumpWatchpoint(watchID = %d): invalid watchID", watchID);
923    }
924    else
925    {
926        m_watchpoints.Dump();
927    }
928}
929
930bool
931MachProcess::EnableBreakpoint(nub_break_t breakID)
932{
933    DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d )", breakID);
934    DNBBreakpoint *bp = m_breakpoints.FindByID (breakID);
935    if (bp)
936    {
937        nub_addr_t addr = bp->Address();
938        if (bp->IsEnabled())
939        {
940            DNBLogWarning("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint already enabled.", breakID, (uint64_t)addr);
941            return true;
942        }
943        else
944        {
945            if (bp->HardwarePreferred())
946            {
947                bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp));
948                if (bp->IsHardware())
949                {
950                    bp->SetEnabled(true);
951                    return true;
952                }
953            }
954
955            const nub_size_t break_op_size = bp->ByteSize();
956            assert (break_op_size != 0);
957            const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(break_op_size);
958            if (break_op_size > 0)
959            {
960                // Save the original opcode by reading it
961                if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
962                {
963                    // Write a software breakpoint in place of the original opcode
964                    if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size)
965                    {
966                        uint8_t verify_break_op[4];
967                        if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size)
968                        {
969                            if (memcmp(break_op, verify_break_op, break_op_size) == 0)
970                            {
971                                bp->SetEnabled(true);
972                                // Let the thread list know that a breakpoint has been modified
973                                m_thread_list.NotifyBreakpointChanged(bp);
974                                DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: SUCCESS.", breakID, (uint64_t)addr);
975                                return true;
976                            }
977                            else
978                            {
979                                DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint opcode verification failed.", breakID, (uint64_t)addr);
980                            }
981                        }
982                        else
983                        {
984                            DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory to verify breakpoint opcode.", breakID, (uint64_t)addr);
985                        }
986                    }
987                    else
988                    {
989                        DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to write breakpoint opcode to memory.", breakID, (uint64_t)addr);
990                    }
991                }
992                else
993                {
994                    DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory at breakpoint address.", breakID, (uint64_t)addr);
995                }
996            }
997            else
998            {
999                DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) no software breakpoint opcode for current architecture.", breakID);
1000            }
1001        }
1002    }
1003    return false;
1004}
1005
1006bool
1007MachProcess::EnableWatchpoint(nub_watch_t watchID)
1008{
1009    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(watchID = %d)", watchID);
1010    DNBBreakpoint *wp = m_watchpoints.FindByID (watchID);
1011    if (wp)
1012    {
1013        nub_addr_t addr = wp->Address();
1014        if (wp->IsEnabled())
1015        {
1016            DNBLogWarning("MachProcess::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr);
1017            return true;
1018        }
1019        else
1020        {
1021            // Currently only try and set hardware watchpoints.
1022            wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp));
1023            if (wp->IsHardware())
1024            {
1025                wp->SetEnabled(true);
1026                return true;
1027            }
1028            // TODO: Add software watchpoints by doing page protection tricks.
1029        }
1030    }
1031    return false;
1032}
1033
1034// Called by the exception thread when an exception has been received from
1035// our process. The exception message is completely filled and the exception
1036// data has already been copied.
1037void
1038MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage)
1039{
1040    PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
1041
1042    if (m_exception_messages.empty())
1043        m_task.Suspend();
1044
1045    DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
1046
1047    // Use a locker to automatically unlock our mutex in case of exceptions
1048    // Add the exception to our internal exception stack
1049    m_exception_messages.push_back(exceptionMessage);
1050}
1051
1052void
1053MachProcess::ExceptionMessageBundleComplete()
1054{
1055    // We have a complete bundle of exceptions for our child process.
1056    PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
1057    DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size());
1058    if (!m_exception_messages.empty())
1059    {
1060        // Let all threads recover from stopping and do any clean up based
1061        // on the previous thread state (if any).
1062        m_thread_list.ProcessDidStop(this);
1063
1064        // Let each thread know of any exceptions
1065        task_t task = m_task.TaskPort();
1066        size_t i;
1067        for (i=0; i<m_exception_messages.size(); ++i)
1068        {
1069            // Let the thread list figure use the MachProcess to forward all exceptions
1070            // on down to each thread.
1071            if (m_exception_messages[i].state.task_port == task)
1072                m_thread_list.NotifyException(m_exception_messages[i].state);
1073            if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
1074                m_exception_messages[i].Dump();
1075        }
1076
1077        if (DNBLogCheckLogBit(LOG_THREAD))
1078            m_thread_list.Dump();
1079
1080        bool step_more = false;
1081        if (m_thread_list.ShouldStop(step_more))
1082        {
1083            // Wait for the eEventProcessRunningStateChanged event to be reset
1084            // before changing state to stopped to avoid race condition with
1085            // very fast start/stops
1086            struct timespec timeout;
1087            //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000);   // Wait for 250 ms
1088            DNBTimer::OffsetTimeOfDay(&timeout, 1, 0);  // Wait for 250 ms
1089            m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout);
1090            SetState(eStateStopped);
1091        }
1092        else
1093        {
1094            // Resume without checking our current state.
1095            PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
1096        }
1097    }
1098    else
1099    {
1100        DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size());
1101    }
1102}
1103
1104nub_size_t
1105MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed)
1106{
1107    if (m_image_infos_callback != NULL)
1108        return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton);
1109    return 0;
1110}
1111
1112void
1113MachProcess::SharedLibrariesUpdated ( )
1114{
1115    uint32_t event_bits = eEventSharedLibsStateChange;
1116    // Set the shared library event bit to let clients know of shared library
1117    // changes
1118    m_events.SetEvents(event_bits);
1119    // Wait for the event bit to reset if a reset ACK is requested
1120    m_events.WaitForResetAck(event_bits);
1121}
1122
1123void
1124MachProcess::AppendSTDOUT (char* s, size_t len)
1125{
1126    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%d> %s) ...", __FUNCTION__, len, s);
1127    PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
1128    m_stdout_data.append(s, len);
1129    m_events.SetEvents(eEventStdioAvailable);
1130
1131    // Wait for the event bit to reset if a reset ACK is requested
1132    m_events.WaitForResetAck(eEventStdioAvailable);
1133}
1134
1135size_t
1136MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size)
1137{
1138    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size);
1139    PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
1140    size_t bytes_available = m_stdout_data.size();
1141    if (bytes_available > 0)
1142    {
1143        if (bytes_available > buf_size)
1144        {
1145            memcpy(buf, m_stdout_data.data(), buf_size);
1146            m_stdout_data.erase(0, buf_size);
1147            bytes_available = buf_size;
1148        }
1149        else
1150        {
1151            memcpy(buf, m_stdout_data.data(), bytes_available);
1152            m_stdout_data.clear();
1153        }
1154    }
1155    return bytes_available;
1156}
1157
1158nub_addr_t
1159MachProcess::GetDYLDAllImageInfosAddress ()
1160{
1161    DNBError err;
1162    return m_task.GetDYLDAllImageInfosAddress(err);
1163}
1164
1165size_t
1166MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size)
1167{
1168    return 0;
1169}
1170
1171void *
1172MachProcess::STDIOThread(void *arg)
1173{
1174    MachProcess *proc = (MachProcess*) arg;
1175    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg);
1176
1177    // We start use a base and more options so we can control if we
1178    // are currently using a timeout on the mach_msg. We do this to get a
1179    // bunch of related exceptions on our exception port so we can process
1180    // then together. When we have multiple threads, we can get an exception
1181    // per thread and they will come in consecutively. The main thread loop
1182    // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
1183    // flag set in the options, so we will wait forever for an exception on
1184    // our exception port. After we get one exception, we then will use the
1185    // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
1186    // exceptions for our process. After we have received the last pending
1187    // exception, we will get a timeout which enables us to then notify
1188    // our main thread that we have an exception bundle avaiable. We then wait
1189    // for the main thread to tell this exception thread to start trying to get
1190    // exceptions messages again and we start again with a mach_msg read with
1191    // infinite timeout.
1192    DNBError err;
1193    int stdout_fd = proc->GetStdoutFileDescriptor();
1194    int stderr_fd = proc->GetStderrFileDescriptor();
1195    if (stdout_fd == stderr_fd)
1196        stderr_fd = -1;
1197
1198    while (stdout_fd >= 0 || stderr_fd >= 0)
1199    {
1200        ::pthread_testcancel ();
1201
1202        fd_set read_fds;
1203        FD_ZERO (&read_fds);
1204        if (stdout_fd >= 0)
1205            FD_SET (stdout_fd, &read_fds);
1206        if (stderr_fd >= 0)
1207            FD_SET (stderr_fd, &read_fds);
1208        int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
1209
1210        int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL);
1211        DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
1212
1213        if (num_set_fds < 0)
1214        {
1215            int select_errno = errno;
1216            if (DNBLogCheckLogBit(LOG_PROCESS))
1217            {
1218                err.SetError (select_errno, DNBError::POSIX);
1219                err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
1220            }
1221
1222            switch (select_errno)
1223            {
1224            case EAGAIN:    // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO
1225                break;
1226            case EBADF:     // One of the descriptor sets specified an invalid descriptor.
1227                return NULL;
1228                break;
1229            case EINTR:     // A signal was delivered before the time limit expired and before any of the selected events occurred.
1230            case EINVAL:    // The specified time limit is invalid. One of its components is negative or too large.
1231            default:        // Other unknown error
1232                break;
1233            }
1234        }
1235        else if (num_set_fds == 0)
1236        {
1237        }
1238        else
1239        {
1240            char s[1024];
1241            s[sizeof(s)-1] = '\0';  // Ensure we have NULL termination
1242            int bytes_read = 0;
1243            if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds))
1244            {
1245                do
1246                {
1247                    bytes_read = ::read (stdout_fd, s, sizeof(s)-1);
1248                    if (bytes_read < 0)
1249                    {
1250                        int read_errno = errno;
1251                        DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d   errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
1252                    }
1253                    else if (bytes_read == 0)
1254                    {
1255                        // EOF...
1256                        DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d  (reached EOF for child STDOUT)", bytes_read);
1257                        stdout_fd = -1;
1258                    }
1259                    else if (bytes_read > 0)
1260                    {
1261                        proc->AppendSTDOUT(s, bytes_read);
1262                    }
1263
1264                } while (bytes_read > 0);
1265            }
1266
1267            if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds))
1268            {
1269                do
1270                {
1271                    bytes_read = ::read (stderr_fd, s, sizeof(s)-1);
1272                    if (bytes_read < 0)
1273                    {
1274                        int read_errno = errno;
1275                        DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d   errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
1276                    }
1277                    else if (bytes_read == 0)
1278                    {
1279                        // EOF...
1280                        DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d  (reached EOF for child STDERR)", bytes_read);
1281                        stderr_fd = -1;
1282                    }
1283                    else if (bytes_read > 0)
1284                    {
1285                        proc->AppendSTDOUT(s, bytes_read);
1286                    }
1287
1288                } while (bytes_read > 0);
1289            }
1290        }
1291    }
1292    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg);
1293    return NULL;
1294}
1295
1296pid_t
1297MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len)
1298{
1299    // Clear out and clean up from any current state
1300    Clear();
1301    if (pid != 0)
1302    {
1303        DNBError err;
1304        // Make sure the process exists...
1305        if (::getpgid (pid) < 0)
1306        {
1307            err.SetErrorToErrno();
1308            const char *err_cstr = err.AsString();
1309            ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process");
1310            return INVALID_NUB_PROCESS;
1311        }
1312
1313        SetState(eStateAttaching);
1314        m_pid = pid;
1315        // Let ourselves know we are going to be using SBS if the correct flag bit is set...
1316#if defined (__arm__)
1317        if (IsSBProcess(pid))
1318            m_flags |= eMachProcessFlagsUsingSBS;
1319#endif
1320        if (!m_task.StartExceptionThread(err))
1321        {
1322            const char *err_cstr = err.AsString();
1323            ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread");
1324            DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
1325            m_pid = INVALID_NUB_PROCESS;
1326            return INVALID_NUB_PROCESS;
1327        }
1328
1329        errno = 0;
1330        if (::ptrace (PT_ATTACHEXC, pid, 0, 0))
1331            err.SetError(errno);
1332        else
1333            err.Clear();
1334
1335        if (err.Success())
1336        {
1337            m_flags |= eMachProcessFlagsAttached;
1338            // Sleep a bit to let the exception get received and set our process status
1339            // to stopped.
1340            ::usleep(250000);
1341            DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
1342            return m_pid;
1343        }
1344        else
1345        {
1346            ::snprintf (err_str, err_len, "%s", err.AsString());
1347            DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
1348        }
1349    }
1350    return INVALID_NUB_PROCESS;
1351}
1352
1353// Do the process specific setup for attach.  If this returns NULL, then there's no
1354// platform specific stuff to be done to wait for the attach.  If you get non-null,
1355// pass that token to the CheckForProcess method, and then to CleanupAfterAttach.
1356
1357//  Call PrepareForAttach before attaching to a process that has not yet launched
1358// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach.
1359// You should call CleanupAfterAttach to free the token, and do whatever other
1360// cleanup seems good.
1361
1362const void *
1363MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str)
1364{
1365#if defined (__arm__)
1366    // Tell SpringBoard to halt the next launch of this application on startup.
1367
1368    if (!waitfor)
1369        return NULL;
1370
1371    const char *app_ext = strstr(path, ".app");
1372    if (app_ext == NULL)
1373    {
1374        DNBLogThreadedIf(LOG_PROCESS, "%s: path '%s' doesn't contain .app, we can't tell springboard to wait for launch...", path);
1375        return NULL;
1376    }
1377
1378    if (launch_flavor != eLaunchFlavorSpringBoard
1379        && launch_flavor != eLaunchFlavorDefault)
1380        return NULL;
1381
1382    std::string app_bundle_path(path, app_ext + strlen(".app"));
1383
1384    CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), err_str);
1385    std::string bundleIDStr;
1386    CFString::UTF8(bundleIDCFStr, bundleIDStr);
1387    DNBLogThreadedIf(LOG_PROCESS, "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", app_bundle_path.c_str (), bundleIDStr.c_str());
1388
1389    if (bundleIDCFStr == NULL)
1390    {
1391        return NULL;
1392    }
1393
1394    SBSApplicationLaunchError sbs_error = 0;
1395
1396    const char *stdout_err = "/dev/null";
1397    CFString stdio_path;
1398    stdio_path.SetFileSystemRepresentation (stdout_err);
1399
1400    DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", bundleIDStr.c_str(), stdout_err, stdout_err);
1401    sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
1402                                                  (CFURLRef)NULL,         // openURL
1403                                                  NULL, // launch_argv.get(),
1404                                                  NULL, // launch_envp.get(),  // CFDictionaryRef environment
1405                                                  stdio_path.get(),
1406                                                  stdio_path.get(),
1407                                                  SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger);
1408
1409    if (sbs_error != SBSApplicationLaunchErrorSuccess)
1410    {
1411        err_str.SetError(sbs_error, DNBError::SpringBoard);
1412        return NULL;
1413    }
1414
1415    DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
1416    return bundleIDCFStr;
1417# else
1418  return NULL;
1419#endif
1420}
1421
1422// Pass in the token you got from PrepareForAttach.  If there is a process
1423// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS
1424// will be returned.
1425
1426nub_process_t
1427MachProcess::CheckForProcess (const void *attach_token)
1428{
1429    if (attach_token == NULL)
1430        return INVALID_NUB_PROCESS;
1431
1432#if defined (__arm__)
1433    CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
1434    Boolean got_it;
1435    nub_process_t attach_pid;
1436    got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid);
1437    if (got_it)
1438        return attach_pid;
1439    else
1440        return INVALID_NUB_PROCESS;
1441#endif
1442    return INVALID_NUB_PROCESS;
1443}
1444
1445// Call this to clean up after you have either attached or given up on the attach.
1446// Pass true for success if you have attached, false if you have not.
1447// The token will also be freed at this point, so you can't use it after calling
1448// this method.
1449
1450void
1451MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str)
1452{
1453#if defined (__arm__)
1454    if (attach_token == NULL)
1455        return;
1456
1457    // Tell SpringBoard to cancel the debug on next launch of this application
1458    // if we failed to attach
1459    if (!success)
1460    {
1461        SBSApplicationLaunchError sbs_error = 0;
1462        CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
1463
1464        sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
1465                                                      (CFURLRef)NULL,
1466                                                      NULL,
1467                                                      NULL,
1468                                                      NULL,
1469                                                      NULL,
1470                                                      SBSApplicationCancelDebugOnNextLaunch);
1471
1472        if (sbs_error != SBSApplicationLaunchErrorSuccess)
1473        {
1474            err_str.SetError(sbs_error, DNBError::SpringBoard);
1475            return;
1476        }
1477    }
1478
1479    CFRelease((CFStringRef) attach_token);
1480#endif
1481}
1482
1483pid_t
1484MachProcess::LaunchForDebug
1485(
1486    const char *path,
1487    char const *argv[],
1488    char const *envp[],
1489    const char *stdio_path,
1490    nub_launch_flavor_t launch_flavor,
1491    int disable_aslr,
1492    DNBError &launch_err
1493)
1494{
1495    // Clear out and clean up from any current state
1496    Clear();
1497
1498    DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr);
1499
1500    // Fork a child process for debugging
1501    SetState(eStateLaunching);
1502
1503    switch (launch_flavor)
1504    {
1505    case eLaunchFlavorForkExec:
1506        m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err);
1507        break;
1508
1509    case eLaunchFlavorPosixSpawn:
1510        m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, argv, envp, stdio_path, this, disable_aslr, launch_err);
1511        break;
1512
1513#if defined (__arm__)
1514
1515    case eLaunchFlavorSpringBoard:
1516        {
1517            const char *app_ext = strstr(path, ".app");
1518            if (app_ext != NULL)
1519            {
1520                std::string app_bundle_path(path, app_ext + strlen(".app"));
1521                return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, launch_err);
1522            }
1523        }
1524        break;
1525
1526#endif
1527
1528    default:
1529        // Invalid  launch
1530        launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
1531        return INVALID_NUB_PROCESS;
1532    }
1533
1534    if (m_pid == INVALID_NUB_PROCESS)
1535    {
1536        // If we don't have a valid process ID and no one has set the error,
1537        // then return a generic error
1538        if (launch_err.Success())
1539            launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
1540    }
1541    else
1542    {
1543        m_path = path;
1544        size_t i;
1545        char const *arg;
1546        for (i=0; (arg = argv[i]) != NULL; i++)
1547            m_args.push_back(arg);
1548
1549        m_task.StartExceptionThread(launch_err);
1550        if (launch_err.Fail())
1551        {
1552            if (launch_err.AsString() == NULL)
1553                launch_err.SetErrorString("unable to start the exception thread");
1554            ::ptrace (PT_KILL, m_pid, 0, 0);
1555            m_pid = INVALID_NUB_PROCESS;
1556            return INVALID_NUB_PROCESS;
1557        }
1558
1559        StartSTDIOThread();
1560
1561        if (launch_flavor == eLaunchFlavorPosixSpawn)
1562        {
1563
1564            SetState (eStateAttaching);
1565            errno = 0;
1566            int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
1567            if (err == 0)
1568            {
1569                m_flags |= eMachProcessFlagsAttached;
1570                DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid);
1571                launch_err.Clear();
1572            }
1573            else
1574            {
1575                SetState (eStateExited);
1576                DNBError ptrace_err(errno, DNBError::POSIX);
1577                DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid %d (err = %i, errno = %i (%s))", m_pid, err, ptrace_err.Error(), ptrace_err.AsString());
1578                launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
1579            }
1580        }
1581        else
1582        {
1583            launch_err.Clear();
1584        }
1585    }
1586    return m_pid;
1587}
1588
1589pid_t
1590MachProcess::PosixSpawnChildForPTraceDebugging
1591(
1592    const char *path,
1593    char const *argv[],
1594    char const *envp[],
1595    const char *stdio_path,
1596    MachProcess* process,
1597    int disable_aslr,
1598    DNBError& err
1599)
1600{
1601    posix_spawnattr_t attr;
1602    short flags;
1603    DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp);
1604
1605    err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX);
1606    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1607        err.LogThreaded("::posix_spawnattr_init ( &attr )");
1608    if (err.Fail())
1609        return INVALID_NUB_PROCESS;
1610
1611    flags = POSIX_SPAWN_START_SUSPENDED;
1612    if (disable_aslr)
1613        flags |= _POSIX_SPAWN_DISABLE_ASLR;
1614
1615    err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX);
1616    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1617        err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : "");
1618    if (err.Fail())
1619        return INVALID_NUB_PROCESS;
1620
1621    // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail
1622    // and we will fail to continue with our process...
1623
1624    // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment....
1625
1626#if !defined(__arm__)
1627
1628    // We don't need to do this for ARM, and we really shouldn't now that we
1629    // have multiple CPU subtypes and no posix_spawnattr call that allows us
1630    // to set which CPU subtype to launch...
1631    cpu_type_t cpu_type = DNBArch::GetCPUType();
1632    size_t ocount = 0;
1633    err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX);
1634    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1635        err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu_type, ocount);
1636
1637    if (err.Fail() != 0 || ocount != 1)
1638        return INVALID_NUB_PROCESS;
1639
1640#endif
1641
1642    PseudoTerminal pty;
1643
1644    posix_spawn_file_actions_t file_actions;
1645    err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX);
1646    int file_actions_valid = err.Success();
1647    if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS))
1648        err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )");
1649    int pty_error = -1;
1650    pid_t pid = INVALID_NUB_PROCESS;
1651    if (file_actions_valid)
1652    {
1653        if (stdio_path == NULL)
1654        {
1655            pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
1656            if (pty_error == PseudoTerminal::success)
1657                stdio_path = pty.SlaveName();
1658            // Make sure we were able to get the slave name
1659            if (stdio_path == NULL)
1660                stdio_path = "/dev/null";
1661        }
1662
1663        if (stdio_path != NULL)
1664        {
1665            int slave_fd_err = open (stdio_path, O_RDWR, 0);
1666            int slave_fd_in = open (stdio_path, O_RDONLY, 0);
1667            int slave_fd_out = open (stdio_path, O_WRONLY, 0);
1668
1669            err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_err, STDERR_FILENO), DNBError::POSIX);
1670            if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1671                err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d, newfiledes = STDERR_FILENO )", slave_fd_err);
1672
1673            err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_in, STDIN_FILENO), DNBError::POSIX);
1674            if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1675                err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d, newfiledes = STDIN_FILENO )", slave_fd_in);
1676
1677            err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_out, STDOUT_FILENO), DNBError::POSIX);
1678            if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1679                err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d, newfiledes = STDOUT_FILENO )", slave_fd_out);
1680        }
1681        err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
1682        if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1683            err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp);
1684    }
1685    else
1686    {
1687        err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
1688        if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1689            err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp);
1690    }
1691
1692    // We have seen some cases where posix_spawnp was returning a valid
1693    // looking pid even when an error was returned, so clear it out
1694    if (err.Fail())
1695        pid = INVALID_NUB_PROCESS;
1696
1697    if (pty_error == 0)
1698    {
1699        if (process != NULL)
1700        {
1701            int master_fd = pty.ReleaseMasterFD();
1702            process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
1703        }
1704    }
1705
1706    ::posix_spawnattr_destroy (&attr);
1707
1708    if (file_actions_valid)
1709    {
1710        DNBError err2;
1711        err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX);
1712        if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1713            err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )");
1714    }
1715
1716    return pid;
1717}
1718
1719pid_t
1720MachProcess::ForkChildForPTraceDebugging
1721(
1722    const char *path,
1723    char const *argv[],
1724    char const *envp[],
1725    MachProcess* process,
1726    DNBError& launch_err
1727)
1728{
1729    PseudoTerminal::Error pty_error = PseudoTerminal::success;
1730
1731    // Use a fork that ties the child process's stdin/out/err to a pseudo
1732    // terminal so we can read it in our MachProcess::STDIOThread
1733    // as unbuffered io.
1734    PseudoTerminal pty;
1735    pid_t pid = pty.Fork(pty_error);
1736
1737    if (pid < 0)
1738    {
1739        //--------------------------------------------------------------
1740        // Error during fork.
1741        //--------------------------------------------------------------
1742        return pid;
1743    }
1744    else if (pid == 0)
1745    {
1746        //--------------------------------------------------------------
1747        // Child process
1748        //--------------------------------------------------------------
1749        ::ptrace (PT_TRACE_ME, 0, 0, 0);    // Debug this process
1750        ::ptrace (PT_SIGEXC, 0, 0, 0);    // Get BSD signals as mach exceptions
1751
1752        // If our parent is setgid, lets make sure we don't inherit those
1753        // extra powers due to nepotism.
1754        ::setgid (getgid ());
1755
1756        // Let the child have its own process group. We need to execute
1757        // this call in both the child and parent to avoid a race condition
1758        // between the two processes.
1759        ::setpgid (0, 0);    // Set the child process group to match its pid
1760
1761        // Sleep a bit to before the exec call
1762        ::sleep (1);
1763
1764        // Turn this process into
1765        ::execv (path, (char * const *)argv);
1766        // Exit with error code. Child process should have taken
1767        // over in above exec call and if the exec fails it will
1768        // exit the child process below.
1769        ::exit (127);
1770    }
1771    else
1772    {
1773        //--------------------------------------------------------------
1774        // Parent process
1775        //--------------------------------------------------------------
1776        // Let the child have its own process group. We need to execute
1777        // this call in both the child and parent to avoid a race condition
1778        // between the two processes.
1779        ::setpgid (pid, pid);    // Set the child process group to match its pid
1780
1781        if (process != NULL)
1782        {
1783            // Release our master pty file descriptor so the pty class doesn't
1784            // close it and so we can continue to use it in our STDIO thread
1785            int master_fd = pty.ReleaseMasterFD();
1786            process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
1787        }
1788    }
1789    return pid;
1790}
1791
1792#if defined (__arm__)
1793
1794pid_t
1795MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], DNBError &launch_err)
1796{
1797    // Clear out and clean up from any current state
1798    Clear();
1799
1800    DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
1801
1802    // Fork a child process for debugging
1803    SetState(eStateLaunching);
1804    m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, this, launch_err);
1805    if (m_pid != 0)
1806    {
1807        m_flags |= eMachProcessFlagsUsingSBS;
1808        m_path = path;
1809        size_t i;
1810        char const *arg;
1811        for (i=0; (arg = argv[i]) != NULL; i++)
1812            m_args.push_back(arg);
1813        m_task.StartExceptionThread(launch_err);
1814
1815        if (launch_err.Fail())
1816        {
1817            if (launch_err.AsString() == NULL)
1818                launch_err.SetErrorString("unable to start the exception thread");
1819            ::ptrace (PT_KILL, m_pid, 0, 0);
1820            m_pid = INVALID_NUB_PROCESS;
1821            return INVALID_NUB_PROCESS;
1822        }
1823
1824        StartSTDIOThread();
1825        SetState (eStateAttaching);
1826        int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
1827        if (err == 0)
1828        {
1829            m_flags |= eMachProcessFlagsAttached;
1830            DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
1831        }
1832        else
1833        {
1834            SetState (eStateExited);
1835            DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
1836        }
1837    }
1838    return m_pid;
1839}
1840
1841#include <servers/bootstrap.h>
1842
1843// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
1844// or NULL if there was some problem getting the bundle id.
1845static CFStringRef
1846CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str)
1847{
1848    CFBundle bundle(app_bundle_path);
1849    CFStringRef bundleIDCFStr = bundle.GetIdentifier();
1850    std::string bundleID;
1851    if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
1852    {
1853        struct stat app_bundle_stat;
1854        char err_msg[PATH_MAX];
1855
1856        if (::stat (app_bundle_path, &app_bundle_stat) < 0)
1857        {
1858            err_str.SetError(errno, DNBError::POSIX);
1859            snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path);
1860            err_str.SetErrorString(err_msg);
1861            DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
1862        }
1863        else
1864        {
1865            err_str.SetError(-1, DNBError::Generic);
1866            snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path);
1867            err_str.SetErrorString(err_msg);
1868            DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
1869        }
1870        return NULL;
1871    }
1872
1873    DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
1874    CFRetain (bundleIDCFStr);
1875
1876    return bundleIDCFStr;
1877}
1878
1879pid_t
1880MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], MachProcess* process, DNBError &launch_err)
1881{
1882    DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process);
1883    CFAllocatorRef alloc = kCFAllocatorDefault;
1884
1885    if (argv[0] == NULL)
1886        return INVALID_NUB_PROCESS;
1887
1888    size_t argc = 0;
1889    // Count the number of arguments
1890    while (argv[argc] != NULL)
1891        argc++;
1892
1893    // Enumerate the arguments
1894    size_t first_launch_arg_idx = 1;
1895    CFReleaser<CFMutableArrayRef> launch_argv;
1896
1897    if (argv[first_launch_arg_idx])
1898    {
1899        size_t launch_argc = argc > 0 ? argc - 1 : 0;
1900        launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks));
1901        size_t i;
1902        char const *arg;
1903        CFString launch_arg;
1904        for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
1905        {
1906            launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8));
1907            if (launch_arg.get() != NULL)
1908                CFArrayAppendValue(launch_argv.get(), launch_arg.get());
1909            else
1910                break;
1911        }
1912    }
1913
1914    // Next fill in the arguments dictionary.  Note, the envp array is of the form
1915    // Variable=value but SpringBoard wants a CF dictionary.  So we have to convert
1916    // this here.
1917
1918    CFReleaser<CFMutableDictionaryRef> launch_envp;
1919
1920    if (envp[0])
1921    {
1922        launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1923        const char *value;
1924        int name_len;
1925        CFString name_string, value_string;
1926
1927        for (int i = 0; envp[i] != NULL; i++)
1928        {
1929            value = strstr (envp[i], "=");
1930
1931            // If the name field is empty or there's no =, skip it.  Somebody's messing with us.
1932            if (value == NULL || value == envp[i])
1933                continue;
1934
1935            name_len = value - envp[i];
1936
1937            // Now move value over the "="
1938            value++;
1939
1940            name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false));
1941            value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
1942            CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get());
1943        }
1944    }
1945
1946    CFString stdio_path;
1947
1948    PseudoTerminal pty;
1949    PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
1950    if (pty_err == PseudoTerminal::success)
1951    {
1952        const char* slave_name = pty.SlaveName();
1953        DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
1954        if (slave_name && slave_name[0])
1955        {
1956            ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
1957            stdio_path.SetFileSystemRepresentation (slave_name);
1958        }
1959    }
1960
1961    if (stdio_path.get() == NULL)
1962    {
1963        stdio_path.SetFileSystemRepresentation ("/dev/null");
1964    }
1965
1966    CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err);
1967    if (bundleIDCFStr == NULL)
1968        return INVALID_NUB_PROCESS;
1969
1970    std::string bundleID;
1971    CFString::UTF8(bundleIDCFStr, bundleID);
1972
1973    CFData argv_data(NULL);
1974
1975    if (launch_argv.get())
1976    {
1977        if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL)
1978        {
1979            DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__);
1980            return INVALID_NUB_PROCESS;
1981        }
1982    }
1983
1984    DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__);
1985
1986    // Find SpringBoard
1987    SBSApplicationLaunchError sbs_error = 0;
1988    sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
1989                                                  (CFURLRef)NULL,         // openURL
1990                                                  launch_argv.get(),
1991                                                  launch_envp.get(),  // CFDictionaryRef environment
1992                                                  stdio_path.get(),
1993                                                  stdio_path.get(),
1994                                                  SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
1995
1996
1997    launch_err.SetError(sbs_error, DNBError::SpringBoard);
1998
1999    if (sbs_error == SBSApplicationLaunchErrorSuccess)
2000    {
2001        static const useconds_t pid_poll_interval = 200000;
2002        static const useconds_t pid_poll_timeout = 30000000;
2003
2004        useconds_t pid_poll_total = 0;
2005
2006        nub_process_t pid = INVALID_NUB_PROCESS;
2007        Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
2008        // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired
2009        // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started
2010        // yet, or that it died very quickly (if you weren't using waitForDebugger).
2011        while (!pid_found && pid_poll_total < pid_poll_timeout)
2012        {
2013            usleep (pid_poll_interval);
2014            pid_poll_total += pid_poll_interval;
2015            DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str());
2016            pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
2017        }
2018
2019        CFRelease (bundleIDCFStr);
2020        if (pid_found)
2021        {
2022            if (process != NULL)
2023            {
2024                // Release our master pty file descriptor so the pty class doesn't
2025                // close it and so we can continue to use it in our STDIO thread
2026                int master_fd = pty.ReleaseMasterFD();
2027                process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
2028            }
2029            DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
2030        }
2031        else
2032        {
2033            DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str());
2034        }
2035        return pid;
2036    }
2037
2038    DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error);
2039    return INVALID_NUB_PROCESS;
2040}
2041
2042#endif // #if defined (__arm__)
2043
2044
2045