1//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  Created by Greg Clayton on 6/15/07.
11//
12//===----------------------------------------------------------------------===//
13
14#include "DNB.h"
15#include <inttypes.h>
16#include <mach/mach.h>
17#include <signal.h>
18#include <spawn.h>
19#include <sys/fcntl.h>
20#include <sys/types.h>
21#include <sys/ptrace.h>
22#include <sys/stat.h>
23#include <sys/sysctl.h>
24#include <unistd.h>
25#include <pthread.h>
26#include <mach-o/loader.h>
27#include <uuid/uuid.h>
28#include "MacOSX/CFUtils.h"
29#include "SysSignal.h"
30
31#include <algorithm>
32#include <map>
33
34#import <Foundation/Foundation.h>
35
36#include "DNBDataRef.h"
37#include "DNBLog.h"
38#include "DNBThreadResumeActions.h"
39#include "DNBTimer.h"
40#include "MachProcess.h"
41#include "PseudoTerminal.h"
42
43#include "CFBundle.h"
44#include "CFData.h"
45#include "CFString.h"
46
47#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
48// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
49// or NULL if there was some problem getting the bundle id.
50static CFStringRef
51CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str)
52{
53    CFBundle bundle(app_bundle_path);
54    CFStringRef bundleIDCFStr = bundle.GetIdentifier();
55    std::string bundleID;
56    if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
57    {
58        struct stat app_bundle_stat;
59        char err_msg[PATH_MAX];
60
61        if (::stat (app_bundle_path, &app_bundle_stat) < 0)
62        {
63            err_str.SetError(errno, DNBError::POSIX);
64            snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path);
65            err_str.SetErrorString(err_msg);
66            DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
67        }
68        else
69        {
70            err_str.SetError(-1, DNBError::Generic);
71            snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path);
72            err_str.SetErrorString(err_msg);
73            DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
74        }
75        return NULL;
76    }
77
78    DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
79    CFRetain (bundleIDCFStr);
80
81    return bundleIDCFStr;
82}
83#endif // #if defined 9WITH_SPRINGBOARD) || defined (WITH_BKS)
84
85#ifdef WITH_SPRINGBOARD
86
87#include <CoreFoundation/CoreFoundation.h>
88#include <SpringBoardServices/SpringBoardServer.h>
89#include <SpringBoardServices/SBSWatchdogAssertion.h>
90
91static bool
92IsSBProcess (nub_process_t pid)
93{
94    CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid));
95    return appIdsForPID.get() != NULL;
96}
97
98#endif // WITH_SPRINGBOARD
99
100#ifdef WITH_BKS
101#import <Foundation/Foundation.h>
102extern "C"
103{
104#import <BackBoardServices/BackBoardServices.h>
105#import <BackBoardServices/BKSSystemService_LaunchServices.h>
106#import <BackBoardServices/BKSOpenApplicationConstants_Private.h>
107}
108
109static bool
110IsBKSProcess (nub_process_t pid)
111{
112    BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init];
113    BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid];
114    return app_state != BKSApplicationStateUnknown;
115}
116
117static void
118SetBKSError (BKSOpenApplicationErrorCode error_code, DNBError &error)
119{
120    error.SetError (error_code, DNBError::BackBoard);
121    NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString(error_code);
122    const char *err_str = NULL;
123    if (err_nsstr == NULL)
124        err_str = "unknown BKS error";
125    else
126    {
127        err_str = [err_nsstr UTF8String];
128        if (err_str == NULL)
129            err_str = "unknown BKS error";
130    }
131    error.SetErrorString(err_str);
132}
133
134static const int BKS_OPEN_APPLICATION_TIMEOUT_ERROR = 111;
135#endif // WITH_BKS
136#if 0
137#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
138#else
139#define DEBUG_LOG(fmt, ...)
140#endif
141
142#ifndef MACH_PROCESS_USE_POSIX_SPAWN
143#define MACH_PROCESS_USE_POSIX_SPAWN 1
144#endif
145
146#ifndef _POSIX_SPAWN_DISABLE_ASLR
147#define _POSIX_SPAWN_DISABLE_ASLR       0x0100
148#endif
149
150MachProcess::MachProcess() :
151    m_pid               (0),
152    m_cpu_type          (0),
153    m_child_stdin       (-1),
154    m_child_stdout      (-1),
155    m_child_stderr      (-1),
156    m_path              (),
157    m_args              (),
158    m_task              (this),
159    m_flags             (eMachProcessFlagsNone),
160    m_stdio_thread      (0),
161    m_stdio_mutex       (PTHREAD_MUTEX_RECURSIVE),
162    m_stdout_data       (),
163    m_profile_enabled   (false),
164    m_profile_interval_usec (0),
165    m_profile_thread    (0),
166    m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE),
167    m_profile_data      (),
168    m_thread_actions    (),
169    m_exception_messages (),
170    m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE),
171    m_thread_list       (),
172    m_activities        (),
173    m_state             (eStateUnloaded),
174    m_state_mutex       (PTHREAD_MUTEX_RECURSIVE),
175    m_events            (0, kAllEventsMask),
176    m_private_events    (0, kAllEventsMask),
177    m_breakpoints       (),
178    m_watchpoints       (),
179    m_name_to_addr_callback(NULL),
180    m_name_to_addr_baton(NULL),
181    m_image_infos_callback(NULL),
182    m_image_infos_baton(NULL),
183    m_sent_interrupt_signo (0),
184    m_auto_resume_signo (0),
185    m_did_exec (false)
186{
187    DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
188}
189
190MachProcess::~MachProcess()
191{
192    DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
193    Clear();
194}
195
196pid_t
197MachProcess::SetProcessID(pid_t pid)
198{
199    // Free any previous process specific data or resources
200    Clear();
201    // Set the current PID appropriately
202    if (pid == 0)
203        m_pid = ::getpid ();
204    else
205        m_pid = pid;
206    return m_pid;    // Return actually PID in case a zero pid was passed in
207}
208
209nub_state_t
210MachProcess::GetState()
211{
212    // If any other threads access this we will need a mutex for it
213    PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
214    return m_state;
215}
216
217const char *
218MachProcess::ThreadGetName(nub_thread_t tid)
219{
220    return m_thread_list.GetName(tid);
221}
222
223nub_state_t
224MachProcess::ThreadGetState(nub_thread_t tid)
225{
226    return m_thread_list.GetState(tid);
227}
228
229
230nub_size_t
231MachProcess::GetNumThreads () const
232{
233    return m_thread_list.NumThreads();
234}
235
236nub_thread_t
237MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const
238{
239    return m_thread_list.ThreadIDAtIndex(thread_idx);
240}
241
242nub_thread_t
243MachProcess::GetThreadIDForMachPortNumber (thread_t mach_port_number) const
244{
245    return m_thread_list.GetThreadIDByMachPortNumber (mach_port_number);
246}
247
248nub_bool_t
249MachProcess::SyncThreadState (nub_thread_t tid)
250{
251    MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid));
252    if (!thread_sp)
253        return false;
254    kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber());
255    DNBLogThreadedIf (LOG_THREAD, "thread = 0x%8.8" PRIx32 " calling thread_abort_safely (tid) => %u (GetGPRState() for stop_count = %u)", thread_sp->MachPortNumber(), kret, thread_sp->Process()->StopCount());
256
257    if (kret == KERN_SUCCESS)
258        return true;
259    else
260        return false;
261
262}
263
264ThreadInfo::QoS
265MachProcess::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index)
266{
267    return m_thread_list.GetRequestedQoS (tid, tsd, dti_qos_class_index);
268}
269
270nub_addr_t
271MachProcess::GetPThreadT (nub_thread_t tid)
272{
273    return m_thread_list.GetPThreadT (tid);
274}
275
276nub_addr_t
277MachProcess::GetDispatchQueueT (nub_thread_t tid)
278{
279    return m_thread_list.GetDispatchQueueT (tid);
280}
281
282nub_addr_t
283MachProcess::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size)
284{
285    return m_thread_list.GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
286}
287
288
289
290JSONGenerator::ObjectSP
291MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count)
292{
293    JSONGenerator::DictionarySP reply_sp;
294
295    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
296    struct kinfo_proc processInfo;
297    size_t bufsize = sizeof(processInfo);
298    if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0)
299    {
300        uint32_t pointer_size = 4;
301        if (processInfo.kp_proc.p_flag & P_LP64)
302            pointer_size = 8;
303
304        struct segment
305        {
306            std::string name;
307            uint64_t vmaddr;
308            uint64_t vmsize;
309            uint64_t fileoff;
310            uint64_t filesize;
311            uint64_t maxprot;
312            uint64_t initprot;
313            uint64_t nsects;
314            uint64_t flags;
315        };
316
317        struct image_info
318        {
319            uint64_t load_address;
320            std::string pathname;
321            uint64_t mod_date;
322            struct mach_header_64 mach_header;
323            std::vector<struct segment> segments;
324            uuid_t uuid;
325        };
326        std::vector<image_info> image_infos;
327        size_t image_infos_size = image_count * 3 * pointer_size;
328
329        uint8_t *image_info_buf = (uint8_t *) malloc (image_infos_size);
330        if (image_info_buf == NULL)
331        {
332            return reply_sp;
333        }
334        if (ReadMemory (image_list_address, image_infos_size, image_info_buf) != image_infos_size)
335        {
336            return reply_sp;
337        }
338
339
340        ////  First the image_infos array with (load addr, pathname, mod date) tuples
341
342
343        for (size_t i = 0; i <  image_count; i++)
344        {
345            struct image_info info;
346            nub_addr_t pathname_address;
347            if (pointer_size == 4)
348            {
349                uint32_t load_address_32;
350                uint32_t pathname_address_32;
351                uint32_t mod_date_32;
352                ::memcpy (&load_address_32, image_info_buf + (i * 3 * pointer_size), 4);
353                ::memcpy (&pathname_address_32, image_info_buf + (i * 3 * pointer_size) + pointer_size, 4);
354                ::memcpy (&mod_date_32, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 4);
355                info.load_address = load_address_32;
356                info.mod_date = mod_date_32;
357                pathname_address = pathname_address_32;
358            }
359            else
360            {
361                uint64_t load_address_64;
362                uint64_t pathname_address_64;
363                uint64_t mod_date_64;
364                ::memcpy (&load_address_64, image_info_buf + (i * 3 * pointer_size), 8);
365                ::memcpy (&pathname_address_64, image_info_buf + (i * 3 * pointer_size) + pointer_size, 8);
366                ::memcpy (&mod_date_64, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 8);
367                info.load_address = load_address_64;
368                info.mod_date = mod_date_64;
369                pathname_address = pathname_address_64;
370            }
371            char strbuf[17];
372            info.pathname = "";
373            uint64_t pathname_ptr = pathname_address;
374            bool still_reading = true;
375            while (still_reading && ReadMemory (pathname_ptr, sizeof (strbuf) - 1, strbuf) == sizeof (strbuf) - 1)
376            {
377                strbuf[sizeof(strbuf) - 1] = '\0';
378                info.pathname += strbuf;
379                pathname_ptr += sizeof (strbuf) - 1;
380                // Stop if we found nul byte indicating the end of the string
381                for (size_t i = 0; i < sizeof(strbuf) - 1; i++)
382                {
383                    if (strbuf[i] == '\0')
384                    {
385                        still_reading = false;
386                        break;
387                    }
388                }
389            }
390            uuid_clear (info.uuid);
391            image_infos.push_back (info);
392        }
393        if (image_infos.size() == 0)
394        {
395            return reply_sp;
396        }
397
398
399        ////  Second, read the mach header / load commands for all the dylibs
400
401
402        for (size_t i = 0; i < image_count; i++)
403        {
404            uint64_t load_cmds_p;
405            if (pointer_size == 4)
406            {
407                struct mach_header header;
408                if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header), &header) != sizeof (struct mach_header))
409                {
410                    return reply_sp;
411                }
412                load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header);
413                image_infos[i].mach_header.magic = header.magic;
414                image_infos[i].mach_header.cputype = header.cputype;
415                image_infos[i].mach_header.cpusubtype = header.cpusubtype;
416                image_infos[i].mach_header.filetype = header.filetype;
417                image_infos[i].mach_header.ncmds = header.ncmds;
418                image_infos[i].mach_header.sizeofcmds = header.sizeofcmds;
419                image_infos[i].mach_header.flags = header.flags;
420            }
421            else
422            {
423                struct mach_header_64 header;
424                if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header_64), &header) != sizeof (struct mach_header_64))
425                {
426                    return reply_sp;
427                }
428                load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header_64);
429                image_infos[i].mach_header.magic = header.magic;
430                image_infos[i].mach_header.cputype = header.cputype;
431                image_infos[i].mach_header.cpusubtype = header.cpusubtype;
432                image_infos[i].mach_header.filetype = header.filetype;
433                image_infos[i].mach_header.ncmds = header.ncmds;
434                image_infos[i].mach_header.sizeofcmds = header.sizeofcmds;
435                image_infos[i].mach_header.flags = header.flags;
436            }
437            for (uint32_t j = 0; j < image_infos[i].mach_header.ncmds; j++)
438            {
439                struct load_command lc;
440                if (ReadMemory (load_cmds_p, sizeof (struct load_command), &lc) != sizeof (struct load_command))
441                {
442                    return reply_sp;
443                }
444                if (lc.cmd == LC_SEGMENT)
445                {
446                    struct segment_command seg;
447                    if (ReadMemory (load_cmds_p, sizeof (struct segment_command), &seg) != sizeof (struct segment_command))
448                    {
449                        return reply_sp;
450                    }
451                    struct segment this_seg;
452                    char name[17];
453                    ::memset (name, 0, sizeof (name));
454                    memcpy (name, seg.segname, sizeof (seg.segname));
455                    this_seg.name = name;
456                    this_seg.vmaddr = seg.vmaddr;
457                    this_seg.vmsize = seg.vmsize;
458                    this_seg.fileoff = seg.fileoff;
459                    this_seg.filesize = seg.filesize;
460                    this_seg.maxprot = seg.maxprot;
461                    this_seg.initprot = seg.initprot;
462                    this_seg.nsects = seg.nsects;
463                    this_seg.flags = seg.flags;
464                    image_infos[i].segments.push_back(this_seg);
465                }
466                if (lc.cmd == LC_SEGMENT_64)
467                {
468                    struct segment_command_64 seg;
469                    if (ReadMemory (load_cmds_p, sizeof (struct segment_command_64), &seg) != sizeof (struct segment_command_64))
470                    {
471                        return reply_sp;
472                    }
473                    struct segment this_seg;
474                    char name[17];
475                    ::memset (name, 0, sizeof (name));
476                    memcpy (name, seg.segname, sizeof (seg.segname));
477                    this_seg.name = name;
478                    this_seg.vmaddr = seg.vmaddr;
479                    this_seg.vmsize = seg.vmsize;
480                    this_seg.fileoff = seg.fileoff;
481                    this_seg.filesize = seg.filesize;
482                    this_seg.maxprot = seg.maxprot;
483                    this_seg.initprot = seg.initprot;
484                    this_seg.nsects = seg.nsects;
485                    this_seg.flags = seg.flags;
486                    image_infos[i].segments.push_back(this_seg);
487                }
488                if (lc.cmd == LC_UUID)
489                {
490                    struct uuid_command uuidcmd;
491                    if (ReadMemory (load_cmds_p, sizeof (struct uuid_command), &uuidcmd) == sizeof (struct uuid_command))
492                        uuid_copy (image_infos[i].uuid, uuidcmd.uuid);
493                }
494                load_cmds_p += lc.cmdsize;
495            }
496        }
497
498
499        ////  Thrid, format all of the above in the JSONGenerator object.
500
501
502        JSONGenerator::ArraySP image_infos_array_sp (new JSONGenerator::Array());
503        for (size_t i = 0; i < image_count; i++)
504        {
505            JSONGenerator::DictionarySP image_info_dict_sp (new JSONGenerator::Dictionary());
506            image_info_dict_sp->AddIntegerItem ("load_address", image_infos[i].load_address);
507            image_info_dict_sp->AddIntegerItem ("mod_date", image_infos[i].mod_date);
508            image_info_dict_sp->AddStringItem ("pathname", image_infos[i].pathname);
509
510            uuid_string_t uuidstr;
511            uuid_unparse_upper (image_infos[i].uuid, uuidstr);
512            image_info_dict_sp->AddStringItem ("uuid", uuidstr);
513
514            JSONGenerator::DictionarySP mach_header_dict_sp (new JSONGenerator::Dictionary());
515            mach_header_dict_sp->AddIntegerItem ("magic", image_infos[i].mach_header.magic);
516            mach_header_dict_sp->AddIntegerItem ("cputype", image_infos[i].mach_header.cputype);
517            mach_header_dict_sp->AddIntegerItem ("cpusubtype", image_infos[i].mach_header.cpusubtype);
518            mach_header_dict_sp->AddIntegerItem ("filetype", image_infos[i].mach_header.filetype);
519
520//          DynamicLoaderMacOSX doesn't currently need these fields, so don't send them.
521//            mach_header_dict_sp->AddIntegerItem ("ncmds", image_infos[i].mach_header.ncmds);
522//            mach_header_dict_sp->AddIntegerItem ("sizeofcmds", image_infos[i].mach_header.sizeofcmds);
523//            mach_header_dict_sp->AddIntegerItem ("flags", image_infos[i].mach_header.flags);
524            image_info_dict_sp->AddItem ("mach_header", mach_header_dict_sp);
525
526            JSONGenerator::ArraySP segments_sp (new JSONGenerator::Array());
527            for (size_t j = 0; j < image_infos[i].segments.size(); j++)
528            {
529                JSONGenerator::DictionarySP segment_sp (new JSONGenerator::Dictionary());
530                segment_sp->AddStringItem ("name", image_infos[i].segments[j].name);
531                segment_sp->AddIntegerItem ("vmaddr", image_infos[i].segments[j].vmaddr);
532                segment_sp->AddIntegerItem ("vmsize", image_infos[i].segments[j].vmsize);
533                segment_sp->AddIntegerItem ("fileoff", image_infos[i].segments[j].fileoff);
534                segment_sp->AddIntegerItem ("filesize", image_infos[i].segments[j].filesize);
535                segment_sp->AddIntegerItem ("maxprot", image_infos[i].segments[j].maxprot);
536
537//              DynamicLoaderMacOSX doesn't currently need these fields, so don't send them.
538//                segment_sp->AddIntegerItem ("initprot", image_infos[i].segments[j].initprot);
539//                segment_sp->AddIntegerItem ("nsects", image_infos[i].segments[j].nsects);
540//                segment_sp->AddIntegerItem ("flags", image_infos[i].segments[j].flags);
541                segments_sp->AddItem (segment_sp);
542            }
543            image_info_dict_sp->AddItem ("segments", segments_sp);
544
545            image_infos_array_sp->AddItem (image_info_dict_sp);
546        }
547        reply_sp.reset (new JSONGenerator::Dictionary());
548        reply_sp->AddItem ("images", image_infos_array_sp);
549    }
550    return reply_sp;
551}
552
553nub_thread_t
554MachProcess::GetCurrentThread ()
555{
556    return m_thread_list.CurrentThreadID();
557}
558
559nub_thread_t
560MachProcess::GetCurrentThreadMachPort ()
561{
562    return m_thread_list.GetMachPortNumberByThreadID(m_thread_list.CurrentThreadID());
563}
564
565nub_thread_t
566MachProcess::SetCurrentThread(nub_thread_t tid)
567{
568    return m_thread_list.SetCurrentThread(tid);
569}
570
571bool
572MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info)
573{
574    if (m_thread_list.GetThreadStoppedReason(tid, stop_info))
575    {
576        if (m_did_exec)
577            stop_info->reason = eStopTypeExec;
578        return true;
579    }
580    return false;
581}
582
583void
584MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const
585{
586    return m_thread_list.DumpThreadStoppedReason(tid);
587}
588
589const char *
590MachProcess::GetThreadInfo(nub_thread_t tid) const
591{
592    return m_thread_list.GetThreadInfo(tid);
593}
594
595uint32_t
596MachProcess::GetCPUType ()
597{
598    if (m_cpu_type == 0 && m_pid != 0)
599        m_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid);
600    return m_cpu_type;
601}
602
603const DNBRegisterSetInfo *
604MachProcess::GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const
605{
606    MachThreadSP thread_sp (m_thread_list.GetThreadByID (tid));
607    if (thread_sp)
608    {
609        DNBArchProtocol *arch = thread_sp->GetArchProtocol();
610        if (arch)
611            return arch->GetRegisterSetInfo (num_reg_sets);
612    }
613    *num_reg_sets = 0;
614    return NULL;
615}
616
617bool
618MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const
619{
620    return m_thread_list.GetRegisterValue(tid, set, reg, value);
621}
622
623bool
624MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const
625{
626    return m_thread_list.SetRegisterValue(tid, set, reg, value);
627}
628
629void
630MachProcess::SetState(nub_state_t new_state)
631{
632    // If any other threads access this we will need a mutex for it
633    uint32_t event_mask = 0;
634
635    // Scope for mutex locker
636    {
637        PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
638        const nub_state_t old_state = m_state;
639
640        if (old_state == eStateExited)
641        {
642            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new state since current state is exited", DNBStateAsString(new_state));
643        }
644        else if (old_state == new_state)
645        {
646            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring redundant state change...", DNBStateAsString(new_state));
647        }
648        else
649        {
650            if (NUB_STATE_IS_STOPPED(new_state))
651                event_mask = eEventProcessStoppedStateChanged;
652            else
653                event_mask = eEventProcessRunningStateChanged;
654
655            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous state was %s), event_mask = 0x%8.8x", DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask);
656
657            m_state = new_state;
658            if (new_state == eStateStopped)
659                m_stop_count++;
660        }
661    }
662
663    if (event_mask != 0)
664    {
665        m_events.SetEvents (event_mask);
666        m_private_events.SetEvents (event_mask);
667        if (event_mask == eEventProcessStoppedStateChanged)
668            m_private_events.ResetEvents (eEventProcessRunningStateChanged);
669        else
670            m_private_events.ResetEvents (eEventProcessStoppedStateChanged);
671
672        // Wait for the event bit to reset if a reset ACK is requested
673        m_events.WaitForResetAck(event_mask);
674    }
675
676}
677
678void
679MachProcess::Clear(bool detaching)
680{
681    // Clear any cached thread list while the pid and task are still valid
682
683    m_task.Clear();
684    // Now clear out all member variables
685    m_pid = INVALID_NUB_PROCESS;
686    if (!detaching)
687        CloseChildFileDescriptors();
688
689    m_path.clear();
690    m_args.clear();
691    SetState(eStateUnloaded);
692    m_flags = eMachProcessFlagsNone;
693    m_stop_count = 0;
694    m_thread_list.Clear();
695    {
696        PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
697        m_exception_messages.clear();
698    }
699    m_activities.Clear();
700    if (m_profile_thread)
701    {
702        pthread_join(m_profile_thread, NULL);
703        m_profile_thread = NULL;
704    }
705}
706
707
708bool
709MachProcess::StartSTDIOThread()
710{
711    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
712    // Create the thread that watches for the child STDIO
713    return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0;
714}
715
716void
717MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec, DNBProfileDataScanType scan_type)
718{
719    m_profile_enabled = enable;
720    m_profile_interval_usec = static_cast<useconds_t>(interval_usec);
721    m_profile_scan_type = scan_type;
722
723    if (m_profile_enabled && (m_profile_thread == NULL))
724    {
725        StartProfileThread();
726    }
727    else if (!m_profile_enabled && m_profile_thread)
728    {
729        pthread_join(m_profile_thread, NULL);
730        m_profile_thread = NULL;
731    }
732}
733
734bool
735MachProcess::StartProfileThread()
736{
737    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
738    // Create the thread that profiles the inferior and reports back if enabled
739    return ::pthread_create (&m_profile_thread, NULL, MachProcess::ProfileThread, this) == 0;
740}
741
742
743nub_addr_t
744MachProcess::LookupSymbol(const char *name, const char *shlib)
745{
746    if (m_name_to_addr_callback != NULL && name && name[0])
747        return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton);
748    return INVALID_NUB_ADDRESS;
749}
750
751bool
752MachProcess::Resume (const DNBThreadResumeActions& thread_actions)
753{
754    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
755    nub_state_t state = GetState();
756
757    if (CanResume(state))
758    {
759        m_thread_actions = thread_actions;
760        PrivateResume();
761        return true;
762    }
763    else if (state == eStateRunning)
764    {
765        DNBLog("Resume() - task 0x%x is already running, ignoring...", m_task.TaskPort());
766        return true;
767    }
768    DNBLog("Resume() - task 0x%x has state %s, can't continue...", m_task.TaskPort(), DNBStateAsString(state));
769    return false;
770}
771
772bool
773MachProcess::Kill (const struct timespec *timeout_abstime)
774{
775    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()");
776    nub_state_t state = DoSIGSTOP(true, false, NULL);
777    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state));
778    errno = 0;
779    DNBLog ("Sending ptrace PT_KILL to terminate inferior process.");
780    ::ptrace (PT_KILL, m_pid, 0, 0);
781    DNBError err;
782    err.SetErrorToErrno();
783    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_pid, err.Error(), err.AsString());
784    m_thread_actions = DNBThreadResumeActions (eStateRunning, 0);
785    PrivateResume ();
786
787    // Try and reap the process without touching our m_events since
788    // we want the code above this to still get the eStateExited event
789    const uint32_t reap_timeout_usec = 1000000;    // Wait 1 second and try to reap the process
790    const uint32_t reap_interval_usec = 10000;  //
791    uint32_t reap_time_elapsed;
792    for (reap_time_elapsed = 0;
793         reap_time_elapsed < reap_timeout_usec;
794         reap_time_elapsed += reap_interval_usec)
795    {
796        if (GetState() == eStateExited)
797            break;
798        usleep(reap_interval_usec);
799    }
800    DNBLog ("Waited %u ms for process to be reaped (state = %s)", reap_time_elapsed/1000, DNBStateAsString(GetState()));
801    return true;
802}
803
804bool
805MachProcess::Interrupt()
806{
807    nub_state_t state = GetState();
808    if (IsRunning(state))
809    {
810        if (m_sent_interrupt_signo == 0)
811        {
812            m_sent_interrupt_signo = SIGSTOP;
813            if (Signal (m_sent_interrupt_signo))
814            {
815                DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - sent %i signal to interrupt process", m_sent_interrupt_signo);
816            }
817            else
818            {
819                m_sent_interrupt_signo = 0;
820                DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to send %i signal to interrupt process", m_sent_interrupt_signo);
821            }
822        }
823        else
824        {
825            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously sent an interrupt signal %i that hasn't been received yet, interrupt aborted", m_sent_interrupt_signo);
826        }
827    }
828    else
829    {
830        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already stopped, no interrupt sent");
831    }
832    return false;
833}
834
835bool
836MachProcess::Signal (int signal, const struct timespec *timeout_abstime)
837{
838    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime);
839    nub_state_t state = GetState();
840    if (::kill (ProcessID(), signal) == 0)
841    {
842        // If we were running and we have a timeout, wait for the signal to stop
843        if (IsRunning(state) && timeout_abstime)
844        {
845            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime);
846            m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime);
847            state = GetState();
848            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state));
849            return !IsRunning (state);
850        }
851        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime);
852        return true;
853    }
854    DNBError err(errno, DNBError::POSIX);
855    err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal);
856    return false;
857
858}
859
860bool
861MachProcess::SendEvent (const char *event, DNBError &send_err)
862{
863    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SendEvent (event = %s) to pid: %d", event, m_pid);
864    if (m_pid == INVALID_NUB_PROCESS)
865        return false;
866#if WITH_BKS
867    return BKSSendEvent (event, send_err);
868#endif
869    return true;
870}
871
872nub_state_t
873MachProcess::DoSIGSTOP (bool clear_bps_and_wps, bool allow_running, uint32_t *thread_idx_ptr)
874{
875    nub_state_t state = GetState();
876    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state));
877
878    if (!IsRunning(state))
879    {
880        if (clear_bps_and_wps)
881        {
882            DisableAllBreakpoints (true);
883            DisableAllWatchpoints (true);
884            clear_bps_and_wps = false;
885        }
886
887        // If we already have a thread stopped due to a SIGSTOP, we don't have
888        // to do anything...
889        uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP);
890        if (thread_idx_ptr)
891            *thread_idx_ptr = thread_idx;
892        if (thread_idx != UINT32_MAX)
893            return GetState();
894
895        // No threads were stopped with a SIGSTOP, we need to run and halt the
896        // process with a signal
897        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state));
898        if (allow_running)
899            m_thread_actions = DNBThreadResumeActions (eStateRunning, 0);
900        else
901            m_thread_actions = DNBThreadResumeActions (eStateSuspended, 0);
902
903        PrivateResume ();
904
905        // Reset the event that says we were indeed running
906        m_events.ResetEvents(eEventProcessRunningStateChanged);
907        state = GetState();
908    }
909
910    // We need to be stopped in order to be able to detach, so we need
911    // to send ourselves a SIGSTOP
912
913    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state));
914    struct timespec sigstop_timeout;
915    DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0);
916    Signal (SIGSTOP, &sigstop_timeout);
917    if (clear_bps_and_wps)
918    {
919        DisableAllBreakpoints (true);
920        DisableAllWatchpoints (true);
921        //clear_bps_and_wps = false;
922    }
923    uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP);
924    if (thread_idx_ptr)
925        *thread_idx_ptr = thread_idx;
926    return GetState();
927}
928
929bool
930MachProcess::Detach()
931{
932    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()");
933
934    uint32_t thread_idx = UINT32_MAX;
935    nub_state_t state = DoSIGSTOP(true, true, &thread_idx);
936    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state));
937
938    {
939        m_thread_actions.Clear();
940        m_activities.Clear();
941        DNBThreadResumeAction thread_action;
942        thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx);
943        thread_action.state = eStateRunning;
944        thread_action.signal = -1;
945        thread_action.addr = INVALID_NUB_ADDRESS;
946
947        m_thread_actions.Append (thread_action);
948        m_thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0);
949
950        PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
951
952        ReplyToAllExceptions ();
953
954    }
955
956    m_task.ShutDownExcecptionThread();
957
958    // Detach from our process
959    errno = 0;
960    nub_process_t pid = m_pid;
961    int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
962    DNBError err(errno, DNBError::POSIX);
963    if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0))
964        err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
965
966    // Resume our task
967    m_task.Resume();
968
969    // NULL our task out as we have already retored all exception ports
970    m_task.Clear();
971
972    // Clear out any notion of the process we once were
973    const bool detaching = true;
974    Clear(detaching);
975
976    SetState(eStateDetached);
977
978    return true;
979}
980
981//----------------------------------------------------------------------
982// ReadMemory from the MachProcess level will always remove any software
983// breakpoints from the memory buffer before returning. If you wish to
984// read memory and see those traps, read from the MachTask
985// (m_task.ReadMemory()) as that version will give you what is actually
986// in inferior memory.
987//----------------------------------------------------------------------
988nub_size_t
989MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
990{
991    // We need to remove any current software traps (enabled software
992    // breakpoints) that we may have placed in our tasks memory.
993
994    // First just read the memory as is
995    nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf);
996
997    // Then place any opcodes that fall into this range back into the buffer
998    // before we return this to callers.
999    if (bytes_read > 0)
1000        m_breakpoints.RemoveTrapsFromBuffer (addr, bytes_read, buf);
1001    return bytes_read;
1002}
1003
1004//----------------------------------------------------------------------
1005// WriteMemory from the MachProcess level will always write memory around
1006// any software breakpoints. Any software breakpoints will have their
1007// opcodes modified if they are enabled. Any memory that doesn't overlap
1008// with software breakpoints will be written to. If you wish to write to
1009// inferior memory without this interference, then write to the MachTask
1010// (m_task.WriteMemory()) as that version will always modify inferior
1011// memory.
1012//----------------------------------------------------------------------
1013nub_size_t
1014MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
1015{
1016    // We need to write any data that would go where any current software traps
1017    // (enabled software breakpoints) any software traps (breakpoints) that we
1018    // may have placed in our tasks memory.
1019
1020    std::vector<DNBBreakpoint *> bps;
1021
1022    const size_t num_bps = m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps);
1023    if (num_bps == 0)
1024        return m_task.WriteMemory(addr, size, buf);
1025
1026    nub_size_t bytes_written = 0;
1027    nub_addr_t intersect_addr;
1028    nub_size_t intersect_size;
1029    nub_size_t opcode_offset;
1030    const uint8_t *ubuf = (const uint8_t *)buf;
1031
1032    for (size_t i=0; i<num_bps; ++i)
1033    {
1034        DNBBreakpoint *bp = bps[i];
1035
1036        const bool intersects = bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset);
1037        UNUSED_IF_ASSERT_DISABLED(intersects);
1038        assert(intersects);
1039        assert(addr <= intersect_addr && intersect_addr < addr + size);
1040        assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
1041        assert(opcode_offset + intersect_size <= bp->ByteSize());
1042
1043        // Check for bytes before this breakpoint
1044        const nub_addr_t curr_addr = addr + bytes_written;
1045        if (intersect_addr > curr_addr)
1046        {
1047            // There are some bytes before this breakpoint that we need to
1048            // just write to memory
1049            nub_size_t curr_size = intersect_addr - curr_addr;
1050            nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written);
1051            bytes_written += curr_bytes_written;
1052            if (curr_bytes_written != curr_size)
1053            {
1054                // We weren't able to write all of the requested bytes, we
1055                // are done looping and will return the number of bytes that
1056                // we have written so far.
1057                break;
1058            }
1059        }
1060
1061        // Now write any bytes that would cover up any software breakpoints
1062        // directly into the breakpoint opcode buffer
1063        ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size);
1064        bytes_written += intersect_size;
1065    }
1066
1067    // Write any remaining bytes after the last breakpoint if we have any left
1068    if (bytes_written < size)
1069        bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written);
1070
1071    return bytes_written;
1072}
1073
1074void
1075MachProcess::ReplyToAllExceptions ()
1076{
1077    PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
1078    if (m_exception_messages.empty() == false)
1079    {
1080        MachException::Message::iterator pos;
1081        MachException::Message::iterator begin = m_exception_messages.begin();
1082        MachException::Message::iterator end = m_exception_messages.end();
1083        for (pos = begin; pos != end; ++pos)
1084        {
1085            DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...", (uint32_t)std::distance(begin, pos));
1086            int thread_reply_signal = 0;
1087
1088            nub_thread_t tid = m_thread_list.GetThreadIDByMachPortNumber (pos->state.thread_port);
1089            const DNBThreadResumeAction *action = NULL;
1090            if (tid != INVALID_NUB_THREAD)
1091            {
1092                action = m_thread_actions.GetActionForThread (tid, false);
1093            }
1094
1095            if (action)
1096            {
1097                thread_reply_signal = action->signal;
1098                if (thread_reply_signal)
1099                    m_thread_actions.SetSignalHandledForThread (tid);
1100            }
1101
1102            DNBError err (pos->Reply(this, thread_reply_signal));
1103            if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
1104                err.LogThreadedIfError("Error replying to exception");
1105        }
1106
1107        // Erase all exception message as we should have used and replied
1108        // to them all already.
1109        m_exception_messages.clear();
1110    }
1111}
1112void
1113MachProcess::PrivateResume ()
1114{
1115    PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
1116
1117    m_auto_resume_signo = m_sent_interrupt_signo;
1118    if (m_auto_resume_signo)
1119        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming (with unhandled interrupt signal %i)...", m_task.TaskPort(), m_auto_resume_signo);
1120    else
1121        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming...", m_task.TaskPort());
1122
1123    ReplyToAllExceptions ();
1124//    bool stepOverBreakInstruction = step;
1125
1126    // Let the thread prepare to resume and see if any threads want us to
1127    // step over a breakpoint instruction (ProcessWillResume will modify
1128    // the value of stepOverBreakInstruction).
1129    m_thread_list.ProcessWillResume (this, m_thread_actions);
1130
1131    // Set our state accordingly
1132    if (m_thread_actions.NumActionsWithState(eStateStepping))
1133        SetState (eStateStepping);
1134    else
1135        SetState (eStateRunning);
1136
1137    // Now resume our task.
1138    m_task.Resume();
1139}
1140
1141DNBBreakpoint *
1142MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware)
1143{
1144    DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu, hardware = %i)", (uint64_t)addr, (uint64_t)length, hardware);
1145
1146    DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
1147    if (bp)
1148        bp->Retain();
1149    else
1150        bp =  m_breakpoints.Add(addr, length, hardware);
1151
1152    if (EnableBreakpoint(addr))
1153    {
1154        DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, bp);
1155        return bp;
1156    }
1157    else if (bp->Release() == 0)
1158    {
1159        m_breakpoints.Remove(addr);
1160    }
1161    // We failed to enable the breakpoint
1162    return NULL;
1163}
1164
1165DNBBreakpoint *
1166MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware)
1167{
1168    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu, flags = 0x%8.8x, hardware = %i)", (uint64_t)addr, (uint64_t)length, watch_flags, hardware);
1169
1170    DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
1171    // since the Z packets only send an address, we can only have one watchpoint at
1172    // an address. If there is already one, we must refuse to create another watchpoint
1173    if (wp)
1174        return NULL;
1175
1176    wp = m_watchpoints.Add(addr, length, hardware);
1177    wp->SetIsWatchpoint(watch_flags);
1178
1179    if (EnableWatchpoint(addr))
1180    {
1181        DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, wp);
1182        return wp;
1183    }
1184    else
1185    {
1186        DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => FAILED", (uint64_t)addr, (uint64_t)length);
1187        m_watchpoints.Remove(addr);
1188    }
1189    // We failed to enable the watchpoint
1190    return NULL;
1191}
1192
1193void
1194MachProcess::DisableAllBreakpoints (bool remove)
1195{
1196    DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
1197
1198    m_breakpoints.DisableAllBreakpoints (this);
1199
1200    if (remove)
1201        m_breakpoints.RemoveDisabled();
1202}
1203
1204void
1205MachProcess::DisableAllWatchpoints(bool remove)
1206{
1207    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
1208
1209    m_watchpoints.DisableAllWatchpoints(this);
1210
1211    if (remove)
1212        m_watchpoints.RemoveDisabled();
1213}
1214
1215bool
1216MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove)
1217{
1218    DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
1219    if (bp)
1220    {
1221        // After "exec" we might end up with a bunch of breakpoints that were disabled
1222        // manually, just ignore them
1223        if (!bp->IsEnabled())
1224        {
1225            // Breakpoint might have been disabled by an exec
1226            if (remove && bp->Release() == 0)
1227            {
1228                m_thread_list.NotifyBreakpointChanged(bp);
1229                m_breakpoints.Remove(addr);
1230            }
1231            return true;
1232        }
1233
1234        // We have multiple references to this breakpoint, decrement the ref count
1235        // and if it isn't zero, then return true;
1236        if (remove && bp->Release() > 0)
1237            return true;
1238
1239        DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove);
1240
1241        if (bp->IsHardware())
1242        {
1243            bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp);
1244
1245            if (hw_disable_result == true)
1246            {
1247                bp->SetEnabled(false);
1248                // Let the thread list know that a breakpoint has been modified
1249                if (remove)
1250                {
1251                    m_thread_list.NotifyBreakpointChanged(bp);
1252                    m_breakpoints.Remove(addr);
1253                }
1254                DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove);
1255                return true;
1256            }
1257
1258            return false;
1259        }
1260
1261        const nub_size_t break_op_size = bp->ByteSize();
1262        assert (break_op_size > 0);
1263        const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (bp->ByteSize());
1264        if (break_op_size > 0)
1265        {
1266            // Clear a software breakpoint instruction
1267            uint8_t curr_break_op[break_op_size];
1268            bool break_op_found = false;
1269
1270            // Read the breakpoint opcode
1271            if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size)
1272            {
1273                bool verify = false;
1274                if (bp->IsEnabled())
1275                {
1276                    // Make sure we have the a breakpoint opcode exists at this address
1277                    if (memcmp(curr_break_op, break_op, break_op_size) == 0)
1278                    {
1279                        break_op_found = true;
1280                        // We found a valid breakpoint opcode at this address, now restore
1281                        // the saved opcode.
1282                        if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
1283                        {
1284                            verify = true;
1285                        }
1286                        else
1287                        {
1288                            DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) memory write failed when restoring original opcode", (uint64_t)addr, remove);
1289                        }
1290                    }
1291                    else
1292                    {
1293                        DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) expected a breakpoint opcode but didn't find one.", (uint64_t)addr, remove);
1294                        // Set verify to true and so we can check if the original opcode has already been restored
1295                        verify = true;
1296                    }
1297                }
1298                else
1299                {
1300                    DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) is not enabled", (uint64_t)addr, remove);
1301                    // Set verify to true and so we can check if the original opcode is there
1302                    verify = true;
1303                }
1304
1305                if (verify)
1306                {
1307                    uint8_t verify_opcode[break_op_size];
1308                    // Verify that our original opcode made it back to the inferior
1309                    if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size)
1310                    {
1311                        // compare the memory we just read with the original opcode
1312                        if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0)
1313                        {
1314                            // SUCCESS
1315                            bp->SetEnabled(false);
1316                            // Let the thread list know that a breakpoint has been modified
1317                            if (remove && bp->Release() == 0)
1318                            {
1319                                m_thread_list.NotifyBreakpointChanged(bp);
1320                                m_breakpoints.Remove(addr);
1321                            }
1322                            DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) => success", (uint64_t)addr, remove);
1323                            return true;
1324                        }
1325                        else
1326                        {
1327                            if (break_op_found)
1328                                DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : failed to restore original opcode", (uint64_t)addr, remove);
1329                            else
1330                                DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : opcode changed", (uint64_t)addr, remove);
1331                        }
1332                    }
1333                    else
1334                    {
1335                        DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr);
1336                    }
1337                }
1338            }
1339            else
1340            {
1341                DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr);
1342            }
1343        }
1344    }
1345    else
1346    {
1347        DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) invalid breakpoint address", (uint64_t)addr, remove);
1348    }
1349    return false;
1350}
1351
1352bool
1353MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove)
1354{
1355    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(addr = 0x%8.8llx, remove = %d)", __FUNCTION__, (uint64_t)addr, remove);
1356    DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
1357    if (wp)
1358    {
1359        // If we have multiple references to a watchpoint, removing the watchpoint shouldn't clear it
1360        if (remove && wp->Release() > 0)
1361            return true;
1362
1363        nub_addr_t addr = wp->Address();
1364        DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove);
1365
1366        if (wp->IsHardware())
1367        {
1368            bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp);
1369
1370            if (hw_disable_result == true)
1371            {
1372                wp->SetEnabled(false);
1373                if (remove)
1374                    m_watchpoints.Remove(addr);
1375                DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove);
1376                return true;
1377            }
1378        }
1379
1380        // TODO: clear software watchpoints if we implement them
1381    }
1382    else
1383    {
1384        DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d ) invalid watchpoint ID", (uint64_t)addr, remove);
1385    }
1386    return false;
1387}
1388
1389
1390uint32_t
1391MachProcess::GetNumSupportedHardwareWatchpoints () const
1392{
1393    return m_thread_list.NumSupportedHardwareWatchpoints();
1394}
1395
1396bool
1397MachProcess::EnableBreakpoint(nub_addr_t addr)
1398{
1399    DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )", (uint64_t)addr);
1400    DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
1401    if (bp)
1402    {
1403        if (bp->IsEnabled())
1404        {
1405            DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint already enabled.", (uint64_t)addr);
1406            return true;
1407        }
1408        else
1409        {
1410            if (bp->HardwarePreferred())
1411            {
1412                bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp));
1413                if (bp->IsHardware())
1414                {
1415                    bp->SetEnabled(true);
1416                    return true;
1417                }
1418            }
1419
1420            const nub_size_t break_op_size = bp->ByteSize();
1421            assert (break_op_size != 0);
1422            const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (break_op_size);
1423            if (break_op_size > 0)
1424            {
1425                // Save the original opcode by reading it
1426                if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
1427                {
1428                    // Write a software breakpoint in place of the original opcode
1429                    if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size)
1430                    {
1431                        uint8_t verify_break_op[4];
1432                        if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size)
1433                        {
1434                            if (memcmp(break_op, verify_break_op, break_op_size) == 0)
1435                            {
1436                                bp->SetEnabled(true);
1437                                // Let the thread list know that a breakpoint has been modified
1438                                m_thread_list.NotifyBreakpointChanged(bp);
1439                                DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) : SUCCESS.", (uint64_t)addr);
1440                                return true;
1441                            }
1442                            else
1443                            {
1444                                DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint opcode verification failed.", (uint64_t)addr);
1445                            }
1446                        }
1447                        else
1448                        {
1449                            DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory to verify breakpoint opcode.", (uint64_t)addr);
1450                        }
1451                    }
1452                    else
1453                    {
1454                        DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to write breakpoint opcode to memory.", (uint64_t)addr);
1455                    }
1456                }
1457                else
1458                {
1459                    DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory at breakpoint address.", (uint64_t)addr);
1460                }
1461            }
1462            else
1463            {
1464                DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no software breakpoint opcode for current architecture.", (uint64_t)addr);
1465            }
1466        }
1467    }
1468    return false;
1469}
1470
1471bool
1472MachProcess::EnableWatchpoint(nub_addr_t addr)
1473{
1474    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)", (uint64_t)addr);
1475    DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
1476    if (wp)
1477    {
1478        nub_addr_t addr = wp->Address();
1479        if (wp->IsEnabled())
1480        {
1481            DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): watchpoint already enabled.", (uint64_t)addr);
1482            return true;
1483        }
1484        else
1485        {
1486            // Currently only try and set hardware watchpoints.
1487            wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp));
1488            if (wp->IsHardware())
1489            {
1490                wp->SetEnabled(true);
1491                return true;
1492            }
1493            // TODO: Add software watchpoints by doing page protection tricks.
1494        }
1495    }
1496    return false;
1497}
1498
1499// Called by the exception thread when an exception has been received from
1500// our process. The exception message is completely filled and the exception
1501// data has already been copied.
1502void
1503MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage)
1504{
1505    PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
1506
1507    if (m_exception_messages.empty())
1508        m_task.Suspend();
1509
1510    DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
1511
1512    // Use a locker to automatically unlock our mutex in case of exceptions
1513    // Add the exception to our internal exception stack
1514    m_exception_messages.push_back(exceptionMessage);
1515}
1516
1517task_t
1518MachProcess::ExceptionMessageBundleComplete()
1519{
1520    // We have a complete bundle of exceptions for our child process.
1521    PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
1522    DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
1523    bool auto_resume = false;
1524    if (!m_exception_messages.empty())
1525    {
1526        m_did_exec = false;
1527        // First check for any SIGTRAP and make sure we didn't exec
1528        const task_t task = m_task.TaskPort();
1529        size_t i;
1530        if (m_pid != 0)
1531        {
1532            bool received_interrupt = false;
1533            uint32_t num_task_exceptions = 0;
1534            for (i=0; i<m_exception_messages.size(); ++i)
1535            {
1536                if (m_exception_messages[i].state.task_port == task)
1537                {
1538                    ++num_task_exceptions;
1539                    const int signo = m_exception_messages[i].state.SoftSignal();
1540                    if (signo == SIGTRAP)
1541                    {
1542                        // SIGTRAP could mean that we exec'ed. We need to check the
1543                        // dyld all_image_infos.infoArray to see if it is NULL and if
1544                        // so, say that we exec'ed.
1545                        const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress();
1546                        if (aii_addr != INVALID_NUB_ADDRESS)
1547                        {
1548                            const nub_addr_t info_array_count_addr = aii_addr + 4;
1549                            uint32_t info_array_count = 0;
1550                            if (m_task.ReadMemory(info_array_count_addr, 4, &info_array_count) == 4)
1551                            {
1552                                if (info_array_count == 0)
1553                                {
1554                                    m_did_exec = true;
1555                                    // Force the task port to update itself in case the task port changed after exec
1556                                    DNBError err;
1557                                    const task_t old_task = m_task.TaskPort();
1558                                    const task_t new_task = m_task.TaskPortForProcessID (err, true);
1559                                    if (old_task != new_task)
1560                                        DNBLogThreadedIf(LOG_PROCESS, "exec: task changed from 0x%4.4x to 0x%4.4x", old_task, new_task);
1561                                }
1562                            }
1563                            else
1564                            {
1565                                DNBLog ("error: failed to read all_image_infos.infoArrayCount from 0x%8.8llx", (uint64_t)info_array_count_addr);
1566                            }
1567                        }
1568                        break;
1569                    }
1570                    else if (m_sent_interrupt_signo != 0 && signo == m_sent_interrupt_signo)
1571                    {
1572                        received_interrupt = true;
1573                    }
1574                }
1575            }
1576
1577            if (m_did_exec)
1578            {
1579                cpu_type_t process_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid);
1580                if (m_cpu_type != process_cpu_type)
1581                {
1582                    DNBLog ("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type, process_cpu_type);
1583                    m_cpu_type = process_cpu_type;
1584                    DNBArchProtocol::SetArchitecture (process_cpu_type);
1585                }
1586                m_thread_list.Clear();
1587                m_activities.Clear();
1588                m_breakpoints.DisableAll();
1589            }
1590
1591            if (m_sent_interrupt_signo != 0)
1592            {
1593                if (received_interrupt)
1594                {
1595                    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): process successfully interrupted with signal %i", m_sent_interrupt_signo);
1596
1597                    // Mark that we received the interrupt signal
1598                    m_sent_interrupt_signo = 0;
1599                    // Not check if we had a case where:
1600                    // 1 - We called MachProcess::Interrupt() but we stopped for another reason
1601                    // 2 - We called MachProcess::Resume() (but still haven't gotten the interrupt signal)
1602                    // 3 - We are now incorrectly stopped because we are handling the interrupt signal we missed
1603                    // 4 - We might need to resume if we stopped only with the interrupt signal that we never handled
1604                    if (m_auto_resume_signo != 0)
1605                    {
1606                        // Only auto_resume if we stopped with _only_ the interrupt signal
1607                        if (num_task_exceptions == 1)
1608                        {
1609                            auto_resume = true;
1610                            DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): auto resuming due to unhandled interrupt signal %i", m_auto_resume_signo);
1611                        }
1612                        m_auto_resume_signo = 0;
1613                    }
1614                }
1615                else
1616                {
1617                    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): didn't get signal %i after MachProcess::Interrupt()",
1618                            m_sent_interrupt_signo);
1619                }
1620            }
1621        }
1622
1623        // Let all threads recover from stopping and do any clean up based
1624        // on the previous thread state (if any).
1625        m_thread_list.ProcessDidStop(this);
1626        m_activities.Clear();
1627
1628        // Let each thread know of any exceptions
1629        for (i=0; i<m_exception_messages.size(); ++i)
1630        {
1631            // Let the thread list figure use the MachProcess to forward all exceptions
1632            // on down to each thread.
1633            if (m_exception_messages[i].state.task_port == task)
1634                m_thread_list.NotifyException(m_exception_messages[i].state);
1635            if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
1636                m_exception_messages[i].Dump();
1637        }
1638
1639        if (DNBLogCheckLogBit(LOG_THREAD))
1640            m_thread_list.Dump();
1641
1642        bool step_more = false;
1643        if (m_thread_list.ShouldStop(step_more) && auto_resume == false)
1644        {
1645            // Wait for the eEventProcessRunningStateChanged event to be reset
1646            // before changing state to stopped to avoid race condition with
1647            // very fast start/stops
1648            struct timespec timeout;
1649            //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000);   // Wait for 250 ms
1650            DNBTimer::OffsetTimeOfDay(&timeout, 1, 0);  // Wait for 250 ms
1651            m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout);
1652            SetState(eStateStopped);
1653        }
1654        else
1655        {
1656            // Resume without checking our current state.
1657            PrivateResume ();
1658        }
1659    }
1660    else
1661    {
1662        DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle (%llu exceptions).", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
1663    }
1664    return m_task.TaskPort();
1665}
1666
1667nub_size_t
1668MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed)
1669{
1670    if (m_image_infos_callback != NULL)
1671        return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton);
1672    return 0;
1673}
1674
1675void
1676MachProcess::SharedLibrariesUpdated ( )
1677{
1678    uint32_t event_bits = eEventSharedLibsStateChange;
1679    // Set the shared library event bit to let clients know of shared library
1680    // changes
1681    m_events.SetEvents(event_bits);
1682    // Wait for the event bit to reset if a reset ACK is requested
1683    m_events.WaitForResetAck(event_bits);
1684}
1685
1686void
1687MachProcess::SetExitInfo (const char *info)
1688{
1689    if (info && info[0])
1690    {
1691        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(\"%s\")", __FUNCTION__, info);
1692        m_exit_info.assign(info);
1693    }
1694    else
1695    {
1696        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(NULL)", __FUNCTION__);
1697        m_exit_info.clear();
1698    }
1699}
1700
1701void
1702MachProcess::AppendSTDOUT (char* s, size_t len)
1703{
1704    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%llu> %s) ...", __FUNCTION__, (uint64_t)len, s);
1705    PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
1706    m_stdout_data.append(s, len);
1707    m_events.SetEvents(eEventStdioAvailable);
1708
1709    // Wait for the event bit to reset if a reset ACK is requested
1710    m_events.WaitForResetAck(eEventStdioAvailable);
1711}
1712
1713size_t
1714MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size)
1715{
1716    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size);
1717    PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
1718    size_t bytes_available = m_stdout_data.size();
1719    if (bytes_available > 0)
1720    {
1721        if (bytes_available > buf_size)
1722        {
1723            memcpy(buf, m_stdout_data.data(), buf_size);
1724            m_stdout_data.erase(0, buf_size);
1725            bytes_available = buf_size;
1726        }
1727        else
1728        {
1729            memcpy(buf, m_stdout_data.data(), bytes_available);
1730            m_stdout_data.clear();
1731        }
1732    }
1733    return bytes_available;
1734}
1735
1736nub_addr_t
1737MachProcess::GetDYLDAllImageInfosAddress ()
1738{
1739    DNBError err;
1740    return m_task.GetDYLDAllImageInfosAddress(err);
1741}
1742
1743size_t
1744MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size)
1745{
1746    return 0;
1747}
1748
1749void *
1750MachProcess::STDIOThread(void *arg)
1751{
1752    MachProcess *proc = (MachProcess*) arg;
1753    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg);
1754
1755#if defined (__APPLE__)
1756    pthread_setname_np ("stdio monitoring thread");
1757#endif
1758
1759    // We start use a base and more options so we can control if we
1760    // are currently using a timeout on the mach_msg. We do this to get a
1761    // bunch of related exceptions on our exception port so we can process
1762    // then together. When we have multiple threads, we can get an exception
1763    // per thread and they will come in consecutively. The main thread loop
1764    // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
1765    // flag set in the options, so we will wait forever for an exception on
1766    // our exception port. After we get one exception, we then will use the
1767    // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
1768    // exceptions for our process. After we have received the last pending
1769    // exception, we will get a timeout which enables us to then notify
1770    // our main thread that we have an exception bundle available. We then wait
1771    // for the main thread to tell this exception thread to start trying to get
1772    // exceptions messages again and we start again with a mach_msg read with
1773    // infinite timeout.
1774    DNBError err;
1775    int stdout_fd = proc->GetStdoutFileDescriptor();
1776    int stderr_fd = proc->GetStderrFileDescriptor();
1777    if (stdout_fd == stderr_fd)
1778        stderr_fd = -1;
1779
1780    while (stdout_fd >= 0 || stderr_fd >= 0)
1781    {
1782        ::pthread_testcancel ();
1783
1784        fd_set read_fds;
1785        FD_ZERO (&read_fds);
1786        if (stdout_fd >= 0)
1787            FD_SET (stdout_fd, &read_fds);
1788        if (stderr_fd >= 0)
1789            FD_SET (stderr_fd, &read_fds);
1790        int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
1791
1792        int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL);
1793        DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
1794
1795        if (num_set_fds < 0)
1796        {
1797            int select_errno = errno;
1798            if (DNBLogCheckLogBit(LOG_PROCESS))
1799            {
1800                err.SetError (select_errno, DNBError::POSIX);
1801                err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
1802            }
1803
1804            switch (select_errno)
1805            {
1806            case EAGAIN:    // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO
1807                break;
1808            case EBADF:     // One of the descriptor sets specified an invalid descriptor.
1809                return NULL;
1810                break;
1811            case EINTR:     // A signal was delivered before the time limit expired and before any of the selected events occurred.
1812            case EINVAL:    // The specified time limit is invalid. One of its components is negative or too large.
1813            default:        // Other unknown error
1814                break;
1815            }
1816        }
1817        else if (num_set_fds == 0)
1818        {
1819        }
1820        else
1821        {
1822            char s[1024];
1823            s[sizeof(s)-1] = '\0';  // Ensure we have NULL termination
1824            ssize_t bytes_read = 0;
1825            if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds))
1826            {
1827                do
1828                {
1829                    bytes_read = ::read (stdout_fd, s, sizeof(s)-1);
1830                    if (bytes_read < 0)
1831                    {
1832                        int read_errno = errno;
1833                        DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %zd   errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
1834                    }
1835                    else if (bytes_read == 0)
1836                    {
1837                        // EOF...
1838                        DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %zd  (reached EOF for child STDOUT)", bytes_read);
1839                        stdout_fd = -1;
1840                    }
1841                    else if (bytes_read > 0)
1842                    {
1843                        proc->AppendSTDOUT(s, bytes_read);
1844                    }
1845
1846                } while (bytes_read > 0);
1847            }
1848
1849            if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds))
1850            {
1851                do
1852                {
1853                    bytes_read = ::read (stderr_fd, s, sizeof(s)-1);
1854                    if (bytes_read < 0)
1855                    {
1856                        int read_errno = errno;
1857                        DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %zd   errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
1858                    }
1859                    else if (bytes_read == 0)
1860                    {
1861                        // EOF...
1862                        DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %zd  (reached EOF for child STDERR)", bytes_read);
1863                        stderr_fd = -1;
1864                    }
1865                    else if (bytes_read > 0)
1866                    {
1867                        proc->AppendSTDOUT(s, bytes_read);
1868                    }
1869
1870                } while (bytes_read > 0);
1871            }
1872        }
1873    }
1874    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg);
1875    return NULL;
1876}
1877
1878
1879void
1880MachProcess::SignalAsyncProfileData (const char *info)
1881{
1882    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info);
1883    PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex);
1884    m_profile_data.push_back(info);
1885    m_events.SetEvents(eEventProfileDataAvailable);
1886
1887    // Wait for the event bit to reset if a reset ACK is requested
1888    m_events.WaitForResetAck(eEventProfileDataAvailable);
1889}
1890
1891
1892size_t
1893MachProcess::GetAsyncProfileData (char *buf, size_t buf_size)
1894{
1895    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size);
1896    PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex);
1897    if (m_profile_data.empty())
1898        return 0;
1899
1900    size_t bytes_available = m_profile_data.front().size();
1901    if (bytes_available > 0)
1902    {
1903        if (bytes_available > buf_size)
1904        {
1905            memcpy(buf, m_profile_data.front().data(), buf_size);
1906            m_profile_data.front().erase(0, buf_size);
1907            bytes_available = buf_size;
1908        }
1909        else
1910        {
1911            memcpy(buf, m_profile_data.front().data(), bytes_available);
1912            m_profile_data.erase(m_profile_data.begin());
1913        }
1914    }
1915    return bytes_available;
1916}
1917
1918
1919void *
1920MachProcess::ProfileThread(void *arg)
1921{
1922    MachProcess *proc = (MachProcess*) arg;
1923    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg);
1924
1925#if defined (__APPLE__)
1926    pthread_setname_np ("performance profiling thread");
1927#endif
1928
1929    while (proc->IsProfilingEnabled())
1930    {
1931        nub_state_t state = proc->GetState();
1932        if (state == eStateRunning)
1933        {
1934            std::string data = proc->Task().GetProfileData(proc->GetProfileScanType());
1935            if (!data.empty())
1936            {
1937                proc->SignalAsyncProfileData(data.c_str());
1938            }
1939        }
1940        else if ((state == eStateUnloaded) || (state == eStateDetached) || (state == eStateUnloaded))
1941        {
1942            // Done. Get out of this thread.
1943            break;
1944        }
1945
1946        // A simple way to set up the profile interval. We can also use select() or dispatch timer source if necessary.
1947        usleep(proc->ProfileInterval());
1948    }
1949    return NULL;
1950}
1951
1952
1953pid_t
1954MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len)
1955{
1956    // Clear out and clean up from any current state
1957    Clear();
1958    if (pid != 0)
1959    {
1960        DNBError err;
1961        // Make sure the process exists...
1962        if (::getpgid (pid) < 0)
1963        {
1964            err.SetErrorToErrno();
1965            const char *err_cstr = err.AsString();
1966            ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process");
1967            return INVALID_NUB_PROCESS;
1968        }
1969
1970        SetState(eStateAttaching);
1971        m_pid = pid;
1972        // Let ourselves know we are going to be using SBS or BKS if the correct flag bit is set...
1973#if defined (WITH_BKS)
1974        if (IsBKSProcess (pid))
1975            m_flags |= eMachProcessFlagsUsingBKS;
1976#elif defined (WITH_SPRINGBOARD)
1977        if (IsSBProcess(pid))
1978            m_flags |= eMachProcessFlagsUsingSBS;
1979#endif
1980        if (!m_task.StartExceptionThread(err))
1981        {
1982            const char *err_cstr = err.AsString();
1983            ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread");
1984            DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
1985            m_pid = INVALID_NUB_PROCESS;
1986            return INVALID_NUB_PROCESS;
1987        }
1988
1989        errno = 0;
1990        if (::ptrace (PT_ATTACHEXC, pid, 0, 0))
1991            err.SetError(errno);
1992        else
1993            err.Clear();
1994
1995        if (err.Success())
1996        {
1997            m_flags |= eMachProcessFlagsAttached;
1998            // Sleep a bit to let the exception get received and set our process status
1999            // to stopped.
2000            ::usleep(250000);
2001            DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
2002            return m_pid;
2003        }
2004        else
2005        {
2006            ::snprintf (err_str, err_len, "%s", err.AsString());
2007            DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
2008        }
2009    }
2010    return INVALID_NUB_PROCESS;
2011}
2012
2013Genealogy::ThreadActivitySP
2014MachProcess::GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out)
2015{
2016    return m_activities.GetGenealogyInfoForThread (m_pid, tid, m_thread_list, m_task.TaskPort(), timed_out);
2017}
2018
2019Genealogy::ProcessExecutableInfoSP
2020MachProcess::GetGenealogyImageInfo (size_t idx)
2021{
2022    return m_activities.GetProcessExecutableInfosAtIndex (idx);
2023}
2024
2025bool
2026MachProcess::GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch)
2027{
2028    bool success = false;
2029
2030#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000)
2031    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2032
2033    NSOperatingSystemVersion vers = [[NSProcessInfo processInfo] operatingSystemVersion];
2034    if (major)
2035        *major = vers.majorVersion;
2036    if (minor)
2037        *minor = vers.minorVersion;
2038    if (patch)
2039        *patch = vers.patchVersion;
2040
2041    success = true;
2042
2043    [pool drain];
2044#endif
2045
2046    return success;
2047}
2048
2049// Do the process specific setup for attach.  If this returns NULL, then there's no
2050// platform specific stuff to be done to wait for the attach.  If you get non-null,
2051// pass that token to the CheckForProcess method, and then to CleanupAfterAttach.
2052
2053//  Call PrepareForAttach before attaching to a process that has not yet launched
2054// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach.
2055// You should call CleanupAfterAttach to free the token, and do whatever other
2056// cleanup seems good.
2057
2058const void *
2059MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &attach_err)
2060{
2061#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
2062    // Tell SpringBoard to halt the next launch of this application on startup.
2063
2064    if (!waitfor)
2065        return NULL;
2066
2067    const char *app_ext = strstr(path, ".app");
2068    const bool is_app = app_ext != NULL && (app_ext[4] == '\0' || app_ext[4] == '/');
2069    if (!is_app)
2070    {
2071        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, "
2072                                      "we can't tell springboard to wait for launch...",
2073                                      path);
2074        return NULL;
2075    }
2076
2077#if defined (WITH_BKS)
2078    if (launch_flavor == eLaunchFlavorDefault)
2079        launch_flavor = eLaunchFlavorBKS;
2080    if (launch_flavor != eLaunchFlavorBKS)
2081        return NULL;
2082#elif defined (WITH_SPRINGBOARD)
2083    if (launch_flavor == eLaunchFlavorDefault)
2084        launch_flavor = eLaunchFlavorSpringBoard;
2085    if (launch_flavor != eLaunchFlavorSpringBoard)
2086        return NULL;
2087#endif
2088
2089    std::string app_bundle_path(path, app_ext + strlen(".app"));
2090
2091    CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), attach_err);
2092    std::string bundleIDStr;
2093    CFString::UTF8(bundleIDCFStr, bundleIDStr);
2094    DNBLogThreadedIf(LOG_PROCESS,
2095                     "CopyBundleIDForPath (%s, err_str) returned @\"%s\"",
2096                     app_bundle_path.c_str (),
2097                     bundleIDStr.c_str());
2098
2099    if (bundleIDCFStr == NULL)
2100    {
2101        return NULL;
2102    }
2103
2104#if defined (WITH_BKS)
2105    if (launch_flavor == eLaunchFlavorBKS)
2106    {
2107        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2108
2109        NSString *stdio_path = nil;
2110        NSFileManager *file_manager = [NSFileManager defaultManager];
2111        const char *null_path = "/dev/null";
2112        stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)];
2113
2114        NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
2115        NSMutableDictionary *options       = [NSMutableDictionary dictionary];
2116
2117        DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", "
2118                                      "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )",
2119                                      bundleIDStr.c_str(),
2120                                      null_path);
2121
2122        [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath];
2123        [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath];
2124        [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger];
2125        [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDebugOnNextLaunch];
2126
2127        [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
2128
2129        BKSSystemService *system_service = [[BKSSystemService alloc] init];
2130
2131        mach_port_t client_port = [system_service createClientPort];
2132        __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
2133        __block BKSOpenApplicationErrorCode attach_error_code = BKSOpenApplicationErrorCodeNone;
2134
2135        NSString *bundleIDNSStr = (NSString *) bundleIDCFStr;
2136
2137        [system_service openApplication: bundleIDNSStr
2138                       options: options
2139                       clientPort: client_port
2140                       withResult: ^(NSError *error)
2141                       {
2142                            // The system service will cleanup the client port we created for us.
2143                            if (error)
2144                                attach_error_code = (BKSOpenApplicationErrorCode)[error code];
2145
2146                            [system_service release];
2147                            dispatch_semaphore_signal(semaphore);
2148                        }
2149        ];
2150
2151        const uint32_t timeout_secs = 9;
2152
2153        dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
2154
2155        long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
2156
2157        if (!success)
2158        {
2159            DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
2160            attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete.");
2161            attach_err.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
2162        }
2163        else if (attach_error_code != BKSOpenApplicationErrorCodeNone)
2164        {
2165            SetBKSError (attach_error_code, attach_err);
2166            DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u",
2167                        bundleIDStr.c_str(),
2168                        attach_error_code);
2169        }
2170        dispatch_release(semaphore);
2171        [pool drain];
2172    }
2173#elif defined (WITH_SPRINGBOARD)
2174    if (launch_flavor == eLaunchFlavorSpringBoard)
2175    {
2176        SBSApplicationLaunchError sbs_error = 0;
2177
2178        const char *stdout_err = "/dev/null";
2179        CFString stdio_path;
2180        stdio_path.SetFileSystemRepresentation (stdout_err);
2181
2182        DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", "
2183                                      "SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )",
2184                                      bundleIDStr.c_str(),
2185                                      stdout_err,
2186                                      stdout_err);
2187
2188        sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
2189                                                      (CFURLRef)NULL,         // openURL
2190                                                      NULL, // launch_argv.get(),
2191                                                      NULL, // launch_envp.get(),  // CFDictionaryRef environment
2192                                                      stdio_path.get(),
2193                                                      stdio_path.get(),
2194                                                      SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger);
2195
2196        if (sbs_error != SBSApplicationLaunchErrorSuccess)
2197        {
2198            attach_err.SetError(sbs_error, DNBError::SpringBoard);
2199            return NULL;
2200        }
2201    }
2202#endif // WITH_SPRINGBOARD
2203
2204    DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
2205    return bundleIDCFStr;
2206# else  // defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
2207  return NULL;
2208#endif
2209}
2210
2211// Pass in the token you got from PrepareForAttach.  If there is a process
2212// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS
2213// will be returned.
2214
2215nub_process_t
2216MachProcess::CheckForProcess (const void *attach_token)
2217{
2218    if (attach_token == NULL)
2219        return INVALID_NUB_PROCESS;
2220
2221#if defined (WITH_BKS)
2222    NSString *bundleIDNSStr = (NSString *) attach_token;
2223    BKSSystemService *systemService = [[BKSSystemService alloc] init];
2224    pid_t pid = [systemService pidForApplication: bundleIDNSStr];
2225    [systemService release];
2226    if (pid == 0)
2227        return INVALID_NUB_PROCESS;
2228    else
2229        return pid;
2230#elif defined (WITH_SPRINGBOARD)
2231    CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
2232    Boolean got_it;
2233    nub_process_t attach_pid;
2234    got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid);
2235    if (got_it)
2236        return attach_pid;
2237    else
2238        return INVALID_NUB_PROCESS;
2239#else
2240    return INVALID_NUB_PROCESS;
2241#endif
2242}
2243
2244// Call this to clean up after you have either attached or given up on the attach.
2245// Pass true for success if you have attached, false if you have not.
2246// The token will also be freed at this point, so you can't use it after calling
2247// this method.
2248
2249void
2250MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str)
2251{
2252    if (attach_token == NULL)
2253        return;
2254
2255#if defined (WITH_BKS)
2256
2257    if (!success)
2258    {
2259        BKSCleanupAfterAttach (attach_token, err_str);
2260    }
2261    CFRelease((CFStringRef) attach_token);
2262
2263#elif defined (WITH_SPRINGBOARD)
2264    // Tell SpringBoard to cancel the debug on next launch of this application
2265    // if we failed to attach
2266    if (!success)
2267    {
2268        SBSApplicationLaunchError sbs_error = 0;
2269        CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
2270
2271        sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
2272                                                      (CFURLRef)NULL,
2273                                                      NULL,
2274                                                      NULL,
2275                                                      NULL,
2276                                                      NULL,
2277                                                      SBSApplicationCancelDebugOnNextLaunch);
2278
2279        if (sbs_error != SBSApplicationLaunchErrorSuccess)
2280        {
2281            err_str.SetError(sbs_error, DNBError::SpringBoard);
2282            return;
2283        }
2284    }
2285
2286    CFRelease((CFStringRef) attach_token);
2287#endif
2288}
2289
2290pid_t
2291MachProcess::LaunchForDebug
2292(
2293    const char *path,
2294    char const *argv[],
2295    char const *envp[],
2296    const char *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this
2297    const char *stdin_path,
2298    const char *stdout_path,
2299    const char *stderr_path,
2300    bool no_stdio,
2301    nub_launch_flavor_t launch_flavor,
2302    int disable_aslr,
2303    const char *event_data,
2304    DNBError &launch_err
2305)
2306{
2307    // Clear out and clean up from any current state
2308    Clear();
2309
2310    DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr);
2311
2312    // Fork a child process for debugging
2313    SetState(eStateLaunching);
2314
2315    switch (launch_flavor)
2316    {
2317    case eLaunchFlavorForkExec:
2318        m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err);
2319        break;
2320#ifdef WITH_BKS
2321    case eLaunchFlavorBKS:
2322        {
2323            const char *app_ext = strstr(path, ".app");
2324            if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/'))
2325            {
2326                std::string app_bundle_path(path, app_ext + strlen(".app"));
2327                if (BKSLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
2328                    return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
2329                else
2330                    break; // We tried a BKS launch, but didn't succeed lets get out
2331            }
2332        }
2333        break;
2334#endif
2335#ifdef WITH_SPRINGBOARD
2336
2337    case eLaunchFlavorSpringBoard:
2338        {
2339            //  .../whatever.app/whatever ?
2340            //  Or .../com.apple.whatever.app/whatever -- be careful of ".app" in "com.apple.whatever" here
2341            const char *app_ext = strstr (path, ".app/");
2342            if (app_ext == NULL)
2343            {
2344                // .../whatever.app ?
2345                int len = strlen (path);
2346                if (len > 5)
2347                {
2348                    if (strcmp (path + len - 4, ".app") == 0)
2349                    {
2350                        app_ext = path + len - 4;
2351                    }
2352                }
2353            }
2354            if (app_ext)
2355            {
2356                std::string app_bundle_path(path, app_ext + strlen(".app"));
2357                if (SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, launch_err) != 0)
2358                    return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
2359                else
2360                    break; // We tried a springboard launch, but didn't succeed lets get out
2361            }
2362        }
2363        break;
2364
2365#endif
2366
2367    case eLaunchFlavorPosixSpawn:
2368        m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path,
2369                                                                DNBArchProtocol::GetArchitecture (),
2370                                                                argv,
2371                                                                envp,
2372                                                                working_directory,
2373                                                                stdin_path,
2374                                                                stdout_path,
2375                                                                stderr_path,
2376                                                                no_stdio,
2377                                                                this,
2378                                                                disable_aslr,
2379                                                                launch_err);
2380        break;
2381
2382    default:
2383        // Invalid  launch
2384        launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
2385        return INVALID_NUB_PROCESS;
2386    }
2387
2388    if (m_pid == INVALID_NUB_PROCESS)
2389    {
2390        // If we don't have a valid process ID and no one has set the error,
2391        // then return a generic error
2392        if (launch_err.Success())
2393            launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
2394    }
2395    else
2396    {
2397        m_path = path;
2398        size_t i;
2399        char const *arg;
2400        for (i=0; (arg = argv[i]) != NULL; i++)
2401            m_args.push_back(arg);
2402
2403        m_task.StartExceptionThread(launch_err);
2404        if (launch_err.Fail())
2405        {
2406            if (launch_err.AsString() == NULL)
2407                launch_err.SetErrorString("unable to start the exception thread");
2408            DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting.");
2409            ::ptrace (PT_KILL, m_pid, 0, 0);
2410            m_pid = INVALID_NUB_PROCESS;
2411            return INVALID_NUB_PROCESS;
2412        }
2413
2414        StartSTDIOThread();
2415
2416        if (launch_flavor == eLaunchFlavorPosixSpawn)
2417        {
2418
2419            SetState (eStateAttaching);
2420            errno = 0;
2421            int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
2422            if (err == 0)
2423            {
2424                m_flags |= eMachProcessFlagsAttached;
2425                DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid);
2426                launch_err.Clear();
2427            }
2428            else
2429            {
2430                SetState (eStateExited);
2431                DNBError ptrace_err(errno, DNBError::POSIX);
2432                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());
2433                launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
2434            }
2435        }
2436        else
2437        {
2438            launch_err.Clear();
2439        }
2440    }
2441    return m_pid;
2442}
2443
2444pid_t
2445MachProcess::PosixSpawnChildForPTraceDebugging
2446(
2447    const char *path,
2448    cpu_type_t cpu_type,
2449    char const *argv[],
2450    char const *envp[],
2451    const char *working_directory,
2452    const char *stdin_path,
2453    const char *stdout_path,
2454    const char *stderr_path,
2455    bool no_stdio,
2456    MachProcess* process,
2457    int disable_aslr,
2458    DNBError& err
2459)
2460{
2461    posix_spawnattr_t attr;
2462    short flags;
2463    DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, working_dir=%s, stdin=%s, stdout=%s stderr=%s, no-stdio=%i)",
2464                     __FUNCTION__,
2465                     path,
2466                     argv,
2467                     envp,
2468                     working_directory,
2469                     stdin_path,
2470                     stdout_path,
2471                     stderr_path,
2472                     no_stdio);
2473
2474    err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX);
2475    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
2476        err.LogThreaded("::posix_spawnattr_init ( &attr )");
2477    if (err.Fail())
2478        return INVALID_NUB_PROCESS;
2479
2480    flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
2481    if (disable_aslr)
2482        flags |= _POSIX_SPAWN_DISABLE_ASLR;
2483
2484    sigset_t no_signals;
2485    sigset_t all_signals;
2486    sigemptyset (&no_signals);
2487    sigfillset (&all_signals);
2488    ::posix_spawnattr_setsigmask(&attr, &no_signals);
2489    ::posix_spawnattr_setsigdefault(&attr, &all_signals);
2490
2491    err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX);
2492    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
2493        err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : "");
2494    if (err.Fail())
2495        return INVALID_NUB_PROCESS;
2496
2497    // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail
2498    // and we will fail to continue with our process...
2499
2500    // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment....
2501
2502#if !defined(__arm__)
2503
2504    // We don't need to do this for ARM, and we really shouldn't now that we
2505    // have multiple CPU subtypes and no posix_spawnattr call that allows us
2506    // to set which CPU subtype to launch...
2507    if (cpu_type != 0)
2508    {
2509        size_t ocount = 0;
2510        err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX);
2511        if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
2512            err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu_type, (uint64_t)ocount);
2513
2514        if (err.Fail() != 0 || ocount != 1)
2515            return INVALID_NUB_PROCESS;
2516    }
2517#endif
2518
2519    PseudoTerminal pty;
2520
2521    posix_spawn_file_actions_t file_actions;
2522    err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX);
2523    int file_actions_valid = err.Success();
2524    if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS))
2525        err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )");
2526    int pty_error = -1;
2527    pid_t pid = INVALID_NUB_PROCESS;
2528    if (file_actions_valid)
2529    {
2530        if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && !no_stdio)
2531        {
2532            pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
2533            if (pty_error == PseudoTerminal::success)
2534            {
2535                stdin_path = stdout_path = stderr_path = pty.SlaveName();
2536            }
2537        }
2538
2539        // if no_stdio or std paths not supplied, then route to "/dev/null".
2540        if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0')
2541            stdin_path = "/dev/null";
2542        if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0')
2543            stdout_path = "/dev/null";
2544        if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0')
2545            stderr_path = "/dev/null";
2546
2547        err.SetError( ::posix_spawn_file_actions_addopen (&file_actions,
2548                                                          STDIN_FILENO,
2549                                                          stdin_path,
2550                                                          O_RDONLY | O_NOCTTY,
2551                                                          0),
2552                     DNBError::POSIX);
2553        if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS))
2554            err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDIN_FILENO, path='%s')", stdin_path);
2555
2556        err.SetError( ::posix_spawn_file_actions_addopen (&file_actions,
2557                                                          STDOUT_FILENO,
2558                                                          stdout_path,
2559                                                          O_WRONLY | O_NOCTTY | O_CREAT,
2560                                                          0640),
2561                     DNBError::POSIX);
2562        if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS))
2563            err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDOUT_FILENO, path='%s')", stdout_path);
2564
2565        err.SetError( ::posix_spawn_file_actions_addopen (&file_actions,
2566                                                          STDERR_FILENO,
2567                                                          stderr_path,
2568                                                          O_WRONLY | O_NOCTTY | O_CREAT,
2569                                                          0640),
2570                     DNBError::POSIX);
2571        if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS))
2572            err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDERR_FILENO, path='%s')", stderr_path);
2573
2574        // TODO: Verify if we can set the working directory back immediately
2575        // after the posix_spawnp call without creating a race condition???
2576        if (working_directory)
2577            ::chdir (working_directory);
2578
2579        err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
2580        if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
2581            err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp);
2582    }
2583    else
2584    {
2585        // TODO: Verify if we can set the working directory back immediately
2586        // after the posix_spawnp call without creating a race condition???
2587        if (working_directory)
2588            ::chdir (working_directory);
2589
2590        err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
2591        if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
2592            err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp);
2593    }
2594
2595    // We have seen some cases where posix_spawnp was returning a valid
2596    // looking pid even when an error was returned, so clear it out
2597    if (err.Fail())
2598        pid = INVALID_NUB_PROCESS;
2599
2600    if (pty_error == 0)
2601    {
2602        if (process != NULL)
2603        {
2604            int master_fd = pty.ReleaseMasterFD();
2605            process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
2606        }
2607    }
2608    ::posix_spawnattr_destroy (&attr);
2609
2610    if (pid != INVALID_NUB_PROCESS)
2611    {
2612        cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess (pid);
2613        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x", __FUNCTION__, pid, pid_cpu_type);
2614        if (pid_cpu_type)
2615            DNBArchProtocol::SetArchitecture (pid_cpu_type);
2616    }
2617
2618    if (file_actions_valid)
2619    {
2620        DNBError err2;
2621        err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX);
2622        if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
2623            err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )");
2624    }
2625
2626    return pid;
2627}
2628
2629uint32_t
2630MachProcess::GetCPUTypeForLocalProcess (pid_t pid)
2631{
2632    int mib[CTL_MAXNAME]={0,};
2633    size_t len = CTL_MAXNAME;
2634    if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
2635        return 0;
2636
2637    mib[len] = pid;
2638    len++;
2639
2640    cpu_type_t cpu;
2641    size_t cpu_len = sizeof(cpu);
2642    if (::sysctl (mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
2643        cpu = 0;
2644    return cpu;
2645}
2646
2647pid_t
2648MachProcess::ForkChildForPTraceDebugging
2649(
2650    const char *path,
2651    char const *argv[],
2652    char const *envp[],
2653    MachProcess* process,
2654    DNBError& launch_err
2655)
2656{
2657    PseudoTerminal::Error pty_error = PseudoTerminal::success;
2658
2659    // Use a fork that ties the child process's stdin/out/err to a pseudo
2660    // terminal so we can read it in our MachProcess::STDIOThread
2661    // as unbuffered io.
2662    PseudoTerminal pty;
2663    pid_t pid = pty.Fork(pty_error);
2664
2665    if (pid < 0)
2666    {
2667        //--------------------------------------------------------------
2668        // Error during fork.
2669        //--------------------------------------------------------------
2670        return pid;
2671    }
2672    else if (pid == 0)
2673    {
2674        //--------------------------------------------------------------
2675        // Child process
2676        //--------------------------------------------------------------
2677        ::ptrace (PT_TRACE_ME, 0, 0, 0);    // Debug this process
2678        ::ptrace (PT_SIGEXC, 0, 0, 0);    // Get BSD signals as mach exceptions
2679
2680        // If our parent is setgid, lets make sure we don't inherit those
2681        // extra powers due to nepotism.
2682        if (::setgid (getgid ()) == 0)
2683        {
2684
2685            // Let the child have its own process group. We need to execute
2686            // this call in both the child and parent to avoid a race condition
2687            // between the two processes.
2688            ::setpgid (0, 0);    // Set the child process group to match its pid
2689
2690            // Sleep a bit to before the exec call
2691            ::sleep (1);
2692
2693            // Turn this process into
2694            ::execv (path, (char * const *)argv);
2695        }
2696        // Exit with error code. Child process should have taken
2697        // over in above exec call and if the exec fails it will
2698        // exit the child process below.
2699        ::exit (127);
2700    }
2701    else
2702    {
2703        //--------------------------------------------------------------
2704        // Parent process
2705        //--------------------------------------------------------------
2706        // Let the child have its own process group. We need to execute
2707        // this call in both the child and parent to avoid a race condition
2708        // between the two processes.
2709        ::setpgid (pid, pid);    // Set the child process group to match its pid
2710
2711        if (process != NULL)
2712        {
2713            // Release our master pty file descriptor so the pty class doesn't
2714            // close it and so we can continue to use it in our STDIO thread
2715            int master_fd = pty.ReleaseMasterFD();
2716            process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
2717        }
2718    }
2719    return pid;
2720}
2721
2722#ifdef WITH_SPRINGBOARD
2723
2724pid_t
2725MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, DNBError &launch_err)
2726{
2727    // Clear out and clean up from any current state
2728    Clear();
2729
2730    DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
2731
2732    // Fork a child process for debugging
2733    SetState(eStateLaunching);
2734    m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio, this, launch_err);
2735    if (m_pid != 0)
2736    {
2737        m_flags |= eMachProcessFlagsUsingSBS;
2738        m_path = path;
2739        size_t i;
2740        char const *arg;
2741        for (i=0; (arg = argv[i]) != NULL; i++)
2742            m_args.push_back(arg);
2743        m_task.StartExceptionThread(launch_err);
2744
2745        if (launch_err.Fail())
2746        {
2747            if (launch_err.AsString() == NULL)
2748                launch_err.SetErrorString("unable to start the exception thread");
2749            DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting.");
2750            ::ptrace (PT_KILL, m_pid, 0, 0);
2751            m_pid = INVALID_NUB_PROCESS;
2752            return INVALID_NUB_PROCESS;
2753        }
2754
2755        StartSTDIOThread();
2756        SetState (eStateAttaching);
2757        int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
2758        if (err == 0)
2759        {
2760            m_flags |= eMachProcessFlagsAttached;
2761            DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
2762        }
2763        else
2764        {
2765            SetState (eStateExited);
2766            DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
2767        }
2768    }
2769    return m_pid;
2770}
2771
2772#include <servers/bootstrap.h>
2773
2774pid_t
2775MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err)
2776{
2777    DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process);
2778    CFAllocatorRef alloc = kCFAllocatorDefault;
2779
2780    if (argv[0] == NULL)
2781        return INVALID_NUB_PROCESS;
2782
2783    size_t argc = 0;
2784    // Count the number of arguments
2785    while (argv[argc] != NULL)
2786        argc++;
2787
2788    // Enumerate the arguments
2789    size_t first_launch_arg_idx = 1;
2790    CFReleaser<CFMutableArrayRef> launch_argv;
2791
2792    if (argv[first_launch_arg_idx])
2793    {
2794        size_t launch_argc = argc > 0 ? argc - 1 : 0;
2795        launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks));
2796        size_t i;
2797        char const *arg;
2798        CFString launch_arg;
2799        for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
2800        {
2801            launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8));
2802            if (launch_arg.get() != NULL)
2803                CFArrayAppendValue(launch_argv.get(), launch_arg.get());
2804            else
2805                break;
2806        }
2807    }
2808
2809    // Next fill in the arguments dictionary.  Note, the envp array is of the form
2810    // Variable=value but SpringBoard wants a CF dictionary.  So we have to convert
2811    // this here.
2812
2813    CFReleaser<CFMutableDictionaryRef> launch_envp;
2814
2815    if (envp[0])
2816    {
2817        launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
2818        const char *value;
2819        int name_len;
2820        CFString name_string, value_string;
2821
2822        for (int i = 0; envp[i] != NULL; i++)
2823        {
2824            value = strstr (envp[i], "=");
2825
2826            // If the name field is empty or there's no =, skip it.  Somebody's messing with us.
2827            if (value == NULL || value == envp[i])
2828                continue;
2829
2830            name_len = value - envp[i];
2831
2832            // Now move value over the "="
2833            value++;
2834
2835            name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false));
2836            value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
2837            CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get());
2838        }
2839    }
2840
2841    CFString stdio_path;
2842
2843    PseudoTerminal pty;
2844    if (!no_stdio)
2845    {
2846        PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
2847        if (pty_err == PseudoTerminal::success)
2848        {
2849            const char* slave_name = pty.SlaveName();
2850            DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
2851            if (slave_name && slave_name[0])
2852            {
2853                ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
2854                stdio_path.SetFileSystemRepresentation (slave_name);
2855            }
2856        }
2857    }
2858
2859    if (stdio_path.get() == NULL)
2860    {
2861        stdio_path.SetFileSystemRepresentation ("/dev/null");
2862    }
2863
2864    CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err);
2865    if (bundleIDCFStr == NULL)
2866        return INVALID_NUB_PROCESS;
2867
2868    // This is just for logging:
2869    std::string bundleID;
2870    CFString::UTF8(bundleIDCFStr, bundleID);
2871
2872    DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__);
2873
2874    // Find SpringBoard
2875    SBSApplicationLaunchError sbs_error = 0;
2876    sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
2877                                                  (CFURLRef)NULL,         // openURL
2878                                                  launch_argv.get(),
2879                                                  launch_envp.get(),  // CFDictionaryRef environment
2880                                                  stdio_path.get(),
2881                                                  stdio_path.get(),
2882                                                  SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
2883
2884
2885    launch_err.SetError(sbs_error, DNBError::SpringBoard);
2886
2887    if (sbs_error == SBSApplicationLaunchErrorSuccess)
2888    {
2889        static const useconds_t pid_poll_interval = 200000;
2890        static const useconds_t pid_poll_timeout = 30000000;
2891
2892        useconds_t pid_poll_total = 0;
2893
2894        nub_process_t pid = INVALID_NUB_PROCESS;
2895        Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
2896        // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired
2897        // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started
2898        // yet, or that it died very quickly (if you weren't using waitForDebugger).
2899        while (!pid_found && pid_poll_total < pid_poll_timeout)
2900        {
2901            usleep (pid_poll_interval);
2902            pid_poll_total += pid_poll_interval;
2903            DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str());
2904            pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
2905        }
2906
2907        CFRelease (bundleIDCFStr);
2908        if (pid_found)
2909        {
2910            if (process != NULL)
2911            {
2912                // Release our master pty file descriptor so the pty class doesn't
2913                // close it and so we can continue to use it in our STDIO thread
2914                int master_fd = pty.ReleaseMasterFD();
2915                process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
2916            }
2917            DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
2918        }
2919        else
2920        {
2921            DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str());
2922        }
2923        return pid;
2924    }
2925
2926    DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error);
2927    return INVALID_NUB_PROCESS;
2928}
2929
2930#endif // #ifdef WITH_SPRINGBOARD
2931
2932#ifdef WITH_BKS
2933
2934
2935// This function runs the BKSSystemService method openApplication:options:clientPort:withResult,
2936// messaging the app passed in bundleIDNSStr.
2937// The function should be run inside of an NSAutoReleasePool.
2938//
2939// It will use the "options" dictionary passed in, and fill the error passed in if there is an error.
2940// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID.
2941// If bundleIDNSStr is NULL, then the system application will be messaged.
2942
2943static bool
2944CallBKSSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid)
2945{
2946    // Now make our systemService:
2947    BKSSystemService *system_service = [[BKSSystemService alloc] init];
2948
2949    if (bundleIDNSStr == nil)
2950    {
2951        bundleIDNSStr = [system_service systemApplicationBundleIdentifier];
2952        if (bundleIDNSStr == nil)
2953        {
2954            // Okay, no system app...
2955            error.SetErrorString("No system application to message.");
2956            return false;
2957        }
2958    }
2959
2960    mach_port_t client_port = [system_service createClientPort];
2961    __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
2962    __block BKSOpenApplicationErrorCode open_app_error = BKSOpenApplicationErrorCodeNone;
2963    bool wants_pid = (return_pid != NULL);
2964    __block pid_t pid_in_block;
2965
2966    const char *cstr = [bundleIDNSStr UTF8String];
2967    if (!cstr)
2968        cstr = "<Unknown Bundle ID>";
2969
2970    DNBLog ("About to launch process for bundle ID: %s", cstr);
2971    [system_service openApplication: bundleIDNSStr
2972                    options: options
2973                    clientPort: client_port
2974                    withResult: ^(NSError *bks_error)
2975                    {
2976                        // The system service will cleanup the client port we created for us.
2977                        if (bks_error)
2978                            open_app_error = (BKSOpenApplicationErrorCode)[bks_error code];
2979
2980                        if (open_app_error == BKSOpenApplicationErrorCodeNone)
2981                        {
2982                            if (wants_pid)
2983                            {
2984                                pid_in_block = [system_service pidForApplication: bundleIDNSStr];
2985                                DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
2986                                DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
2987                            }
2988                            else
2989                                DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success.");
2990                        }
2991                        else
2992                        {
2993                            const char *error_str = [[bks_error localizedDescription] UTF8String];
2994                            DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%d).",
2995                                             error_str ? error_str : "<unknown error>",
2996                                             open_app_error);
2997                            // REMOVE ME
2998                            DNBLogError ("In completion handler for send event, got error \"%s\"(%d).",
2999                                             error_str ? error_str : "<unknown error>",
3000                                             open_app_error);
3001                        }
3002
3003                        [system_service release];
3004                        dispatch_semaphore_signal(semaphore);
3005                    }
3006
3007    ];
3008
3009    const uint32_t timeout_secs = 9;
3010
3011    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
3012
3013    long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
3014
3015    dispatch_release(semaphore);
3016
3017    if (!success)
3018    {
3019        DNBLogError("timed out trying to send openApplication to %s.", cstr);
3020        error.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
3021        error.SetErrorString ("timed out trying to launch app");
3022    }
3023    else if (open_app_error != BKSOpenApplicationErrorCodeNone)
3024    {
3025        SetBKSError (open_app_error, error);
3026        DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error);
3027        success = false;
3028    }
3029    else if (wants_pid)
3030    {
3031        *return_pid = pid_in_block;
3032        DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid);
3033    }
3034
3035
3036    return success;
3037}
3038
3039void
3040MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str)
3041{
3042    bool success;
3043
3044    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3045
3046    // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
3047    NSString *bundleIDNSStr = (NSString *) attach_token;
3048
3049    // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
3050
3051    // First we have the debug sub-dictionary:
3052    NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
3053    [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch];
3054
3055    // That will go in the overall dictionary:
3056
3057    NSMutableDictionary *options = [NSMutableDictionary dictionary];
3058    [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
3059
3060    success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, err_str, NULL);
3061
3062    if (!success)
3063    {
3064        DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString());
3065    }
3066
3067    [pool drain];
3068}
3069
3070bool
3071AddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error)
3072{
3073    if (strcmp (event_data, "BackgroundContentFetching") == 0)
3074    {
3075        DNBLog("Setting ActivateForEvent key in options dictionary.");
3076        NSDictionary *event_details = [NSDictionary dictionary];
3077        NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching];
3078        [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent];
3079        return true;
3080    }
3081    else
3082    {
3083        DNBLogError ("Unrecognized event type: %s.  Ignoring.", event_data);
3084        option_error.SetErrorString("Unrecognized event data.");
3085        return false;
3086    }
3087
3088}
3089
3090pid_t
3091MachProcess::BKSLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err)
3092{
3093    // Clear out and clean up from any current state
3094    Clear();
3095
3096    DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
3097
3098    // Fork a child process for debugging
3099    SetState(eStateLaunching);
3100    m_pid = BKSForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err);
3101    if (m_pid != 0)
3102    {
3103        m_flags |= eMachProcessFlagsUsingBKS;
3104        m_path = path;
3105        size_t i;
3106        char const *arg;
3107        for (i=0; (arg = argv[i]) != NULL; i++)
3108            m_args.push_back(arg);
3109        m_task.StartExceptionThread(launch_err);
3110
3111        if (launch_err.Fail())
3112        {
3113            if (launch_err.AsString() == NULL)
3114                launch_err.SetErrorString("unable to start the exception thread");
3115            DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting.");
3116            ::ptrace (PT_KILL, m_pid, 0, 0);
3117            m_pid = INVALID_NUB_PROCESS;
3118            return INVALID_NUB_PROCESS;
3119        }
3120
3121        StartSTDIOThread();
3122        SetState (eStateAttaching);
3123        int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
3124        if (err == 0)
3125        {
3126            m_flags |= eMachProcessFlagsAttached;
3127            DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
3128        }
3129        else
3130        {
3131            SetState (eStateExited);
3132            DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
3133        }
3134    }
3135    return m_pid;
3136}
3137
3138pid_t
3139MachProcess::BKSForkChildForPTraceDebugging (const char *app_bundle_path,
3140                                             char const *argv[],
3141                                             char const *envp[],
3142                                             bool no_stdio,
3143                                             bool disable_aslr,
3144                                             const char *event_data,
3145                                             DNBError &launch_err)
3146{
3147    if (argv[0] == NULL)
3148        return INVALID_NUB_PROCESS;
3149
3150    DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, this);
3151
3152    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3153
3154    size_t argc = 0;
3155    // Count the number of arguments
3156    while (argv[argc] != NULL)
3157        argc++;
3158
3159    // Enumerate the arguments
3160    size_t first_launch_arg_idx = 1;
3161
3162    NSMutableArray *launch_argv = nil;
3163
3164    if (argv[first_launch_arg_idx])
3165    {
3166        size_t launch_argc = argc > 0 ? argc - 1 : 0;
3167        launch_argv = [NSMutableArray arrayWithCapacity: launch_argc];
3168        size_t i;
3169        char const *arg;
3170        NSString *launch_arg;
3171        for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
3172        {
3173            launch_arg = [NSString stringWithUTF8String: arg];
3174            // FIXME: Should we silently eat an argument that we can't convert into a UTF8 string?
3175            if (launch_arg != nil)
3176                [launch_argv addObject: launch_arg];
3177            else
3178                break;
3179        }
3180    }
3181
3182    NSMutableDictionary *launch_envp = nil;
3183    if (envp[0])
3184    {
3185        launch_envp = [[NSMutableDictionary alloc] init];
3186        const char *value;
3187        int name_len;
3188        NSString *name_string, *value_string;
3189
3190        for (int i = 0; envp[i] != NULL; i++)
3191        {
3192            value = strstr (envp[i], "=");
3193
3194            // If the name field is empty or there's no =, skip it.  Somebody's messing with us.
3195            if (value == NULL || value == envp[i])
3196                continue;
3197
3198            name_len = value - envp[i];
3199
3200            // Now move value over the "="
3201            value++;
3202            name_string = [[NSString alloc] initWithBytes: envp[i] length: name_len encoding: NSUTF8StringEncoding];
3203            value_string = [NSString stringWithUTF8String: value];
3204            [launch_envp setObject: value_string forKey: name_string];
3205        }
3206    }
3207
3208    NSString *stdio_path = nil;
3209    NSFileManager *file_manager = [NSFileManager defaultManager];
3210
3211    PseudoTerminal pty;
3212    if (!no_stdio)
3213    {
3214        PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
3215        if (pty_err == PseudoTerminal::success)
3216        {
3217            const char* slave_name = pty.SlaveName();
3218            DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
3219            if (slave_name && slave_name[0])
3220            {
3221                ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
3222                stdio_path = [file_manager stringWithFileSystemRepresentation: slave_name length: strlen(slave_name)];
3223            }
3224        }
3225    }
3226
3227    if (stdio_path == nil)
3228    {
3229        const char *null_path = "/dev/null";
3230        stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)];
3231    }
3232
3233    CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err);
3234    if (bundleIDCFStr == NULL)
3235    {
3236        [pool drain];
3237        return INVALID_NUB_PROCESS;
3238    }
3239
3240    // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
3241    NSString *bundleIDNSStr = (NSString *) bundleIDCFStr;
3242
3243    // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
3244
3245    // First we have the debug sub-dictionary:
3246    NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
3247    if (launch_argv != nil)
3248        [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments];
3249    if (launch_envp != nil)
3250        [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment];
3251
3252    [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath];
3253    [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath];
3254    [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger];
3255    if (disable_aslr)
3256        [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR];
3257
3258    // That will go in the overall dictionary:
3259
3260    NSMutableDictionary *options = [NSMutableDictionary dictionary];
3261    [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
3262
3263    // For now we only support one kind of event: the "fetch" event, which is indicated by the fact that its data
3264    // is an empty dictionary.
3265    if (event_data != NULL && *event_data != '\0')
3266    {
3267        if (!AddEventDataToOptions(options, event_data, launch_err))
3268        {
3269            [pool drain];
3270            return INVALID_NUB_PROCESS;
3271        }
3272    }
3273
3274    // And there are some other options at the top level in this dictionary:
3275    [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice];
3276
3277    pid_t return_pid = INVALID_NUB_PROCESS;
3278    bool success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, launch_err, &return_pid);
3279
3280    if (success)
3281    {
3282        int master_fd = pty.ReleaseMasterFD();
3283        SetChildFileDescriptors(master_fd, master_fd, master_fd);
3284        CFString::UTF8(bundleIDCFStr, m_bundle_id);
3285    }
3286
3287    [pool drain];
3288
3289    return return_pid;
3290}
3291
3292bool
3293MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err)
3294{
3295    bool return_value = true;
3296
3297    if (event_data == NULL || *event_data == '\0')
3298    {
3299        DNBLogError ("SendEvent called with NULL event data.");
3300        send_err.SetErrorString("SendEvent called with empty event data");
3301        return false;
3302    }
3303
3304    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3305
3306    if (strcmp (event_data, "BackgroundApplication") == 0)
3307    {
3308        // This is an event I cooked up.  What you actually do is foreground the system app, so:
3309        return_value = CallBKSSystemServiceOpenApplication(nil, nil, send_err, NULL);
3310        if (!return_value)
3311        {
3312            DNBLogError ("Failed to background application, error: %s.", send_err.AsString());
3313        }
3314    }
3315    else
3316    {
3317        if (m_bundle_id.empty())
3318        {
3319            // See if we can figure out the bundle ID for this PID:
3320
3321            DNBLogError ("Tried to send event \"%s\" to a process that has no bundle ID.", event_data);
3322            return false;
3323        }
3324
3325        NSString *bundleIDNSStr = [NSString stringWithUTF8String:m_bundle_id.c_str()];
3326
3327        NSMutableDictionary *options = [NSMutableDictionary dictionary];
3328
3329        if (!AddEventDataToOptions(options, event_data, send_err))
3330        {
3331            [pool drain];
3332            return false;
3333        }
3334
3335
3336        return_value = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, send_err, NULL);
3337
3338        if (!return_value)
3339        {
3340            DNBLogError ("Failed to send event: %s, error: %s.", event_data, send_err.AsString());
3341        }
3342    }
3343
3344    [pool drain];
3345    return return_value;
3346}
3347#endif // WITH_BKS
3348