summaryrefslogtreecommitdiff
path: root/source/Host
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host')
-rw-r--r--source/Host/CMakeLists.txt190
-rw-r--r--source/Host/Makefile65
-rw-r--r--source/Host/android/HostInfoAndroid.cpp104
-rw-r--r--source/Host/android/LibcGlue.cpp40
-rw-r--r--source/Host/android/ProcessLauncherAndroid.cpp108
-rw-r--r--source/Host/linux/AbstractSocket.cpp31
-rw-r--r--source/Host/linux/Host.cpp393
-rw-r--r--source/Host/linux/HostInfoLinux.cpp282
-rw-r--r--source/Host/linux/HostThreadLinux.cpp52
-rw-r--r--source/Host/linux/LibcGlue.cpp30
-rw-r--r--source/Host/linux/ThisThread.cpp29
-rw-r--r--source/Host/macosx/Host.mm1587
-rw-r--r--source/Host/macosx/HostInfoMacOSX.mm372
-rw-r--r--source/Host/macosx/HostThreadMacOSX.mm102
-rw-r--r--source/Host/macosx/Symbols.cpp559
-rw-r--r--source/Host/macosx/ThisThread.cpp39
-rw-r--r--source/Host/macosx/cfcpp/CFCBundle.cpp99
-rw-r--r--source/Host/macosx/cfcpp/CFCBundle.h50
-rw-r--r--source/Host/macosx/cfcpp/CFCData.cpp82
-rw-r--r--source/Host/macosx/cfcpp/CFCData.h35
-rw-r--r--source/Host/macosx/cfcpp/CFCMutableArray.cpp166
-rw-r--r--source/Host/macosx/cfcpp/CFCMutableArray.h39
-rw-r--r--source/Host/macosx/cfcpp/CFCMutableDictionary.cpp529
-rw-r--r--source/Host/macosx/cfcpp/CFCMutableDictionary.h79
-rw-r--r--source/Host/macosx/cfcpp/CFCMutableSet.cpp114
-rw-r--r--source/Host/macosx/cfcpp/CFCMutableSet.h53
-rw-r--r--source/Host/macosx/cfcpp/CFCReleaser.h158
-rw-r--r--source/Host/macosx/cfcpp/CFCString.cpp195
-rw-r--r--source/Host/macosx/cfcpp/CFCString.h41
-rw-r--r--source/Host/macosx/cfcpp/CoreFoundationCPP.h30
-rw-r--r--source/Host/netbsd/Makefile14
-rw-r--r--source/Host/windows/Condition.cpp98
-rw-r--r--source/Host/windows/ConnectionGenericFileWindows.cpp354
-rw-r--r--source/Host/windows/EditLineWin.cpp435
-rw-r--r--source/Host/windows/FileSystem.cpp221
-rw-r--r--source/Host/windows/Host.cpp326
-rw-r--r--source/Host/windows/HostInfoWindows.cpp118
-rw-r--r--source/Host/windows/HostProcessWindows.cpp137
-rw-r--r--source/Host/windows/HostThreadWindows.cpp98
-rw-r--r--source/Host/windows/LockFileWindows.cpp93
-rw-r--r--source/Host/windows/Mutex.cpp109
-rw-r--r--source/Host/windows/PipeWindows.cpp336
-rw-r--r--source/Host/windows/ProcessLauncherWindows.cpp105
-rw-r--r--source/Host/windows/ProcessRunLock.cpp106
-rw-r--r--source/Host/windows/ThisThread.cpp66
-rw-r--r--source/Host/windows/Windows.cpp231
46 files changed, 8500 insertions, 0 deletions
diff --git a/source/Host/CMakeLists.txt b/source/Host/CMakeLists.txt
new file mode 100644
index 000000000000..776fcfb44ffa
--- /dev/null
+++ b/source/Host/CMakeLists.txt
@@ -0,0 +1,190 @@
+macro(add_host_subdirectory group)
+ list(APPEND HOST_SOURCES ${ARGN})
+ source_group(${group} FILES ${ARGN})
+endmacro()
+
+add_host_subdirectory(common
+ common/Condition.cpp
+ common/File.cpp
+ common/FileCache.cpp
+ common/FileSpec.cpp
+ common/FileSystem.cpp
+ common/GetOptInc.cpp
+ common/Host.cpp
+ common/HostInfoBase.cpp
+ common/HostNativeThreadBase.cpp
+ common/HostProcess.cpp
+ common/HostThread.cpp
+ common/IOObject.cpp
+ common/LockFileBase.cpp
+ common/Mutex.cpp
+ common/MonitoringProcessLauncher.cpp
+ common/NativeBreakpoint.cpp
+ common/NativeBreakpointList.cpp
+ common/NativeWatchpointList.cpp
+ common/NativeProcessProtocol.cpp
+ common/NativeRegisterContext.cpp
+ common/NativeRegisterContextRegisterInfo.cpp
+ common/NativeThreadProtocol.cpp
+ common/OptionParser.cpp
+ common/PipeBase.cpp
+ common/ProcessRunLock.cpp
+ common/Socket.cpp
+ common/SocketAddress.cpp
+ common/SoftwareBreakpoint.cpp
+ common/StringConvert.cpp
+ common/Symbols.cpp
+ common/TCPSocket.cpp
+ common/Terminal.cpp
+ common/ThisThread.cpp
+ common/ThreadLauncher.cpp
+ common/TimeValue.cpp
+ common/XML.cpp
+ common/UDPSocket.cpp
+ )
+
+# Keep track of whether we want to provide a define for the
+# Python's architecture-specific lib path (i.e. where a
+# Python lldb module would go).
+set (get_python_libdir 0)
+
+if (NOT LLDB_DISABLE_LIBEDIT)
+ add_host_subdirectory(common
+ common/Editline.cpp
+ )
+endif()
+
+add_host_subdirectory(posix
+ posix/ConnectionFileDescriptorPosix.cpp
+ )
+
+if (CMAKE_SYSTEM_NAME MATCHES "Windows")
+ add_host_subdirectory(windows
+ windows/Condition.cpp
+ windows/ConnectionGenericFileWindows.cpp
+ windows/EditLineWin.cpp
+ windows/FileSystem.cpp
+ windows/Host.cpp
+ windows/HostInfoWindows.cpp
+ windows/HostProcessWindows.cpp
+ windows/HostThreadWindows.cpp
+ windows/LockFileWindows.cpp
+ windows/Mutex.cpp
+ windows/PipeWindows.cpp
+ windows/ProcessLauncherWindows.cpp
+ windows/ProcessRunLock.cpp
+ windows/ThisThread.cpp
+ windows/Windows.cpp
+ )
+else()
+ if (NOT LLDB_DISABLE_PYTHON)
+ # We'll grab the arch-specific python libdir on POSIX systems.
+ set (get_python_libdir 1)
+ endif()
+
+ add_host_subdirectory(posix
+ posix/DomainSocket.cpp
+ posix/FileSystem.cpp
+ posix/HostInfoPosix.cpp
+ posix/HostProcessPosix.cpp
+ posix/HostThreadPosix.cpp
+ posix/LockFilePosix.cpp
+ posix/MainLoopPosix.cpp
+ posix/PipePosix.cpp
+ )
+
+ if (NOT __ANDROID_NDK__)
+ add_host_subdirectory(posix
+ posix/ProcessLauncherPosix.cpp
+ )
+ endif()
+
+ 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
+ macosx/Symbols.cpp
+ macosx/ThisThread.cpp
+ macosx/cfcpp/CFCBundle.cpp
+ macosx/cfcpp/CFCData.cpp
+ macosx/cfcpp/CFCMutableArray.cpp
+ macosx/cfcpp/CFCMutableDictionary.cpp
+ macosx/cfcpp/CFCMutableSet.cpp
+ macosx/cfcpp/CFCString.cpp
+ )
+
+ elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
+ if (__ANDROID_NDK__)
+ add_host_subdirectory(android
+ android/HostInfoAndroid.cpp
+ android/LibcGlue.cpp
+ android/ProcessLauncherAndroid.cpp
+ linux/AbstractSocket.cpp
+ linux/Host.cpp
+ linux/HostInfoLinux.cpp
+ linux/HostThreadLinux.cpp
+ linux/LibcGlue.cpp
+ linux/ThisThread.cpp
+ )
+ else()
+ add_host_subdirectory(linux
+ linux/AbstractSocket.cpp
+ linux/Host.cpp
+ linux/HostInfoLinux.cpp
+ linux/HostThreadLinux.cpp
+ linux/LibcGlue.cpp
+ linux/ThisThread.cpp
+ )
+ endif()
+
+ elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ add_host_subdirectory(freebsd
+ freebsd/Host.cpp
+ freebsd/HostInfoFreeBSD.cpp
+ freebsd/HostThreadFreeBSD.cpp
+ freebsd/ThisThread.cpp
+ )
+
+ elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
+ add_host_subdirectory(netbsd
+ netbsd/Host.cpp
+ netbsd/HostInfoNetBSD.cpp
+ netbsd/HostThreadNetBSD.cpp
+ netbsd/ThisThread.cpp
+ )
+ endif()
+endif()
+
+if (${get_python_libdir})
+ # Call a python script to gather the arch-specific libdir for
+ # modules like the lldb module.
+ execute_process(
+ COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/get_relative_lib_dir.py
+ RESULT_VARIABLE get_libdir_status
+ OUTPUT_VARIABLE relative_libdir
+ )
+ if (get_libdir_status EQUAL 0)
+ add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${relative_libdir}")
+ endif()
+endif()
+
+if (${get_python_libdir})
+ # Call a python script to gather the arch-specific libdir for
+ # modules like the lldb module.
+ execute_process(
+ COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/get_relative_lib_dir.py
+ RESULT_VARIABLE get_libdir_status
+ OUTPUT_VARIABLE relative_libdir
+ )
+ if (get_libdir_status EQUAL 0)
+ add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${relative_libdir}")
+ endif()
+endif()
+
+add_lldb_library(lldbHost ${HOST_SOURCES})
+
+if (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
+target_link_libraries(lldbHost kvm)
+endif ()
diff --git a/source/Host/Makefile b/source/Host/Makefile
new file mode 100644
index 000000000000..da90c8c364a3
--- /dev/null
+++ b/source/Host/Makefile
@@ -0,0 +1,65 @@
+##===- source/Host/Makefile --------------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LLDB_LEVEL := ../..
+LEVEL := $(LLDB_LEVEL)/../..
+
+include $(LEVEL)/Makefile.config
+
+define DIR_SOURCES
+SOURCES += $$(addprefix $(1)/,$$(notdir $$(wildcard $$(PROJ_SRC_DIR)/$(1)/*.cpp \
+ $$(PROJ_SRC_DIR)/*.cc $$(PROJ_SRC_DIR)/$(1)/*.c $$(PROJ_SRC_DIR)/$(1)/*.mm)))
+endef
+
+$(eval $(call DIR_SOURCES,common))
+
+ifeq ($(HOST_OS),Darwin)
+$(eval $(call DIR_SOURCES,posix))
+$(eval $(call DIR_SOURCES,macosx))
+CFCPP_SOURCES = \
+ $(addprefix macosx/cfcpp/,$(notdir $(wildcard $(PROJ_SRC_DIR)/macosx/cfcpp/*.cpp)))
+SOURCES += $(CFCPP_SOURCES)
+
+CFCPP_BaseNameSources := $(sort $(basename $(CFCPP_SOURCES)))
+CFCPP_OBJECTS := $(CFCPP_BaseNameSources:%=$(ObjDir)/%.o)
+
+# Make sure the cfcpp output directory exists
+$(CFCPP_OBJECTS): $(ObjDir)/cfcpp/.dir
+endif
+
+ifeq ($(HOST_OS),Linux)
+$(eval $(call DIR_SOURCES,posix))
+$(eval $(call DIR_SOURCES,linux))
+endif
+
+ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD))
+$(eval $(call DIR_SOURCES,posix))
+$(eval $(call DIR_SOURCES,freebsd))
+endif
+
+ifeq ($(HOST_OS),NetBSD)
+$(eval $(call DIR_SOURCES,posix))
+$(eval $(call DIR_SOURCES,netbsd))
+endif
+
+ifeq ($(HOST_OS),MingW)
+$(eval $(call DIR_SOURCES,windows))
+SOURCES += posix/ConnectionFileDescriptorPosix.cpp
+endif
+
+ifeq ($(HOST_OS),Android)
+$(eval $(call DIR_SOURCES,posix))
+$(eval $(call DIR_SOURCES,linux))
+$(eval $(call DIR_SOURCES,android))
+endif
+
+LIBRARYNAME := lldbHost
+BUILD_ARCHIVE = 1
+
+include $(LLDB_LEVEL)/Makefile
diff --git a/source/Host/android/HostInfoAndroid.cpp b/source/Host/android/HostInfoAndroid.cpp
new file mode 100644
index 000000000000..3fa50ec8ddfd
--- /dev/null
+++ b/source/Host/android/HostInfoAndroid.cpp
@@ -0,0 +1,104 @@
+//===-- HostInfoAndroid.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/android/HostInfoAndroid.h"
+#include "lldb/Host/linux/HostInfoLinux.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb_private;
+using namespace llvm;
+
+void
+HostInfoAndroid::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64)
+{
+ HostInfoLinux::ComputeHostArchitectureSupport(arch_32, arch_64);
+
+ if (arch_32.IsValid())
+ {
+ arch_32.GetTriple().setEnvironment(llvm::Triple::Android);
+ }
+ if (arch_64.IsValid())
+ {
+ arch_64.GetTriple().setEnvironment(llvm::Triple::Android);
+ }
+}
+
+FileSpec
+HostInfoAndroid::GetDefaultShell()
+{
+ return FileSpec("/system/bin/sh", false);
+}
+
+FileSpec
+HostInfoAndroid::ResolveLibraryPath(const std::string& module_path, const ArchSpec& arch)
+{
+ static const char* const ld_library_path_separator = ":";
+ static const char* const default_lib32_path[] = {
+ "/vendor/lib",
+ "/system/lib",
+ nullptr
+ };
+ static const char* const default_lib64_path[] = {
+ "/vendor/lib64",
+ "/system/lib64",
+ nullptr
+ };
+
+ if (module_path.empty() || module_path[0] == '/')
+ return FileSpec(module_path.c_str(), true);
+
+ SmallVector<StringRef, 4> ld_paths;
+
+ if (const char* ld_library_path = ::getenv("LD_LIBRARY_PATH"))
+ StringRef(ld_library_path).split(ld_paths, StringRef(ld_library_path_separator), -1, false);
+
+ const char* const* default_lib_path = nullptr;
+ switch (arch.GetAddressByteSize())
+ {
+ case 4:
+ default_lib_path = default_lib32_path;
+ break;
+ case 8:
+ default_lib_path = default_lib64_path;
+ break;
+ default:
+ assert(false && "Unknown address byte size");
+ return FileSpec();
+ }
+
+ for(const char* const* it = default_lib_path; *it; ++it)
+ ld_paths.push_back(StringRef(*it));
+
+ for (const StringRef& path : ld_paths)
+ {
+ FileSpec file_candidate(path.str().c_str(), true);
+ file_candidate.AppendPathComponent(module_path.c_str());
+
+ if (file_candidate.Exists())
+ return file_candidate;
+ }
+
+ return FileSpec();
+}
+
+bool
+HostInfoAndroid::ComputeTempFileBaseDirectory(FileSpec &file_spec)
+{
+ bool success = HostInfoLinux::ComputeTempFileBaseDirectory(file_spec);
+
+ // On Android, there is no path which is guaranteed to be writable. If the user has not
+ // provided a path via an environment variable, the generic algorithm will deduce /tmp, which
+ // is plain wrong. In that case we have an invalid directory, we substitute the path with
+ // /data/local/tmp, which is correct at least in some cases (i.e., when running as shell user).
+ if (!success || !file_spec.Exists())
+ file_spec = FileSpec("/data/local/tmp", false);
+
+ return file_spec.Exists();
+}
diff --git a/source/Host/android/LibcGlue.cpp b/source/Host/android/LibcGlue.cpp
new file mode 100644
index 000000000000..3842fb6c2a8e
--- /dev/null
+++ b/source/Host/android/LibcGlue.cpp
@@ -0,0 +1,40 @@
+//===-- LibcGlue.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This files adds functions missing from libc on earlier versions of Android
+
+#include <android/api-level.h>
+
+#include <sys/syscall.h>
+
+#if __ANDROID_API__ < 21
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "lldb/Host/Time.h"
+
+time_t timegm(struct tm* t)
+{
+ return (time_t) timegm64(t);
+}
+
+int signalfd (int fd, const sigset_t *mask, int flags)
+{
+ return syscall(__NR_signalfd4, fd, mask, _NSIG / 8, flags);
+}
+
+int posix_openpt(int flags)
+{
+ return open("/dev/ptmx", flags);
+}
+
+#endif
diff --git a/source/Host/android/ProcessLauncherAndroid.cpp b/source/Host/android/ProcessLauncherAndroid.cpp
new file mode 100644
index 000000000000..24eebc8c030f
--- /dev/null
+++ b/source/Host/android/ProcessLauncherAndroid.cpp
@@ -0,0 +1,108 @@
+//===-- ProcessLauncherAndroid.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/FileSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/android/ProcessLauncherAndroid.h"
+
+#include "lldb/Target/ProcessLaunchInfo.h"
+
+#include <limits.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool
+DupDescriptor(const FileSpec &file_spec, int fd, int flags)
+{
+ int target_fd = ::open(file_spec.GetCString(), flags, 0666);
+
+ if (target_fd == -1)
+ return false;
+
+ if (::dup2(target_fd, fd) == -1)
+ return false;
+
+ return (::close(target_fd) == -1) ? false : true;
+}
+
+// If there is no PATH variable specified inside the environment then set the path to /system/bin.
+// It is required because the default path used by execve() is wrong on android.
+static void
+FixupEnvironment(Args& env)
+{
+ static const char* path = "PATH=";
+ static const int path_len = ::strlen(path);
+ for (const char** args = env.GetConstArgumentVector(); *args; ++args)
+ if (::strncmp(path, *args, path_len) == 0)
+ return;
+ env.AppendArgument("PATH=/system/bin");
+}
+
+HostProcess
+ProcessLauncherAndroid::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error)
+{
+ // TODO: Handle other launch parameters specified in launc_info
+
+ char exe_path[PATH_MAX];
+ launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path));
+
+ lldb::pid_t pid = ::fork();
+ if (pid == static_cast<lldb::pid_t>(-1))
+ {
+ // Fork failed
+ error.SetErrorStringWithFormat("Fork failed with error message: %s", strerror(errno));
+ return HostProcess(LLDB_INVALID_PROCESS_ID);
+ }
+ else if (pid == 0)
+ {
+ if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO)) {
+ FileSpec file_spec = file_action->GetFileSpec();
+ if (file_spec)
+ if (!DupDescriptor(file_spec, STDIN_FILENO, O_RDONLY))
+ exit(-1);
+ }
+
+ if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDOUT_FILENO)) {
+ FileSpec file_spec = file_action->GetFileSpec();
+ if (file_spec)
+ if (!DupDescriptor(file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
+ exit(-1);
+ }
+
+ if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDERR_FILENO)) {
+ FileSpec file_spec = file_action->GetFileSpec();
+ if (file_spec)
+ if (!DupDescriptor(file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
+ exit(-1);
+ }
+
+ // Child process
+ const char **argv = launch_info.GetArguments().GetConstArgumentVector();
+
+ Args env = launch_info.GetEnvironmentEntries();
+ FixupEnvironment(env);
+ const char **envp = env.GetConstArgumentVector();
+
+ FileSpec working_dir = launch_info.GetWorkingDirectory();
+ if (working_dir)
+ {
+ if (::chdir(working_dir.GetCString()) != 0)
+ exit(-1);
+ }
+
+ execve(argv[0],
+ const_cast<char *const *>(argv),
+ const_cast<char *const *>(envp));
+ exit(-1);
+ }
+
+ return HostProcess(pid);
+}
diff --git a/source/Host/linux/AbstractSocket.cpp b/source/Host/linux/AbstractSocket.cpp
new file mode 100644
index 000000000000..8ac0107123bb
--- /dev/null
+++ b/source/Host/linux/AbstractSocket.cpp
@@ -0,0 +1,31 @@
+//===-- AbstractSocket.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/linux/AbstractSocket.h"
+
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+AbstractSocket::AbstractSocket(bool child_processes_inherit, Error &error)
+ : DomainSocket(ProtocolUnixAbstract, child_processes_inherit, error)
+{
+}
+
+size_t
+AbstractSocket::GetNameOffset() const
+{
+ return 1;
+}
+
+void
+AbstractSocket::DeleteSocketFile(llvm::StringRef name)
+{
+}
diff --git a/source/Host/linux/Host.cpp b/source/Host/linux/Host.cpp
new file mode 100644
index 000000000000..cb7369fe7aec
--- /dev/null
+++ b/source/Host/linux/Host.cpp
@@ -0,0 +1,393 @@
+//===-- source/Host/linux/Host.cpp ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <stdio.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Target/Process.h"
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#ifdef __ANDROID_NDK__
+#include "lldb/Host/android/Android.h"
+#endif
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "Plugins/Process/Linux/ProcFileReader.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+typedef enum ProcessStateFlags
+{
+ eProcessStateRunning = (1u << 0), // Running
+ eProcessStateSleeping = (1u << 1), // Sleeping in an interruptible wait
+ eProcessStateWaiting = (1u << 2), // Waiting in an uninterruptible disk sleep
+ eProcessStateZombie = (1u << 3), // Zombie
+ eProcessStateTracedOrStopped = (1u << 4), // Traced or stopped (on a signal)
+ eProcessStatePaging = (1u << 5) // Paging
+} ProcessStateFlags;
+
+typedef struct ProcessStatInfo
+{
+ lldb::pid_t ppid; // Parent Process ID
+ uint32_t fProcessState; // ProcessStateFlags
+} ProcessStatInfo;
+
+// Get the process info with additional information from /proc/$PID/stat (like process state, and tracer pid).
+static bool GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid);
+
+static bool
+ReadProcPseudoFileStat (lldb::pid_t pid, ProcessStatInfo& stat_info)
+{
+ // Read the /proc/$PID/stat file.
+ lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "stat");
+
+ // The filename of the executable is stored in parenthesis right after the pid. We look for the closing
+ // parenthesis for the filename and work from there in case the name has something funky like ')' in it.
+ const char *filename_end = strrchr ((const char *)buf_sp->GetBytes(), ')');
+ if (filename_end)
+ {
+ char state = '\0';
+ int ppid = LLDB_INVALID_PROCESS_ID;
+
+ // Read state and ppid.
+ sscanf (filename_end + 1, " %c %d", &state, &ppid);
+
+ stat_info.ppid = ppid;
+
+ switch (state)
+ {
+ case 'R':
+ stat_info.fProcessState |= eProcessStateRunning;
+ break;
+ case 'S':
+ stat_info.fProcessState |= eProcessStateSleeping;
+ break;
+ case 'D':
+ stat_info.fProcessState |= eProcessStateWaiting;
+ break;
+ case 'Z':
+ stat_info.fProcessState |= eProcessStateZombie;
+ break;
+ case 'T':
+ stat_info.fProcessState |= eProcessStateTracedOrStopped;
+ break;
+ case 'W':
+ stat_info.fProcessState |= eProcessStatePaging;
+ break;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+static void
+GetLinuxProcessUserAndGroup (lldb::pid_t pid, ProcessInstanceInfo &process_info, lldb::pid_t &tracerpid)
+{
+ tracerpid = 0;
+ uint32_t rUid = UINT32_MAX; // Real User ID
+ uint32_t eUid = UINT32_MAX; // Effective User ID
+ uint32_t rGid = UINT32_MAX; // Real Group ID
+ uint32_t eGid = UINT32_MAX; // Effective Group ID
+
+ // Read the /proc/$PID/status file and parse the Uid:, Gid:, and TracerPid: fields.
+ lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "status");
+
+ static const char uid_token[] = "Uid:";
+ char *buf_uid = strstr ((char *)buf_sp->GetBytes(), uid_token);
+ if (buf_uid)
+ {
+ // Real, effective, saved set, and file system UIDs. Read the first two.
+ buf_uid += sizeof(uid_token);
+ rUid = strtol (buf_uid, &buf_uid, 10);
+ eUid = strtol (buf_uid, &buf_uid, 10);
+ }
+
+ static const char gid_token[] = "Gid:";
+ char *buf_gid = strstr ((char *)buf_sp->GetBytes(), gid_token);
+ if (buf_gid)
+ {
+ // Real, effective, saved set, and file system GIDs. Read the first two.
+ buf_gid += sizeof(gid_token);
+ rGid = strtol (buf_gid, &buf_gid, 10);
+ eGid = strtol (buf_gid, &buf_gid, 10);
+ }
+
+ static const char tracerpid_token[] = "TracerPid:";
+ char *buf_tracerpid = strstr((char *)buf_sp->GetBytes(), tracerpid_token);
+ if (buf_tracerpid)
+ {
+ // Tracer PID. 0 if we're not being debugged.
+ buf_tracerpid += sizeof(tracerpid_token);
+ tracerpid = strtol (buf_tracerpid, &buf_tracerpid, 10);
+ }
+
+ process_info.SetUserID (rUid);
+ process_info.SetEffectiveUserID (eUid);
+ process_info.SetGroupID (rGid);
+ process_info.SetEffectiveGroupID (eGid);
+}
+
+lldb::DataBufferSP
+Host::GetAuxvData(lldb_private::Process *process)
+{
+ return process_linux::ProcFileReader::ReadIntoDataBuffer (process->GetID(), "auxv");
+}
+
+lldb::DataBufferSP
+Host::GetAuxvData (lldb::pid_t pid)
+{
+ return process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "auxv");
+}
+
+static bool
+IsDirNumeric(const char *dname)
+{
+ for (; *dname; dname++)
+ {
+ if (!isdigit (*dname))
+ return false;
+ }
+ return true;
+}
+
+uint32_t
+Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos)
+{
+ static const char procdir[] = "/proc/";
+
+ DIR *dirproc = opendir (procdir);
+ if (dirproc)
+ {
+ struct dirent *direntry = NULL;
+ const uid_t our_uid = getuid();
+ const lldb::pid_t our_pid = getpid();
+ bool all_users = match_info.GetMatchAllUsers();
+
+ while ((direntry = readdir (dirproc)) != NULL)
+ {
+ if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name))
+ continue;
+
+ lldb::pid_t pid = atoi (direntry->d_name);
+
+ // Skip this process.
+ if (pid == our_pid)
+ continue;
+
+ lldb::pid_t tracerpid;
+ ProcessStatInfo stat_info;
+ ProcessInstanceInfo process_info;
+
+ if (!GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid))
+ continue;
+
+ // Skip if process is being debugged.
+ if (tracerpid != 0)
+ continue;
+
+ // Skip zombies.
+ if (stat_info.fProcessState & eProcessStateZombie)
+ continue;
+
+ // Check for user match if we're not matching all users and not running as root.
+ if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))
+ continue;
+
+ if (match_info.Matches (process_info))
+ {
+ process_infos.Append (process_info);
+ }
+ }
+
+ closedir (dirproc);
+ }
+
+ return process_infos.GetSize();
+}
+
+bool
+Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach)
+{
+ bool tids_changed = false;
+ static const char procdir[] = "/proc/";
+ static const char taskdir[] = "/task/";
+ std::string process_task_dir = procdir + std::to_string(pid) + taskdir;
+ DIR *dirproc = opendir (process_task_dir.c_str());
+
+ if (dirproc)
+ {
+ struct dirent *direntry = NULL;
+ while ((direntry = readdir (dirproc)) != NULL)
+ {
+ if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name))
+ continue;
+
+ lldb::tid_t tid = atoi(direntry->d_name);
+ TidMap::iterator it = tids_to_attach.find(tid);
+ if (it == tids_to_attach.end())
+ {
+ tids_to_attach.insert(TidPair(tid, false));
+ tids_changed = true;
+ }
+ }
+ closedir (dirproc);
+ }
+
+ return tids_changed;
+}
+
+static bool
+GetELFProcessCPUType (const char *exe_path, ProcessInstanceInfo &process_info)
+{
+ // Clear the architecture.
+ process_info.GetArchitecture().Clear();
+
+ ModuleSpecList specs;
+ FileSpec filespec (exe_path, false);
+ const size_t num_specs = ObjectFile::GetModuleSpecifications (filespec, 0, 0, specs);
+ // GetModuleSpecifications() could fail if the executable has been deleted or is locked.
+ // But it shouldn't return more than 1 architecture.
+ assert(num_specs <= 1 && "Linux plugin supports only a single architecture");
+ if (num_specs == 1)
+ {
+ ModuleSpec module_spec;
+ if (specs.GetModuleSpecAtIndex (0, module_spec) && module_spec.GetArchitecture().IsValid())
+ {
+ process_info.GetArchitecture () = module_spec.GetArchitecture();
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid)
+{
+ tracerpid = 0;
+ process_info.Clear();
+ ::memset (&stat_info, 0, sizeof(stat_info));
+ stat_info.ppid = LLDB_INVALID_PROCESS_ID;
+
+ // Use special code here because proc/[pid]/exe is a symbolic link.
+ char link_path[PATH_MAX];
+ char exe_path[PATH_MAX] = "";
+ if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", pid) <= 0)
+ return false;
+
+ ssize_t len = readlink (link_path, exe_path, sizeof(exe_path) - 1);
+ if (len <= 0)
+ return false;
+
+ // readlink does not append a null byte.
+ exe_path[len] = 0;
+
+ // If the binary has been deleted, the link name has " (deleted)" appended.
+ // Remove if there.
+ static const ssize_t deleted_len = strlen(" (deleted)");
+ if (len > deleted_len &&
+ !strcmp(exe_path + len - deleted_len, " (deleted)"))
+ {
+ exe_path[len - deleted_len] = 0;
+ }
+ else
+ {
+ GetELFProcessCPUType (exe_path, process_info);
+ }
+
+ process_info.SetProcessID(pid);
+ process_info.GetExecutableFile().SetFile(exe_path, false);
+ process_info.GetArchitecture().MergeFrom(HostInfo::GetArchitecture());
+
+ lldb::DataBufferSP buf_sp;
+
+ // Get the process environment.
+ buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(pid, "environ");
+ Args &info_env = process_info.GetEnvironmentEntries();
+ char *next_var = (char *)buf_sp->GetBytes();
+ char *end_buf = next_var + buf_sp->GetByteSize();
+ while (next_var < end_buf && 0 != *next_var)
+ {
+ info_env.AppendArgument(next_var);
+ next_var += strlen(next_var) + 1;
+ }
+
+ // Get the command line used to start the process.
+ buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(pid, "cmdline");
+
+ // Grab Arg0 first, if there is one.
+ char *cmd = (char *)buf_sp->GetBytes();
+ if (cmd)
+ {
+ process_info.SetArg0(cmd);
+
+ // Now process any remaining arguments.
+ Args &info_args = process_info.GetArguments();
+ char *next_arg = cmd + strlen(cmd) + 1;
+ end_buf = cmd + buf_sp->GetByteSize();
+ while (next_arg < end_buf && 0 != *next_arg)
+ {
+ info_args.AppendArgument(next_arg);
+ next_arg += strlen(next_arg) + 1;
+ }
+ }
+
+ // Read /proc/$PID/stat to get our parent pid.
+ if (ReadProcPseudoFileStat (pid, stat_info))
+ {
+ process_info.SetParentProcessID (stat_info.ppid);
+ }
+
+ // Get User and Group IDs and get tracer pid.
+ GetLinuxProcessUserAndGroup (pid, process_info, tracerpid);
+
+ return true;
+}
+
+bool
+Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
+{
+ lldb::pid_t tracerpid;
+ ProcessStatInfo stat_info;
+
+ return GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid);
+}
+
+size_t
+Host::GetEnvironment (StringList &env)
+{
+ char **host_env = environ;
+ char *env_entry;
+ size_t i;
+ for (i=0; (env_entry = host_env[i]) != NULL; ++i)
+ env.AppendString(env_entry);
+ return i;
+}
+
+Error
+Host::ShellExpandArguments (ProcessLaunchInfo &launch_info)
+{
+ return Error("unimplemented");
+}
diff --git a/source/Host/linux/HostInfoLinux.cpp b/source/Host/linux/HostInfoLinux.cpp
new file mode 100644
index 000000000000..4732a2a571b6
--- /dev/null
+++ b/source/Host/linux/HostInfoLinux.cpp
@@ -0,0 +1,282 @@
+//===-- HostInfoLinux.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/Core/Log.h"
+#include "lldb/Host/linux/HostInfoLinux.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#include <algorithm>
+#include <mutex> // std::once
+
+using namespace lldb_private;
+
+namespace
+{
+struct HostInfoLinuxFields
+{
+ HostInfoLinuxFields()
+ : m_os_major(0)
+ , m_os_minor(0)
+ , m_os_update(0)
+ {
+ }
+
+ std::string m_distribution_id;
+ uint32_t m_os_major;
+ uint32_t m_os_minor;
+ uint32_t m_os_update;
+};
+
+HostInfoLinuxFields *g_fields = nullptr;
+}
+
+void
+HostInfoLinux::Initialize()
+{
+ HostInfoPosix::Initialize();
+
+ g_fields = new HostInfoLinuxFields();
+}
+
+uint32_t
+HostInfoLinux::GetMaxThreadNameLength()
+{
+ return 16;
+}
+
+bool
+HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update)
+{
+ static bool success = false;
+ static std::once_flag g_once_flag;
+ std::call_once(g_once_flag, []() {
+
+ struct utsname un;
+ if (uname(&un) == 0)
+ {
+ int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update);
+ if (status == 3)
+ success = true;
+ else
+ {
+ // Some kernels omit the update version, so try looking for just "X.Y" and
+ // set update to 0.
+ g_fields->m_os_update = 0;
+ status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor);
+ if (status == 2)
+ success = true;
+ }
+ }
+ });
+
+
+ major = g_fields->m_os_major;
+ minor = g_fields->m_os_minor;
+ update = g_fields->m_os_update;
+ return success;
+}
+
+bool
+HostInfoLinux::GetOSBuildString(std::string &s)
+{
+ struct utsname un;
+ ::memset(&un, 0, sizeof(utsname));
+ s.clear();
+
+ if (uname(&un) < 0)
+ return false;
+
+ s.assign(un.release);
+ return true;
+}
+
+bool
+HostInfoLinux::GetOSKernelDescription(std::string &s)
+{
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(utsname));
+ s.clear();
+
+ if (uname(&un) < 0)
+ return false;
+
+ s.assign(un.version);
+ return true;
+}
+
+llvm::StringRef
+HostInfoLinux::GetDistributionId()
+{
+ // Try to run 'lbs_release -i', and use that response
+ // for the distribution id.
+ static std::once_flag g_once_flag;
+ std::call_once(g_once_flag, []() {
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST));
+ if (log)
+ log->Printf("attempting to determine Linux distribution...");
+
+ // check if the lsb_release command exists at one of the
+ // following paths
+ const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"};
+
+ for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index)
+ {
+ const char *const get_distribution_info_exe = exe_paths[exe_index];
+ if (access(get_distribution_info_exe, F_OK))
+ {
+ // this exe doesn't exist, move on to next exe
+ if (log)
+ log->Printf("executable doesn't exist: %s", get_distribution_info_exe);
+ continue;
+ }
+
+ // execute the distribution-retrieval command, read output
+ std::string get_distribution_id_command(get_distribution_info_exe);
+ get_distribution_id_command += " -i";
+
+ FILE *file = popen(get_distribution_id_command.c_str(), "r");
+ if (!file)
+ {
+ if (log)
+ log->Printf("failed to run command: \"%s\", cannot retrieve "
+ "platform information",
+ get_distribution_id_command.c_str());
+ break;
+ }
+
+ // retrieve the distribution id string.
+ char distribution_id[256] = {'\0'};
+ if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL)
+ {
+ if (log)
+ log->Printf("distribution id command returned \"%s\"", distribution_id);
+
+ const char *const distributor_id_key = "Distributor ID:\t";
+ if (strstr(distribution_id, distributor_id_key))
+ {
+ // strip newlines
+ std::string id_string(distribution_id + strlen(distributor_id_key));
+ id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end());
+
+ // lower case it and convert whitespace to underscores
+ std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch)
+ {
+ return tolower(isspace(ch) ? '_' : ch);
+ });
+
+ g_fields->m_distribution_id = id_string;
+ if (log)
+ log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str());
+ }
+ else
+ {
+ if (log)
+ log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id);
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf("failed to retrieve distribution id, \"%s\" returned no"
+ " lines",
+ get_distribution_id_command.c_str());
+ }
+
+ // clean up the file
+ pclose(file);
+ }
+ });
+
+ return g_fields->m_distribution_id.c_str();
+}
+
+FileSpec
+HostInfoLinux::GetProgramFileSpec()
+{
+ static FileSpec g_program_filespec;
+
+ if (!g_program_filespec)
+ {
+ char exe_path[PATH_MAX];
+ ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
+ if (len > 0)
+ {
+ exe_path[len] = 0;
+ g_program_filespec.SetFile(exe_path, false);
+ }
+ }
+
+ return g_program_filespec;
+}
+
+bool
+HostInfoLinux::ComputeSupportExeDirectory(FileSpec &file_spec)
+{
+ if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) &&
+ file_spec.IsAbsolute() &&
+ file_spec.Exists())
+ return true;
+ file_spec.GetDirectory() = GetProgramFileSpec().GetDirectory();
+ return !file_spec.GetDirectory().IsEmpty();
+}
+
+bool
+HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec)
+{
+ FileSpec temp_file("/usr/lib/lldb", true);
+ file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str());
+ return true;
+}
+
+bool
+HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec)
+{
+ // XDG Base Directory Specification
+ // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb.
+ const char *xdg_data_home = getenv("XDG_DATA_HOME");
+ if (xdg_data_home && xdg_data_home[0])
+ {
+ std::string user_plugin_dir(xdg_data_home);
+ user_plugin_dir += "/lldb";
+ file_spec.GetDirectory().SetCString(user_plugin_dir.c_str());
+ }
+ else
+ file_spec.GetDirectory().SetCString("~/.local/share/lldb");
+ return true;
+}
+
+void
+HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64)
+{
+ HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64);
+
+ const char *distribution_id = GetDistributionId().data();
+
+ // On Linux, "unknown" in the vendor slot isn't what we want for the default
+ // triple. It's probably an artifact of config.guess.
+ if (arch_32.IsValid())
+ {
+ arch_32.SetDistributionId(distribution_id);
+ if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
+ arch_32.GetTriple().setVendorName(llvm::StringRef());
+ }
+ if (arch_64.IsValid())
+ {
+ arch_64.SetDistributionId(distribution_id);
+ if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
+ arch_64.GetTriple().setVendorName(llvm::StringRef());
+ }
+}
diff --git a/source/Host/linux/HostThreadLinux.cpp b/source/Host/linux/HostThreadLinux.cpp
new file mode 100644
index 000000000000..2312ced0107b
--- /dev/null
+++ b/source/Host/linux/HostThreadLinux.cpp
@@ -0,0 +1,52 @@
+//===-- HostThreadLinux.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/Core/DataBuffer.h"
+#include "lldb/Host/linux/HostThreadLinux.h"
+#include "Plugins/Process/Linux/ProcFileReader.h"
+
+#include "llvm/ADT/SmallVector.h"
+
+#include <pthread.h>
+
+using namespace lldb_private;
+
+HostThreadLinux::HostThreadLinux()
+ : HostThreadPosix()
+{
+}
+
+HostThreadLinux::HostThreadLinux(lldb::thread_t thread)
+ : HostThreadPosix(thread)
+{
+}
+
+void
+HostThreadLinux::SetName(lldb::thread_t thread, llvm::StringRef name)
+{
+#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__)
+ ::pthread_setname_np(thread, name.data());
+#else
+ (void) thread;
+ (void) name;
+#endif
+}
+
+void
+HostThreadLinux::GetName(lldb::thread_t thread, llvm::SmallVectorImpl<char> &name)
+{
+ // Read /proc/$TID/comm file.
+ lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(thread, "comm");
+ const char *comm_str = (const char *)buf_sp->GetBytes();
+ const char *cr_str = ::strchr(comm_str, '\n');
+ size_t length = cr_str ? (cr_str - comm_str) : strlen(comm_str);
+
+ name.clear();
+ name.append(comm_str, comm_str + length);
+}
diff --git a/source/Host/linux/LibcGlue.cpp b/source/Host/linux/LibcGlue.cpp
new file mode 100644
index 000000000000..63d026f76c62
--- /dev/null
+++ b/source/Host/linux/LibcGlue.cpp
@@ -0,0 +1,30 @@
+//===-- LibcGlue.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This file adds functions missing from libc on older versions of linux
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <lldb/Host/linux/Uio.h>
+#include <cerrno>
+
+#ifndef HAVE_PROCESS_VM_READV // If the syscall wrapper is not available, provide one.
+ssize_t process_vm_readv(::pid_t pid,
+ const struct iovec *local_iov, unsigned long liovcnt,
+ const struct iovec *remote_iov, unsigned long riovcnt,
+ unsigned long flags)
+{
+#ifdef HAVE_NR_PROCESS_VM_READV // If we have the syscall number, we can issue the syscall ourselves.
+ return syscall(__NR_process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags);
+#else // If not, let's pretend the syscall is not present.
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+#endif
diff --git a/source/Host/linux/ThisThread.cpp b/source/Host/linux/ThisThread.cpp
new file mode 100644
index 000000000000..1c68c8ba16d7
--- /dev/null
+++ b/source/Host/linux/ThisThread.cpp
@@ -0,0 +1,29 @@
+//===-- ThisThread.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/HostNativeThread.h"
+#include "lldb/Host/ThisThread.h"
+
+#include "llvm/ADT/SmallVector.h"
+
+#include <pthread.h>
+
+using namespace lldb_private;
+
+void
+ThisThread::SetName(llvm::StringRef name)
+{
+ HostNativeThread::SetName(::pthread_self(), name);
+}
+
+void
+ThisThread::GetName(llvm::SmallVectorImpl<char> &name)
+{
+ HostNativeThread::GetName(::pthread_self(), name);
+}
diff --git a/source/Host/macosx/Host.mm b/source/Host/macosx/Host.mm
new file mode 100644
index 000000000000..2db27a276e4a
--- /dev/null
+++ b/source/Host/macosx/Host.mm
@@ -0,0 +1,1587 @@
+//===-- 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 <AvailabilityMacros.h>
+
+#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+#define NO_XPC_SERVICES 1
+#endif
+
+#if !defined(NO_XPC_SERVICES)
+#define __XPC_PRIVATE_H__
+#include <xpc/xpc.h>
+
+#define LaunchUsingXPCRightName "com.apple.dt.Xcode.RootDebuggingXPCService"
+
+// These XPC messaging keys are used for communication between Host.mm and the XPC service.
+#define LauncherXPCServiceAuthKey "auth-key"
+#define LauncherXPCServiceArgPrefxKey "arg"
+#define LauncherXPCServiceEnvPrefxKey "env"
+#define LauncherXPCServiceCPUTypeKey "cpuType"
+#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags"
+#define LauncherXPCServiceStdInPathKeyKey "stdInPath"
+#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath"
+#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath"
+#define LauncherXPCServiceChildPIDKey "childPID"
+#define LauncherXPCServiceErrorTypeKey "errorType"
+#define LauncherXPCServiceCodeTypeKey "errorCode"
+
+#endif
+
+#include "llvm/Support/Host.h"
+
+#include <asl.h>
+#include <crt_externs.h>
+#include <grp.h>
+#include <libproc.h>
+#include <pwd.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Communication.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/StructuredData.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Endian.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/CleanUp.h"
+#include "lldb/Utility/NameMatches.h"
+
+#include "cfcpp/CFCBundle.h"
+#include "cfcpp/CFCMutableArray.h"
+#include "cfcpp/CFCMutableDictionary.h"
+#include "cfcpp/CFCReleaser.h"
+#include "cfcpp/CFCString.h"
+
+
+#include <objc/objc-auto.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Foundation/Foundation.h>
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#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 (file.GetFileType () == FileSpec::eFileTypeDirectory)
+ {
+ char path[PATH_MAX];
+ if (file.GetPath(path, sizeof(path)))
+ {
+ CFCBundle bundle (path);
+ if (bundle.GetPath (path, sizeof(path)))
+ {
+ bundle_directory.SetFile (path, false);
+ return true;
+ }
+ }
+ }
+#endif
+ bundle_directory.Clear();
+ return false;
+}
+
+
+bool
+Host::ResolveExecutableInBundle (FileSpec &file)
+{
+#if defined (__APPLE__)
+ if (file.GetFileType () == FileSpec::eFileTypeDirectory)
+ {
+ char path[PATH_MAX];
+ if (file.GetPath(path, sizeof(path)))
+ {
+ CFCBundle bundle (path);
+ CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
+ if (url.get())
+ {
+ if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path)))
+ {
+ file.SetFile(path, false);
+ return true;
+ }
+ }
+ }
+ }
+#endif
+ return false;
+}
+
+static void *
+AcceptPIDFromInferior (void *arg)
+{
+ const char *connect_url = (const char *)arg;
+ ConnectionFileDescriptor file_conn;
+ Error error;
+ if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess)
+ {
+ char pid_str[256];
+ ::memset (pid_str, 0, sizeof(pid_str));
+ ConnectionStatus status;
+ const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL);
+ if (pid_str_len > 0)
+ {
+ int pid = atoi (pid_str);
+ return (void *)(intptr_t)pid;
+ }
+ }
+ 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__)
+
+//static lldb::pid_t
+//LaunchInNewTerminalWithCommandFile
+//(
+// const char **argv,
+// const char **envp,
+// const char *working_dir,
+// const ArchSpec *arch_spec,
+// bool stop_at_entry,
+// bool disable_aslr
+//)
+//{
+// if (!argv || !argv[0])
+// return LLDB_INVALID_PROCESS_ID;
+//
+// OSStatus error = 0;
+//
+// FileSpec program (argv[0], false);
+//
+//
+// std::string unix_socket_name;
+//
+// char temp_file_path[PATH_MAX];
+// const char *tmpdir = ::getenv ("TMPDIR");
+// if (tmpdir == NULL)
+// tmpdir = "/tmp/";
+// ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString());
+//
+// if (::mktemp (temp_file_path) == NULL)
+// return LLDB_INVALID_PROCESS_ID;
+//
+// unix_socket_name.assign (temp_file_path);
+//
+// ::strlcat (temp_file_path, ".command", sizeof (temp_file_path));
+//
+// StreamFile command_file;
+// command_file.GetFile().Open (temp_file_path,
+// File::eOpenOptionWrite | File::eOpenOptionCanCreate,
+// lldb::eFilePermissionsDefault);
+//
+// if (!command_file.GetFile().IsValid())
+// return LLDB_INVALID_PROCESS_ID;
+//
+// FileSpec darwin_debug_file_spec;
+// if (!HostInfo::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec))
+// return LLDB_INVALID_PROCESS_ID;
+// darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
+//
+// if (!darwin_debug_file_spec.Exists())
+// return LLDB_INVALID_PROCESS_ID;
+//
+// char launcher_path[PATH_MAX];
+// darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
+// command_file.Printf("\"%s\" ", launcher_path);
+//
+// command_file.Printf("--unix-socket=%s ", unix_socket_name.c_str());
+//
+// if (arch_spec && arch_spec->IsValid())
+// {
+// command_file.Printf("--arch=%s ", arch_spec->GetArchitectureName());
+// }
+//
+// if (disable_aslr)
+// {
+// command_file.PutCString("--disable-aslr ");
+// }
+//
+// command_file.PutCString("-- ");
+//
+// if (argv)
+// {
+// for (size_t i=0; argv[i] != NULL; ++i)
+// {
+// command_file.Printf("\"%s\" ", argv[i]);
+// }
+// }
+// command_file.PutCString("\necho Process exited with status $?\n");
+// command_file.GetFile().Close();
+// if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0)
+// return LLDB_INVALID_PROCESS_ID;
+//
+// CFCMutableDictionary cf_env_dict;
+//
+// const bool can_create = true;
+// if (envp)
+// {
+// for (size_t i=0; envp[i] != NULL; ++i)
+// {
+// const char *env_entry = envp[i];
+// const char *equal_pos = strchr(env_entry, '=');
+// if (equal_pos)
+// {
+// std::string env_key (env_entry, equal_pos);
+// std::string env_val (equal_pos + 1);
+// CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8);
+// CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8);
+// cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create);
+// }
+// }
+// }
+//
+// LSApplicationParameters app_params;
+// ::memset (&app_params, 0, sizeof (app_params));
+// app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync;
+// app_params.argv = NULL;
+// app_params.environment = (CFDictionaryRef)cf_env_dict.get();
+//
+// CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL,
+// (const UInt8 *)temp_file_path,
+// strlen(temp_file_path),
+// false));
+//
+// CFCMutableArray urls;
+//
+// // Terminal.app will open the ".command" file we have created
+// // and run our process inside it which will wait at the entry point
+// // for us to attach.
+// urls.AppendValue(command_file_url.get());
+//
+//
+// lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+//
+// Error lldb_error;
+// // Sleep and wait a bit for debugserver to start to listen...
+// char connect_url[128];
+// ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str());
+//
+// // Spawn a new thread to accept incoming connection on the connect_url
+// // so we can grab the pid from the inferior
+// lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(),
+// AcceptPIDFromInferior,
+// connect_url,
+// &lldb_error);
+//
+// ProcessSerialNumber psn;
+// error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1);
+// if (error == noErr)
+// {
+// thread_result_t accept_thread_result = NULL;
+// if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error))
+// {
+// if (accept_thread_result)
+// {
+// pid = (intptr_t)accept_thread_result;
+//
+// // Wait for process to be stopped the entry point by watching
+// // for the process status to be set to SSTOP which indicates it it
+// // SIGSTOP'ed at the entry point
+// WaitForProcessToSIGSTOP (pid, 5);
+// }
+// }
+// }
+// else
+// {
+// Host::ThreadCancel (accept_thread, &lldb_error);
+// }
+//
+// return pid;
+//}
+
+const char *applscript_in_new_tty =
+"tell application \"Terminal\"\n"
+" do script \"%s\"\n"
+"end tell\n";
+
+
+const char *applscript_in_existing_tty = "\
+set the_shell_script to \"%s\"\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 Error
+LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX";
+ if (::mktemp (unix_socket_name) == NULL)
+ {
+ error.SetErrorString ("failed to make temporary path for a unix socket");
+ return error;
+ }
+
+ StreamString command;
+ FileSpec darwin_debug_file_spec;
+ if (!HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, darwin_debug_file_spec))
+ {
+ error.SetErrorString ("can't locate the 'darwin-debug' executable");
+ return error;
+ }
+
+ darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
+
+ if (!darwin_debug_file_spec.Exists())
+ {
+ error.SetErrorStringWithFormat ("the 'darwin-debug' executable doesn't exists at '%s'",
+ darwin_debug_file_spec.GetPath().c_str());
+ return error;
+ }
+
+ char launcher_path[PATH_MAX];
+ darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
+
+ const ArchSpec &arch_spec = launch_info.GetArchitecture();
+ // Only set the architecture if it is valid and if it isn't Haswell (x86_64h).
+ if (arch_spec.IsValid() && arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h)
+ command.Printf("arch -arch %s ", arch_spec.GetArchitectureName());
+
+ command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name);
+
+ if (arch_spec.IsValid())
+ command.Printf(" --arch=%s", arch_spec.GetArchitectureName());
+
+ FileSpec working_dir{launch_info.GetWorkingDirectory()};
+ if (working_dir)
+ command.Printf(" --working-dir '%s'", working_dir.GetCString());
+ else
+ {
+ char cwd[PATH_MAX];
+ if (getcwd(cwd, PATH_MAX))
+ command.Printf(" --working-dir '%s'", cwd);
+ }
+
+ if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR))
+ command.PutCString(" --disable-aslr");
+
+ // We are launching on this host in a terminal. So compare the environment on the host
+ // to what is supplied in the launch_info. Any items that aren't in the host environment
+ // need to be sent to darwin-debug. If we send all environment entries, we might blow the
+ // max command line length, so we only send user modified entries.
+ const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector ();
+
+ StringList host_env;
+ const size_t host_env_count = Host::GetEnvironment (host_env);
+
+ if (envp && envp[0])
+ {
+ const char *env_entry;
+ for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx)
+ {
+ bool add_entry = true;
+ for (size_t i=0; i<host_env_count; ++i)
+ {
+ const char *host_env_entry = host_env.GetStringAtIndex(i);
+ if (strcmp(env_entry, host_env_entry) == 0)
+ {
+ add_entry = false;
+ break;
+ }
+ }
+ if (add_entry)
+ {
+ command.Printf(" --env='%s'", env_entry);
+ }
+ }
+ }
+
+ command.PutCString(" -- ");
+
+ const char **argv = launch_info.GetArguments().GetConstArgumentVector ();
+ if (argv)
+ {
+ for (size_t i=0; argv[i] != NULL; ++i)
+ {
+ if (i==0)
+ command.Printf(" '%s'", exe_path);
+ else
+ command.Printf(" '%s'", argv[i]);
+ }
+ }
+ else
+ {
+ command.Printf(" '%s'", exe_path);
+ }
+ command.PutCString (" ; echo Process exited with status $?");
+ if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit))
+ command.PutCString (" ; exit");
+
+ StreamString applescript_source;
+
+ const char *tty_command = command.GetString().c_str();
+// if (tty_name && tty_name[0])
+// {
+// applescript_source.Printf (applscript_in_existing_tty,
+// tty_command,
+// tty_name);
+// }
+// else
+// {
+ applescript_source.Printf (applscript_in_new_tty,
+ tty_command);
+// }
+
+
+
+ const char *script_source = applescript_source.GetString().c_str();
+ //puts (script_source);
+ NSAppleScript* applescript = [[NSAppleScript alloc] initWithSource:[NSString stringWithCString:script_source encoding:NSUTF8StringEncoding]];
+
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ Error lldb_error;
+ // Sleep and wait a bit for debugserver to start to listen...
+ ConnectionFileDescriptor file_conn;
+ char connect_url[128];
+ ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name);
+
+ // Spawn a new thread to accept incoming connection on the connect_url
+ // so we can grab the pid from the inferior. We have to do this because we
+ // are sending an AppleScript that will launch a process in Terminal.app,
+ // in a shell and the shell will fork/exec a couple of times before we get
+ // to the process that we wanted to launch. So when our process actually
+ // gets launched, we will handshake with it and get the process ID for it.
+ HostThread accept_thread = ThreadLauncher::LaunchThread(unix_socket_name, AcceptPIDFromInferior, connect_url, &lldb_error);
+
+ [applescript executeAndReturnError:nil];
+
+ thread_result_t accept_thread_result = NULL;
+ lldb_error = accept_thread.Join(&accept_thread_result);
+ if (lldb_error.Success() && accept_thread_result)
+ {
+ pid = (intptr_t)accept_thread_result;
+
+ // Wait for process to be stopped at the entry point by watching
+ // for the process status to be set to SSTOP which indicates it it
+ // SIGSTOP'ed at the entry point
+ WaitForProcessToSIGSTOP(pid, 5);
+ }
+
+ FileSystem::Unlink(FileSpec{unix_socket_name, false});
+ [applescript release];
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ launch_info.SetProcessID (pid);
+ return error;
+}
+
+#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__)
+
+
+// On MacOSX CrashReporter will display a string for each shared library if
+// the shared library has an exported symbol named "__crashreporter_info__".
+
+static Mutex&
+GetCrashReporterMutex ()
+{
+ static Mutex g_mutex;
+ return g_mutex;
+}
+
+extern "C" {
+ const char *__crashreporter_info__ = NULL;
+}
+
+asm(".desc ___crashreporter_info__, 0x10");
+
+void
+Host::SetCrashDescriptionWithFormat (const char *format, ...)
+{
+ static StreamString g_crash_description;
+ Mutex::Locker locker (GetCrashReporterMutex ());
+
+ if (format)
+ {
+ va_list args;
+ va_start (args, format);
+ g_crash_description.GetString().clear();
+ g_crash_description.PrintfVarArg(format, args);
+ va_end (args);
+ __crashreporter_info__ = g_crash_description.GetData();
+ }
+ else
+ {
+ __crashreporter_info__ = NULL;
+ }
+}
+
+void
+Host::SetCrashDescription (const char *cstr)
+{
+ Mutex::Locker locker (GetCrashReporterMutex ());
+ static std::string g_crash_description;
+ if (cstr)
+ {
+ g_crash_description.assign (cstr);
+ __crashreporter_info__ = g_crash_description.c_str();
+ }
+ else
+ {
+ __crashreporter_info__ = NULL;
+ }
+}
+
+bool
+Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no)
+{
+#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<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL,
+ file_cfstr.get(),
+ kCFURLPOSIXPathStyle,
+ false));
+
+ if (log)
+ log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no);
+
+ long error;
+ BabelAESelInfo file_and_line_info =
+ {
+ 0, // reserved0
+ (int16_t)(line_no - 1), // fLineNumber (zero based line number)
+ 1, // fSelStart
+ 1024, // fSelEnd
+ 0, // reserved1
+ 0 // reserved2
+ };
+
+ AEKeyDesc file_and_line_desc;
+
+ error = ::AECreateDesc (typeUTF8Text,
+ &file_and_line_info,
+ sizeof (file_and_line_info),
+ &(file_and_line_desc.descContent));
+
+ if (error != noErr)
+ {
+ if (log)
+ log->Printf("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<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL));
+ error = ::LSOpenURLsWithRole (file_array.get(),
+ kLSRolesAll,
+ &file_and_line_desc,
+ &app_params,
+ &psn,
+ 1);
+
+ AEDisposeDesc (&(file_and_line_desc.descContent));
+
+ if (error != noErr)
+ {
+ if (log)
+ log->Printf("LSOpenURLsWithRole failed, error: %ld.\n", error);
+
+ return false;
+ }
+
+ return true;
+#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__)
+}
+
+size_t
+Host::GetEnvironment (StringList &env)
+{
+ char **host_env = *_NSGetEnviron();
+ char *env_entry;
+ size_t i;
+ for (i=0; (env_entry = host_env[i]) != NULL; ++i)
+ env.AppendString(env_entry);
+ return i;
+
+}
+
+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<int>(argc); ++i)
+ {
+ cstr = data.GetCStr(&offset);
+ if (cstr)
+ proc_args.AppendArgument(cstr);
+ }
+
+ Args &proc_env = process_info.GetEnvironmentEntries ();
+ while ((cstr = data.GetCStr(&offset)))
+ {
+ if (cstr[0] == '\0')
+ break;
+
+ if (check_for_ios_simulator)
+ {
+ if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0)
+ process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::IOS);
+ else
+ process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::MacOSX);
+ }
+
+ proc_env.AppendArgument(cstr);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ 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<struct kinfo_proc> kinfos;
+
+ int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
+
+ size_t pid_data_size = 0;
+ if (::sysctl (mib, 4, NULL, &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ // Add a few extra in case a few more show up
+ const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10;
+
+ kinfos.resize (estimated_pid_count);
+ pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
+
+ if (::sysctl (mib, 4, &kinfos[0], &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
+
+ bool all_users = match_info.GetMatchAllUsers();
+ const lldb::pid_t our_pid = getpid();
+ const uid_t our_uid = getuid();
+ for (size_t i = 0; i < actual_pid_count; i++)
+ {
+ const struct kinfo_proc &kinfo = kinfos[i];
+
+ bool kinfo_user_matches = false;
+ if (all_users)
+ kinfo_user_matches = true;
+ else
+ kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid;
+
+ // Special case, if lldb is being run as root we can attach to anything.
+ if (our_uid == 0)
+ kinfo_user_matches = true;
+
+ if (kinfo_user_matches == false || // Make sure the user is acceptable
+ static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) == our_pid || // Skip this process
+ kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero)
+ kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains...
+ kinfo.kp_proc.p_flag & P_TRACED || // Being debugged?
+ kinfo.kp_proc.p_flag & P_WEXIT || // Working on exiting?
+ kinfo.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta)
+ continue;
+
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID (kinfo.kp_proc.p_pid);
+ process_info.SetParentProcessID (kinfo.kp_eproc.e_ppid);
+ process_info.SetUserID (kinfo.kp_eproc.e_pcred.p_ruid);
+ process_info.SetGroupID (kinfo.kp_eproc.e_pcred.p_rgid);
+ process_info.SetEffectiveUserID (kinfo.kp_eproc.e_ucred.cr_uid);
+ if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
+ process_info.SetEffectiveGroupID (kinfo.kp_eproc.e_ucred.cr_groups[0]);
+ else
+ process_info.SetEffectiveGroupID (UINT32_MAX);
+
+ // Make sure our info matches before we go fetch the name and cpu type
+ if (match_info.Matches (process_info))
+ {
+ // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64
+ if (GetMacOSXProcessCPUType (process_info))
+ {
+ if (GetMacOSXProcessArgs (&match_info, process_info))
+ {
+ if (match_info.Matches (process_info))
+ process_infos.Append (process_info);
+ }
+ }
+ }
+ }
+ 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));
+ }
+}
+
+/*
+ A valid authorizationRef means that
+ - there is the LaunchUsingXPCRightName rights in the /etc/authorization
+ - we have successfully copied the rights to be send over the XPC wire
+ Once obtained, it will be valid for as long as the process lives.
+ */
+static AuthorizationRef authorizationRef = NULL;
+static Error
+getXPCAuthorization (ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));
+
+ if ((launch_info.GetUserID() == 0) && !authorizationRef)
+ {
+ OSStatus createStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
+ if (createStatus != errAuthorizationSuccess)
+ {
+ error.SetError(1, eErrorTypeGeneric);
+ error.SetErrorString("Can't create authorizationRef.");
+ if (log)
+ {
+ error.PutToLog(log, "%s", error.AsCString());
+ }
+ return error;
+ }
+
+ OSStatus rightsStatus = AuthorizationRightGet(LaunchUsingXPCRightName, NULL);
+ if (rightsStatus != errAuthorizationSuccess)
+ {
+ // No rights in the security database, Create it with the right prompt.
+ CFStringRef prompt = CFSTR("Xcode is trying to take control of a root process.");
+ CFStringRef keys[] = { CFSTR("en") };
+ CFTypeRef values[] = { prompt };
+ CFDictionaryRef promptDict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ CFStringRef keys1[] = { CFSTR("class"), CFSTR("group"), CFSTR("comment"), CFSTR("default-prompt"), CFSTR("shared") };
+ CFTypeRef values1[] = { CFSTR("user"), CFSTR("admin"), CFSTR(LaunchUsingXPCRightName), promptDict, kCFBooleanFalse };
+ CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ rightsStatus = AuthorizationRightSet(authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL);
+ CFRelease(promptDict);
+ CFRelease(dict);
+ }
+
+ OSStatus copyRightStatus = errAuthorizationDenied;
+ if (rightsStatus == errAuthorizationSuccess)
+ {
+ AuthorizationItem item1 = { LaunchUsingXPCRightName, 0, NULL, 0 };
+ AuthorizationItem items[] = {item1};
+ AuthorizationRights requestedRights = {1, items };
+ AuthorizationFlags authorizationFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
+ copyRightStatus = AuthorizationCopyRights(authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment, authorizationFlags, NULL);
+ }
+
+ if (copyRightStatus != errAuthorizationSuccess)
+ {
+ // Eventually when the commandline supports running as root and the user is not
+ // logged in in the current audit session, we will need the trick in gdb where
+ // we ask the user to type in the root passwd in the terminal.
+ error.SetError(2, eErrorTypeGeneric);
+ error.SetErrorStringWithFormat("Launching as root needs root authorization.");
+ if (log)
+ {
+ error.PutToLog(log, "%s", error.AsCString());
+ }
+
+ if (authorizationRef)
+ {
+ AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
+ authorizationRef = NULL;
+ }
+ }
+ }
+
+ return error;
+}
+#endif
+
+static Error
+LaunchProcessXPC(const char *exe_path, ProcessLaunchInfo &launch_info, lldb::pid_t &pid)
+{
+#if !NO_XPC_SERVICES
+ Error error = getXPCAuthorization(launch_info);
+ if (error.Fail())
+ return error;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));
+
+ uid_t requested_uid = launch_info.GetUserID();
+ const char *xpc_service = nil;
+ bool send_auth = false;
+ AuthorizationExternalForm extForm;
+ if (requested_uid == 0)
+ {
+ if (AuthorizationMakeExternalForm(authorizationRef, &extForm) == errAuthorizationSuccess)
+ {
+ send_auth = true;
+ }
+ else
+ {
+ error.SetError(3, eErrorTypeGeneric);
+ error.SetErrorStringWithFormat("Launching root via XPC needs to externalize authorization reference.");
+ if (log)
+ {
+ error.PutToLog(log, "%s", error.AsCString());
+ }
+ return error;
+ }
+ xpc_service = LaunchUsingXPCRightName;
+ }
+ else
+ {
+ error.SetError(4, eErrorTypeGeneric);
+ error.SetErrorStringWithFormat("Launching via XPC is only currently available for root.");
+ if (log)
+ {
+ error.PutToLog(log, "%s", error.AsCString());
+ }
+ return error;
+ }
+
+ xpc_connection_t conn = xpc_connection_create(xpc_service, NULL);
+
+ xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
+ xpc_type_t type = xpc_get_type(event);
+
+ if (type == XPC_TYPE_ERROR) {
+ if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
+ // The service has either canceled itself, crashed, or been terminated.
+ // The XPC connection is still valid and sending a message to it will re-launch the service.
+ // If the service is state-full, this is the time to initialize the new service.
+ return;
+ } else if (event == XPC_ERROR_CONNECTION_INVALID) {
+ // The service is invalid. Either the service name supplied to xpc_connection_create() is incorrect
+ // or we (this process) have canceled the service; we can do any cleanup of application state at this point.
+ // printf("Service disconnected");
+ return;
+ } else {
+ // printf("Unexpected error from service: %s", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
+ }
+
+ } else {
+ // printf("Received unexpected event in handler");
+ }
+ });
+
+ xpc_connection_set_finalizer_f (conn, xpc_finalizer_t(xpc_release));
+ xpc_connection_resume (conn);
+ xpc_object_t message = xpc_dictionary_create (nil, nil, 0);
+
+ if (send_auth)
+ {
+ xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes, sizeof(AuthorizationExternalForm));
+ }
+
+ PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, launch_info.GetArguments());
+ PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey, launch_info.GetEnvironmentEntries());
+
+ // Posix spawn stuff.
+ xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType());
+ xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, Host::GetPosixspawnFlags(launch_info));
+ const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
+ if (file_action && file_action->GetPath())
+ {
+ xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, file_action->GetPath());
+ }
+ file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
+ if (file_action && file_action->GetPath())
+ {
+ xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, file_action->GetPath());
+ }
+ file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
+ if (file_action && file_action->GetPath())
+ {
+ xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, file_action->GetPath());
+ }
+
+ xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message);
+ xpc_type_t returnType = xpc_get_type(reply);
+ if (returnType == XPC_TYPE_DICTIONARY)
+ {
+ pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey);
+ if (pid == 0)
+ {
+ int errorType = xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey);
+ int errorCode = xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey);
+
+ error.SetError(errorCode, eErrorTypeGeneric);
+ error.SetErrorStringWithFormat("Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode);
+ if (log)
+ {
+ error.PutToLog(log, "%s", error.AsCString());
+ }
+
+ if (authorizationRef)
+ {
+ AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
+ authorizationRef = NULL;
+ }
+ }
+ }
+ else if (returnType == XPC_TYPE_ERROR)
+ {
+ error.SetError(5, eErrorTypeGeneric);
+ error.SetErrorStringWithFormat("Problems with launching via XPC. XPC error : %s", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION));
+ if (log)
+ {
+ error.PutToLog(log, "%s", error.AsCString());
+ }
+ }
+
+ return error;
+#else
+ Error error;
+ return error;
+#endif
+}
+
+static bool
+ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info)
+{
+ bool result = false;
+
+#if !NO_XPC_SERVICES
+ bool launchingAsRoot = launch_info.GetUserID() == 0;
+ bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0;
+
+ if (launchingAsRoot && !currentUserIsRoot)
+ {
+ // If current user is already root, we don't need XPC's help.
+ result = true;
+ }
+#endif
+
+ return result;
+}
+
+Error
+Host::LaunchProcess (ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ char exe_path[PATH_MAX];
+ PlatformSP host_platform_sp (Platform::GetHostPlatform ());
+
+ ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), launch_info.GetArchitecture());
+
+ FileSpec::FileType file_type = exe_module_spec.GetFileSpec().GetFileType();
+ if (file_type != FileSpec::eFileTypeRegular)
+ {
+ lldb::ModuleSP exe_module_sp;
+ error = host_platform_sp->ResolveExecutable (exe_module_spec,
+ exe_module_sp,
+ NULL);
+
+ if (error.Fail())
+ return error;
+
+ if (exe_module_sp)
+ exe_module_spec.GetFileSpec() = exe_module_sp->GetFileSpec();
+ }
+
+ if (exe_module_spec.GetFileSpec().Exists())
+ {
+ exe_module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path));
+ }
+ else
+ {
+ launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path));
+ error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path);
+ return error;
+ }
+
+ if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY))
+ {
+#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__)
+ return LaunchInNewTerminalWithAppleScript (exe_path, launch_info);
+#else
+ error.SetErrorString ("launching a process in a new terminal is not supported on iOS devices");
+ return error;
+#endif
+ }
+
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ if (ShouldLaunchUsingXPC(launch_info))
+ {
+ error = LaunchProcessXPC(exe_path, launch_info, pid);
+ }
+ else
+ {
+ error = LaunchProcessPosixSpawn(exe_path, launch_info, pid);
+ }
+
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ {
+ // If all went well, then set the process ID into the launch info
+ launch_info.SetProcessID(pid);
+
+ // Make sure we reap any processes we spawn or we will have zombies.
+ if (!launch_info.MonitorProcess())
+ {
+ const bool monitor_signals = false;
+ Host::MonitorChildProcessCallback callback = nullptr;
+
+ if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus))
+ callback = Process::SetProcessExitStatus;
+
+ StartMonitoringChildProcess (callback,
+ NULL,
+ pid,
+ monitor_signals);
+ }
+ }
+ else
+ {
+ // Invalid process ID, something didn't go well
+ if (error.Success())
+ error.SetErrorString ("process launch failed for unknown reasons");
+ }
+ return error;
+}
+
+Error
+Host::ShellExpandArguments (ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments))
+ {
+ FileSpec expand_tool_spec;
+ if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec))
+ {
+ error.SetErrorString("could not get support executable directory for lldb-argdumper tool");
+ return error;
+ }
+ 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, 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().c_str());
+ }
+ }
+
+ return error;
+}
+
+HostThread
+Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, 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=%p, baton=%p, pid=%i, monitor_signals=%i) source = %p\n",
+ callback,
+ callback_baton,
+ (int)pid,
+ monitor_signals,
+ source);
+
+ if (source)
+ {
+ ::dispatch_source_set_cancel_handler (source, ^{
+ ::dispatch_release (source);
+ });
+ ::dispatch_source_set_event_handler (source, ^{
+
+ int status= 0;
+ int wait_pid = 0;
+ bool cancel = false;
+ bool exited = false;
+ do
+ {
+ wait_pid = ::waitpid (pid, &status, 0);
+ } while (wait_pid < 0 && errno == EINTR);
+
+ if (wait_pid >= 0)
+ {
+ int signal = 0;
+ int exit_status = 0;
+ const char *status_cstr = NULL;
+ if (WIFSTOPPED(status))
+ {
+ signal = WSTOPSIG(status);
+ status_cstr = "STOPPED";
+ }
+ else if (WIFEXITED(status))
+ {
+ exit_status = WEXITSTATUS(status);
+ status_cstr = "EXITED";
+ exited = true;
+ }
+ else if (WIFSIGNALED(status))
+ {
+ signal = WTERMSIG(status);
+ status_cstr = "SIGNALED";
+ exited = true;
+ exit_status = -1;
+ }
+ else
+ {
+ status_cstr = "???";
+ }
+
+ if (log)
+ log->Printf ("::waitpid (pid = %llu, &status, 0) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_status = %i",
+ pid,
+ wait_pid,
+ status,
+ status_cstr,
+ signal,
+ exit_status);
+
+ if (callback)
+ cancel = callback (callback_baton, 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);
+ }
+}
+
+lldb::DataBufferSP
+Host::GetAuxvData(lldb_private::Process *process)
+{
+ return lldb::DataBufferSP();
+}
diff --git a/source/Host/macosx/HostInfoMacOSX.mm b/source/Host/macosx/HostInfoMacOSX.mm
new file mode 100644
index 000000000000..f5a0540e8774
--- /dev/null
+++ b/source/Host/macosx/HostInfoMacOSX.mm
@@ -0,0 +1,372 @@
+//===-- 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/Core/Log.h"
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Utility/SafeMachO.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+// C++ Includes
+#include <string>
+
+// C inclues
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/syslimits.h>
+#include <sys/types.h>
+
+// Objective C/C++ includes
+#include <CoreFoundation/CoreFoundation.h>
+#include <Foundation/Foundation.h>
+#include <mach-o/dyld.h>
+#include <objc/objc-auto.h>
+
+// These are needed when compiling on systems
+// that do not yet have these definitions
+#include <AvailabilityMacros.h>
+#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 <TargetConditionals.h> // 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(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 (!support_dir_spec.Exists() || !support_dir_spec.IsDirectory())
+ {
+ 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::ComputeClangDirectory(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");
+ raw_path.resize(framework_pos);
+ raw_path.append("/Resources/Clang");
+ }
+ file_spec.SetFile (raw_path.c_str(), true);
+ return true;
+}
+
+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();
+ }
+ }
+}
diff --git a/source/Host/macosx/HostThreadMacOSX.mm b/source/Host/macosx/HostThreadMacOSX.mm
new file mode 100644
index 000000000000..c84a78efd9e6
--- /dev/null
+++ b/source/Host/macosx/HostThreadMacOSX.mm
@@ -0,0 +1,102 @@
+//===-- 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 <CoreFoundation/CoreFoundation.h>
+#include <Foundation/Foundation.h>
+
+#include <objc/objc-auto.h>
+#include <pthread.h>
+
+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)
+ {
+ // Register our thread with the collector if garbage collection is enabled.
+ if (objc_collectingEnabled())
+ {
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+ // On Leopard and earlier there is no way objc_registerThreadWithCollector
+ // function, so we do it manually.
+ auto_zone_register_thread(auto_zone());
+#else
+ // On SnowLeopard and later we just call the thread registration function.
+ objc_registerThreadWithCollector();
+#endif
+ }
+ else
+ {
+ 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<MacOSXDarwinThread *>(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);
+}
diff --git a/source/Host/macosx/Symbols.cpp b/source/Host/macosx/Symbols.cpp
new file mode 100644
index 000000000000..f6a18febe6da
--- /dev/null
+++ b/source/Host/macosx/Symbols.cpp
@@ -0,0 +1,559 @@
+//===-- Symbols.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/Symbols.h"
+
+// C Includes
+#include <dirent.h>
+#include <pwd.h>
+#include "lldb/Utility/SafeMachO.h"
+
+// C++ Includes
+// Other libraries and framework includes
+#include <CoreFoundation/CoreFoundation.h>
+
+// Project includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/DataBuffer.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Core/UUID.h"
+#include "lldb/Host/Endian.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/CleanUp.h"
+#include "Host/macosx/cfcpp/CFCBundle.h"
+#include "Host/macosx/cfcpp/CFCData.h"
+#include "Host/macosx/cfcpp/CFCReleaser.h"
+#include "Host/macosx/cfcpp/CFCString.h"
+#include "mach/machine.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::MachO;
+
+#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices
+extern "C" {
+
+CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
+CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
+
+}
+#endif
+
+int
+LocateMacOSXFilesUsingDebugSymbols
+(
+ const ModuleSpec &module_spec,
+ ModuleSpec &return_module_spec
+)
+{
+ return_module_spec = module_spec;
+ return_module_spec.GetFileSpec().Clear();
+ return_module_spec.GetSymbolFileSpec().Clear();
+
+ int items_found = 0;
+
+#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices
+
+ const UUID *uuid = module_spec.GetUUIDPtr();
+ const ArchSpec *arch = module_spec.GetArchitecturePtr();
+
+ if (uuid && uuid->IsValid())
+ {
+ // Try and locate the dSYM file using DebugSymbols first
+ const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes();
+ if (module_uuid != NULL)
+ {
+ CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL,
+ module_uuid[0],
+ module_uuid[1],
+ module_uuid[2],
+ module_uuid[3],
+ module_uuid[4],
+ module_uuid[5],
+ module_uuid[6],
+ module_uuid[7],
+ module_uuid[8],
+ module_uuid[9],
+ module_uuid[10],
+ module_uuid[11],
+ module_uuid[12],
+ module_uuid[13],
+ module_uuid[14],
+ module_uuid[15]));
+
+ if (module_uuid_ref.get())
+ {
+ CFCReleaser<CFURLRef> exec_url;
+ const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (exec_fspec)
+ {
+ char exec_cf_path[PATH_MAX];
+ if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
+ exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL,
+ (const UInt8 *)exec_cf_path,
+ strlen(exec_cf_path),
+ FALSE));
+ }
+
+ CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
+ char path[PATH_MAX];
+
+ if (dsym_url.get())
+ {
+ if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
+ {
+ if (log)
+ {
+ log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str());
+ }
+ FileSpec dsym_filespec(path, path[0] == '~');
+
+ if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory)
+ {
+ dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch);
+ ++items_found;
+ }
+ else
+ {
+ ++items_found;
+ }
+ return_module_spec.GetSymbolFileSpec() = dsym_filespec;
+ }
+
+ bool success = false;
+ if (log)
+ {
+ if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
+ {
+ log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str());
+ }
+
+ }
+
+ CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));
+ CFDictionaryRef uuid_dict = NULL;
+ if (dict.get())
+ {
+ CFCString uuid_cfstr (uuid->GetAsString().c_str());
+ uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get()));
+ }
+ if (uuid_dict)
+ {
+ CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable")));
+ if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path)))
+ {
+ if (log)
+ {
+ log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str());
+ }
+ ++items_found;
+ FileSpec exec_filespec (path, path[0] == '~');
+ if (exec_filespec.Exists())
+ {
+ success = true;
+ return_module_spec.GetFileSpec() = exec_filespec;
+ }
+ }
+ }
+
+ if (!success)
+ {
+ // No dictionary, check near the dSYM bundle for an executable that matches...
+ if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
+ {
+ char *dsym_extension_pos = ::strstr (path, ".dSYM");
+ if (dsym_extension_pos)
+ {
+ *dsym_extension_pos = '\0';
+ if (log)
+ {
+ log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path);
+ }
+ FileSpec file_spec (path, true);
+ ModuleSpecList module_specs;
+ ModuleSpec matched_module_spec;
+ switch (file_spec.GetFileType())
+ {
+ case FileSpec::eFileTypeDirectory: // Bundle directory?
+ {
+ CFCBundle bundle (path);
+ CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ());
+ if (bundle_exe_url.get())
+ {
+ if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1))
+ {
+ FileSpec bundle_exe_file_spec (path, true);
+ if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) &&
+ module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
+
+ {
+ ++items_found;
+ return_module_spec.GetFileSpec() = bundle_exe_file_spec;
+ if (log)
+ {
+ log->Printf ("Executable binary %s next to dSYM is compatible; using", path);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FileSpec::eFileTypePipe: // Forget pipes
+ case FileSpec::eFileTypeSocket: // We can't process socket files
+ case FileSpec::eFileTypeInvalid: // File doesn't exist...
+ break;
+
+ case FileSpec::eFileTypeUnknown:
+ case FileSpec::eFileTypeRegular:
+ case FileSpec::eFileTypeSymbolicLink:
+ case FileSpec::eFileTypeOther:
+ if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) &&
+ module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
+
+ {
+ ++items_found;
+ return_module_spec.GetFileSpec() = file_spec;
+ if (log)
+ {
+ log->Printf ("Executable binary %s next to dSYM is compatible; using", path);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__)
+
+ return items_found;
+}
+
+FileSpec
+Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec,
+ const lldb_private::UUID *uuid,
+ const ArchSpec *arch)
+{
+ char path[PATH_MAX];
+
+ FileSpec dsym_fspec;
+
+ if (dsym_bundle_fspec.GetPath(path, sizeof(path)))
+ {
+ ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
+
+ lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir);
+ if (dirp.is_valid())
+ {
+ dsym_fspec.GetDirectory().SetCString(path);
+ struct dirent* dp;
+ while ((dp = readdir(dirp.get())) != NULL)
+ {
+ // Only search directories
+ if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
+ {
+ if (dp->d_namlen == 1 && dp->d_name[0] == '.')
+ continue;
+
+ if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
+ continue;
+ }
+
+ if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN)
+ {
+ dsym_fspec.GetFilename().SetCString(dp->d_name);
+ ModuleSpecList module_specs;
+ if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs))
+ {
+ ModuleSpec spec;
+ for (size_t i = 0; i < module_specs.GetSize(); ++i)
+ {
+ assert(module_specs.GetModuleSpecAtIndex(i, spec));
+ if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
+ (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch))))
+ {
+ return dsym_fspec;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ dsym_fspec.Clear();
+ return dsym_fspec;
+}
+
+static bool
+GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec)
+{
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ bool success = false;
+ if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ())
+ {
+ std::string str;
+ CFStringRef cf_str;
+
+ cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable"));
+ if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
+ {
+ if (CFCString::FileSystemRepresentation(cf_str, str))
+ {
+ module_spec.GetFileSpec().SetFile (str.c_str(), true);
+ if (log)
+ {
+ log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str());
+ }
+ }
+ }
+
+ cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath"));
+ if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
+ {
+ if (CFCString::FileSystemRepresentation(cf_str, str))
+ {
+ module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true);
+ success = true;
+ if (log)
+ {
+ log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str());
+ }
+ }
+ }
+
+ cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture"));
+ if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
+ {
+ if (CFCString::FileSystemRepresentation(cf_str, str))
+ module_spec.GetArchitecture().SetTriple(str.c_str());
+ }
+
+ std::string DBGBuildSourcePath;
+ std::string DBGSourcePath;
+
+ cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath"));
+ if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
+ {
+ CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
+ }
+
+ cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath"));
+ if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
+ {
+ CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
+ }
+
+ if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty())
+ {
+ module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true);
+ }
+ }
+ return success;
+}
+
+
+bool
+Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup)
+{
+ bool success = false;
+ const UUID *uuid_ptr = module_spec.GetUUIDPtr();
+ const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
+
+ // It's expensive to check for the DBGShellCommands defaults setting, only do it once per
+ // lldb run and cache the result.
+ static bool g_have_checked_for_dbgshell_command = false;
+ static const char *g_dbgshell_command = NULL;
+ if (g_have_checked_for_dbgshell_command == false)
+ {
+ g_have_checked_for_dbgshell_command = true;
+ CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols"));
+ if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID())
+ {
+ char cstr_buf[PATH_MAX];
+ if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8))
+ {
+ g_dbgshell_command = strdup (cstr_buf); // this malloc'ed memory will never be freed
+ }
+ }
+ if (defaults_setting)
+ {
+ CFRelease (defaults_setting);
+ }
+ }
+
+ // When g_dbgshell_command is NULL, the user has not enabled the use of an external program
+ // to find the symbols, don't run it for them.
+ if (force_lookup == false && g_dbgshell_command == NULL)
+ {
+ return false;
+ }
+
+ if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists()))
+ {
+ static bool g_located_dsym_for_uuid_exe = false;
+ static bool g_dsym_for_uuid_exe_exists = false;
+ static char g_dsym_for_uuid_exe_path[PATH_MAX];
+ if (!g_located_dsym_for_uuid_exe)
+ {
+ g_located_dsym_for_uuid_exe = true;
+ const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
+ FileSpec dsym_for_uuid_exe_spec;
+ if (dsym_for_uuid_exe_path_cstr)
+ {
+ dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true);
+ g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
+ }
+
+ if (!g_dsym_for_uuid_exe_exists)
+ {
+ dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false);
+ g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
+ if (!g_dsym_for_uuid_exe_exists)
+ {
+ long bufsize;
+ if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1)
+ {
+ char buffer[bufsize];
+ struct passwd pwd;
+ struct passwd *tilde_rc = NULL;
+ // we are a library so we need to use the reentrant version of getpwnam()
+ if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0
+ && tilde_rc
+ && tilde_rc->pw_dir)
+ {
+ std::string dsymforuuid_path(tilde_rc->pw_dir);
+ dsymforuuid_path += "/bin/dsymForUUID";
+ dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false);
+ g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
+ }
+ }
+ }
+ }
+ if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL)
+ {
+ dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true);
+ g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
+ }
+
+ if (g_dsym_for_uuid_exe_exists)
+ dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path));
+ }
+ if (g_dsym_for_uuid_exe_exists)
+ {
+ std::string uuid_str;
+ char file_path[PATH_MAX];
+ file_path[0] = '\0';
+
+ if (uuid_ptr)
+ uuid_str = uuid_ptr->GetAsString();
+
+ if (file_spec_ptr)
+ file_spec_ptr->GetPath(file_path, sizeof(file_path));
+
+ StreamString command;
+ if (!uuid_str.empty())
+ command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str());
+ else if (file_path[0] != '\0')
+ command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path);
+
+ if (!command.GetString().empty())
+ {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ int exit_status = -1;
+ int signo = -1;
+ std::string command_output;
+ if (log)
+ {
+ if (!uuid_str.empty())
+ log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str());
+ else if (file_path[0] != '\0')
+ log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path);
+ }
+ Error error = Host::RunShellCommand (command.GetData(),
+ NULL, // current working directory
+ &exit_status, // Exit status
+ &signo, // Signal int *
+ &command_output, // Command output
+ 30, // Large timeout to allow for long dsym download times
+ false); // Don't run in a shell (we don't need shell expansion)
+ if (error.Success() && exit_status == 0 && !command_output.empty())
+ {
+ CFCData data (CFDataCreateWithBytesNoCopy (NULL,
+ (const UInt8 *)command_output.data(),
+ command_output.size(),
+ kCFAllocatorNull));
+
+ CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL));
+
+ if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ())
+ {
+ if (!uuid_str.empty())
+ {
+ CFCString uuid_cfstr(uuid_str.c_str());
+ CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get());
+ success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec);
+ }
+ else
+ {
+ const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
+ if (num_values > 0)
+ {
+ std::vector<CFStringRef> keys (num_values, NULL);
+ std::vector<CFDictionaryRef> values (num_values, NULL);
+ ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]);
+ if (num_values == 1)
+ {
+ return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec);
+ }
+ else
+ {
+ for (CFIndex i=0; i<num_values; ++i)
+ {
+ ModuleSpec curr_module_spec;
+ if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec))
+ {
+ if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture()))
+ {
+ module_spec = curr_module_spec;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ {
+ if (!uuid_str.empty())
+ log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str());
+ else if (file_path[0] != '\0')
+ log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path);
+ }
+ }
+ }
+ }
+ }
+ return success;
+}
+
diff --git a/source/Host/macosx/ThisThread.cpp b/source/Host/macosx/ThisThread.cpp
new file mode 100644
index 000000000000..95c7f2bf1e38
--- /dev/null
+++ b/source/Host/macosx/ThisThread.cpp
@@ -0,0 +1,39 @@
+//===-- ThisThread.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/ThisThread.h"
+
+#include <pthread.h>
+
+using namespace lldb_private;
+
+void
+ThisThread::SetName(llvm::StringRef name)
+{
+#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
+ ::pthread_setname_np(name);
+#endif
+}
+
+void
+ThisThread::GetName(llvm::SmallVectorImpl<char> &name)
+{
+#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
+ char pthread_name[1024];
+ dispatch_queue_t current_queue = ::dispatch_get_current_queue();
+ if (current_queue != NULL)
+ {
+ const char *queue_name = dispatch_queue_get_label(current_queue);
+ if (queue_name && queue_name[0])
+ {
+ name = queue_name;
+ }
+ }
+#endif
+}
diff --git a/source/Host/macosx/cfcpp/CFCBundle.cpp b/source/Host/macosx/cfcpp/CFCBundle.cpp
new file mode 100644
index 000000000000..71b074993661
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCBundle.cpp
@@ -0,0 +1,99 @@
+//===-- CFCBundle.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFCBundle.h"
+#include "CFCString.h"
+
+//----------------------------------------------------------------------
+// CFCBundle constructor
+//----------------------------------------------------------------------
+CFCBundle::CFCBundle(const char *path) :
+ CFCReleaser<CFBundleRef>()
+{
+ if (path && path[0])
+ SetPath(path);
+}
+
+CFCBundle::CFCBundle(CFURLRef url) :
+ CFCReleaser<CFBundleRef>(url ? CFBundleCreate(NULL, url) : NULL)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFCBundle::~CFCBundle()
+{
+}
+
+//----------------------------------------------------------------------
+// Set the path for a bundle by supplying a
+//----------------------------------------------------------------------
+bool
+CFCBundle::SetPath (const char *path)
+{
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ // Release our old bundle and URL
+ reset();
+
+ // Make a CFStringRef from the supplied path
+ CFCString cf_path;
+ cf_path.SetFileSystemRepresentation(path);
+ if (cf_path.get())
+ {
+ // Make our Bundle URL
+ CFCReleaser<CFURLRef> bundle_url (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true));
+ if (bundle_url.get())
+ reset (::CFBundleCreate (alloc, bundle_url.get()));
+ }
+ return get() != NULL;
+}
+
+bool
+CFCBundle::GetPath (char *dst, size_t dst_len)
+{
+ CFBundleRef bundle = get();
+ if (bundle)
+ {
+ CFCReleaser<CFURLRef> bundle_url (CFBundleCopyBundleURL (bundle));
+ if (bundle_url.get())
+ {
+ Boolean resolveAgainstBase = 0;
+ return ::CFURLGetFileSystemRepresentation (bundle_url.get(), resolveAgainstBase, (UInt8 *)dst, dst_len) != 0;
+ }
+ }
+ return false;
+}
+
+CFStringRef
+CFCBundle::GetIdentifier () const
+{
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return ::CFBundleGetIdentifier (bundle);
+ return NULL;
+}
+
+CFTypeRef
+CFCBundle::GetValueForInfoDictionaryKey(CFStringRef key) const
+{
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);
+ return NULL;
+}
+
+CFURLRef
+CFCBundle::CopyExecutableURL () const
+{
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return CFBundleCopyExecutableURL(bundle);
+ return NULL;
+}
diff --git a/source/Host/macosx/cfcpp/CFCBundle.h b/source/Host/macosx/cfcpp/CFCBundle.h
new file mode 100644
index 000000000000..1cd1b681af84
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCBundle.h
@@ -0,0 +1,50 @@
+//===-- CFCBundle.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CoreFoundationCPP_CFBundle_h_
+#define CoreFoundationCPP_CFBundle_h_
+
+#include "CFCReleaser.h"
+
+class CFCBundle : public CFCReleaser<CFBundleRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFCBundle (const char *path = NULL);
+ CFCBundle (CFURLRef url);
+
+ virtual
+ ~CFCBundle();
+
+ CFURLRef
+ CopyExecutableURL () const;
+
+ CFStringRef
+ GetIdentifier () const;
+
+ CFTypeRef
+ GetValueForInfoDictionaryKey(CFStringRef key) const;
+
+ bool
+ GetPath (char *dst, size_t dst_len);
+
+ bool
+ SetPath (const char *path);
+
+private:
+ // Disallow copy and assignment constructors
+ CFCBundle(const CFCBundle&);
+
+ const CFCBundle&
+ operator=(const CFCBundle&);
+};
+
+#endif // #ifndef CoreFoundationCPP_CFBundle_h_
diff --git a/source/Host/macosx/cfcpp/CFCData.cpp b/source/Host/macosx/cfcpp/CFCData.cpp
new file mode 100644
index 000000000000..4f49368ad8ad
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCData.cpp
@@ -0,0 +1,82 @@
+//===-- CFCData.cpp ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFCData.h"
+
+//----------------------------------------------------------------------
+// CFCData constructor
+//----------------------------------------------------------------------
+CFCData::CFCData(CFDataRef data) :
+ CFCReleaser<CFDataRef>(data)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFCData copy constructor
+//----------------------------------------------------------------------
+CFCData::CFCData(const CFCData& rhs) :
+ CFCReleaser<CFDataRef>(rhs)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFCData copy constructor
+//----------------------------------------------------------------------
+CFCData&
+CFCData::operator=(const CFCData& rhs)
+
+{
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFCData::~CFCData()
+{
+}
+
+
+CFIndex
+CFCData::GetLength() const
+{
+ CFDataRef data = get();
+ if (data)
+ return CFDataGetLength (data);
+ return 0;
+}
+
+
+const uint8_t*
+CFCData::GetBytePtr() const
+{
+ CFDataRef data = get();
+ if (data)
+ return CFDataGetBytePtr (data);
+ return NULL;
+}
+
+CFDataRef
+CFCData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format)
+{
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ reset();
+ CFCReleaser<CFWriteStreamRef> stream (::CFWriteStreamCreateWithAllocatedBuffers (alloc, alloc));
+ ::CFWriteStreamOpen (stream.get());
+ CFIndex len = ::CFPropertyListWriteToStream (plist, stream.get(), format, NULL);
+ if (len > 0)
+ reset((CFDataRef)::CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten));
+ ::CFWriteStreamClose (stream.get());
+ return get();
+}
+
diff --git a/source/Host/macosx/cfcpp/CFCData.h b/source/Host/macosx/cfcpp/CFCData.h
new file mode 100644
index 000000000000..6a718f54c055
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCData.h
@@ -0,0 +1,35 @@
+//===-- CFCData.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CoreFoundationCPP_CFData_h_
+#define CoreFoundationCPP_CFData_h_
+
+#include "CFCReleaser.h"
+
+class CFCData : public CFCReleaser<CFDataRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFCData(CFDataRef data = NULL);
+ CFCData(const CFCData& rhs);
+ CFCData& operator=(const CFCData& rhs);
+ virtual ~CFCData();
+
+ CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format);
+ const uint8_t* GetBytePtr () const;
+ CFIndex GetLength () const;
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from CFCData can see and modify these
+ //------------------------------------------------------------------
+};
+
+#endif // #ifndef CoreFoundationCPP_CFData_h_
diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.cpp b/source/Host/macosx/cfcpp/CFCMutableArray.cpp
new file mode 100644
index 000000000000..c3c0a11193a7
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCMutableArray.cpp
@@ -0,0 +1,166 @@
+//===-- CFCMutableArray.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFCMutableArray.h"
+#include "CFCString.h"
+
+//----------------------------------------------------------------------
+// CFCString constructor
+//----------------------------------------------------------------------
+CFCMutableArray::CFCMutableArray(CFMutableArrayRef s) :
+ CFCReleaser<CFMutableArrayRef> (s)
+{
+}
+
+//----------------------------------------------------------------------
+// CFCMutableArray copy constructor
+//----------------------------------------------------------------------
+CFCMutableArray::CFCMutableArray(const CFCMutableArray& rhs) :
+ CFCReleaser<CFMutableArrayRef> (rhs) // NOTE: this won't make a copy of the array, just add a new reference to it
+{
+}
+
+//----------------------------------------------------------------------
+// CFCMutableArray copy constructor
+//----------------------------------------------------------------------
+CFCMutableArray&
+CFCMutableArray::operator=(const CFCMutableArray& rhs)
+{
+ if (this != &rhs)
+ *this = rhs; // NOTE: this operator won't make a copy of the array, just add a new reference to it
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFCMutableArray::~CFCMutableArray()
+{
+}
+
+
+CFIndex
+CFCMutableArray::GetCount() const
+{
+ CFMutableArrayRef array = get();
+ if (array)
+ return ::CFArrayGetCount (array);
+ return 0;
+}
+
+CFIndex
+CFCMutableArray::GetCountOfValue(CFRange range, const void *value) const
+{
+ CFMutableArrayRef array = get();
+ if (array)
+ return ::CFArrayGetCountOfValue (array, range, value);
+ return 0;
+}
+
+CFIndex
+CFCMutableArray::GetCountOfValue(const void *value) const
+{
+ CFMutableArrayRef array = get();
+ if (array)
+ return ::CFArrayGetCountOfValue (array, CFRangeMake(0, GetCount()), value);
+ return 0;
+}
+
+const void *
+CFCMutableArray::GetValueAtIndex(CFIndex idx) const
+{
+ CFMutableArrayRef array = get();
+ if (array)
+ {
+ const CFIndex num_array_items = ::CFArrayGetCount (array);
+ if (0 <= idx && idx < num_array_items)
+ {
+ return ::CFArrayGetValueAtIndex (array, idx);
+ }
+ }
+ return NULL;
+}
+
+bool
+CFCMutableArray::SetValueAtIndex(CFIndex idx, const void *value)
+{
+ CFMutableArrayRef array = get();
+ if (array != NULL)
+ {
+ const CFIndex num_array_items = ::CFArrayGetCount (array);
+ if (0 <= idx && idx < num_array_items)
+ {
+ ::CFArraySetValueAtIndex (array, idx, value);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool
+CFCMutableArray::AppendValue(const void *value, bool can_create)
+{
+ CFMutableArrayRef array = get();
+ if (array == NULL)
+ {
+ if (can_create == false)
+ return false;
+ array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ reset ( array );
+ }
+ if (array != NULL)
+ {
+ ::CFArrayAppendValue(array, value);
+ return true;
+ }
+ return false;
+}
+
+
+bool
+CFCMutableArray::AppendCStringAsCFString (const char *s, CFStringEncoding encoding, bool can_create)
+{
+ CFMutableArrayRef array = get();
+ if (array == NULL)
+ {
+ if (can_create == false)
+ return false;
+ array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ reset ( array );
+ }
+ if (array != NULL)
+ {
+ CFCString cf_str (s, encoding);
+ ::CFArrayAppendValue (array, cf_str.get());
+ return true;
+ }
+ return false;
+}
+
+bool
+CFCMutableArray::AppendFileSystemRepresentationAsCFString (const char *s, bool can_create)
+{
+ CFMutableArrayRef array = get();
+ if (array == NULL)
+ {
+ if (can_create == false)
+ return false;
+ array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ reset ( array );
+ }
+ if (array != NULL)
+ {
+ CFCString cf_path;
+ cf_path.SetFileSystemRepresentation(s);
+ ::CFArrayAppendValue (array, cf_path.get());
+ return true;
+ }
+ return false;
+}
diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.h b/source/Host/macosx/cfcpp/CFCMutableArray.h
new file mode 100644
index 000000000000..f78cd92ffab1
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCMutableArray.h
@@ -0,0 +1,39 @@
+//===-- CFCMutableArray.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CoreFoundationCPP_CFMutableArray_h_
+#define CoreFoundationCPP_CFMutableArray_h_
+
+#include "CFCReleaser.h"
+
+class CFCMutableArray : public CFCReleaser<CFMutableArrayRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFCMutableArray(CFMutableArrayRef array = NULL);
+ CFCMutableArray(const CFCMutableArray& rhs); // This will copy the array contents into a new array
+ CFCMutableArray& operator=(const CFCMutableArray& rhs); // This will re-use the same array and just bump the ref count
+ virtual ~CFCMutableArray();
+
+ CFIndex GetCount() const;
+ CFIndex GetCountOfValue(const void *value) const;
+ CFIndex GetCountOfValue(CFRange range, const void *value) const;
+ const void * GetValueAtIndex(CFIndex idx) const;
+ bool SetValueAtIndex(CFIndex idx, const void *value);
+ bool AppendValue(const void *value, bool can_create = true); // Appends value and optionally creates a CFCMutableArray if this class doesn't contain one
+ bool AppendCStringAsCFString (const char *cstr,
+ CFStringEncoding encoding = kCFStringEncodingUTF8,
+ bool can_create = true);
+ bool AppendFileSystemRepresentationAsCFString (const char *s,
+ bool can_create = true);
+};
+
+#endif // #ifndef CoreFoundationCPP_CFMutableArray_h_
diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp
new file mode 100644
index 000000000000..bce023bfd616
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp
@@ -0,0 +1,529 @@
+//===-- CFCMutableDictionary.cpp --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFCMutableDictionary.h"
+#include "CFCString.h"
+//----------------------------------------------------------------------
+// CFCString constructor
+//----------------------------------------------------------------------
+CFCMutableDictionary::CFCMutableDictionary(CFMutableDictionaryRef s) :
+ CFCReleaser<CFMutableDictionaryRef> (s)
+{
+}
+
+//----------------------------------------------------------------------
+// CFCMutableDictionary copy constructor
+//----------------------------------------------------------------------
+CFCMutableDictionary::CFCMutableDictionary(const CFCMutableDictionary& rhs) :
+ CFCReleaser<CFMutableDictionaryRef> (rhs)
+{
+}
+
+//----------------------------------------------------------------------
+// CFCMutableDictionary copy constructor
+//----------------------------------------------------------------------
+const CFCMutableDictionary&
+CFCMutableDictionary::operator=(const CFCMutableDictionary& rhs)
+{
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFCMutableDictionary::~CFCMutableDictionary()
+{
+}
+
+
+CFIndex
+CFCMutableDictionary::GetCount() const
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ return ::CFDictionaryGetCount (dict);
+ return 0;
+}
+
+CFIndex
+CFCMutableDictionary::GetCountOfKey(const void *key) const
+
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ return ::CFDictionaryGetCountOfKey (dict, key);
+ return 0;
+}
+
+CFIndex
+CFCMutableDictionary::GetCountOfValue(const void *value) const
+
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ return ::CFDictionaryGetCountOfValue (dict, value);
+ return 0;
+}
+
+void
+CFCMutableDictionary::GetKeysAndValues(const void **keys, const void **values) const
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ ::CFDictionaryGetKeysAndValues (dict, keys, values);
+}
+
+
+const void *
+CFCMutableDictionary::GetValue(const void *key) const
+
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ return ::CFDictionaryGetValue (dict, key);
+ return NULL;
+}
+
+Boolean
+CFCMutableDictionary::GetValueIfPresent(const void *key, const void **value_handle) const
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ return ::CFDictionaryGetValueIfPresent (dict, key, value_handle);
+ return false;
+}
+
+
+CFMutableDictionaryRef
+CFCMutableDictionary::Dictionary(bool can_create)
+{
+ CFMutableDictionaryRef dict = get();
+ if (can_create && dict == NULL)
+ {
+ dict = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ reset ( dict );
+ }
+ return dict;
+}
+
+bool
+CFCMutableDictionary::AddValue(CFStringRef key, const void *value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, value);
+ return true;
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValue(CFStringRef key, const void *value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, value);
+ return true;
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueSInt8(CFStringRef key, int8_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueSInt8(CFStringRef key, int8_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueSInt16(CFStringRef key, int16_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueSInt16(CFStringRef key, int16_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueSInt32(CFStringRef key, int32_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueSInt32(CFStringRef key, int32_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueSInt64(CFStringRef key, int64_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueSInt64(CFStringRef key, int64_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueUInt8(CFStringRef key, uint8_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Have to promote to the next size type so things don't appear negative of the MSBit is set...
+ int16_t sval = value;
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueUInt8(CFStringRef key, uint8_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Have to promote to the next size type so things don't appear negative of the MSBit is set...
+ int16_t sval = value;
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool
+CFCMutableDictionary::AddValueUInt16(CFStringRef key, uint16_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Have to promote to the next size type so things don't appear negative of the MSBit is set...
+ int32_t sval = value;
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueUInt16(CFStringRef key, uint16_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Have to promote to the next size type so things don't appear negative of the MSBit is set...
+ int32_t sval = value;
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueUInt32(CFStringRef key, uint32_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Have to promote to the next size type so things don't appear negative of the MSBit is set...
+ int64_t sval = value;
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueUInt32(CFStringRef key, uint32_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // Have to promote to the next size type so things don't appear negative of the MSBit is set...
+ int64_t sval = value;
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool
+CFCMutableDictionary::AddValueUInt64(CFStringRef key, uint64_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // The number may appear negative if the MSBit is set in "value". Due to a limitation of
+ // CFNumber, there isn't a way to have it show up otherwise as of this writing.
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool
+CFCMutableDictionary::SetValueUInt64(CFStringRef key, uint64_t value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // The number may appear negative if the MSBit is set in "value". Due to a limitation of
+ // CFNumber, there isn't a way to have it show up otherwise as of this writing.
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueDouble(CFStringRef key, double value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // The number may appear negative if the MSBit is set in "value". Due to a limitation of
+ // CFNumber, there isn't a way to have it show up otherwise as of this writing.
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueDouble(CFStringRef key, double value, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ // The number may appear negative if the MSBit is set in "value". Due to a limitation of
+ // CFNumber, there isn't a way to have it show up otherwise as of this writing.
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value));
+ if (cf_number.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_number.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::AddValueCString(CFStringRef key, const char *cstr, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCString cf_str(cstr, kCFStringEncodingUTF8);
+ if (cf_str.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionaryAddValue (dict, key, cf_str.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CFCMutableDictionary::SetValueCString(CFStringRef key, const char *cstr, bool can_create)
+{
+ CFMutableDictionaryRef dict = Dictionary(can_create);
+ if (dict != NULL)
+ {
+ CFCString cf_str(cstr, kCFStringEncodingUTF8);
+ if (cf_str.get())
+ {
+ // Let the dictionary own the CFNumber
+ ::CFDictionarySetValue (dict, key, cf_str.get());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void
+CFCMutableDictionary::RemoveAllValues()
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ ::CFDictionaryRemoveAllValues(dict);
+}
+
+void
+CFCMutableDictionary::RemoveValue(const void *value)
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ ::CFDictionaryRemoveValue(dict, value);
+}
+void
+CFCMutableDictionary::ReplaceValue(const void *key, const void *value)
+{
+ CFMutableDictionaryRef dict = get();
+ if (dict)
+ ::CFDictionaryReplaceValue (dict, key, value);
+}
+
diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.h b/source/Host/macosx/cfcpp/CFCMutableDictionary.h
new file mode 100644
index 000000000000..a1cfb68f569e
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.h
@@ -0,0 +1,79 @@
+//===-- CFCMutableDictionary.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CoreFoundationCPP_CFMutableDictionary_h_
+#define CoreFoundationCPP_CFMutableDictionary_h_
+
+#include "CFCReleaser.h"
+
+class CFCMutableDictionary : public CFCReleaser<CFMutableDictionaryRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFCMutableDictionary(CFMutableDictionaryRef s = NULL);
+ CFCMutableDictionary(const CFCMutableDictionary& rhs);
+ virtual ~CFCMutableDictionary();
+
+ //------------------------------------------------------------------
+ // Operators
+ //------------------------------------------------------------------
+ const CFCMutableDictionary&
+ operator=(const CFCMutableDictionary& rhs);
+
+
+ CFIndex GetCount() const;
+ CFIndex GetCountOfKey(const void *value) const;
+ CFIndex GetCountOfValue(const void *value) const;
+ void GetKeysAndValues(const void **keys, const void **values) const;
+ const void * GetValue(const void *key) const;
+ Boolean GetValueIfPresent(const void *key, const void **value_handle) const;
+ bool AddValue(CFStringRef key, const void *value, bool can_create = false);
+ bool SetValue(CFStringRef key, const void *value, bool can_create = false);
+ bool AddValueSInt8(CFStringRef key, int8_t value, bool can_create = false);
+ bool SetValueSInt8(CFStringRef key, int8_t value, bool can_create = false);
+ bool AddValueSInt16(CFStringRef key, int16_t value, bool can_create = false);
+ bool SetValueSInt16(CFStringRef key, int16_t value, bool can_create = false);
+ bool AddValueSInt32(CFStringRef key, int32_t value, bool can_create = false);
+ bool SetValueSInt32(CFStringRef key, int32_t value, bool can_create = false);
+ bool AddValueSInt64(CFStringRef key, int64_t value, bool can_create = false);
+ bool SetValueSInt64(CFStringRef key, int64_t value, bool can_create = false);
+ bool AddValueUInt8(CFStringRef key, uint8_t value, bool can_create = false);
+ bool SetValueUInt8(CFStringRef key, uint8_t value, bool can_create = false);
+ bool AddValueUInt16(CFStringRef key, uint16_t value, bool can_create = false);
+ bool SetValueUInt16(CFStringRef key, uint16_t value, bool can_create = false);
+ bool AddValueUInt32(CFStringRef key, uint32_t value, bool can_create = false);
+ bool SetValueUInt32(CFStringRef key, uint32_t value, bool can_create = false);
+ bool AddValueUInt64(CFStringRef key, uint64_t value, bool can_create = false);
+ bool SetValueUInt64(CFStringRef key, uint64_t value, bool can_create = false);
+ bool AddValueDouble(CFStringRef key, double value, bool can_create = false);
+ bool SetValueDouble(CFStringRef key, double value, bool can_create = false);
+ bool AddValueCString(CFStringRef key, const char *cstr, bool can_create = false);
+ bool SetValueCString(CFStringRef key, const char *cstr, bool can_create = false);
+ void RemoveValue(const void *value);
+ void ReplaceValue(const void *key, const void *value);
+ void RemoveAllValues();
+ CFMutableDictionaryRef Dictionary(bool can_create);
+
+
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from CFCMutableDictionary can see and modify these
+ //------------------------------------------------------------------
+
+private:
+ //------------------------------------------------------------------
+ // For CFCMutableDictionary only
+ //------------------------------------------------------------------
+
+};
+
+
+#endif // CoreFoundationCPP_CFMutableDictionary_h_
diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.cpp b/source/Host/macosx/cfcpp/CFCMutableSet.cpp
new file mode 100644
index 000000000000..afc09e180b6b
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCMutableSet.cpp
@@ -0,0 +1,114 @@
+//===-- CFCMutableSet.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFCMutableSet.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+
+//----------------------------------------------------------------------
+// CFCString constructor
+//----------------------------------------------------------------------
+CFCMutableSet::CFCMutableSet(CFMutableSetRef s) :
+ CFCReleaser<CFMutableSetRef> (s)
+{
+}
+
+//----------------------------------------------------------------------
+// CFCMutableSet copy constructor
+//----------------------------------------------------------------------
+CFCMutableSet::CFCMutableSet(const CFCMutableSet& rhs) :
+ CFCReleaser<CFMutableSetRef> (rhs)
+{
+}
+
+//----------------------------------------------------------------------
+// CFCMutableSet copy constructor
+//----------------------------------------------------------------------
+const CFCMutableSet&
+CFCMutableSet::operator=(const CFCMutableSet& rhs)
+{
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFCMutableSet::~CFCMutableSet()
+{
+}
+
+
+CFIndex
+CFCMutableSet::GetCount() const
+{
+ CFMutableSetRef set = get();
+ if (set)
+ return ::CFSetGetCount (set);
+ return 0;
+}
+
+CFIndex
+CFCMutableSet::GetCountOfValue(const void *value) const
+{
+ CFMutableSetRef set = get();
+ if (set)
+ return ::CFSetGetCountOfValue (set, value);
+ return 0;
+}
+
+const void *
+CFCMutableSet::GetValue(const void *value) const
+{
+ CFMutableSetRef set = get();
+ if (set)
+ return ::CFSetGetValue(set, value);
+ return NULL;
+}
+
+
+const void *
+CFCMutableSet::AddValue(const void *value, bool can_create)
+{
+ CFMutableSetRef set = get();
+ if (set == NULL)
+ {
+ if (can_create == false)
+ return NULL;
+ set = ::CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
+ reset ( set );
+ }
+ if (set != NULL)
+ {
+ ::CFSetAddValue(set, value);
+ return value;
+ }
+ return NULL;
+}
+
+void
+CFCMutableSet::RemoveValue(const void *value)
+{
+ CFMutableSetRef set = get();
+ if (set)
+ ::CFSetRemoveValue(set, value);
+}
+
+void
+CFCMutableSet::RemoveAllValues()
+{
+ CFMutableSetRef set = get();
+ if (set)
+ ::CFSetRemoveAllValues(set);
+}
+
diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.h b/source/Host/macosx/cfcpp/CFCMutableSet.h
new file mode 100644
index 000000000000..78f7a8be81d2
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCMutableSet.h
@@ -0,0 +1,53 @@
+//===-- CFCMutableSet.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CoreFoundationCPP_CFMutableSet_h_
+#define CoreFoundationCPP_CFMutableSet_h_
+
+#include "CFCReleaser.h"
+
+class CFCMutableSet : public CFCReleaser<CFMutableSetRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFCMutableSet(CFMutableSetRef s = NULL);
+ CFCMutableSet(const CFCMutableSet& rhs);
+ virtual ~CFCMutableSet();
+
+ //------------------------------------------------------------------
+ // Operators
+ //------------------------------------------------------------------
+ const CFCMutableSet&
+ operator=(const CFCMutableSet& rhs);
+
+
+ CFIndex GetCount() const;
+ CFIndex GetCountOfValue(const void *value) const;
+ const void * GetValue(const void *value) const;
+ const void * AddValue(const void *value, bool can_create);
+ void RemoveValue(const void *value);
+ void RemoveAllValues();
+
+
+
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from CFCMutableSet can see and modify these
+ //------------------------------------------------------------------
+
+private:
+ //------------------------------------------------------------------
+ // For CFCMutableSet only
+ //------------------------------------------------------------------
+
+};
+
+#endif // CoreFoundationCPP_CFMutableSet_h_
diff --git a/source/Host/macosx/cfcpp/CFCReleaser.h b/source/Host/macosx/cfcpp/CFCReleaser.h
new file mode 100644
index 000000000000..67dd2ead5799
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCReleaser.h
@@ -0,0 +1,158 @@
+//===-- CFCReleaser.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CoreFoundationCPP_CFReleaser_h_
+#define CoreFoundationCPP_CFReleaser_h_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#ifdef __cplusplus
+
+#include <assert.h>
+
+//----------------------------------------------------------------------
+// Templatized CF helper class that can own any CF pointer and will
+// call CFRelease() on any valid pointer it owns unless that pointer is
+// explicitly released using the release() member function. This class
+// is designed to mimic the std::auto_ptr<T> class and has all of the
+// same functions. The one thing to watch out for is the
+// CFCReleaser<T>::release() function won't actually CFRelease any owned
+// pointer, it is designed to relinquish ownership of the pointer just
+// like std:auto_ptr<T>::release() does.
+//----------------------------------------------------------------------
+template <class T>
+class CFCReleaser
+{
+public:
+ //----------------------------------------------------------
+ // Constructor that takes a pointer to a CF object that is
+ // to be released when this object goes out of scope
+ //----------------------------------------------------------
+ CFCReleaser(T ptr = NULL) :
+ _ptr(ptr)
+ {
+ }
+
+ //----------------------------------------------------------
+ // Copy constructor
+ //
+ // Note that copying a CFCReleaser will not transfer
+ // ownership of the contained pointer, but it will bump its
+ // reference count. This is where this class differs from
+ // std::auto_ptr.
+ //----------------------------------------------------------
+ CFCReleaser(const CFCReleaser& rhs) :
+ _ptr(rhs.get())
+ {
+ if (get())
+ ::CFRetain(get());
+ }
+
+
+ //----------------------------------------------------------
+ // The destructor will release the pointer that it contains
+ // if it has a valid pointer.
+ //----------------------------------------------------------
+ virtual ~CFCReleaser()
+ {
+ reset();
+ }
+
+ //----------------------------------------------------------
+ // Assignment operator.
+ //
+ // Note that assigning one CFCReleaser to another will
+ // not transfer ownership of the contained pointer, but it
+ // will bump its reference count. This is where this class
+ // differs from std::auto_ptr.
+ //----------------------------------------------------------
+ CFCReleaser&
+ operator= (const CFCReleaser<T>& rhs)
+ {
+ if (this != &rhs)
+ {
+ // Replace our owned pointer with the new one
+ reset(rhs.get());
+ // Retain the current pointer that we own
+ if (get())
+ ::CFRetain(get());
+ }
+ return *this;
+ }
+
+ //----------------------------------------------------------
+ // Get the address of the contained type in case it needs
+ // to be passed to a function that will fill in a pointer
+ // value. The function currently will assert if _ptr is not
+ // NULL because the only time this method should be used is
+ // if another function will modify the contents, and we
+ // could leak a pointer if this is not NULL. If the
+ // assertion fires, check the offending code, or call
+ // reset() prior to using the "ptr_address()" member to make
+ // sure any owned objects has CFRelease called on it.
+ // I had to add the "enforce_null" bool here because some
+ // API's require the pointer address even though they don't change it.
+ //----------------------------------------------------------
+ T*
+ ptr_address(bool enforce_null = true)
+ {
+ if (enforce_null)
+ assert (_ptr == NULL);
+ return &_ptr;
+ }
+
+ //----------------------------------------------------------
+ // Access the pointer itself
+ //----------------------------------------------------------
+ T
+ get()
+ {
+ return _ptr;
+ }
+
+ const T
+ get() const
+ {
+ return _ptr;
+ }
+
+
+ //----------------------------------------------------------
+ // Set a new value for the pointer and CFRelease our old
+ // value if we had a valid one.
+ //----------------------------------------------------------
+ void
+ reset(T ptr = NULL)
+ {
+ if ((_ptr != NULL) && (ptr != _ptr))
+ ::CFRelease(_ptr);
+ _ptr = ptr;
+ }
+
+ //----------------------------------------------------------
+ // Release ownership without calling CFRelease. This class
+ // is designed to mimic std::auto_ptr<T>, so the release
+ // method releases ownership of the contained pointer
+ // and does NOT call CFRelease.
+ //----------------------------------------------------------
+ T
+ release()
+ {
+ T tmp = _ptr;
+ _ptr = NULL;
+ return tmp;
+ }
+
+private:
+ T _ptr;
+};
+
+#endif // #ifdef __cplusplus
+#endif // #ifndef CoreFoundationCPP_CFReleaser_h_
+
diff --git a/source/Host/macosx/cfcpp/CFCString.cpp b/source/Host/macosx/cfcpp/CFCString.cpp
new file mode 100644
index 000000000000..81a96b824999
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCString.cpp
@@ -0,0 +1,195 @@
+//===-- CFCString.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFCString.h"
+#include <string>
+#include <glob.h>
+
+//----------------------------------------------------------------------
+// CFCString constructor
+//----------------------------------------------------------------------
+CFCString::CFCString(CFStringRef s) :
+ CFCReleaser<CFStringRef> (s)
+{
+}
+
+//----------------------------------------------------------------------
+// CFCString copy constructor
+//----------------------------------------------------------------------
+CFCString::CFCString(const CFCString& rhs) :
+ CFCReleaser<CFStringRef> (rhs)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFCString copy constructor
+//----------------------------------------------------------------------
+CFCString&
+CFCString::operator=(const CFCString& rhs)
+{
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+CFCString::CFCString (const char *cstr, CFStringEncoding cstr_encoding) :
+ CFCReleaser<CFStringRef> ()
+{
+ if (cstr && cstr[0])
+ {
+ reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
+ }
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFCString::~CFCString()
+{
+}
+
+const char *
+CFCString::GetFileSystemRepresentation(std::string& s)
+{
+ return CFCString::FileSystemRepresentation(get(), s);
+}
+
+CFStringRef
+CFCString::SetFileSystemRepresentation (const char *path)
+{
+ CFStringRef new_value = NULL;
+ if (path && path[0])
+ new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path);
+ reset(new_value);
+ return get();
+}
+
+
+CFStringRef
+CFCString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type)
+{
+ CFStringRef new_value = NULL;
+ if (cf_type != NULL)
+ {
+ CFTypeID cf_type_id = ::CFGetTypeID(cf_type);
+
+ if (cf_type_id == ::CFStringGetTypeID())
+ {
+ // Retain since we are using the existing object
+ new_value = (CFStringRef)::CFRetain(cf_type);
+ }
+ else if (cf_type_id == ::CFURLGetTypeID())
+ {
+ new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle);
+ }
+ }
+ reset(new_value);
+ return get();
+}
+
+CFStringRef
+CFCString::SetFileSystemRepresentationAndExpandTilde (const char *path)
+{
+ std::string expanded_path;
+ if (CFCString::ExpandTildeInPath(path, expanded_path))
+ SetFileSystemRepresentation(expanded_path.c_str());
+ else
+ reset();
+ return get();
+}
+
+const char *
+CFCString::UTF8(std::string& str)
+{
+ return CFCString::UTF8(get(), str);
+}
+
+// Static function that puts a copy of the UTF8 contents of CF_STR into STR
+// and returns the C string pointer that is contained in STR when successful, else
+// NULL is returned. This allows the std::string parameter to own the extracted string,
+// and also allows that string to be returned as a C string pointer that can be used.
+
+const char *
+CFCString::UTF8 (CFStringRef cf_str, std::string& str)
+{
+ if (cf_str)
+ {
+ const CFStringEncoding encoding = kCFStringEncodingUTF8;
+ CFIndex max_utf8_str_len = CFStringGetLength (cf_str);
+ max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding);
+ if (max_utf8_str_len > 0)
+ {
+ str.resize(max_utf8_str_len);
+ if (!str.empty())
+ {
+ if (CFStringGetCString (cf_str, &str[0], str.size(), encoding))
+ {
+ str.resize(strlen(str.c_str()));
+ return str.c_str();
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+const char*
+CFCString::ExpandTildeInPath(const char* path, std::string &expanded_path)
+{
+ glob_t globbuf;
+ if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0)
+ {
+ expanded_path = globbuf.gl_pathv[0];
+ ::globfree (&globbuf);
+ }
+ else
+ expanded_path.clear();
+
+ return expanded_path.c_str();
+}
+
+// Static function that puts a copy of the file system representation of CF_STR
+// into STR and returns the C string pointer that is contained in STR when
+// successful, else NULL is returned. This allows the std::string parameter
+// to own the extracted string, and also allows that string to be returned as
+// a C string pointer that can be used.
+
+const char *
+CFCString::FileSystemRepresentation (CFStringRef cf_str, std::string& str)
+{
+ if (cf_str)
+ {
+ CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str);
+ if (max_length > 0)
+ {
+ str.resize(max_length);
+ if (!str.empty())
+ {
+ if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size()))
+ {
+ str.erase(::strlen(str.c_str()));
+ return str.c_str();
+ }
+ }
+ }
+ }
+ str.erase();
+ return NULL;
+}
+
+
+CFIndex
+CFCString::GetLength() const
+{
+ CFStringRef str = get();
+ if (str)
+ return CFStringGetLength (str);
+ return 0;
+}
diff --git a/source/Host/macosx/cfcpp/CFCString.h b/source/Host/macosx/cfcpp/CFCString.h
new file mode 100644
index 000000000000..27c090313ece
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CFCString.h
@@ -0,0 +1,41 @@
+//===-- CFCString.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CoreFoundationCPP_CFString_h_
+#define CoreFoundationCPP_CFString_h_
+
+#include <iosfwd>
+
+#include "CFCReleaser.h"
+
+class CFCString : public CFCReleaser<CFStringRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFCString (CFStringRef cf_str = NULL);
+ CFCString (const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8);
+ CFCString (const CFCString& rhs);
+ CFCString& operator= (const CFCString& rhs);
+ virtual ~CFCString ();
+
+ const char * GetFileSystemRepresentation (std::string& str);
+ CFStringRef SetFileSystemRepresentation (const char *path);
+ CFStringRef SetFileSystemRepresentationFromCFType (CFTypeRef cf_type);
+ CFStringRef SetFileSystemRepresentationAndExpandTilde (const char *path);
+ const char * UTF8 (std::string& str);
+ CFIndex GetLength() const;
+ static const char *UTF8 (CFStringRef cf_str, std::string& str);
+ static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str);
+ static const char *ExpandTildeInPath(const char* path, std::string &expanded_path);
+
+};
+
+#endif // #ifndef CoreFoundationCPP_CFString_h_
diff --git a/source/Host/macosx/cfcpp/CoreFoundationCPP.h b/source/Host/macosx/cfcpp/CoreFoundationCPP.h
new file mode 100644
index 000000000000..6843e2649cda
--- /dev/null
+++ b/source/Host/macosx/cfcpp/CoreFoundationCPP.h
@@ -0,0 +1,30 @@
+//===-- CoreFoundationCPP.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// CoreFoundationCPP.h
+// CoreFoundationCPP
+//
+// Created by Greg Clayton on 4/23/09.
+//
+//
+//----------------------------------------------------------------------
+
+#ifndef CoreFoundationCPP_CoreFoundationCPP_H_
+#define CoreFoundationCPP_CoreFoundationCPP_H_
+
+#include <CoreFoundationCPP/CFCBundle.h>
+#include <CoreFoundationCPP/CFCData.h>
+#include <CoreFoundationCPP/CFCReleaser.h>
+#include <CoreFoundationCPP/CFCMutableArray.h>
+#include <CoreFoundationCPP/CFCMutableDictionary.h>
+#include <CoreFoundationCPP/CFCMutableSet.h>
+#include <CoreFoundationCPP/CFCString.h>
+
+#endif // CoreFoundationCPP_CoreFoundationCPP_H_
diff --git a/source/Host/netbsd/Makefile b/source/Host/netbsd/Makefile
new file mode 100644
index 000000000000..2502cc49c15b
--- /dev/null
+++ b/source/Host/netbsd/Makefile
@@ -0,0 +1,14 @@
+##===- source/Host/netbsd/Makefile --------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LLDB_LEVEL := ../../..
+LIBRARYNAME := lldbHostNetBSD
+BUILD_ARCHIVE = 1
+
+include $(LLDB_LEVEL)/Makefile
diff --git a/source/Host/windows/Condition.cpp b/source/Host/windows/Condition.cpp
new file mode 100644
index 000000000000..2f16ad77d7a3
--- /dev/null
+++ b/source/Host/windows/Condition.cpp
@@ -0,0 +1,98 @@
+//===-- Condition.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+
+#include "lldb/Host/Condition.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Host/windows/windows.h"
+
+
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Default constructor
+//
+// The default constructor will initialize a new pthread condition
+// and maintain the condition in the object state.
+//----------------------------------------------------------------------
+Condition::Condition () :
+ m_condition()
+{
+ m_condition = static_cast<PCONDITION_VARIABLE>(malloc(sizeof(CONDITION_VARIABLE)));
+ InitializeConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition));
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//
+// Destroys the pthread condition that the object owns.
+//----------------------------------------------------------------------
+Condition::~Condition ()
+{
+ free(m_condition);
+}
+
+//----------------------------------------------------------------------
+// Unblock all threads waiting for a condition variable
+//----------------------------------------------------------------------
+int
+Condition::Broadcast ()
+{
+ WakeAllConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition));
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Unblocks one thread waiting for the condition variable
+//----------------------------------------------------------------------
+int
+Condition::Signal ()
+{
+ WakeConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition));
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// The Wait() function atomically blocks the current thread
+// waiting on the owned condition variable, and unblocks the mutex
+// specified by "mutex". The waiting thread unblocks only after
+// another thread calls Signal(), or Broadcast() with the same
+// condition variable, or if "abstime" is valid (non-NULL) this
+// function will return when the system time reaches the time
+// specified in "abstime". If "abstime" is NULL this function will
+// wait for an infinite amount of time for the condition variable
+// to be signaled or broadcasted.
+//
+// The current thread re-acquires the lock on "mutex".
+//----------------------------------------------------------------------
+int
+Condition::Wait (Mutex &mutex, const TimeValue *abstime, bool *timed_out)
+{
+ DWORD wait = INFINITE;
+ if (abstime != NULL) {
+ int wval = (*abstime - TimeValue::Now()) / 1000000;
+ if (wval < 0) wval = 0;
+
+ wait = wval;
+ }
+
+ int err = SleepConditionVariableCS(static_cast<PCONDITION_VARIABLE>(m_condition), static_cast<PCRITICAL_SECTION>(mutex.m_mutex), wait);
+
+ if (timed_out != NULL)
+ {
+ if ((err == 0) && GetLastError() == ERROR_TIMEOUT)
+ *timed_out = true;
+ else
+ *timed_out = false;
+ }
+
+ return err == 0;
+}
+
diff --git a/source/Host/windows/ConnectionGenericFileWindows.cpp b/source/Host/windows/ConnectionGenericFileWindows.cpp
new file mode 100644
index 000000000000..eebf3d4f633c
--- /dev/null
+++ b/source/Host/windows/ConnectionGenericFileWindows.cpp
@@ -0,0 +1,354 @@
+//===-- ConnectionGenericFileWindows.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/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Host/windows/ConnectionGenericFileWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+// This is a simple helper class to package up the information needed to return from a Read/Write
+// operation function. Since there is a lot of code to be run before exit regardless of whether the
+// operation succeeded or failed, combined with many possible return paths, this is the cleanest
+// way to represent it.
+class ReturnInfo
+{
+ public:
+ void
+ Set(size_t bytes, ConnectionStatus status, DWORD error_code)
+ {
+ m_error.SetError(error_code, eErrorTypeWin32);
+ m_bytes = bytes;
+ m_status = status;
+ }
+
+ void
+ Set(size_t bytes, ConnectionStatus status, llvm::StringRef error_msg)
+ {
+ m_error.SetErrorString(error_msg.data());
+ m_bytes = bytes;
+ m_status = status;
+ }
+
+ size_t
+ GetBytes() const
+ {
+ return m_bytes;
+ }
+ ConnectionStatus
+ GetStatus() const
+ {
+ return m_status;
+ }
+ const Error &
+ GetError() const
+ {
+ return m_error;
+ }
+
+ private:
+ Error m_error;
+ size_t m_bytes;
+ ConnectionStatus m_status;
+};
+}
+
+ConnectionGenericFile::ConnectionGenericFile()
+ : m_file(INVALID_HANDLE_VALUE)
+ , m_owns_file(false)
+{
+ ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
+ ::ZeroMemory(&m_file_position, sizeof(m_file_position));
+ InitializeEventHandles();
+}
+
+ConnectionGenericFile::ConnectionGenericFile(lldb::file_t file, bool owns_file)
+ : m_file(file)
+ , m_owns_file(owns_file)
+{
+ ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
+ ::ZeroMemory(&m_file_position, sizeof(m_file_position));
+ InitializeEventHandles();
+}
+
+ConnectionGenericFile::~ConnectionGenericFile()
+{
+ if (m_owns_file && IsConnected())
+ ::CloseHandle(m_file);
+
+ ::CloseHandle(m_event_handles[kBytesAvailableEvent]);
+ ::CloseHandle(m_event_handles[kInterruptEvent]);
+}
+
+void
+ConnectionGenericFile::InitializeEventHandles()
+{
+ m_event_handles[kInterruptEvent] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ // Note, we should use a manual reset event for the hEvent argument of the OVERLAPPED. This
+ // is because both WaitForMultipleObjects and GetOverlappedResult (if you set the bWait
+ // argument to TRUE) will wait for the event to be signalled. If we use an auto-reset event,
+ // WaitForMultipleObjects will reset the event, return successfully, and then
+ // GetOverlappedResult will block since the event is no longer signalled.
+ m_event_handles[kBytesAvailableEvent] = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+bool
+ConnectionGenericFile::IsConnected() const
+{
+ return m_file && (m_file != INVALID_HANDLE_VALUE);
+}
+
+lldb::ConnectionStatus
+ConnectionGenericFile::Connect(const char *s, Error *error_ptr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p ConnectionGenericFile::Connect (url = '%s')", static_cast<void *>(this), s);
+
+ if (strstr(s, "file://") != s)
+ {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s);
+ return eConnectionStatusError;
+ }
+
+ if (IsConnected())
+ {
+ ConnectionStatus status = Disconnect(error_ptr);
+ if (status != eConnectionStatusSuccess)
+ return status;
+ }
+
+ // file://PATH
+ const char *path = s + strlen("file://");
+ // Open the file for overlapped access. If it does not exist, create it. We open it overlapped
+ // so that we can issue asynchronous reads and then use WaitForMultipleObjects to allow the read
+ // to be interrupted by an event object.
+ m_file = ::CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
+ if (m_file == INVALID_HANDLE_VALUE)
+ {
+ if (error_ptr)
+ error_ptr->SetError(::GetLastError(), eErrorTypeWin32);
+ return eConnectionStatusError;
+ }
+
+ m_owns_file = true;
+ m_uri.assign(s);
+ return eConnectionStatusSuccess;
+}
+
+lldb::ConnectionStatus
+ConnectionGenericFile::Disconnect(Error *error_ptr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p ConnectionGenericFile::Disconnect ()", static_cast<void *>(this));
+
+ if (!IsConnected())
+ return eConnectionStatusSuccess;
+
+ // Reset the handle so that after we unblock any pending reads, subsequent calls to Read() will
+ // see a disconnected state.
+ HANDLE old_file = m_file;
+ m_file = INVALID_HANDLE_VALUE;
+
+ // Set the disconnect event so that any blocking reads unblock, then cancel any pending IO operations.
+ ::CancelIoEx(old_file, &m_overlapped);
+
+ // Close the file handle if we owned it, but don't close the event handles. We could always
+ // reconnect with the same Connection instance.
+ if (m_owns_file)
+ ::CloseHandle(old_file);
+
+ ::ZeroMemory(&m_file_position, sizeof(m_file_position));
+ m_owns_file = false;
+ m_uri.clear();
+ return eConnectionStatusSuccess;
+}
+
+size_t
+ConnectionGenericFile::Read(void *dst, size_t dst_len, uint32_t timeout_usec, lldb::ConnectionStatus &status, Error *error_ptr)
+{
+ ReturnInfo return_info;
+ BOOL result = 0;
+ DWORD bytes_read = 0;
+
+ if (error_ptr)
+ error_ptr->Clear();
+
+ if (!IsConnected())
+ {
+ return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
+ goto finish;
+ }
+
+ m_overlapped.hEvent = m_event_handles[kBytesAvailableEvent];
+
+ result = ::ReadFile(m_file, dst, dst_len, NULL, &m_overlapped);
+ if (result || ::GetLastError() == ERROR_IO_PENDING)
+ {
+ if (!result)
+ {
+ // The expected return path. The operation is pending. Wait for the operation to complete
+ // or be interrupted.
+ TimeValue time_value;
+ time_value.OffsetWithMicroSeconds(timeout_usec);
+ DWORD milliseconds = time_value.milliseconds();
+ DWORD wait_result = ::WaitForMultipleObjects(llvm::array_lengthof(m_event_handles), m_event_handles, FALSE, milliseconds);
+ // All of the events are manual reset events, so make sure we reset them to non-signalled.
+ switch (wait_result)
+ {
+ case WAIT_OBJECT_0 + kBytesAvailableEvent:
+ break;
+ case WAIT_OBJECT_0 + kInterruptEvent:
+ return_info.Set(0, eConnectionStatusInterrupted, 0);
+ goto finish;
+ case WAIT_TIMEOUT:
+ return_info.Set(0, eConnectionStatusTimedOut, 0);
+ goto finish;
+ case WAIT_FAILED:
+ return_info.Set(0, eConnectionStatusError, ::GetLastError());
+ goto finish;
+ }
+ }
+ // The data is ready. Figure out how much was read and return;
+ if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_read, FALSE))
+ {
+ DWORD result_error = ::GetLastError();
+ // ERROR_OPERATION_ABORTED occurs when someone calls Disconnect() during a blocking read.
+ // This triggers a call to CancelIoEx, which causes the operation to complete and the
+ // result to be ERROR_OPERATION_ABORTED.
+ if (result_error == ERROR_HANDLE_EOF || result_error == ERROR_OPERATION_ABORTED || result_error == ERROR_BROKEN_PIPE)
+ return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
+ else
+ return_info.Set(bytes_read, eConnectionStatusError, result_error);
+ }
+ else if (bytes_read == 0)
+ return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
+ else
+ return_info.Set(bytes_read, eConnectionStatusSuccess, 0);
+
+ goto finish;
+ }
+ else if (::GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ // The write end of a pipe was closed. This is equivalent to EOF.
+ return_info.Set(0, eConnectionStatusEndOfFile, 0);
+ }
+ else
+ {
+ // An unknown error occurred. Fail out.
+ return_info.Set(0, eConnectionStatusError, ::GetLastError());
+ }
+ goto finish;
+
+finish:
+ status = return_info.GetStatus();
+ if (error_ptr)
+ *error_ptr = return_info.GetError();
+
+ // kBytesAvailableEvent is a manual reset event. Make sure it gets reset here so that any
+ // subsequent operations don't immediately see bytes available.
+ ResetEvent(m_event_handles[kBytesAvailableEvent]);
+
+ IncrementFilePointer(return_info.GetBytes());
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ {
+ log->Printf("%" PRIxPTR " ConnectionGenericFile::Read() handle = %" PRIxPTR ", dst = %" PRIxPTR ", dst_len = %" PRIu64
+ ") => %" PRIu64 ", error = %s",
+ this, m_file, dst, static_cast<uint64_t>(dst_len), static_cast<uint64_t>(return_info.GetBytes()),
+ return_info.GetError().AsCString());
+ }
+
+ return return_info.GetBytes();
+}
+
+size_t
+ConnectionGenericFile::Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr)
+{
+ ReturnInfo return_info;
+ DWORD bytes_written = 0;
+ BOOL result = 0;
+
+ if (error_ptr)
+ error_ptr->Clear();
+
+ if (!IsConnected())
+ {
+ return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
+ goto finish;
+ }
+
+ m_overlapped.hEvent = NULL;
+
+ // Writes are not interruptible like reads are, so just block until it's done.
+ result = ::WriteFile(m_file, src, src_len, NULL, &m_overlapped);
+ if (!result && ::GetLastError() != ERROR_IO_PENDING)
+ {
+ return_info.Set(0, eConnectionStatusError, ::GetLastError());
+ goto finish;
+ }
+
+ if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_written, TRUE))
+ {
+ return_info.Set(bytes_written, eConnectionStatusError, ::GetLastError());
+ goto finish;
+ }
+
+ return_info.Set(bytes_written, eConnectionStatusSuccess, 0);
+ goto finish;
+
+finish:
+ status = return_info.GetStatus();
+ if (error_ptr)
+ *error_ptr = return_info.GetError();
+
+ IncrementFilePointer(return_info.GetBytes());
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ {
+ log->Printf("%" PRIxPTR " ConnectionGenericFile::Write() handle = %" PRIxPTR ", src = %" PRIxPTR ", src_len = %" PRIu64
+ ") => %" PRIu64 ", error = %s",
+ this, m_file, src, static_cast<uint64_t>(src_len), static_cast<uint64_t>(return_info.GetBytes()),
+ return_info.GetError().AsCString());
+ }
+ return return_info.GetBytes();
+}
+
+std::string
+ConnectionGenericFile::GetURI()
+{
+ return m_uri;
+}
+
+bool
+ConnectionGenericFile::InterruptRead()
+{
+ return ::SetEvent(m_event_handles[kInterruptEvent]);
+}
+
+void
+ConnectionGenericFile::IncrementFilePointer(DWORD amount)
+{
+ LARGE_INTEGER old_pos;
+ old_pos.HighPart = m_overlapped.OffsetHigh;
+ old_pos.LowPart = m_overlapped.Offset;
+ old_pos.QuadPart += amount;
+ m_overlapped.Offset = old_pos.LowPart;
+ m_overlapped.OffsetHigh = old_pos.HighPart;
+}
diff --git a/source/Host/windows/EditLineWin.cpp b/source/Host/windows/EditLineWin.cpp
new file mode 100644
index 000000000000..55fe52dc8ccd
--- /dev/null
+++ b/source/Host/windows/EditLineWin.cpp
@@ -0,0 +1,435 @@
+//===-- EditLineWin.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// this file is only relevant for Visual C++
+#if defined( _WIN32 )
+
+#include "lldb/Host/windows/windows.h"
+
+#include "lldb/Host/windows/editlinewin.h"
+#include <vector>
+#include <assert.h>
+
+// edit line EL_ADDFN function pointer type
+typedef unsigned char(*el_addfn_func)(EditLine *e, int ch);
+typedef const char* (*el_prompt_func)(EditLine *);
+
+// edit line wrapper binding container
+struct el_binding
+{
+ //
+ const char *name;
+ const char *help;
+ // function pointer to callback routine
+ el_addfn_func func;
+ // ascii key this function is bound to
+ const char *key;
+};
+
+// stored key bindings
+static std::vector<el_binding*> _bindings;
+
+//TODO: this should in fact be related to the exact edit line context we create
+static void *clientData = NULL;
+
+// store the current prompt string
+// default to what we expect to receive anyway
+static const char *_prompt = "(lldb) ";
+
+#if !defined( _WIP_INPUT_METHOD )
+
+static char *
+el_get_s (char *buffer, int chars)
+{
+ return gets_s(buffer, chars);
+}
+#else
+
+static void
+con_output (char _in)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get the cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // output this char
+ WriteConsoleOutputCharacterA( hout, &_in, 1, info.dwCursorPosition, &written );
+ // advance cursor position
+ info.dwCursorPosition.X++;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+}
+
+static void
+con_backspace (void)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // nudge cursor backwards
+ info.dwCursorPosition.X--;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+ // blank out the last character
+ WriteConsoleOutputCharacterA( hout, " ", 1, info.dwCursorPosition, &written );
+}
+
+static void
+con_return (void)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // move onto the new line
+ info.dwCursorPosition.X = 0;
+ info.dwCursorPosition.Y++;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+}
+
+static bool
+runBind (char _key)
+{
+ for ( int i=0; i<_bindings.size(); i++ )
+ {
+ el_binding *bind = _bindings[i];
+ if ( bind->key[0] == _key )
+ {
+ bind->func( (EditLine*) -1, _key );
+ return true;
+ }
+ }
+ return false;
+}
+
+// replacement get_s which is EL_BIND aware
+static char *
+el_get_s (char *buffer, int chars)
+{
+ //
+ char *head = buffer;
+ //
+ for ( ;; Sleep( 10 ) )
+ {
+ //
+ INPUT_RECORD _record;
+ //
+ DWORD _read = 0;
+ if ( ReadConsoleInputA( GetStdHandle( STD_INPUT_HANDLE ), &_record, 1, &_read ) == FALSE )
+ break;
+ // if we didn't read a key
+ if ( _read == 0 )
+ continue;
+ // only interested in key events
+ if ( _record.EventType != KEY_EVENT )
+ continue;
+ // is the key down
+ if (! _record.Event.KeyEvent.bKeyDown )
+ continue;
+ // read the ascii key character
+ char _key = _record.Event.KeyEvent.uChar.AsciiChar;
+ // non ascii conformant key press
+ if ( _key == 0 )
+ {
+ // check the scan code
+ // if VK_UP scroll back through history
+ // if VK_DOWN scroll forward through history
+ continue;
+ }
+ // try to execute any bind this key may have
+ if ( runBind( _key ) )
+ continue;
+ // if we read a return key
+ if ( _key == '\n' || _key == '\r' )
+ {
+ con_return( );
+ break;
+ }
+ // key is backspace
+ if ( _key == 0x8 )
+ {
+ // avoid deleting past beginning
+ if ( head > buffer )
+ {
+ con_backspace( );
+ head--;
+ }
+ continue;
+ }
+
+ // add this key to the input buffer
+ if ( (head-buffer) < (chars-1) )
+ {
+ con_output( _key );
+ *(head++) = _key;
+ }
+ }
+ // insert end of line character
+ *head = '\0';
+
+ return buffer;
+}
+#endif
+
+// edit line initialize
+EditLine *
+el_init (const char *, FILE *, FILE *, FILE *)
+{
+ //
+ SetConsoleTitleA( "lldb" );
+ // return dummy handle
+ return (EditLine*) -1;
+}
+
+const char *
+el_gets (EditLine *el, int *length)
+{
+ // print the prompt if we have one
+ if ( _prompt != NULL )
+ printf("%s", _prompt);
+ // create a buffer for the user input
+ char *buffer = new char[ MAX_PATH ];
+ // try to get user input string
+ if ( el_get_s( buffer, MAX_PATH ) )
+ {
+ // get the string length in 'length'
+ while ( buffer[ *length ] != '\0' )
+ (*length)++;
+ // return the input buffer
+ // remember that this memory has the be free'd somewhere
+ return buffer;
+ }
+ else
+ {
+ // on error
+ delete [] buffer;
+ return NULL;
+ }
+}
+
+int
+el_set (EditLine *el, int code, ...)
+{
+ va_list vl;
+ va_start(vl, code);
+ //
+ switch ( code )
+ {
+ // edit line set prompt message
+ case ( EL_PROMPT ):
+ {
+ // EL_PROMPT, char *(*f)( EditLine *)
+ // define a prompt printing function as 'f', which is to return a string that
+ // contains the prompt.
+
+ // get the function pointer from the arg list
+ void *func_vp = (void*)va_arg(vl, el_prompt_func);
+ // cast to suitable prototype
+ el_prompt_func func_fp = (el_prompt_func)func_vp;
+ // call to get the prompt as a string
+ _prompt = func_fp( el );
+ }
+ break;
+
+ case (EL_PROMPT_ESC) :
+ {
+ // EL_PROMPT, char *(*f)( EditLine *)
+ // define a prompt printing function as 'f', which is to return a string that
+ // contains the prompt.
+
+ // get the function pointer from the arg list
+ void *func_vp = (void*)va_arg(vl, el_prompt_func);
+ va_arg(vl, int);
+ // call to get the prompt as a string
+ el_prompt_func func_fp = (el_prompt_func)func_vp;
+ _prompt = func_fp(el);
+ }
+ break;
+
+ case ( EL_EDITOR ):
+ {
+ // EL_EDITOR, const char *mode
+ // set editing mode to "emacs" or "vi"
+ }
+ break;
+ case ( EL_HIST ):
+ {
+ // EL_HIST, History *(*fun)(History *, int op, ... ), const char *ptr
+ // defines which history function to use, which is usually history(). Ptr should be the
+ // value returned by history_init().
+ }
+ break;
+ case ( EL_ADDFN ):
+ {
+ // EL_ADDFN, const char *name, const char *help, unsigned char (*func)(EditLine *e, int ch)
+ // add a user defined function, func), referred to as 'name' which is invoked when a key which is bound to 'name' is
+ // entered. 'help' is a description of 'name'. at invocation time, 'ch' is the key which caused the invocation. the
+ // return value of 'func()' should be one of:
+ // CC_NORM add a normal character
+ // CC_NEWLINE end of line was entered
+ // CC_EOF EOF was entered
+ // CC_ARGHACK expecting further command input as arguments, do nothing visually.
+ // CC_REFRESH refresh display.
+ // CC_REFRESH_BEEP refresh display and beep.
+ // CC_CURSOR cursor moved so update and perform CC_REFRESH
+ // CC_REDISPLAY redisplay entire input line. this is useful if a key binding outputs extra information.
+ // CC_ERROR an error occurred. beep and flush tty.
+ // CC_FATAL fatal error, reset tty to known state.
+
+ el_binding *binding = new el_binding;
+ binding->name = va_arg( vl, const char *);
+ binding->help = va_arg( vl, const char *);
+ binding->func = va_arg( vl, el_addfn_func );
+ binding->key = 0;
+ // add this to the bindings list
+ _bindings.push_back( binding );
+ }
+ break;
+ case ( EL_BIND ):
+ {
+ // EL_BIND, const char *, ..., NULL
+ // perform the BIND built-in command. Refer to editrc(5) for more information.
+
+ const char *name = va_arg( vl, const char* );
+
+ for ( int i=0; i<_bindings.size(); i++ )
+ {
+ el_binding *bind = _bindings[i];
+ if ( strcmp( bind->name, name ) == 0 )
+ {
+ bind->key = va_arg( vl, const char * );
+ break;
+ }
+ }
+
+ }
+ break;
+ case ( EL_CLIENTDATA ):
+ {
+ clientData = va_arg(vl, void*);
+ }
+ break;
+ }
+ return 0;
+}
+
+void
+el_end (EditLine *el)
+{
+ //assert( !"Not implemented!" );
+}
+
+void
+el_reset (EditLine *)
+{
+ assert( !"Not implemented!" );
+}
+
+int
+el_getc (EditLine *, char *)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+void
+el_push (EditLine *, const char *)
+{
+}
+
+void
+el_beep (EditLine *)
+{
+ Beep( 1000, 500 );
+}
+
+int
+el_parse (EditLine *, int, const char **)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+int
+el_get (EditLine *el, int code, ...)
+{
+ va_list vl;
+ va_start( vl, code );
+
+ switch ( code )
+ {
+ case ( EL_CLIENTDATA ):
+ {
+ void **dout = va_arg( vl, void** );
+ *dout = clientData;
+ }
+ break;
+ default:
+ assert( !"Not implemented!" );
+ }
+ return 0;
+}
+
+int
+el_source (EditLine *el, const char *file)
+{
+ // init edit line by reading the contents of 'file'
+ // nothing to do here on windows...
+ return 0;
+}
+
+void
+el_resize (EditLine *)
+{
+ assert( !"Not implemented!" );
+}
+
+const LineInfo *
+el_line (EditLine *el)
+{
+ return 0;
+}
+
+int
+el_insertstr (EditLine *, const char *)
+{
+// assert( !"Not implemented!" );
+ return 0;
+}
+
+void
+el_deletestr (EditLine *, int)
+{
+ assert( !"Not implemented!" );
+}
+
+History *
+history_init (void)
+{
+ // return dummy handle
+ return (History*) -1;
+}
+
+void
+history_end (History *)
+{
+// assert( !"Not implemented!" );
+}
+
+int
+history (History *, HistEvent *, int op, ...)
+{
+ // perform operation 'op' on the history list with
+ // optional arguments as needed by the operation.
+ return 0;
+}
+
+#endif
diff --git a/source/Host/windows/FileSystem.cpp b/source/Host/windows/FileSystem.cpp
new file mode 100644
index 000000000000..2cca12b92711
--- /dev/null
+++ b/source/Host/windows/FileSystem.cpp
@@ -0,0 +1,221 @@
+//===-- FileSystem.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/windows/windows.h"
+
+#include <shellapi.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/windows/AutoHandle.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb_private;
+
+const char *
+FileSystem::DEV_NULL = "nul";
+
+FileSpec::PathSyntax
+FileSystem::GetNativePathSyntax()
+{
+ return FileSpec::ePathSyntaxWindows;
+}
+
+Error
+FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
+{
+ // On Win32, the mode parameter is ignored, as Windows files and directories support a
+ // different permission model than POSIX.
+ Error error;
+ const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
+ if (err_code)
+ {
+ error.SetErrorString(err_code.message().c_str());
+ }
+
+ return error;
+}
+
+Error
+FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
+{
+ Error error;
+ if (!recurse)
+ {
+ BOOL result = ::RemoveDirectory(file_spec.GetCString());
+ if (!result)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ }
+ else
+ {
+ // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
+ // indicate the end of the list.
+ std::string path_buffer{file_spec.GetPath()};
+ path_buffer.push_back(0);
+
+ SHFILEOPSTRUCT shfos = {0};
+ shfos.wFunc = FO_DELETE;
+ shfos.pFrom = path_buffer.c_str();
+ shfos.fFlags = FOF_NO_UI;
+
+ int result = ::SHFileOperation(&shfos);
+ // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
+ if (result != 0)
+ error.SetErrorStringWithFormat("SHFileOperation failed");
+ }
+ return error;
+}
+
+Error
+FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
+{
+ Error error;
+ // Beware that Windows's permission model is different from Unix's, and it's
+ // not clear if this API is supposed to check ACLs. To match the caller's
+ // expectations as closely as possible, we'll use Microsoft's _stat, which
+ // attempts to emulate POSIX stat. This should be good enough for basic
+ // checks like FileSpec::Readable.
+ struct _stat file_stats;
+ if (::_stat(file_spec.GetCString(), &file_stats) == 0)
+ {
+ // The owner permission bits in "st_mode" currently match the definitions
+ // for the owner file mode bits.
+ file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC);
+ }
+ else
+ {
+ error.SetErrorToErrno();
+ }
+
+ return error;
+}
+
+Error
+FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
+{
+ Error error;
+ error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
+ return error;
+}
+
+lldb::user_id_t
+FileSystem::GetFileSize(const FileSpec &file_spec)
+{
+ return file_spec.GetByteSize();
+}
+
+bool
+FileSystem::GetFileExists(const FileSpec &file_spec)
+{
+ return file_spec.Exists();
+}
+
+Error
+FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
+{
+ Error error;
+ if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr))
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+}
+
+int
+FileSystem::GetHardlinkCount(const FileSpec &file_spec)
+{
+ HANDLE file_handle = ::CreateFile(file_spec.GetCString(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+
+ if (file_handle == INVALID_HANDLE_VALUE)
+ return -1;
+
+ AutoHandle auto_file_handle(file_handle);
+ BY_HANDLE_FILE_INFORMATION file_info;
+ if (::GetFileInformationByHandle(file_handle, &file_info))
+ return file_info.nNumberOfLinks;
+
+ return -1;
+}
+
+Error
+FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
+{
+ Error error;
+ DWORD attrib = ::GetFileAttributes(dst.GetCString());
+ if (attrib == INVALID_FILE_ATTRIBUTES)
+ {
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+ }
+ bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
+ DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
+ BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag);
+ if (!result)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+}
+
+Error
+FileSystem::Unlink(const FileSpec &file_spec)
+{
+ Error error;
+ BOOL result = ::DeleteFile(file_spec.GetCString());
+ if (!result)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+}
+
+Error
+FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
+{
+ Error error;
+ HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+ }
+
+ char buf[PATH_MAX];
+ // Subtract 1 from the path length since this function does not add a null terminator.
+ DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1,
+ FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+ if (result == 0)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ else
+ dst.SetFile(buf, false);
+
+ ::CloseHandle(h);
+ return error;
+}
+
+Error
+FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst)
+{
+ return Error("ResolveSymbolicLink() isn't implemented on Windows");
+}
+
+bool
+FileSystem::IsLocal(const FileSpec &spec)
+{
+ if (spec)
+ {
+ // TODO: return true if the file is on a locally mounted file system
+ return true;
+ }
+
+ return false;
+}
diff --git a/source/Host/windows/Host.cpp b/source/Host/windows/Host.cpp
new file mode 100644
index 000000000000..2c9a139df256
--- /dev/null
+++ b/source/Host/windows/Host.cpp
@@ -0,0 +1,326 @@
+//===-- source/Host/windows/Host.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <stdio.h>
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/AutoHandle.h"
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Target/Process.h"
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredData.h"
+
+// Windows includes
+#include <TlHelp32.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+ bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple)
+ {
+ // Open the PE File as a binary file, and parse just enough information to determine the
+ // machine type.
+ File imageBinary(
+ executable.GetPath().c_str(),
+ File::eOpenOptionRead,
+ lldb::eFilePermissionsUserRead);
+ imageBinary.SeekFromStart(0x3c);
+ int32_t peOffset = 0;
+ uint32_t peHead = 0;
+ uint16_t machineType = 0;
+ size_t readSize = sizeof(peOffset);
+ imageBinary.Read(&peOffset, readSize);
+ imageBinary.SeekFromStart(peOffset);
+ imageBinary.Read(&peHead, readSize);
+ if (peHead != 0x00004550) // "PE\0\0", little-endian
+ return false; // Error: Can't find PE header
+ readSize = 2;
+ imageBinary.Read(&machineType, readSize);
+ triple.setVendor(llvm::Triple::PC);
+ triple.setOS(llvm::Triple::Win32);
+ triple.setArch(llvm::Triple::UnknownArch);
+ if (machineType == 0x8664)
+ triple.setArch(llvm::Triple::x86_64);
+ else if (machineType == 0x14c)
+ triple.setArch(llvm::Triple::x86);
+
+ return true;
+ }
+
+ bool GetExecutableForProcess(const AutoHandle &handle, std::string &path)
+ {
+ // Get the process image path. MAX_PATH isn't long enough, paths can actually be up to 32KB.
+ std::vector<char> buffer(32768);
+ DWORD dwSize = buffer.size();
+ if (!::QueryFullProcessImageNameA(handle.get(), 0, &buffer[0], &dwSize))
+ return false;
+ path.assign(&buffer[0]);
+ return true;
+ }
+
+ void GetProcessExecutableAndTriple(const AutoHandle &handle, ProcessInstanceInfo &process)
+ {
+ // We may not have permissions to read the path from the process. So start off by
+ // setting the executable file to whatever Toolhelp32 gives us, and then try to
+ // enhance this with more detailed information, but fail gracefully.
+ std::string executable;
+ llvm::Triple triple;
+ triple.setVendor(llvm::Triple::PC);
+ triple.setOS(llvm::Triple::Win32);
+ triple.setArch(llvm::Triple::UnknownArch);
+ if (GetExecutableForProcess(handle, executable))
+ {
+ FileSpec executableFile(executable.c_str(), false);
+ process.SetExecutableFile(executableFile, true);
+ GetTripleForProcess(executableFile, triple);
+ }
+ process.SetArchitecture(ArchSpec(triple));
+
+ // TODO(zturner): Add the ability to get the process user name.
+ }
+}
+
+lldb::DataBufferSP
+Host::GetAuxvData(lldb_private::Process *process)
+{
+ return 0;
+}
+
+lldb::tid_t
+Host::GetCurrentThreadID()
+{
+ return lldb::tid_t(::GetCurrentThreadId());
+}
+
+lldb::thread_t
+Host::GetCurrentThread ()
+{
+ return lldb::thread_t(::GetCurrentThread());
+}
+
+lldb::thread_key_t
+Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback)
+{
+ return TlsAlloc();
+}
+
+void*
+Host::ThreadLocalStorageGet(lldb::thread_key_t key)
+{
+ return ::TlsGetValue (key);
+}
+
+void
+Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value)
+{
+ ::TlsSetValue (key, value);
+}
+
+void
+Host::Kill(lldb::pid_t pid, int signo)
+{
+ TerminateProcess((HANDLE) pid, 1);
+}
+
+
+const char *
+Host::GetSignalAsCString(int signo)
+{
+ return NULL;
+}
+
+FileSpec
+Host::GetModuleFileSpecForHostAddress (const void *host_addr)
+{
+ FileSpec module_filespec;
+
+ HMODULE hmodule = NULL;
+ if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)host_addr, &hmodule))
+ return module_filespec;
+
+ std::vector<char> buffer(MAX_PATH);
+ DWORD chars_copied = 0;
+ do {
+ chars_copied = ::GetModuleFileName(hmodule, &buffer[0], buffer.size());
+ if (chars_copied == buffer.size() && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ buffer.resize(buffer.size() * 2);
+ } while (chars_copied >= buffer.size());
+
+ module_filespec.SetFile(&buffer[0], false);
+ return module_filespec;
+}
+
+uint32_t
+Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos)
+{
+ process_infos.Clear();
+
+ AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+ if (!snapshot.IsValid())
+ return 0;
+
+ PROCESSENTRY32 pe = {0};
+ pe.dwSize = sizeof(PROCESSENTRY32);
+ if (Process32First(snapshot.get(), &pe))
+ {
+ do
+ {
+ AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID), nullptr);
+
+ ProcessInstanceInfo process;
+ process.SetExecutableFile(FileSpec(pe.szExeFile, false), true);
+ process.SetProcessID(pe.th32ProcessID);
+ process.SetParentProcessID(pe.th32ParentProcessID);
+ GetProcessExecutableAndTriple(handle, process);
+
+ if (match_info.MatchAllProcesses() || match_info.Matches(process))
+ process_infos.Append(process);
+ } while (Process32Next(snapshot.get(), &pe));
+ }
+ return process_infos.GetSize();
+}
+
+bool
+Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
+{
+ process_info.Clear();
+
+ AutoHandle handle(::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid),
+ nullptr);
+ if (!handle.IsValid())
+ return false;
+
+ process_info.SetProcessID(pid);
+ GetProcessExecutableAndTriple(handle, process_info);
+
+ // Need to read the PEB to get parent process and command line arguments.
+ return true;
+}
+
+HostThread
+Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals)
+{
+ return HostThread();
+}
+
+Error
+Host::ShellExpandArguments (ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments))
+ {
+ FileSpec expand_tool_spec;
+ if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec))
+ {
+ error.SetErrorString("could not find support executable directory for the lldb-argdumper tool");
+ return error;
+ }
+ expand_tool_spec.AppendPathComponent("lldb-argdumper.exe");
+ if (!expand_tool_spec.Exists())
+ {
+ error.SetErrorString("could not find the lldb-argdumper tool");
+ return error;
+ }
+
+ std::string quoted_cmd_string;
+ launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string);
+ std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/');
+ StreamString expand_command;
+
+ expand_command.Printf("\"%s\" %s",
+ expand_tool_spec.GetPath().c_str(),
+ quoted_cmd_string.c_str());
+
+ int status;
+ std::string output;
+ RunShellCommand(expand_command.GetData(), launch_info.GetWorkingDirectory(), &status, nullptr, &output, 10);
+
+ if (status != 0)
+ {
+ error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status);
+ return error;
+ }
+
+ auto data_sp = StructuredData::ParseJSON(output);
+ if (!data_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ auto dict_sp = data_sp->GetAsDictionary();
+ if (!data_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
+ if (!args_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ auto args_array_sp = args_sp->GetAsArray();
+ if (!args_array_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ launch_info.GetArguments().Clear();
+
+ for (size_t i = 0;
+ i < args_array_sp->GetSize();
+ i++)
+ {
+ auto item_sp = args_array_sp->GetItemAtIndex(i);
+ if (!item_sp)
+ continue;
+ auto str_sp = item_sp->GetAsString();
+ if (!str_sp)
+ continue;
+
+ launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str());
+ }
+ }
+
+ return error;
+}
+
+size_t
+Host::GetEnvironment(StringList &env)
+{
+ // The environment block on Windows is a contiguous buffer of NULL terminated strings,
+ // where the end of the environment block is indicated by two consecutive NULLs.
+ LPCH environment_block = ::GetEnvironmentStrings();
+ env.Clear();
+ while (*environment_block != '\0')
+ {
+ llvm::StringRef current_var(environment_block);
+ if (current_var[0] != '=')
+ env.AppendString(current_var);
+
+ environment_block += current_var.size()+1;
+ }
+ return env.GetSize();
+}
diff --git a/source/Host/windows/HostInfoWindows.cpp b/source/Host/windows/HostInfoWindows.cpp
new file mode 100644
index 000000000000..6dce71d9172a
--- /dev/null
+++ b/source/Host/windows/HostInfoWindows.cpp
@@ -0,0 +1,118 @@
+//===-- HostInfoWindows.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/windows/windows.h"
+
+#include <mutex> // std::once
+
+#include "lldb/Host/windows/HostInfoWindows.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Path.h"
+
+using namespace lldb_private;
+
+FileSpec HostInfoWindows::m_program_filespec;
+
+size_t
+HostInfoWindows::GetPageSize()
+{
+ SYSTEM_INFO systemInfo;
+ GetNativeSystemInfo(&systemInfo);
+ return systemInfo.dwPageSize;
+}
+
+bool
+HostInfoWindows::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update)
+{
+ OSVERSIONINFOEX info;
+
+ ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+#pragma warning(push)
+#pragma warning(disable : 4996)
+ // Starting with Microsoft SDK for Windows 8.1, this function is deprecated in favor of the
+ // new Windows Version Helper APIs. Since we don't specify a minimum SDK version, it's easier
+ // to simply disable the warning rather than try to support both APIs.
+ if (GetVersionEx((LPOSVERSIONINFO)&info) == 0)
+ {
+ return false;
+ }
+#pragma warning(pop)
+
+ major = info.dwMajorVersion;
+ minor = info.dwMinorVersion;
+ update = info.wServicePackMajor;
+
+ return true;
+}
+
+bool
+HostInfoWindows::GetOSBuildString(std::string &s)
+{
+ s.clear();
+ uint32_t major, minor, update;
+ if (!GetOSVersion(major, minor, update))
+ return false;
+
+ llvm::raw_string_ostream stream(s);
+ stream << "Windows NT " << major << "." << minor << "." << update;
+ return true;
+}
+
+bool
+HostInfoWindows::GetOSKernelDescription(std::string &s)
+{
+ return GetOSBuildString(s);
+}
+
+bool
+HostInfoWindows::GetHostname(std::string &s)
+{
+ char buffer[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+ if (!::GetComputerName(buffer, &dwSize))
+ return false;
+
+ s.assign(buffer, buffer + dwSize);
+ return true;
+}
+
+FileSpec
+HostInfoWindows::GetProgramFileSpec()
+{
+ static std::once_flag g_once_flag;
+ std::call_once(g_once_flag, []() {
+ char buffer[PATH_MAX];
+ ::GetModuleFileName(NULL, buffer, sizeof(buffer));
+ m_program_filespec.SetFile(buffer, false);
+ });
+ return m_program_filespec;
+}
+
+FileSpec
+HostInfoWindows::GetDefaultShell()
+{
+ return FileSpec(::getenv("ComSpec"), false);
+}
+
+bool
+HostInfoWindows::ComputePythonDirectory(FileSpec &file_spec)
+{
+ FileSpec lldb_file_spec;
+ if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec))
+ return false;
+ llvm::SmallString<64> path(lldb_file_spec.GetDirectory().AsCString());
+ llvm::sys::path::remove_filename(path);
+ llvm::sys::path::append(path, "lib", "site-packages");
+ std::replace(path.begin(), path.end(), '\\', '/');
+ file_spec.GetDirectory().SetString(path.c_str());
+ return true;
+}
diff --git a/source/Host/windows/HostProcessWindows.cpp b/source/Host/windows/HostProcessWindows.cpp
new file mode 100644
index 000000000000..0f81c18d34af
--- /dev/null
+++ b/source/Host/windows/HostProcessWindows.cpp
@@ -0,0 +1,137 @@
+//===-- HostProcessWindows.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/FileSpec.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/HostProcessWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <Psapi.h>
+
+using namespace lldb_private;
+
+namespace
+{
+struct MonitorInfo
+{
+ HostProcess::MonitorCallback callback;
+ void *baton;
+ HANDLE process_handle;
+};
+}
+
+HostProcessWindows::HostProcessWindows()
+ : HostNativeProcessBase()
+ , m_owns_handle(true)
+{
+}
+
+HostProcessWindows::HostProcessWindows(lldb::process_t process)
+ : HostNativeProcessBase(process)
+ , m_owns_handle(true)
+{
+}
+
+HostProcessWindows::~HostProcessWindows()
+{
+ Close();
+}
+
+void
+HostProcessWindows::SetOwnsHandle(bool owns)
+{
+ m_owns_handle = owns;
+}
+
+Error HostProcessWindows::Terminate()
+{
+ Error error;
+ if (m_process == nullptr)
+ error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32);
+
+ if (!::TerminateProcess(m_process, 0))
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+
+ return error;
+}
+
+Error HostProcessWindows::GetMainModule(FileSpec &file_spec) const
+{
+ Error error;
+ if (m_process == nullptr)
+ error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32);
+
+ char path[MAX_PATH] = { 0 };
+ if (::GetProcessImageFileName(m_process, path, llvm::array_lengthof(path)))
+ file_spec.SetFile(path, false);
+ else
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+
+ return error;
+}
+
+lldb::pid_t HostProcessWindows::GetProcessId() const
+{
+ return (m_process == LLDB_INVALID_PROCESS) ? -1 : ::GetProcessId(m_process);
+}
+
+bool HostProcessWindows::IsRunning() const
+{
+ if (m_process == nullptr)
+ return false;
+
+ DWORD code = 0;
+ if (!::GetExitCodeProcess(m_process, &code))
+ return false;
+
+ return (code == STILL_ACTIVE);
+}
+
+HostThread
+HostProcessWindows::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals)
+{
+ HostThread monitor_thread;
+ MonitorInfo *info = new MonitorInfo;
+ info->callback = callback;
+ info->baton = callback_baton;
+
+ // Since the life of this HostProcessWindows instance and the life of the process may be different, duplicate the handle so that
+ // the monitor thread can have ownership over its own copy of the handle.
+ HostThread result;
+ if (::DuplicateHandle(GetCurrentProcess(), m_process, GetCurrentProcess(), &info->process_handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ result = ThreadLauncher::LaunchThread("ChildProcessMonitor", HostProcessWindows::MonitorThread, info, nullptr);
+ return result;
+}
+
+lldb::thread_result_t
+HostProcessWindows::MonitorThread(void *thread_arg)
+{
+ DWORD exit_code;
+
+ MonitorInfo *info = static_cast<MonitorInfo *>(thread_arg);
+ if (info)
+ {
+ ::WaitForSingleObject(info->process_handle, INFINITE);
+ ::GetExitCodeProcess(info->process_handle, &exit_code);
+ info->callback(info->baton, ::GetProcessId(info->process_handle), true, 0, exit_code);
+ ::CloseHandle(info->process_handle);
+ delete (info);
+ }
+ return 0;
+}
+
+void HostProcessWindows::Close()
+{
+ if (m_owns_handle && m_process != LLDB_INVALID_PROCESS)
+ ::CloseHandle(m_process);
+ m_process = nullptr;
+}
diff --git a/source/Host/windows/HostThreadWindows.cpp b/source/Host/windows/HostThreadWindows.cpp
new file mode 100644
index 000000000000..d59064fb1c15
--- /dev/null
+++ b/source/Host/windows/HostThreadWindows.cpp
@@ -0,0 +1,98 @@
+//===-- HostThreadWindows.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/Core/Error.h"
+
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/HostThreadWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+void __stdcall ExitThreadProxy(ULONG_PTR dwExitCode)
+{
+ ::ExitThread(dwExitCode);
+}
+}
+
+HostThreadWindows::HostThreadWindows()
+ : HostNativeThreadBase()
+ , m_owns_handle(true)
+{
+}
+
+HostThreadWindows::HostThreadWindows(lldb::thread_t thread)
+ : HostNativeThreadBase(thread)
+ , m_owns_handle(true)
+{
+}
+
+HostThreadWindows::~HostThreadWindows()
+{
+ Reset();
+}
+
+void
+HostThreadWindows::SetOwnsHandle(bool owns)
+{
+ m_owns_handle = owns;
+}
+
+Error
+HostThreadWindows::Join(lldb::thread_result_t *result)
+{
+ Error error;
+ if (IsJoinable())
+ {
+ DWORD wait_result = ::WaitForSingleObject(m_thread, INFINITE);
+ if (WAIT_OBJECT_0 == wait_result && result)
+ {
+ DWORD exit_code = 0;
+ if (!::GetExitCodeThread(m_thread, &exit_code))
+ *result = 0;
+ *result = exit_code;
+ }
+ else if (WAIT_OBJECT_0 != wait_result)
+ error.SetError(::GetLastError(), eErrorTypeWin32);
+ }
+ else
+ error.SetError(ERROR_INVALID_HANDLE, eErrorTypeWin32);
+
+ Reset ();
+ return error;
+}
+
+Error
+HostThreadWindows::Cancel()
+{
+ Error error;
+
+ DWORD result = ::QueueUserAPC(::ExitThreadProxy, m_thread, 0);
+ error.SetError(result, eErrorTypeWin32);
+ return error;
+}
+
+lldb::tid_t
+HostThreadWindows::GetThreadId() const
+{
+ return ::GetThreadId(m_thread);
+}
+
+void
+HostThreadWindows::Reset()
+{
+ if (m_owns_handle && m_thread != LLDB_INVALID_HOST_THREAD)
+ ::CloseHandle(m_thread);
+
+ HostNativeThreadBase::Reset();
+}
diff --git a/source/Host/windows/LockFileWindows.cpp b/source/Host/windows/LockFileWindows.cpp
new file mode 100644
index 000000000000..161d3dbd8260
--- /dev/null
+++ b/source/Host/windows/LockFileWindows.cpp
@@ -0,0 +1,93 @@
+//===-- LockFileWindows.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/windows/LockFileWindows.h"
+
+#include <io.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+
+Error fileLock (HANDLE file_handle, DWORD flags, const uint64_t start, const uint64_t len)
+{
+ if (start != 0)
+ return Error ("Non-zero start lock regions are not supported");
+
+ OVERLAPPED overlapped = {0};
+
+ if (!::LockFileEx (file_handle, flags, 0, len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING)
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ DWORD bytes;
+ if (!::GetOverlappedResult (file_handle, &overlapped, &bytes, TRUE))
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ return Error ();
+}
+
+} // namespace
+
+LockFileWindows::LockFileWindows (int fd)
+ : LockFileBase (fd),
+ m_file (reinterpret_cast<HANDLE> (_get_osfhandle (fd)))
+{
+}
+
+LockFileWindows::~LockFileWindows ()
+{
+ Unlock ();
+}
+
+bool
+LockFileWindows::IsValidFile () const
+{
+ return LockFileBase::IsValidFile() && m_file != INVALID_HANDLE_VALUE;
+}
+
+Error
+LockFileWindows::DoWriteLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK, start, len);
+}
+
+Error
+LockFileWindows::DoTryWriteLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, start, len);
+}
+
+Error
+LockFileWindows::DoReadLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, 0, start, len);
+}
+
+Error
+LockFileWindows::DoTryReadLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, LOCKFILE_FAIL_IMMEDIATELY, start, len);
+}
+
+Error
+LockFileWindows::DoUnlock ()
+{
+ OVERLAPPED overlapped = {0};
+
+ if (!::UnlockFileEx (m_file, 0, m_len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING)
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ DWORD bytes;
+ if (!::GetOverlappedResult (m_file, &overlapped, &bytes, TRUE))
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ return Error ();
+}
diff --git a/source/Host/windows/Mutex.cpp b/source/Host/windows/Mutex.cpp
new file mode 100644
index 000000000000..ed90f46eb54b
--- /dev/null
+++ b/source/Host/windows/Mutex.cpp
@@ -0,0 +1,109 @@
+//===-- Mutex.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/Mutex.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/windows/windows.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#if 0
+// This logging is way too verbose to enable even for a log channel.
+// This logging can be enabled by changing the "#if 0", but should be
+// reverted prior to checking in.
+#include <cstdio>
+#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_LOG(fmt, ...)
+#endif
+
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Default constructor.
+//
+// Creates a pthread mutex with no attributes.
+//----------------------------------------------------------------------
+Mutex::Mutex () :
+ m_mutex()
+{
+ m_mutex = static_cast<PCRITICAL_SECTION>(malloc(sizeof(CRITICAL_SECTION)));
+ InitializeCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+}
+
+//----------------------------------------------------------------------
+// Default constructor.
+//
+// Creates a pthread mutex with "type" as the mutex type.
+//----------------------------------------------------------------------
+Mutex::Mutex (Mutex::Type type) :
+ m_mutex()
+{
+ m_mutex = static_cast<PCRITICAL_SECTION>(malloc(sizeof(CRITICAL_SECTION)));
+ InitializeCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+}
+
+//----------------------------------------------------------------------
+// Destructor.
+//
+// Destroys the mutex owned by this object.
+//----------------------------------------------------------------------
+Mutex::~Mutex()
+{
+ DeleteCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+ free(m_mutex);
+}
+
+//----------------------------------------------------------------------
+// Locks the mutex owned by this object, if the mutex is already
+// locked, the calling thread will block until the mutex becomes
+// available.
+//
+// RETURNS
+// The error code from the pthread_mutex_lock() function call.
+//----------------------------------------------------------------------
+int
+Mutex::Lock()
+{
+ DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), m_mutex);
+
+ EnterCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Attempts to lock the mutex owned by this object without blocking.
+// If the mutex is already locked, TryLock() will not block waiting
+// for the mutex, but will return an error condition.
+//
+// RETURNS
+// The error code from the pthread_mutex_trylock() function call.
+//----------------------------------------------------------------------
+int
+Mutex::TryLock(const char *failure_message)
+{
+ return TryEnterCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)) == 0;
+}
+
+//----------------------------------------------------------------------
+// If the current thread holds the lock on the owned mutex, then
+// Unlock() will unlock the mutex. Calling Unlock() on this object
+// that the calling thread does not hold will result in undefined
+// behavior.
+//
+// RETURNS
+// The error code from the pthread_mutex_unlock() function call.
+//----------------------------------------------------------------------
+int
+Mutex::Unlock()
+{
+ LeaveCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+ return 0;
+}
diff --git a/source/Host/windows/PipeWindows.cpp b/source/Host/windows/PipeWindows.cpp
new file mode 100644
index 000000000000..d4afd6e74943
--- /dev/null
+++ b/source/Host/windows/PipeWindows.cpp
@@ -0,0 +1,336 @@
+//===-- PipeWindows.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/windows/PipeWindows.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <rpc.h>
+
+#include <atomic>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+std::atomic<uint32_t> g_pipe_serial(0);
+}
+
+PipeWindows::PipeWindows()
+{
+ m_read = INVALID_HANDLE_VALUE;
+ m_write = INVALID_HANDLE_VALUE;
+
+ m_read_fd = -1;
+ m_write_fd = -1;
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+}
+
+PipeWindows::~PipeWindows()
+{
+ Close();
+}
+
+Error
+PipeWindows::CreateNew(bool child_process_inherit)
+{
+ // Even for anonymous pipes, we open a named pipe. This is because you cannot get
+ // overlapped i/o on Windows without using a named pipe. So we synthesize a unique
+ // name.
+ uint32_t serial = g_pipe_serial.fetch_add(1);
+ std::string pipe_name;
+ llvm::raw_string_ostream pipe_name_stream(pipe_name);
+ pipe_name_stream << "lldb.pipe." << ::GetCurrentProcessId() << "." << serial;
+ pipe_name_stream.flush();
+
+ return CreateNew(pipe_name.c_str(), child_process_inherit);
+}
+
+Error
+PipeWindows::CreateNew(llvm::StringRef name, bool child_process_inherit)
+{
+ if (name.empty())
+ return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32);
+
+ if (CanRead() || CanWrite())
+ return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
+
+ std::string pipe_path = "\\\\.\\Pipe\\";
+ pipe_path.append(name);
+
+ // Always open for overlapped i/o. We implement blocking manually in Read and Write.
+ DWORD read_mode = FILE_FLAG_OVERLAPPED;
+ m_read =
+ ::CreateNamedPipe(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL);
+ if (INVALID_HANDLE_VALUE == m_read)
+ return Error(::GetLastError(), eErrorTypeWin32);
+ m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+ // Open the write end of the pipe.
+ Error result = OpenNamedPipe(name, child_process_inherit, false);
+ if (!result.Success())
+ {
+ CloseReadFileDescriptor();
+ return result;
+ }
+
+ return result;
+}
+
+Error
+PipeWindows::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char>& name)
+{
+ llvm::SmallString<128> pipe_name;
+ Error error;
+ ::UUID unique_id;
+ RPC_CSTR unique_string;
+ RPC_STATUS status = ::UuidCreate(&unique_id);
+ if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY)
+ status = ::UuidToStringA(&unique_id, &unique_string);
+ if (status == RPC_S_OK)
+ {
+ pipe_name = prefix;
+ pipe_name += "-";
+ pipe_name += reinterpret_cast<char *>(unique_string);
+ ::RpcStringFreeA(&unique_string);
+ error = CreateNew(pipe_name, child_process_inherit);
+ }
+ else
+ {
+ error.SetError(status, eErrorTypeWin32);
+ }
+ if (error.Success())
+ name = pipe_name;
+ return error;
+}
+
+Error
+PipeWindows::OpenAsReader(llvm::StringRef name, bool child_process_inherit)
+{
+ if (CanRead() || CanWrite())
+ return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
+
+ return OpenNamedPipe(name, child_process_inherit, true);
+}
+
+Error
+PipeWindows::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout)
+{
+ if (CanRead() || CanWrite())
+ return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
+
+ return OpenNamedPipe(name, child_process_inherit, false);
+}
+
+Error
+PipeWindows::OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, bool is_read)
+{
+ if (name.empty())
+ return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32);
+
+ assert(is_read ? !CanRead() : !CanWrite());
+
+ SECURITY_ATTRIBUTES attributes = {0};
+ attributes.bInheritHandle = child_process_inherit;
+
+ std::string pipe_path = "\\\\.\\Pipe\\";
+ pipe_path.append(name);
+
+ if (is_read)
+ {
+ m_read = ::CreateFile(pipe_path.c_str(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (INVALID_HANDLE_VALUE == m_read)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
+
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ }
+ else
+ {
+ m_write = ::CreateFile(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (INVALID_HANDLE_VALUE == m_write)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY);
+
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+ }
+
+ return Error();
+}
+
+int
+PipeWindows::GetReadFileDescriptor() const
+{
+ return m_read_fd;
+}
+
+int
+PipeWindows::GetWriteFileDescriptor() const
+{
+ return m_write_fd;
+}
+
+int
+PipeWindows::ReleaseReadFileDescriptor()
+{
+ if (!CanRead())
+ return -1;
+ int result = m_read_fd;
+ m_read_fd = -1;
+ if (m_read_overlapped.hEvent)
+ ::CloseHandle(m_read_overlapped.hEvent);
+ m_read = INVALID_HANDLE_VALUE;
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ return result;
+}
+
+int
+PipeWindows::ReleaseWriteFileDescriptor()
+{
+ if (!CanWrite())
+ return -1;
+ int result = m_write_fd;
+ m_write_fd = -1;
+ m_write = INVALID_HANDLE_VALUE;
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+ return result;
+}
+
+void
+PipeWindows::CloseReadFileDescriptor()
+{
+ if (!CanRead())
+ return;
+
+ if (m_read_overlapped.hEvent)
+ ::CloseHandle(m_read_overlapped.hEvent);
+ _close(m_read_fd);
+ m_read = INVALID_HANDLE_VALUE;
+ m_read_fd = -1;
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+}
+
+void
+PipeWindows::CloseWriteFileDescriptor()
+{
+ if (!CanWrite())
+ return;
+
+ _close(m_write_fd);
+ m_write = INVALID_HANDLE_VALUE;
+ m_write_fd = -1;
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+}
+
+void
+PipeWindows::Close()
+{
+ CloseReadFileDescriptor();
+ CloseWriteFileDescriptor();
+}
+
+Error
+PipeWindows::Delete(llvm::StringRef name)
+{
+ return Error();
+}
+
+bool
+PipeWindows::CanRead() const
+{
+ return (m_read != INVALID_HANDLE_VALUE);
+}
+
+bool
+PipeWindows::CanWrite() const
+{
+ return (m_write != INVALID_HANDLE_VALUE);
+}
+
+HANDLE
+PipeWindows::GetReadNativeHandle()
+{
+ return m_read;
+}
+
+HANDLE
+PipeWindows::GetWriteNativeHandle()
+{
+ return m_write;
+}
+
+Error
+PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &duration, size_t &bytes_read)
+{
+ if (!CanRead())
+ return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32);
+
+ bytes_read = 0;
+ DWORD sys_bytes_read = size;
+ BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, &m_read_overlapped);
+ if (!result && GetLastError() != ERROR_IO_PENDING)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ DWORD timeout = (duration == std::chrono::microseconds::zero()) ? INFINITE : duration.count() * 1000;
+ DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout);
+ if (wait_result != WAIT_OBJECT_0)
+ {
+ // The operation probably failed. However, if it timed out, we need to cancel the I/O.
+ // Between the time we returned from WaitForSingleObject and the time we call CancelIoEx,
+ // the operation may complete. If that hapens, CancelIoEx will fail and return ERROR_NOT_FOUND.
+ // If that happens, the original operation should be considered to have been successful.
+ bool failed = true;
+ DWORD failure_error = ::GetLastError();
+ if (wait_result == WAIT_TIMEOUT)
+ {
+ BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped);
+ if (!cancel_result && GetLastError() == ERROR_NOT_FOUND)
+ failed = false;
+ }
+ if (failed)
+ return Error(failure_error, eErrorTypeWin32);
+ }
+
+ // Now we call GetOverlappedResult setting bWait to false, since we've already waited
+ // as long as we're willing to.
+ if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE))
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ bytes_read = sys_bytes_read;
+ return Error();
+}
+
+Error
+PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written)
+{
+ if (!CanWrite())
+ return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32);
+
+ DWORD sys_bytes_written = 0;
+ BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, &m_write_overlapped);
+ if (!write_result && GetLastError() != ERROR_IO_PENDING)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, TRUE);
+ if (!result)
+ return Error(::GetLastError(), eErrorTypeWin32);
+ return Error();
+}
diff --git a/source/Host/windows/ProcessLauncherWindows.cpp b/source/Host/windows/ProcessLauncherWindows.cpp
new file mode 100644
index 000000000000..355dd40544f9
--- /dev/null
+++ b/source/Host/windows/ProcessLauncherWindows.cpp
@@ -0,0 +1,105 @@
+//===-- ProcessLauncherWindows.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/HostProcess.h"
+#include "lldb/Host/windows/ProcessLauncherWindows.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+
+#include <string>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostProcess
+ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error)
+{
+ error.Clear();
+
+ std::string executable;
+ std::string commandLine;
+ std::vector<char> environment;
+ STARTUPINFO startupinfo = {0};
+ PROCESS_INFORMATION pi = {0};
+
+ HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
+ HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
+ HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
+
+ startupinfo.cb = sizeof(startupinfo);
+ startupinfo.dwFlags |= STARTF_USESTDHANDLES;
+ startupinfo.hStdError = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
+ startupinfo.hStdInput = stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
+ startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
+
+ const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
+ if (hide_console_var && llvm::StringRef(hide_console_var).equals_lower("true"))
+ {
+ startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
+ startupinfo.wShowWindow = SW_HIDE;
+ }
+
+ DWORD flags = CREATE_NEW_CONSOLE;
+ if (launch_info.GetFlags().Test(eLaunchFlagDebug))
+ flags |= DEBUG_ONLY_THIS_PROCESS;
+
+ executable = launch_info.GetExecutableFile().GetPath();
+ launch_info.GetArguments().GetQuotedCommandString(commandLine);
+ BOOL result = ::CreateProcessA(executable.c_str(), const_cast<char *>(commandLine.c_str()), NULL, NULL, TRUE, flags, NULL,
+ launch_info.GetWorkingDirectory().GetCString(), &startupinfo, &pi);
+ if (result)
+ {
+ // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess.
+ ::CloseHandle(pi.hThread);
+ }
+
+ if (stdin_handle)
+ ::CloseHandle(stdin_handle);
+ if (stdout_handle)
+ ::CloseHandle(stdout_handle);
+ if (stderr_handle)
+ ::CloseHandle(stderr_handle);
+
+ if (!result)
+ error.SetError(::GetLastError(), eErrorTypeWin32);
+ return HostProcess(pi.hProcess);
+}
+
+HANDLE
+ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd)
+{
+ const FileAction *action = launch_info.GetFileActionForFD(fd);
+ if (action == nullptr)
+ return NULL;
+ SECURITY_ATTRIBUTES secattr = {0};
+ secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secattr.bInheritHandle = TRUE;
+
+ const char *path = action->GetPath();
+ DWORD access = 0;
+ DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ DWORD create = 0;
+ DWORD flags = 0;
+ if (fd == STDIN_FILENO)
+ {
+ access = GENERIC_READ;
+ create = OPEN_EXISTING;
+ flags = FILE_ATTRIBUTE_READONLY;
+ }
+ if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ {
+ access = GENERIC_WRITE;
+ create = CREATE_ALWAYS;
+ if (fd == STDERR_FILENO)
+ flags = FILE_FLAG_WRITE_THROUGH;
+ }
+
+ HANDLE result = ::CreateFile(path, access, share, &secattr, create, flags, NULL);
+ return (result == INVALID_HANDLE_VALUE) ? NULL : result;
+}
diff --git a/source/Host/windows/ProcessRunLock.cpp b/source/Host/windows/ProcessRunLock.cpp
new file mode 100644
index 000000000000..1f21552a3062
--- /dev/null
+++ b/source/Host/windows/ProcessRunLock.cpp
@@ -0,0 +1,106 @@
+#include "lldb/Host/ProcessRunLock.h"
+#include "lldb/Host/windows/windows.h"
+
+namespace
+{
+#if defined(__MINGW32__)
+// Taken from WinNT.h
+typedef struct _RTL_SRWLOCK {
+ PVOID Ptr;
+} RTL_SRWLOCK, *PRTL_SRWLOCK;
+
+// Taken from WinBase.h
+typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
+#endif
+}
+
+
+static PSRWLOCK GetLock(lldb::rwlock_t lock)
+{
+ return static_cast<PSRWLOCK>(lock);
+}
+
+static bool ReadLock(lldb::rwlock_t rwlock)
+{
+ ::AcquireSRWLockShared(GetLock(rwlock));
+ return true;
+}
+
+static bool ReadUnlock(lldb::rwlock_t rwlock)
+{
+ ::ReleaseSRWLockShared(GetLock(rwlock));
+ return true;
+}
+
+static bool WriteLock(lldb::rwlock_t rwlock)
+{
+ ::AcquireSRWLockExclusive(GetLock(rwlock));
+ return true;
+}
+
+static bool WriteTryLock(lldb::rwlock_t rwlock)
+{
+ return !!::TryAcquireSRWLockExclusive(GetLock(rwlock));
+}
+
+static bool WriteUnlock(lldb::rwlock_t rwlock)
+{
+ ::ReleaseSRWLockExclusive(GetLock(rwlock));
+ return true;
+}
+
+using namespace lldb_private;
+
+ProcessRunLock::ProcessRunLock()
+ : m_running(false)
+{
+ m_rwlock = new SRWLOCK;
+ InitializeSRWLock(GetLock(m_rwlock));
+}
+
+ProcessRunLock::~ProcessRunLock()
+{
+ delete m_rwlock;
+}
+
+bool ProcessRunLock::ReadTryLock()
+{
+ ::ReadLock(m_rwlock);
+ if (m_running == false)
+ return true;
+ ::ReadUnlock(m_rwlock);
+ return false;
+}
+
+bool ProcessRunLock::ReadUnlock()
+{
+ return ::ReadUnlock(m_rwlock);
+}
+
+bool ProcessRunLock::SetRunning ()
+{
+ WriteLock(m_rwlock);
+ m_running = true;
+ WriteUnlock(m_rwlock);
+ return true;
+}
+
+bool ProcessRunLock::TrySetRunning ()
+{
+ if (WriteTryLock(m_rwlock))
+ {
+ bool was_running = m_running;
+ m_running = true;
+ WriteUnlock(m_rwlock);
+ return !was_running;
+ }
+ return false;
+}
+
+bool ProcessRunLock::SetStopped ()
+{
+ WriteLock(m_rwlock);
+ m_running = false;
+ WriteUnlock(m_rwlock);
+ return true;
+}
diff --git a/source/Host/windows/ThisThread.cpp b/source/Host/windows/ThisThread.cpp
new file mode 100644
index 000000000000..bcd5b8d1c1d0
--- /dev/null
+++ b/source/Host/windows/ThisThread.cpp
@@ -0,0 +1,66 @@
+//===-- ThisThread.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/Core/Error.h"
+
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/ThisThread.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if defined(_MSC_VER) && !defined(__clang__)
+
+namespace
+{
+static const DWORD MS_VC_EXCEPTION = 0x406D1388;
+
+#pragma pack(push, 8)
+struct THREADNAME_INFO
+{
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to thread name
+ DWORD dwThreadId; // Thread ID (-1 == current thread)
+ DWORD dwFlags; // Reserved. Do not use.
+};
+#pragma pack(pop)
+}
+
+#endif
+
+void
+ThisThread::SetName(llvm::StringRef name)
+{
+// Other compilers don't yet support SEH, so we can only set the thread if compiling with MSVC.
+// TODO(zturner): Once clang-cl supports SEH, relax this conditional.
+#if defined(_MSC_VER) && !defined(__clang__)
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name.data();
+ info.dwThreadId = ::GetCurrentThreadId();
+ info.dwFlags = 0;
+
+ __try {
+ ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER) {}
+#endif
+}
+
+void
+ThisThread::GetName(llvm::SmallVectorImpl<char> &name)
+{
+ // Getting the thread name is not supported on Windows.
+ // TODO(zturner): In SetName(), make a TLS entry that contains the thread's name, and in this function
+ // try to extract that TLS entry.
+ name.clear();
+}
diff --git a/source/Host/windows/Windows.cpp b/source/Host/windows/Windows.cpp
new file mode 100644
index 000000000000..71bff7a9d2e2
--- /dev/null
+++ b/source/Host/windows/Windows.cpp
@@ -0,0 +1,231 @@
+//===-- Windows.cpp ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This file provides Windows support functions
+
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/win32.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <io.h>
+#include <cerrno>
+#include <ctype.h>
+
+// These prototypes are defined in <direct.h>, but it also defines chdir() and getcwd(), giving multiply defined errors
+extern "C"
+{
+ char *_getcwd(char *buffer, int maxlen);
+ int _chdir(const char *path);
+}
+
+int vasprintf(char **ret, const char *fmt, va_list ap)
+{
+ char *buf;
+ int len;
+ size_t buflen;
+ va_list ap2;
+
+#if defined(_MSC_VER) || defined(__MINGW64)
+ ap2 = ap;
+ len = _vscprintf(fmt, ap2);
+#else
+ va_copy(ap2, ap);
+ len = vsnprintf(NULL, 0, fmt, ap2);
+#endif
+
+ if (len >= 0 && (buf = (char*) malloc ((buflen = (size_t) (len + 1)))) != NULL) {
+ len = vsnprintf(buf, buflen, fmt, ap);
+ *ret = buf;
+ } else {
+ *ret = NULL;
+ len = -1;
+ }
+
+ va_end(ap2);
+ return len;
+}
+
+char* strcasestr(const char *s, const char* find)
+{
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != 0) {
+ c = tolower((unsigned char) c);
+ len = strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return 0;
+ } while ((char) tolower((unsigned char) sc) != c);
+ } while (strncasecmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *) s);
+}
+
+char* realpath(const char * name, char * resolved)
+{
+ char *retname = NULL; /* we will return this, if we fail */
+
+ /* SUSv3 says we must set `errno = EINVAL', and return NULL,
+ * if `name' is passed as a NULL pointer.
+ */
+
+ if (name == NULL)
+ errno = EINVAL;
+
+ /* Otherwise, `name' must refer to a readable filesystem object,
+ * if we are going to resolve its absolute path name.
+ */
+
+ else if (access(name, 4) == 0)
+ {
+ /* If `name' didn't point to an existing entity,
+ * then we don't get to here; we simply fall past this block,
+ * returning NULL, with `errno' appropriately set by `access'.
+ *
+ * When we _do_ get to here, then we can use `_fullpath' to
+ * resolve the full path for `name' into `resolved', but first,
+ * check that we have a suitable buffer, in which to return it.
+ */
+
+ if ((retname = resolved) == NULL)
+ {
+ /* Caller didn't give us a buffer, so we'll exercise the
+ * option granted by SUSv3, and allocate one.
+ *
+ * `_fullpath' would do this for us, but it uses `malloc', and
+ * Microsoft's implementation doesn't set `errno' on failure.
+ * If we don't do this explicitly ourselves, then we will not
+ * know if `_fullpath' fails on `malloc' failure, or for some
+ * other reason, and we want to set `errno = ENOMEM' for the
+ * `malloc' failure case.
+ */
+
+ retname = (char*) malloc(_MAX_PATH);
+ }
+
+ /* By now, we should have a valid buffer.
+ * If we don't, then we know that `malloc' failed,
+ * so we can set `errno = ENOMEM' appropriately.
+ */
+
+ if (retname == NULL)
+ errno = ENOMEM;
+
+ /* Otherwise, when we do have a valid buffer,
+ * `_fullpath' should only fail if the path name is too long.
+ */
+
+ else if ((retname = _fullpath(retname, name, _MAX_PATH)) == NULL)
+ errno = ENAMETOOLONG;
+ }
+
+ /* By the time we get to here,
+ * `retname' either points to the required resolved path name,
+ * or it is NULL, with `errno' set appropriately, either of which
+ * is our required return condition.
+ */
+
+ if (retname != NULL)
+ {
+ // Do a LongPath<->ShortPath roundtrip so that case is resolved by OS
+ int initialLength = strlen(retname);
+ TCHAR buffer[MAX_PATH];
+ GetShortPathName(retname, buffer, MAX_PATH);
+ GetLongPathName(buffer, retname, initialLength + 1);
+
+ // Force drive to be upper case
+ if (retname[1] == ':')
+ retname[0] = toupper(retname[0]);
+ }
+
+ return retname;
+}
+
+#ifdef _MSC_VER
+
+char* basename(char *path)
+{
+ char* l1 = strrchr(path, '\\');
+ char* l2 = strrchr(path, '/');
+ if (l2 > l1) l1 = l2;
+ if (!l1) return path; // no base name
+ return &l1[1];
+}
+
+// use _getcwd() instead of GetCurrentDirectory() because it updates errno
+char* getcwd(char* path, int max)
+{
+ return _getcwd(path, max);
+}
+
+// use _chdir() instead of SetCurrentDirectory() because it updates errno
+int chdir(const char* path)
+{
+ return _chdir(path);
+}
+
+char *dirname(char *path)
+{
+ char* l1 = strrchr(path, '\\');
+ char* l2 = strrchr(path, '/');
+ if (l2 > l1) l1 = l2;
+ if (!l1) return NULL; // no dir name
+ *l1 = 0;
+ return path;
+}
+
+int strcasecmp(const char* s1, const char* s2)
+{
+ return stricmp(s1, s2);
+}
+
+int strncasecmp(const char* s1, const char* s2, size_t n)
+{
+ return strnicmp(s1, s2, n);
+}
+
+int usleep(uint32_t useconds)
+{
+ Sleep(useconds / 1000);
+ return 0;
+}
+
+#if _MSC_VER < 1900
+namespace lldb_private {
+int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
+{
+ int old_errno = errno;
+ int r = ::vsnprintf(buffer, count, format, argptr);
+ int new_errno = errno;
+ buffer[count-1] = '\0';
+ if (r == -1 || r == count)
+ {
+ FILE *nul = fopen("nul", "w");
+ int bytes_written = ::vfprintf(nul, format, argptr);
+ fclose(nul);
+ if (bytes_written < count)
+ errno = new_errno;
+ else
+ {
+ errno = old_errno;
+ r = bytes_written;
+ }
+ }
+ return r;
+}
+} // namespace lldb_private
+#endif
+
+#endif // _MSC_VER