diff options
-rw-r--r-- | lldb/include/lldb/Host/Host.h | 10 | ||||
-rw-r--r-- | lldb/source/Host/common/Host.cpp | 298 | ||||
-rw-r--r-- | lldb/source/Host/macosx/Host.mm | 219 |
3 files changed, 242 insertions, 285 deletions
diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h index aa37684a620..862b1ed7943 100644 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -476,7 +476,15 @@ public: static bool GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info); - + +#if defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__) + static short + GetPosixspawnFlags (ProcessLaunchInfo &launch_info); + + static Error + LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid); +#endif + static lldb::pid_t LaunchApplication (const FileSpec &app_file_spec); diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index a2d8e9cfa10..9302fa1ddc0 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -38,7 +38,7 @@ #include <AvailabilityMacros.h> #endif -#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) #include <spawn.h> #include <sys/wait.h> #include <sys/syscall.h> @@ -69,6 +69,18 @@ #include "llvm/Support/Host.h" #include "llvm/Support/raw_ostream.h" +#if defined (__APPLE__) +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#endif + +extern "C" +{ + int __pthread_chdir(const char *path); + int __pthread_fchdir (int fildes); +} + +#endif using namespace lldb; using namespace lldb_private; @@ -1637,24 +1649,73 @@ Host::RunShellCommand (const char *command, return error; } -#if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__) -// The functions below implement process launching via posix_spawn() for Linux -// and FreeBSD. -// The posix_spawn() and posix_spawnp() functions first appeared in FreeBSD 8.0, -static Error -LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid) +// LaunchProcessPosixSpawn for Apple, Linux, FreeBSD and other GLIBC +// systems + +#if defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__) + +// this method needs to be visible to macosx/Host.cpp and +// common/Host.cpp. + +short +Host::GetPosixspawnFlags (ProcessLaunchInfo &launch_info) +{ + short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + +#if defined (__APPLE__) + if (launch_info.GetFlags().Test (eLaunchFlagExec)) + flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag + + if (launch_info.GetFlags().Test (eLaunchFlagDebug)) + flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag + + if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) + flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag + + if (launch_info.GetLaunchInSeparateProcessGroup()) + flags |= POSIX_SPAWN_SETPGROUP; + +#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT +#if defined (__APPLE__) && (defined (__x86_64__) || defined (__i386__)) + static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; + if (g_use_close_on_exec_flag == eLazyBoolCalculate) + { + g_use_close_on_exec_flag = eLazyBoolNo; + + uint32_t major, minor, update; + if (Host::GetOSVersion(major, minor, update)) + { + // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or earlier + if (major > 10 || (major == 10 && minor > 7)) + { + // Only enable for 10.8 and later OS versions + g_use_close_on_exec_flag = eLazyBoolYes; + } + } + } +#else + static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; +#endif + // Close all files exception those with file actions if this is supported. + if (g_use_close_on_exec_flag == eLazyBoolYes) + flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; +#endif +#endif // #if defined (__APPLE__) + return flags; +} + +Error +Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid) { Error error; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); - assert(exe_path); - assert(!launch_info.GetFlags().Test (eLaunchFlagDebug)); - posix_spawnattr_t attr; - error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); - error.LogIfError(log, "::posix_spawnattr_init ( &attr )"); + + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_init ( &attr )"); if (error.Fail()) return error; @@ -1666,52 +1727,82 @@ LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, : sigset_t all_signals; sigemptyset (&no_signals); sigfillset (&all_signals); - ::posix_spawnattr_setsigmask(&attr, &all_signals); + ::posix_spawnattr_setsigmask(&attr, &no_signals); +#if defined (__linux__) ::posix_spawnattr_setsigdefault(&attr, &no_signals); +#else + ::posix_spawnattr_setsigdefault(&attr, &all_signals); +#endif - short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + short flags = GetPosixspawnFlags(launch_info); error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX); - error.LogIfError(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags); if (error.Fail()) return error; - const size_t num_file_actions = launch_info.GetNumFileActions (); - posix_spawn_file_actions_t file_actions, *file_action_ptr = NULL; - // Make a quick class that will cleanup the posix spawn attributes in case - // we return in the middle of this function. - lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> - posix_spawn_file_actions_cleanup (file_action_ptr, NULL, posix_spawn_file_actions_destroy); + // posix_spawnattr_setbinpref_np appears to be an Apple extension per: + // http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/ +#if defined (__APPLE__) && !defined (__arm__) - if (num_file_actions > 0) + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + cpu_type_t cpu = arch_spec.GetMachOCPUType(); + cpu_type_t sub = arch_spec.GetMachOCPUSubType(); + if (cpu != 0 && + cpu != UINT32_MAX && + cpu != LLDB_INVALID_CPUTYPE && + !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try to set the CPU type or we will fail { - error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); - error.LogIfError(log, "::posix_spawn_file_actions_init ( &file_actions )"); - if (error.Fail()) + size_t ocount = 0; + error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu, (uint64_t)ocount); + + if (error.Fail() || ocount != 1) return error; + } - file_action_ptr = &file_actions; - posix_spawn_file_actions_cleanup.set(file_action_ptr); +#endif - for (size_t i = 0; i < num_file_actions; ++i) - { - const ProcessLaunchInfo::FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i); - if (launch_file_action && - !ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (&file_actions, - launch_file_action, - log, - error)) - return error; - } + const char *tmp_argv[2]; + char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector(); + char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector(); + if (argv == NULL) + { + // posix_spawn gets very unhappy if it doesn't have at least the program + // name in argv[0]. One of the side affects I have noticed is the environment + // variables don't make it into the child process if "argv == NULL"!!! + tmp_argv[0] = exe_path; + tmp_argv[1] = NULL; + argv = (char * const*)tmp_argv; } - // Change working directory if neccessary. +#if !defined (__APPLE__) + // manage the working directory char current_dir[PATH_MAX]; current_dir[0] = '\0'; +#endif const char *working_dir = launch_info.GetWorkingDirectory(); - if (working_dir != NULL) + if (working_dir) { +#if defined (__APPLE__) + // Set the working directory on this thread only + if (__pthread_chdir (working_dir) < 0) { + if (errno == ENOENT) { + error.SetErrorStringWithFormat("No such file or directory: %s", working_dir); + } else if (errno == ENOTDIR) { + error.SetErrorStringWithFormat("Path doesn't name a directory: %s", working_dir); + } else { + error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution."); + } + return error; + } +#else if (::getcwd(current_dir, sizeof(current_dir)) == NULL) { error.SetError(errno, eErrorTypePOSIX); @@ -1725,45 +1816,111 @@ LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, : error.LogIfError(log, "unable to change working directory to %s", working_dir); return error; } +#endif } - const char *tmp_argv[2]; - char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector(); - char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector(); - - // Prepare minimal argument list if we didn't get it from the launch_info structure. - // We must pass argv into posix_spawnp and it must contain at least two items - - // pointer to an executable and NULL. - if (argv == NULL) + const size_t num_file_actions = launch_info.GetNumFileActions (); + if (num_file_actions > 0) { - tmp_argv[0] = exe_path; - tmp_argv[1] = NULL; - argv = (char * const*)tmp_argv; - } + posix_spawn_file_actions_t file_actions; + error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )"); + if (error.Fail()) + return error; + + // Make a quick class that will cleanup the posix spawn attributes in case + // we return in the middle of this function. + lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> posix_spawn_file_actions_cleanup (&file_actions, posix_spawn_file_actions_destroy); + + for (size_t i=0; i<num_file_actions; ++i) + { + const ProcessLaunchInfo::FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i); + if (launch_file_action) + { + if (!ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (&file_actions, + launch_file_action, + log, + error)) + return error; + } + } - error.SetError (::posix_spawnp (&pid, - exe_path, - (num_file_actions > 0) ? &file_actions : NULL, - &attr, - argv, - envp), - eErrorTypePOSIX); + error.SetError (::posix_spawnp (&pid, + exe_path, + &file_actions, + &attr, + argv, + envp), + eErrorTypePOSIX); - error.LogIfError(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", - pid, exe_path, file_action_ptr, &attr, argv, envp); + if (error.Fail() || log) + { + error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", + pid, + exe_path, + &file_actions, + &attr, + argv, + envp); + if (log) + { + for (int ii=0; argv[ii]; ++ii) + log->Printf("argv[%i] = '%s'", ii, argv[ii]); + } + } - // Change back the current directory. - // NOTE: do not override previously established error from posix_spawnp. - if (working_dir != NULL && ::chdir(current_dir) == -1 && error.Success()) + } + else + { + error.SetError (::posix_spawnp (&pid, + exe_path, + NULL, + &attr, + argv, + envp), + eErrorTypePOSIX); + + if (error.Fail() || log) + { + error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = NULL, attr = %p, argv = %p, envp = %p )", + pid, + exe_path, + &attr, + argv, + envp); + if (log) + { + for (int ii=0; argv[ii]; ++ii) + log->Printf("argv[%i] = '%s'", ii, argv[ii]); + } + } + } + + if (working_dir) { - error.SetError(errno, eErrorTypePOSIX); - error.LogIfError(log, "unable to change current directory back to %s", - current_dir); +#if defined (__APPLE__) + // No more thread specific current working directory + __pthread_fchdir (-1); +#else + if (::chdir(current_dir) == -1 && error.Success()) + { + error.SetError(errno, eErrorTypePOSIX); + error.LogIfError(log, "unable to change current directory back to %s", + current_dir); + } +#endif } return error; } +#endif // LaunchProcedssPosixSpawn: Apple, Linux, FreeBSD and other GLIBC systems + + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__) +// The functions below implement process launching via posix_spawn() for Linux +// and FreeBSD. Error Host::LaunchProcess (ProcessLaunchInfo &launch_info) @@ -1815,6 +1972,8 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info) // If all went well, then set the process ID into the launch info launch_info.SetProcessID(pid); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + // Make sure we reap any processes we spawn or we will have zombies. if (!launch_info.MonitorProcess()) { @@ -1823,6 +1982,13 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info) NULL, pid, monitor_signals); + if (log) + log->PutCString ("monitored child process with default Process::SetProcessExitStatus."); + } + else + { + if (log) + log->PutCString ("monitored child process with user-specified process monitor."); } } else diff --git a/lldb/source/Host/macosx/Host.mm b/lldb/source/Host/macosx/Host.mm index 5729a7375d3..fad2aaac8ad 100644 --- a/lldb/source/Host/macosx/Host.mm +++ b/lldb/source/Host/macosx/Host.mm @@ -1318,51 +1318,6 @@ Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) return false; } -static short -GetPosixspawnFlags (ProcessLaunchInfo &launch_info) -{ - short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; - if (launch_info.GetFlags().Test (eLaunchFlagExec)) - flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag - - if (launch_info.GetFlags().Test (eLaunchFlagDebug)) - flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag - - if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) - flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag - - if (launch_info.GetLaunchInSeparateProcessGroup()) - flags |= POSIX_SPAWN_SETPGROUP; - -#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT -#if defined (__APPLE__) && (defined (__x86_64__) || defined (__i386__)) - static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; - if (g_use_close_on_exec_flag == eLazyBoolCalculate) - { - g_use_close_on_exec_flag = eLazyBoolNo; - - uint32_t major, minor, update; - if (Host::GetOSVersion(major, minor, update)) - { - // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or earlier - if (major > 10 || (major == 10 && minor > 7)) - { - // Only enable for 10.8 and later OS versions - g_use_close_on_exec_flag = eLazyBoolYes; - } - } - } -#else - static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; -#endif - // Close all files exception those with file actions if this is supported. - if (g_use_close_on_exec_flag == eLazyBoolYes) - flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; -#endif - - return flags; -} - #if !NO_XPC_SERVICES static void PackageXPCArguments (xpc_object_t message, const char *prefix, const Args& args) @@ -1543,7 +1498,7 @@ LaunchProcessXPC (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t // Posix spawn stuff. xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType()); - xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, GetPosixspawnFlags(launch_info)); + xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, Host::GetPosixspawnFlags(launch_info)); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message); xpc_type_t returnType = xpc_get_type(reply); @@ -1586,178 +1541,6 @@ LaunchProcessXPC (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t #endif } -static Error -LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid) -{ - Error error; - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); - - posix_spawnattr_t attr; - error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); - - if (error.Fail() || log) - error.PutToLog(log, "::posix_spawnattr_init ( &attr )"); - if (error.Fail()) - return error; - - // Make a quick class that will cleanup the posix spawn attributes in case - // we return in the middle of this function. - lldb_utility::CleanUp <posix_spawnattr_t *, int> posix_spawnattr_cleanup(&attr, posix_spawnattr_destroy); - - sigset_t no_signals; - sigset_t all_signals; - sigemptyset (&no_signals); - sigfillset (&all_signals); - ::posix_spawnattr_setsigmask(&attr, &no_signals); - ::posix_spawnattr_setsigdefault(&attr, &all_signals); - - short flags = GetPosixspawnFlags(launch_info); - error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX); - if (error.Fail() || log) - error.PutToLog(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags); - if (error.Fail()) - return error; - -#if !defined(__arm__) - - // We don't need to do this for ARM, and we really shouldn't now that we - // have multiple CPU subtypes and no posix_spawnattr call that allows us - // to set which CPU subtype to launch... - const ArchSpec &arch_spec = launch_info.GetArchitecture(); - cpu_type_t cpu = arch_spec.GetMachOCPUType(); - cpu_type_t sub = arch_spec.GetMachOCPUSubType(); - if (cpu != 0 && - cpu != UINT32_MAX && - cpu != LLDB_INVALID_CPUTYPE && - !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try to set the CPU type or we will fail - { - size_t ocount = 0; - error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); - if (error.Fail() || log) - error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu, (uint64_t)ocount); - - if (error.Fail() || ocount != 1) - return error; - } - -#endif - - const char *tmp_argv[2]; - char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector(); - char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector(); - if (argv == NULL) - { - // posix_spawn gets very unhappy if it doesn't have at least the program - // name in argv[0]. One of the side affects I have noticed is the environment - // variables don't make it into the child process if "argv == NULL"!!! - tmp_argv[0] = exe_path; - tmp_argv[1] = NULL; - argv = (char * const*)tmp_argv; - } - - const char *working_dir = launch_info.GetWorkingDirectory(); - if (working_dir) - { - // No more thread specific current working directory - if (__pthread_chdir (working_dir) < 0) { - if (errno == ENOENT) { - error.SetErrorStringWithFormat("No such file or directory: %s", working_dir); - } else if (errno == ENOTDIR) { - error.SetErrorStringWithFormat("Path doesn't name a directory: %s", working_dir); - } else { - error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution."); - } - return error; - } - } - - const size_t num_file_actions = launch_info.GetNumFileActions (); - if (num_file_actions > 0) - { - posix_spawn_file_actions_t file_actions; - error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); - if (error.Fail() || log) - error.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )"); - if (error.Fail()) - return error; - - // Make a quick class that will cleanup the posix spawn attributes in case - // we return in the middle of this function. - lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> posix_spawn_file_actions_cleanup (&file_actions, posix_spawn_file_actions_destroy); - - for (size_t i=0; i<num_file_actions; ++i) - { - const ProcessLaunchInfo::FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i); - if (launch_file_action) - { - if (!ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (&file_actions, - launch_file_action, - log, - error)) - return error; - } - } - - error.SetError (::posix_spawnp (&pid, - exe_path, - &file_actions, - &attr, - argv, - envp), - eErrorTypePOSIX); - - if (error.Fail() || log) - { - error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", - pid, - exe_path, - &file_actions, - &attr, - argv, - envp); - if (log) - { - for (int ii=0; argv[ii]; ++ii) - log->Printf("argv[%i] = '%s'", ii, argv[ii]); - } - } - - } - else - { - error.SetError (::posix_spawnp (&pid, - exe_path, - NULL, - &attr, - argv, - envp), - eErrorTypePOSIX); - - if (error.Fail() || log) - { - error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = NULL, attr = %p, argv = %p, envp = %p )", - pid, - exe_path, - &attr, - argv, - envp); - if (log) - { - for (int ii=0; argv[ii]; ++ii) - log->Printf("argv[%i] = '%s'", ii, argv[ii]); - } - } - } - - if (working_dir) - { - // No more thread specific current working directory - __pthread_fchdir (-1); - } - - return error; -} - static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) { |