diff options
Diffstat (limited to 'lldb/source/Host/macosx/Host.mm')
-rw-r--r-- | lldb/source/Host/macosx/Host.mm | 2336 |
1 files changed, 1126 insertions, 1210 deletions
diff --git a/lldb/source/Host/macosx/Host.mm b/lldb/source/Host/macosx/Host.mm index 7754bc6..01231ce 100644 --- a/lldb/source/Host/macosx/Host.mm +++ b/lldb/source/Host/macosx/Host.mm @@ -11,8 +11,9 @@ #include <AvailabilityMacros.h> -#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 -#define NO_XPC_SERVICES 1 +#if !defined(MAC_OS_X_VERSION_10_7) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#define NO_XPC_SERVICES 1 #endif #if !defined(NO_XPC_SERVICES) @@ -21,18 +22,19 @@ #define LaunchUsingXPCRightName "com.apple.dt.Xcode.RootDebuggingXPCService" -// These XPC messaging keys are used for communication between Host.mm and the XPC service. -#define LauncherXPCServiceAuthKey "auth-key" -#define LauncherXPCServiceArgPrefxKey "arg" -#define LauncherXPCServiceEnvPrefxKey "env" -#define LauncherXPCServiceCPUTypeKey "cpuType" -#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags" -#define LauncherXPCServiceStdInPathKeyKey "stdInPath" -#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath" -#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath" -#define LauncherXPCServiceChildPIDKey "childPID" -#define LauncherXPCServiceErrorTypeKey "errorType" -#define LauncherXPCServiceCodeTypeKey "errorCode" +// These XPC messaging keys are used for communication between Host.mm and the +// XPC service. +#define LauncherXPCServiceAuthKey "auth-key" +#define LauncherXPCServiceArgPrefxKey "arg" +#define LauncherXPCServiceEnvPrefxKey "env" +#define LauncherXPCServiceCPUTypeKey "cpuType" +#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags" +#define LauncherXPCServiceStdInPathKeyKey "stdInPath" +#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath" +#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath" +#define LauncherXPCServiceChildPIDKey "childPID" +#define LauncherXPCServiceErrorTypeKey "errorType" +#define LauncherXPCServiceCodeTypeKey "errorCode" #endif @@ -79,132 +81,112 @@ #include "cfcpp/CFCReleaser.h" #include "cfcpp/CFCString.h" - #include <objc/objc-auto.h> #include <CoreFoundation/CoreFoundation.h> #include <Foundation/Foundation.h> #ifndef _POSIX_SPAWN_DISABLE_ASLR -#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 #endif -extern "C" -{ - int __pthread_chdir(const char *path); - int __pthread_fchdir (int fildes); +extern "C" { +int __pthread_chdir(const char *path); +int __pthread_fchdir(int fildes); } using namespace lldb; using namespace lldb_private; -bool -Host::GetBundleDirectory (const FileSpec &file, FileSpec &bundle_directory) -{ -#if defined (__APPLE__) - if (file.GetFileType () == FileSpec::eFileTypeDirectory) - { - char path[PATH_MAX]; - if (file.GetPath(path, sizeof(path))) - { - CFCBundle bundle (path); - if (bundle.GetPath (path, sizeof(path))) - { - bundle_directory.SetFile (path, false); - return true; - } - } +bool Host::GetBundleDirectory(const FileSpec &file, + FileSpec &bundle_directory) { +#if defined(__APPLE__) + if (file.GetFileType() == FileSpec::eFileTypeDirectory) { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) { + CFCBundle bundle(path); + if (bundle.GetPath(path, sizeof(path))) { + bundle_directory.SetFile(path, false); + return true; + } } + } #endif - bundle_directory.Clear(); - return false; + bundle_directory.Clear(); + return false; } - -bool -Host::ResolveExecutableInBundle (FileSpec &file) -{ -#if defined (__APPLE__) - if (file.GetFileType () == FileSpec::eFileTypeDirectory) - { - char path[PATH_MAX]; - if (file.GetPath(path, sizeof(path))) - { - CFCBundle bundle (path); - CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ()); - if (url.get()) - { - if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path))) - { - file.SetFile(path, false); - return true; - } - } +bool Host::ResolveExecutableInBundle(FileSpec &file) { +#if defined(__APPLE__) + if (file.GetFileType() == FileSpec::eFileTypeDirectory) { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) { + CFCBundle bundle(path); + CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL()); + if (url.get()) { + if (::CFURLGetFileSystemRepresentation(url.get(), YES, (UInt8 *)path, + sizeof(path))) { + file.SetFile(path, false); + return true; } + } } + } #endif return false; } -static void * -AcceptPIDFromInferior (void *arg) -{ - const char *connect_url = (const char *)arg; - ConnectionFileDescriptor file_conn; - Error error; - if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess) - { - char pid_str[256]; - ::memset (pid_str, 0, sizeof(pid_str)); - ConnectionStatus status; - const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL); - if (pid_str_len > 0) - { - int pid = atoi (pid_str); - return (void *)(intptr_t)pid; - } +static void *AcceptPIDFromInferior(void *arg) { + const char *connect_url = (const char *)arg; + ConnectionFileDescriptor file_conn; + Error error; + if (file_conn.Connect(connect_url, &error) == eConnectionStatusSuccess) { + char pid_str[256]; + ::memset(pid_str, 0, sizeof(pid_str)); + ConnectionStatus status; + const size_t pid_str_len = + file_conn.Read(pid_str, sizeof(pid_str), 0, status, NULL); + if (pid_str_len > 0) { + int pid = atoi(pid_str); + return (void *)(intptr_t)pid; } - return NULL; + } + return NULL; } -static bool -WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) -{ - const int time_delta_usecs = 100000; - const int num_retries = timeout_in_seconds/time_delta_usecs; - for (int i=0; i<num_retries; i++) - { - struct proc_bsdinfo bsd_info; - int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, - (uint64_t) 0, - &bsd_info, - PROC_PIDTBSDINFO_SIZE); - - switch (error) - { - case EINVAL: - case ENOTSUP: - case ESRCH: - case EPERM: - return false; - - default: - break; - - case 0: - if (bsd_info.pbi_status == SSTOP) - return true; - } - ::usleep (time_delta_usecs); +static bool WaitForProcessToSIGSTOP(const lldb::pid_t pid, + const int timeout_in_seconds) { + const int time_delta_usecs = 100000; + const int num_retries = timeout_in_seconds / time_delta_usecs; + for (int i = 0; i < num_retries; i++) { + struct proc_bsdinfo bsd_info; + int error = ::proc_pidinfo(pid, PROC_PIDTBSDINFO, (uint64_t)0, &bsd_info, + PROC_PIDTBSDINFO_SIZE); + + switch (error) { + case EINVAL: + case ENOTSUP: + case ESRCH: + case EPERM: + return false; + + default: + break; + + case 0: + if (bsd_info.pbi_status == SSTOP) + return true; } - return false; + ::usleep(time_delta_usecs); + } + return false; } #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) -//static lldb::pid_t -//LaunchInNewTerminalWithCommandFile +// static lldb::pid_t +// LaunchInNewTerminalWithCommandFile //( -// const char **argv, +// const char **argv, // const char **envp, // const char *working_dir, // const ArchSpec *arch_spec, @@ -216,18 +198,19 @@ WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) // return LLDB_INVALID_PROCESS_ID; // // OSStatus error = 0; -// +// // FileSpec program (argv[0], false); -// -// +// +// // std::string unix_socket_name; // // char temp_file_path[PATH_MAX]; // const char *tmpdir = ::getenv ("TMPDIR"); // if (tmpdir == NULL) // tmpdir = "/tmp/"; -// ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString()); -// +// ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, +// program.GetFilename().AsCString()); +// // if (::mktemp (temp_file_path) == NULL) // return LLDB_INVALID_PROCESS_ID; // @@ -236,15 +219,17 @@ WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) // ::strlcat (temp_file_path, ".command", sizeof (temp_file_path)); // // StreamFile command_file; -// command_file.GetFile().Open (temp_file_path, -// File::eOpenOptionWrite | File::eOpenOptionCanCreate, +// command_file.GetFile().Open (temp_file_path, +// File::eOpenOptionWrite | +// File::eOpenOptionCanCreate, // lldb::eFilePermissionsDefault); -// +// // if (!command_file.GetFile().IsValid()) // return LLDB_INVALID_PROCESS_ID; -// +// // FileSpec darwin_debug_file_spec; -// if (!HostInfo::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec)) +// if (!HostInfo::GetLLDBPath (ePathTypeSupportExecutableDir, +// darwin_debug_file_spec)) // return LLDB_INVALID_PROCESS_ID; // darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); // @@ -296,7 +281,8 @@ WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) // std::string env_val (equal_pos + 1); // CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8); // CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8); -// cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create); +// cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), +// can_create); // } // } // } @@ -307,7 +293,8 @@ WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) // app_params.argv = NULL; // app_params.environment = (CFDictionaryRef)cf_env_dict.get(); // -// CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL, +// CFCReleaser<CFURLRef> command_file_url +// (::CFURLCreateFromFileSystemRepresentation (NULL, // (const UInt8 *)temp_file_path, // strlen(temp_file_path), // false)); @@ -325,28 +312,33 @@ WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) // Error lldb_error; // // Sleep and wait a bit for debugserver to start to listen... // char connect_url[128]; -// ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str()); +// ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", +// unix_socket_name.c_str()); // // // Spawn a new thread to accept incoming connection on the connect_url // // so we can grab the pid from the inferior -// lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(), +// lldb::thread_t accept_thread = Host::ThreadCreate +// (unix_socket_name.c_str(), // AcceptPIDFromInferior, // connect_url, // &lldb_error); // // ProcessSerialNumber psn; -// error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1); +// error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, +// &psn, 1); // if (error == noErr) // { // thread_result_t accept_thread_result = NULL; -// if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error)) +// if (Host::ThreadJoin (accept_thread, &accept_thread_result, +// &lldb_error)) // { // if (accept_thread_result) // { // pid = (intptr_t)accept_thread_result; // // // Wait for process to be stopped the entry point by watching -// // for the process status to be set to SSTOP which indicates it it +// // for the process status to be set to SSTOP which indicates +// it it // // SIGSTOP'ed at the entry point // WaitForProcessToSIGSTOP (pid, 5); // } @@ -360,12 +352,10 @@ WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) // return pid; //} -const char *applscript_in_new_tty = -"tell application \"Terminal\"\n" -" activate\n" -" do script \"%s\"\n" -"end tell\n"; - +const char *applscript_in_new_tty = "tell application \"Terminal\"\n" + " activate\n" + " do script \"%s\"\n" + "end tell\n"; const char *applscript_in_existing_tty = "\ set the_shell_script to \"%s\"\n\ @@ -386,1202 +376,1128 @@ tell application \"Terminal\"\n\ do script the_shell_script\n\ end tell\n"; - static Error -LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &launch_info) -{ - Error error; - char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; - if (::mktemp (unix_socket_name) == NULL) - { - error.SetErrorString ("failed to make temporary path for a unix socket"); - return error; - } - - StreamString command; - FileSpec darwin_debug_file_spec; - if (!HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, darwin_debug_file_spec)) - { - error.SetErrorString ("can't locate the 'darwin-debug' executable"); - return error; - } - - darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); - - if (!darwin_debug_file_spec.Exists()) - { - error.SetErrorStringWithFormat ("the 'darwin-debug' executable doesn't exists at '%s'", - darwin_debug_file_spec.GetPath().c_str()); - return error; - } - - char launcher_path[PATH_MAX]; - darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); - - const ArchSpec &arch_spec = launch_info.GetArchitecture(); - // Only set the architecture if it is valid and if it isn't Haswell (x86_64h). - if (arch_spec.IsValid() && arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h) - command.Printf("arch -arch %s ", arch_spec.GetArchitectureName()); +LaunchInNewTerminalWithAppleScript(const char *exe_path, + ProcessLaunchInfo &launch_info) { + Error error; + char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; + if (::mktemp(unix_socket_name) == NULL) { + error.SetErrorString("failed to make temporary path for a unix socket"); + return error; + } - command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name); + StreamString command; + FileSpec darwin_debug_file_spec; + if (!HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, + darwin_debug_file_spec)) { + error.SetErrorString("can't locate the 'darwin-debug' executable"); + return error; + } - if (arch_spec.IsValid()) - command.Printf(" --arch=%s", arch_spec.GetArchitectureName()); + darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); - FileSpec working_dir{launch_info.GetWorkingDirectory()}; - if (working_dir) - command.Printf(" --working-dir '%s'", working_dir.GetCString()); - else - { - char cwd[PATH_MAX]; - if (getcwd(cwd, PATH_MAX)) - command.Printf(" --working-dir '%s'", cwd); - } - - if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) - command.PutCString(" --disable-aslr"); - - // We are launching on this host in a terminal. So compare the environment on the host - // to what is supplied in the launch_info. Any items that aren't in the host environment - // need to be sent to darwin-debug. If we send all environment entries, we might blow the - // max command line length, so we only send user modified entries. - const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector (); - - StringList host_env; - const size_t host_env_count = Host::GetEnvironment (host_env); - - if (envp && envp[0]) - { - const char *env_entry; - for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx) - { - bool add_entry = true; - for (size_t i=0; i<host_env_count; ++i) - { - const char *host_env_entry = host_env.GetStringAtIndex(i); - if (strcmp(env_entry, host_env_entry) == 0) - { - add_entry = false; - break; - } - } - if (add_entry) - { - command.Printf(" --env='%s'", env_entry); - } + if (!darwin_debug_file_spec.Exists()) { + error.SetErrorStringWithFormat( + "the 'darwin-debug' executable doesn't exists at '%s'", + darwin_debug_file_spec.GetPath().c_str()); + return error; + } + + char launcher_path[PATH_MAX]; + darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); + + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + // Only set the architecture if it is valid and if it isn't Haswell (x86_64h). + if (arch_spec.IsValid() && + arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h) + command.Printf("arch -arch %s ", arch_spec.GetArchitectureName()); + + command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name); + + if (arch_spec.IsValid()) + command.Printf(" --arch=%s", arch_spec.GetArchitectureName()); + + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir) + command.Printf(" --working-dir '%s'", working_dir.GetCString()); + else { + char cwd[PATH_MAX]; + if (getcwd(cwd, PATH_MAX)) + command.Printf(" --working-dir '%s'", cwd); + } + + if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR)) + command.PutCString(" --disable-aslr"); + + // We are launching on this host in a terminal. So compare the environment on + // the host + // to what is supplied in the launch_info. Any items that aren't in the host + // environment + // need to be sent to darwin-debug. If we send all environment entries, we + // might blow the + // max command line length, so we only send user modified entries. + const char **envp = + launch_info.GetEnvironmentEntries().GetConstArgumentVector(); + + StringList host_env; + const size_t host_env_count = Host::GetEnvironment(host_env); + + if (envp && envp[0]) { + const char *env_entry; + for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx) { + bool add_entry = true; + for (size_t i = 0; i < host_env_count; ++i) { + const char *host_env_entry = host_env.GetStringAtIndex(i); + if (strcmp(env_entry, host_env_entry) == 0) { + add_entry = false; + break; } + } + if (add_entry) { + command.Printf(" --env='%s'", env_entry); + } } + } - command.PutCString(" -- "); - - const char **argv = launch_info.GetArguments().GetConstArgumentVector (); - if (argv) - { - for (size_t i=0; argv[i] != NULL; ++i) - { - if (i==0) - command.Printf(" '%s'", exe_path); - else - command.Printf(" '%s'", argv[i]); - } - } - else - { - command.Printf(" '%s'", exe_path); - } - command.PutCString (" ; echo Process exited with status $?"); - if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit)) - command.PutCString (" ; exit"); - - StreamString applescript_source; - - const char *tty_command = command.GetString().c_str(); -// if (tty_name && tty_name[0]) -// { -// applescript_source.Printf (applscript_in_existing_tty, -// tty_command, -// tty_name); -// } -// else -// { - applescript_source.Printf (applscript_in_new_tty, - tty_command); -// } + command.PutCString(" -- "); - - - const char *script_source = applescript_source.GetString().c_str(); - //puts (script_source); - NSAppleScript* applescript = [[NSAppleScript alloc] initWithSource:[NSString stringWithCString:script_source encoding:NSUTF8StringEncoding]]; - - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; - - Error lldb_error; - // Sleep and wait a bit for debugserver to start to listen... - ConnectionFileDescriptor file_conn; - char connect_url[128]; - ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); - - // Spawn a new thread to accept incoming connection on the connect_url - // so we can grab the pid from the inferior. We have to do this because we - // are sending an AppleScript that will launch a process in Terminal.app, - // in a shell and the shell will fork/exec a couple of times before we get - // to the process that we wanted to launch. So when our process actually - // gets launched, we will handshake with it and get the process ID for it. - HostThread accept_thread = ThreadLauncher::LaunchThread(unix_socket_name, AcceptPIDFromInferior, connect_url, &lldb_error); - - [applescript executeAndReturnError:nil]; - - thread_result_t accept_thread_result = NULL; - lldb_error = accept_thread.Join(&accept_thread_result); - if (lldb_error.Success() && accept_thread_result) - { - pid = (intptr_t)accept_thread_result; - - // Wait for process to be stopped at the entry point by watching - // for the process status to be set to SSTOP which indicates it it - // SIGSTOP'ed at the entry point - WaitForProcessToSIGSTOP(pid, 5); + const char **argv = launch_info.GetArguments().GetConstArgumentVector(); + if (argv) { + for (size_t i = 0; argv[i] != NULL; ++i) { + if (i == 0) + command.Printf(" '%s'", exe_path); + else + command.Printf(" '%s'", argv[i]); } - - FileSystem::Unlink(FileSpec{unix_socket_name, false}); - [applescript release]; - if (pid != LLDB_INVALID_PROCESS_ID) - launch_info.SetProcessID (pid); - return error; + } else { + command.Printf(" '%s'", exe_path); + } + command.PutCString(" ; echo Process exited with status $?"); + if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit)) + command.PutCString(" ; exit"); + + StreamString applescript_source; + + const char *tty_command = command.GetString().c_str(); + // if (tty_name && tty_name[0]) + // { + // applescript_source.Printf (applscript_in_existing_tty, + // tty_command, + // tty_name); + // } + // else + // { + applescript_source.Printf(applscript_in_new_tty, tty_command); + // } + + const char *script_source = applescript_source.GetString().c_str(); + // puts (script_source); + NSAppleScript *applescript = [[NSAppleScript alloc] + initWithSource:[NSString stringWithCString:script_source + encoding:NSUTF8StringEncoding]]; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + Error lldb_error; + // Sleep and wait a bit for debugserver to start to listen... + ConnectionFileDescriptor file_conn; + char connect_url[128]; + ::snprintf(connect_url, sizeof(connect_url), "unix-accept://%s", + unix_socket_name); + + // Spawn a new thread to accept incoming connection on the connect_url + // so we can grab the pid from the inferior. We have to do this because we + // are sending an AppleScript that will launch a process in Terminal.app, + // in a shell and the shell will fork/exec a couple of times before we get + // to the process that we wanted to launch. So when our process actually + // gets launched, we will handshake with it and get the process ID for it. + HostThread accept_thread = ThreadLauncher::LaunchThread( + unix_socket_name, AcceptPIDFromInferior, connect_url, &lldb_error); + + [applescript executeAndReturnError:nil]; + + thread_result_t accept_thread_result = NULL; + lldb_error = accept_thread.Join(&accept_thread_result); + if (lldb_error.Success() && accept_thread_result) { + pid = (intptr_t)accept_thread_result; + + // Wait for process to be stopped at the entry point by watching + // for the process status to be set to SSTOP which indicates it it + // SIGSTOP'ed at the entry point + WaitForProcessToSIGSTOP(pid, 5); + } + + FileSystem::Unlink(FileSpec{unix_socket_name, false}); + [applescript release]; + if (pid != LLDB_INVALID_PROCESS_ID) + launch_info.SetProcessID(pid); + return error; } #endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) - // On MacOSX CrashReporter will display a string for each shared library if // the shared library has an exported symbol named "__crashreporter_info__". -static std::mutex & -GetCrashReporterMutex() -{ - static std::mutex g_mutex; - return g_mutex; +static std::mutex &GetCrashReporterMutex() { + static std::mutex g_mutex; + return g_mutex; } extern "C" { - const char *__crashreporter_info__ = NULL; +const char *__crashreporter_info__ = NULL; } asm(".desc ___crashreporter_info__, 0x10"); -void -Host::SetCrashDescriptionWithFormat (const char *format, ...) -{ - static StreamString g_crash_description; - std::lock_guard<std::mutex> guard(GetCrashReporterMutex()); - - if (format) - { - va_list args; - va_start (args, format); - g_crash_description.GetString().clear(); - g_crash_description.PrintfVarArg(format, args); - va_end (args); - __crashreporter_info__ = g_crash_description.GetData(); - } - else - { - __crashreporter_info__ = NULL; - } +void Host::SetCrashDescriptionWithFormat(const char *format, ...) { + static StreamString g_crash_description; + std::lock_guard<std::mutex> guard(GetCrashReporterMutex()); + + if (format) { + va_list args; + va_start(args, format); + g_crash_description.GetString().clear(); + g_crash_description.PrintfVarArg(format, args); + va_end(args); + __crashreporter_info__ = g_crash_description.GetData(); + } else { + __crashreporter_info__ = NULL; + } } -void -Host::SetCrashDescription (const char *cstr) -{ - std::lock_guard<std::mutex> guard(GetCrashReporterMutex()); - static std::string g_crash_description; - if (cstr) - { - g_crash_description.assign (cstr); - __crashreporter_info__ = g_crash_description.c_str(); - } - else - { - __crashreporter_info__ = NULL; - } +void Host::SetCrashDescription(const char *cstr) { + std::lock_guard<std::mutex> guard(GetCrashReporterMutex()); + static std::string g_crash_description; + if (cstr) { + g_crash_description.assign(cstr); + __crashreporter_info__ = g_crash_description.c_str(); + } else { + __crashreporter_info__ = NULL; + } } -bool -Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no) -{ +bool Host::OpenFileInExternalEditor(const FileSpec &file_spec, + uint32_t line_no) { #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - return false; + return false; #else - // We attach this to an 'odoc' event to specify a particular selection - typedef struct { - int16_t reserved0; // must be zero - int16_t fLineNumber; - int32_t fSelStart; - int32_t fSelEnd; - uint32_t reserved1; // must be zero - uint32_t reserved2; // must be zero - } BabelAESelInfo; - - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST)); - char file_path[PATH_MAX]; - file_spec.GetPath(file_path, PATH_MAX); - CFCString file_cfstr (file_path, kCFStringEncodingUTF8); - CFCReleaser<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL, - file_cfstr.get(), - kCFURLPOSIXPathStyle, - false)); - + // We attach this to an 'odoc' event to specify a particular selection + typedef struct { + int16_t reserved0; // must be zero + int16_t fLineNumber; + int32_t fSelStart; + int32_t fSelEnd; + uint32_t reserved1; // must be zero + uint32_t reserved2; // must be zero + } BabelAESelInfo; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_HOST)); + char file_path[PATH_MAX]; + file_spec.GetPath(file_path, PATH_MAX); + CFCString file_cfstr(file_path, kCFStringEncodingUTF8); + CFCReleaser<CFURLRef> file_URL(::CFURLCreateWithFileSystemPath( + NULL, file_cfstr.get(), kCFURLPOSIXPathStyle, false)); + + if (log) + log->Printf( + "Sending source file: \"%s\" and line: %d to external editor.\n", + file_path, line_no); + + long error; + BabelAESelInfo file_and_line_info = { + 0, // reserved0 + (int16_t)(line_no - 1), // fLineNumber (zero based line number) + 1, // fSelStart + 1024, // fSelEnd + 0, // reserved1 + 0 // reserved2 + }; + + AEKeyDesc file_and_line_desc; + + error = ::AECreateDesc(typeUTF8Text, &file_and_line_info, + sizeof(file_and_line_info), + &(file_and_line_desc.descContent)); + + if (error != noErr) { if (log) - log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no); - - long error; - BabelAESelInfo file_and_line_info = - { - 0, // reserved0 - (int16_t)(line_no - 1), // fLineNumber (zero based line number) - 1, // fSelStart - 1024, // fSelEnd - 0, // reserved1 - 0 // reserved2 - }; + log->Printf("Error creating AEDesc: %ld.\n", error); + return false; + } + + file_and_line_desc.descKey = keyAEPosition; + + static std::string g_app_name; + static FSRef g_app_fsref; + + LSApplicationParameters app_params; + ::memset(&app_params, 0, sizeof(app_params)); + app_params.flags = + kLSLaunchDefaults | kLSLaunchDontAddToRecents | kLSLaunchDontSwitch; + + char *external_editor = ::getenv("LLDB_EXTERNAL_EDITOR"); + + if (external_editor) { + if (log) + log->Printf("Looking for external editor \"%s\".\n", external_editor); - AEKeyDesc file_and_line_desc; - - error = ::AECreateDesc (typeUTF8Text, - &file_and_line_info, - sizeof (file_and_line_info), - &(file_and_line_desc.descContent)); - - if (error != noErr) - { + if (g_app_name.empty() || + strcmp(g_app_name.c_str(), external_editor) != 0) { + CFCString editor_name(external_editor, kCFStringEncodingUTF8); + error = ::LSFindApplicationForInfo(kLSUnknownCreator, NULL, + editor_name.get(), &g_app_fsref, NULL); + + // If we found the app, then store away the name so we don't have to + // re-look it up. + if (error != noErr) { if (log) - log->Printf("Error creating AEDesc: %ld.\n", error); + log->Printf( + "Could not find External Editor application, error: %ld.\n", + error); return false; + } } - - file_and_line_desc.descKey = keyAEPosition; - - static std::string g_app_name; - static FSRef g_app_fsref; - - LSApplicationParameters app_params; - ::memset (&app_params, 0, sizeof (app_params)); - app_params.flags = kLSLaunchDefaults | - kLSLaunchDontAddToRecents | - kLSLaunchDontSwitch; - - char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR"); - - if (external_editor) - { - if (log) - log->Printf("Looking for external editor \"%s\".\n", external_editor); - - if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0) - { - CFCString editor_name (external_editor, kCFStringEncodingUTF8); - error = ::LSFindApplicationForInfo (kLSUnknownCreator, - NULL, - editor_name.get(), - &g_app_fsref, - NULL); - - // If we found the app, then store away the name so we don't have to re-look it up. - if (error != noErr) - { - if (log) - log->Printf("Could not find External Editor application, error: %ld.\n", error); - return false; - } - - } - app_params.application = &g_app_fsref; - } + app_params.application = &g_app_fsref; + } - ProcessSerialNumber psn; - CFCReleaser<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL)); - error = ::LSOpenURLsWithRole (file_array.get(), - kLSRolesAll, - &file_and_line_desc, - &app_params, - &psn, - 1); - - AEDisposeDesc (&(file_and_line_desc.descContent)); - - if (error != noErr) - { - if (log) - log->Printf("LSOpenURLsWithRole failed, error: %ld.\n", error); + ProcessSerialNumber psn; + CFCReleaser<CFArrayRef> file_array( + CFArrayCreate(NULL, (const void **)file_URL.ptr_address(false), 1, NULL)); + error = ::LSOpenURLsWithRole(file_array.get(), kLSRolesAll, + &file_and_line_desc, &app_params, &psn, 1); - return false; - } + AEDisposeDesc(&(file_and_line_desc.descContent)); - return true; + if (error != noErr) { + if (log) + log->Printf("LSOpenURLsWithRole failed, error: %ld.\n", error); + + return false; + } + + return true; #endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) } -size_t -Host::GetEnvironment (StringList &env) -{ - char **host_env = *_NSGetEnviron(); - char *env_entry; - size_t i; - for (i=0; (env_entry = host_env[i]) != NULL; ++i) - env.AppendString(env_entry); - return i; - +size_t Host::GetEnvironment(StringList &env) { + char **host_env = *_NSGetEnviron(); + char *env_entry; + size_t i; + for (i = 0; (env_entry = host_env[i]) != NULL; ++i) + env.AppendString(env_entry); + return i; } -static bool -GetMacOSXProcessCPUType (ProcessInstanceInfo &process_info) -{ - if (process_info.ProcessIDIsValid()) - { - // Make a new mib to stay thread safe - int mib[CTL_MAXNAME]={0,}; - size_t mib_len = CTL_MAXNAME; - if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len)) - return false; - - mib[mib_len] = process_info.GetProcessID(); - mib_len++; - - cpu_type_t cpu, sub = 0; - size_t len = sizeof(cpu); - if (::sysctl (mib, mib_len, &cpu, &len, 0, 0) == 0) - { - switch (cpu) - { - case CPU_TYPE_I386: sub = CPU_SUBTYPE_I386_ALL; break; - case CPU_TYPE_X86_64: sub = CPU_SUBTYPE_X86_64_ALL; break; - -#if defined (CPU_TYPE_ARM64) && defined (CPU_SUBTYPE_ARM64_ALL) - case CPU_TYPE_ARM64: sub = CPU_SUBTYPE_ARM64_ALL; break; +static bool GetMacOSXProcessCPUType(ProcessInstanceInfo &process_info) { + if (process_info.ProcessIDIsValid()) { + // Make a new mib to stay thread safe + int mib[CTL_MAXNAME] = { + 0, + }; + size_t mib_len = CTL_MAXNAME; + if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len)) + return false; + + mib[mib_len] = process_info.GetProcessID(); + mib_len++; + + cpu_type_t cpu, sub = 0; + size_t len = sizeof(cpu); + if (::sysctl(mib, mib_len, &cpu, &len, 0, 0) == 0) { + switch (cpu) { + case CPU_TYPE_I386: + sub = CPU_SUBTYPE_I386_ALL; + break; + case CPU_TYPE_X86_64: + sub = CPU_SUBTYPE_X86_64_ALL; + break; + +#if defined(CPU_TYPE_ARM64) && defined(CPU_SUBTYPE_ARM64_ALL) + case CPU_TYPE_ARM64: + sub = CPU_SUBTYPE_ARM64_ALL; + break; #endif - case CPU_TYPE_ARM: - { - // Note that we fetched the cpu type from the PROCESS but we can't get a cpusubtype of the - // process -- we can only get the host's cpu subtype. - uint32_t cpusubtype = 0; - len = sizeof(cpusubtype); - if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) - sub = cpusubtype; - - bool host_cpu_is_64bit; - uint32_t is64bit_capable; - size_t is64bit_capable_len = sizeof (is64bit_capable); - if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, &is64bit_capable_len, NULL, 0) == 0) - host_cpu_is_64bit = true; - else - host_cpu_is_64bit = false; - - // if the host is an armv8 device, its cpusubtype will be in CPU_SUBTYPE_ARM64 numbering - // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value instead. - - if (host_cpu_is_64bit) - { - sub = CPU_SUBTYPE_ARM_V7; - } - } - break; - - default: - break; - } - process_info.GetArchitecture ().SetArchitecture (eArchTypeMachO, cpu, sub); - return true; + case CPU_TYPE_ARM: { + // Note that we fetched the cpu type from the PROCESS but we can't get a + // cpusubtype of the + // process -- we can only get the host's cpu subtype. + uint32_t cpusubtype = 0; + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + sub = cpusubtype; + + bool host_cpu_is_64bit; + uint32_t is64bit_capable; + size_t is64bit_capable_len = sizeof(is64bit_capable); + if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, + &is64bit_capable_len, NULL, 0) == 0) + host_cpu_is_64bit = true; + else + host_cpu_is_64bit = false; + + // if the host is an armv8 device, its cpusubtype will be in + // CPU_SUBTYPE_ARM64 numbering + // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value + // instead. + + if (host_cpu_is_64bit) { + sub = CPU_SUBTYPE_ARM_V7; } + } break; + + default: + break; + } + process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, sub); + return true; } - process_info.GetArchitecture().Clear(); - return false; + } + process_info.GetArchitecture().Clear(); + return false; } -static bool -GetMacOSXProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, - ProcessInstanceInfo &process_info) -{ - if (process_info.ProcessIDIsValid()) - { - int proc_args_mib[3] = { CTL_KERN, KERN_PROCARGS2, (int)process_info.GetProcessID() }; - - size_t arg_data_size = 0; - if (::sysctl (proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) || arg_data_size == 0) - arg_data_size = 8192; - - // Add a few bytes to the calculated length, I know we need to add at least one byte - // to this number otherwise we get junk back, so add 128 just in case... - DataBufferHeap arg_data(arg_data_size+128, 0); - arg_data_size = arg_data.GetByteSize(); - if (::sysctl (proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size , NULL, 0) == 0) - { - DataExtractor data (arg_data.GetBytes(), arg_data_size, endian::InlHostByteOrder(), sizeof(void *)); - lldb::offset_t offset = 0; - uint32_t argc = data.GetU32 (&offset); - llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); - const llvm::Triple::ArchType triple_arch = triple.getArch(); - const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || triple_arch == llvm::Triple::x86_64); - const char *cstr = data.GetCStr (&offset); +static bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr, + ProcessInstanceInfo &process_info) { + if (process_info.ProcessIDIsValid()) { + int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2, + (int)process_info.GetProcessID()}; + + size_t arg_data_size = 0; + if (::sysctl(proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) || + arg_data_size == 0) + arg_data_size = 8192; + + // Add a few bytes to the calculated length, I know we need to add at least + // one byte + // to this number otherwise we get junk back, so add 128 just in case... + DataBufferHeap arg_data(arg_data_size + 128, 0); + arg_data_size = arg_data.GetByteSize(); + if (::sysctl(proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size, NULL, + 0) == 0) { + DataExtractor data(arg_data.GetBytes(), arg_data_size, + endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + uint32_t argc = data.GetU32(&offset); + llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); + const llvm::Triple::ArchType triple_arch = triple.getArch(); + const bool check_for_ios_simulator = + (triple_arch == llvm::Triple::x86 || + triple_arch == llvm::Triple::x86_64); + const char *cstr = data.GetCStr(&offset); + if (cstr) { + process_info.GetExecutableFile().SetFile(cstr, false); + + if (match_info_ptr == NULL || + NameMatches( + process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName())) { + // Skip NULLs + while (1) { + const uint8_t *p = data.PeekData(offset, 1); + if ((p == NULL) || (*p != '\0')) + break; + ++offset; + } + // Now extract all arguments + Args &proc_args = process_info.GetArguments(); + for (int i = 0; i < static_cast<int>(argc); ++i) { + cstr = data.GetCStr(&offset); if (cstr) - { - process_info.GetExecutableFile().SetFile(cstr, false); - - if (match_info_ptr == NULL || - NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), - match_info_ptr->GetNameMatchType(), - match_info_ptr->GetProcessInfo().GetName())) - { - // Skip NULLs - while (1) - { - const uint8_t *p = data.PeekData(offset, 1); - if ((p == NULL) || (*p != '\0')) - break; - ++offset; - } - // Now extract all arguments - Args &proc_args = process_info.GetArguments(); - for (int i=0; i<static_cast<int>(argc); ++i) - { - cstr = data.GetCStr(&offset); - if (cstr) - proc_args.AppendArgument(cstr); - } - - Args &proc_env = process_info.GetEnvironmentEntries (); - while ((cstr = data.GetCStr(&offset))) - { - if (cstr[0] == '\0') - break; - - if (check_for_ios_simulator) - { - if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0) - process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::IOS); - else - process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::MacOSX); - } - - proc_env.AppendArgument(cstr); - } - return true; - } + proc_args.AppendArgument(cstr); + } + + Args &proc_env = process_info.GetEnvironmentEntries(); + while ((cstr = data.GetCStr(&offset))) { + if (cstr[0] == '\0') + break; + + if (check_for_ios_simulator) { + if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == + 0) + process_info.GetArchitecture().GetTriple().setOS( + llvm::Triple::IOS); + else + process_info.GetArchitecture().GetTriple().setOS( + llvm::Triple::MacOSX); } + + proc_env.AppendArgument(cstr); + } + return true; } + } } - return false; + } + return false; } -static bool -GetMacOSXProcessUserAndGroup (ProcessInstanceInfo &process_info) -{ - if (process_info.ProcessIDIsValid()) - { - int mib[4]; - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = process_info.GetProcessID(); - struct kinfo_proc proc_kinfo; - size_t proc_kinfo_size = sizeof(struct kinfo_proc); - - if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) - { - if (proc_kinfo_size > 0) - { - process_info.SetParentProcessID (proc_kinfo.kp_eproc.e_ppid); - process_info.SetUserID (proc_kinfo.kp_eproc.e_pcred.p_ruid); - process_info.SetGroupID (proc_kinfo.kp_eproc.e_pcred.p_rgid); - process_info.SetEffectiveUserID (proc_kinfo.kp_eproc.e_ucred.cr_uid); - if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) - process_info.SetEffectiveGroupID (proc_kinfo.kp_eproc.e_ucred.cr_groups[0]); - else - process_info.SetEffectiveGroupID (UINT32_MAX); - return true; - } - } +static bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) { + if (process_info.ProcessIDIsValid()) { + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = process_info.GetProcessID(); + struct kinfo_proc proc_kinfo; + size_t proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) { + if (proc_kinfo_size > 0) { + process_info.SetParentProcessID(proc_kinfo.kp_eproc.e_ppid); + process_info.SetUserID(proc_kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID(proc_kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID(proc_kinfo.kp_eproc.e_ucred.cr_uid); + if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID( + proc_kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID(UINT32_MAX); + return true; + } } - process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); - process_info.SetUserID (UINT32_MAX); - process_info.SetGroupID (UINT32_MAX); - process_info.SetEffectiveUserID (UINT32_MAX); - process_info.SetEffectiveGroupID (UINT32_MAX); - return false; + } + process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID); + process_info.SetUserID(UINT32_MAX); + process_info.SetGroupID(UINT32_MAX); + process_info.SetEffectiveUserID(UINT32_MAX); + process_info.SetEffectiveGroupID(UINT32_MAX); + return false; } +uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + std::vector<struct kinfo_proc> kinfos; -uint32_t -Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) -{ - std::vector<struct kinfo_proc> kinfos; - - int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; - - size_t pid_data_size = 0; - if (::sysctl (mib, 4, NULL, &pid_data_size, NULL, 0) != 0) - return 0; - - // Add a few extra in case a few more show up - const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; - - kinfos.resize (estimated_pid_count); - pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); - - if (::sysctl (mib, 4, &kinfos[0], &pid_data_size, NULL, 0) != 0) - return 0; - - const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); - - bool all_users = match_info.GetMatchAllUsers(); - const lldb::pid_t our_pid = getpid(); - const uid_t our_uid = getuid(); - for (size_t i = 0; i < actual_pid_count; i++) - { - const struct kinfo_proc &kinfo = kinfos[i]; - - bool kinfo_user_matches = false; - if (all_users) - kinfo_user_matches = true; - else - kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid; - - // Special case, if lldb is being run as root we can attach to anything. - if (our_uid == 0) - kinfo_user_matches = true; - - if (kinfo_user_matches == false || // Make sure the user is acceptable - static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) == our_pid || // Skip this process - kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero) - kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... - kinfo.kp_proc.p_flag & P_TRACED || // Being debugged? - kinfo.kp_proc.p_flag & P_WEXIT || // Working on exiting? - kinfo.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta) - continue; - - ProcessInstanceInfo process_info; - process_info.SetProcessID (kinfo.kp_proc.p_pid); - process_info.SetParentProcessID (kinfo.kp_eproc.e_ppid); - process_info.SetUserID (kinfo.kp_eproc.e_pcred.p_ruid); - process_info.SetGroupID (kinfo.kp_eproc.e_pcred.p_rgid); - process_info.SetEffectiveUserID (kinfo.kp_eproc.e_ucred.cr_uid); - if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0) - process_info.SetEffectiveGroupID (kinfo.kp_eproc.e_ucred.cr_groups[0]); - else - process_info.SetEffectiveGroupID (UINT32_MAX); - - // Make sure our info matches before we go fetch the name and cpu type - if (match_info.Matches (process_info)) - { - // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 - if (GetMacOSXProcessCPUType (process_info)) - { - if (GetMacOSXProcessArgs (&match_info, process_info)) - { - if (match_info.Matches (process_info)) - process_infos.Append (process_info); - } - } + int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; + + size_t pid_data_size = 0; + if (::sysctl(mib, 4, NULL, &pid_data_size, NULL, 0) != 0) + return 0; + + // Add a few extra in case a few more show up + const size_t estimated_pid_count = + (pid_data_size / sizeof(struct kinfo_proc)) + 10; + + kinfos.resize(estimated_pid_count); + pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); + + if (::sysctl(mib, 4, &kinfos[0], &pid_data_size, NULL, 0) != 0) + return 0; + + const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); + + bool all_users = match_info.GetMatchAllUsers(); + const lldb::pid_t our_pid = getpid(); + const uid_t our_uid = getuid(); + for (size_t i = 0; i < actual_pid_count; i++) { + const struct kinfo_proc &kinfo = kinfos[i]; + + bool kinfo_user_matches = false; + if (all_users) + kinfo_user_matches = true; + else + kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid; + + // Special case, if lldb is being run as root we can attach to anything. + if (our_uid == 0) + kinfo_user_matches = true; + + if (kinfo_user_matches == false || // Make sure the user is acceptable + static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) == + our_pid || // Skip this process + kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero) + kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... + kinfo.kp_proc.p_flag & P_TRACED || // Being debugged? + kinfo.kp_proc.p_flag & P_WEXIT || // Working on exiting? + kinfo.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta) + continue; + + ProcessInstanceInfo process_info; + process_info.SetProcessID(kinfo.kp_proc.p_pid); + process_info.SetParentProcessID(kinfo.kp_eproc.e_ppid); + process_info.SetUserID(kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID(kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID(kinfo.kp_eproc.e_ucred.cr_uid); + if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID(kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID(UINT32_MAX); + + // Make sure our info matches before we go fetch the name and cpu type + if (match_info.Matches(process_info)) { + // Get CPU type first so we can know to look for iOS simulator is we have + // x86 or x86_64 + if (GetMacOSXProcessCPUType(process_info)) { + if (GetMacOSXProcessArgs(&match_info, process_info)) { + if (match_info.Matches(process_info)) + process_infos.Append(process_info); } - } - return process_infos.GetSize(); + } + } + } + return process_infos.GetSize(); } -bool -Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) -{ - process_info.SetProcessID(pid); - bool success = false; - - // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 - if (GetMacOSXProcessCPUType (process_info)) - success = true; - - if (GetMacOSXProcessArgs (NULL, process_info)) - success = true; - - if (GetMacOSXProcessUserAndGroup (process_info)) - success = true; - - if (success) - return true; - - process_info.Clear(); - return false; +bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { + process_info.SetProcessID(pid); + bool success = false; + + // Get CPU type first so we can know to look for iOS simulator is we have x86 + // or x86_64 + if (GetMacOSXProcessCPUType(process_info)) + success = true; + + if (GetMacOSXProcessArgs(NULL, process_info)) + success = true; + + if (GetMacOSXProcessUserAndGroup(process_info)) + success = true; + + if (success) + return true; + + process_info.Clear(); + return false; } #if !NO_XPC_SERVICES -static void -PackageXPCArguments (xpc_object_t message, const char *prefix, const Args& args) -{ - size_t count = args.GetArgumentCount(); - char buf[50]; // long enough for 'argXXX' +static void PackageXPCArguments(xpc_object_t message, const char *prefix, + const Args &args) { + size_t count = args.GetArgumentCount(); + char buf[50]; // long enough for 'argXXX' + memset(buf, 0, 50); + sprintf(buf, "%sCount", prefix); + xpc_dictionary_set_int64(message, buf, count); + for (size_t i = 0; i < count; i++) { memset(buf, 0, 50); - sprintf(buf, "%sCount", prefix); - xpc_dictionary_set_int64(message, buf, count); - for (size_t i=0; i<count; i++) { - memset(buf, 0, 50); - sprintf(buf, "%s%zi", prefix, i); - xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i)); - } + sprintf(buf, "%s%zi", prefix, i); + xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i)); + } } /* - A valid authorizationRef means that + A valid authorizationRef means that - there is the LaunchUsingXPCRightName rights in the /etc/authorization - we have successfully copied the rights to be send over the XPC wire Once obtained, it will be valid for as long as the process lives. */ static AuthorizationRef authorizationRef = NULL; -static Error -getXPCAuthorization (ProcessLaunchInfo &launch_info) -{ - Error error; - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); - - if ((launch_info.GetUserID() == 0) && !authorizationRef) - { - OSStatus createStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); - if (createStatus != errAuthorizationSuccess) - { - error.SetError(1, eErrorTypeGeneric); - error.SetErrorString("Can't create authorizationRef."); - if (log) - { - error.PutToLog(log, "%s", error.AsCString()); - } - return error; - } - - OSStatus rightsStatus = AuthorizationRightGet(LaunchUsingXPCRightName, NULL); - if (rightsStatus != errAuthorizationSuccess) - { - // No rights in the security database, Create it with the right prompt. - CFStringRef prompt = CFSTR("Xcode is trying to take control of a root process."); - CFStringRef keys[] = { CFSTR("en") }; - CFTypeRef values[] = { prompt }; - CFDictionaryRef promptDict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFStringRef keys1[] = { CFSTR("class"), CFSTR("group"), CFSTR("comment"), CFSTR("default-prompt"), CFSTR("shared") }; - CFTypeRef values1[] = { CFSTR("user"), CFSTR("admin"), CFSTR(LaunchUsingXPCRightName), promptDict, kCFBooleanFalse }; - CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - rightsStatus = AuthorizationRightSet(authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL); - CFRelease(promptDict); - CFRelease(dict); - } - - OSStatus copyRightStatus = errAuthorizationDenied; - if (rightsStatus == errAuthorizationSuccess) - { - AuthorizationItem item1 = { LaunchUsingXPCRightName, 0, NULL, 0 }; - AuthorizationItem items[] = {item1}; - AuthorizationRights requestedRights = {1, items }; - AuthorizationFlags authorizationFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; - copyRightStatus = AuthorizationCopyRights(authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment, authorizationFlags, NULL); - } - - if (copyRightStatus != errAuthorizationSuccess) - { - // Eventually when the commandline supports running as root and the user is not - // logged in in the current audit session, we will need the trick in gdb where - // we ask the user to type in the root passwd in the terminal. - error.SetError(2, eErrorTypeGeneric); - error.SetErrorStringWithFormat("Launching as root needs root authorization."); - if (log) - { - error.PutToLog(log, "%s", error.AsCString()); - } - - if (authorizationRef) - { - AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); - authorizationRef = NULL; - } - } +static Error getXPCAuthorization(ProcessLaunchInfo &launch_info) { + Error error; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | + LIBLLDB_LOG_PROCESS)); + + if ((launch_info.GetUserID() == 0) && !authorizationRef) { + OSStatus createStatus = + AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, &authorizationRef); + if (createStatus != errAuthorizationSuccess) { + error.SetError(1, eErrorTypeGeneric); + error.SetErrorString("Can't create authorizationRef."); + if (log) { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; } - return error; + OSStatus rightsStatus = + AuthorizationRightGet(LaunchUsingXPCRightName, NULL); + if (rightsStatus != errAuthorizationSuccess) { + // No rights in the security database, Create it with the right prompt. + CFStringRef prompt = + CFSTR("Xcode is trying to take control of a root process."); + CFStringRef keys[] = {CFSTR("en")}; + CFTypeRef values[] = {prompt}; + CFDictionaryRef promptDict = CFDictionaryCreate( + kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CFStringRef keys1[] = {CFSTR("class"), CFSTR("group"), CFSTR("comment"), + CFSTR("default-prompt"), CFSTR("shared")}; + CFTypeRef values1[] = {CFSTR("user"), CFSTR("admin"), + CFSTR(LaunchUsingXPCRightName), promptDict, + kCFBooleanFalse}; + CFDictionaryRef dict = CFDictionaryCreate( + kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + rightsStatus = AuthorizationRightSet( + authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL); + CFRelease(promptDict); + CFRelease(dict); + } + + OSStatus copyRightStatus = errAuthorizationDenied; + if (rightsStatus == errAuthorizationSuccess) { + AuthorizationItem item1 = {LaunchUsingXPCRightName, 0, NULL, 0}; + AuthorizationItem items[] = {item1}; + AuthorizationRights requestedRights = {1, items}; + AuthorizationFlags authorizationFlags = + kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; + copyRightStatus = AuthorizationCopyRights( + authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment, + authorizationFlags, NULL); + } + + if (copyRightStatus != errAuthorizationSuccess) { + // Eventually when the commandline supports running as root and the user + // is not + // logged in in the current audit session, we will need the trick in gdb + // where + // we ask the user to type in the root passwd in the terminal. + error.SetError(2, eErrorTypeGeneric); + error.SetErrorStringWithFormat( + "Launching as root needs root authorization."); + if (log) { + error.PutToLog(log, "%s", error.AsCString()); + } + + if (authorizationRef) { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } + } + } + + return error; } #endif -static Error -LaunchProcessXPC(const char *exe_path, ProcessLaunchInfo &launch_info, lldb::pid_t &pid) -{ +static Error LaunchProcessXPC(const char *exe_path, + ProcessLaunchInfo &launch_info, + lldb::pid_t &pid) { #if !NO_XPC_SERVICES - Error error = getXPCAuthorization(launch_info); - if (error.Fail()) - return error; - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); - - uid_t requested_uid = launch_info.GetUserID(); - const char *xpc_service = nil; - bool send_auth = false; - AuthorizationExternalForm extForm; - if (requested_uid == 0) - { - if (AuthorizationMakeExternalForm(authorizationRef, &extForm) == errAuthorizationSuccess) - { - send_auth = true; - } - else - { - error.SetError(3, eErrorTypeGeneric); - error.SetErrorStringWithFormat("Launching root via XPC needs to externalize authorization reference."); - if (log) - { - error.PutToLog(log, "%s", error.AsCString()); - } - return error; - } - xpc_service = LaunchUsingXPCRightName; - } - else - { - error.SetError(4, eErrorTypeGeneric); - error.SetErrorStringWithFormat("Launching via XPC is only currently available for root."); - if (log) - { - error.PutToLog(log, "%s", error.AsCString()); - } - return error; - } - - xpc_connection_t conn = xpc_connection_create(xpc_service, NULL); - - xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { - xpc_type_t type = xpc_get_type(event); - - if (type == XPC_TYPE_ERROR) { - if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { - // The service has either canceled itself, crashed, or been terminated. - // The XPC connection is still valid and sending a message to it will re-launch the service. - // If the service is state-full, this is the time to initialize the new service. - return; - } else if (event == XPC_ERROR_CONNECTION_INVALID) { - // The service is invalid. Either the service name supplied to xpc_connection_create() is incorrect - // or we (this process) have canceled the service; we can do any cleanup of application state at this point. - // printf("Service disconnected"); - return; - } else { - // printf("Unexpected error from service: %s", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); - } - - } else { - // printf("Received unexpected event in handler"); - } - }); - - xpc_connection_set_finalizer_f (conn, xpc_finalizer_t(xpc_release)); - xpc_connection_resume (conn); - xpc_object_t message = xpc_dictionary_create (nil, nil, 0); - - if (send_auth) - { - xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes, sizeof(AuthorizationExternalForm)); - } - - PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, launch_info.GetArguments()); - PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey, launch_info.GetEnvironmentEntries()); - - // Posix spawn stuff. - xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType()); - xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, Host::GetPosixspawnFlags(launch_info)); - const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); - if (file_action && file_action->GetPath()) - { - xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, file_action->GetPath()); + Error error = getXPCAuthorization(launch_info); + if (error.Fail()) + return error; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | + LIBLLDB_LOG_PROCESS)); + + uid_t requested_uid = launch_info.GetUserID(); + const char *xpc_service = nil; + bool send_auth = false; + AuthorizationExternalForm extForm; + if (requested_uid == 0) { + if (AuthorizationMakeExternalForm(authorizationRef, &extForm) == + errAuthorizationSuccess) { + send_auth = true; + } else { + error.SetError(3, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Launching root via XPC needs to " + "externalize authorization reference."); + if (log) { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; } - file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); - if (file_action && file_action->GetPath()) - { - xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, file_action->GetPath()); + xpc_service = LaunchUsingXPCRightName; + } else { + error.SetError(4, eErrorTypeGeneric); + error.SetErrorStringWithFormat( + "Launching via XPC is only currently available for root."); + if (log) { + error.PutToLog(log, "%s", error.AsCString()); } - file_action = launch_info.GetFileActionForFD(STDERR_FILENO); - if (file_action && file_action->GetPath()) - { - xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, file_action->GetPath()); + return error; + } + + xpc_connection_t conn = xpc_connection_create(xpc_service, NULL); + + xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { + xpc_type_t type = xpc_get_type(event); + + if (type == XPC_TYPE_ERROR) { + if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { + // The service has either canceled itself, crashed, or been terminated. + // The XPC connection is still valid and sending a message to it will + // re-launch the service. + // If the service is state-full, this is the time to initialize the new + // service. + return; + } else if (event == XPC_ERROR_CONNECTION_INVALID) { + // The service is invalid. Either the service name supplied to + // xpc_connection_create() is incorrect + // or we (this process) have canceled the service; we can do any cleanup + // of application state at this point. + // printf("Service disconnected"); + return; + } else { + // printf("Unexpected error from service: %s", + // xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + } + + } else { + // printf("Received unexpected event in handler"); } - - xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message); - xpc_type_t returnType = xpc_get_type(reply); - if (returnType == XPC_TYPE_DICTIONARY) - { - pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey); - if (pid == 0) - { - int errorType = xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey); - int errorCode = xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey); - - error.SetError(errorCode, eErrorTypeGeneric); - error.SetErrorStringWithFormat("Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode); - if (log) - { - error.PutToLog(log, "%s", error.AsCString()); - } - - if (authorizationRef) - { - AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); - authorizationRef = NULL; - } - } + }); + + xpc_connection_set_finalizer_f(conn, xpc_finalizer_t(xpc_release)); + xpc_connection_resume(conn); + xpc_object_t message = xpc_dictionary_create(nil, nil, 0); + + if (send_auth) { + xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes, + sizeof(AuthorizationExternalForm)); + } + + PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, + launch_info.GetArguments()); + PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey, + launch_info.GetEnvironmentEntries()); + + // Posix spawn stuff. + xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, + launch_info.GetArchitecture().GetMachOCPUType()); + xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, + Host::GetPosixspawnFlags(launch_info)); + const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); + if (file_action && file_action->GetPath()) { + xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, + file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); + if (file_action && file_action->GetPath()) { + xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, + file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDERR_FILENO); + if (file_action && file_action->GetPath()) { + xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, + file_action->GetPath()); + } + + xpc_object_t reply = + xpc_connection_send_message_with_reply_sync(conn, message); + xpc_type_t returnType = xpc_get_type(reply); + if (returnType == XPC_TYPE_DICTIONARY) { + pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey); + if (pid == 0) { + int errorType = + xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey); + int errorCode = + xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey); + + error.SetError(errorCode, eErrorTypeGeneric); + error.SetErrorStringWithFormat( + "Problems with launching via XPC. Error type : %i, code : %i", + errorType, errorCode); + if (log) { + error.PutToLog(log, "%s", error.AsCString()); + } + + if (authorizationRef) { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } } - else if (returnType == XPC_TYPE_ERROR) - { - error.SetError(5, eErrorTypeGeneric); - error.SetErrorStringWithFormat("Problems with launching via XPC. XPC error : %s", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); - if (log) - { - error.PutToLog(log, "%s", error.AsCString()); - } + } else if (returnType == XPC_TYPE_ERROR) { + error.SetError(5, eErrorTypeGeneric); + error.SetErrorStringWithFormat( + "Problems with launching via XPC. XPC error : %s", + xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); + if (log) { + error.PutToLog(log, "%s", error.AsCString()); } - - return error; + } + + return error; #else - Error error; - return error; + Error error; + return error; #endif } -static bool -ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) -{ - bool result = false; - -#if !NO_XPC_SERVICES - bool launchingAsRoot = launch_info.GetUserID() == 0; - bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0; - - if (launchingAsRoot && !currentUserIsRoot) - { - // If current user is already root, we don't need XPC's help. - result = true; - } +static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) { + bool result = false; + +#if !NO_XPC_SERVICES + bool launchingAsRoot = launch_info.GetUserID() == 0; + bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0; + + if (launchingAsRoot && !currentUserIsRoot) { + // If current user is already root, we don't need XPC's help. + result = true; + } #endif - - return result; + + return result; } -Error -Host::LaunchProcess (ProcessLaunchInfo &launch_info) -{ - Error error; - char exe_path[PATH_MAX]; - PlatformSP host_platform_sp (Platform::GetHostPlatform ()); - - ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()); - - FileSpec::FileType file_type = exe_module_spec.GetFileSpec().GetFileType(); - if (file_type != FileSpec::eFileTypeRegular) - { - lldb::ModuleSP exe_module_sp; - error = host_platform_sp->ResolveExecutable (exe_module_spec, - exe_module_sp, - NULL); - - if (error.Fail()) - return error; - - if (exe_module_sp) - exe_module_spec.GetFileSpec() = exe_module_sp->GetFileSpec(); +Error Host::LaunchProcess(ProcessLaunchInfo &launch_info) { + Error error; + char exe_path[PATH_MAX]; + PlatformSP host_platform_sp(Platform::GetHostPlatform()); + + ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), + launch_info.GetArchitecture()); + + FileSpec::FileType file_type = exe_module_spec.GetFileSpec().GetFileType(); + if (file_type != FileSpec::eFileTypeRegular) { + lldb::ModuleSP exe_module_sp; + error = host_platform_sp->ResolveExecutable(exe_module_spec, exe_module_sp, + NULL); + + if (error.Fail()) + return error; + + if (exe_module_sp) + exe_module_spec.GetFileSpec() = exe_module_sp->GetFileSpec(); + } + + if (exe_module_spec.GetFileSpec().Exists()) { + exe_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + } else { + launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("executable doesn't exist: '%s'", exe_path); + return error; + } + + if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + return LaunchInNewTerminalWithAppleScript(exe_path, launch_info); +#else + error.SetErrorString("launching a process in a new terminal is not " + "supported on iOS devices"); + return error; +#endif + } + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (ShouldLaunchUsingXPC(launch_info)) { + error = LaunchProcessXPC(exe_path, launch_info, pid); + } else { + error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); + } + + if (pid != LLDB_INVALID_PROCESS_ID) { + // If all went well, then set the process ID into the launch info + launch_info.SetProcessID(pid); + + // Make sure we reap any processes we spawn or we will have zombies. + if (!launch_info.MonitorProcess()) { + const bool monitor_signals = false; + Host::MonitorChildProcessCallback callback = nullptr; + + if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus)) + callback = Process::SetProcessExitStatus; + + StartMonitoringChildProcess(callback, pid, monitor_signals); } - - if (exe_module_spec.GetFileSpec().Exists()) - { - exe_module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path)); + } else { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString("process launch failed for unknown reasons"); + } + return error; +} + +Error Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { + Error error; + if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) { + FileSpec expand_tool_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, + expand_tool_spec)) { + error.SetErrorString( + "could not get support executable directory for lldb-argdumper tool"); + return error; } - else - { - launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); - error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); - return error; + expand_tool_spec.AppendPathComponent("lldb-argdumper"); + if (!expand_tool_spec.Exists()) { + error.SetErrorStringWithFormat( + "could not find the lldb-argdumper tool: %s", + expand_tool_spec.GetPath().c_str()); + return error; } - - if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) - { -#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) - return LaunchInNewTerminalWithAppleScript (exe_path, launch_info); -#else - error.SetErrorString ("launching a process in a new terminal is not supported on iOS devices"); + + StreamString expand_tool_spec_stream; + expand_tool_spec_stream.Printf("\"%s\"", + expand_tool_spec.GetPath().c_str()); + + Args expand_command(expand_tool_spec_stream.GetData()); + expand_command.AppendArguments(launch_info.GetArguments()); + + int status; + std::string output; + FileSpec cwd(launch_info.GetWorkingDirectory()); + if (!cwd.Exists()) { + char *wd = getcwd(nullptr, 0); + if (wd == nullptr) { + error.SetErrorStringWithFormat( + "cwd does not exist; cannot launch with shell argument expansion"); return error; -#endif + } else { + FileSpec working_dir(wd, false); + free(wd); + launch_info.SetWorkingDirectory(working_dir); + } } + RunShellCommand(expand_command, cwd, &status, nullptr, &output, 10); - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + if (status != 0) { + error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", + status); + return error; + } - if (ShouldLaunchUsingXPC(launch_info)) - { - error = LaunchProcessXPC(exe_path, launch_info, pid); + auto data_sp = StructuredData::ParseJSON(output); + if (!data_sp) { + error.SetErrorString("invalid JSON"); + return error; } - else - { - error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); + + auto dict_sp = data_sp->GetAsDictionary(); + if (!data_sp) { + error.SetErrorString("invalid JSON"); + return error; } - - if (pid != LLDB_INVALID_PROCESS_ID) - { - // If all went well, then set the process ID into the launch info - launch_info.SetProcessID(pid); - - // Make sure we reap any processes we spawn or we will have zombies. - if (!launch_info.MonitorProcess()) - { - const bool monitor_signals = false; - Host::MonitorChildProcessCallback callback = nullptr; - - if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus)) - callback = Process::SetProcessExitStatus; - - StartMonitoringChildProcess (callback, - pid, - monitor_signals); - } + + auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); + if (!args_sp) { + error.SetErrorString("invalid JSON"); + return error; } - else - { - // Invalid process ID, something didn't go well - if (error.Success()) - error.SetErrorString ("process launch failed for unknown reasons"); + + auto args_array_sp = args_sp->GetAsArray(); + if (!args_array_sp) { + error.SetErrorString("invalid JSON"); + return error; } - return error; -} -Error -Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) -{ - Error error; - if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) - { - FileSpec expand_tool_spec; - if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec)) - { - error.SetErrorString("could not get support executable directory for lldb-argdumper tool"); - return error; - } - expand_tool_spec.AppendPathComponent("lldb-argdumper"); - if (!expand_tool_spec.Exists()) - { - error.SetErrorStringWithFormat("could not find the lldb-argdumper tool: %s", expand_tool_spec.GetPath().c_str()); - return error; - } - - StreamString expand_tool_spec_stream; - expand_tool_spec_stream.Printf("\"%s\"",expand_tool_spec.GetPath().c_str()); - - Args expand_command(expand_tool_spec_stream.GetData()); - expand_command.AppendArguments (launch_info.GetArguments()); - - int status; - std::string output; - FileSpec cwd(launch_info.GetWorkingDirectory()); - if (!cwd.Exists()) - { - char *wd = getcwd(nullptr, 0); - if (wd == nullptr) - { - error.SetErrorStringWithFormat("cwd does not exist; cannot launch with shell argument expansion"); - return error; - } - else - { - FileSpec working_dir(wd, false); - free(wd); - launch_info.SetWorkingDirectory(working_dir); + launch_info.GetArguments().Clear(); - } - } - RunShellCommand(expand_command, cwd, &status, nullptr, &output, 10); - - if (status != 0) - { - error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status); - return error; - } - - auto data_sp = StructuredData::ParseJSON(output); - if (!data_sp) - { - error.SetErrorString("invalid JSON"); - return error; - } - - auto dict_sp = data_sp->GetAsDictionary(); - if (!data_sp) - { - error.SetErrorString("invalid JSON"); - return error; - } - - auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); - if (!args_sp) - { - error.SetErrorString("invalid JSON"); - return error; - } + for (size_t i = 0; i < args_array_sp->GetSize(); i++) { + auto item_sp = args_array_sp->GetItemAtIndex(i); + if (!item_sp) + continue; + auto str_sp = item_sp->GetAsString(); + if (!str_sp) + continue; - auto args_array_sp = args_sp->GetAsArray(); - if (!args_array_sp) - { - error.SetErrorString("invalid JSON"); - return error; - } - - launch_info.GetArguments().Clear(); - - for (size_t i = 0; - i < args_array_sp->GetSize(); - i++) - { - auto item_sp = args_array_sp->GetItemAtIndex(i); - if (!item_sp) - continue; - auto str_sp = item_sp->GetAsString(); - if (!str_sp) - continue; - - launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); - } + launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); } - - return error; -} + } -HostThread -Host::StartMonitoringChildProcess(const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid, - bool monitor_signals) -{ - unsigned long mask = DISPATCH_PROC_EXIT; - if (monitor_signals) - mask |= DISPATCH_PROC_SIGNAL; + return error; +} - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); +HostThread Host::StartMonitoringChildProcess( + const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid, + bool monitor_signals) { + unsigned long mask = DISPATCH_PROC_EXIT; + if (monitor_signals) + mask |= DISPATCH_PROC_SIGNAL; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_HOST | + LIBLLDB_LOG_PROCESS)); + + dispatch_source_t source = ::dispatch_source_create( + DISPATCH_SOURCE_TYPE_PROC, pid, mask, + ::dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); + + if (log) + log->Printf("Host::StartMonitoringChildProcess " + "(callback, pid=%i, monitor_signals=%i) " + "source = %p\n", + static_cast<int>(pid), monitor_signals, + reinterpret_cast<void *>(source)); + + if (source) { + Host::MonitorChildProcessCallback callback_copy = callback; + ::dispatch_source_set_cancel_handler(source, ^{ + ::dispatch_release(source); + }); + ::dispatch_source_set_event_handler(source, ^{ + + int status = 0; + int wait_pid = 0; + bool cancel = false; + bool exited = false; + do { + wait_pid = ::waitpid(pid, &status, 0); + } while (wait_pid < 0 && errno == EINTR); + + if (wait_pid >= 0) { + int signal = 0; + int exit_status = 0; + const char *status_cstr = NULL; + if (WIFSTOPPED(status)) { + signal = WSTOPSIG(status); + status_cstr = "STOPPED"; + } else if (WIFEXITED(status)) { + exit_status = WEXITSTATUS(status); + status_cstr = "EXITED"; + exited = true; + } else if (WIFSIGNALED(status)) { + signal = WTERMSIG(status); + status_cstr = "SIGNALED"; + exited = true; + exit_status = -1; + } else { + status_cstr = "???"; + } + if (log) + log->Printf("::waitpid (pid = %llu, &status, 0) => pid = %i, status " + "= 0x%8.8x (%s), signal = %i, exit_status = %i", + pid, wait_pid, status, status_cstr, signal, exit_status); - dispatch_source_t source = ::dispatch_source_create (DISPATCH_SOURCE_TYPE_PROC, - pid, - mask, - ::dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT,0)); + if (callback_copy) + cancel = callback_copy(pid, exited, signal, exit_status); - if (log) - log->Printf("Host::StartMonitoringChildProcess " - "(callback, pid=%i, monitor_signals=%i) " - "source = %p\n", - static_cast<int>(pid), monitor_signals, reinterpret_cast<void *>(source)); - - if (source) - { - Host::MonitorChildProcessCallback callback_copy = callback; - ::dispatch_source_set_cancel_handler (source, ^{ - ::dispatch_release (source); - }); - ::dispatch_source_set_event_handler (source, ^{ - - int status= 0; - int wait_pid = 0; - bool cancel = false; - bool exited = false; - do - { - wait_pid = ::waitpid (pid, &status, 0); - } while (wait_pid < 0 && errno == EINTR); - - if (wait_pid >= 0) - { - int signal = 0; - int exit_status = 0; - const char *status_cstr = NULL; - if (WIFSTOPPED(status)) - { - signal = WSTOPSIG(status); - status_cstr = "STOPPED"; - } - else if (WIFEXITED(status)) - { - exit_status = WEXITSTATUS(status); - status_cstr = "EXITED"; - exited = true; - } - else if (WIFSIGNALED(status)) - { - signal = WTERMSIG(status); - status_cstr = "SIGNALED"; - exited = true; - exit_status = -1; - } - else - { - status_cstr = "???"; - } - - if (log) - log->Printf ("::waitpid (pid = %llu, &status, 0) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_status = %i", - pid, - wait_pid, - status, - status_cstr, - signal, - exit_status); - - if (callback_copy) - cancel = callback_copy(pid, exited, signal, exit_status); - - if (exited || cancel) - { - ::dispatch_source_cancel(source); - } - } - }); + if (exited || cancel) { + ::dispatch_source_cancel(source); + } + } + }); - ::dispatch_resume (source); - } - return HostThread(); + ::dispatch_resume(source); + } + return HostThread(); } //---------------------------------------------------------------------- // Log to both stderr and to ASL Logging when running on MacOSX. //---------------------------------------------------------------------- -void -Host::SystemLog (SystemLogType type, const char *format, va_list args) -{ - if (format && format[0]) - { - static aslmsg g_aslmsg = NULL; - if (g_aslmsg == NULL) - { - g_aslmsg = ::asl_new (ASL_TYPE_MSG); - char asl_key_sender[PATH_MAX]; - snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.LLDB.framework"); - ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); - } - - // Copy the va_list so we can log this message twice - va_list copy_args; - va_copy (copy_args, args); - // Log to stderr - ::vfprintf (stderr, format, copy_args); - va_end (copy_args); - - int asl_level; - switch (type) - { - case eSystemLogError: - asl_level = ASL_LEVEL_ERR; - break; - - case eSystemLogWarning: - asl_level = ASL_LEVEL_WARNING; - break; - } - - // Log to ASL - ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); +void Host::SystemLog(SystemLogType type, const char *format, va_list args) { + if (format && format[0]) { + static aslmsg g_aslmsg = NULL; + if (g_aslmsg == NULL) { + g_aslmsg = ::asl_new(ASL_TYPE_MSG); + char asl_key_sender[PATH_MAX]; + snprintf(asl_key_sender, sizeof(asl_key_sender), + "com.apple.LLDB.framework"); + ::asl_set(g_aslmsg, ASL_KEY_SENDER, asl_key_sender); } + + // Copy the va_list so we can log this message twice + va_list copy_args; + va_copy(copy_args, args); + // Log to stderr + ::vfprintf(stderr, format, copy_args); + va_end(copy_args); + + int asl_level; + switch (type) { + case eSystemLogError: + asl_level = ASL_LEVEL_ERR; + break; + + case eSystemLogWarning: + asl_level = ASL_LEVEL_WARNING; + break; + } + + // Log to ASL + ::asl_vlog(NULL, g_aslmsg, asl_level, format, args); + } } -lldb::DataBufferSP -Host::GetAuxvData(lldb_private::Process *process) -{ - return lldb::DataBufferSP(); +lldb::DataBufferSP Host::GetAuxvData(lldb_private::Process *process) { + return lldb::DataBufferSP(); } |