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