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