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