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