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