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