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