Index: lldb/trunk/include/lldb/module.modulemap =================================================================== --- lldb/trunk/include/lldb/module.modulemap +++ lldb/trunk/include/lldb/module.modulemap @@ -0,0 +1,139 @@ + +module lldb_API { + requires cplusplus + + umbrella "API" + module * { export * } +} + +module lldb_Host { + requires cplusplus + + // Because we have OS-specific headers in Host, we just list + // all OS-independent headers here that will include the correct + // OS-specific header for us. + module ConnectionFileDescriptor { header "Host/ConnectionFileDescriptor.h" export * } + module Debug { header "Host/Debug.h" export * } + module Editline { header "Host/Editline.h" export * } + module FileCache { header "Host/FileCache.h" export * } + module File { header "Host/File.h" export * } + module FileSystem { header "Host/FileSystem.h" export * } + module HostGetOpt { header "Host/HostGetOpt.h" export * } + module Host { header "Host/Host.h" export * } + module HostInfoBase { header "Host/HostInfoBase.h" export * } + module HostInfo { header "Host/HostInfo.h" export * } + module HostNativeProcessBase { header "Host/HostNativeProcessBase.h" export * } + module HostNativeProcess { header "Host/HostNativeProcess.h" export * } + module HostNativeThreadBase { header "Host/HostNativeThreadBase.h" export * } + module HostNativeThreadForward { header "Host/HostNativeThreadForward.h" export * } + module HostNativeThread { header "Host/HostNativeThread.h" export * } + module HostProcess { header "Host/HostProcess.h" export * } + module HostThread { header "Host/HostThread.h" export * } + module LockFileBase { header "Host/LockFileBase.h" export * } + module LockFile { header "Host/LockFile.h" export * } + module MainLoopBase { header "Host/MainLoopBase.h" export * } + module MainLoop { header "Host/MainLoop.h" export * } + module MonitoringProcessLauncher { header "Host/MonitoringProcessLauncher.h" export * } + module OptionParser { header "Host/OptionParser.h" export * } + module PipeBase { header "Host/PipeBase.h" export * } + module Pipe { header "Host/Pipe.h" export * } + module PosixApi { header "Host/PosixApi.h" export * } + module Predicate { header "Host/Predicate.h" export * } + module ProcessLauncher { header "Host/ProcessLauncher.h" export * } + module ProcessRunLock { header "Host/ProcessRunLock.h" export * } + module PseudoTerminal { header "Host/PseudoTerminal.h" export * } + module SocketAddress { header "Host/SocketAddress.h" export * } + module Socket { header "Host/Socket.h" export * } + module StringConvert { header "Host/StringConvert.h" export * } + module Symbols { header "Host/Symbols.h" export * } + module TaskPool { header "Host/TaskPool.h" export * } + module Terminal { header "Host/Terminal.h" export * } + module ThreadLauncher { header "Host/ThreadLauncher.h" export * } + module Time { header "Host/Time.h" export * } + module XML { header "Host/XML.h" export * } + + export * +} + +module lldb_Initialization { + requires cplusplus + + umbrella "Initialization" + module * { export * } +} + + +module lldb_Wrapper { + + module lldb_Breakpoint { + requires cplusplus + + umbrella "Breakpoint" + module * { export * } + } + + module lldb_Core { + requires cplusplus + + umbrella "Core" + module * { export * } + } + + module lldb_DataFormatters { + requires cplusplus + + umbrella "DataFormatters" + module * { export * } + } + + module lldb_Expression { + requires cplusplus + + umbrella "Expression" + module * { export * } + // TODO: This file includes a non-public header. + exclude header "Expression/REPL.h" + } + + module lldb_Interpreter { + requires cplusplus + + umbrella "Interpreter" + module * { export * } + } + + module lldb_Symbol { + requires cplusplus + + umbrella "Symbol" + module * { export * } + } + module lldb_Target { + requires cplusplus + + umbrella "Target" + module * { export * } + } +} + + +module lldb_Utility { + requires cplusplus + + umbrella "Utility" + module * { export * } + + module lldb_defines { header "lldb-defines.h" export * } + module lldb_enumerations { header "lldb-enumerations.h" export * } + module lldb_forward { header "lldb-forward.h" export * } + module lldb_private_enumerations { header "lldb-private-enumerations.h" export * } + module lldb_private_defines { header "lldb-private-defines.h" export * } + module lldb_private_forward { header "lldb-private-forward.h" export * } + module lldb_private { header "lldb-private.h" export * } + module lldb_private_interfaces { header "lldb-private-interfaces.h" export * } + module lldb_private_types { header "lldb-private-types.h" export * } + module lldb_public { header "lldb-public.h" export * } + module lldb_types { header "lldb-types.h" export * } + module lldb_versioning { header "lldb-versioning.h" export * } + +} Index: lldb/trunk/source/Host/CMakeLists.txt =================================================================== --- lldb/trunk/source/Host/CMakeLists.txt +++ lldb/trunk/source/Host/CMakeLists.txt @@ -3,6 +3,18 @@ source_group(${group} FILES ${ARGN}) endmacro() +# Removes all module flags from the current CMAKE_CXX_FLAGS. Used for +# the Objective-C++ code in lldb which we don't want to build with modules. +# Reasons for this are that modules with Objective-C++ would require that +# all LLVM/Clang modules are Objective-C++ compatible (which they are likely +# not) and we would have rebuild a second set of modules just for the few +# Objective-C++ files in lldb (which slows down the build process). +macro(remove_module_flags) + string(REGEX REPLACE "-fmodules-cache-path=[^ ]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REGEX REPLACE "-fmodules" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REGEX REPLACE "-fcxx-modules" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +endmacro() + add_host_subdirectory(common common/File.cpp common/FileCache.cpp @@ -92,10 +104,9 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin") include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) - add_host_subdirectory(macosx - macosx/Host.mm - macosx/HostInfoMacOSX.mm - macosx/HostThreadMacOSX.mm + add_subdirectory(macosx/objcxx) + set(LLDBObjCLibs lldbHostMacOSXObjCXX) + add_host_subdirectory(maqcosx macosx/Symbols.cpp macosx/cfcpp/CFCBundle.cpp macosx/cfcpp/CFCData.cpp @@ -177,7 +188,7 @@ add_lldb_library(lldbHost ${HOST_SOURCES} - + LINK_LIBS lldbCore lldbSymbol @@ -185,7 +196,8 @@ lldbUtility ${LLDB_PLUGINS} ${EXTRA_LIBS} - + ${LLDBObjCLibs} + LINK_COMPONENTS Object Support Index: lldb/trunk/source/Host/common/Terminal.cpp =================================================================== --- lldb/trunk/source/Host/common/Terminal.cpp +++ lldb/trunk/source/Host/common/Terminal.cpp @@ -9,6 +9,7 @@ #include "lldb/Host/Terminal.h" +#include "lldb/Host/Config.h" #include "lldb/Host/PosixApi.h" #include "llvm/ADT/STLExtras.h" Index: lldb/trunk/source/Host/macosx/Host.mm =================================================================== --- lldb/trunk/source/Host/macosx/Host.mm +++ lldb/trunk/source/Host/macosx/Host.mm @@ -1,1530 +0,0 @@ -//===-- Host.mm -------------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Host/Host.h" - -#include - -#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) -#define __XPC_PRIVATE_H__ -#include - -#define LaunchUsingXPCRightName "com.apple.lldb.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" - -#endif - -#include "llvm/Support/Host.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lldb/Host/ConnectionFileDescriptor.h" -#include "lldb/Host/HostInfo.h" -#include "lldb/Host/ThreadLauncher.h" -#include "lldb/Target/ProcessLaunchInfo.h" -#include "lldb/Target/Process.h" -#include "lldb/Utility/ArchSpec.h" -#include "lldb/Utility/CleanUp.h" -#include "lldb/Utility/DataBufferHeap.h" -#include "lldb/Utility/DataExtractor.h" -#include "lldb/Utility/Endian.h" -#include "lldb/Utility/FileSpec.h" -#include "lldb/Utility/Log.h" -#include "lldb/Utility/NameMatches.h" -#include "lldb/Utility/StreamString.h" -#include "lldb/Utility/StructuredData.h" -#include "lldb/lldb-defines.h" - -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Errno.h" - -#include "cfcpp/CFCBundle.h" -#include "cfcpp/CFCMutableArray.h" -#include "cfcpp/CFCMutableDictionary.h" -#include "cfcpp/CFCReleaser.h" -#include "cfcpp/CFCString.h" - -#include - -#include -#include - -#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); -} - -using namespace lldb; -using namespace lldb_private; - -bool Host::GetBundleDirectory(const FileSpec &file, - FileSpec &bundle_directory) { -#if defined(__APPLE__) - if (llvm::sys::fs::is_directory(file.GetPath())) { - 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; -} - -bool Host::ResolveExecutableInBundle(FileSpec &file) { -#if defined(__APPLE__) - if (llvm::sys::fs::is_directory(file.GetPath())) { - char path[PATH_MAX]; - if (file.GetPath(path, sizeof(path))) { - CFCBundle bundle(path); - CFCReleaser 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; - Status 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), std::chrono::seconds(0), status, NULL); - if (pid_str_len > 0) { - int pid = atoi(pid_str); - return (void *)(intptr_t)pid; - } - } - 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); - } - return false; -} -#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) - -const char *applscript_in_new_tty = "tell application \"Terminal\"\n" - " activate\n" - " do script \"/bin/bash -c '%s';exit\"\n" - "end tell\n"; - -const char *applscript_in_existing_tty = "\ -set the_shell_script to \"/bin/bash -c '%s';exit\"\n\ -tell application \"Terminal\"\n\ - repeat with the_window in (get windows)\n\ - repeat with the_tab in tabs of the_window\n\ - set the_tty to tty in the_tab\n\ - if the_tty contains \"%s\" then\n\ - if the_tab is not busy then\n\ - set selected of the_tab to true\n\ - set frontmost of the_window to true\n\ - do script the_shell_script in the_tab\n\ - return\n\ - end if\n\ - end if\n\ - end repeat\n\ - end repeat\n\ - do script the_shell_script\n\ -end tell\n"; - -static Status -LaunchInNewTerminalWithAppleScript(const char *exe_path, - ProcessLaunchInfo &launch_info) { - Status 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()); - - 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. - Environment host_env = Host::GetEnvironment(); - - for (const auto &KV : launch_info.GetEnvironment()) { - auto host_entry = host_env.find(KV.first()); - if (host_entry == host_env.end() || host_entry->second != KV.second) - command.Format(" --env='{0}'", Environment::compose(KV)); - } - - 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; - - applescript_source.Printf(applscript_in_new_tty, - command.GetString().str().c_str()); - NSAppleScript *applescript = [[NSAppleScript alloc] - initWithSource:[NSString stringWithCString:applescript_source.GetString() - .str() - .c_str() - encoding:NSUTF8StringEncoding]]; - - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; - - Status 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); - } - - llvm::sys::fs::remove(unix_socket_name); - [applescript release]; - if (pid != LLDB_INVALID_PROCESS_ID) - launch_info.SetProcessID(pid); - return error; -} - -#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) - -bool Host::OpenFileInExternalEditor(const FileSpec &file_spec, - uint32_t line_no) { -#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - 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 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("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); - - 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; - } - - ProcessSerialNumber psn; - CFCReleaser 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); - - return false; - } - - return true; -#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) -} - -Environment Host::GetEnvironment() { return Environment(*_NSGetEnviron()); } - -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; - } - } - 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); - 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(argc); ++i) { - cstr = data.GetCStr(&offset); - if (cstr) - proc_args.AppendArgument(llvm::StringRef(cstr)); - } - - Environment &proc_env = process_info.GetEnvironment(); - 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.insert(cstr); - } - return true; - } - } - } - } - 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; - } - } - } - 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 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(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(); -} - -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' - 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)); - } -} - -static void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix, - const Environment &env) { - xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(), - env.size()); - size_t i = 0; - for (const auto &KV : env) { - xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(), - Environment::compose(KV).c_str()); - } -} - -/* - 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 Status getXPCAuthorization(ProcessLaunchInfo &launch_info) { - Status 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."); - LLDB_LOG(log, "error: {0}", 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."); - LLDB_LOG(log, "error: {0}", error); - - if (authorizationRef) { - AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); - authorizationRef = NULL; - } - } - } - - return error; -} -#endif - -static short GetPosixspawnFlags(const 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(__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 (HostInfo::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 // defined(__x86_64__) || defined(__i386__) - // 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 // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT - return flags; -} - -static Status LaunchProcessXPC(const char *exe_path, - ProcessLaunchInfo &launch_info, - lldb::pid_t &pid) { -#if !NO_XPC_SERVICES - Status 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."); - LLDB_LOG(log, "error: {0}", error); - return error; - } - xpc_service = LaunchUsingXPCRightName; - } else { - error.SetError(4, eErrorTypeGeneric); - error.SetErrorStringWithFormat( - "Launching via XPC is only currently available for root."); - LLDB_LOG(log, "error: {0}", error); - 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()); - PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey, - launch_info.GetEnvironment()); - - // Posix spawn stuff. - xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, - launch_info.GetArchitecture().GetMachOCPUType()); - xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, - GetPosixspawnFlags(launch_info)); - const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); - if (file_action && !file_action->GetPath().empty()) { - xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, - file_action->GetPath().str().c_str()); - } - file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); - if (file_action && !file_action->GetPath().empty()) { - xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, - file_action->GetPath().str().c_str()); - } - file_action = launch_info.GetFileActionForFD(STDERR_FILENO); - if (file_action && !file_action->GetPath().empty()) { - xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, - file_action->GetPath().str().c_str()); - } - - 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); - LLDB_LOG(log, "error: {0}", error); - - 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)); - LLDB_LOG(log, "error: {0}", error); - } - - return error; -#else - Status error; - return error; -#endif -} - -static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, - Log *log, Status &error) { - if (info == NULL) - return false; - - posix_spawn_file_actions_t *file_actions = - reinterpret_cast(_file_actions); - - switch (info->GetAction()) { - case FileAction::eFileActionNone: - error.Clear(); - break; - - case FileAction::eFileActionClose: - if (info->GetFD() == -1) - error.SetErrorString( - "invalid fd for posix_spawn_file_actions_addclose(...)"); - else { - error.SetError( - ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), - eErrorTypePOSIX); - if (error.Fail()) - LLDB_LOG(log, - "error: {0}, posix_spawn_file_actions_addclose " - "(action={1}, fd={2})", - error, file_actions, info->GetFD()); - } - break; - - case FileAction::eFileActionDuplicate: - if (info->GetFD() == -1) - error.SetErrorString( - "invalid fd for posix_spawn_file_actions_adddup2(...)"); - else if (info->GetActionArgument() == -1) - error.SetErrorString( - "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); - else { - error.SetError( - ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), - info->GetActionArgument()), - eErrorTypePOSIX); - if (error.Fail()) - LLDB_LOG(log, - "error: {0}, posix_spawn_file_actions_adddup2 " - "(action={1}, fd={2}, dup_fd={3})", - error, file_actions, info->GetFD(), info->GetActionArgument()); - } - break; - - case FileAction::eFileActionOpen: - if (info->GetFD() == -1) - error.SetErrorString( - "invalid fd in posix_spawn_file_actions_addopen(...)"); - else { - int oflag = info->GetActionArgument(); - - mode_t mode = 0; - - if (oflag & O_CREAT) - mode = 0640; - - error.SetError(::posix_spawn_file_actions_addopen( - file_actions, info->GetFD(), - info->GetPath().str().c_str(), oflag, mode), - eErrorTypePOSIX); - if (error.Fail()) - LLDB_LOG(log, - "error: {0}, posix_spawn_file_actions_addopen (action={1}, " - "fd={2}, path='{3}', oflag={4}, mode={5})", - error, file_actions, info->GetFD(), info->GetPath(), oflag, - mode); - } - break; - } - return error.Success(); -} - -static Status LaunchProcessPosixSpawn(const char *exe_path, - const ProcessLaunchInfo &launch_info, - lldb::pid_t &pid) { - Status 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()) { - LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error); - return error; - } - - // Make sure we clean up the posix spawn attributes before exiting this scope. - CleanUp cleanup_attr(posix_spawnattr_destroy, &attr); - - 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()) { - LLDB_LOG(log, - "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )", - error, flags); - return error; - } - -// 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(__arm__) - - // Don't set the binpref if a shell was provided. After all, that's only - // going to affect what version of the shell - // is launched, not what fork of the binary is launched. We insert "arch - // --arch as part of the shell invocation - // to do that job on OSX. - - if (launch_info.GetShell() == nullptr) { - // 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 != static_cast(UINT32_MAX) && - cpu != static_cast(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()) - LLDB_LOG(log, - "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, " - "cpu_type = {1:x}, count => {2} )", - error, cpu, ocount); - - if (error.Fail() || ocount != 1) - return error; - } - } -#endif // !defined(__arm__) - - const char *tmp_argv[2]; - char *const *argv = const_cast( - launch_info.GetArguments().GetConstArgumentVector()); - Environment::Envp envp = launch_info.GetEnvironment().getEnvp(); - 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 = const_cast(tmp_argv); - } - - FileSpec working_dir{launch_info.GetWorkingDirectory()}; - if (working_dir) { - // Set the working directory on this thread only - if (__pthread_chdir(working_dir.GetCString()) < 0) { - if (errno == ENOENT) { - error.SetErrorStringWithFormat("No such file or directory: %s", - working_dir.GetCString()); - } else if (errno == ENOTDIR) { - error.SetErrorStringWithFormat("Path doesn't name a directory: %s", - working_dir.GetCString()); - } else { - error.SetErrorStringWithFormat("An unknown error occurred when " - "changing directory for process " - "execution."); - } - return error; - } - } - - ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; - 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()) { - LLDB_LOG(log, - "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )", - error); - return error; - } - - // Make sure we clean up the posix file actions before exiting this scope. - CleanUp cleanup_fileact(posix_spawn_file_actions_destroy, &file_actions); - - for (size_t i = 0; i < num_file_actions; ++i) { - const FileAction *launch_file_action = - launch_info.GetFileActionAtIndex(i); - if (launch_file_action) { - if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, - error)) - return error; - } - } - - error.SetError( - ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), - eErrorTypePOSIX); - - if (error.Fail()) { - LLDB_LOG(log, - "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', " - "file_actions = {3}, " - "attr = {4}, argv = {5}, envp = {6} )", - error, result_pid, exe_path, &file_actions, &attr, argv, - envp.get()); - if (log) { - for (int ii = 0; argv[ii]; ++ii) - LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); - } - } - - } else { - error.SetError( - ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), - eErrorTypePOSIX); - - if (error.Fail()) { - LLDB_LOG(log, - "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', " - "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )", - error, result_pid, exe_path, &attr, argv, envp.get()); - if (log) { - for (int ii = 0; argv[ii]; ++ii) - LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); - } - } - } - pid = result_pid; - - if (working_dir) { - // No more thread specific current working directory - __pthread_fchdir(-1); - } - - return error; -} - -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; -} - -Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) { - Status error; - FileSpec exe_spec(launch_info.GetExecutableFile()); - - llvm::sys::fs::file_status stats; - status(exe_spec.GetPath(), stats); - if (!exists(stats)) { - exe_spec.ResolvePath(); - status(exe_spec.GetPath(), stats); - } - if (!exists(stats)) { - exe_spec.ResolveExecutableLocation(); - status(exe_spec.GetPath(), stats); - } - if (!exists(stats)) { - error.SetErrorStringWithFormatv("executable doesn't exist: '{0}'", - launch_info.GetExecutableFile()); - return error; - } - - if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { -#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) - return LaunchInNewTerminalWithAppleScript(exe_spec.GetPath().c_str(), - 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_spec.GetPath().c_str(), launch_info, pid); - } else { - error = - LaunchProcessPosixSpawn(exe_spec.GetPath().c_str(), 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. - bool monitoring = launch_info.MonitorProcess(); - UNUSED_IF_ASSERT_DISABLED(monitoring); - assert(monitoring); - } else { - // Invalid process ID, something didn't go well - if (error.Success()) - error.SetErrorString("process launch failed for unknown reasons"); - } - return error; -} - -Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { - Status 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); - } - } - RunShellCommand(expand_command, cwd, &status, nullptr, &output, - std::chrono::seconds(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; - } - - 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()); - } - } - - 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; - - 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(pid), monitor_signals, - reinterpret_cast(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; - wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, 0); - 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); - } - } - }); - - ::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); - } -} Index: lldb/trunk/source/Host/macosx/HostInfoMacOSX.mm =================================================================== --- lldb/trunk/source/Host/macosx/HostInfoMacOSX.mm +++ lldb/trunk/source/Host/macosx/HostInfoMacOSX.mm @@ -1,319 +0,0 @@ -//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#if !defined(LLDB_DISABLE_PYTHON) -#include "Plugins/ScriptInterpreter/Python/lldb-python.h" -#endif - -#include "lldb/Host/HostInfo.h" -#include "lldb/Host/macosx/HostInfoMacOSX.h" -#include "lldb/Utility/Args.h" -#include "lldb/Utility/Log.h" -#include "lldb/Utility/SafeMachO.h" - -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" - -// C++ Includes -#include - -// C inclues -#include -#include -#include -#include - -// Objective C/C++ includes -#include -#include -#include -#include - -// These are needed when compiling on systems -// that do not yet have these definitions -#include -#ifndef CPU_SUBTYPE_X86_64_H -#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) -#endif -#ifndef CPU_TYPE_ARM64 -#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) -#endif - -#include // for TARGET_OS_TV, TARGET_OS_WATCH - -using namespace lldb_private; - -bool HostInfoMacOSX::GetOSBuildString(std::string &s) { - int mib[2] = {CTL_KERN, KERN_OSVERSION}; - char cstr[PATH_MAX]; - size_t cstr_len = sizeof(cstr); - if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { - s.assign(cstr, cstr_len); - return true; - } - - s.clear(); - return false; -} - -bool HostInfoMacOSX::GetOSKernelDescription(std::string &s) { - int mib[2] = {CTL_KERN, KERN_VERSION}; - char cstr[PATH_MAX]; - size_t cstr_len = sizeof(cstr); - if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { - s.assign(cstr, cstr_len); - return true; - } - s.clear(); - return false; -} - -bool HostInfoMacOSX::GetOSVersion(uint32_t &major, uint32_t &minor, - uint32_t &update) { - static uint32_t g_major = 0; - static uint32_t g_minor = 0; - static uint32_t g_update = 0; - - if (g_major == 0) { - @autoreleasepool { - NSDictionary *version_info = [NSDictionary - dictionaryWithContentsOfFile: - @"/System/Library/CoreServices/SystemVersion.plist"]; - NSString *version_value = [version_info objectForKey:@"ProductVersion"]; - const char *version_str = [version_value UTF8String]; - if (version_str) - Args::StringToVersion(llvm::StringRef(version_str), g_major, g_minor, - g_update); - } - } - - if (g_major != 0) { - major = g_major; - minor = g_minor; - update = g_update; - return true; - } - return false; -} - -FileSpec HostInfoMacOSX::GetProgramFileSpec() { - static FileSpec g_program_filespec; - if (!g_program_filespec) { - char program_fullpath[PATH_MAX]; - // If DST is NULL, then return the number of bytes needed. - uint32_t len = sizeof(program_fullpath); - int err = _NSGetExecutablePath(program_fullpath, &len); - if (err == 0) - g_program_filespec.SetFile(program_fullpath, false); - else if (err == -1) { - char *large_program_fullpath = (char *)::malloc(len + 1); - - err = _NSGetExecutablePath(large_program_fullpath, &len); - if (err == 0) - g_program_filespec.SetFile(large_program_fullpath, false); - - ::free(large_program_fullpath); - } - } - return g_program_filespec; -} - -bool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) { - FileSpec lldb_file_spec; - if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) - return false; - - std::string raw_path = lldb_file_spec.GetPath(); - - size_t framework_pos = raw_path.find("LLDB.framework"); - if (framework_pos != std::string::npos) { - framework_pos += strlen("LLDB.framework"); -#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - // Shallow bundle - raw_path.resize(framework_pos); -#else - // Normal bundle - raw_path.resize(framework_pos); - raw_path.append("/Resources"); -#endif - } else { - // Find the bin path relative to the lib path where the cmake-based - // OS X .dylib lives. This is not going to work if the bin and lib - // dir are not both in the same dir. - // - // It is not going to work to do it by the executable path either, - // as in the case of a python script, the executable is python, not - // the lldb driver. - raw_path.append("/../bin"); - FileSpec support_dir_spec(raw_path, true); - if (!llvm::sys::fs::is_directory(support_dir_spec.GetPath())) { - Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); - if (log) - log->Printf("HostInfoMacOSX::%s(): failed to find support directory", - __FUNCTION__); - return false; - } - - // Get normalization from support_dir_spec. Note the FileSpec resolve - // does not remove '..' in the path. - char *const dir_realpath = - realpath(support_dir_spec.GetPath().c_str(), NULL); - if (dir_realpath) { - raw_path = dir_realpath; - free(dir_realpath); - } else { - raw_path = support_dir_spec.GetPath(); - } - } - - file_spec.GetDirectory().SetString( - llvm::StringRef(raw_path.c_str(), raw_path.size())); - return (bool)file_spec.GetDirectory(); -} - -bool HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) { - FileSpec lldb_file_spec; - if (!HostInfo::GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) - return false; - - std::string raw_path = lldb_file_spec.GetPath(); - - size_t framework_pos = raw_path.find("LLDB.framework"); - if (framework_pos != std::string::npos) { - framework_pos += strlen("LLDB.framework"); - raw_path.resize(framework_pos); - raw_path.append("/Headers"); - } - file_spec.GetDirectory().SetString( - llvm::StringRef(raw_path.c_str(), raw_path.size())); - return true; -} - -bool HostInfoMacOSX::ComputePythonDirectory(FileSpec &file_spec) { -#ifndef LLDB_DISABLE_PYTHON - FileSpec lldb_file_spec; - if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) - return false; - - std::string raw_path = lldb_file_spec.GetPath(); - - size_t framework_pos = raw_path.find("LLDB.framework"); - if (framework_pos != std::string::npos) { - framework_pos += strlen("LLDB.framework"); - raw_path.resize(framework_pos); - raw_path.append("/Resources/Python"); - } else { - llvm::SmallString<256> python_version_dir; - llvm::raw_svector_ostream os(python_version_dir); - os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION - << "/site-packages"; - - // We may get our string truncated. Should we protect this with an assert? - raw_path.append(python_version_dir.c_str()); - } - file_spec.GetDirectory().SetString( - llvm::StringRef(raw_path.c_str(), raw_path.size())); - return true; -#else - return false; -#endif -} - -bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { - FileSpec lldb_file_spec; - if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) - return false; - - std::string raw_path = lldb_file_spec.GetPath(); - - size_t framework_pos = raw_path.find("LLDB.framework"); - if (framework_pos == std::string::npos) - return false; - - framework_pos += strlen("LLDB.framework"); - raw_path.resize(framework_pos); - raw_path.append("/Resources/PlugIns"); - file_spec.GetDirectory().SetString( - llvm::StringRef(raw_path.c_str(), raw_path.size())); - return true; -} - -bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { - FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns", true); - file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); - return true; -} - -void HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, - ArchSpec &arch_64) { - // All apple systems support 32 bit execution. - uint32_t cputype, cpusubtype; - uint32_t is_64_bit_capable = false; - size_t len = sizeof(cputype); - ArchSpec host_arch; - // These will tell us about the kernel architecture, which even on a 64 - // bit machine can be 32 bit... - if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) { - len = sizeof(cpusubtype); - if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) - cpusubtype = CPU_TYPE_ANY; - - len = sizeof(is_64_bit_capable); - ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); - - if (is_64_bit_capable) { - if (cputype & CPU_ARCH_ABI64) { - // We have a 64 bit kernel on a 64 bit system - arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); - } else { - // We have a 64 bit kernel that is returning a 32 bit cputype, the - // cpusubtype will be correct as if it were for a 64 bit architecture - arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, - cpusubtype); - } - - // Now we need modify the cpusubtype for the 32 bit slices. - uint32_t cpusubtype32 = cpusubtype; -#if defined(__i386__) || defined(__x86_64__) - if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) - cpusubtype32 = CPU_SUBTYPE_I386_ALL; -#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) - cpusubtype32 = CPU_SUBTYPE_ARM_V7S; -#endif - arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), - cpusubtype32); - - if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) { -// When running on a watch or tv, report the host os correctly -#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 - arch_32.GetTriple().setOS(llvm::Triple::TvOS); - arch_64.GetTriple().setOS(llvm::Triple::TvOS); -#else - arch_32.GetTriple().setOS(llvm::Triple::IOS); - arch_64.GetTriple().setOS(llvm::Triple::IOS); -#endif - } else { - arch_32.GetTriple().setOS(llvm::Triple::MacOSX); - arch_64.GetTriple().setOS(llvm::Triple::MacOSX); - } - } else { - // We have a 32 bit kernel on a 32 bit system - arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); -#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 - arch_32.GetTriple().setOS(llvm::Triple::WatchOS); -#else - arch_32.GetTriple().setOS(llvm::Triple::IOS); -#endif - arch_64.Clear(); - } - } -} Index: lldb/trunk/source/Host/macosx/HostThreadMacOSX.mm =================================================================== --- lldb/trunk/source/Host/macosx/HostThreadMacOSX.mm +++ lldb/trunk/source/Host/macosx/HostThreadMacOSX.mm @@ -1,70 +0,0 @@ -//===-- HostThreadMacOSX.cpp ------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Host/macosx/HostThreadMacOSX.h" -#include "lldb/Host/Host.h" - -#include -#include - -#include - -using namespace lldb_private; - -namespace { - -pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; -pthread_key_t g_thread_create_key = 0; - -class MacOSXDarwinThread { -public: - MacOSXDarwinThread() : m_pool(nil) { - m_pool = [[NSAutoreleasePool alloc] init]; - } - - ~MacOSXDarwinThread() { - if (m_pool) { - [m_pool drain]; - m_pool = nil; - } - } - - static void PThreadDestructor(void *v) { - if (v) - delete static_cast(v); - ::pthread_setspecific(g_thread_create_key, NULL); - } - -protected: - NSAutoreleasePool *m_pool; - -private: - DISALLOW_COPY_AND_ASSIGN(MacOSXDarwinThread); -}; - -void InitThreadCreated() { - ::pthread_key_create(&g_thread_create_key, - MacOSXDarwinThread::PThreadDestructor); -} -} // namespace - -HostThreadMacOSX::HostThreadMacOSX() : HostThreadPosix() {} - -HostThreadMacOSX::HostThreadMacOSX(lldb::thread_t thread) - : HostThreadPosix(thread) {} - -lldb::thread_result_t -HostThreadMacOSX::ThreadCreateTrampoline(lldb::thread_arg_t arg) { - ::pthread_once(&g_thread_create_once, InitThreadCreated); - if (g_thread_create_key) { - ::pthread_setspecific(g_thread_create_key, new MacOSXDarwinThread()); - } - - return HostThreadPosix::ThreadCreateTrampoline(arg); -} Index: lldb/trunk/source/Host/macosx/objcxx/CMakeLists.txt =================================================================== --- lldb/trunk/source/Host/macosx/objcxx/CMakeLists.txt +++ lldb/trunk/source/Host/macosx/objcxx/CMakeLists.txt @@ -0,0 +1,21 @@ + +remove_module_flags() +include_directories(..) + +add_lldb_library(lldbHostMacOSXObjCXX + Host.mm + HostInfoMacOSX.mm + HostThreadMacOSX.mm + + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + lldbUtility + ${LLDB_PLUGINS} + ${EXTRA_LIBS} + + LINK_COMPONENTS + Object + Support + ) Index: lldb/trunk/source/Host/macosx/objcxx/Host.mm =================================================================== --- lldb/trunk/source/Host/macosx/objcxx/Host.mm +++ lldb/trunk/source/Host/macosx/objcxx/Host.mm @@ -0,0 +1,1530 @@ +//===-- Host.mm -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Host.h" + +#include + +#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) +#define __XPC_PRIVATE_H__ +#include + +#define LaunchUsingXPCRightName "com.apple.lldb.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" + +#endif + +#include "llvm/Support/Host.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/NameMatches.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-defines.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Errno.h" + +#include "cfcpp/CFCBundle.h" +#include "cfcpp/CFCMutableArray.h" +#include "cfcpp/CFCMutableDictionary.h" +#include "cfcpp/CFCReleaser.h" +#include "cfcpp/CFCString.h" + +#include + +#include +#include + +#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); +} + +using namespace lldb; +using namespace lldb_private; + +bool Host::GetBundleDirectory(const FileSpec &file, + FileSpec &bundle_directory) { +#if defined(__APPLE__) + if (llvm::sys::fs::is_directory(file.GetPath())) { + 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; +} + +bool Host::ResolveExecutableInBundle(FileSpec &file) { +#if defined(__APPLE__) + if (llvm::sys::fs::is_directory(file.GetPath())) { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) { + CFCBundle bundle(path); + CFCReleaser 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; + Status 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), std::chrono::seconds(0), status, NULL); + if (pid_str_len > 0) { + int pid = atoi(pid_str); + return (void *)(intptr_t)pid; + } + } + 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); + } + return false; +} +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + +const char *applscript_in_new_tty = "tell application \"Terminal\"\n" + " activate\n" + " do script \"/bin/bash -c '%s';exit\"\n" + "end tell\n"; + +const char *applscript_in_existing_tty = "\ +set the_shell_script to \"/bin/bash -c '%s';exit\"\n\ +tell application \"Terminal\"\n\ + repeat with the_window in (get windows)\n\ + repeat with the_tab in tabs of the_window\n\ + set the_tty to tty in the_tab\n\ + if the_tty contains \"%s\" then\n\ + if the_tab is not busy then\n\ + set selected of the_tab to true\n\ + set frontmost of the_window to true\n\ + do script the_shell_script in the_tab\n\ + return\n\ + end if\n\ + end if\n\ + end repeat\n\ + end repeat\n\ + do script the_shell_script\n\ +end tell\n"; + +static Status +LaunchInNewTerminalWithAppleScript(const char *exe_path, + ProcessLaunchInfo &launch_info) { + Status 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()); + + 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. + Environment host_env = Host::GetEnvironment(); + + for (const auto &KV : launch_info.GetEnvironment()) { + auto host_entry = host_env.find(KV.first()); + if (host_entry == host_env.end() || host_entry->second != KV.second) + command.Format(" --env='{0}'", Environment::compose(KV)); + } + + 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; + + applescript_source.Printf(applscript_in_new_tty, + command.GetString().str().c_str()); + NSAppleScript *applescript = [[NSAppleScript alloc] + initWithSource:[NSString stringWithCString:applescript_source.GetString() + .str() + .c_str() + encoding:NSUTF8StringEncoding]]; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + Status 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); + } + + llvm::sys::fs::remove(unix_socket_name); + [applescript release]; + if (pid != LLDB_INVALID_PROCESS_ID) + launch_info.SetProcessID(pid); + return error; +} + +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + +bool Host::OpenFileInExternalEditor(const FileSpec &file_spec, + uint32_t line_no) { +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + 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 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("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); + + 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; + } + + ProcessSerialNumber psn; + CFCReleaser 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); + + return false; + } + + return true; +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) +} + +Environment Host::GetEnvironment() { return Environment(*_NSGetEnviron()); } + +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; + } + } + 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); + 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(argc); ++i) { + cstr = data.GetCStr(&offset); + if (cstr) + proc_args.AppendArgument(llvm::StringRef(cstr)); + } + + Environment &proc_env = process_info.GetEnvironment(); + 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.insert(cstr); + } + return true; + } + } + } + } + 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; + } + } + } + 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 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(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(); +} + +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' + 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)); + } +} + +static void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix, + const Environment &env) { + xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(), + env.size()); + size_t i = 0; + for (const auto &KV : env) { + xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(), + Environment::compose(KV).c_str()); + } +} + +/* + 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 Status getXPCAuthorization(ProcessLaunchInfo &launch_info) { + Status 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."); + LLDB_LOG(log, "error: {0}", 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."); + LLDB_LOG(log, "error: {0}", error); + + if (authorizationRef) { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } + } + } + + return error; +} +#endif + +static short GetPosixspawnFlags(const 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(__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 (HostInfo::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 // defined(__x86_64__) || defined(__i386__) + // 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 // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT + return flags; +} + +static Status LaunchProcessXPC(const char *exe_path, + ProcessLaunchInfo &launch_info, + lldb::pid_t &pid) { +#if !NO_XPC_SERVICES + Status 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."); + LLDB_LOG(log, "error: {0}", error); + return error; + } + xpc_service = LaunchUsingXPCRightName; + } else { + error.SetError(4, eErrorTypeGeneric); + error.SetErrorStringWithFormat( + "Launching via XPC is only currently available for root."); + LLDB_LOG(log, "error: {0}", error); + 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()); + PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey, + launch_info.GetEnvironment()); + + // Posix spawn stuff. + xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, + launch_info.GetArchitecture().GetMachOCPUType()); + xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, + GetPosixspawnFlags(launch_info)); + const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); + if (file_action && !file_action->GetPath().empty()) { + xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, + file_action->GetPath().str().c_str()); + } + file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); + if (file_action && !file_action->GetPath().empty()) { + xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, + file_action->GetPath().str().c_str()); + } + file_action = launch_info.GetFileActionForFD(STDERR_FILENO); + if (file_action && !file_action->GetPath().empty()) { + xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, + file_action->GetPath().str().c_str()); + } + + 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); + LLDB_LOG(log, "error: {0}", error); + + 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)); + LLDB_LOG(log, "error: {0}", error); + } + + return error; +#else + Status error; + return error; +#endif +} + +static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, + Log *log, Status &error) { + if (info == NULL) + return false; + + posix_spawn_file_actions_t *file_actions = + reinterpret_cast(_file_actions); + + switch (info->GetAction()) { + case FileAction::eFileActionNone: + error.Clear(); + break; + + case FileAction::eFileActionClose: + if (info->GetFD() == -1) + error.SetErrorString( + "invalid fd for posix_spawn_file_actions_addclose(...)"); + else { + error.SetError( + ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, posix_spawn_file_actions_addclose " + "(action={1}, fd={2})", + error, file_actions, info->GetFD()); + } + break; + + case FileAction::eFileActionDuplicate: + if (info->GetFD() == -1) + error.SetErrorString( + "invalid fd for posix_spawn_file_actions_adddup2(...)"); + else if (info->GetActionArgument() == -1) + error.SetErrorString( + "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); + else { + error.SetError( + ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), + info->GetActionArgument()), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, posix_spawn_file_actions_adddup2 " + "(action={1}, fd={2}, dup_fd={3})", + error, file_actions, info->GetFD(), info->GetActionArgument()); + } + break; + + case FileAction::eFileActionOpen: + if (info->GetFD() == -1) + error.SetErrorString( + "invalid fd in posix_spawn_file_actions_addopen(...)"); + else { + int oflag = info->GetActionArgument(); + + mode_t mode = 0; + + if (oflag & O_CREAT) + mode = 0640; + + error.SetError(::posix_spawn_file_actions_addopen( + file_actions, info->GetFD(), + info->GetPath().str().c_str(), oflag, mode), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, posix_spawn_file_actions_addopen (action={1}, " + "fd={2}, path='{3}', oflag={4}, mode={5})", + error, file_actions, info->GetFD(), info->GetPath(), oflag, + mode); + } + break; + } + return error.Success(); +} + +static Status LaunchProcessPosixSpawn(const char *exe_path, + const ProcessLaunchInfo &launch_info, + lldb::pid_t &pid) { + Status 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()) { + LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error); + return error; + } + + // Make sure we clean up the posix spawn attributes before exiting this scope. + CleanUp cleanup_attr(posix_spawnattr_destroy, &attr); + + 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()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )", + error, flags); + return error; + } + +// 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(__arm__) + + // Don't set the binpref if a shell was provided. After all, that's only + // going to affect what version of the shell + // is launched, not what fork of the binary is launched. We insert "arch + // --arch as part of the shell invocation + // to do that job on OSX. + + if (launch_info.GetShell() == nullptr) { + // 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 != static_cast(UINT32_MAX) && + cpu != static_cast(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()) + LLDB_LOG(log, + "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, " + "cpu_type = {1:x}, count => {2} )", + error, cpu, ocount); + + if (error.Fail() || ocount != 1) + return error; + } + } +#endif // !defined(__arm__) + + const char *tmp_argv[2]; + char *const *argv = const_cast( + launch_info.GetArguments().GetConstArgumentVector()); + Environment::Envp envp = launch_info.GetEnvironment().getEnvp(); + 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 = const_cast(tmp_argv); + } + + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir) { + // Set the working directory on this thread only + if (__pthread_chdir(working_dir.GetCString()) < 0) { + if (errno == ENOENT) { + error.SetErrorStringWithFormat("No such file or directory: %s", + working_dir.GetCString()); + } else if (errno == ENOTDIR) { + error.SetErrorStringWithFormat("Path doesn't name a directory: %s", + working_dir.GetCString()); + } else { + error.SetErrorStringWithFormat("An unknown error occurred when " + "changing directory for process " + "execution."); + } + return error; + } + } + + ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; + 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()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )", + error); + return error; + } + + // Make sure we clean up the posix file actions before exiting this scope. + CleanUp cleanup_fileact(posix_spawn_file_actions_destroy, &file_actions); + + for (size_t i = 0; i < num_file_actions; ++i) { + const FileAction *launch_file_action = + launch_info.GetFileActionAtIndex(i); + if (launch_file_action) { + if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, + error)) + return error; + } + } + + error.SetError( + ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), + eErrorTypePOSIX); + + if (error.Fail()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', " + "file_actions = {3}, " + "attr = {4}, argv = {5}, envp = {6} )", + error, result_pid, exe_path, &file_actions, &attr, argv, + envp.get()); + if (log) { + for (int ii = 0; argv[ii]; ++ii) + LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); + } + } + + } else { + error.SetError( + ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), + eErrorTypePOSIX); + + if (error.Fail()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', " + "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )", + error, result_pid, exe_path, &attr, argv, envp.get()); + if (log) { + for (int ii = 0; argv[ii]; ++ii) + LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); + } + } + } + pid = result_pid; + + if (working_dir) { + // No more thread specific current working directory + __pthread_fchdir(-1); + } + + return error; +} + +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; +} + +Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) { + Status error; + FileSpec exe_spec(launch_info.GetExecutableFile()); + + llvm::sys::fs::file_status stats; + status(exe_spec.GetPath(), stats); + if (!exists(stats)) { + exe_spec.ResolvePath(); + status(exe_spec.GetPath(), stats); + } + if (!exists(stats)) { + exe_spec.ResolveExecutableLocation(); + status(exe_spec.GetPath(), stats); + } + if (!exists(stats)) { + error.SetErrorStringWithFormatv("executable doesn't exist: '{0}'", + launch_info.GetExecutableFile()); + return error; + } + + if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + return LaunchInNewTerminalWithAppleScript(exe_spec.GetPath().c_str(), + 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_spec.GetPath().c_str(), launch_info, pid); + } else { + error = + LaunchProcessPosixSpawn(exe_spec.GetPath().c_str(), 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. + bool monitoring = launch_info.MonitorProcess(); + UNUSED_IF_ASSERT_DISABLED(monitoring); + assert(monitoring); + } else { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString("process launch failed for unknown reasons"); + } + return error; +} + +Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { + Status 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); + } + } + RunShellCommand(expand_command, cwd, &status, nullptr, &output, + std::chrono::seconds(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; + } + + 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()); + } + } + + 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; + + 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(pid), monitor_signals, + reinterpret_cast(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; + wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, 0); + 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); + } + } + }); + + ::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); + } +} Index: lldb/trunk/source/Host/macosx/objcxx/HostInfoMacOSX.mm =================================================================== --- lldb/trunk/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ lldb/trunk/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -0,0 +1,319 @@ +//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if !defined(LLDB_DISABLE_PYTHON) +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#endif + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/macosx/HostInfoMacOSX.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/SafeMachO.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +// C++ Includes +#include + +// C inclues +#include +#include +#include +#include + +// Objective C/C++ includes +#include +#include +#include +#include + +// These are needed when compiling on systems +// that do not yet have these definitions +#include +#ifndef CPU_SUBTYPE_X86_64_H +#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) +#endif +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +#endif + +#include // for TARGET_OS_TV, TARGET_OS_WATCH + +using namespace lldb_private; + +bool HostInfoMacOSX::GetOSBuildString(std::string &s) { + int mib[2] = {CTL_KERN, KERN_OSVERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { + s.assign(cstr, cstr_len); + return true; + } + + s.clear(); + return false; +} + +bool HostInfoMacOSX::GetOSKernelDescription(std::string &s) { + int mib[2] = {CTL_KERN, KERN_VERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { + s.assign(cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +bool HostInfoMacOSX::GetOSVersion(uint32_t &major, uint32_t &minor, + uint32_t &update) { + static uint32_t g_major = 0; + static uint32_t g_minor = 0; + static uint32_t g_update = 0; + + if (g_major == 0) { + @autoreleasepool { + NSDictionary *version_info = [NSDictionary + dictionaryWithContentsOfFile: + @"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *version_value = [version_info objectForKey:@"ProductVersion"]; + const char *version_str = [version_value UTF8String]; + if (version_str) + Args::StringToVersion(llvm::StringRef(version_str), g_major, g_minor, + g_update); + } + } + + if (g_major != 0) { + major = g_major; + minor = g_minor; + update = g_update; + return true; + } + return false; +} + +FileSpec HostInfoMacOSX::GetProgramFileSpec() { + static FileSpec g_program_filespec; + if (!g_program_filespec) { + char program_fullpath[PATH_MAX]; + // If DST is NULL, then return the number of bytes needed. + uint32_t len = sizeof(program_fullpath); + int err = _NSGetExecutablePath(program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(program_fullpath, false); + else if (err == -1) { + char *large_program_fullpath = (char *)::malloc(len + 1); + + err = _NSGetExecutablePath(large_program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(large_program_fullpath, false); + + ::free(large_program_fullpath); + } + } + return g_program_filespec; +} + +bool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) { + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) { + framework_pos += strlen("LLDB.framework"); +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + // Shallow bundle + raw_path.resize(framework_pos); +#else + // Normal bundle + raw_path.resize(framework_pos); + raw_path.append("/Resources"); +#endif + } else { + // Find the bin path relative to the lib path where the cmake-based + // OS X .dylib lives. This is not going to work if the bin and lib + // dir are not both in the same dir. + // + // It is not going to work to do it by the executable path either, + // as in the case of a python script, the executable is python, not + // the lldb driver. + raw_path.append("/../bin"); + FileSpec support_dir_spec(raw_path, true); + if (!llvm::sys::fs::is_directory(support_dir_spec.GetPath())) { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoMacOSX::%s(): failed to find support directory", + __FUNCTION__); + return false; + } + + // Get normalization from support_dir_spec. Note the FileSpec resolve + // does not remove '..' in the path. + char *const dir_realpath = + realpath(support_dir_spec.GetPath().c_str(), NULL); + if (dir_realpath) { + raw_path = dir_realpath; + free(dir_realpath); + } else { + raw_path = support_dir_spec.GetPath(); + } + } + + file_spec.GetDirectory().SetString( + llvm::StringRef(raw_path.c_str(), raw_path.size())); + return (bool)file_spec.GetDirectory(); +} + +bool HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) { + FileSpec lldb_file_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Headers"); + } + file_spec.GetDirectory().SetString( + llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool HostInfoMacOSX::ComputePythonDirectory(FileSpec &file_spec) { +#ifndef LLDB_DISABLE_PYTHON + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/Python"); + } else { + llvm::SmallString<256> python_version_dir; + llvm::raw_svector_ostream os(python_version_dir); + os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION + << "/site-packages"; + + // We may get our string truncated. Should we protect this with an assert? + raw_path.append(python_version_dir.c_str()); + } + file_spec.GetDirectory().SetString( + llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +#else + return false; +#endif +} + +bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos == std::string::npos) + return false; + + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/PlugIns"); + file_spec.GetDirectory().SetString( + llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { + FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns", true); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +void HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, + ArchSpec &arch_64) { + // All apple systems support 32 bit execution. + uint32_t cputype, cpusubtype; + uint32_t is_64_bit_capable = false; + size_t len = sizeof(cputype); + ArchSpec host_arch; + // These will tell us about the kernel architecture, which even on a 64 + // bit machine can be 32 bit... + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) { + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) + cpusubtype = CPU_TYPE_ANY; + + len = sizeof(is_64_bit_capable); + ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); + + if (is_64_bit_capable) { + if (cputype & CPU_ARCH_ABI64) { + // We have a 64 bit kernel on a 64 bit system + arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); + } else { + // We have a 64 bit kernel that is returning a 32 bit cputype, the + // cpusubtype will be correct as if it were for a 64 bit architecture + arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, + cpusubtype); + } + + // Now we need modify the cpusubtype for the 32 bit slices. + uint32_t cpusubtype32 = cpusubtype; +#if defined(__i386__) || defined(__x86_64__) + if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) + cpusubtype32 = CPU_SUBTYPE_I386_ALL; +#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + cpusubtype32 = CPU_SUBTYPE_ARM_V7S; +#endif + arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), + cpusubtype32); + + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) { +// When running on a watch or tv, report the host os correctly +#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 + arch_32.GetTriple().setOS(llvm::Triple::TvOS); + arch_64.GetTriple().setOS(llvm::Triple::TvOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); + arch_64.GetTriple().setOS(llvm::Triple::IOS); +#endif + } else { + arch_32.GetTriple().setOS(llvm::Triple::MacOSX); + arch_64.GetTriple().setOS(llvm::Triple::MacOSX); + } + } else { + // We have a 32 bit kernel on a 32 bit system + arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); +#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + arch_32.GetTriple().setOS(llvm::Triple::WatchOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); +#endif + arch_64.Clear(); + } + } +} Index: lldb/trunk/source/Host/macosx/objcxx/HostThreadMacOSX.mm =================================================================== --- lldb/trunk/source/Host/macosx/objcxx/HostThreadMacOSX.mm +++ lldb/trunk/source/Host/macosx/objcxx/HostThreadMacOSX.mm @@ -0,0 +1,70 @@ +//===-- HostThreadMacOSX.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/macosx/HostThreadMacOSX.h" +#include "lldb/Host/Host.h" + +#include +#include + +#include + +using namespace lldb_private; + +namespace { + +pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; +pthread_key_t g_thread_create_key = 0; + +class MacOSXDarwinThread { +public: + MacOSXDarwinThread() : m_pool(nil) { + m_pool = [[NSAutoreleasePool alloc] init]; + } + + ~MacOSXDarwinThread() { + if (m_pool) { + [m_pool drain]; + m_pool = nil; + } + } + + static void PThreadDestructor(void *v) { + if (v) + delete static_cast(v); + ::pthread_setspecific(g_thread_create_key, NULL); + } + +protected: + NSAutoreleasePool *m_pool; + +private: + DISALLOW_COPY_AND_ASSIGN(MacOSXDarwinThread); +}; + +void InitThreadCreated() { + ::pthread_key_create(&g_thread_create_key, + MacOSXDarwinThread::PThreadDestructor); +} +} // namespace + +HostThreadMacOSX::HostThreadMacOSX() : HostThreadPosix() {} + +HostThreadMacOSX::HostThreadMacOSX(lldb::thread_t thread) + : HostThreadPosix(thread) {} + +lldb::thread_result_t +HostThreadMacOSX::ThreadCreateTrampoline(lldb::thread_arg_t arg) { + ::pthread_once(&g_thread_create_once, InitThreadCreated); + if (g_thread_create_key) { + ::pthread_setspecific(g_thread_create_key, new MacOSXDarwinThread()); + } + + return HostThreadPosix::ThreadCreateTrampoline(arg); +} Index: lldb/trunk/source/Plugins/Platform/MacOSX/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/Platform/MacOSX/CMakeLists.txt +++ lldb/trunk/source/Plugins/Platform/MacOSX/CMakeLists.txt @@ -11,13 +11,14 @@ list(APPEND PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES PlatformAppleSimulator.cpp PlatformiOSSimulator.cpp - PlatformiOSSimulatorCoreSimulatorSupport.mm PlatformAppleTVSimulator.cpp PlatformAppleWatchSimulator.cpp ) if(CMAKE_SYSTEM_NAME MATCHES "Darwin") include_directories(${LIBXML2_INCLUDE_DIR}) + add_subdirectory(objcxx) + set(OBJC_LIBS "lldbPluginPlatformMacOSXObjCXX") list(APPEND PLUGIN_PLATFORM_MACOSX_SOURCES ${PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES}) else() @@ -38,6 +39,7 @@ lldbTarget lldbUtility lldbPluginPlatformPOSIX + ${OBJC_LIBS} LINK_COMPONENTS Support ) Index: lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm =================================================================== --- lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm +++ lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm @@ -1,634 +0,0 @@ -//===-- PlatformiOSSimulatorCoreSimulatorSupport.cpp ---------------*- C++ -//-*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "PlatformiOSSimulatorCoreSimulatorSupport.h" - -// C Includes -// C++ Includes -// Other libraries and framework includes -#include -#include -// Project includes -#include "lldb/Host/PseudoTerminal.h" -#include "lldb/Target/FileAction.h" - -#include "llvm/ADT/StringRef.h" - -using namespace lldb_private; -// CoreSimulator lives as part of Xcode, which means we can't really link -// against it, so we dlopen() -// it at runtime, and error out nicely if that fails -@interface SimServiceContext { -} -+ (id)sharedServiceContextForDeveloperDir:(NSString *)dir - error:(NSError **)error; -@end -// However, the drawback is that the compiler will not know about the selectors -// we're trying to use -// until runtime; to appease clang in this regard, define a fake protocol on -// NSObject that exposes -// the needed interface names for us -@protocol LLDBCoreSimulatorSupport -- (id)defaultDeviceSetWithError:(NSError **)error; -- (NSArray *)devices; -- (id)deviceType; -- (NSString *)name; -- (NSString *)identifier; -- (NSString *)modelIdentifier; -- (NSString *)productFamily; -- (int32_t)productFamilyID; -- (id)runtime; -- (BOOL)available; -- (NSString *)versionString; -- (NSString *)buildVersionString; -- (BOOL)bootWithOptions:(NSDictionary *)options error:(NSError **)error; -- (NSUInteger)state; -- (BOOL)shutdownWithError:(NSError **)error; -- (NSUUID *)UDID; -- (pid_t)spawnWithPath:(NSString *)path - options:(NSDictionary *)options - terminationHandler:(void (^)(int status))terminationHandler - error:(NSError **)error; -@end - -CoreSimulatorSupport::Process::Process(lldb::pid_t p) : m_pid(p), m_error() {} - -CoreSimulatorSupport::Process::Process(Status error) - : m_pid(LLDB_INVALID_PROCESS_ID), m_error(error) {} - -CoreSimulatorSupport::Process::Process(lldb::pid_t p, Status error) - : m_pid(p), m_error(error) {} - -CoreSimulatorSupport::DeviceType::DeviceType() - : m_dev(nil), m_model_identifier() {} - -CoreSimulatorSupport::DeviceType::DeviceType(id d) - : m_dev(d), m_model_identifier() {} - -CoreSimulatorSupport::DeviceType::operator bool() { return m_dev != nil; } - -ConstString CoreSimulatorSupport::DeviceType::GetIdentifier() { - return ConstString([[m_dev identifier] UTF8String]); -} - -ConstString CoreSimulatorSupport::DeviceType::GetProductFamily() { - return ConstString([[m_dev productFamily] UTF8String]); -} - -CoreSimulatorSupport::DeviceType::ProductFamilyID -CoreSimulatorSupport::DeviceType::GetProductFamilyID() { - return ProductFamilyID([m_dev productFamilyID]); -} - -CoreSimulatorSupport::DeviceRuntime::DeviceRuntime() - : m_dev(nil), m_os_version() {} - -CoreSimulatorSupport::DeviceRuntime::DeviceRuntime(id d) - : m_dev(d), m_os_version() {} - -CoreSimulatorSupport::DeviceRuntime::operator bool() { return m_dev != nil; } - -bool CoreSimulatorSupport::DeviceRuntime::IsAvailable() { - return [m_dev available]; -} - -CoreSimulatorSupport::Device::Device() - : m_dev(nil), m_dev_type(), m_dev_runtime() {} - -CoreSimulatorSupport::Device::Device(id d) - : m_dev(d), m_dev_type(), m_dev_runtime() {} - -CoreSimulatorSupport::Device::operator bool() { return m_dev != nil; } - -CoreSimulatorSupport::Device::State CoreSimulatorSupport::Device::GetState() { - return (State)([m_dev state]); -} - -CoreSimulatorSupport::ModelIdentifier::ModelIdentifier(const std::string &mi) - : m_family(), m_versions() { - bool any = false; - bool first_digit = false; - unsigned int val = 0; - - for (char c : mi) { - any = true; - if (::isdigit(c)) { - if (!first_digit) - first_digit = true; - val = 10 * val + (c - '0'); - } else if (c == ',') { - if (first_digit) { - m_versions.push_back(val); - val = 0; - } else - m_family.push_back(c); - } else { - if (first_digit) { - m_family.clear(); - m_versions.clear(); - return; - } else { - m_family.push_back(c); - } - } - } - - if (first_digit) - m_versions.push_back(val); -} - -CoreSimulatorSupport::ModelIdentifier::ModelIdentifier() - : ModelIdentifier("") {} - -CoreSimulatorSupport::OSVersion::OSVersion(const std::string &ver, - const std::string &build) - : m_versions(), m_build(build) { - bool any = false; - unsigned int val = 0; - for (char c : ver) { - if (c == '.') { - m_versions.push_back(val); - val = 0; - } else if (::isdigit(c)) { - val = 10 * val + (c - '0'); - any = true; - } else { - m_versions.clear(); - return; - } - } - if (any) - m_versions.push_back(val); -} - -CoreSimulatorSupport::OSVersion::OSVersion() : OSVersion("", "") {} - -CoreSimulatorSupport::ModelIdentifier -CoreSimulatorSupport::DeviceType::GetModelIdentifier() { - if (!m_model_identifier.hasValue()) { - auto utf8_model_id = [[m_dev modelIdentifier] UTF8String]; - if (utf8_model_id && *utf8_model_id) - m_model_identifier = ModelIdentifier(utf8_model_id); - } - - if (m_model_identifier.hasValue()) - return m_model_identifier.getValue(); - else - return ModelIdentifier(); -} - -CoreSimulatorSupport::OSVersion -CoreSimulatorSupport::DeviceRuntime::GetVersion() { - if (!m_os_version.hasValue()) { - auto utf8_ver_string = [[m_dev versionString] UTF8String]; - auto utf8_build_ver = [[m_dev buildVersionString] UTF8String]; - if (utf8_ver_string && *utf8_ver_string && utf8_build_ver && - *utf8_build_ver) { - m_os_version = OSVersion(utf8_ver_string, utf8_build_ver); - } - } - - if (m_os_version.hasValue()) - return m_os_version.getValue(); - return OSVersion(); -} - -std::string CoreSimulatorSupport::DeviceType::GetName() { - auto utf8_name = [[m_dev name] UTF8String]; - if (utf8_name) - return std::string(utf8_name); - return ""; -} - -std::string CoreSimulatorSupport::Device::GetName() const { - auto utf8_name = [[m_dev name] UTF8String]; - if (utf8_name) - return std::string(utf8_name); - return ""; -} - -std::string CoreSimulatorSupport::Device::GetUDID() const { - auto utf8_udid = [[[m_dev UDID] UUIDString] UTF8String]; - if (utf8_udid) - return std::string(utf8_udid); - else - return std::string(); -} - -CoreSimulatorSupport::DeviceType CoreSimulatorSupport::Device::GetDeviceType() { - if (!m_dev_type.hasValue()) - m_dev_type = DeviceType([m_dev deviceType]); - - return m_dev_type.getValue(); -} - -CoreSimulatorSupport::DeviceRuntime -CoreSimulatorSupport::Device::GetDeviceRuntime() { - if (!m_dev_runtime.hasValue()) - m_dev_runtime = DeviceRuntime([m_dev runtime]); - - return m_dev_runtime.getValue(); -} - -bool CoreSimulatorSupport:: -operator>(const CoreSimulatorSupport::OSVersion &lhs, - const CoreSimulatorSupport::OSVersion &rhs) { - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l > r) - return true; - } - return false; -} - -bool CoreSimulatorSupport:: -operator>(const CoreSimulatorSupport::ModelIdentifier &lhs, - const CoreSimulatorSupport::ModelIdentifier &rhs) { - if (lhs.GetFamily() != rhs.GetFamily()) - return false; - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l > r) - return true; - } - return false; -} - -bool CoreSimulatorSupport:: -operator<(const CoreSimulatorSupport::OSVersion &lhs, - const CoreSimulatorSupport::OSVersion &rhs) { - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l < r) - return true; - } - return false; -} - -bool CoreSimulatorSupport:: -operator<(const CoreSimulatorSupport::ModelIdentifier &lhs, - const CoreSimulatorSupport::ModelIdentifier &rhs) { - if (lhs.GetFamily() != rhs.GetFamily()) - return false; - - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l < r) - return true; - } - return false; -} - -bool CoreSimulatorSupport:: -operator==(const CoreSimulatorSupport::OSVersion &lhs, - const CoreSimulatorSupport::OSVersion &rhs) { - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l != r) - return false; - } - return true; -} - -bool CoreSimulatorSupport:: -operator==(const CoreSimulatorSupport::ModelIdentifier &lhs, - const CoreSimulatorSupport::ModelIdentifier &rhs) { - if (lhs.GetFamily() != rhs.GetFamily()) - return false; - - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l != r) - return false; - } - return true; -} - -bool CoreSimulatorSupport:: -operator!=(const CoreSimulatorSupport::OSVersion &lhs, - const CoreSimulatorSupport::OSVersion &rhs) { - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l != r) - return true; - } - return false; -} - -bool CoreSimulatorSupport:: -operator!=(const CoreSimulatorSupport::ModelIdentifier &lhs, - const CoreSimulatorSupport::ModelIdentifier &rhs) { - if (lhs.GetFamily() != rhs.GetFamily()) - return false; - - for (size_t i = 0; i < rhs.GetNumVersions(); i++) { - unsigned int l = lhs.GetVersionAtIndex(i); - unsigned int r = rhs.GetVersionAtIndex(i); - if (l != r) - return true; - } - return false; -} - -bool CoreSimulatorSupport::Device::Boot(Status &err) { - if (m_dev == nil) { - err.SetErrorString("no valid simulator instance"); - return false; - } - -#define kSimDeviceBootPersist \ - @"persist" /* An NSNumber (boolean) indicating whether or not the session \ - should outlive the calling process (default false) */ - - NSDictionary *options = @{ - kSimDeviceBootPersist : @NO, - }; - -#undef kSimDeviceBootPersist - - NSError *nserror; - if ([m_dev bootWithOptions:options error:&nserror]) { - err.Clear(); - return true; - } else { - err.SetErrorString([[nserror description] UTF8String]); - return false; - } -} - -bool CoreSimulatorSupport::Device::Shutdown(Status &err) { - NSError *nserror; - if ([m_dev shutdownWithError:&nserror]) { - err.Clear(); - return true; - } else { - err.SetErrorString([[nserror description] UTF8String]); - return false; - } -} - -static Status HandleFileAction(ProcessLaunchInfo &launch_info, - NSMutableDictionary *options, NSString *key, - const int fd, File &file) { - Status error; - const FileAction *file_action = launch_info.GetFileActionForFD(fd); - if (file_action) { - switch (file_action->GetAction()) { - case FileAction::eFileActionNone: - break; - - case FileAction::eFileActionClose: - error.SetErrorStringWithFormat("close file action for %i not supported", - fd); - break; - - case FileAction::eFileActionDuplicate: - error.SetErrorStringWithFormat( - "duplication file action for %i not supported", fd); - break; - - case FileAction::eFileActionOpen: { - FileSpec file_spec = file_action->GetFileSpec(); - if (file_spec) { - const int master_fd = launch_info.GetPTY().GetMasterFileDescriptor(); - if (master_fd != PseudoTerminal::invalid_fd) { - // Check in case our file action open wants to open the slave - const char *slave_path = launch_info.GetPTY().GetSlaveName(NULL, 0); - if (slave_path) { - FileSpec slave_spec(slave_path, false); - if (file_spec == slave_spec) { - int slave_fd = launch_info.GetPTY().GetSlaveFileDescriptor(); - if (slave_fd == PseudoTerminal::invalid_fd) - slave_fd = launch_info.GetPTY().OpenSlave(O_RDWR, nullptr, 0); - if (slave_fd == PseudoTerminal::invalid_fd) { - error.SetErrorStringWithFormat("unable to open slave pty '%s'", - slave_path); - return error; // Failure - } - [options setValue:[NSNumber numberWithInteger:slave_fd] - forKey:key]; - return error; // Success - } - } - } - Status posix_error; - int created_fd = - open(file_spec.GetPath().c_str(), file_action->GetActionArgument(), - S_IRUSR | S_IWUSR); - if (created_fd >= 0) { - file.SetDescriptor(created_fd, true); - [options setValue:[NSNumber numberWithInteger:created_fd] forKey:key]; - return error; // Success - } else { - posix_error.SetErrorToErrno(); - error.SetErrorStringWithFormat("unable to open file '%s': %s", - file_spec.GetPath().c_str(), - posix_error.AsCString()); - } - } - } break; - } - } - return error; // Success, no file action, nothing to do -} - -CoreSimulatorSupport::Process -CoreSimulatorSupport::Device::Spawn(ProcessLaunchInfo &launch_info) { -#define kSimDeviceSpawnEnvironment \ - @"environment" /* An NSDictionary (NSStrings -> NSStrings) of environment \ - key/values */ -#define kSimDeviceSpawnStdin @"stdin" /* An NSNumber corresponding to a fd */ -#define kSimDeviceSpawnStdout @"stdout" /* An NSNumber corresponding to a fd \ - */ -#define kSimDeviceSpawnStderr @"stderr" /* An NSNumber corresponding to a fd \ - */ -#define kSimDeviceSpawnArguments \ - @"arguments" /* An NSArray of strings to use as the argv array. If not \ - provided, path will be argv[0] */ -#define kSimDeviceSpawnWaitForDebugger \ - @"wait_for_debugger" /* An NSNumber (bool) */ - - NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; - - if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug)) - [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger]; - - if (launch_info.GetArguments().GetArgumentCount()) { - const Args &args(launch_info.GetArguments()); - NSMutableArray *args_array = [[NSMutableArray alloc] init]; - for (size_t idx = 0; idx < args.GetArgumentCount(); idx++) - [args_array - addObject:[NSString - stringWithUTF8String:args.GetArgumentAtIndex(idx)]]; - - [options setObject:args_array forKey:kSimDeviceSpawnArguments]; - } - - NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init]; - - for (const auto &KV : launch_info.GetEnvironment()) { - NSString *key_ns = [NSString stringWithUTF8String:KV.first().str().c_str()]; - NSString *value_ns = [NSString stringWithUTF8String:KV.second.c_str()]; - - [env_dict setValue:value_ns forKey:key_ns]; - } - - [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment]; - - Status error; - File stdin_file; - File stdout_file; - File stderr_file; - error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin, - STDIN_FILENO, stdin_file); - - if (error.Fail()) - return CoreSimulatorSupport::Process(error); - - error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout, - STDOUT_FILENO, stdout_file); - - if (error.Fail()) - return CoreSimulatorSupport::Process(error); - - error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr, - STDERR_FILENO, stderr_file); - - if (error.Fail()) - return CoreSimulatorSupport::Process(error); - -#undef kSimDeviceSpawnEnvironment -#undef kSimDeviceSpawnStdin -#undef kSimDeviceSpawnStdout -#undef kSimDeviceSpawnStderr -#undef kSimDeviceSpawnWaitForDebugger -#undef kSimDeviceSpawnArguments - - NSError *nserror; - - pid_t pid = [m_dev - spawnWithPath:[NSString stringWithUTF8String:launch_info - .GetExecutableFile() - .GetPath() - .c_str()] - options:options - terminationHandler:nil - error:&nserror]; - - if (pid < 0) { - const char *nserror_string = [[nserror description] UTF8String]; - error.SetErrorString(nserror_string ? nserror_string : "unable to launch"); - } - - return CoreSimulatorSupport::Process(pid, error); -} - -CoreSimulatorSupport::DeviceSet -CoreSimulatorSupport::DeviceSet::GetAllDevices(const char *developer_dir) { - if (!developer_dir || !developer_dir[0]) - return DeviceSet([NSArray new]); - - Class SimServiceContextClass = NSClassFromString(@"SimServiceContext"); - NSString *dev_dir = @(developer_dir); - NSError *error = nil; - - id serviceContext = - [SimServiceContextClass sharedServiceContextForDeveloperDir:dev_dir - error:&error]; - if (!serviceContext) - return DeviceSet([NSArray new]); - - return DeviceSet([[serviceContext defaultDeviceSetWithError:&error] devices]); -} - -CoreSimulatorSupport::DeviceSet -CoreSimulatorSupport::DeviceSet::GetAvailableDevices( - const char *developer_dir) { - return GetAllDevices(developer_dir).GetDevicesIf([](Device d) -> bool { - return (d && d.GetDeviceType() && d.GetDeviceRuntime() && - d.GetDeviceRuntime().IsAvailable()); - }); -} - -size_t CoreSimulatorSupport::DeviceSet::GetNumDevices() { - return [m_dev count]; -} - -CoreSimulatorSupport::Device -CoreSimulatorSupport::DeviceSet::GetDeviceAtIndex(size_t idx) { - if (idx < GetNumDevices()) - return Device([m_dev objectAtIndex:idx]); - return Device(); -} - -CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevicesIf( - std::function f) { - NSMutableArray *array = [[NSMutableArray alloc] init]; - for (NSUInteger i = 0; i < GetNumDevices(); i++) { - Device d(GetDeviceAtIndex(i)); - if (f(d)) - [array addObject:(id)d.m_dev]; - } - - return DeviceSet(array); -} - -void CoreSimulatorSupport::DeviceSet::ForEach( - std::function f) { - const size_t n = GetNumDevices(); - for (NSUInteger i = 0; i < n; ++i) { - if (f(GetDeviceAtIndex(i)) == false) - break; - } -} - -CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevices( - CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) { - NSMutableArray *array = [[NSMutableArray alloc] init]; - const size_t n = GetNumDevices(); - for (NSUInteger i = 0; i < n; ++i) { - Device d(GetDeviceAtIndex(i)); - if (d && d.GetDeviceType() && - d.GetDeviceType().GetProductFamilyID() == dev_id) - [array addObject:(id)d.m_dev]; - } - - return DeviceSet(array); -} - -CoreSimulatorSupport::Device CoreSimulatorSupport::DeviceSet::GetFanciest( - CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) { - Device dev; - - for (NSUInteger i = 0; i < GetNumDevices(); i++) { - Device d(GetDeviceAtIndex(i)); - if (d && d.GetDeviceType() && - d.GetDeviceType().GetProductFamilyID() == dev_id) { - if (!dev) - dev = d; - else { - if ((d.GetDeviceType().GetModelIdentifier() > - dev.GetDeviceType().GetModelIdentifier()) || - d.GetDeviceRuntime().GetVersion() > - dev.GetDeviceRuntime().GetVersion()) - dev = d; - } - } - } - - return dev; -} Index: lldb/trunk/source/Plugins/Platform/MacOSX/objcxx/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/Platform/MacOSX/objcxx/CMakeLists.txt +++ lldb/trunk/source/Plugins/Platform/MacOSX/objcxx/CMakeLists.txt @@ -0,0 +1,18 @@ +remove_module_flags() +include_directories(..) + +add_lldb_library(lldbPluginPlatformMacOSXObjCXX + PlatformiOSSimulatorCoreSimulatorSupport.mm + + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + lldbUtility + ${LLDB_PLUGINS} + ${EXTRA_LIBS} + + LINK_COMPONENTS + Object + Support + ) Index: lldb/trunk/source/Plugins/Platform/MacOSX/objcxx/PlatformiOSSimulatorCoreSimulatorSupport.mm =================================================================== --- lldb/trunk/source/Plugins/Platform/MacOSX/objcxx/PlatformiOSSimulatorCoreSimulatorSupport.mm +++ lldb/trunk/source/Plugins/Platform/MacOSX/objcxx/PlatformiOSSimulatorCoreSimulatorSupport.mm @@ -0,0 +1,634 @@ +//===-- PlatformiOSSimulatorCoreSimulatorSupport.cpp ---------------*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformiOSSimulatorCoreSimulatorSupport.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include +#include +// Project includes +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Target/FileAction.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb_private; +// CoreSimulator lives as part of Xcode, which means we can't really link +// against it, so we dlopen() +// it at runtime, and error out nicely if that fails +@interface SimServiceContext { +} ++ (id)sharedServiceContextForDeveloperDir:(NSString *)dir + error:(NSError **)error; +@end +// However, the drawback is that the compiler will not know about the selectors +// we're trying to use +// until runtime; to appease clang in this regard, define a fake protocol on +// NSObject that exposes +// the needed interface names for us +@protocol LLDBCoreSimulatorSupport +- (id)defaultDeviceSetWithError:(NSError **)error; +- (NSArray *)devices; +- (id)deviceType; +- (NSString *)name; +- (NSString *)identifier; +- (NSString *)modelIdentifier; +- (NSString *)productFamily; +- (int32_t)productFamilyID; +- (id)runtime; +- (BOOL)available; +- (NSString *)versionString; +- (NSString *)buildVersionString; +- (BOOL)bootWithOptions:(NSDictionary *)options error:(NSError **)error; +- (NSUInteger)state; +- (BOOL)shutdownWithError:(NSError **)error; +- (NSUUID *)UDID; +- (pid_t)spawnWithPath:(NSString *)path + options:(NSDictionary *)options + terminationHandler:(void (^)(int status))terminationHandler + error:(NSError **)error; +@end + +CoreSimulatorSupport::Process::Process(lldb::pid_t p) : m_pid(p), m_error() {} + +CoreSimulatorSupport::Process::Process(Status error) + : m_pid(LLDB_INVALID_PROCESS_ID), m_error(error) {} + +CoreSimulatorSupport::Process::Process(lldb::pid_t p, Status error) + : m_pid(p), m_error(error) {} + +CoreSimulatorSupport::DeviceType::DeviceType() + : m_dev(nil), m_model_identifier() {} + +CoreSimulatorSupport::DeviceType::DeviceType(id d) + : m_dev(d), m_model_identifier() {} + +CoreSimulatorSupport::DeviceType::operator bool() { return m_dev != nil; } + +ConstString CoreSimulatorSupport::DeviceType::GetIdentifier() { + return ConstString([[m_dev identifier] UTF8String]); +} + +ConstString CoreSimulatorSupport::DeviceType::GetProductFamily() { + return ConstString([[m_dev productFamily] UTF8String]); +} + +CoreSimulatorSupport::DeviceType::ProductFamilyID +CoreSimulatorSupport::DeviceType::GetProductFamilyID() { + return ProductFamilyID([m_dev productFamilyID]); +} + +CoreSimulatorSupport::DeviceRuntime::DeviceRuntime() + : m_dev(nil), m_os_version() {} + +CoreSimulatorSupport::DeviceRuntime::DeviceRuntime(id d) + : m_dev(d), m_os_version() {} + +CoreSimulatorSupport::DeviceRuntime::operator bool() { return m_dev != nil; } + +bool CoreSimulatorSupport::DeviceRuntime::IsAvailable() { + return [m_dev available]; +} + +CoreSimulatorSupport::Device::Device() + : m_dev(nil), m_dev_type(), m_dev_runtime() {} + +CoreSimulatorSupport::Device::Device(id d) + : m_dev(d), m_dev_type(), m_dev_runtime() {} + +CoreSimulatorSupport::Device::operator bool() { return m_dev != nil; } + +CoreSimulatorSupport::Device::State CoreSimulatorSupport::Device::GetState() { + return (State)([m_dev state]); +} + +CoreSimulatorSupport::ModelIdentifier::ModelIdentifier(const std::string &mi) + : m_family(), m_versions() { + bool any = false; + bool first_digit = false; + unsigned int val = 0; + + for (char c : mi) { + any = true; + if (::isdigit(c)) { + if (!first_digit) + first_digit = true; + val = 10 * val + (c - '0'); + } else if (c == ',') { + if (first_digit) { + m_versions.push_back(val); + val = 0; + } else + m_family.push_back(c); + } else { + if (first_digit) { + m_family.clear(); + m_versions.clear(); + return; + } else { + m_family.push_back(c); + } + } + } + + if (first_digit) + m_versions.push_back(val); +} + +CoreSimulatorSupport::ModelIdentifier::ModelIdentifier() + : ModelIdentifier("") {} + +CoreSimulatorSupport::OSVersion::OSVersion(const std::string &ver, + const std::string &build) + : m_versions(), m_build(build) { + bool any = false; + unsigned int val = 0; + for (char c : ver) { + if (c == '.') { + m_versions.push_back(val); + val = 0; + } else if (::isdigit(c)) { + val = 10 * val + (c - '0'); + any = true; + } else { + m_versions.clear(); + return; + } + } + if (any) + m_versions.push_back(val); +} + +CoreSimulatorSupport::OSVersion::OSVersion() : OSVersion("", "") {} + +CoreSimulatorSupport::ModelIdentifier +CoreSimulatorSupport::DeviceType::GetModelIdentifier() { + if (!m_model_identifier.hasValue()) { + auto utf8_model_id = [[m_dev modelIdentifier] UTF8String]; + if (utf8_model_id && *utf8_model_id) + m_model_identifier = ModelIdentifier(utf8_model_id); + } + + if (m_model_identifier.hasValue()) + return m_model_identifier.getValue(); + else + return ModelIdentifier(); +} + +CoreSimulatorSupport::OSVersion +CoreSimulatorSupport::DeviceRuntime::GetVersion() { + if (!m_os_version.hasValue()) { + auto utf8_ver_string = [[m_dev versionString] UTF8String]; + auto utf8_build_ver = [[m_dev buildVersionString] UTF8String]; + if (utf8_ver_string && *utf8_ver_string && utf8_build_ver && + *utf8_build_ver) { + m_os_version = OSVersion(utf8_ver_string, utf8_build_ver); + } + } + + if (m_os_version.hasValue()) + return m_os_version.getValue(); + return OSVersion(); +} + +std::string CoreSimulatorSupport::DeviceType::GetName() { + auto utf8_name = [[m_dev name] UTF8String]; + if (utf8_name) + return std::string(utf8_name); + return ""; +} + +std::string CoreSimulatorSupport::Device::GetName() const { + auto utf8_name = [[m_dev name] UTF8String]; + if (utf8_name) + return std::string(utf8_name); + return ""; +} + +std::string CoreSimulatorSupport::Device::GetUDID() const { + auto utf8_udid = [[[m_dev UDID] UUIDString] UTF8String]; + if (utf8_udid) + return std::string(utf8_udid); + else + return std::string(); +} + +CoreSimulatorSupport::DeviceType CoreSimulatorSupport::Device::GetDeviceType() { + if (!m_dev_type.hasValue()) + m_dev_type = DeviceType([m_dev deviceType]); + + return m_dev_type.getValue(); +} + +CoreSimulatorSupport::DeviceRuntime +CoreSimulatorSupport::Device::GetDeviceRuntime() { + if (!m_dev_runtime.hasValue()) + m_dev_runtime = DeviceRuntime([m_dev runtime]); + + return m_dev_runtime.getValue(); +} + +bool CoreSimulatorSupport:: +operator>(const CoreSimulatorSupport::OSVersion &lhs, + const CoreSimulatorSupport::OSVersion &rhs) { + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l > r) + return true; + } + return false; +} + +bool CoreSimulatorSupport:: +operator>(const CoreSimulatorSupport::ModelIdentifier &lhs, + const CoreSimulatorSupport::ModelIdentifier &rhs) { + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l > r) + return true; + } + return false; +} + +bool CoreSimulatorSupport:: +operator<(const CoreSimulatorSupport::OSVersion &lhs, + const CoreSimulatorSupport::OSVersion &rhs) { + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l < r) + return true; + } + return false; +} + +bool CoreSimulatorSupport:: +operator<(const CoreSimulatorSupport::ModelIdentifier &lhs, + const CoreSimulatorSupport::ModelIdentifier &rhs) { + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l < r) + return true; + } + return false; +} + +bool CoreSimulatorSupport:: +operator==(const CoreSimulatorSupport::OSVersion &lhs, + const CoreSimulatorSupport::OSVersion &rhs) { + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return false; + } + return true; +} + +bool CoreSimulatorSupport:: +operator==(const CoreSimulatorSupport::ModelIdentifier &lhs, + const CoreSimulatorSupport::ModelIdentifier &rhs) { + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return false; + } + return true; +} + +bool CoreSimulatorSupport:: +operator!=(const CoreSimulatorSupport::OSVersion &lhs, + const CoreSimulatorSupport::OSVersion &rhs) { + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return true; + } + return false; +} + +bool CoreSimulatorSupport:: +operator!=(const CoreSimulatorSupport::ModelIdentifier &lhs, + const CoreSimulatorSupport::ModelIdentifier &rhs) { + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; i < rhs.GetNumVersions(); i++) { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return true; + } + return false; +} + +bool CoreSimulatorSupport::Device::Boot(Status &err) { + if (m_dev == nil) { + err.SetErrorString("no valid simulator instance"); + return false; + } + +#define kSimDeviceBootPersist \ + @"persist" /* An NSNumber (boolean) indicating whether or not the session \ + should outlive the calling process (default false) */ + + NSDictionary *options = @{ + kSimDeviceBootPersist : @NO, + }; + +#undef kSimDeviceBootPersist + + NSError *nserror; + if ([m_dev bootWithOptions:options error:&nserror]) { + err.Clear(); + return true; + } else { + err.SetErrorString([[nserror description] UTF8String]); + return false; + } +} + +bool CoreSimulatorSupport::Device::Shutdown(Status &err) { + NSError *nserror; + if ([m_dev shutdownWithError:&nserror]) { + err.Clear(); + return true; + } else { + err.SetErrorString([[nserror description] UTF8String]); + return false; + } +} + +static Status HandleFileAction(ProcessLaunchInfo &launch_info, + NSMutableDictionary *options, NSString *key, + const int fd, File &file) { + Status error; + const FileAction *file_action = launch_info.GetFileActionForFD(fd); + if (file_action) { + switch (file_action->GetAction()) { + case FileAction::eFileActionNone: + break; + + case FileAction::eFileActionClose: + error.SetErrorStringWithFormat("close file action for %i not supported", + fd); + break; + + case FileAction::eFileActionDuplicate: + error.SetErrorStringWithFormat( + "duplication file action for %i not supported", fd); + break; + + case FileAction::eFileActionOpen: { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) { + const int master_fd = launch_info.GetPTY().GetMasterFileDescriptor(); + if (master_fd != PseudoTerminal::invalid_fd) { + // Check in case our file action open wants to open the slave + const char *slave_path = launch_info.GetPTY().GetSlaveName(NULL, 0); + if (slave_path) { + FileSpec slave_spec(slave_path, false); + if (file_spec == slave_spec) { + int slave_fd = launch_info.GetPTY().GetSlaveFileDescriptor(); + if (slave_fd == PseudoTerminal::invalid_fd) + slave_fd = launch_info.GetPTY().OpenSlave(O_RDWR, nullptr, 0); + if (slave_fd == PseudoTerminal::invalid_fd) { + error.SetErrorStringWithFormat("unable to open slave pty '%s'", + slave_path); + return error; // Failure + } + [options setValue:[NSNumber numberWithInteger:slave_fd] + forKey:key]; + return error; // Success + } + } + } + Status posix_error; + int created_fd = + open(file_spec.GetPath().c_str(), file_action->GetActionArgument(), + S_IRUSR | S_IWUSR); + if (created_fd >= 0) { + file.SetDescriptor(created_fd, true); + [options setValue:[NSNumber numberWithInteger:created_fd] forKey:key]; + return error; // Success + } else { + posix_error.SetErrorToErrno(); + error.SetErrorStringWithFormat("unable to open file '%s': %s", + file_spec.GetPath().c_str(), + posix_error.AsCString()); + } + } + } break; + } + } + return error; // Success, no file action, nothing to do +} + +CoreSimulatorSupport::Process +CoreSimulatorSupport::Device::Spawn(ProcessLaunchInfo &launch_info) { +#define kSimDeviceSpawnEnvironment \ + @"environment" /* An NSDictionary (NSStrings -> NSStrings) of environment \ + key/values */ +#define kSimDeviceSpawnStdin @"stdin" /* An NSNumber corresponding to a fd */ +#define kSimDeviceSpawnStdout @"stdout" /* An NSNumber corresponding to a fd \ + */ +#define kSimDeviceSpawnStderr @"stderr" /* An NSNumber corresponding to a fd \ + */ +#define kSimDeviceSpawnArguments \ + @"arguments" /* An NSArray of strings to use as the argv array. If not \ + provided, path will be argv[0] */ +#define kSimDeviceSpawnWaitForDebugger \ + @"wait_for_debugger" /* An NSNumber (bool) */ + + NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; + + if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug)) + [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger]; + + if (launch_info.GetArguments().GetArgumentCount()) { + const Args &args(launch_info.GetArguments()); + NSMutableArray *args_array = [[NSMutableArray alloc] init]; + for (size_t idx = 0; idx < args.GetArgumentCount(); idx++) + [args_array + addObject:[NSString + stringWithUTF8String:args.GetArgumentAtIndex(idx)]]; + + [options setObject:args_array forKey:kSimDeviceSpawnArguments]; + } + + NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init]; + + for (const auto &KV : launch_info.GetEnvironment()) { + NSString *key_ns = [NSString stringWithUTF8String:KV.first().str().c_str()]; + NSString *value_ns = [NSString stringWithUTF8String:KV.second.c_str()]; + + [env_dict setValue:value_ns forKey:key_ns]; + } + + [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment]; + + Status error; + File stdin_file; + File stdout_file; + File stderr_file; + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin, + STDIN_FILENO, stdin_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout, + STDOUT_FILENO, stdout_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr, + STDERR_FILENO, stderr_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + +#undef kSimDeviceSpawnEnvironment +#undef kSimDeviceSpawnStdin +#undef kSimDeviceSpawnStdout +#undef kSimDeviceSpawnStderr +#undef kSimDeviceSpawnWaitForDebugger +#undef kSimDeviceSpawnArguments + + NSError *nserror; + + pid_t pid = [m_dev + spawnWithPath:[NSString stringWithUTF8String:launch_info + .GetExecutableFile() + .GetPath() + .c_str()] + options:options + terminationHandler:nil + error:&nserror]; + + if (pid < 0) { + const char *nserror_string = [[nserror description] UTF8String]; + error.SetErrorString(nserror_string ? nserror_string : "unable to launch"); + } + + return CoreSimulatorSupport::Process(pid, error); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetAllDevices(const char *developer_dir) { + if (!developer_dir || !developer_dir[0]) + return DeviceSet([NSArray new]); + + Class SimServiceContextClass = NSClassFromString(@"SimServiceContext"); + NSString *dev_dir = @(developer_dir); + NSError *error = nil; + + id serviceContext = + [SimServiceContextClass sharedServiceContextForDeveloperDir:dev_dir + error:&error]; + if (!serviceContext) + return DeviceSet([NSArray new]); + + return DeviceSet([[serviceContext defaultDeviceSetWithError:&error] devices]); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetAvailableDevices( + const char *developer_dir) { + return GetAllDevices(developer_dir).GetDevicesIf([](Device d) -> bool { + return (d && d.GetDeviceType() && d.GetDeviceRuntime() && + d.GetDeviceRuntime().IsAvailable()); + }); +} + +size_t CoreSimulatorSupport::DeviceSet::GetNumDevices() { + return [m_dev count]; +} + +CoreSimulatorSupport::Device +CoreSimulatorSupport::DeviceSet::GetDeviceAtIndex(size_t idx) { + if (idx < GetNumDevices()) + return Device([m_dev objectAtIndex:idx]); + return Device(); +} + +CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevicesIf( + std::function f) { + NSMutableArray *array = [[NSMutableArray alloc] init]; + for (NSUInteger i = 0; i < GetNumDevices(); i++) { + Device d(GetDeviceAtIndex(i)); + if (f(d)) + [array addObject:(id)d.m_dev]; + } + + return DeviceSet(array); +} + +void CoreSimulatorSupport::DeviceSet::ForEach( + std::function f) { + const size_t n = GetNumDevices(); + for (NSUInteger i = 0; i < n; ++i) { + if (f(GetDeviceAtIndex(i)) == false) + break; + } +} + +CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevices( + CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) { + NSMutableArray *array = [[NSMutableArray alloc] init]; + const size_t n = GetNumDevices(); + for (NSUInteger i = 0; i < n; ++i) { + Device d(GetDeviceAtIndex(i)); + if (d && d.GetDeviceType() && + d.GetDeviceType().GetProductFamilyID() == dev_id) + [array addObject:(id)d.m_dev]; + } + + return DeviceSet(array); +} + +CoreSimulatorSupport::Device CoreSimulatorSupport::DeviceSet::GetFanciest( + CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) { + Device dev; + + for (NSUInteger i = 0; i < GetNumDevices(); i++) { + Device d(GetDeviceAtIndex(i)); + if (d && d.GetDeviceType() && + d.GetDeviceType().GetProductFamilyID() == dev_id) { + if (!dev) + dev = d; + else { + if ((d.GetDeviceType().GetModelIdentifier() > + dev.GetDeviceType().GetModelIdentifier()) || + d.GetDeviceRuntime().GetVersion() > + dev.GetDeviceRuntime().GetVersion()) + dev = d; + } + } + } + + return dev; +}