aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2016-01-06 20:12:03 +0000
committerDimitry Andric <dim@FreeBSD.org>2016-01-06 20:12:03 +0000
commit9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch)
treedd2a1ddf0476664c2b823409c36cbccd52662ca7 /tools
parent3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff)
downloadsrc-9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc.tar.gz
src-9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc.zip
Vendor import of lldb trunk r256945:vendor/lldb/lldb-trunk-r256945
Notes
Notes: svn path=/vendor/lldb/dist/; revision=293262 svn path=/vendor/lldb/lldb-trunk-r256945/; revision=293263; tag=vendor/lldb/lldb-trunk-r256945
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt12
-rw-r--r--tools/Makefile30
-rw-r--r--tools/argdumper/CMakeLists.txt8
-rw-r--r--tools/darwin-debug/CMakeLists.txt6
-rw-r--r--tools/darwin-debug/darwin-debug.cpp354
-rw-r--r--tools/darwin-threads/examine-threads.c514
-rw-r--r--tools/debugserver/CMakeLists.txt2
-rw-r--r--tools/debugserver/Makefile13
-rw-r--r--tools/debugserver/debugnub-exports2
-rw-r--r--tools/debugserver/debugserver.xcodeproj/project.pbxproj1945
-rw-r--r--tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme110
-rw-r--r--tools/debugserver/resources/lldb-debugserver-Info.plist21
-rw-r--r--tools/debugserver/scripts/diagnose-termination.d18
-rw-r--r--tools/debugserver/source/ARM_DWARF_Registers.h209
-rw-r--r--tools/debugserver/source/ARM_ehframe_Registers.h35
-rw-r--r--tools/debugserver/source/CMakeLists.txt62
-rw-r--r--tools/debugserver/source/ChangeLog1515
-rw-r--r--tools/debugserver/source/DNB.cpp1979
-rw-r--r--tools/debugserver/source/DNB.h173
-rw-r--r--tools/debugserver/source/DNBArch.cpp97
-rw-r--r--tools/debugserver/source/DNBArch.h129
-rw-r--r--tools/debugserver/source/DNBBreakpoint.cpp225
-rw-r--r--tools/debugserver/source/DNBBreakpoint.h165
-rw-r--r--tools/debugserver/source/DNBDataRef.cpp386
-rw-r--r--tools/debugserver/source/DNBDataRef.h125
-rw-r--r--tools/debugserver/source/DNBDefs.h372
-rw-r--r--tools/debugserver/source/DNBError.cpp128
-rw-r--r--tools/debugserver/source/DNBError.h102
-rw-r--r--tools/debugserver/source/DNBLog.cpp377
-rw-r--r--tools/debugserver/source/DNBLog.h96
-rw-r--r--tools/debugserver/source/DNBRegisterInfo.cpp219
-rw-r--r--tools/debugserver/source/DNBRegisterInfo.h31
-rw-r--r--tools/debugserver/source/DNBRuntimeAction.h25
-rw-r--r--tools/debugserver/source/DNBThreadResumeActions.cpp116
-rw-r--r--tools/debugserver/source/DNBThreadResumeActions.h102
-rw-r--r--tools/debugserver/source/DNBTimer.h163
-rw-r--r--tools/debugserver/source/JSONGenerator.h490
-rw-r--r--tools/debugserver/source/MacOSX/CFBundle.cpp97
-rw-r--r--tools/debugserver/source/MacOSX/CFBundle.h43
-rw-r--r--tools/debugserver/source/MacOSX/CFData.cpp85
-rw-r--r--tools/debugserver/source/MacOSX/CFData.h39
-rw-r--r--tools/debugserver/source/MacOSX/CFString.cpp201
-rw-r--r--tools/debugserver/source/MacOSX/CFString.h43
-rw-r--r--tools/debugserver/source/MacOSX/CFUtils.h81
-rw-r--r--tools/debugserver/source/MacOSX/CMakeLists.txt89
-rw-r--r--tools/debugserver/source/MacOSX/Genealogy.cpp300
-rw-r--r--tools/debugserver/source/MacOSX/Genealogy.h116
-rw-r--r--tools/debugserver/source/MacOSX/GenealogySPI.h96
-rw-r--r--tools/debugserver/source/MacOSX/HasAVX.h27
-rw-r--r--tools/debugserver/source/MacOSX/HasAVX.s50
-rw-r--r--tools/debugserver/source/MacOSX/MachException.cpp582
-rw-r--r--tools/debugserver/source/MacOSX/MachException.h133
-rw-r--r--tools/debugserver/source/MacOSX/MachProcess.h364
-rw-r--r--tools/debugserver/source/MacOSX/MachProcess.mm3685
-rw-r--r--tools/debugserver/source/MacOSX/MachTask.h134
-rw-r--r--tools/debugserver/source/MacOSX/MachTask.mm1144
-rw-r--r--tools/debugserver/source/MacOSX/MachThread.cpp922
-rw-r--r--tools/debugserver/source/MacOSX/MachThread.h159
-rw-r--r--tools/debugserver/source/MacOSX/MachThreadList.cpp668
-rw-r--r--tools/debugserver/source/MacOSX/MachThreadList.h87
-rw-r--r--tools/debugserver/source/MacOSX/MachVMMemory.cpp598
-rw-r--r--tools/debugserver/source/MacOSX/MachVMMemory.h51
-rw-r--r--tools/debugserver/source/MacOSX/MachVMRegion.cpp202
-rw-r--r--tools/debugserver/source/MacOSX/MachVMRegion.h77
-rw-r--r--tools/debugserver/source/MacOSX/Makefile54
-rw-r--r--tools/debugserver/source/MacOSX/ThreadInfo.h26
-rw-r--r--tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp2162
-rw-r--r--tools/debugserver/source/MacOSX/arm/DNBArchImpl.h282
-rw-r--r--tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp2095
-rw-r--r--tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h272
-rw-r--r--tools/debugserver/source/MacOSX/dbgnub-mig.defs5
-rw-r--r--tools/debugserver/source/MacOSX/i386/CMakeLists.txt4
-rw-r--r--tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp1901
-rw-r--r--tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h255
-rw-r--r--tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h180
-rw-r--r--tools/debugserver/source/MacOSX/i386/Makefile19
-rw-r--r--tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp569
-rw-r--r--tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h179
-rw-r--r--tools/debugserver/source/MacOSX/stack_logging.h122
-rw-r--r--tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt8
-rw-r--r--tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp2289
-rw-r--r--tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h260
-rw-r--r--tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h210
-rw-r--r--tools/debugserver/source/MacOSX/x86_64/Makefile19
-rw-r--r--tools/debugserver/source/Makefile46
-rw-r--r--tools/debugserver/source/PThreadCondition.h53
-rw-r--r--tools/debugserver/source/PThreadEvent.cpp227
-rw-r--r--tools/debugserver/source/PThreadEvent.h59
-rw-r--r--tools/debugserver/source/PThreadMutex.cpp84
-rw-r--r--tools/debugserver/source/PThreadMutex.h148
-rw-r--r--tools/debugserver/source/PseudoTerminal.cpp227
-rw-r--r--tools/debugserver/source/PseudoTerminal.h94
-rw-r--r--tools/debugserver/source/RNBContext.cpp279
-rw-r--r--tools/debugserver/source/RNBContext.h159
-rw-r--r--tools/debugserver/source/RNBDefs.h87
-rw-r--r--tools/debugserver/source/RNBRemote.cpp6083
-rw-r--r--tools/debugserver/source/RNBRemote.h452
-rw-r--r--tools/debugserver/source/RNBServices.cpp226
-rw-r--r--tools/debugserver/source/RNBServices.h28
-rw-r--r--tools/debugserver/source/RNBSocket.cpp421
-rw-r--r--tools/debugserver/source/RNBSocket.h85
-rw-r--r--tools/debugserver/source/SysSignal.cpp66
-rw-r--r--tools/debugserver/source/SysSignal.h23
-rw-r--r--tools/debugserver/source/TTYState.cpp122
-rw-r--r--tools/debugserver/source/TTYState.h61
-rw-r--r--tools/debugserver/source/com.apple.debugserver.applist.internal.plist16
-rw-r--r--tools/debugserver/source/com.apple.debugserver.applist.plist19
-rw-r--r--tools/debugserver/source/com.apple.debugserver.internal.plist15
-rw-r--r--tools/debugserver/source/com.apple.debugserver.plist18
-rw-r--r--tools/debugserver/source/com.apple.debugserver.posix.plist18
-rw-r--r--tools/debugserver/source/debugserver-entitlements.plist28
-rw-r--r--tools/debugserver/source/debugserver-macosx-entitlements.plist8
-rw-r--r--tools/debugserver/source/debugserver.cpp1672
-rw-r--r--tools/debugserver/source/libdebugserver.cpp397
-rw-r--r--tools/debugserver/source/libdebugserver.h15
-rw-r--r--tools/driver/CMakeLists.txt28
-rw-r--r--tools/driver/Makefile36
-rw-r--r--tools/driver/lldb-Info.plist21
-rw-r--r--tools/install-headers/Makefile23
-rw-r--r--tools/lldb-mi/CMakeLists.txt102
-rw-r--r--tools/lldb-mi/MICmdCmdSymbol.cpp28
-rw-r--r--tools/lldb-mi/Makefile32
-rw-r--r--tools/lldb-mi/lldb-Info.plist21
-rw-r--r--tools/lldb-perf/README295
-rwxr-xr-xtools/lldb-perf/common/clang/build-clang.sh33
-rw-r--r--tools/lldb-perf/common/clang/lldb_perf_clang.cpp484
-rw-r--r--tools/lldb-perf/common/clang/main.cpp24
-rw-r--r--tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp335
-rw-r--r--tools/lldb-perf/common/stepping/stepping-testcase.cpp42
-rw-r--r--tools/lldb-perf/darwin/formatters/fmts_tester.mm79
-rw-r--r--tools/lldb-perf/darwin/formatters/formatters.cpp246
-rw-r--r--tools/lldb-perf/darwin/sketch/foobar.sketch2bin0 -> 10027 bytes
-rw-r--r--tools/lldb-perf/darwin/sketch/sketch.cpp380
-rw-r--r--tools/lldb-perf/lib/Gauge.cpp53
-rw-r--r--tools/lldb-perf/lib/Gauge.h64
-rw-r--r--tools/lldb-perf/lib/Measurement.h217
-rw-r--r--tools/lldb-perf/lib/MemoryGauge.cpp165
-rw-r--r--tools/lldb-perf/lib/MemoryGauge.h147
-rw-r--r--tools/lldb-perf/lib/Metric.cpp85
-rw-r--r--tools/lldb-perf/lib/Metric.h72
-rw-r--r--tools/lldb-perf/lib/Results.cpp275
-rw-r--r--tools/lldb-perf/lib/Results.h312
-rw-r--r--tools/lldb-perf/lib/TestCase.cpp358
-rw-r--r--tools/lldb-perf/lib/TestCase.h205
-rw-r--r--tools/lldb-perf/lib/Timer.cpp61
-rw-r--r--tools/lldb-perf/lib/Timer.h66
-rw-r--r--tools/lldb-perf/lib/Xcode.cpp165
-rw-r--r--tools/lldb-perf/lib/Xcode.h64
-rw-r--r--tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj1224
-rw-r--r--tools/lldb-server/CMakeLists.txt58
-rw-r--r--tools/lldb-server/Makefile25
152 files changed, 48390 insertions, 18 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 000000000000..49af928c3815
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,12 @@
+if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ add_subdirectory(darwin-debug)
+ add_subdirectory(debugserver)
+endif()
+ add_subdirectory(argdumper)
+ add_subdirectory(driver)
+if (NOT __ANDROID_NDK__)
+ add_subdirectory(lldb-mi)
+endif()
+if (LLDB_CAN_USE_LLDB_SERVER)
+ add_subdirectory(lldb-server)
+endif()
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 000000000000..2588d87dc98a
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,30 @@
+##===- source/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 := ..
+include $(LLDB_LEVEL)/../../Makefile.config
+
+DIRS :=
+
+# enable lldb-gdbserver for supported platforms
+ifneq (,$(strip $(filter $(HOST_OS), FreeBSD Linux NetBSD GNU/kFreeBSD)))
+DIRS += lldb-server
+endif
+
+ifeq ($(HOST_OS),Darwin)
+DIRS += debugserver
+endif
+
+ifeq ($(ENABLE_WERROR),0)
+DIRS += lldb-mi
+endif
+
+DIRS += driver
+
+include $(LLDB_LEVEL)/Makefile
diff --git a/tools/argdumper/CMakeLists.txt b/tools/argdumper/CMakeLists.txt
new file mode 100644
index 000000000000..69bc97c7e518
--- /dev/null
+++ b/tools/argdumper/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_lldb_executable(lldb-argdumper
+ argdumper.cpp
+ )
+
+target_link_libraries(lldb-argdumper liblldb)
+
+install(TARGETS lldb-argdumper
+ RUNTIME DESTINATION bin)
diff --git a/tools/darwin-debug/CMakeLists.txt b/tools/darwin-debug/CMakeLists.txt
new file mode 100644
index 000000000000..352a573e25e1
--- /dev/null
+++ b/tools/darwin-debug/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_lldb_executable(lldb-launcher
+ darwin-debug.cpp
+ )
+
+install(TARGETS lldb-launcher
+ RUNTIME DESTINATION bin)
diff --git a/tools/darwin-debug/darwin-debug.cpp b/tools/darwin-debug/darwin-debug.cpp
new file mode 100644
index 000000000000..ca0a8d48328b
--- /dev/null
+++ b/tools/darwin-debug/darwin-debug.cpp
@@ -0,0 +1,354 @@
+//===-- Launcher.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//----------------------------------------------------------------------
+// Darwin launch helper
+//
+// This program was written to allow programs to be launched in a new
+// Terminal.app window and have the application be stopped for debugging
+// at the program entry point.
+//
+// Although it uses posix_spawn(), it uses Darwin specific posix spawn
+// attribute flags to accomplish its task. It uses an "exec only" flag
+// which avoids forking this process, and it uses a "stop at entry"
+// flag to stop the program at the entry point.
+//
+// Since it uses darwin specific flags this code should not be compiled
+// on other systems.
+//----------------------------------------------------------------------
+#if defined (__APPLE__)
+
+#include <crt_externs.h> // for _NSGetEnviron()
+#include <getopt.h>
+#include <limits.h>
+#include <mach/machine.h>
+#include <signal.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <string>
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+#define streq(a,b) strcmp(a,b) == 0
+
+static struct option g_long_options[] =
+{
+ { "arch", required_argument, NULL, 'a' },
+ { "disable-aslr", no_argument, NULL, 'd' },
+ { "no-env", no_argument, NULL, 'e' },
+ { "help", no_argument, NULL, 'h' },
+ { "setsid", no_argument, NULL, 's' },
+ { "unix-socket", required_argument, NULL, 'u' },
+ { "working-dir", required_argument, NULL, 'w' },
+ { "env", required_argument, NULL, 'E' },
+ { NULL, 0, NULL, 0 }
+};
+
+static void
+usage()
+{
+ puts (
+"NAME\n"
+" darwin-debug -- posix spawn a process that is stopped at the entry point\n"
+" for debugging.\n"
+"\n"
+"SYNOPSIS\n"
+" darwin-debug --unix-socket=<SOCKET> [--arch=<ARCH>] [--working-dir=<PATH>] [--disable-aslr] [--no-env] [--setsid] [--help] -- <PROGRAM> [<PROGRAM-ARG> <PROGRAM-ARG> ....]\n"
+"\n"
+"DESCRIPTION\n"
+" darwin-debug will exec itself into a child process <PROGRAM> that is\n"
+" halted for debugging. It does this by using posix_spawn() along with\n"
+" darwin specific posix_spawn flags that allows exec only (no fork), and\n"
+" stop at the program entry point. Any program arguments <PROGRAM-ARG> are\n"
+" passed on to the exec as the arguments for the new process. The current\n"
+" environment will be passed to the new process unless the \"--no-env\"\n"
+" option is used. A unix socket must be supplied using the\n"
+" --unix-socket=<SOCKET> option so the calling program can handshake with\n"
+" this process and get its process id.\n"
+"\n"
+"EXAMPLE\n"
+" darwin-debug --arch=i386 -- /bin/ls -al /tmp\n"
+);
+ exit (1);
+}
+
+static void
+exit_with_errno (int err, const char *prefix)
+{
+ if (err)
+ {
+ fprintf (stderr,
+ "%s%s",
+ prefix ? prefix : "",
+ strerror(err));
+ exit (err);
+ }
+}
+
+pid_t
+posix_spawn_for_debug
+(
+ char *const *argv,
+ char *const *envp,
+ const char *working_dir,
+ cpu_type_t cpu_type,
+ int disable_aslr)
+{
+ pid_t pid = 0;
+
+ const char *path = argv[0];
+
+ posix_spawnattr_t attr;
+
+ exit_with_errno (::posix_spawnattr_init (&attr), "::posix_spawnattr_init (&attr) error: ");
+
+ // Here we are using a darwin specific feature that allows us to exec only
+ // since we want this program to turn into the program we want to debug,
+ // and also have the new program start suspended (right at __dyld_start)
+ // so we can debug it
+ short flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETEXEC | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
+
+ // Disable ASLR if we were asked to
+ if (disable_aslr)
+ flags |= _POSIX_SPAWN_DISABLE_ASLR;
+
+ sigset_t no_signals;
+ sigset_t all_signals;
+ sigemptyset (&no_signals);
+ sigfillset (&all_signals);
+ ::posix_spawnattr_setsigmask(&attr, &no_signals);
+ ::posix_spawnattr_setsigdefault(&attr, &all_signals);
+
+ // Set the flags we just made into our posix spawn attributes
+ exit_with_errno (::posix_spawnattr_setflags (&attr, flags), "::posix_spawnattr_setflags (&attr, flags) error: ");
+
+
+ // Another darwin specific thing here where we can select the architecture
+ // of the binary we want to re-exec as.
+ if (cpu_type != 0)
+ {
+ size_t ocount = 0;
+ exit_with_errno (::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), "posix_spawnattr_setbinpref_np () error: ");
+ }
+
+ // I wish there was a posix_spawn flag to change the working directory of
+ // the inferior process we will spawn, but there currently isn't. If there
+ // ever is a better way to do this, we should use it. I would rather not
+ // manually fork, chdir in the child process, and then posix_spawn with exec
+ // as the whole reason for doing posix_spawn is to not hose anything up
+ // after the fork and prior to the exec...
+ if (working_dir)
+ ::chdir (working_dir);
+
+ exit_with_errno (::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), "posix_spawn() error: ");
+
+ // This code will only be reached if the posix_spawn exec failed...
+ ::posix_spawnattr_destroy (&attr);
+
+ return pid;
+}
+
+
+int main (int argc, char *const *argv, char *const *envp, const char **apple)
+{
+#if defined (DEBUG_LLDB_LAUNCHER)
+ const char *program_name = strrchr(apple[0], '/');
+
+ if (program_name)
+ program_name++; // Skip the last slash..
+ else
+ program_name = apple[0];
+
+ printf("%s called with:\n", program_name);
+ for (int i=0; i<argc; ++i)
+ printf("argv[%u] = '%s'\n", i, argv[i]);
+#endif
+
+ cpu_type_t cpu_type = 0;
+ bool show_usage = false;
+ int ch;
+ int disable_aslr = 0; // By default we disable ASLR
+ bool pass_env = true;
+ std::string unix_socket_name;
+ std::string working_dir;
+
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+
+ while ((ch = getopt_long_only(argc, argv, "a:deE:hsu:?", g_long_options, NULL)) != -1)
+ {
+ switch (ch)
+ {
+ case 0:
+ break;
+
+ case 'a': // "-a i386" or "--arch=i386"
+ if (optarg)
+ {
+ if (streq (optarg, "i386"))
+ cpu_type = CPU_TYPE_I386;
+ else if (streq (optarg, "x86_64"))
+ cpu_type = CPU_TYPE_X86_64;
+ else if (streq (optarg, "x86_64h"))
+ cpu_type = 0; // Don't set CPU type when we have x86_64h
+ else if (strstr (optarg, "arm") == optarg)
+ cpu_type = CPU_TYPE_ARM;
+ else
+ {
+ ::fprintf (stderr, "error: unsupported cpu type '%s'\n", optarg);
+ ::exit (1);
+ }
+ }
+ break;
+
+ case 'd':
+ disable_aslr = 1;
+ break;
+
+ case 'e':
+ pass_env = false;
+ break;
+
+ case 'E':
+ {
+ // Since we will exec this program into our new program, we can just set environment
+ // variables in this process and they will make it into the child process.
+ std::string name;
+ std::string value;
+ const char *equal_pos = strchr (optarg, '=');
+ if (equal_pos)
+ {
+ name.assign (optarg, equal_pos - optarg);
+ value.assign (equal_pos + 1);
+ }
+ else
+ {
+ name = optarg;
+ }
+ ::setenv (name.c_str(), value.c_str(), 1);
+ }
+ break;
+
+ case 's':
+ // Create a new session to avoid having control-C presses kill our current
+ // terminal session when this program is launched from a .command file
+ ::setsid();
+ break;
+
+ case 'u':
+ unix_socket_name.assign (optarg);
+ break;
+
+ case 'w':
+ {
+ struct stat working_dir_stat;
+ if (stat (optarg, &working_dir_stat) == 0)
+ working_dir.assign (optarg);
+ else
+ ::fprintf(stderr, "warning: working directory doesn't exist: '%s'\n", optarg);
+ }
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ show_usage = true;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (show_usage || argc <= 0 || unix_socket_name.empty())
+ usage();
+
+#if defined (DEBUG_LLDB_LAUNCHER)
+ printf ("\n%s post options:\n", program_name);
+ for (int i=0; i<argc; ++i)
+ printf ("argv[%u] = '%s'\n", i, argv[i]);
+#endif
+
+ // Open the socket that was passed in as an option
+ struct sockaddr_un saddr_un;
+ int s = ::socket (AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ {
+ perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");
+ exit(1);
+ }
+
+ saddr_un.sun_family = AF_UNIX;
+ ::strncpy(saddr_un.sun_path, unix_socket_name.c_str(), sizeof(saddr_un.sun_path) - 1);
+ saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
+ saddr_un.sun_len = SUN_LEN (&saddr_un);
+
+ if (::connect (s, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0)
+ {
+ perror("error: connect (socket, &saddr_un, saddr_un_len)");
+ exit(1);
+ }
+
+ // We were able to connect to the socket, now write our PID so whomever
+ // launched us will know this process's ID
+ char pid_str[64];
+ const int pid_str_len = ::snprintf (pid_str, sizeof(pid_str), "%i", ::getpid());
+ const int bytes_sent = ::send (s, pid_str, pid_str_len, 0);
+
+ if (pid_str_len != bytes_sent)
+ {
+ perror("error: send (s, pid_str, pid_str_len, 0)");
+ exit (1);
+ }
+
+ // We are done with the socket
+ close (s);
+
+ system("clear");
+ printf ("Launching: '%s'\n", argv[0]);
+ if (working_dir.empty())
+ {
+ char cwd[PATH_MAX];
+ const char *cwd_ptr = getcwd(cwd, sizeof(cwd));
+ printf ("Working directory: '%s'\n", cwd_ptr);
+ }
+ else
+ {
+ printf ("Working directory: '%s'\n", working_dir.c_str());
+ }
+ printf ("%i arguments:\n", argc);
+
+ for (int i=0; i<argc; ++i)
+ printf ("argv[%u] = '%s'\n", i, argv[i]);
+
+ // Now we posix spawn to exec this process into the inferior that we want
+ // to debug.
+ posix_spawn_for_debug (argv,
+ pass_env ? *_NSGetEnviron() : NULL, // Pass current environment as we may have modified it if "--env" options was used, do NOT pass "envp" here
+ working_dir.empty() ? NULL : working_dir.c_str(),
+ cpu_type,
+ disable_aslr);
+
+ return 0;
+}
+
+#endif // #if defined (__APPLE__)
+
diff --git a/tools/darwin-threads/examine-threads.c b/tools/darwin-threads/examine-threads.c
new file mode 100644
index 000000000000..07212e999f99
--- /dev/null
+++ b/tools/darwin-threads/examine-threads.c
@@ -0,0 +1,514 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mach/mach.h>
+#include <mach/task_info.h>
+#include <time.h>
+#include <sys/sysctl.h>
+#include <ctype.h>
+#include <libproc.h>
+#include <errno.h>
+#include <dispatch/dispatch.h>
+
+// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
+#define CS_OPS_STATUS 0 /* return status */
+#define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
+int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
+
+/* Step through the process table, find a matching process name, return
+ the pid of that matched process.
+ If there are multiple processes with that name, issue a warning on stdout
+ and return the highest numbered process.
+ The proc_pidpath() call is used which gets the full process name including
+ directories to the executable and the full (longer than 16 character)
+ executable name. */
+
+pid_t
+get_pid_for_process_name (const char *procname)
+{
+ int process_count = proc_listpids (PROC_ALL_PIDS, 0, NULL, 0) / sizeof (pid_t);
+ if (process_count < 1)
+ {
+ printf ("Only found %d processes running!\n", process_count);
+ exit (1);
+ }
+
+ // Allocate a few extra slots in case new processes are spawned
+ int all_pids_size = sizeof (pid_t) * (process_count + 3);
+ pid_t *all_pids = (pid_t *) malloc (all_pids_size);
+
+ // re-set process_count in case the number of processes changed (got smaller; we won't do bigger)
+ process_count = proc_listpids (PROC_ALL_PIDS, 0, all_pids, all_pids_size) / sizeof (pid_t);
+
+ int i;
+ pid_t highest_pid = 0;
+ int match_count = 0;
+ for (i = 1; i < process_count; i++)
+ {
+ char pidpath[PATH_MAX];
+ int pidpath_len = proc_pidpath (all_pids[i], pidpath, sizeof (pidpath));
+ if (pidpath_len == 0)
+ continue;
+ char *j = strrchr (pidpath, '/');
+ if ((j == NULL && strcmp (procname, pidpath) == 0)
+ || (j != NULL && strcmp (j + 1, procname) == 0))
+ {
+ match_count++;
+ if (all_pids[i] > highest_pid)
+ highest_pid = all_pids[i];
+ }
+ }
+ free (all_pids);
+
+ if (match_count == 0)
+ {
+ printf ("Did not find process '%s'.\n", procname);
+ exit (1);
+ }
+ if (match_count > 1)
+ {
+ printf ("Warning: More than one process '%s'!\n", procname);
+ printf (" defaulting to the highest-pid one, %d\n", highest_pid);
+ }
+ return highest_pid;
+}
+
+/* Given a pid, get the full executable name (including directory
+ paths and the longer-than-16-chars executable name) and return
+ the basename of that (i.e. do not include the directory components).
+ This function mallocs the memory for the string it returns;
+ the caller must free this memory. */
+
+const char *
+get_process_name_for_pid (pid_t pid)
+{
+ char tmp_name[PATH_MAX];
+ if (proc_pidpath (pid, tmp_name, sizeof (tmp_name)) == 0)
+ {
+ printf ("Could not find process with pid of %d\n", (int) pid);
+ exit (1);
+ }
+ if (strrchr (tmp_name, '/'))
+ return strdup (strrchr (tmp_name, '/') + 1);
+ else
+ return strdup (tmp_name);
+}
+
+/* Get a struct kinfo_proc structure for a given pid.
+ Process name is required for error printing.
+ Gives you the current state of the process and whether it is being debugged by anyone.
+ memory is malloc()'ed for the returned struct kinfo_proc
+ and must be freed by the caller. */
+
+struct kinfo_proc *
+get_kinfo_proc_for_pid (pid_t pid, const char *process_name)
+{
+ struct kinfo_proc *kinfo = (struct kinfo_proc *) malloc (sizeof (struct kinfo_proc));
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
+ size_t len = sizeof (struct kinfo_proc);
+ if (sysctl (mib, sizeof (mib) / sizeof (mib[0]), kinfo, &len, NULL, 0) != 0)
+ {
+ free ((void *) kinfo);
+ printf ("Could not get kinfo_proc for pid %d\n", (int) pid);
+ exit (1);
+ }
+ return kinfo;
+}
+
+/* Get the basic information (thread_basic_info_t) about a given
+ thread.
+ Gives you the suspend count; thread state; user time; system time; sleep time; etc.
+ The return value is a pointer to malloc'ed memory - it is the caller's
+ responsibility to free it. */
+
+thread_basic_info_t
+get_thread_basic_info (thread_t thread)
+{
+ kern_return_t kr;
+ integer_t *thinfo = (integer_t *) malloc (sizeof (integer_t) * THREAD_INFO_MAX);
+ mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX;
+ kr = thread_info (thread, THREAD_BASIC_INFO,
+ (thread_info_t) thinfo, &thread_info_count);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to get basic thread info for a thread\n");
+ exit (1);
+ }
+ return (thread_basic_info_t) thinfo;
+}
+
+/* Get the thread identifier info (thread_identifier_info_data_t)
+ about a given thread.
+ Gives you the system-wide unique thread number; the pthread identifier number
+*/
+
+thread_identifier_info_data_t
+get_thread_identifier_info (thread_t thread)
+{
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info (thread, THREAD_IDENTIFIER_INFO,
+ (thread_info_t) &tident, &tident_count);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to get thread ident for a thread\n");
+ exit (1);
+ }
+ return tident;
+}
+
+
+/* Given a mach port # (in the examine-threads mach port namespace) for a thread,
+ find the mach port # in the inferior program's port namespace.
+ Sets inferior_port if successful.
+ Returns true if successful, false if unable to find the port number. */
+
+bool
+inferior_namespace_mach_port_num (task_t task, thread_t examine_threads_port, thread_t *inferior_port)
+{
+ kern_return_t retval;
+ mach_port_name_array_t names;
+ mach_msg_type_number_t nameslen;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t typeslen;
+
+ if (inferior_port == NULL)
+ return false;
+
+ retval = mach_port_names (task, &names, &nameslen, &types, &typeslen);
+ if (retval != KERN_SUCCESS)
+ {
+ printf ("Error - unable to get mach port names for inferior.\n");
+ return false;
+ }
+ int i = 0;
+ for (i = 0; i < nameslen; i++)
+ {
+ mach_port_t local_name;
+ mach_msg_type_name_t local_type;
+ retval = mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &local_name, &local_type);
+ if (retval == KERN_SUCCESS)
+ {
+ mach_port_deallocate (mach_task_self(), local_name);
+ if (local_name == examine_threads_port)
+ {
+ *inferior_port = names[i];
+ vm_deallocate (mach_task_self (), (vm_address_t) names, nameslen * sizeof (mach_port_t));
+ vm_deallocate (mach_task_self (), (vm_address_t) types, typeslen * sizeof (mach_port_t));
+ return true;
+ }
+ }
+ }
+ vm_deallocate (mach_task_self (), (vm_address_t) names, nameslen * sizeof (mach_port_t));
+ vm_deallocate (mach_task_self (), (vm_address_t) types, typeslen * sizeof (mach_port_t));
+ return false;
+}
+
+/* Get the current pc value for a given thread. */
+
+uint64_t
+get_current_pc (thread_t thread, int *wordsize)
+{
+ kern_return_t kr ;
+
+#if defined (__x86_64__) || defined (__i386__)
+ x86_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT;
+ kr = thread_get_state (thread, x86_THREAD_STATE,
+ (thread_state_t) &gp_regs, &gp_count);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to get registers for a thread\n");
+ exit (1);
+ }
+
+ if (gp_regs.tsh.flavor == x86_THREAD_STATE64)
+ {
+ *wordsize = 8;
+ return gp_regs.uts.ts64.__rip;
+ }
+ else
+ {
+ *wordsize = 4;
+ return gp_regs.uts.ts32.__eip;
+ }
+#endif
+
+#if defined (__arm__)
+ arm_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = ARM_THREAD_STATE_COUNT;
+ kr = thread_get_state (thread, ARM_THREAD_STATE,
+ (thread_state_t) &gp_regs, &gp_count);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to get registers for a thread\n");
+ exit (1);
+ }
+ *wordsize = 4;
+ return gp_regs.__pc;
+#endif
+
+#if defined (__arm64__)
+ arm_thread_state64_t gp_regs;
+ mach_msg_type_number_t gp_count = ARM_THREAD_STATE64_COUNT;
+ kr = thread_get_state (thread, ARM_THREAD_STATE64,
+ (thread_state_t) &gp_regs, &gp_count);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to get registers for a thread\n");
+ exit (1);
+ }
+ *wordsize = 8;
+ return gp_regs.__pc;
+#endif
+
+}
+
+/* Get the proc_threadinfo for a given thread.
+ Gives you the thread name, if set; current and max priorities.
+ Returns 1 if successful
+ Returns 0 if proc_pidinfo() failed
+*/
+
+int
+get_proc_threadinfo (pid_t pid, uint64_t thread_handle, struct proc_threadinfo *pth)
+{
+ pth->pth_name[0] = '\0';
+ int ret = proc_pidinfo (pid, PROC_PIDTHREADINFO, thread_handle,
+ pth, sizeof (struct proc_threadinfo));
+ if (ret != 0)
+ return 1;
+ else
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ kern_return_t kr;
+ task_t task;
+ pid_t pid = 0;
+ char *procname = NULL;
+ int arg_is_procname = 0;
+ int do_loop = 0;
+ int verbose = 0;
+ int resume_when_done = 0;
+ mach_port_t mytask = mach_task_self ();
+
+ if (argc != 2 && argc != 3 && argc != 4 && argc != 5)
+ {
+ printf ("Usage: tdump [-l] [-v] [-r] pid/procname\n");
+ exit (1);
+ }
+
+ if (argc == 3 || argc == 4)
+ {
+ int i = 1;
+ while (i < argc - 1)
+ {
+ if (strcmp (argv[i], "-l") == 0)
+ do_loop = 1;
+ if (strcmp (argv[i], "-v") == 0)
+ verbose = 1;
+ if (strcmp (argv[i], "-r") == 0)
+ resume_when_done++;
+ i++;
+ }
+ }
+
+ char *c = argv[argc - 1];
+ if (*c == '\0')
+ {
+ printf ("Usage: tdump [-l] [-v] pid/procname\n");
+ exit (1);
+ }
+ while (*c != '\0')
+ {
+ if (!isdigit (*c))
+ {
+ arg_is_procname = 1;
+ procname = argv[argc - 1];
+ break;
+ }
+ c++;
+ }
+
+ if (arg_is_procname && procname)
+ {
+ pid = get_pid_for_process_name (procname);
+ }
+ else
+ {
+ errno = 0;
+ pid = (pid_t) strtol (argv[argc - 1], NULL, 10);
+ if (pid == 0 && errno == EINVAL)
+ {
+ printf ("Usage: tdump [-l] [-v] pid/procname\n");
+ exit (1);
+ }
+ }
+
+ const char *process_name = get_process_name_for_pid (pid);
+
+ // At this point "pid" is the process id and "process_name" is the process name
+ // Now we have to get the process list from the kernel (which only has the truncated
+ // 16 char names)
+
+ struct kinfo_proc *kinfo = get_kinfo_proc_for_pid (pid, process_name);
+
+ printf ("pid %d (%s) is currently ", pid, process_name);
+ switch (kinfo->kp_proc.p_stat) {
+ case SIDL: printf ("being created by fork"); break;
+ case SRUN: printf ("runnable"); break;
+ case SSLEEP: printf ("sleeping on an address"); break;
+ case SSTOP: printf ("suspended"); break;
+ case SZOMB: printf ("zombie state - awaiting collection by parent"); break;
+ default: printf ("unknown");
+ }
+ if (kinfo->kp_proc.p_flag & P_TRACED)
+ printf (" and is being debugged.");
+ free ((void *) kinfo);
+
+ printf ("\n");
+
+ int csops_flags = 0;
+ if (csops (pid, CS_OPS_STATUS, &csops_flags, sizeof (csops_flags)) != -1
+ && (csops_flags & CS_RESTRICT))
+ {
+ printf ("pid %d (%s) is restricted so nothing can attach to it.\n", pid, process_name);
+ }
+
+ kr = task_for_pid (mach_task_self (), pid, &task);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to task_for_pid()\n");
+ exit (1);
+ }
+
+ struct task_basic_info info;
+ unsigned int info_count = TASK_BASIC_INFO_COUNT;
+
+ kr = task_info (task, TASK_BASIC_INFO, (task_info_t) &info, &info_count);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to call task_info.\n");
+ exit (1);
+ }
+ printf ("Task suspend count: %d.\n", info.suspend_count);
+
+ struct timespec *rqtp = (struct timespec *) malloc (sizeof (struct timespec));
+ rqtp->tv_sec = 0;
+ rqtp->tv_nsec = 150000000;
+
+ int loop_cnt = 1;
+ do
+ {
+ int i;
+ if (do_loop)
+ printf ("Iteration %d:\n", loop_cnt++);
+ thread_array_t thread_list;
+ mach_msg_type_number_t thread_count;
+
+ kr = task_threads (task, &thread_list, &thread_count);
+ if (kr != KERN_SUCCESS)
+ {
+ printf ("Error - unable to get thread list\n");
+ exit (1);
+ }
+ printf ("pid %d has %d threads\n", pid, thread_count);
+ if (verbose)
+ printf ("\n");
+
+ for (i = 0; i < thread_count; i++)
+ {
+ thread_basic_info_t basic_info = get_thread_basic_info (thread_list[i]);
+
+ thread_identifier_info_data_t identifier_info = get_thread_identifier_info (thread_list[i]);
+
+ int wordsize;
+ uint64_t pc = get_current_pc (thread_list[i], &wordsize);
+
+ printf ("thread #%d, system-wide-unique-tid %lld, suspend count is %d, ", i,
+ identifier_info.thread_id,
+ basic_info->suspend_count);
+ if (wordsize == 8)
+ printf ("pc 0x%016llx, ", pc);
+ else
+ printf ("pc 0x%08llx, ", pc);
+ printf ("run state is ");
+ switch (basic_info->run_state) {
+ case TH_STATE_RUNNING: puts ("running"); break;
+ case TH_STATE_STOPPED: puts ("stopped"); break;
+ case TH_STATE_WAITING: puts ("waiting"); break;
+ case TH_STATE_UNINTERRUPTIBLE: puts ("uninterruptible"); break;
+ case TH_STATE_HALTED: puts ("halted"); break;
+ default: puts ("");
+ }
+
+ printf (" pthread handle id 0x%llx (not the same value as pthread_self() returns)\n", (uint64_t) identifier_info.thread_handle);
+
+ struct proc_threadinfo pth;
+ int proc_threadinfo_succeeded = get_proc_threadinfo (pid, identifier_info.thread_handle, &pth);
+
+ if (proc_threadinfo_succeeded && pth.pth_name[0] != '\0')
+ printf (" thread name '%s'\n", pth.pth_name);
+
+ printf (" libdispatch qaddr 0x%llx (not the same as the dispatch_queue_t token)\n", (uint64_t) identifier_info.dispatch_qaddr);
+
+ if (verbose)
+ {
+ printf (" (examine-threads port namespace) mach port # 0x%4.4x\n", (int) thread_list[i]);
+ thread_t mach_port_inferior_namespace;
+ if (inferior_namespace_mach_port_num (task, thread_list[i], &mach_port_inferior_namespace))
+ printf (" (inferior port namepsace) mach port # 0x%4.4x\n", (int) mach_port_inferior_namespace);
+ printf (" user %d.%06ds, system %d.%06ds",
+ basic_info->user_time.seconds, basic_info->user_time.microseconds,
+ basic_info->system_time.seconds, basic_info->system_time.microseconds);
+ if (basic_info->cpu_usage > 0)
+ {
+ float cpu_percentage = basic_info->cpu_usage / 10.0;
+ printf (", using %.1f%% cpu currently", cpu_percentage);
+ }
+ if (basic_info->sleep_time > 0)
+ printf (", this thread has slept for %d seconds", basic_info->sleep_time);
+
+ printf ("\n ");
+ printf ("scheduling policy %d", basic_info->policy);
+
+ if (basic_info->flags != 0)
+ {
+ printf (", flags %d", basic_info->flags);
+ if ((basic_info->flags | TH_FLAGS_SWAPPED) == TH_FLAGS_SWAPPED)
+ printf (" (thread is swapped out)");
+ if ((basic_info->flags | TH_FLAGS_IDLE) == TH_FLAGS_IDLE)
+ printf (" (thread is idle)");
+ }
+ if (proc_threadinfo_succeeded)
+ printf (", current pri %d, max pri %d", pth.pth_curpri, pth.pth_maxpriority);
+
+ printf ("\n\n");
+ }
+
+ free ((void *) basic_info);
+ }
+ if (do_loop)
+ printf ("\n");
+ vm_deallocate (mytask, (vm_address_t) thread_list,
+ thread_count * sizeof (thread_act_t));
+ nanosleep (rqtp, NULL);
+ } while (do_loop);
+
+ while (resume_when_done > 0)
+ {
+ kern_return_t err = task_resume (task);
+ if (err != KERN_SUCCESS)
+ printf ("Error resuming task: %d.", err);
+ resume_when_done--;
+ }
+
+ vm_deallocate (mytask, (vm_address_t) task, sizeof (task_t));
+ free ((void *) process_name);
+
+ return 0;
+}
diff --git a/tools/debugserver/CMakeLists.txt b/tools/debugserver/CMakeLists.txt
new file mode 100644
index 000000000000..d8414e5a2fe0
--- /dev/null
+++ b/tools/debugserver/CMakeLists.txt
@@ -0,0 +1,2 @@
+project(C CXX ASM-ATT)
+add_subdirectory(source)
diff --git a/tools/debugserver/Makefile b/tools/debugserver/Makefile
new file mode 100644
index 000000000000..0284ea42f407
--- /dev/null
+++ b/tools/debugserver/Makefile
@@ -0,0 +1,13 @@
+##===- tools/debugserver/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 := ../..
+
+DIRS := source
+
+include $(LLDB_LEVEL)/Makefile
diff --git a/tools/debugserver/debugnub-exports b/tools/debugserver/debugnub-exports
new file mode 100644
index 000000000000..662bf9308a6f
--- /dev/null
+++ b/tools/debugserver/debugnub-exports
@@ -0,0 +1,2 @@
+_DNB*
+__DNB*
diff --git a/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/tools/debugserver/debugserver.xcodeproj/project.pbxproj
new file mode 100644
index 000000000000..2f7a557bf0b9
--- /dev/null
+++ b/tools/debugserver/debugserver.xcodeproj/project.pbxproj
@@ -0,0 +1,1945 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; };
+ 2660D9CE1192280900958FBD /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9CC1192280900958FBD /* StringExtractor.cpp */; };
+ 266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; };
+ 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; };
+ 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; };
+ 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; };
+ 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; };
+ 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; };
+ 26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; };
+ 26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; };
+ 26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; };
+ 26CE05B0115C36340022F371 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; };
+ 26CE05B1115C36350022F371 /* MachProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; };
+ 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; };
+ 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; };
+ 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; };
+ 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; };
+ 26CE05B6115C36390022F371 /* MachTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; };
+ 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; };
+ 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; };
+ 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; };
+ 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; };
+ 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; };
+ 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; };
+ 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; };
+ 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; };
+ 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; };
+ 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; };
+ 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; };
+ 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; };
+ 26CE05C3115C36580022F371 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; };
+ 26CE05C4115C36590022F371 /* CFData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */; };
+ 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; };
+ 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; };
+ 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; };
+ 456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; };
+ 456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; };
+ 456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; };
+ 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; };
+ 456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; };
+ 456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; };
+ 456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; };
+ 456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; };
+ 456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; };
+ 456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; };
+ 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; };
+ 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; };
+ 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; };
+ 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; };
+ 456F67541AD46CE9002850C2 /* MachTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; };
+ 456F67551AD46CE9002850C2 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; };
+ 456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; };
+ 456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; };
+ 456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; };
+ 456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; };
+ 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; };
+ 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; };
+ 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; };
+ 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; };
+ 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; };
+ 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; };
+ 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; };
+ 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; };
+ 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; };
+ 456F67631AD46CE9002850C2 /* CFData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */; };
+ 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; };
+ 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; };
+ 456F67661AD46CE9002850C2 /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9CC1192280900958FBD /* StringExtractor.cpp */; };
+ 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; };
+ 456F67681AD46CE9002850C2 /* HasAVX.s in Sources */ = {isa = PBXBuildFile; fileRef = 4971AE7113D10F4F00649E37 /* HasAVX.s */; };
+ 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; };
+ 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; };
+ 4971AE7213D10F4F00649E37 /* HasAVX.s in Sources */ = {isa = PBXBuildFile; fileRef = 4971AE7113D10F4F00649E37 /* HasAVX.s */; };
+ AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 260828DE0CBAF7F400F95054 /* DNBRuntimeAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBRuntimeAction.h; sourceTree = "<group>"; };
+ 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBThreadResumeActions.cpp; sourceTree = "<group>"; };
+ 260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBThreadResumeActions.h; sourceTree = "<group>"; };
+ 260FC7320E5B290400043FC9 /* debugnub-exports */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "debugnub-exports"; sourceTree = SOURCE_ROOT; };
+ 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.internal.plist; sourceTree = "<group>"; };
+ 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.internal.plist; sourceTree = "<group>"; };
+ 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "debugserver-entitlements.plist"; sourceTree = "<group>"; };
+ 264D5D571293835600ED4C01 /* DNBArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArch.cpp; sourceTree = "<group>"; };
+ 264F679A1B2F9EB200140093 /* JSONGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSONGenerator.h; sourceTree = "<group>"; };
+ 26593A060D4931CC001C9FE3 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
+ 2660D9CC1192280900958FBD /* StringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractor.cpp; path = ../../source/Utility/StringExtractor.cpp; sourceTree = SOURCE_ROOT; };
+ 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplARM64.cpp; sourceTree = "<group>"; };
+ 266B5ED01460A68200E43F0A /* DNBArchImplARM64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBArchImplARM64.h; sourceTree = "<group>"; };
+ 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PThreadMutex.cpp; sourceTree = "<group>"; };
+ 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = DNBArchImpl.cpp; path = arm/DNBArchImpl.cpp; sourceTree = "<group>"; };
+ 2675D4230CCEB705000F49AF /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = DNBArchImpl.h; path = arm/DNBArchImpl.h; sourceTree = "<group>"; };
+ 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFBundle.cpp; sourceTree = "<group>"; };
+ 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFBundle.h; sourceTree = "<group>"; };
+ 2695DD9A0D3EC160007E4CA2 /* CFString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFString.h; sourceTree = "<group>"; };
+ 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFString.cpp; sourceTree = "<group>"; };
+ 2695DE2D0D3EE55B007E4CA2 /* CFData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFData.h; sourceTree = "<group>"; };
+ 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFData.cpp; sourceTree = "<group>"; };
+ 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.posix.plist; sourceTree = "<group>"; };
+ 26A02918114AB9240029C479 /* debugserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugserver.cpp; sourceTree = "<group>"; };
+ 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.plist; sourceTree = "<group>"; };
+ 26A68F7D0D104EC800665A9E /* RNBContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBContext.h; sourceTree = "<group>"; };
+ 26A68F7E0D104EC800665A9E /* RNBContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBContext.cpp; sourceTree = "<group>"; };
+ 26A68FAF0D1054DA00665A9E /* RNBSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBSocket.h; sourceTree = "<group>"; };
+ 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBSocket.cpp; sourceTree = "<group>"; };
+ 26A68FD50D10574500665A9E /* RNBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBRemote.h; sourceTree = "<group>"; };
+ 26A68FD60D10574500665A9E /* RNBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBRemote.cpp; sourceTree = "<group>"; };
+ 26A8FE1E0D11A77B00203048 /* DNBTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBTimer.h; sourceTree = "<group>"; };
+ 26ACA3340D3E956300A2120B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+ 26B67DE00EE9BC30006C8BC0 /* MachTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachTask.h; sourceTree = "<group>"; };
+ 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MachTask.mm; sourceTree = "<group>"; };
+ 26C637D60C71334A0024798E /* DNB.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNB.cpp; sourceTree = "<group>"; };
+ 26C637D70C71334A0024798E /* DNB.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNB.h; sourceTree = "<group>"; };
+ 26C637D80C71334A0024798E /* DNBArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArch.h; sourceTree = "<group>"; };
+ 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBBreakpoint.cpp; sourceTree = "<group>"; };
+ 26C637DA0C71334A0024798E /* DNBBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBBreakpoint.h; sourceTree = "<group>"; };
+ 26C637DB0C71334A0024798E /* DNBDataRef.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBDataRef.cpp; sourceTree = "<group>"; };
+ 26C637DC0C71334A0024798E /* DNBDataRef.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBDataRef.h; sourceTree = "<group>"; };
+ 26C637DD0C71334A0024798E /* DNBDefs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = DNBDefs.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 26C637DE0C71334A0024798E /* DNBError.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBError.cpp; sourceTree = "<group>"; };
+ 26C637DF0C71334A0024798E /* DNBError.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBError.h; sourceTree = "<group>"; };
+ 26C637E00C71334A0024798E /* DNBLog.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBLog.cpp; sourceTree = "<group>"; };
+ 26C637E10C71334A0024798E /* DNBLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBLog.h; sourceTree = "<group>"; };
+ 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBRegisterInfo.cpp; sourceTree = "<group>"; };
+ 26C637E30C71334A0024798E /* DNBRegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBRegisterInfo.h; sourceTree = "<group>"; };
+ 26C637E70C71334A0024798E /* CFUtils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CFUtils.h; sourceTree = "<group>"; };
+ 26C637E80C71334A0024798E /* dbgnub-mig.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 30; path = "dbgnub-mig.defs"; sourceTree = "<group>"; };
+ 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplI386.cpp; sourceTree = "<group>"; };
+ 26C637EB0C71334A0024798E /* DNBArchImplI386.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImplI386.h; sourceTree = "<group>"; };
+ 26C637EE0C71334A0024798E /* MachException.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachException.cpp; sourceTree = "<group>"; };
+ 26C637EF0C71334A0024798E /* MachException.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachException.h; sourceTree = "<group>"; };
+ 26C637F00C71334A0024798E /* MachProcess.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = MachProcess.mm; sourceTree = "<group>"; };
+ 26C637F10C71334A0024798E /* MachProcess.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachProcess.h; sourceTree = "<group>"; };
+ 26C637F20C71334A0024798E /* MachThread.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachThread.cpp; sourceTree = "<group>"; };
+ 26C637F30C71334A0024798E /* MachThread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachThread.h; sourceTree = "<group>"; };
+ 26C637F40C71334A0024798E /* MachThreadList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachThreadList.cpp; sourceTree = "<group>"; };
+ 26C637F50C71334A0024798E /* MachThreadList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachThreadList.h; sourceTree = "<group>"; };
+ 26C637F60C71334A0024798E /* MachVMMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMMemory.cpp; sourceTree = "<group>"; };
+ 26C637F70C71334A0024798E /* MachVMMemory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachVMMemory.h; sourceTree = "<group>"; };
+ 26C637F80C71334A0024798E /* MachVMRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMRegion.cpp; sourceTree = "<group>"; };
+ 26C637F90C71334A0024798E /* MachVMRegion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachVMRegion.h; sourceTree = "<group>"; };
+ 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImpl.cpp; sourceTree = "<group>"; };
+ 26C637FC0C71334A0024798E /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImpl.h; sourceTree = "<group>"; };
+ 26C637FD0C71334A0024798E /* PThreadCondition.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadCondition.h; sourceTree = "<group>"; };
+ 26C637FE0C71334A0024798E /* PThreadEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = PThreadEvent.cpp; sourceTree = "<group>"; };
+ 26C637FF0C71334A0024798E /* PThreadEvent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadEvent.h; sourceTree = "<group>"; };
+ 26C638000C71334A0024798E /* PThreadMutex.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadMutex.h; sourceTree = "<group>"; };
+ 26C638010C71334A0024798E /* SysSignal.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SysSignal.cpp; sourceTree = "<group>"; };
+ 26C638020C71334A0024798E /* SysSignal.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SysSignal.h; sourceTree = "<group>"; };
+ 26C638050C71334A0024798E /* TTYState.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = TTYState.cpp; sourceTree = "<group>"; };
+ 26C638060C71334A0024798E /* TTYState.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TTYState.h; sourceTree = "<group>"; };
+ 26CE0594115C31C20022F371 /* debugserver */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = debugserver; sourceTree = BUILT_PRODUCTS_DIR; };
+ 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplX86_64.cpp; sourceTree = "<group>"; };
+ 26CF99A31142EB7400011AAB /* DNBArchImplX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBArchImplX86_64.h; sourceTree = "<group>"; };
+ 26E6B9DA0D1329010037ECDD /* RNBDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBDefs.h; sourceTree = "<group>"; };
+ 456F67721AD46CE9002850C2 /* debugserver */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = debugserver; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4971AE7013D10F4F00649E37 /* HasAVX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HasAVX.h; sourceTree = "<group>"; };
+ 4971AE7113D10F4F00649E37 /* HasAVX.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = HasAVX.s; sourceTree = "<group>"; };
+ 49F530111331519C008956F6 /* MachRegisterStatesI386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesI386.h; sourceTree = "<group>"; };
+ 49F5301213316D7F008956F6 /* MachRegisterStatesX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesX86_64.h; sourceTree = "<group>"; };
+ 9457ECF61419864100DFE7D8 /* stack_logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stack_logging.h; sourceTree = "<group>"; };
+ AF0934BA18E12B92005A11FD /* Genealogy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Genealogy.h; sourceTree = "<group>"; };
+ AF0934BB18E12B92005A11FD /* GenealogySPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenealogySPI.h; sourceTree = "<group>"; };
+ AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "debugserver-macosx-entitlements.plist"; sourceTree = "<group>"; };
+ AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PseudoTerminal.cpp; sourceTree = "<group>"; };
+ AF67AC000D34604D0022D128 /* PseudoTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PseudoTerminal.h; sourceTree = "<group>"; };
+ AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Genealogy.cpp; sourceTree = "<group>"; };
+ ED128B7918E1F163003F6A7B /* libpmenergy.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmenergy.dylib; path = usr/lib/libpmenergy.dylib; sourceTree = SDKROOT; };
+ ED128B7A18E1F163003F6A7B /* libpmsample.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmsample.dylib; path = usr/lib/libpmsample.dylib; sourceTree = SDKROOT; };
+ EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.plist; sourceTree = "<group>"; };
+ EF88789F0D9C797C001831DA /* RNBServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBServices.h; sourceTree = "<group>"; };
+ EF8878A00D9C797C001831DA /* RNBServices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBServices.cpp; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 26CE0592115C31C20022F371 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 456F676A1AD46CE9002850C2 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 08FB7794FE84155DC02AAC07 /* dbgnub */ = {
+ isa = PBXGroup;
+ children = (
+ 26ACA3330D3E94F200A2120B /* Framework */,
+ 26C637D50C71334A0024798E /* source */,
+ 1AB674ADFE9D54B511CA2CBB /* Products */,
+ );
+ name = dbgnub;
+ sourceTree = "<group>";
+ };
+ 1AB674ADFE9D54B511CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 26CE0594115C31C20022F371 /* debugserver */,
+ 456F67721AD46CE9002850C2 /* debugserver */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 266B5ECE1460A68200E43F0A /* arm64 */ = {
+ isa = PBXGroup;
+ children = (
+ 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */,
+ 266B5ED01460A68200E43F0A /* DNBArchImplARM64.h */,
+ );
+ path = arm64;
+ sourceTree = "<group>";
+ };
+ 2675D41C0CCEB6CF000F49AF /* arm */ = {
+ isa = PBXGroup;
+ children = (
+ 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */,
+ 2675D4230CCEB705000F49AF /* DNBArchImpl.h */,
+ );
+ name = arm;
+ sourceTree = "<group>";
+ };
+ 26A028FE114AB6A60029C479 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */,
+ 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */,
+ 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */,
+ 260FC7320E5B290400043FC9 /* debugnub-exports */,
+ 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */,
+ AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */,
+ 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */,
+ EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 26A028FF114AB6BB0029C479 /* libdebugnub */ = {
+ isa = PBXGroup;
+ children = (
+ 26C637E60C71334A0024798E /* MacOSX */,
+ 260828DE0CBAF7F400F95054 /* DNBRuntimeAction.h */,
+ 26A8FE1E0D11A77B00203048 /* DNBTimer.h */,
+ 26C637D70C71334A0024798E /* DNB.h */,
+ 26C637D60C71334A0024798E /* DNB.cpp */,
+ 26C637D80C71334A0024798E /* DNBArch.h */,
+ 264D5D571293835600ED4C01 /* DNBArch.cpp */,
+ 26C637DA0C71334A0024798E /* DNBBreakpoint.h */,
+ 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */,
+ 26C637DC0C71334A0024798E /* DNBDataRef.h */,
+ 26C637DB0C71334A0024798E /* DNBDataRef.cpp */,
+ 26C637DD0C71334A0024798E /* DNBDefs.h */,
+ 26C637DF0C71334A0024798E /* DNBError.h */,
+ 26C637DE0C71334A0024798E /* DNBError.cpp */,
+ 26C637E10C71334A0024798E /* DNBLog.h */,
+ 26C637E00C71334A0024798E /* DNBLog.cpp */,
+ 26C637E30C71334A0024798E /* DNBRegisterInfo.h */,
+ 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */,
+ 260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */,
+ 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */,
+ 264F679A1B2F9EB200140093 /* JSONGenerator.h */,
+ AF67AC000D34604D0022D128 /* PseudoTerminal.h */,
+ AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */,
+ 26C637FD0C71334A0024798E /* PThreadCondition.h */,
+ 26C637FF0C71334A0024798E /* PThreadEvent.h */,
+ 26C637FE0C71334A0024798E /* PThreadEvent.cpp */,
+ 26C638000C71334A0024798E /* PThreadMutex.h */,
+ 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */,
+ 26C638020C71334A0024798E /* SysSignal.h */,
+ 26C638010C71334A0024798E /* SysSignal.cpp */,
+ 26C638060C71334A0024798E /* TTYState.h */,
+ 26C638050C71334A0024798E /* TTYState.cpp */,
+ );
+ name = libdebugnub;
+ sourceTree = "<group>";
+ };
+ 26ACA3330D3E94F200A2120B /* Framework */ = {
+ isa = PBXGroup;
+ children = (
+ ED128B7918E1F163003F6A7B /* libpmenergy.dylib */,
+ ED128B7A18E1F163003F6A7B /* libpmsample.dylib */,
+ 26ACA3340D3E956300A2120B /* CoreFoundation.framework */,
+ );
+ name = Framework;
+ sourceTree = "<group>";
+ };
+ 26C637D50C71334A0024798E /* source */ = {
+ isa = PBXGroup;
+ children = (
+ 26593A060D4931CC001C9FE3 /* ChangeLog */,
+ 26DEFD6C0D104C23008A5A07 /* debugserver */,
+ 26A028FF114AB6BB0029C479 /* libdebugnub */,
+ );
+ indentWidth = 4;
+ path = source;
+ sourceTree = "<group>";
+ tabWidth = 4;
+ usesTabs = 0;
+ };
+ 26C637E60C71334A0024798E /* MacOSX */ = {
+ isa = PBXGroup;
+ children = (
+ AF0934BA18E12B92005A11FD /* Genealogy.h */,
+ AF0934BB18E12B92005A11FD /* GenealogySPI.h */,
+ 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */,
+ 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */,
+ 2695DE2D0D3EE55B007E4CA2 /* CFData.h */,
+ 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */,
+ 2695DD9A0D3EC160007E4CA2 /* CFString.h */,
+ 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */,
+ 26C637E70C71334A0024798E /* CFUtils.h */,
+ 2675D41C0CCEB6CF000F49AF /* arm */,
+ 266B5ECE1460A68200E43F0A /* arm64 */,
+ 26C637E90C71334A0024798E /* i386 */,
+ 26C637FA0C71334A0024798E /* ppc */,
+ 26CF99A11142EB7400011AAB /* x86_64 */,
+ 4971AE7013D10F4F00649E37 /* HasAVX.h */,
+ 4971AE7113D10F4F00649E37 /* HasAVX.s */,
+ 26C637E80C71334A0024798E /* dbgnub-mig.defs */,
+ AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */,
+ 26C637EF0C71334A0024798E /* MachException.h */,
+ 26C637EE0C71334A0024798E /* MachException.cpp */,
+ 26C637F10C71334A0024798E /* MachProcess.h */,
+ 26C637F00C71334A0024798E /* MachProcess.mm */,
+ 26C637F30C71334A0024798E /* MachThread.h */,
+ 26C637F20C71334A0024798E /* MachThread.cpp */,
+ 26C637F50C71334A0024798E /* MachThreadList.h */,
+ 26C637F40C71334A0024798E /* MachThreadList.cpp */,
+ 26C637F70C71334A0024798E /* MachVMMemory.h */,
+ 26C637F60C71334A0024798E /* MachVMMemory.cpp */,
+ 26C637F90C71334A0024798E /* MachVMRegion.h */,
+ 26C637F80C71334A0024798E /* MachVMRegion.cpp */,
+ 26B67DE00EE9BC30006C8BC0 /* MachTask.h */,
+ 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */,
+ 9457ECF61419864100DFE7D8 /* stack_logging.h */,
+ );
+ path = MacOSX;
+ sourceTree = "<group>";
+ };
+ 26C637E90C71334A0024798E /* i386 */ = {
+ isa = PBXGroup;
+ children = (
+ 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */,
+ 26C637EB0C71334A0024798E /* DNBArchImplI386.h */,
+ 49F530111331519C008956F6 /* MachRegisterStatesI386.h */,
+ );
+ path = i386;
+ sourceTree = "<group>";
+ };
+ 26C637FA0C71334A0024798E /* ppc */ = {
+ isa = PBXGroup;
+ children = (
+ 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */,
+ 26C637FC0C71334A0024798E /* DNBArchImpl.h */,
+ );
+ path = ppc;
+ sourceTree = "<group>";
+ };
+ 26CF99A11142EB7400011AAB /* x86_64 */ = {
+ isa = PBXGroup;
+ children = (
+ 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */,
+ 26CF99A31142EB7400011AAB /* DNBArchImplX86_64.h */,
+ 49F5301213316D7F008956F6 /* MachRegisterStatesX86_64.h */,
+ );
+ path = x86_64;
+ sourceTree = "<group>";
+ };
+ 26DEFD6C0D104C23008A5A07 /* debugserver */ = {
+ isa = PBXGroup;
+ children = (
+ 26A02918114AB9240029C479 /* debugserver.cpp */,
+ 26A028FE114AB6A60029C479 /* Resources */,
+ 26A68F7D0D104EC800665A9E /* RNBContext.h */,
+ 26A68F7E0D104EC800665A9E /* RNBContext.cpp */,
+ EF88789F0D9C797C001831DA /* RNBServices.h */,
+ EF8878A00D9C797C001831DA /* RNBServices.cpp */,
+ 26A68FAF0D1054DA00665A9E /* RNBSocket.h */,
+ 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */,
+ 26A68FD50D10574500665A9E /* RNBRemote.h */,
+ 26A68FD60D10574500665A9E /* RNBRemote.cpp */,
+ 26E6B9DA0D1329010037ECDD /* RNBDefs.h */,
+ 2660D9CC1192280900958FBD /* StringExtractor.cpp */,
+ );
+ name = debugserver;
+ sourceTree = "<group>";
+ usesTabs = 0;
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 26CE0593115C31C20022F371 /* debugserver */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 26CE05A4115C31ED0022F371 /* Build configuration list for PBXNativeTarget "debugserver" */;
+ buildPhases = (
+ 26CE0591115C31C20022F371 /* Sources */,
+ 26CE0592115C31C20022F371 /* Frameworks */,
+ 4C3326CB18B2A2F600EB5DD7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = debugserver;
+ productName = "lldb-debugserver";
+ productReference = 26CE0594115C31C20022F371 /* debugserver */;
+ productType = "com.apple.product-type.tool";
+ };
+ 456F67431AD46CE9002850C2 /* debugserver-mini */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 456F676D1AD46CE9002850C2 /* Build configuration list for PBXNativeTarget "debugserver-mini" */;
+ buildPhases = (
+ 456F67451AD46CE9002850C2 /* Sources */,
+ 456F676A1AD46CE9002850C2 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "debugserver-mini";
+ productName = "lldb-debugserver";
+ productReference = 456F67721AD46CE9002850C2 /* debugserver */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 08FB7793FE84155DC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0700;
+ LastUpgradeCheck = 0720;
+ };
+ buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
+ mainGroup = 08FB7794FE84155DC02AAC07 /* dbgnub */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 26CE0593115C31C20022F371 /* debugserver */,
+ 456F67431AD46CE9002850C2 /* debugserver-mini */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 4C3326CB18B2A2F600EB5DD7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = "/bin/sh -x";
+ shellScript = "if [ \"${CONFIGURATION}\" != BuildAndIntegration ]\nthen\n if [ -n \"${DEBUGSERVER_USE_FROM_SYSTEM}\" ]\n then\n\t\tditto \"${DEVELOPER_DIR}/../SharedFrameworks/LLDB.framework/Resources/debugserver\" \"${TARGET_BUILD_DIR}/${TARGET_NAME}\"\n elif [ \"${DEBUGSERVER_DISABLE_CODESIGN}\" == \"\" ]\n then\n codesign -f -s lldb_codesign \"${TARGET_BUILD_DIR}/${TARGET_NAME}\"\n fi\nfi\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 26CE0591115C31C20022F371 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */,
+ 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */,
+ 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */,
+ 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */,
+ 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */,
+ 26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */,
+ 26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */,
+ 26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */,
+ 26CE05B0115C36340022F371 /* MachException.cpp in Sources */,
+ 26CE05B1115C36350022F371 /* MachProcess.mm in Sources */,
+ 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */,
+ 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */,
+ 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */,
+ 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */,
+ 26CE05B6115C36390022F371 /* MachTask.mm in Sources */,
+ 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */,
+ AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */,
+ 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */,
+ 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */,
+ 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */,
+ 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */,
+ 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */,
+ 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */,
+ 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */,
+ 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */,
+ 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */,
+ 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */,
+ 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */,
+ 26CE05C3115C36580022F371 /* CFString.cpp in Sources */,
+ 26CE05C4115C36590022F371 /* CFData.cpp in Sources */,
+ 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */,
+ 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */,
+ 2660D9CE1192280900958FBD /* StringExtractor.cpp in Sources */,
+ 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */,
+ 4971AE7213D10F4F00649E37 /* HasAVX.s in Sources */,
+ 266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 456F67451AD46CE9002850C2 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */,
+ 456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */,
+ 456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */,
+ 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */,
+ 456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */,
+ 456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */,
+ 456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */,
+ 456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */,
+ 456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */,
+ 456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */,
+ 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */,
+ 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */,
+ 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */,
+ 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */,
+ 456F67541AD46CE9002850C2 /* MachTask.mm in Sources */,
+ 456F67551AD46CE9002850C2 /* DNB.cpp in Sources */,
+ 456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */,
+ 456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */,
+ 456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */,
+ 456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */,
+ 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */,
+ 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */,
+ 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */,
+ 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */,
+ 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */,
+ 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */,
+ 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */,
+ 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */,
+ 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */,
+ 456F67631AD46CE9002850C2 /* CFData.cpp in Sources */,
+ 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */,
+ 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */,
+ 456F67661AD46CE9002850C2 /* StringExtractor.cpp in Sources */,
+ 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */,
+ 456F67681AD46CE9002850C2 /* HasAVX.s in Sources */,
+ 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB914F08733D8E0010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "ARCHS[sdk=iphoneos*]" = (
+ arm64,
+ armv7,
+ );
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = "";
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = Debug;
+ };
+ 1DEB915008733D8E0010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "ARCHS[sdk=iphoneos*]" = (
+ armv7,
+ armv7s,
+ );
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = "";
+ STRIPFLAGS = "-x";
+ STRIP_STYLE = debugging;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = Release;
+ };
+ 262419A11198A93E00067686 /* BuildAndIntegration */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ OTHER_CFLAGS = "";
+ STRIPFLAGS = "-x";
+ STRIP_STYLE = debugging;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = BuildAndIntegration;
+ };
+ 262419A21198A93E00067686 /* BuildAndIntegration */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_BUILDANDINTEGRATION;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/bin;
+ "INSTALL_PATH[sdk=iphoneos*]" = /Developer/usr/bin/;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx*]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx*]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-framework",
+ Foundation,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ SDKROOT = macosx;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ "SKIP_INSTALL[sdk=iphoneos*]" = NO;
+ STRIP_INSTALLED_PRODUCT = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = BuildAndIntegration;
+ };
+ 26CE0596115C31C30022F371 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_BKS",
+ "-DWITH_FBS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-framework",
+ Foundation,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = Debug;
+ };
+ 26CE0597115C31C30022F371 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-framework",
+ Foundation,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ 456F676E1AD46CE9002850C2 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ CODE_SIGN_IDENTITY = "";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = "";
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ STRIP_INSTALLED_PRODUCT = NO;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ ZERO_LINK = NO;
+ };
+ name = Debug;
+ };
+ 456F676F1AD46CE9002850C2 /* DebugClang */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugClang;
+ };
+ 456F67701AD46CE9002850C2 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/bin;
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ 456F67711AD46CE9002850C2 /* BuildAndIntegration */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_BUILDANDINTEGRATION;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/bin;
+ "INSTALL_PATH[sdk=iphoneos*]" = /Developer/usr/bin/;
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx*]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx*]" = "-weak-lpmenergy -weak-lpmsample";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ SDKROOT = macosx.internal;
+ SKIP_INSTALL = YES;
+ "SKIP_INSTALL[sdk=iphoneos*]" = NO;
+ STRIP_INSTALLED_PRODUCT = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = BuildAndIntegration;
+ };
+ 4968B7A916657FAE00741ABB /* DebugClang */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "ARCHS[sdk=iphoneos*]" = (
+ arm64,
+ armv7,
+ );
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = DebugClang;
+ };
+ 4968B7AA16657FAE00741ABB /* DebugClang */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-framework",
+ Foundation,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugClang;
+ };
+ 940AD5251B1FE3B10051E88F /* DebugPresubmission */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "ARCHS[sdk=iphoneos*]" = (
+ arm64,
+ armv7,
+ );
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = DebugPresubmission;
+ };
+ 940AD5261B1FE3B10051E88F /* DebugPresubmission */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_CFLAGS = "";
+ "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1";
+ "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression";
+ "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-framework",
+ Foundation,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugPresubmission;
+ };
+ 940AD5271B1FE3B10051E88F /* DebugPresubmission */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugPresubmission;
+ };
+ 94BA9B361B1A7C5700035A23 /* CustomSwift-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "lldb-debugserver";
+ };
+ name = "CustomSwift-Debug";
+ };
+ 94BA9B371B1A7C5700035A23 /* CustomSwift-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "lldb-debugserver";
+ };
+ name = "CustomSwift-Release";
+ };
+ 94D72C871ADF10AA00A3F718 /* CustomSwift-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "ARCHS[sdk=iphoneos*]" = (
+ arm64,
+ armv7,
+ );
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = "CustomSwift-Debug";
+ };
+ 94D72C881ADF10AA00A3F718 /* CustomSwift-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_CFLAGS = "";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_BKS",
+ "-DWITH_FBS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-framework",
+ Foundation,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = "CustomSwift-Debug";
+ };
+ 94D72C891ADF10B000A3F718 /* CustomSwift-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "ARCHS[sdk=iphoneos*]" = (
+ armv7,
+ armv7s,
+ );
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx.internal;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ STRIPFLAGS = "-x";
+ STRIP_STYLE = debugging;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = "CustomSwift-Release";
+ };
+ 94D72C8A1ADF10B000A3F718 /* CustomSwift-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 350.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_CFLAGS = "";
+ LLDB_COMPRESSION_LDFLAGS = "";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LFLAGS = "";
+ "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample";
+ LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+ LLDB_ZLIB_LDFLAGS = "-lz";
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_COMPRESSION_CFLAGS)",
+ "$(LLDB_ZLIB_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-framework",
+ Foundation,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ "$(LLDB_ZLIB_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ "SDKROOT[arch=i386]" = macosx;
+ "SDKROOT[arch=x86_64]" = macosx;
+ "SDKROOT[arch=x86_64h]" = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = "CustomSwift-Release";
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB914F08733D8E0010E9CD /* Debug */,
+ 94D72C871ADF10AA00A3F718 /* CustomSwift-Debug */,
+ 4968B7A916657FAE00741ABB /* DebugClang */,
+ 940AD5251B1FE3B10051E88F /* DebugPresubmission */,
+ 1DEB915008733D8E0010E9CD /* Release */,
+ 94D72C891ADF10B000A3F718 /* CustomSwift-Release */,
+ 262419A11198A93E00067686 /* BuildAndIntegration */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = BuildAndIntegration;
+ };
+ 26CE05A4115C31ED0022F371 /* Build configuration list for PBXNativeTarget "debugserver" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 26CE0596115C31C30022F371 /* Debug */,
+ 94D72C881ADF10AA00A3F718 /* CustomSwift-Debug */,
+ 4968B7AA16657FAE00741ABB /* DebugClang */,
+ 940AD5261B1FE3B10051E88F /* DebugPresubmission */,
+ 26CE0597115C31C30022F371 /* Release */,
+ 94D72C8A1ADF10B000A3F718 /* CustomSwift-Release */,
+ 262419A21198A93E00067686 /* BuildAndIntegration */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = BuildAndIntegration;
+ };
+ 456F676D1AD46CE9002850C2 /* Build configuration list for PBXNativeTarget "debugserver-mini" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 456F676E1AD46CE9002850C2 /* Debug */,
+ 456F676F1AD46CE9002850C2 /* DebugClang */,
+ 940AD5271B1FE3B10051E88F /* DebugPresubmission */,
+ 456F67701AD46CE9002850C2 /* Release */,
+ 456F67711AD46CE9002850C2 /* BuildAndIntegration */,
+ 94BA9B361B1A7C5700035A23 /* CustomSwift-Debug */,
+ 94BA9B371B1A7C5700035A23 /* CustomSwift-Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = BuildAndIntegration;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000000..c24931480d46
--- /dev/null
+++ b/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:debugserver.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme b/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme
new file mode 100644
index 000000000000..2f27b5e4eff5
--- /dev/null
+++ b/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0720"
+ version = "1.8">
+ <BuildAction
+ parallelizeBuildables = "NO"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "UBBY"
+ value = ""
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ displayScaleIsEnabled = "NO"
+ displayScale = "1.00"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES"
+ queueDebuggingEnabled = "No">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ displayScaleIsEnabled = "NO"
+ displayScale = "1.00"
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "UBBY"
+ value = ""
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/tools/debugserver/resources/lldb-debugserver-Info.plist b/tools/debugserver/resources/lldb-debugserver-Info.plist
new file mode 100644
index 000000000000..343325c2765c
--- /dev/null
+++ b/tools/debugserver/resources/lldb-debugserver-Info.plist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.debugserver</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>debugserver</string>
+ <key>CFBundleVersion</key>
+ <string>2</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/tools/debugserver/scripts/diagnose-termination.d b/tools/debugserver/scripts/diagnose-termination.d
new file mode 100644
index 000000000000..d216c9750033
--- /dev/null
+++ b/tools/debugserver/scripts/diagnose-termination.d
@@ -0,0 +1,18 @@
+fbt::exception_deliver:entry
+{
+ printf("pid %d got an exception of type %d\n", pid, arg1);
+ stack();
+ ustack();
+}
+
+syscall::kill:entry
+{
+ printf("pid %d called kill(%d, %d)\n", pid, arg0, arg1);
+ ustack();
+}
+
+syscall::__pthread_kill:entry
+{
+ printf("pid %d called pthread_kill(%p, %d)\n", pid, arg0, arg1);
+ ustack();
+}
diff --git a/tools/debugserver/source/ARM_DWARF_Registers.h b/tools/debugserver/source/ARM_DWARF_Registers.h
new file mode 100644
index 000000000000..845260ba187c
--- /dev/null
+++ b/tools/debugserver/source/ARM_DWARF_Registers.h
@@ -0,0 +1,209 @@
+//===-- ARM_DWARF_Registers.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ARM_DWARF_Registers_h_
+#define ARM_DWARF_Registers_h_
+
+
+enum
+{
+ dwarf_r0 = 0,
+ dwarf_r1,
+ dwarf_r2,
+ dwarf_r3,
+ dwarf_r4,
+ dwarf_r5,
+ dwarf_r6,
+ dwarf_r7,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_sp,
+ dwarf_lr,
+ dwarf_pc,
+ dwarf_cpsr,
+
+ dwarf_s0 = 64,
+ dwarf_s1,
+ dwarf_s2,
+ dwarf_s3,
+ dwarf_s4,
+ dwarf_s5,
+ dwarf_s6,
+ dwarf_s7,
+ dwarf_s8,
+ dwarf_s9,
+ dwarf_s10,
+ dwarf_s11,
+ dwarf_s12,
+ dwarf_s13,
+ dwarf_s14,
+ dwarf_s15,
+ dwarf_s16,
+ dwarf_s17,
+ dwarf_s18,
+ dwarf_s19,
+ dwarf_s20,
+ dwarf_s21,
+ dwarf_s22,
+ dwarf_s23,
+ dwarf_s24,
+ dwarf_s25,
+ dwarf_s26,
+ dwarf_s27,
+ dwarf_s28,
+ dwarf_s29,
+ dwarf_s30,
+ dwarf_s31,
+
+ // FPA Registers 0-7
+ dwarf_f0 = 96,
+ dwarf_f1,
+ dwarf_f2,
+ dwarf_f3,
+ dwarf_f4,
+ dwarf_f5,
+ dwarf_f6,
+ dwarf_f7,
+
+ // Intel wireless MMX general purpose registers 0 - 7
+ dwarf_wCGR0 = 104,
+ dwarf_wCGR1,
+ dwarf_wCGR2,
+ dwarf_wCGR3,
+ dwarf_wCGR4,
+ dwarf_wCGR5,
+ dwarf_wCGR6,
+ dwarf_wCGR7,
+
+ // XScale accumulator register 0–7 (they do overlap with wCGR0 - wCGR7)
+ dwarf_ACC0 = 104,
+ dwarf_ACC1,
+ dwarf_ACC2,
+ dwarf_ACC3,
+ dwarf_ACC4,
+ dwarf_ACC5,
+ dwarf_ACC6,
+ dwarf_ACC7,
+
+ // Intel wireless MMX data registers 0 - 15
+ dwarf_wR0 = 112,
+ dwarf_wR1,
+ dwarf_wR2,
+ dwarf_wR3,
+ dwarf_wR4,
+ dwarf_wR5,
+ dwarf_wR6,
+ dwarf_wR7,
+ dwarf_wR8,
+ dwarf_wR9,
+ dwarf_wR10,
+ dwarf_wR11,
+ dwarf_wR12,
+ dwarf_wR13,
+ dwarf_wR14,
+ dwarf_wR15,
+
+ dwarf_spsr = 128,
+ dwarf_spsr_fiq,
+ dwarf_spsr_irq,
+ dwarf_spsr_abt,
+ dwarf_spsr_und,
+ dwarf_spsr_svc,
+
+ dwarf_r8_usr = 144,
+ dwarf_r9_usr,
+ dwarf_r10_usr,
+ dwarf_r11_usr,
+ dwarf_r12_usr,
+ dwarf_r13_usr,
+ dwarf_r14_usr,
+ dwarf_r8_fiq,
+ dwarf_r9_fiq,
+ dwarf_r10_fiq,
+ dwarf_r11_fiq,
+ dwarf_r12_fiq,
+ dwarf_r13_fiq,
+ dwarf_r14_fiq,
+ dwarf_r13_irq,
+ dwarf_r14_irq,
+ dwarf_r13_abt,
+ dwarf_r14_abt,
+ dwarf_r13_und,
+ dwarf_r14_und,
+ dwarf_r13_svc,
+ dwarf_r14_svc,
+
+ // Intel wireless MMX control register in co-processor 0 - 7
+ dwarf_wC0 = 192,
+ dwarf_wC1,
+ dwarf_wC2,
+ dwarf_wC3,
+ dwarf_wC4,
+ dwarf_wC5,
+ dwarf_wC6,
+ dwarf_wC7,
+
+ // VFP-v3/Neon
+ dwarf_d0 = 256,
+ dwarf_d1,
+ dwarf_d2,
+ dwarf_d3,
+ dwarf_d4,
+ dwarf_d5,
+ dwarf_d6,
+ dwarf_d7,
+ dwarf_d8,
+ dwarf_d9,
+ dwarf_d10,
+ dwarf_d11,
+ dwarf_d12,
+ dwarf_d13,
+ dwarf_d14,
+ dwarf_d15,
+ dwarf_d16,
+ dwarf_d17,
+ dwarf_d18,
+ dwarf_d19,
+ dwarf_d20,
+ dwarf_d21,
+ dwarf_d22,
+ dwarf_d23,
+ dwarf_d24,
+ dwarf_d25,
+ dwarf_d26,
+ dwarf_d27,
+ dwarf_d28,
+ dwarf_d29,
+ dwarf_d30,
+ dwarf_d31,
+
+ // Neon quadword registers
+ dwarf_q0 = 288,
+ dwarf_q1,
+ dwarf_q2,
+ dwarf_q3,
+ dwarf_q4,
+ dwarf_q5,
+ dwarf_q6,
+ dwarf_q7,
+ dwarf_q8,
+ dwarf_q9,
+ dwarf_q10,
+ dwarf_q11,
+ dwarf_q12,
+ dwarf_q13,
+ dwarf_q14,
+ dwarf_q15
+};
+
+#endif // ARM_DWARF_Registers_h_
+
diff --git a/tools/debugserver/source/ARM_ehframe_Registers.h b/tools/debugserver/source/ARM_ehframe_Registers.h
new file mode 100644
index 000000000000..f6d93b3cee02
--- /dev/null
+++ b/tools/debugserver/source/ARM_ehframe_Registers.h
@@ -0,0 +1,35 @@
+//===-- ARM_ehframe_Registers.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_ARM_ehframe_Registers_h_
+#define utility_ARM_ehframe_Registers_h_
+
+enum
+{
+ ehframe_r0 = 0,
+ ehframe_r1,
+ ehframe_r2,
+ ehframe_r3,
+ ehframe_r4,
+ ehframe_r5,
+ ehframe_r6,
+ ehframe_r7,
+ ehframe_r8,
+ ehframe_r9,
+ ehframe_r10,
+ ehframe_r11,
+ ehframe_r12,
+ ehframe_sp,
+ ehframe_lr,
+ ehframe_pc,
+ ehframe_cpsr
+};
+
+#endif // utility_ARM_ehframe_Registers_h_
+
diff --git a/tools/debugserver/source/CMakeLists.txt b/tools/debugserver/source/CMakeLists.txt
new file mode 100644
index 000000000000..94cef6c31203
--- /dev/null
+++ b/tools/debugserver/source/CMakeLists.txt
@@ -0,0 +1,62 @@
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
+include_directories(${LLDB_SOURCE_DIR}/source)
+
+if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ include_directories(MacOSX)
+ #include_directories(${CMAKE_CURRENT_BINARY_DIR}/MacOSX)
+
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/../resources/lldb-debugserver-Info.plist")
+endif()
+
+check_cxx_compiler_flag("-Wno-gnu-zero-variadic-macro-arguments"
+ CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS)
+if (CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
+endif ()
+
+check_cxx_compiler_flag("-Wno-zero-length-array"
+ CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY)
+if (CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-zero-length-array")
+endif ()
+
+check_cxx_compiler_flag("-Wno-extended-offsetof"
+ CXX_SUPPORTS_NO_EXTENDED_OFFSETOF)
+if (CXX_SUPPORTS_NO_EXTENDED_OFFSETOF)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-extended-offsetof")
+endif ()
+
+if (NOT CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ add_definitions(
+ -DDEBUGSERVER_VERSION_STR="${LLDB_VERSION}"
+ )
+endif ()
+
+add_library(lldbDebugserverCommon
+ debugserver.cpp
+ DNBArch.cpp
+ DNBBreakpoint.cpp
+ DNB.cpp
+ DNBDataRef.cpp
+ DNBError.cpp
+ DNBLog.cpp
+ DNBRegisterInfo.cpp
+ DNBThreadResumeActions.cpp
+ libdebugserver.cpp
+ PseudoTerminal.cpp
+ PThreadEvent.cpp
+ PThreadMutex.cpp
+ RNBContext.cpp
+ RNBRemote.cpp
+ RNBServices.cpp
+ RNBSocket.cpp
+ SysSignal.cpp
+ TTYState.cpp
+ )
+
+if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ find_library(COCOA_LIBRARY Cocoa)
+ target_link_libraries(lldbDebugserverCommon ${COCOA_LIBRARY})
+ add_subdirectory(MacOSX)
+endif()
+
diff --git a/tools/debugserver/source/ChangeLog b/tools/debugserver/source/ChangeLog
new file mode 100644
index 000000000000..898f2fba7b04
--- /dev/null
+++ b/tools/debugserver/source/ChangeLog
@@ -0,0 +1,1515 @@
+2010-01-29 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::PrepareForAttach): No longer use the
+ SBSLaunchApplication macro from the SpringBoard.framework, use the actual
+ function name SBSLaunchApplicationForDebugging.
+ (MachProcess::CleanupAfterAttach): Ditto.
+ (MachProcess::SBForkChildForPTraceDebugging): Ditto.
+ (debugserver-entitlements.plist): Added the "seatbelt-profiles" entitlement
+ so debugserver can be sandboxed.
+
+2009-07-06 Greg Clayton <gclayton@apple.com>
+
+ * MachTask.cpp (MachTask::GetDYLDAllImageInfosAddress): Hack around bad
+ kernel code that renamed the first member of the TASK_DYLD_INFO without
+ any way to detect it has changed.
+
+2009-06-29 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (GetAllInfosMatchingName): Correctly truncate process name string
+ to MAXCOMLEN when searching kinfo_proc structs for process matches by name.
+ * MachProcess.cpp (MachProcess::PrepareForAttach): Added logging when
+ attaching to a program by name.
+
+2009-06-25 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (DNBProcessLaunch): Added a stat on the incoming path that we are
+ about to launch to make sure the file exists. If the file doesn't, then an
+ appropriate error string is returned. Also if we fail to get the task for
+ our process ID, we return an error string right away instead of letting the
+ debug session go for a little bit and then later failing after a few more
+ packets.
+
+2009-04-07 Jim Ingham <jingham@apple.com>
+
+ * RNBRemote.h: Add vAttachWait
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Add vattachwait.
+ (RNBRemoteShouldCancelCallback): New function.
+ (RNBRemote::HandlePacket_v): Handle vattachwait.
+ * RNBSocket.cpp (RNBSocket::Read): Mark the connection as closed when the
+ port goes away.
+ * DNB.cpp (DNBProcessAttachByName): New function.
+ (DNBProcessAttach): Make this handle catching the attach when done and
+ dealing with timeout & return conditions.
+ (GetAllInfos): New function.
+ (GetAlInfosMatchingName): New function.
+ (DNBProcessAttachWait): New function.
+ DNB.h: Declare DNBProcessAttachByName, DNBProcessAttachWait, change
+ signature of DNBProcessAttach.
+ * MachProcess.cpp (MachProcess::PrepareForAttach): New function.
+ (MachProcess::CheckForProcess): New function.
+ (MachProcess::CleanupAfterAttach): New function.
+ (CopyBundleIDForPath): New function.
+ (MachProcess::SBForkChildForPTraceDebugging): Convert to using
+ CopyBundleIDForPath.
+ * MachProcess.h: Declare PrepareForAttach, CleanupAfterAttach and
+ CheckForProcess.
+ * DNBTimer.h (TimeOfDayLaterThan): New function.
+ * test-remotenub.cpp (RNBRunLoopGetStartModeFromRemote): Rename from
+ RNBRunLoopGetArgsFromRemote, and handle vattachwait.
+ (RNBRunLoopLaunchAttaching): Code was moved from here into DNBProcessAttach.
+ (StartListening): New function.
+ (GetAllProcessInfos, GetAllProcessInfosMatchingName): Moved to
+ DNBProcess.cpp.
+ (main): Handle attach waitfor, and make debugserver with only a host and
+ port wait on commands from gdb.
+
+2009-04-03 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (PacketEnum): Added enum for qShlibInfoAddr.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable) Added the qShlibInfoAddr
+ packet definition to m_packets.
+ (RNBRemote::GetPacket): Log when we run into an unimplemented packet.
+ (RNBRemote::HandleReceivedPacket): Only log the packet when logging
+ LOG_RNB_REMOTE.
+ (RNBRemote::HandlePacket_q): Add support for the new qShlibInfoAddr packet.
+ * DNB.h (DNBProcessGetSharedLibraryInfoAddress): New prototype.
+ * DNB.cpp (DNBProcessGetSharedLibraryInfoAddress): New function.
+ * MachTask.h (MachProcess::GetDYLDAllImageInfosAddress): New prototype.
+ * MachTask.cpp (MachProcess::GetDYLDAllImageInfosAddress): New function.
+
+2009-04-01 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Display the detailed error message if any when
+ attaching fails.
+
+2009-03-25 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopGetArgsFromRemote): Cleaned up logging and
+ removed time deltas form the messages.
+ (RNBRunLoopLaunchAttaching): Ditto.
+ (RNBRunLoopLaunchInferior): Ditto and also use new DNBProcessLaunch that
+ takes an error string pointer.
+ * RNBContext.h (class RNBContext): Removed the m_timer member.
+ * RNBContext.cpp (RNBContext::StartProcessStatusThread): Cleaned up logging
+ and removed time deltas form the messages.
+ (RNBContext::ThreadFunctionProcessStatus): Ditto.
+ * RNBSocket.h (class RNBSocket): Removed unused m_last_errno member and
+ accessor functions.
+ * RNBSocket.cpp (RNBSocket::Listen): Cleaned up logging and
+ removed time deltas form the messages.
+ (RNBSocket::ConnectToService): Ditto.
+ (RNBSocket::Read): Ditto.
+ (RNBSocket::Write): Ditto.
+ (RNBSocket::SaveErrno): Removed.
+ (RNBSocket::ClosePort): Don't call RNBSocket::SaveErrno().
+ * RNBRemote.cpp (RNBRemote::RNBRemote): Cleaned up logging and
+ removed time deltas form the messages.
+ (RNBRemote::~RNBRemote): Ditto.
+ (RNBRemote::SendPacket): Ditto.
+ (RNBRemote::GetPacketPayload): Ditto.
+ (RNBRemote::GetPacket): Ditto): Ditto.
+ (RNBRemote::HandleAsyncPacket): Ditto.
+ (RNBRemote::HandleReceivedPacket): Ditto.
+ (RNBRemote::CommDataReceived): Ditto.
+ * DNB.cpp (DNBProcessLaunch): Changed to take a eror string pointer with
+ size for more desciptive error reporting (instead of a uint32_t pointer).
+ * DNB.h (DNBProcessLaunch): Ditto.
+ * DNBError.cpp (DNBError::AsString): Now returns NULL if there is no error.
+ * DNBError.h (DNBError::SetErrorString): New accessor to allow custom error
+ strings.
+ * arm/DNBArchImpl.cpp (DNBArchMachARM::GetGPRState): Improved logging.
+ * MachProcess.cpp (MachProcess::SBForkChildForPTraceDebugging): Improved
+ error messages when a file doesn't exist, or when unable to extract the
+ CFBundleIdentifier.
+ * PThreadEvent.cpp (class PThreadEvent): Commented out all logging calls.
+
+2009-03-07 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (GetAllProcessInfosMatchingName): New function that
+ returns matching kinfo_proc structs given a process name.
+ (main): Enhanced the --attach option to be able to take a PROCNAME or
+ a PID. Changed the --waitfor=PROCNAME option to ignore any existing
+ processes with PROCNAME so we only catch new process invocations.
+
+2009-03-07 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_p): Use the correct get current
+ thread function call so we get the correct thread registers.
+
+2009-03-03 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (g_isatty): New global that gets set to non-zero if
+ STDOUT is a TTY in the beginning of main.
+ (RNBLogSTDOUT): New macro that logs to STDOUT if g_isatty is non-zero, else
+ it logs to asl.
+ (RNBLogSTDERR): New macro that logs to STDERR if g_isatty is non-zero, else
+ it logs to asl.
+ (RNBRunLoopGetArgsFromRemote): Use new RNBLogSTDOUT/RNBLogSTDERR macros.
+ (GetAllProcessInfos): Get all process info structs for everything on the
+ system.
+ (main): Implemented new --waitfor=NAME option to allow waiting for a process
+ to run by polling the system processes. The new --waitfor-interval=N option
+ allows fine control over the polling interval where N is the number of mirco
+ seconds (usec) to wait between polls (defaults to 1000). The new
+ --waitfor-duration=N allows a timeout in seconds to be specified when
+ waiting for a process (defaults to infinite).
+
+2009-03-02 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchImpl.cpp (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup):
+ Take care of a case where no instructions execute in a Thumb IT block and
+ the last of which is a branch.
+
+2009-02-10 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (PacketEnum): Added 'detach' enumeration.
+ (RNBRemote::HandlePacket_D): New member function prototype.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Added detach support.
+ (RNBRemote::HandlePacket_D): New function for detach support.
+
+2009-02-10 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_UNIMPLEMENTED): Log this
+ packet with the packet that is unimplemented.
+ (RNBRemote::GetPacket): Call RNBRemote::HandlePacket_UNIMPLEMENTED()
+ when we don't recognize a packet.
+ (RNBRemote::HandleReceivedPacket): Don't reply to packets we don't
+ recognize with unimplemented in this function as that should have
+ already been done for us in RNBRemote::GetPacket().
+
+2009-02-10 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (PacketEnum): Added query_step_packet_supported.
+ * RNBRemot.cpp (RNBRemote::CreatePacketTable): Added new
+ qStepPacketSupported packet.
+ (RNBRemote::HandlePacket_q): Added support for the new
+ "qStepPacketSupported" packet.
+ (RNBRemote::HandlePacket_G): Some cleanup when reading registers
+ to avoid spurious console logging.
+
+2009-01-30 Greg Clayton <gclayton@apple.com>
+
+ * debugserver-entitlements.plist: Changed the entitlement
+ "run-invalid-allow" to "run-unsigned-code".
+
+2009-01-23 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchImpl.cpp (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup):
+ Merged Yusuf's changes to make software single stepping work.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Call new
+ DNBResolveExecutablePath function to resolve executable paths.
+ * DNB.h (DNBResolveExecutablePath): New function prototype.
+ * DNB.cpp (DNBResolveExecutablePath): New function that will resolve
+ relative paths and also executable paths for executables that aren't relative
+ but yet are in the shell PATH environment variable.
+
+2009-01-22 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchImpl.h (class DBNArchMachARM): Renamed member variable
+ m_chained_hw_single_step_addr to m_hw_single_chained_step_addr. Added
+ new member variables: m_sw_single_step_itblock_break_id, m_last_decode_pc,
+ and m_sw_single_step_itblock_break_count. Renamed m_thumbStaticData to
+ m_last_decode_thumb, and renamed m_decodedInstruction to m_last_decode_arm.
+ (DBNArchMachARM::DecodeITBlockInstructions): New prototype.
+ (DBNArchMachARM::DecodeInstructionUsingDisassembler): New prototype.
+ (DBNArchMachARM::BreakpointHit): New prototype.
+ * DNBArchImpl.cpp (DNBArchMachARM::ThreadDidStop): Disable any of the
+ many software single step breakpoints if any are set.
+ (DNBArchMachARM::StepNotComplete): Changed renamed member accesses.
+ (DNBArchMachARM::DecodeITBlockInstructions): New function for software
+ single stepping through Thumb IT blocks.
+ (DNBArchMachARM::EnableHardwareSingleStep): Cleaned up logging.
+ (DNBArchMachARM::ComputeNextPC): Ditto.
+ (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): Now
+ properly handles Thumb IT software single stepping.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Ditto.
+ (DNBArchMachARM::DecodeInstructionUsingDisassembler): New function.
+ (DNBArchMachARM::BreakpointHit): New breakpoint callback function.
+
+2009-01-21 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::PrivateResume): Set the process state before
+ we actually resume so we are sure to get the events in the correct order.
+
+2009-01-16 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): Include only
+ registers which are to be expedited in the T packets.
+ (RNBRemote::HandlePacket_p): Enable for all targets.
+ (struct register_map_entry): Added an expedite member so we know which
+ registers need to be sent up to the host with each stop reply packet.
+ (register_map): Updated each array members' expedite member with an
+ appropriate value.
+
+2009-01-16 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_s): Enabled the step command ("s"
+ packet) for ARM now that libdebugnub.dylib can do both hardware and software
+ single stepping.
+
+2009-01-13 Greg Clayton <gclayton@apple.com>
+
+ *DNBArchImpl.cpp (bit): New function.
+ (bits): New function.
+ (DNBArchMachARM::ConditionPassed): Use new "bit" function.
+ (DNBArchMachARM::ComputeNextPC): Use new "bit" function, remove inline
+ assembly for "RSC" instruction so this compiles for armv7 (which defaults
+ to thumb)
+ (DNBArchMachARM::NumSupportedHardwareBreakpoints): Use new "bits" function.
+ (DNBArchMachARM::NumSupportedHardwareWatchpoints): Use new "bits" function.
+
+2009-01-12 Greg Clayton <gclayton@apple.com>
+
+ * DNBArch.h (DNBArchProtocol::NumSupportedHardwareBreakpoints()): Removed
+ the "const" qualifier to allow arches to auto detect how many hardware
+ breakpoints they have.
+ (DNBArchProtocol::NumSupportedHardwareWatchpoints()): Removed the "const"
+ qualifier to allow arches to auto detect how many hardware watchpoints they
+ have.
+ * DNBArchImpl.h (DNBArchMachARM::NumSupportedHardwareBreakpoints()): Auto
+ detect how many BRP pairs are avialable and disable for armv7 for the time
+ being (rdar://problem/6372672).
+ (DNBArchMachARM::NumSupportedHardwareWatchpoints()): Auto detect how many
+ WRP pairs are avialable and disable for armv7 for the time being
+ (rdar://problem/6372672).
+
+2009-01-09 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Filled in short argument versions for
+ --applist (-t) and --lockdown (-k) options.
+ * DNBArchImpl.h (DNBArchMachARM::ConditionPassed): New protected
+ member function.
+ (DNBArchMachARM::ComputeNextPC): New protected member function.
+ (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): New
+ protected member function.
+ (DNBArchMachARM::m_thumbStaticData): New protected member variable.
+ (DNBArchMachARM::m_decodedInstruction): New protected member variable.
+ * DNBArchImpl.cpp (DNBArchMachARM::ThreadDidStop): Added extra code that
+ will log and exit when we are verifying software single stepping (a
+ compile time option).
+ (DNBArchMachARM::ConditionPassed): New function.
+ (DNBArchMachARM::ComputeNextPC): New function.
+ (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): New
+ function.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Added the guts of the
+ software single stepping.
+ (DNBArchMachARM::NumSupportedHardwareBreakpoints): Prepared for adding
+ auto detection code.
+ (DNBArchMachARM::NumSupportedHardwareWatchpoints): Prepared for adding
+ auto detection code.
+
+2008-12-11 Greg Clayton <gclayton@apple.com>
+
+ * DNB.h (DNBProcessWaitForEvent): Renamed to DNBProcessWaitForEvents.
+ (DNBProcessSetEvents): Removed (deprecated).
+ (DNBProcessGetWaitForResetMask): Removed (unused).
+ (DNBProcessSetWaitForResetMask): Removed (unused).
+ (DNBProcessInterruptEvents): New function prototype.
+ * DNB.cpp (DNBProcessWaitForEvent): Renamed to DNBProcessWaitForEvents.
+ (DNBProcessSetEvents): Removed (deprecated).
+ (DNBProcessGetWaitForResetMask): Removed (unused).
+ (DNBProcessSetWaitForResetMask): Removed (unused).
+ (DNBProcessInterruptEvents): New function that can be used to
+ asynchronously interrupt infinite wait for events calls.
+ RNBRemote.cpp (RNBRemote::HandlePacket_v): Call DNBProcessWaitForEvents.
+ RNBContext.cpp (RNBContext::ThreadFunctionProcessStatus): Ditto.
+ test-remotenub.cpp (RNBRunLoopLaunchInferior): Ditto.
+ (RNBRunLoopLaunchAttaching): Ditto.
+
+2008-12-11 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (GetProcessMap): Use new PTHREAD_MUTEX_LOCKER macro to ease
+ debugging of deadlocks.
+ (DNBProcessLaunch): Improved logging.
+ (DNBProcessMemoryRead): Call MachProcess::ReadMemory so breakpoint
+ opcodes can be removed from memory.
+ (DNBProcessMemoryWrite): Call MachProcess::WriteMemory so that we work
+ around enabled software breakpoint traps.
+ * DNBLog.cpp (GetLogThreadedMutex): New function.
+ (_DNBLogThreaded): Use new PTHREAD_MUTEX_LOCKER macro to ease
+ debugging of deadlocks.
+ (_DNBLogThreadedIf): Ditto.
+ * DNBBreakpoint.h (DNBBreakpoint::IntersectsRange): New function.
+ * DNBBreakpoint.cpp (DNBBreakpointList::FindIDByAddress): Improved
+ logging.
+ * MacOSX/MachThread.cpp (MachThread::MachThread): Improved logging.
+ (MachThread::~MachThread): Ditto.
+ (MachThread::Suspend): Ditto.
+ (MachThread::Resume): Ditto.
+ (MachThread::RestoreSuspendCount): Ditto.
+ (MachThread::GetState): Use new PTHREAD_MUTEX_LOCKER macro to ease
+ debugging of deadlocks.
+ (MachThread::SetState): Ditto.
+ * MacOSX/MachVMMemory.cpp (MachVMMemory::Read): Improved logging.
+ (MachVMMemory::Write): Ditto.
+ (MachVMMemory::WriteRegion): Ditto.
+ * MacOSX/MachProcess.cpp (MachProcess::GetState): Use new
+ PTHREAD_MUTEX_LOCKER macro to ease debugging of deadlocks.
+ (MachProcess::SetState): Ditto.
+ (MachProcess::Clear): Ditto.
+ (MachProcess::PrivateResume): Ditto.
+ (MachProcess::ReplyToAllExceptions): Ditto.
+ (MachProcess::ExceptionMessageReceived): Ditto.
+ (MachProcess::AppendSTDOUT): Ditto.
+ (MachProcess::GetAvailableSTDOUT): Ditto.
+ (MachProcess::ThreadFunctionSTDIO): Renamed from to
+ MachProcess::STDIOThread.
+ (MachProcess::StartSTDIOThread): Improved logging.
+ (MachProcess::CreateBreakpoint): Ditto.
+ (MachProcess::CreateWatchpoint): Ditto.
+ (MachProcess::DisableAllBreakpoints): Ditto.
+ (MachProcess::DisableBreakpoint): Ditto.
+ (MachProcess::DisableWatchpoint): Ditto.
+ (MachProcess::EnableBreakpoint): Ditto.
+ (MachProcess::EnableWatchpoint): Ditto.
+ (MachProcess::LaunchForDebug): Ditto.
+ (MachProcess::PosixSpawnChildForPTraceDebugging): Ditto.
+ (MachProcess::Detach): Reset the running event bit after resuming prior
+ to issuing the SIGSTOP to avoid a pause.
+ (MachProcess::RemoveTrapsFromBuffer): New function that removes
+ breakpoint traps from a memory buffer.
+ (MachProcess::ReadMemory): Read memory from the task, then removes any
+ breakpoint traps prior to returning the buffer.
+ (MachProcess::WriteMemory): Write memory and any needed data to the
+ breakpoint saved opcodes for any software breakpoint traps that are
+ enabled.
+ * MacOSX/MachProcess.h (MachProcess::ThreadFunctionException): Removed.
+ (MachProcess::ThreadFunctionSTDIO): Renamed to MachProcess::STDIOThread().
+ (MachProcess::RemoveTrapsFromBuffer): New function.
+ * MacOSX/MachVMRegion.cpp (MachVMRegion::SetProtections): Improved
+ logging.
+ (MachVMRegion::RestoreProtections): Ditto.
+ (MachVMRegion::GetRegionForAddress): Ditto.
+ * MacOSX/MachException.cpp (catch_mach_exception_raise_state): Improved
+ logging.
+ (catch_mach_exception_raise_state_identity): Ditto.
+ (catch_mach_exception_raise): Ditto.
+ (MachException::Message::Dump): Ditto.
+ (MachException::Data::GetStopInfo): Ditto.
+ (MachException::Message::Receive): Ditto.
+ (MachException::Message::Reply): Ditto.
+ (MachException::Data::Dump): Ditto.
+ (MachException::PortInfo::Save): Ditto.
+ (MachException::PortInfo::Restore): Ditto.
+ * MacOSX/MachTask.cpp (MachTask::Suspend): Improved logging.
+ (MachTask::Resume): Ditto.
+ (MachTask::ReadMemory): Ditto.
+ (MachTask::WriteMemory): Ditto.
+ (MachTask::TaskPortForProcessID): Ditto.
+ (MachTask::BasicInfo): Ditto.
+ (MachTask::StartExceptionThread): Ditto.
+ (MachTask::ShutDownExcecptionThread): Ditto and use pthread_cancel to
+ interrupt the exception thread.
+ (MachTask::ExceptionThread): Ditto and revert back to infinite timeout
+ as pthread_cancel will break us out of infinite mach_msg receive calls.
+ * MacOSX/MachThreadList.cpp (MachThreadList::UpdateThreadList): Improved
+ logging.
+ (MachThreadList::CurrentThread): Use new PTHREAD_MUTEX_LOCKER macro to
+ ease debugging of deadlocks.
+ * DNBTimer.h (DNBTimer::DNBTimer): Initialize the mutex with a recursive
+ pthread.
+ (DNBTimer::Reset): Use new PTHREAD_MUTEX_LOCKER macro to ease debugging
+ of deadlocks.
+ (DNBTimer::TotalMicroSeconds): Ditto.
+ (DNBTimer::GetTime): Ditto.
+ (DNBTimer::ElapsedMicroSeconds): Ditto.
+ (DNBTimer::GetTimeOfDay): New class function.
+ * DNBError.cpp (DNBError::LogThreaded): Improved logging.
+ * test-dbgnub.cpp
+ * PThreadMutex.h: Added the ability to debug deadlocks by defining
+ DEBUG_PTHREAD_MUTEX_DEADLOCKS.
+ * FunctionProfiler.cpp
+ * PThreadEvent.cpp (PThreadEvent::NewEventBit): Use new
+ PTHREAD_MUTEX_LOCKER macro to ease debugging of deadlocks.
+ (PThreadEvent::FreeEventBits): Ditto.
+ (PThreadEvent::GetEventBits): Ditto.
+ (PThreadEvent::ReplaceEventBits): Ditto.
+ (PThreadEvent::SetEvents): Ditto.
+ (PThreadEvent::ResetEvents): Ditto.
+ (PThreadEvent::WaitForSetEvents): Ditto.
+ (PThreadEvent::WaitForEventsToReset): Ditto.
+
+2008-12-05 Greg Clayton <gclayton@apple.com>
+
+ * DNBDefs.h (LOG_TASK): New log bit.
+ * DNB.cpp (DNBProcessIsAlive): User newly abstracted MachTask class.
+ (DNBProcessMemoryRead): Ditto.
+ (DNBProcessMemoryWrite): Ditto.
+ * DNBArchImpl.cpp (DNBArchMachARM::EnableHardwareSingleStep): Ditto.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints) Ditto.
+ * MachException.cpp (MachException::Message::Receive): Cleaned up logging
+ so it doesn't always log timeout errors.
+ (MachException::Message::Reply): Use abstracted MachTask class for any
+ task related queries.
+ (MachException::PortInfo::Save): Cleaned up logging.
+ (MachException::PortInfo::Restore): Cleaned up logging and now return an
+ error instead of the number of restored port infos.
+ * MachProcess.cpp (class MachProcess): Abstracted out all of the task_t
+ related stuff (suspend, resume, exception ports, exception thread, and
+ more) into a new class MachTask.
+ (MachProcess::Task): Now returns a reference to a MachTask class.
+ (MachProcess::Clear): Uses new abstracted MachTask class.
+ (MachProcess::Detach): Ditto.
+ (MachProcess::PrivateResume): Ditto.
+ (MachProcess::DisableBreakpoint): Ditto.
+ (MachProcess::ExceptionMessageReceived): Ditto.
+ (MachProcess::ExceptionMessageBundleComplete): Ditto.
+ (MachProcess::AttachForDebug): Ditto.
+ (MachProcess::LaunchForDebug): Ditto.
+ (MachProcess::SBLaunchForDebug): Ditto.
+ (MachProcess::TaskIsValid): Removed (replaced by similar functionality
+ in the new MachTask class).
+ (MachProcess::ExceptionPort): Ditto.
+ (MachProcess::ExceptionPortIsValid): Ditto.
+ (MachProcess::StartExceptionThread): Ditto.
+ (MachProcess::Suspend): Ditto.
+ (MachProcess::TaskResume): Ditto.
+ (MachProcess::TaskBasicInfo): Ditto.
+ (MachProcess::TaskBasicInfo): Ditto.
+ (MachProcess::ReadMemory): Ditto.
+ (MachProcess::WriteMemory): Ditto.
+ (MachProcess::ThreadFunctionException): Ditto.
+
+2008-12-04 Greg Clayton <gclayton@apple.com>
+
+ * DNB.h (DNBProcessSetEvents): New API function prototype.
+ * DNB.cpp (DNBProcessSetEvents): New API function.
+ (DNBProcessHalt): Send our process a SIGINT instead of suspending
+ the task.
+ * DNBDefs.h (NUB_STATE_IS_STOPPED): Removed up duplicate entry in macro.
+ (eEventPrcoessAsyncInterrupt): New prcoess event bit that allows async
+ interrupting of infinite DNBProcessWaitForEvent() function calls.
+ * MachException.cpp (MachException::Message::Receive): Improved logging.
+ (MachException::Message::Reply): Improved logging.
+ * MachProcess.h (MachProcess::TaskBasicInfo): New member and static
+ functions.
+ * MachProcess.cpp (MachProcess::TaskIsValid): Use new TaskBasicInfo()
+ member function.
+ (MachProcess::Resume): Removed the detach parameter from the PrivateResume()
+ function call.
+ (MachProcess::Kill): Added a absolute timeout pointer to allow callers to
+ wait for the signal to be received if the timeout is non-NULL.
+ (MachProcess::TaskBasicInfo): New member and static function.
+ (MachProcess::TaskResume): New function that resumes the task by making sure
+ the suspend count is correctly ref counted.
+ (MachProcess::Detach): When detaching from a process make sure it is
+ stopped (SIGSTOP) first, then we can successfully detach. The exception
+ thread now also properly exits.
+ (MachProcess::PrivateResume): Call new TaskResume function, and removed the
+ detach functionality.
+ (MachProcess::DisableBreakpoint): Only notify the thread list that a
+ breakpoint has changed if the breakpoint is going to be removed.
+ (MachProcess::ThreadFunctionException): Added a permanent 1 second timeout
+ for each call to mach_msg() so we can exit the thread in the event that
+ we detach from a process/task.
+ * test-debugnub (main): Modified to show an example of how to detach using
+ a signal_handler to asynchronously receive a SIGINT and properly interrupt
+ and detach from a running process.
+
+2008-11-26 Greg Clayton <gclayton@apple.com>
+
+ * DNBDefs.h (LOG_STEP): New logging define.
+ * DNBError.cpp (DNBError::LogThreaded): If there is no error, then
+ log with "success: " as a prefix instead of "error: ".
+ * arm/DBNArchImpl.cpp (DNBArchMachARM::EnableHardwareSingleStep): Log using
+ new LOG_STEP instead of LOG_BREAKPOINTS.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Ditto.
+ * MachException.cpp (MachException::Message::Dump): Log exception header
+ and reply header on two separate lines.
+ * MachProcess.cpp (IsSBProcess): Check for NULL CFArrayRef returned from
+ SBSCopyApplicationDisplayIdentifiers for SkankPhone.
+ (MachProcess::Suspend): Check if process state is not running instead of
+ having to receive an event after a timeout if one is given.
+ (MachProcess::Detach): Deallocate the exception port when detaching and
+ restore the inferior task exception ports prior to clearing and detaching.
+ (MachProcess::PrivateResume): Grab the task's basic info and make sure we
+ get the resume the correct number of times.
+ (MachProcess::DisableBreakpoint): Removed unused variable opcode_restored
+ and make sure the breakpoint is enabled before we start warning that
+ our opcode wasn't there.
+ * ppc/DBNArchImpl.cpp (DNBArchMachPPC::EnableHardwareSingleStep): Log
+ using LOG_STEP instead of LOAD_BREAKPOINTS.
+ * RNBServices.cpp (IsSBProcess): Check for NULL CFArrayRef returned from
+ SBSCopyApplicationDisplayIdentifiers for SkankPhone.
+
+2008-11-26 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.h (MachProcess::Suspend): Now takes an optional absolute
+ timeout that, if non-NULL, will case the function to return after the
+ process has been suspended and is in a stopped state. If the timeout is
+ NULL, then no waiting will occur.
+ * MachProcess.cpp (MachProcess::Suspend): Ditto.
+ (MachProcess::Detach): Now replies to all exceptions, un-suspends all
+ threads and resumes the task.
+ (MachProcess::ReplyToAllExceptions): New function.
+ (MachProcess::PrivateResume): Now takes an additional parameter named
+ detach that will do the right thing when detaching from a process.
+ * DNBArchImpl.h (DNBArchMachI386::ThreadWillResume): Returns void.
+ * DNBArchImpl.cpp (DNBArchMachI386::ThreadWillResume): Returns void.
+ * RNBServices.cpp (ListApplications): #ifdef-ed for ARM only as it
+ currently uses SpringBoard.
+ (IsSBProcess): Ditto.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): #ifdef-ed around
+ ARM parts so it compiles for i386.
+ (main): Ditto.
+
+2008-11-24 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchProtocol.h (DNBArchProtocol::ThreadWillResume): Now returns void.
+ * DNBArchImpl.cpp (DNBArchMachARM::ThreadWillResume): Returns void and
+ has hollowed out support for software single step.
+ (DNBArchMachARM::ThreadDidStop): Has a debug mode that uses hardware single
+ step to verify software single step that can be enabled by defining
+ DNB_ARCH_MACH_ARM_DEBUG_SW_STEP.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): New function.
+ * DNBArchImpl.h (DNBArchMachARM::ThreadWillResume): Returns void.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): New prototype.
+ (DNBArchMachARM::m_sw_single_step_next_pc): New member variable.
+ (DNBArchMachARM::m_sw_single_step_break_id): New member variable.
+ * MachThread.cpp (MachThread::ThreadWillResume): Now returns void.
+ * MachThread.h (MachThread::ThreadWillResume): Now returns void.
+
+2008-11-19 Greg Clayton <gclayton@apple.com>
+
+ * DNBError.h (FlavorType): Added SpringBoard error type for arm builds.
+ * DNBError.cpp (DNBError::AsString): Now returns SpringBoard error strings
+ if the error type is SpringBoard.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Set the error into
+ RNBContext as either a POSIX error or a SpringBoard error.
+ * RNBContext.h (m_launch_status): Changed this member to be a DNBError
+ instead of a uint32_t.
+ (RNBContext::LaunchStatus): Now returns a reference to the DNBError object
+ in m_launch_status.
+ * RNBContext.cpp (RNBContext::LaunchStatusAsString): Let DNBError handle
+ any error string descriptions, including SpringBoard errors.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): Use new error class in
+ RNBContext.
+ (RNBRemote::HandlePacket_C): Return without an erroneous error when resuming
+ a process with a signal.
+ * DNBArch.h (DNBArchProtocol::StepNotComplete): New protocol function with
+ default return value.
+ * DNBArchImpl.cpp (DNBArchMachARM::StepNotComplete): New function.
+ (DNBArchMachARM::EnableHardwareSingleStep): Handle hardware single stepping
+ over 32 bit thumb instructions better so we always do a true instruction
+ level single step.
+ * MachProcess.cpp (MachProcess::ExceptionMessageBundleComplete): Now resumes
+ if single stepping wasn't able to complete in a single run.
+ * MachThread.cpp (MachThread::ShouldStop): Fills in new step_more parameter
+ if stepping is not complete.
+ * MachThreadList.cpp (MachThreadList::ShouldStop): Pass step_more parameter
+ to each MachThread::ShouldStop call.
+
+2008-11-13 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::PosixSpawnChildForPTraceDebugging): Don't
+ call posix_spawnattr_setbinpref_np when launching with posix_spawn on ARM
+ targets as it currently selects the incorrect slice due to multiple slices
+ that contain the same cputype, yet they all have differing cpusubtypes.
+
+2008-11-04 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (GetContinueThread): Don't return the current thread when
+ the continue thread is zero or -1.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_c): Resume the process if we
+ have no continue thread set.
+ (RNBRemote::HandlePacket_s): Ditto.
+ (RNBRemote::HandlePacket_C): Ditto unless a continue address is specified
+ in which case we will only succeed if we have one thread when the continue
+ with signal and address doesn't have a continue thread specified.
+ (RNBRemote::HandlePacket_S): Ditto.
+ * DNB.cpp (DNBProcessResumeWithSignal): New function.
+ (DNBProcessResume): Added better logging.
+ (DNBProcessHalt): Ditto.
+ (DNBThreadResume): Ditto.
+ (DNBThreadResumeWithSignal): Ditto.
+ * DNB.h (DNBProcessResumeWithSignal): New prototype.
+ * DNBError.cpp (DNBError::LogThreaded): New function.
+ * DNBError.h (DNBError::LogThreaded): New prototype.
+ * DNBLog.cpp (_DNBLogThreaded): Added sequence ID for threaded logs.
+ (_DNBLogThreadedIf): Ditto.
+ * MachException.cpp (MachException::Data::GetStopInfo): Use new SoftSignal()
+ accessor.
+ (MachException::Data::DumpStopReason): Ditto.
+ (MachException::Message::Reply): Added better logging and log using the
+ soft signal if our task matches that in the exception.
+ (MachException::Data::Dump): Added better logging.
+ * MachException.h (IsSoftSignal): Removed.
+ (SoftSignal): New function that returns the soft signal in the exception
+ data if there is one, or zero otherwise.
+ * MachProcess.cpp (MachProcess::Suspend): Improved logging.
+ (MachProcess::Resume): Ditto.
+ (MachProcess::PrivateResume): Handle the case where the process is told
+ to resume with a signal by matching the signal up to the thread that had
+ the soft signal if no thread id is specified.
+ * MachThread.cpp (MachThread::Suspend): Improved logging.
+ (MachThread::Resume): Improved logging.
+ (MachThread::RestoreSuspendCount): Improved logging.
+ (MachThread::Resume): Improved logging.
+ (MachThread::Dump): Improved logging.
+ * MachThreadList.cpp (MachThreadList::Dump): Improved logging.
+
+2008-10-22 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopMode): Added a new enum value
+ eRNBRunLoopModeInferiorAttaching.
+ (g_long_options): Added "--attach=PID" for attaching to existing processes
+ and "--launch=(auto|posix|fork|springboard)" options.
+ (RNBRunLoopLaunchInferior): Now launches process with new
+ nub_launch_flavor_t enum that can be overridden with the --launch option.
+ (RNBRunLoopLaunchAttaching): New function for attaching to existing
+ processes.
+ (main): Added command line option support for the "--attach" and "--launch"
+ options and added attach to pid support and better logging.
+ * DNB.cpp/h: (DNBProcessLaunch): Added nub_launch_flavor_t and error
+ parameter for more precise control when launching processes.
+ (DNBProcessSBLaunch): Removed function as launching with SpringBoard can
+ now be done using DNBProcessLaunch with launch_flavor being set to
+ eLaunchTypeSpringBoard (arm only).
+ (DNBProcessSBAttach): Removed function (SpringBoard processes are now auto
+ detected in the MachProcess::AttachForDebug function on ARM).
+ * DNBDefs.h (NUB_GENERIC_ERROR): New generic error definition.
+ (nub_launch_flavor_t): New enumeration used for control over process
+ launching.
+ * MachProcess.cpp (IsSBProcess): New function.
+ (MachProcess::AttachForDebug): Removed flags parameter that was being used
+ for SpringBoard flags and we now detect if a process belongs to SpringBoard
+ by calling IsSBProcess.
+ (MachProcess::LaunchForDebug): Now has launch parameter that tells it how
+ to launch the inferior process and there is also an error code that gets
+ returned. This function can now launch using fork + exec, posix_spawn,
+ or SpringBoard on ARM targets.
+ (MachProcess::SBLaunchForDebug): Now uses DNBError reference instead of
+ uint32_t pointer for the error code.
+ (MachProcess::SBForkChildForPTraceDebugging): Ditto.
+
+2008-10-22 Greg Clayton <gclayton@apple.com>
+
+ * MacOSX/arm/DNBArchImpl.cpp (DNBArchMachARM::GetRegisterValue): Set
+ register value to a uint32 value instead of a float64 value for s0 -
+ s31.
+
+2008-10-17 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Don't listen for
+ the qLaunchSuccess if we aren't doing a lockdown connnection.
+
+2008-10-13 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (class RNBRemote): Added m_watchpoints member.
+ * DNB.cpp (DNBBreakpointSet): Added boolean hardware parameter for
+ requesting that a hardware breakpoint be set.
+ (DNBWatchpointSet): New function.
+ (DNBWatchpointClear): New function.
+ (DNBWatchpointGetHitCount): New function.
+ (DNBWatchpointGetIgnoreCount): New function.
+ (DNBWatchpointSetIgnoreCount): New function.
+ (DNBWatchpointSetCallback): New function.
+ (DNBWatchpointPrint): New function.
+ * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Modified to emit
+ a single DNBLog() call so there aren't multiple newlines when logging
+ to ASL.
+ * RNBContext.cpp (RNBContext::ThreadFunctionProcessStatus): Use new
+ process state changed events.
+ * DNBBreakpoint.h (class DNBBreakpoint): Removed m_state member and
+ added m_tid, m_enabled, m_hw_preferred, m_is_watchpoint, m_watch_read,
+ m_watch_write, and m_hw_index.
+ (DNBBreakpoint::ThreadID()): New accessor.
+ (DNBBreakpoint::IsEnabled()): New accessor.
+ (DNBBreakpoint::SetEnabled()): New accessor.
+ (DNBBreakpoint::IsWatchpoint()): New accessor.
+ (DNBBreakpoint::IsBreakpoint()): New accessor.
+ (DNBBreakpoint::SetIsWatchpoint()): New accessor.
+ (DNBBreakpoint::WatchpointRead()): New accessor.
+ (DNBBreakpoint::WatchpointWrite()): New accessor.
+ (DNBBreakpoint::HardwarePreferred()): New accessor.
+ (DNBBreakpoint::IsHardware()): New accessor.
+ (DNBBreakpoint::GetHardwareIndex()): New accessor.
+ (DNBBreakpoint::SetHardwareIndex()): New accessor.
+ (DNBBreakpoint::ThreadID()): New accessor.
+ (DNBBreakpoint::GetState()): Removed accessor.
+ (DNBBreakpoint::SetState()): Removed accessor.
+ (DNBBreakpoint::AddBreakpoint()): Renamed to Add().
+ (DNBBreakpoint::RemoveBreakpoint()): Renamed to Remove().
+ (DNBBreakpoint::FindBreakIDForAddress()): Renamed to FindIDByAddress().
+ (DNBBreakpoint::ShouldStopAtBreakpoint()): Renamed to ShouldStop().
+ (DNBBreakpoint::SetBreakpointCallback()): Renamed to SetCallback().
+ (DNBBreakpoint::FindBreakpointWithAddress()): Renamed to
+ FindByAddress().
+ (DNBBreakpoint::FindBreakpointWithBreakID()): Renamed to FindByID().
+ (DNBBreakpoint::GetBreakpointAtIndex()): Renamed to GetByIndex().
+ * FunctionProfiler.h: New header for subclass of DNBRuntimeAction.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_v): Use new process state
+ changed events.
+ (RNBRemote::HandlePacket_z): Implement the hardware breakpoint and
+ watchpoint commands z1, Z1, z2, Z2, z3 and Z3
+ * PThreadEvent.h (PThreadEvent::GetEventBits): Made member function
+ const.
+ (PThreadEvent::WaitForSetEvents): Ditto.
+ (PThreadEvent::WaitForEventsToReset): Ditto.
+ (PThreadEvent::WaitForResetAck): Ditto.
+ (PThreadEvent::m_mutex): Made class member mutable.
+ (PThreadEvent::m_set_condition): Made class member mutable.
+ (PThreadEvent::m_reset_condition): New mutable class member.
+ * ProfileObjectiveC.cpp
+ * DNBArch.h (DNBArch::NotifyException): Now has default implementation
+ that returns false.
+ (DNBArch::NumSupportedHardwareBreakpoints): New virtual member
+ function with a default implementation.
+ (DNBArch::NumSupportedHardwareWatchpoints): Ditto.
+ (DNBArch::EnableHardwareBreakpoint): Ditto.
+ (DNBArch::EnableHardwareWatchpoint): Ditto.
+ (DNBArch::DisableHardwareBreakpoint): Ditto.
+ (DNBArch::DisableHardwareWatchpoint): Ditto.
+ * DNB.h (DNBBreakpointSet): New take a HARDWARE parameter that allows
+ requests for setting hardware breakpoints.
+ (DNBWatchpointSet): New function prototype.
+ (DNBWatchpointClear): New function prototype.
+ (DNBWatchpointGetHitCount): New function prototype.
+ (DNBWatchpointGetIgnoreCount): New function prototype.
+ (DNBWatchpointSetIgnoreCount): New function prototype.
+ (DNBWatchpointSetCallback): New function prototype.
+ (DNBWatchpointPrint): New function prototype.
+ * MacOSX/arm/DNBArchImpl.cpp: Added hardware breakpoint and watchpoint
+ support for ARM.
+ (DNBArchMachARM::GetCPUType): New function.
+ (DNBArchMachARM::DumpDBGState): New function.
+ (DNBArchMachARM::GetDBGState): New function.
+ (DNBArchMachARM::SetDBGState): New function.
+ (DNBArchMachARM::EnableHardwareSingleStep): New function.
+ (DNBArchMachARM::EnableHardwareBreakpoint): New function.
+ (DNBArchMachARM::NotifyException): Removed.
+ (DNBArchMachARM::DisableHardwareBreakpoint): New function.
+ (DNBArchMachARM::EnableHardwareWatchpoint): New function.
+ (DNBArchMachARM::DisableHardwareWatchpoint): New function.
+ * MacOSX/MachThread.cpp (MachThread::Suspend): Added better logging.
+ (MachThread::Resume): Ditto.
+ (MachThread::RestoreSuspendCount): Ditto.
+ (MachThread::Dump): Ditto.
+ (MachThread::EnableHardwareBreakpoint): New function.
+ (MachThread::EnableHardwareWatchpoint): New function.
+ (MachThread::DisableHardwareBreakpoint): New function.
+ (MachThread::DisableHardwareWatchpoint): New function.
+ * MacOSX/MachThreadList.h (MachThreadList::GetLastError): Removed.
+ (MachThread::EnableHardwareBreakpoint): New prototype.
+ (MachThread::DisableHardwareBreakpoint): New prototype.
+ (MachThread::EnableHardwareWatchpoint): New prototype.
+ (MachThread::DisableHardwareWatchpoint): New prototype.
+ (class MachThread): Remove m_err member variable.
+ * MacOSX/ppc/DNBArchImpl.cpp (DNBArchMachPPC::GetCPUType) New
+ function.
+ (DNBArchMachPPC::NotifyException): Removed.
+ * MacOSX/ppc/DNBArchImpl.h (DNBArchMachPPC::NotifyException): Removed.
+ * MacOSX/MachThread.h (MachThread::EnableHardwareBreakpoint): New
+ prototype.
+ (MachThread::EnableHardwareWatchpoint): New prototype.
+ (MachThread::DisableHardwareBreakpoint): New prototype.
+ (MachThread::DisableHardwareWatchpoint): New prototype.
+ (class MachThread): Renambed class member m_exception to
+ m_stop_exception.
+ * MacOSX/MachProcess.cpp (MachProcess::SetState): Updated to use new
+ process event enumerations.
+ (MachProcess::PrivateResume): Added better logging.
+ (MachProcess::CreateBreakpoint): Added bool HARDWARE parameter for
+ requesting hardware breakpoints.
+ (MachProcess::CreateWatchpoint): New function.
+ (MachProcess::DisableAllWatchpoints): New function.
+ (MachProcess::DisableWatchpoint): New function.
+ (MachProcess::DumpWatchpoint): New function.
+ (MachProcess::EnableBreakpoint): Enabled breakpoints in hardware if
+ requested and supported.
+ (MachProcess::DisableBreakpoint): Disable hardware breakpoints if that
+ is how they were set.
+ (MachProcess::EnableWatchpoint): New function.
+ (MachProcess::ExceptionMessageBundleComplete): Wait for the
+ eEventProcessRunningStateChanged event to be reset before changing
+ state to stopped to avoid race condition with very fast start/stops.
+ (MachProcess::LaunchForDebug): Added posix_spawn support.
+ (MachProcess::PosixSpawnChildForPTraceDebugging): New function.
+ * MacOSX/i386/DNBArchImpl.cpp (DNBArchMachI386::GetCPUType): New
+ function.
+ * MacOSX/i386/DNBArchImpl.h (DNBArchMachI386::GetCPUType): New
+ prototype.
+ * MacOSX/MachProcess.h (PosixSpawnChildForPTraceDebugging): New
+ prototype.
+ * MacOSX/MachException.cpp (class MachException::ThreadMessage):
+ Renamed class to MachException::Data.
+ * MacOSX/MachThreadList.cpp (class MachThreadList): Removed m_err
+ class member.
+ (MachThreadList::EnableHardwareBreakpoint): New function.
+ (MachThreadList::DisableHardwareBreakpoint): New function.
+ (MachThreadList::EnableHardwareWatchpoint): New function.
+ (MachThreadList::DisableHardwareWatchpoint): New function.
+ * MacOSX/MachException.h (class MachException::ThreadMessage):
+ Renamed class to MachException::Data.
+ * DNBDefs.h (nub_watch_t): New typedef.
+ (INVALID_NUB_HW_INDEX): New macro definition.
+ (WATCH_TYPE_READ): New macro definition.
+ (WATCH_TYPE_WRITE): New macro definition.
+ (NUB_STATE_IS_RUNNING): New macro to see if state is a running state.
+ (NUB_STATE_IS_STOPPED): New macro to see if state is a stopped state.
+ (eEventProcessStateChanged): Deprecated.
+ (eEventProcessRunningStateChanged): New process event state.
+ (eEventProcessStoppedStateChanged): New process event state.
+ (LOG_WATCHPOINTS): New macro definition for logging watchpoints.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Use new process
+ event states.
+ * FunctionProfiler.cpp: New class that allows single stepping through
+ an address range for tracing exact call graphs.
+
+2008-09-22 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (GetContinueThread): If the continue thread is zero or
+ -1 then return GetCurrentThread().
+ * RNBRemote.cpp (m_packets): Made the vCont functions call
+ RNBRemote::HandlePacket_v().
+ (RNBRemote::HandlePacket_H): Cleaned up whitespace.
+ (RNBRemote::HandlePacket_last_signal): Return actual signal values for
+ EXE_SOFTWARE/EXC_SOFT_SIGNAL mach exceptions.
+ (RNBRemote::HandlePacket_v): Implemented the 'vCont?' and 'vCont;'
+ packets.
+ (RNBRemote::HandlePacket_c): Handle the case where an address is
+ provided.
+ (RNBRemote::HandlePacket_C): Implemented the continue with signal
+ including when an address is provided.
+ (RNBRemote::HandlePacket_S): Implemented the step with signal
+ including when an address is provided.
+ * DNB.cpp (DNBProcessResume): Pass 0 as the signal when resuming
+ a process without specifying a thread.
+ (DNBThreadResume): Pass 0 as the signal when resuming a specific thread.
+ (DNBThreadResumeWithSignal): New function.
+ * DNB.h (DNBThreadResumeWithSignal): New prototype.
+ * MachException.h (MachException::Message::Reply): Added a signal
+ parameter.
+ * MachException.cpp (MachException::Message::Reply): Update the thread
+ with the new SIGNAL parameter instead of always zero so signals can be
+ passed on to programs.
+ * MachProcess.h (MachProcess::Resume): Added a signal parameter.
+ * MachProcess.h (MachProcess::PrivateResume): Added a signal parameter.
+ * MachProcess.cpp (MachProcess::Resume): Pass new SIGNAL parameter to
+ MachProcess::PrivateResume.
+ * MachProcess.cpp (MachProcess::PrivateResume): Pass new SIGNAL
+ parameter to the mach exception reply.
+
+2008-08-08 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (gProcessMap): Removed static C++ global.
+ (GetProcessMap): New Function.
+ (AddProcessToMap): New function.
+ (RemoveProcessFromMap): New function.
+ (GetProcessSP): Use new GetProcessMap function to get process list.
+
+2008-07-30 Greg Clayton <gclayton@apple.com>
+
+ * debugserver-entitlements.plist (get-task-allow): Removed.
+ (run-invalid-allow): Added boolean value set to TRUE.
+
+2008-04-18 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::Task): Added getuid(), geteuid(),
+ getgid(), getegid() to the log message if task for pid fails.
+
+2008-04-07 Greg Clayton <gclayton@apple.com>
+
+ * RNBContext.cpp (RNBContext::LaunchStatusAsString): Removed unused
+ tmp_str variable.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * CFString.cpp/h (UTF8): Made a static function that can convert
+ a CFStringRef to UTF8.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Make sure we exit after we send the
+ application list.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * RNBServices.h (IsSBProcess): New prototype;
+ * RNBServices.cpp (IsSBProcess): New function that returns true it
+ SpringBoard owns or knows about the process.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_v): Made attach work correctly.
+ * DNB.cpp (DNBProcessSBAttach): New function for use when attaching to
+ a process owned by SpringBoard.
+ (DNBProcessAttach): Fixed an issue where a local was shadowing a
+ parameter.
+ * DNB.h (DNBProcessSBAttach): New prototype.
+ * MachProcess.cpp (MachProcess::AttachForDebug): AttachForDebug now
+ takes some flags so it knows to enable SpringBoard functionality.
+ * MachProcess.h (MachProcess::AttachForDebug): Added flags parameter
+ to prototype.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopGetArgsFromRemote): Handle the new
+ attach packet and watch for connection being lost.
+ (main): handle the --applist option when there we aren't using lockdown
+ by printing the results to stdout and exiting with appropriate error code
+ if we failed. Also handle the new prototype for ListApplications.
+ * RNBServices.h (ListApplications): Change first parameter to be a std::string
+ that will get the contents of the plist so we can use this for more than
+ just lockdown.
+ * RNBServices.cpp (ListApplications): Change first parameter to be a std::string
+ that will get the contents of the plist so we can use this for more than
+ just lockdown and also fixed the logic so we actually create a full list of
+ applications instead of just overwriting the first entry.
+ * RNBRemote.h (PacketEnum): Added a new 'vattach' enum for the "vAttach;PID"
+ gdb remote command.
+ (RNBRemote::HandlePacket_v): New prototype;
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): add the vattach packet definition
+ to m_packets.
+ (RNBRemote::HandlePacket_v): New function that handles attach to a process.
+
+2008-04-03 Jim Ingham <jingham@apple.com>
+
+ * RNBRemote.h: Add query_launch_success to packet enum.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable_): Add query_launch_success.
+ (HandlePacket_q): Handle query_launch_success.
+ * DNB.cpp (DNBProcessSBLaunch): Pass in launch_retval.
+ * DNB.h: Change prototype of DNBProcessSBLaunch to take launch_retval.
+ * RNBContext.cpp (RNBContext::LaunchStatusAsString): New function.
+ * RNBContext.h (RNBContext): Add m_launch_status & accessors.
+ * macosx/MachProcess.cpp (MachProcess::SBLaunchForDebug): Pass launch_retval.
+ (MachProcess::SBForkChildForPTraceDebugging): Accept & set launch_retval.
+ * Macosx/MachProcess.h: Change prototypes of SBLaunchForDebug &
+ ForkChildForPTraceDebugging to accept launch_retval.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Get the launch status and
+ put it in the context, then wait for the qLaunchStatus packet.
+
+2008-04-03 Greg Clayton <gclayton@apple.com>
+
+ * com.apple.debugserver.plist: Changed plist so debugserver
+ runs as mobile user.
+ * com.apple.debugserver.applist.plist: Ditto.
+
+2008-04-03 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp: (MachProcess::SBForkChildForPTraceDebugging):
+ Increased SBS application launch timeout to 30 seconds.
+
+2008-03-27 Christopher Friesen <friesen@apple.com>
+
+ * RNBServices.h: Pass tasks from SpringBoard as a plist
+ * RNBServices.cpp: Ditto.
+ * test-remotenub.cpp: added --applist flag
+ * com.apple.debugserver.applist.plist: Agent plist
+
+2008-03-17 Jim Ingham <jingham@apple.com>
+
+ * DNB.h: Pass envp to DNBProcessLaunch & DNBProcessSBLaunch.
+ * DNB.cpp: Ditto.
+ * MachProcess.h: Ditto for *LaunchForDebug and
+ *ForkChildForPtraceDebugging.
+ * MachProcess.cpp (MachProcess::LaunchForDebug): Pass on envp.
+ (MachProcess::SBLaunchForDebug): Ditto.
+ (MachProcess::ForkChildForPtraceDebugging): Accept envp, haven't actually
+ implemented the passing yet.
+ (MachProcess::SBForkChildForPtraceDebuggin): Accept envp, convert to
+ CFDictionary and pass to SBSLaunchApplication.
+ * RNBContext.h: Add environment to the context.
+ * RBNContext.cpp (RNBContext::EnvironmentAtIndex): New function.
+ * RNBRemote.h: Add set_environment_variable to the PacketEnum.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Add QEnvironment:.
+ * (RNBRemote::HandlePacket_Q): Ingest the environment variable.
+ * test-remotenub.cpp (RNBRunLoppLaunchInferior): Convert the env
+ array in the context into an array, and pass it to the DNBProcess*Launch
+ methods.
+
+2008-03-17 Greg Clayton <gclayton@apple.com>
+
+ * DNBBreakpoint.cpp (DNBBreakpointList::GetBreakpointAtIndex): New
+ functions (const and non-const versions).
+ * DNBBreakpoint.h (DNBBreakpointList::GetBreakpointAtIndex): New
+ prototypes (const and non-const versions).
+ * DNBError.h (DNBError::Success()): Don't use KERN_SUCCESS define.
+ (DNBError::Fail()): Don't use KERN_SUCCESS define.
+ * MachProcess.cpp (MachProcess::DisableAllBreakpoints): New function.
+ (MachProcess::Detach): Added initial implementation that will halt
+ the process, disable all breakpoints and call PT_DETACH.
+ * MachProcess.h (MachProcess::DisableAllBreakpoints): New prototype.
+
+2008-03-04 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (RNBRemote::SendHexEncodedBytePacket): New prototype.
+ * RNBRemote.cpp (RNBRemote::SendHexEncodedBytePacket): New function.
+ (RNBRemote::SendSTDOUTPacket): Use SendHexEncodedBytePacket function
+ to send bytes.
+ (RNBRemote::SendSTDERRPacket): Ditto.
+ (RNBRemote::HandlePacket_q): Return a valid thread info string for
+ qThreadExtraInfo queries.
+ * DNB.cpp (DNBThreadPrintStopReason): Commented out unused function.
+ (DNBThreadGetInfo): New function.
+ * DNB.h (DNBThreadPrintStopReason): Commented out prototype.
+ (DNBThreadGetInfo): New prototype.
+ * MachProcess.cpp (MachProcess::GetThreadInfo): New function.
+ * MachProcess.h (MachProcess::GetThreadInfo): New prototype.
+ * MachThreadList.cpp (MachThreadList::GetThreadInfo): New function.
+ * MachThreadList.h (MachThreadList::GetThreadInfo): New prototype.
+ * MachThread.cpp (MachThread::GetBasicInfoAsString): New function.
+ (MachThread::InferiorThreadID): New function.
+ * MachThread.cpp (MachThread::GetBasicInfoAsString): New prototype.
+ (MachThread::InferiorThreadID): New prototype.
+
+2008-02-27 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): Set the
+ current thread when we notify a thread has stopped to subsequent
+ g and p packets get the correct data.
+
+2008-02-26 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add query_thread_extra_info enum.
+ * RNBRemote.cpp: Add support for qThreadExtraInfo.
+ Currently we return 'Ok' as the packet status for
+ every thread.
+
+2008-02-26 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): Correct handling
+ of qfThreadInfo/qsThreadInfo.
+
+2008-02-20 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Change default for gdb's max incoming packet size to
+ reflect the real default size.
+ * RNBRemote.cpp (HandlePacket_Q): Correct the string comparisons for
+ the QSetMaxPayloadSize and QSetMaxPacketSize packets.
+
+2008-02-19 Christopher Friesen <friesen@apple.com>
+
+ * CFDataFormatters.c: CoreFoundation data formatters added to project.
+
+2008-02-19 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Record the max payload size, not the max packet
+ size for less ambiguous meaning.
+ * RNBRemote.cpp: Add support for QSetMaxPayloadSize: packet which
+ should have a clearer meaning than QSetMaxPacketSize.
+ QSetMaxPacketSize will be removed once we get have a chance to get
+ a new debugserver and gdb submitted.
+
+2008-02-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Make default size 1024.
+ * RNBRemote.cpp: Questionmark packet should stay under
+ max_packet_size - 5 to allow for start, end, checksum and nul
+ char bytes.
+
+2008-02-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add m_max_packet_size to class defn.
+ * RNBRemote.cpp: Initialize it, use it.
+
+2008-02-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add set_max_packet_size.
+ * RNBRemote.cpp: Add QSetMaxPacketSize packet handling.
+
+2008-02-18 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (HandleProcessStateChange): Call new
+ RNBRemote::FlushSTDIO function.
+ (RNBRunLoopInferiorExecuting): Ditto.
+ * RNBRemote.h (RNBRemote::FlushSTDIO): New prototype.
+ * RNBRemote.cpp (RNBRemote::FlushSTDIO): New function to
+ centralize the stdio.
+
+2008-02-18 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (DNBProcessWaitForEvent): Added timeout pointer as
+ parameter that can be NULL for infinite timeout to simplify
+ the DNB interface.
+ (DNBProcessTimedWaitForEvent): Removed function.
+ * DNB.h (DNBProcessWaitForEvent): Added timeout argument.
+ (DNBProcessTimedWaitForEvent): Removed prototype.
+ * DNBTimer.h (DNBTimer::OffsetTimeOfDay): New function.
+ * CFString.cpp (CFString::GetLength() const): New function.
+ * CFString.h (CFString::GetLength() const): New prototype.
+ * MachProcess.h (MachProcess class): Removed m_attached and
+ added m_flags.
+ * MachProcess.cpp (MachProcess::AttachForDebug): Set m_flags
+ to indicate we attached.
+ (MachProcess::SBLaunchForDebug): Set m_flags to indicate we
+ attached using SpringBoard and that we attached.
+ (MachProcess::SBForkChildForPTraceDebugging): Changed to new
+ SpringBoardServices API.
+ (MachProcess::ThreadFunctionException): Added code that will
+ renew a watchdog assertion when we launch apps through
+ SpringBoardServices.
+ * PThreadEvent.cpp (PThreadEvent::WaitForSetEvents): Simplified
+ PThreadEvent API to have only one version of WaitForSetEvents
+ that has an optional timeout pointer argument.
+ * RNBContext.cpp (RNBContext::StopProcessStatusThread): Adapt
+ to new PThreadEvent API changes.
+ (RNBContext::ThreadFunctionProcessStatus): Adapt to new
+ DNBProcessWaitForEvent API changes.
+ * RNBRemote.cpp (RNBRemote::StopReadRemoteDataThread): Adapt
+ to new PThreadEvent API changes.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Adapt to new
+ DNBProcessWaitForEvent API changes.
+ (RNBRunLoopInferiorExecuting): Process STDIO first, then
+ incoming packets.
+
+2008-02-14 Jason Molenda (jmolenda@apple.com)
+
+ * MachProcess.cpp: (MachProcess::SBForkChildForPTraceDebugging):
+ Set mode bits on slave side of pty.
+
+2008-02-12 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (DNBEnableLogging): Removed function.
+ (DNBThreadPrintStopReason): Removed the file handle from this
+ function and use DNBLog calls.
+ * DNB.h (DNBEnableLogging): Removed function prototype.
+ (DNBThreadPrintStopReason): Removed the file handle
+ from the function prototype in favor of using DNBLog calls.
+ * DNBDataRef.cpp (DNBDataRef::Dump): Removed file handle to use
+ DNBLog for the logging and print a log line each time a full line
+ is ready for output after caching it in a local buffer.
+ * DNBDataRef.cpp (DNBDataRef::Dump): Removed file handle from
+ prototype.
+ * DNBDefs.h (DNBCallbackLog): New callback prototype for all
+ logging.
+ DNBLog.cpp(g_debug_opt): Renamed to d_debug and made it a file
+ static.
+ (DNBLogGetDebug): New accessor function for g_debug.
+ (DNBLogSetDebug): New accessor function for g_debug.
+ (g_verbose): Made into a file static and added accessors.
+ (DNBLogGetVerbose): New accessor function for g_verbose.
+ (DNBLogSetVerbose): New accessor function for g_verbose.
+ (DNBLogSetLogCallback): New function call that registers a logging
+ callback for all logging in libdebugnub.dylib and any code that
+ loads it.
+ (DNBLogToASL): Removed function as it is deprecated in favor of
+ using DNBLogSetLogCallback to register a callback function that
+ implements the logging.
+ (DNBLogToFile): Ditto.
+ (DNBLogCloseLogFile): Ditto.
+ (DNBLogToFile): Ditto.
+ (DNBLogToFile): Ditto.
+ (_DNBLogPuts): Removed unused function.
+ (_DNBLogVAPrintf): Calls the callback function to do the logging
+ if one has been registered.
+ * DNBLog.h (DNBLOG_FLAG_FATAL): New defines that get passed to
+ any registered logging callback functions.
+ (DNBLOG_FLAG_FATAL): Ditto.
+ (DNBLOG_FLAG_ERROR): Ditto.
+ (DNBLOG_FLAG_WARNING): Ditto.
+ (DNBLOG_FLAG_DEBUG): Ditto.
+ (DNBLOG_FLAG_VERBOSE): Ditto.
+ (DNBLOG_FLAG_THREADED): Ditto.
+ (DNBLog*): All logging calls are now exported from libdebugnub.dylib
+ so there aren't two copies (one in debugserver and one in debugnub).
+ C99 vararg Macros wrap all logging calls so no var arg processing
+ occurs when logging is disabled.
+ * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Removed file
+ handle and now use DNBLog calls.
+ * DNBRegisterInfo.h (DNBRegisterValueClass::Dump): Removed file
+ handle from prototype.
+ * MachException.cpp (catch_mach_exception_raise_state_identity):
+ Removed newlines from logging call.
+ (catch_mach_exception_raise): Ditto.
+ (MachException::Message::Dump): Removed file handle from params
+ and removed newlines from logging call.
+ (MachException::ThreadMessage::DumpStopReason): Removed file handle
+ from params and use DNBLog for logging output.
+ (MachException::ThreadMessage::Dump): Log using DNBLog instead of
+ file handle.
+ * MachProcess.cpp (MachProcess::DumpThreadStoppedReason): Ditto.
+ (MachProcess::ReadMemory): Ditto.
+ (MachProcess::WriteMemory): Ditto.
+ (ExceptionMessageBundleComplete): Ditto.
+ * MachThread.cpp (MachThread::Dump): Ditto.
+ (MachThread::DumpRegisterState): Ditto.
+ * MachThreadList.cpp (MachThreadList::DumpThreadStoppedReason): Ditto.
+ (MachThreadList::Dump): Ditto.
+ * RNBRemote.cpp (set_logging): Use new function callback registration
+ calls when enabling ASL logging.
+ test-remotenub.cpp (ASLLogCallback): New function to handle all ASL
+ logging. This function gets registered with libdebugnub.dylib when we
+ want to log using ASL.
+ (FileLogCallback): New function to handle all file logging. This
+ function gets registered with libdebugnub.dylib when we want to log
+ to a 'FILE *'.
+ (main): Register the logging callback functions when we want to log
+ to file or using ASL.
+
+2008-02-12 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Default to ASL logging with no log
+ bits set to allow for warning and error logging.
+ * RNBRemote.h (struct Breakpoint): New structure for ref counting
+ breakpoints in Z and z packets.
+ * RNBRemote.cpp (RNBRemote::SendPacket): Use new LOG_RNB_PACKETS
+ defined when logging actual packet content.
+ (RNBRemote::HandleAsyncPacket): Ditto.
+ (RNBRemote::HandleReceivedPacket): Ditto.
+ (RNBRemote::HandlePacket_z): Ref count the setting and removing
+ of breakpoints with the Z and z packets using new struct
+ RNBRemote::Breakpoint.
+ * RNBDefs.h (LOG_RNB_PACKETS): New define for logging the sending
+ and receiving of packets data.
+ * DNB.cpp (DNBPrintf): Check for NULL file handle.
+ * DNBBreakpoint.cpp (DNBBreakpoint::Dump): Ditto.
+ (DNBBreakpointList::Dump): Ditto.
+ * DNBDefs.h (LOG_EVENTS): New define for logging PThreadEvent.
+ * DNBLog.cpp (g_debug_opt): Relocated outside of #if that turns off
+ logging completely to allow option parsing code that uses it to
+ still compile.
+ (g_verbose): Ditto.
+ * DNBLog.h (DNBLogToASL): Added prototype for when logging is
+ disabled via preprocessor macro.
+ (DNBLogToFile): Ditto.
+ * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Check for NULL
+ file handle.
+ * MachException.cpp (MachException::ThreadMessage::DumpStopReason): Ditto.
+ (MachException::ThreadMessage::Dump): Ditto.
+ * MachProcess.cpp (MachProcess::CreateBreakpoint): Improved logging.
+ (MachProcess::DisableBreakpoint): Verify the original opcode gets
+ restored, improved logging and added unconditional logging for when
+ things go wrong.
+ (MachProcess::EnableBreakpoint): Verify the breakpoint opcode gets
+ written, improved logging and added unconditional logging for when
+ things go wrong.
+ * MachThread.cpp (MachThread::Dump): Check for NULL file handle.
+ * MachVMMemory.cpp (MachVMMemory::WriteRegion): Flush caches in inferior
+ after writing to inferior memory.
+ * PThreadEvent.cpp: Changed all logging calls to key off of LOG_EVENTS
+ instead of LOG_VERBOSE.
+ MachDYLD.cpp (MachDYLD::Dump): Check for NULL file handle.
+ (MachDYLD::DYLIBInfo::Dump): Ditto.
+ ProfileObjectiveC.cpp (ProfileObjectiveC::DumpStats): Ditto.
+
+2008-02-09 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): Log to ASL unconditionally when
+ processing a QSetLogging packet.
+
+2008-02-06 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Dup stdout and stderr to /dev/NULL
+ when we use lockdown.
+
+2008-02-06 Greg Clayton <gclayton@apple.com>
+
+ * RNBSocket.cpp (RNBSocket::Disconnect): Removed unused var ERR.
+ * RNBRemote.cpp(RNBRemote::HandlePacket_Q): Removed unused var PID.
+ * DNBError.cpp (DNBError::LogThreadedIfError): Removed unused var
+ ERR_MSG.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Removed unused
+ variable EXECUTABLE_LENGTH.
+ (main): Removed unused variable ARG_IDX.
+
+2008-02-06 Chris Marcellino (cmarcellino@apple.com) and Myke Olson (molson@apple.com)
+
+ * MachProcess.cpp (SBForkChildForPTraceDebugging): Bring up to date with
+ current SpringBoardServices.framework types and imports.
+
+2008-02-05 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): Remove the mode=file and filename=
+ options to the QSetLogging packet. We're only going to support logging
+ to ASL for now. Logging to a file can still be accomplished by the
+ -l command line argument.
+
+2008-02-02 Christopher Friesen (cfriesen@apple.com)
+
+ * Added libXcodeDebugerSupport.dylib target
+ * XCDebuggerIntrospection.[hc]: Support for Xcode's debugger introspection.
+
+2008-02-01 Jason Molenda (jmolenda@apple.com)
+
+ * DNBLog.cpp (DNBLogCloseLogFile): New function to close a logfile
+ at exit.
+ * DNBLog.h: Prototype.
+ * test-remotenub.cpp (main): Close the log file before exiting.
+
+2008-02-01 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): Recognize the "filename=" argument
+ to the QSetLogging directive.
+ * DNBLog.cpp (DNBLogGetLogMask): New fun.c
+ * DNBLog.h: Prototype.
+
+2008-01-31 Jason Molenda (jmolenda@apple.com)
+
+ * DNBLog.cpp: Add ASL logging as a run-time selectable option.
+ (DNBLogToASL, DNBLogToFile): Functions to switch between logging to
+ a file and logging via ASL.
+ * DNBLog.h: Prototypes.
+ * RNBRemote.cpp (set_logging): Recognize the "mode=" field to enable
+ asl logging. Skip unrecognized keys.
+
+2008-01-31 Greg Clayton (gclayton@apple.com)
+
+ * DNB.cpp (sigchld_handler): Better logging when we get a
+ SIGCHILD and we are watching for process related logging events.
+ * test-remotenub.cpp (RNBRunLoopInferiorExecuting): Only reset
+ events when we still have event bits set.
+
+2008-01-29 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add set_logging_mode.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Recognize
+ QSetLogging.
+
+2008-01-29 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): New function to parse the QSetLogging
+ packet.
+ (RNBRemote::HandlePacket_Q): Call it.
+
+2008-01-28 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Minimal packet size is 1024 in our gdb now.
+ * RNBRemote.cpp: Add the stop_pc value in big-endian order to the
+ T response packet to make it a little easier to follow where gdb
+ is stepping.
+
+2008-01-28 Greg Clayton <gclayton@apple.com>
+
+ * RNBContext.h: Removed m_pid_state from RNBContext class so that
+ it couldn't get out of sync with the actual process and its accessors
+ SetProcessState() and GetProcessState().
+ * RNBContext.cpp (RNBContext::ProcessStateRunning): Always return the
+ current state of the process instead of a cached value.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Remove call to
+ deprecated RNBContext::SetProcessState().
+ (HandleProcessStateChange): Ditto.
+
+2008-01-24 Greg Clayton (gclayton@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): See if command starts with
+ "qSymbol" (no trailing "s") and return the empty string.
+
+2008-01-24 Greg Clayton (gclayton@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): See if command starts with
+ "qSymbols" and return the empty string.
+
+2008-01-24 Greg Clayton (gclayton@apple.com)
+
+ * DNBError.h (DNBError::DumpIfError): Removed prototype.
+ * DNBError.cpp (DNBError::DumpIfError): Removed function.
+ (DNBError::LogThreadedIfError): Output error as hex.
+ * MachException.cpp (MachException::Message::Receive): Don't use
+ DNBError::DumpIfError, now use DNBError::LogThreadedIfError.
+ * MachProcess.cpp (MachProcess::StartExceptionThread): Ditto.
+ (MachProcess::Suspend): Ditto.
+ (MachProcess::SBForkChildForPTraceDebugging): Ditto.
+ * MachVMMemory.cpp (MachVMMemory::Read): Cleaned up logging
+ calls.
+ (MachVMMemory::Write): Ditto.
+ (MachVMMemory::WriteRegion): Added logging.
+ * RNBContenxt.cpp (display_thread_info): Removed function.
+ * RNBRemote.cpp (RNBRemote::GetPacket): Commented out stderr
+ messages to avoid SpringBoard from killing us.
+ (RNBRemote::HandlePacket_p): Ditto.
+ (RNBRemote::HandlePacket_P): Ditto.
+ (RNBRemote::HandlePacket_c): Ditto.
+ (RNBRemote::HandlePacket_A): Removed code that was already
+ * RNBSocket.cpp (RNBSocket::Listen): Commented out stdout
+ messages to avoid SpringBoard from killing us.
+ (RNBSocket::ConnectToService): Ditto.
+
+2008-01-24 Jim Ingham <jingham@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): Reply "" to qSymbols
+ and qOffsets.
+
+2008-01-23 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: m_noack_mode to RNBRemote class.
+ * RNBRemote.cpp: Change #ifdef NO_ACKS code blocks
+ to use m_noack_mode instance variable.
+ (RNBRemote::HandlePacket_Q): New function to handle
+ QStartNoAckMode packet and set m_noack_mode appropriately.
+ * test-remotenub.cpp: Remove NO_ACKS ifdefs.
+
+2008-01-22 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Recognize
+ QStartNoAckMode as an unsupported remote protocol request.
+ * RNBRemote.h: Add start_noack_mode enum entry.
+
+2008-01-22 Greg Clayton (gclayton@apple.com)
+
+ * DNBLog.h: Removed C++ namespace for DNBLog (changed all DNBLog::
+ to DNBLog) so C99 var arg macros can be used to completely disable
+ all logging and any functions that may be called when making the
+ variable arguments.
+ * DNBLog.cpp: Ditto.
+ * DNB.cpp: Ditto.
+ * DNBBreakpoint.cpp: Ditto.
+ * DNBError.cpp: Ditto.
+ * MacOSX/MachDYLD.cpp: Ditto.
+ * MacOSX/MachException.cpp: Ditto.
+ * MacOSX/MachProcess.cpp: Ditto.
+ * MacOSX/MachThread.cpp: Ditto.
+ * MacOSX/MachThreadList.cpp: Ditto.
+ * MacOSX/MachVMMemory.cpp: Ditto.
+ * MacOSX/MachVMRegion.cpp: Ditto.
+ * MacOSX/arm/DNBArchImpl.cpp: Ditto.
+ * MacOSX/ppc/DNBArchImpl.cpp: Ditto.
+ * PThreadEvent.cpp: Ditto.
+ * RNBContext.cpp: Ditto.
+ * RNBRemote.cpp: Ditto.
+ * RNBSocket.cpp: Ditto.
+ * test-remotenub.cpp: Ditto.
+
+2008-01-21 Jason Molenda (jmolenda@apple.com)
+
+ * test-remotenub.cpp: Add NO_SPRINGBOARD for turning off SpringBoard
+ dependency ala NO_ACKS.
+
+2008-01-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBSocket.h (RNBSocket::RNBSocket): Take either a port # or
+ an already-opened socket, with a boolean to indicate which it is.
+ * RNBRemote.cpp (RNBRemote::RNBRemote): Ditto.
+ * RNBRemote.h: Prototype update.
+ * test-remotenub.cpp: Include lockdown.h. Take --lockdown command
+ line arg, get the socket from liblockdown.dylib instead of opening
+ our own socket if it is specified. --lockdown indicates that
+ the program name/args will be provided via remote protocol instead
+ of on the command line.
+
+2008-01-17 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp: Add NO_ACKS #ifdefs around code that computes
+ the checksums and sends/expects the gdb remote protocol ACK packets.
+ If NO_ACKS is defined, debugserver will not send or expect acks.
+ * test-remotenub.cpp (main): Print a different version string
+ if NO_ACKS is defined.
+
+2008-01-16 Greg Clayton (gclayton@apple.com)
+
+ * PThreadEvent.cpp: Added this pointer to all logging calls.
+
+2008-01-16 Greg Clayton (gclayton@apple.com)
+
+ * RNBSocket.cpp (RNBSocket::Connect()): Use TCP so we can try the
+ TCP_NODELAY socket option.
+ (RNBSocket::SetSocketOption()): New function.
+ * RNBSocket.h (RNBSocket::SetSocketOption()): New class function.
+
+2008-01-14 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): When printing
+ registers, skip over gdb regs which don't map to DNB regs.
+
+2008-01-14 Jim Ingham <jingham@apple.com>
+
+ * ChangeLog - created.
+ * RBNContext.h: Added m_arg_vec and accessors.
+ * RNBContext.cpp (SetProcessID): New function.
+ * RBNRemote.h: Added packet type to HandlePacket & HandleAsyncPacket
+ * RNBRemote.cpp (HandlePacket, HandleAsyncPacket): Return type.
+ (HandlePacket_A): Fix a few bugs.
+ (HandlePacket_H): Return OK if target is not yet running.
+ (HandlePacket_q): Return PID of 0 if target is not yet running.
+ * testremotenub.cpp (RNBRunLoopGetArgsFromRemote): Implement.
+ (RNBRunLoopLaunchInferior): Fetch arguments from context.
+ (main) Store arguments in context, call RNBRunLoopGetArgsFromRemote
+ if appropriate.
diff --git a/tools/debugserver/source/DNB.cpp b/tools/debugserver/source/DNB.cpp
new file mode 100644
index 000000000000..03c85df441de
--- /dev/null
+++ b/tools/debugserver/source/DNB.cpp
@@ -0,0 +1,1979 @@
+//===-- DNB.cpp -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/23/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNB.h"
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <map>
+#include <vector>
+#include <libproc.h>
+
+#if defined (__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#define TRY_KQUEUE 1
+
+#ifdef TRY_KQUEUE
+ #include <sys/event.h>
+ #include <sys/time.h>
+ #ifdef NOTE_EXIT_DETAIL
+ #define USE_KQUEUE
+ #endif
+#endif
+
+#include "MacOSX/MachProcess.h"
+#include "MacOSX/MachTask.h"
+#include "MacOSX/Genealogy.h"
+#include "MacOSX/ThreadInfo.h"
+#include "CFString.h"
+#include "DNBLog.h"
+#include "DNBDataRef.h"
+#include "DNBThreadResumeActions.h"
+#include "DNBTimer.h"
+#include "CFBundle.h"
+
+
+typedef std::shared_ptr<MachProcess> MachProcessSP;
+typedef std::map<nub_process_t, MachProcessSP> ProcessMap;
+typedef ProcessMap::iterator ProcessMapIter;
+typedef ProcessMap::const_iterator ProcessMapConstIter;
+
+size_t GetAllInfos (std::vector<struct kinfo_proc>& proc_infos);
+static size_t GetAllInfosMatchingName (const char *process_name, std::vector<struct kinfo_proc>& matching_proc_infos);
+
+//----------------------------------------------------------------------
+// A Thread safe singleton to get a process map pointer.
+//
+// Returns a pointer to the existing process map, or a pointer to a
+// newly created process map if CAN_CREATE is non-zero.
+//----------------------------------------------------------------------
+static ProcessMap*
+GetProcessMap(bool can_create)
+{
+ static ProcessMap* g_process_map_ptr = NULL;
+
+ if (can_create && g_process_map_ptr == NULL)
+ {
+ static pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER;
+ PTHREAD_MUTEX_LOCKER (locker, &g_process_map_mutex);
+ if (g_process_map_ptr == NULL)
+ g_process_map_ptr = new ProcessMap;
+ }
+ return g_process_map_ptr;
+}
+
+//----------------------------------------------------------------------
+// Add PID to the shared process pointer map.
+//
+// Return non-zero value if we succeed in adding the process to the map.
+// The only time this should fail is if we run out of memory and can't
+// allocate a ProcessMap.
+//----------------------------------------------------------------------
+static nub_bool_t
+AddProcessToMap (nub_process_t pid, MachProcessSP& procSP)
+{
+ ProcessMap* process_map = GetProcessMap(true);
+ if (process_map)
+ {
+ process_map->insert(std::make_pair(pid, procSP));
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------
+// Remove the shared pointer for PID from the process map.
+//
+// Returns the number of items removed from the process map.
+//----------------------------------------------------------------------
+//static size_t
+//RemoveProcessFromMap (nub_process_t pid)
+//{
+// ProcessMap* process_map = GetProcessMap(false);
+// if (process_map)
+// {
+// return process_map->erase(pid);
+// }
+// return 0;
+//}
+
+//----------------------------------------------------------------------
+// Get the shared pointer for PID from the existing process map.
+//
+// Returns true if we successfully find a shared pointer to a
+// MachProcess object.
+//----------------------------------------------------------------------
+static nub_bool_t
+GetProcessSP (nub_process_t pid, MachProcessSP& procSP)
+{
+ ProcessMap* process_map = GetProcessMap(false);
+ if (process_map != NULL)
+ {
+ ProcessMapIter pos = process_map->find(pid);
+ if (pos != process_map->end())
+ {
+ procSP = pos->second;
+ return true;
+ }
+ }
+ procSP.reset();
+ return false;
+}
+
+#ifdef USE_KQUEUE
+void *
+kqueue_thread (void *arg)
+{
+ int kq_id = (int) (intptr_t) arg;
+
+#if defined (__APPLE__)
+ pthread_setname_np ("kqueue thread");
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0)
+ {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ struct kevent death_event;
+ while (1)
+ {
+ int n_events = kevent (kq_id, NULL, 0, &death_event, 1, NULL);
+ if (n_events == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ {
+ DNBLogError ("kqueue failed with error: (%d): %s", errno, strerror(errno));
+ return NULL;
+ }
+ }
+ else if (death_event.flags & EV_ERROR)
+ {
+ int error_no = static_cast<int>(death_event.data);
+ const char *error_str = strerror(error_no);
+ if (error_str == NULL)
+ error_str = "Unknown error";
+ DNBLogError ("Failed to initialize kqueue event: (%d): %s", error_no, error_str );
+ return NULL;
+ }
+ else
+ {
+ int status;
+ const pid_t pid = (pid_t)death_event.ident;
+ const pid_t child_pid = waitpid (pid, &status, 0);
+
+
+ bool exited = false;
+ int signal = 0;
+ int exit_status = 0;
+ if (WIFSTOPPED(status))
+ {
+ signal = WSTOPSIG(status);
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> STOPPED (signal = %i)", child_pid, signal);
+ }
+ else if (WIFEXITED(status))
+ {
+ exit_status = WEXITSTATUS(status);
+ exited = true;
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> EXITED (status = %i)", child_pid, exit_status);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ signal = WTERMSIG(status);
+ if (child_pid == abs(pid))
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> SIGNALED and EXITED (signal = %i)", child_pid, signal);
+ char exit_info[64];
+ ::snprintf (exit_info, sizeof(exit_info), "Terminated due to signal %i", signal);
+ DNBProcessSetExitInfo (child_pid, exit_info);
+ exited = true;
+ exit_status = INT8_MAX;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> SIGNALED (signal = %i)", child_pid, signal);
+ }
+ }
+
+ if (exited)
+ {
+ if (death_event.data & NOTE_EXIT_MEMORY)
+ DNBProcessSetExitInfo (child_pid, "Terminated due to memory issue");
+ else if (death_event.data & NOTE_EXIT_DECRYPTFAIL)
+ DNBProcessSetExitInfo (child_pid, "Terminated due to decrypt failure");
+ else if (death_event.data & NOTE_EXIT_CSERROR)
+ DNBProcessSetExitInfo (child_pid, "Terminated due to code signing error");
+
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): setting exit status for pid = %i to %i", child_pid, exit_status);
+ DNBProcessSetExitStatus (child_pid, status);
+ return NULL;
+ }
+ }
+ }
+}
+
+static bool
+spawn_kqueue_thread (pid_t pid)
+{
+ pthread_t thread;
+ int kq_id;
+
+ kq_id = kqueue();
+ if (kq_id == -1)
+ {
+ DNBLogError ("Could not get kqueue for pid = %i.", pid);
+ return false;
+ }
+
+ struct kevent reg_event;
+
+ EV_SET(&reg_event, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT|NOTE_EXITSTATUS|NOTE_EXIT_DETAIL, 0, NULL);
+ // Register the event:
+ int result = kevent (kq_id, &reg_event, 1, NULL, 0, NULL);
+ if (result != 0)
+ {
+ DNBLogError ("Failed to register kqueue NOTE_EXIT event for pid %i, error: %d.", pid, result);
+ return false;
+ }
+
+ int ret = ::pthread_create (&thread, NULL, kqueue_thread, (void *)(intptr_t)kq_id);
+
+ // pthread_create returns 0 if successful
+ if (ret == 0)
+ {
+ ::pthread_detach (thread);
+ return true;
+ }
+ return false;
+}
+#endif // #if USE_KQUEUE
+
+static void *
+waitpid_thread (void *arg)
+{
+ const pid_t pid = (pid_t)(intptr_t)arg;
+ int status;
+
+#if defined (__APPLE__)
+ pthread_setname_np ("waitpid thread");
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0)
+ {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ while (1)
+ {
+ pid_t child_pid = waitpid(pid, &status, 0);
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): waitpid (pid = %i, &status, 0) => %i, status = %i, errno = %i", pid, child_pid, status, errno);
+
+ if (child_pid < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ else
+ {
+ if (WIFSTOPPED(status))
+ {
+ continue;
+ }
+ else// if (WIFEXITED(status) || WIFSIGNALED(status))
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): setting exit status for pid = %i to %i", child_pid, status);
+ DNBProcessSetExitStatus (child_pid, status);
+ return NULL;
+ }
+ }
+ }
+
+ // We should never exit as long as our child process is alive, so if we
+ // do something else went wrong and we should exit...
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): main loop exited, setting exit status to an invalid value (-1) for pid %i", pid);
+ DNBProcessSetExitStatus (pid, -1);
+ return NULL;
+}
+static bool
+spawn_waitpid_thread (pid_t pid)
+{
+#ifdef USE_KQUEUE
+ bool success = spawn_kqueue_thread (pid);
+ if (success)
+ return true;
+#endif
+
+ pthread_t thread;
+ int ret = ::pthread_create (&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);
+ // pthread_create returns 0 if successful
+ if (ret == 0)
+ {
+ ::pthread_detach (thread);
+ return true;
+ }
+ return false;
+}
+
+nub_process_t
+DNBProcessLaunch (const char *path,
+ char const *argv[],
+ const char *envp[],
+ const char *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ bool no_stdio,
+ nub_launch_flavor_t launch_flavor,
+ int disable_aslr,
+ const char *event_data,
+ char *err_str,
+ size_t err_len)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv = %p, envp = %p, working_dir=%s, stdin=%s, stdout=%s, stderr=%s, no-stdio=%i, launch_flavor = %u, disable_aslr = %d, err = %p, err_len = %llu) called...",
+ __FUNCTION__,
+ path,
+ argv,
+ envp,
+ working_directory,
+ stdin_path,
+ stdout_path,
+ stderr_path,
+ no_stdio,
+ launch_flavor,
+ disable_aslr,
+ err_str,
+ (uint64_t)err_len);
+
+ if (err_str && err_len > 0)
+ err_str[0] = '\0';
+ struct stat path_stat;
+ if (::stat(path, &path_stat) == -1)
+ {
+ char stat_error[256];
+ ::strerror_r (errno, stat_error, sizeof(stat_error));
+ snprintf(err_str, err_len, "%s (%s)", stat_error, path);
+ return INVALID_NUB_PROCESS;
+ }
+
+ MachProcessSP processSP (new MachProcess);
+ if (processSP.get())
+ {
+ DNBError launch_err;
+ pid_t pid = processSP->LaunchForDebug (path,
+ argv,
+ envp,
+ working_directory,
+ stdin_path,
+ stdout_path,
+ stderr_path,
+ no_stdio,
+ launch_flavor,
+ disable_aslr,
+ event_data,
+ launch_err);
+ if (err_str)
+ {
+ *err_str = '\0';
+ if (launch_err.Fail())
+ {
+ const char *launch_err_str = launch_err.AsString();
+ if (launch_err_str)
+ {
+ strncpy(err_str, launch_err_str, err_len-1);
+ err_str[err_len-1] = '\0'; // Make sure the error string is terminated
+ }
+ }
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid);
+
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ // Spawn a thread to reap our child inferior process...
+ spawn_waitpid_thread (pid);
+
+ if (processSP->Task().TaskPortForProcessID (launch_err) == TASK_NULL)
+ {
+ // We failed to get the task for our process ID which is bad.
+ // Kill our process otherwise it will be stopped at the entry
+ // point and get reparented to someone else and never go away.
+ DNBLog ("Could not get task port for process, sending SIGKILL and exiting.");
+ kill (SIGKILL, pid);
+
+ if (err_str && err_len > 0)
+ {
+ if (launch_err.AsString())
+ {
+ ::snprintf (err_str, err_len, "failed to get the task for process %i (%s)", pid, launch_err.AsString());
+ }
+ else
+ {
+ ::snprintf (err_str, err_len, "failed to get the task for process %i", pid);
+ }
+ }
+ }
+ else
+ {
+ bool res = AddProcessToMap(pid, processSP);
+ UNUSED_IF_ASSERT_DISABLED(res);
+ assert(res && "Couldn't add process to map!");
+ return pid;
+ }
+ }
+ }
+ return INVALID_NUB_PROCESS;
+}
+
+// If there is one process with a given name, return the pid for that process.
+nub_process_t
+DNBProcessGetPIDByName (const char *name)
+{
+ std::vector<struct kinfo_proc> matching_proc_infos;
+ size_t num_matching_proc_infos = GetAllInfosMatchingName(name, matching_proc_infos);
+ if (num_matching_proc_infos == 1)
+ {
+ return matching_proc_infos[0].kp_proc.p_pid;
+ }
+ return INVALID_NUB_PROCESS;
+}
+
+nub_process_t
+DNBProcessAttachByName (const char *name, struct timespec *timeout, char *err_str, size_t err_len)
+{
+ if (err_str && err_len > 0)
+ err_str[0] = '\0';
+ std::vector<struct kinfo_proc> matching_proc_infos;
+ size_t num_matching_proc_infos = GetAllInfosMatchingName(name, matching_proc_infos);
+ if (num_matching_proc_infos == 0)
+ {
+ DNBLogError ("error: no processes match '%s'\n", name);
+ return INVALID_NUB_PROCESS;
+ }
+ else if (num_matching_proc_infos > 1)
+ {
+ DNBLogError ("error: %llu processes match '%s':\n", (uint64_t)num_matching_proc_infos, name);
+ size_t i;
+ for (i=0; i<num_matching_proc_infos; ++i)
+ DNBLogError ("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid, matching_proc_infos[i].kp_proc.p_comm);
+ return INVALID_NUB_PROCESS;
+ }
+
+ return DNBProcessAttach (matching_proc_infos[0].kp_proc.p_pid, timeout, err_str, err_len);
+}
+
+nub_process_t
+DNBProcessAttach (nub_process_t attach_pid, struct timespec *timeout, char *err_str, size_t err_len)
+{
+ if (err_str && err_len > 0)
+ err_str[0] = '\0';
+
+ pid_t pid = INVALID_NUB_PROCESS;
+ MachProcessSP processSP(new MachProcess);
+ if (processSP.get())
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...", attach_pid);
+ pid = processSP->AttachForDebug (attach_pid, err_str, err_len);
+
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ bool res = AddProcessToMap(pid, processSP);
+ UNUSED_IF_ASSERT_DISABLED(res);
+ assert(res && "Couldn't add process to map!");
+ spawn_waitpid_thread(pid);
+ }
+ }
+
+ while (pid != INVALID_NUB_PROCESS)
+ {
+ // Wait for process to start up and hit entry point
+ DNBLogThreadedIf (LOG_PROCESS,
+ "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...",
+ __FUNCTION__,
+ pid);
+ nub_event_t set_events = DNBProcessWaitForEvents (pid,
+ eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged,
+ true,
+ timeout);
+
+ DNBLogThreadedIf (LOG_PROCESS,
+ "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x",
+ __FUNCTION__,
+ pid,
+ set_events);
+
+ if (set_events == 0)
+ {
+ if (err_str && err_len > 0)
+ snprintf(err_str, err_len, "operation timed out");
+ pid = INVALID_NUB_PROCESS;
+ }
+ else
+ {
+ if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
+ {
+ nub_state_t pid_state = DNBProcessGetState (pid);
+ DNBLogThreadedIf (LOG_PROCESS, "%s process %4.4x state changed (eEventProcessStateChanged): %s",
+ __FUNCTION__, pid, DNBStateAsString(pid_state));
+
+ switch (pid_state)
+ {
+ default:
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateSuspended:
+ break; // Ignore
+
+ case eStateRunning:
+ case eStateStepping:
+ // Still waiting to stop at entry point...
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ return pid;
+
+ case eStateDetached:
+ case eStateExited:
+ if (err_str && err_len > 0)
+ snprintf(err_str, err_len, "process exited");
+ return INVALID_NUB_PROCESS;
+ }
+ }
+
+ DNBProcessResetEvents(pid, set_events);
+ }
+ }
+
+ return INVALID_NUB_PROCESS;
+}
+
+size_t
+GetAllInfos (std::vector<struct kinfo_proc>& proc_infos)
+{
+ size_t size = 0;
+ int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
+ u_int namelen = sizeof(name)/sizeof(int);
+ int err;
+
+ // Try to find out how many processes are around so we can
+ // size the buffer appropriately. sysctl's man page specifically suggests
+ // this approach, and says it returns a bit larger size than needed to
+ // handle any new processes created between then and now.
+
+ err = ::sysctl (name, namelen, NULL, &size, NULL, 0);
+
+ if ((err < 0) && (err != ENOMEM))
+ {
+ proc_infos.clear();
+ perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");
+ return 0;
+ }
+
+
+ // Increase the size of the buffer by a few processes in case more have
+ // been spawned
+ proc_infos.resize (size / sizeof(struct kinfo_proc));
+ size = proc_infos.size() * sizeof(struct kinfo_proc); // Make sure we don't exceed our resize...
+ err = ::sysctl (name, namelen, &proc_infos[0], &size, NULL, 0);
+ if (err < 0)
+ {
+ proc_infos.clear();
+ return 0;
+ }
+
+ // Trim down our array to fit what we actually got back
+ proc_infos.resize(size / sizeof(struct kinfo_proc));
+ return proc_infos.size();
+}
+
+static size_t
+GetAllInfosMatchingName(const char *full_process_name, std::vector<struct kinfo_proc>& matching_proc_infos)
+{
+
+ matching_proc_infos.clear();
+ if (full_process_name && full_process_name[0])
+ {
+ // We only get the process name, not the full path, from the proc_info. So just take the
+ // base name of the process name...
+ const char *process_name;
+ process_name = strrchr (full_process_name, '/');
+ if (process_name == NULL)
+ process_name = full_process_name;
+ else
+ process_name++;
+
+ const size_t process_name_len = strlen(process_name);
+ std::vector<struct kinfo_proc> proc_infos;
+ const size_t num_proc_infos = GetAllInfos(proc_infos);
+ if (num_proc_infos > 0)
+ {
+ uint32_t i;
+ for (i=0; i<num_proc_infos; i++)
+ {
+ // Skip zombie processes and processes with unset status
+ if (proc_infos[i].kp_proc.p_stat == 0 || proc_infos[i].kp_proc.p_stat == SZOMB)
+ continue;
+
+ // Check for process by name. We only check the first MAXCOMLEN
+ // chars as that is all that kp_proc.p_comm holds.
+
+ if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm, MAXCOMLEN) == 0)
+ {
+ if (process_name_len > MAXCOMLEN)
+ {
+ // We found a matching process name whose first MAXCOMLEN
+ // characters match, but there is more to the name than
+ // this. We need to get the full process name. Use proc_pidpath, which will get
+ // us the full path to the executed process.
+
+ char proc_path_buf[PATH_MAX];
+
+ int return_val = proc_pidpath (proc_infos[i].kp_proc.p_pid, proc_path_buf, PATH_MAX);
+ if (return_val > 0)
+ {
+ // Okay, now search backwards from that to see if there is a
+ // slash in the name. Note, even though we got all the args we don't care
+ // because the list data is just a bunch of concatenated null terminated strings
+ // so strrchr will start from the end of argv0.
+
+ const char *argv_basename = strrchr(proc_path_buf, '/');
+ if (argv_basename)
+ {
+ // Skip the '/'
+ ++argv_basename;
+ }
+ else
+ {
+ // We didn't find a directory delimiter in the process argv[0], just use what was in there
+ argv_basename = proc_path_buf;
+ }
+
+ if (argv_basename)
+ {
+ if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0)
+ {
+ matching_proc_infos.push_back(proc_infos[i]);
+ }
+ }
+ }
+ }
+ else
+ {
+ // We found a matching process, add it to our list
+ matching_proc_infos.push_back(proc_infos[i]);
+ }
+ }
+ }
+ }
+ }
+ // return the newly added matches.
+ return matching_proc_infos.size();
+}
+
+nub_process_t
+DNBProcessAttachWait (const char *waitfor_process_name,
+ nub_launch_flavor_t launch_flavor,
+ bool ignore_existing,
+ struct timespec *timeout_abstime,
+ useconds_t waitfor_interval,
+ char *err_str,
+ size_t err_len,
+ DNBShouldCancelCallback should_cancel_callback,
+ void *callback_data)
+{
+ DNBError prepare_error;
+ std::vector<struct kinfo_proc> exclude_proc_infos;
+ size_t num_exclude_proc_infos;
+
+ // If the PrepareForAttach returns a valid token, use MachProcess to check
+ // for the process, otherwise scan the process table.
+
+ const void *attach_token = MachProcess::PrepareForAttach (waitfor_process_name, launch_flavor, true, prepare_error);
+
+ if (prepare_error.Fail())
+ {
+ DNBLogError ("Error in PrepareForAttach: %s", prepare_error.AsString());
+ return INVALID_NUB_PROCESS;
+ }
+
+ if (attach_token == NULL)
+ {
+ if (ignore_existing)
+ num_exclude_proc_infos = GetAllInfosMatchingName (waitfor_process_name, exclude_proc_infos);
+ else
+ num_exclude_proc_infos = 0;
+ }
+
+ DNBLogThreadedIf (LOG_PROCESS, "Waiting for '%s' to appear...\n", waitfor_process_name);
+
+ // Loop and try to find the process by name
+ nub_process_t waitfor_pid = INVALID_NUB_PROCESS;
+
+ while (waitfor_pid == INVALID_NUB_PROCESS)
+ {
+ if (attach_token != NULL)
+ {
+ nub_process_t pid;
+ pid = MachProcess::CheckForProcess(attach_token, launch_flavor);
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ waitfor_pid = pid;
+ break;
+ }
+ }
+ else
+ {
+
+ // Get the current process list, and check for matches that
+ // aren't in our original list. If anyone wants to attach
+ // to an existing process by name, they should do it with
+ // --attach=PROCNAME. Else we will wait for the first matching
+ // process that wasn't in our exclusion list.
+ std::vector<struct kinfo_proc> proc_infos;
+ const size_t num_proc_infos = GetAllInfosMatchingName (waitfor_process_name, proc_infos);
+ for (size_t i=0; i<num_proc_infos; i++)
+ {
+ nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;
+ for (size_t j=0; j<num_exclude_proc_infos; j++)
+ {
+ if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid)
+ {
+ // This process was in our exclusion list, don't use it.
+ curr_pid = INVALID_NUB_PROCESS;
+ break;
+ }
+ }
+
+ // If we didn't find CURR_PID in our exclusion list, then use it.
+ if (curr_pid != INVALID_NUB_PROCESS)
+ {
+ // We found our process!
+ waitfor_pid = curr_pid;
+ break;
+ }
+ }
+ }
+
+ // If we haven't found our process yet, check for a timeout
+ // and then sleep for a bit until we poll again.
+ if (waitfor_pid == INVALID_NUB_PROCESS)
+ {
+ if (timeout_abstime != NULL)
+ {
+ // Check to see if we have a waitfor-duration option that
+ // has timed out?
+ if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime))
+ {
+ if (err_str && err_len > 0)
+ snprintf(err_str, err_len, "operation timed out");
+ DNBLogError ("error: waiting for process '%s' timed out.\n", waitfor_process_name);
+ return INVALID_NUB_PROCESS;
+ }
+ }
+
+ // Call the should cancel callback as well...
+
+ if (should_cancel_callback != NULL
+ && should_cancel_callback (callback_data))
+ {
+ DNBLogThreadedIf (LOG_PROCESS, "DNBProcessAttachWait cancelled by should_cancel callback.");
+ waitfor_pid = INVALID_NUB_PROCESS;
+ break;
+ }
+
+ ::usleep (waitfor_interval); // Sleep for WAITFOR_INTERVAL, then poll again
+ }
+ }
+
+ if (waitfor_pid != INVALID_NUB_PROCESS)
+ {
+ DNBLogThreadedIf (LOG_PROCESS, "Attaching to %s with pid %i...\n", waitfor_process_name, waitfor_pid);
+ waitfor_pid = DNBProcessAttach (waitfor_pid, timeout_abstime, err_str, err_len);
+ }
+
+ bool success = waitfor_pid != INVALID_NUB_PROCESS;
+ MachProcess::CleanupAfterAttach (attach_token, launch_flavor, success, prepare_error);
+
+ return waitfor_pid;
+}
+
+nub_bool_t
+DNBProcessDetach (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ const bool remove = true;
+ DNBLogThreaded("Disabling breakpoints and watchpoints, and detaching from %d.", pid);
+ procSP->DisableAllBreakpoints(remove);
+ procSP->DisableAllWatchpoints (remove);
+ return procSP->Detach();
+ }
+ return false;
+}
+
+nub_bool_t
+DNBProcessKill (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->Kill ();
+ }
+ return false;
+}
+
+nub_bool_t
+DNBProcessSignal (nub_process_t pid, int signal)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->Signal (signal);
+ }
+ return false;
+}
+
+
+nub_bool_t
+DNBProcessInterrupt(nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->Interrupt();
+ return false;
+}
+
+nub_bool_t
+DNBProcessSendEvent (nub_process_t pid, const char *event)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ // FIXME: Do something with the error...
+ DNBError send_error;
+ return procSP->SendEvent (event, send_error);
+ }
+ return false;
+}
+
+
+nub_bool_t
+DNBProcessIsAlive (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return MachTask::IsValid (procSP->Task().TaskPort());
+ }
+ return eStateInvalid;
+}
+
+//----------------------------------------------------------------------
+// Process and Thread state information
+//----------------------------------------------------------------------
+nub_state_t
+DNBProcessGetState (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetState();
+ }
+ return eStateInvalid;
+}
+
+//----------------------------------------------------------------------
+// Process and Thread state information
+//----------------------------------------------------------------------
+nub_bool_t
+DNBProcessGetExitStatus (nub_process_t pid, int* status)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetExitStatus(status);
+ }
+ return false;
+}
+
+nub_bool_t
+DNBProcessSetExitStatus (nub_process_t pid, int status)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ procSP->SetExitStatus(status);
+ return true;
+ }
+ return false;
+}
+
+const char *
+DNBProcessGetExitInfo (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetExitInfo();
+ }
+ return NULL;
+}
+
+nub_bool_t
+DNBProcessSetExitInfo (nub_process_t pid, const char *info)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ procSP->SetExitInfo(info);
+ return true;
+ }
+ return false;
+}
+
+const char *
+DNBThreadGetName (nub_process_t pid, nub_thread_t tid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->ThreadGetName(tid);
+ return NULL;
+}
+
+
+nub_bool_t
+DNBThreadGetIdentifierInfo (nub_process_t pid, nub_thread_t tid, thread_identifier_info_data_t *ident_info)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);
+ return false;
+}
+
+nub_state_t
+DNBThreadGetState (nub_process_t pid, nub_thread_t tid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->ThreadGetState(tid);
+ }
+ return eStateInvalid;
+}
+
+const char *
+DNBStateAsString(nub_state_t state)
+{
+ switch (state)
+ {
+ case eStateInvalid: return "Invalid";
+ case eStateUnloaded: return "Unloaded";
+ case eStateAttaching: return "Attaching";
+ case eStateLaunching: return "Launching";
+ case eStateStopped: return "Stopped";
+ case eStateRunning: return "Running";
+ case eStateStepping: return "Stepping";
+ case eStateCrashed: return "Crashed";
+ case eStateDetached: return "Detached";
+ case eStateExited: return "Exited";
+ case eStateSuspended: return "Suspended";
+ }
+ return "nub_state_t ???";
+}
+
+Genealogy::ThreadActivitySP
+DNBGetGenealogyInfoForThread (nub_process_t pid, nub_thread_t tid, bool &timed_out)
+{
+ Genealogy::ThreadActivitySP thread_activity_sp;
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ thread_activity_sp = procSP->GetGenealogyInfoForThread (tid, timed_out);
+ return thread_activity_sp;
+}
+
+Genealogy::ProcessExecutableInfoSP
+DNBGetGenealogyImageInfo (nub_process_t pid, size_t idx)
+{
+ Genealogy::ProcessExecutableInfoSP image_info_sp;
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ image_info_sp = procSP->GetGenealogyImageInfo (idx);
+ }
+ return image_info_sp;
+}
+
+ThreadInfo::QoS
+DNBGetRequestedQoSForThread (nub_process_t pid, nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetRequestedQoS (tid, tsd, dti_qos_class_index);
+ }
+ return ThreadInfo::QoS();
+}
+
+nub_addr_t
+DNBGetPThreadT (nub_process_t pid, nub_thread_t tid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetPThreadT (tid);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+DNBGetDispatchQueueT (nub_process_t pid, nub_thread_t tid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetDispatchQueueT (tid);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+JSONGenerator::ObjectSP
+DNBGetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->GetLoadedDynamicLibrariesInfos (pid, image_list_address, image_count);
+ }
+ return JSONGenerator::ObjectSP();
+}
+
+
+
+const char *
+DNBProcessGetExecutablePath (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->Path();
+ }
+ return NULL;
+}
+
+nub_size_t
+DNBProcessGetArgumentCount (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->ArgumentCount();
+ }
+ return 0;
+}
+
+const char *
+DNBProcessGetArgumentAtIndex (nub_process_t pid, nub_size_t idx)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->ArgumentAtIndex (idx);
+ }
+ return NULL;
+}
+
+
+//----------------------------------------------------------------------
+// Execution control
+//----------------------------------------------------------------------
+nub_bool_t
+DNBProcessResume (nub_process_t pid, const DNBThreadResumeAction *actions, size_t num_actions)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ DNBThreadResumeActions thread_actions (actions, num_actions);
+
+ // Below we add a default thread plan just in case one wasn't
+ // provided so all threads always know what they were supposed to do
+ if (thread_actions.IsEmpty())
+ {
+ // No thread plans were given, so the default it to run all threads
+ thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0);
+ }
+ else
+ {
+ // Some thread plans were given which means anything that wasn't
+ // specified should remain stopped.
+ thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0);
+ }
+ return procSP->Resume (thread_actions);
+ }
+ return false;
+}
+
+nub_bool_t
+DNBProcessHalt (nub_process_t pid)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->Signal (SIGSTOP);
+ return false;
+}
+//
+//nub_bool_t
+//DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)
+//{
+// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)", __FUNCTION__, pid, tid, (uint32_t)step);
+// MachProcessSP procSP;
+// if (GetProcessSP (pid, procSP))
+// {
+// return procSP->Resume(tid, step, 0);
+// }
+// return false;
+//}
+//
+//nub_bool_t
+//DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t step, int signal)
+//{
+// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u, signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);
+// MachProcessSP procSP;
+// if (GetProcessSP (pid, procSP))
+// {
+// return procSP->Resume(tid, step, signal);
+// }
+// return false;
+//}
+
+nub_event_t
+DNBProcessWaitForEvents (nub_process_t pid, nub_event_t event_mask, bool wait_for_set, struct timespec* timeout)
+{
+ nub_event_t result = 0;
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ if (wait_for_set)
+ result = procSP->Events().WaitForSetEvents(event_mask, timeout);
+ else
+ result = procSP->Events().WaitForEventsToReset(event_mask, timeout);
+ }
+ return result;
+}
+
+void
+DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ procSP->Events().ResetEvents(event_mask);
+}
+
+// Breakpoints
+nub_bool_t
+DNBBreakpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->CreateBreakpoint(addr, size, hardware) != NULL;
+ return false;
+}
+
+nub_bool_t
+DNBBreakpointClear (nub_process_t pid, nub_addr_t addr)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->DisableBreakpoint(addr, true);
+ return false; // Failed
+}
+
+
+//----------------------------------------------------------------------
+// Watchpoints
+//----------------------------------------------------------------------
+nub_bool_t
+DNBWatchpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, uint32_t watch_flags, nub_bool_t hardware)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->CreateWatchpoint(addr, size, watch_flags, hardware) != NULL;
+ return false;
+}
+
+nub_bool_t
+DNBWatchpointClear (nub_process_t pid, nub_addr_t addr)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->DisableWatchpoint(addr, true);
+ return false; // Failed
+}
+
+//----------------------------------------------------------------------
+// Return the number of supported hardware watchpoints.
+//----------------------------------------------------------------------
+uint32_t
+DNBWatchpointGetNumSupportedHWP (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetNumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Read memory in the address space of process PID. This call will take
+// care of setting and restoring permissions and breaking up the memory
+// read into multiple chunks as required.
+//
+// RETURNS: number of bytes actually read
+//----------------------------------------------------------------------
+nub_size_t
+DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void *buf)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->ReadMemory(addr, size, buf);
+ return 0;
+}
+
+uint64_t
+DNBProcessMemoryReadInteger (nub_process_t pid, nub_addr_t addr, nub_size_t integer_size, uint64_t fail_value)
+{
+ union Integers
+ {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ };
+
+ if (integer_size <= sizeof(uint64_t))
+ {
+ Integers ints;
+ if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size)
+ {
+ switch (integer_size)
+ {
+ case 1: return ints.u8;
+ case 2: return ints.u16;
+ case 3: return ints.u32 & 0xffffffu;
+ case 4: return ints.u32;
+ case 5: return ints.u32 & 0x000000ffffffffffull;
+ case 6: return ints.u32 & 0x0000ffffffffffffull;
+ case 7: return ints.u32 & 0x00ffffffffffffffull;
+ case 8: return ints.u64;
+ }
+ }
+ }
+ return fail_value;
+
+}
+
+nub_addr_t
+DNBProcessMemoryReadPointer (nub_process_t pid, nub_addr_t addr)
+{
+ cpu_type_t cputype = DNBProcessGetCPUType (pid);
+ if (cputype)
+ {
+ const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4;
+ return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0);
+ }
+ return 0;
+
+}
+
+std::string
+DNBProcessMemoryReadCString (nub_process_t pid, nub_addr_t addr)
+{
+ std::string cstr;
+ char buffer[256];
+ const nub_size_t max_buffer_cstr_length = sizeof(buffer)-1;
+ buffer[max_buffer_cstr_length] = '\0';
+ nub_size_t length = 0;
+ nub_addr_t curr_addr = addr;
+ do
+ {
+ nub_size_t bytes_read = DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer);
+ if (bytes_read == 0)
+ break;
+ length = strlen(buffer);
+ cstr.append(buffer, length);
+ curr_addr += length;
+ } while (length == max_buffer_cstr_length);
+ return cstr;
+}
+
+std::string
+DNBProcessMemoryReadCStringFixed (nub_process_t pid, nub_addr_t addr, nub_size_t fixed_length)
+{
+ std::string cstr;
+ char buffer[fixed_length+1];
+ buffer[fixed_length] = '\0';
+ nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer);
+ if (bytes_read > 0)
+ cstr.assign(buffer);
+ return cstr;
+}
+
+
+//----------------------------------------------------------------------
+// Write memory to the address space of process PID. This call will take
+// care of setting and restoring permissions and breaking up the memory
+// write into multiple chunks as required.
+//
+// RETURNS: number of bytes actually written
+//----------------------------------------------------------------------
+nub_size_t
+DNBProcessMemoryWrite (nub_process_t pid, nub_addr_t addr, nub_size_t size, const void *buf)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->WriteMemory(addr, size, buf);
+ return 0;
+}
+
+nub_addr_t
+DNBProcessMemoryAllocate (nub_process_t pid, nub_size_t size, uint32_t permissions)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->Task().AllocateMemory (size, permissions);
+ return 0;
+}
+
+nub_bool_t
+DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->Task().DeallocateMemory (addr);
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Find attributes of the memory region that contains ADDR for process PID,
+// if possible, and return a string describing those attributes.
+//
+// Returns 1 if we could find attributes for this region and OUTBUF can
+// be sent to the remote debugger.
+//
+// Returns 0 if we couldn't find the attributes for a region of memory at
+// that address and OUTBUF should not be sent.
+//
+// Returns -1 if this platform cannot look up information about memory regions
+// or if we do not yet have a valid launched process.
+//
+//----------------------------------------------------------------------
+int
+DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->Task().GetMemoryRegionInfo (addr, region_info);
+
+ return -1;
+}
+
+std::string
+DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->Task().GetProfileData(scanType);
+
+ return std::string("");
+}
+
+nub_bool_t
+DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);
+ return true;
+ }
+
+ return false;
+}
+
+//----------------------------------------------------------------------
+// Get the number of threads for the specified process.
+//----------------------------------------------------------------------
+nub_size_t
+DNBProcessGetNumThreads (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetNumThreads();
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Get the thread ID of the current thread.
+//----------------------------------------------------------------------
+nub_thread_t
+DNBProcessGetCurrentThread (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetCurrentThread();
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Get the mach port number of the current thread.
+//----------------------------------------------------------------------
+nub_thread_t
+DNBProcessGetCurrentThreadMachPort (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetCurrentThreadMachPort();
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Change the current thread.
+//----------------------------------------------------------------------
+nub_thread_t
+DNBProcessSetCurrentThread (nub_process_t pid, nub_thread_t tid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->SetCurrentThread (tid);
+ return INVALID_NUB_THREAD;
+}
+
+
+//----------------------------------------------------------------------
+// Dump a string describing a thread's stop reason to the specified file
+// handle
+//----------------------------------------------------------------------
+nub_bool_t
+DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, struct DNBThreadStopInfo *stop_info)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetThreadStoppedReason (tid, stop_info);
+ return false;
+}
+
+//----------------------------------------------------------------------
+// Return string description for the specified thread.
+//
+// RETURNS: NULL if the thread isn't valid, else a NULL terminated C
+// string from a static buffer that must be copied prior to subsequent
+// calls.
+//----------------------------------------------------------------------
+const char *
+DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetThreadInfo (tid);
+ return NULL;
+}
+
+//----------------------------------------------------------------------
+// Get the thread ID given a thread index.
+//----------------------------------------------------------------------
+nub_thread_t
+DNBProcessGetThreadAtIndex (nub_process_t pid, size_t thread_idx)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetThreadAtIndex (thread_idx);
+ return INVALID_NUB_THREAD;
+}
+
+//----------------------------------------------------------------------
+// Do whatever is needed to sync the thread's register state with it's kernel values.
+//----------------------------------------------------------------------
+nub_bool_t
+DNBProcessSyncThreadState (nub_process_t pid, nub_thread_t tid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->SyncThreadState (tid);
+ return false;
+
+}
+
+nub_addr_t
+DNBProcessGetSharedLibraryInfoAddress (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ DNBError err;
+ if (GetProcessSP (pid, procSP))
+ return procSP->Task().GetDYLDAllImageInfosAddress (err);
+ return INVALID_NUB_ADDRESS;
+}
+
+
+nub_bool_t
+DNBProcessSharedLibrariesUpdated(nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ procSP->SharedLibrariesUpdated ();
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------
+// Get the current shared library information for a process. Only return
+// the shared libraries that have changed since the last shared library
+// state changed event if only_changed is non-zero.
+//----------------------------------------------------------------------
+nub_size_t
+DNBProcessGetSharedLibraryInfo (nub_process_t pid, nub_bool_t only_changed, struct DNBExecutableImageInfo **image_infos)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->CopyImageInfos (image_infos, only_changed);
+
+ // If we have no process, then return NULL for the shared library info
+ // and zero for shared library count
+ *image_infos = NULL;
+ return 0;
+}
+
+uint32_t
+DNBGetRegisterCPUType()
+{
+ return DNBArchProtocol::GetRegisterCPUType ();
+
+}
+//----------------------------------------------------------------------
+// Get the register set information for a specific thread.
+//----------------------------------------------------------------------
+const DNBRegisterSetInfo *
+DNBGetRegisterSetInfo (nub_size_t *num_reg_sets)
+{
+ return DNBArchProtocol::GetRegisterSetInfo (num_reg_sets);
+}
+
+
+//----------------------------------------------------------------------
+// Read a register value by register set and register index.
+//----------------------------------------------------------------------
+nub_bool_t
+DNBThreadGetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value)
+{
+ MachProcessSP procSP;
+ ::bzero (value, sizeof(DNBRegisterValue));
+ if (GetProcessSP (pid, procSP))
+ {
+ if (tid != INVALID_NUB_THREAD)
+ return procSP->GetRegisterValue (tid, set, reg, value);
+ }
+ return false;
+}
+
+nub_bool_t
+DNBThreadSetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value)
+{
+ if (tid != INVALID_NUB_THREAD)
+ {
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->SetRegisterValue (tid, set, reg, value);
+ }
+ return false;
+}
+
+nub_size_t
+DNBThreadGetRegisterContext (nub_process_t pid, nub_thread_t tid, void *buf, size_t buf_len)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ if (tid != INVALID_NUB_THREAD)
+ return procSP->GetThreadList().GetRegisterContext (tid, buf, buf_len);
+ }
+ ::bzero (buf, buf_len);
+ return 0;
+
+}
+
+nub_size_t
+DNBThreadSetRegisterContext (nub_process_t pid, nub_thread_t tid, const void *buf, size_t buf_len)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ if (tid != INVALID_NUB_THREAD)
+ return procSP->GetThreadList().SetRegisterContext (tid, buf, buf_len);
+ }
+ return 0;
+}
+
+uint32_t
+DNBThreadSaveRegisterState (nub_process_t pid, nub_thread_t tid)
+{
+ if (tid != INVALID_NUB_THREAD)
+ {
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetThreadList().SaveRegisterState (tid);
+ }
+ return 0;
+}
+nub_bool_t
+DNBThreadRestoreRegisterState (nub_process_t pid, nub_thread_t tid, uint32_t save_id)
+{
+ if (tid != INVALID_NUB_THREAD)
+ {
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetThreadList().RestoreRegisterState (tid, save_id);
+ }
+ return false;
+}
+
+
+
+//----------------------------------------------------------------------
+// Read a register value by name.
+//----------------------------------------------------------------------
+nub_bool_t
+DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t reg_set, const char *reg_name, DNBRegisterValue *value)
+{
+ MachProcessSP procSP;
+ ::bzero (value, sizeof(DNBRegisterValue));
+ if (GetProcessSP (pid, procSP))
+ {
+ const struct DNBRegisterSetInfo *set_info;
+ nub_size_t num_reg_sets = 0;
+ set_info = DNBGetRegisterSetInfo (&num_reg_sets);
+ if (set_info)
+ {
+ uint32_t set = reg_set;
+ uint32_t reg;
+ if (set == REGISTER_SET_ALL)
+ {
+ for (set = 1; set < num_reg_sets; ++set)
+ {
+ for (reg = 0; reg < set_info[set].num_registers; ++reg)
+ {
+ if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
+ return procSP->GetRegisterValue (tid, set, reg, value);
+ }
+ }
+ }
+ else
+ {
+ for (reg = 0; reg < set_info[set].num_registers; ++reg)
+ {
+ if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
+ return procSP->GetRegisterValue (tid, set, reg, value);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+//----------------------------------------------------------------------
+// Read a register set and register number from the register name.
+//----------------------------------------------------------------------
+nub_bool_t
+DNBGetRegisterInfoByName (const char *reg_name, DNBRegisterInfo* info)
+{
+ const struct DNBRegisterSetInfo *set_info;
+ nub_size_t num_reg_sets = 0;
+ set_info = DNBGetRegisterSetInfo (&num_reg_sets);
+ if (set_info)
+ {
+ uint32_t set, reg;
+ for (set = 1; set < num_reg_sets; ++set)
+ {
+ for (reg = 0; reg < set_info[set].num_registers; ++reg)
+ {
+ if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
+ {
+ *info = set_info[set].registers[reg];
+ return true;
+ }
+ }
+ }
+
+ for (set = 1; set < num_reg_sets; ++set)
+ {
+ uint32_t reg;
+ for (reg = 0; reg < set_info[set].num_registers; ++reg)
+ {
+ if (set_info[set].registers[reg].alt == NULL)
+ continue;
+
+ if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0)
+ {
+ *info = set_info[set].registers[reg];
+ return true;
+ }
+ }
+ }
+ }
+
+ ::bzero (info, sizeof(DNBRegisterInfo));
+ return false;
+}
+
+
+//----------------------------------------------------------------------
+// Set the name to address callback function that this nub can use
+// for any name to address lookups that are needed.
+//----------------------------------------------------------------------
+nub_bool_t
+DNBProcessSetNameToAddressCallback (nub_process_t pid, DNBCallbackNameToAddress callback, void *baton)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ procSP->SetNameToAddressCallback (callback, baton);
+ return true;
+ }
+ return false;
+}
+
+
+//----------------------------------------------------------------------
+// Set the name to address callback function that this nub can use
+// for any name to address lookups that are needed.
+//----------------------------------------------------------------------
+nub_bool_t
+DNBProcessSetSharedLibraryInfoCallback (nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback, void *baton)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ procSP->SetSharedLibraryInfoCallback (callback, baton);
+ return true;
+ }
+ return false;
+}
+
+nub_addr_t
+DNBProcessLookupAddress (nub_process_t pid, const char *name, const char *shlib)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ {
+ return procSP->LookupSymbol (name, shlib);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+
+nub_size_t
+DNBProcessGetAvailableSTDOUT (nub_process_t pid, char *buf, nub_size_t buf_size)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetAvailableSTDOUT (buf, buf_size);
+ return 0;
+}
+
+nub_size_t
+DNBProcessGetAvailableSTDERR (nub_process_t pid, char *buf, nub_size_t buf_size)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetAvailableSTDERR (buf, buf_size);
+ return 0;
+}
+
+nub_size_t
+DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_size)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetAsyncProfileData (buf, buf_size);
+ return 0;
+}
+
+nub_size_t
+DNBProcessGetStopCount (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->StopCount();
+ return 0;
+}
+
+uint32_t
+DNBProcessGetCPUType (nub_process_t pid)
+{
+ MachProcessSP procSP;
+ if (GetProcessSP (pid, procSP))
+ return procSP->GetCPUType ();
+ return 0;
+
+}
+
+nub_bool_t
+DNBResolveExecutablePath (const char *path, char *resolved_path, size_t resolved_path_size)
+{
+ if (path == NULL || path[0] == '\0')
+ return false;
+
+ char max_path[PATH_MAX];
+ std::string result;
+ CFString::GlobPath(path, result);
+
+ if (result.empty())
+ result = path;
+
+ struct stat path_stat;
+ if (::stat(path, &path_stat) == 0)
+ {
+ if ((path_stat.st_mode & S_IFMT) == S_IFDIR)
+ {
+ CFBundle bundle (path);
+ CFReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
+ if (url.get())
+ {
+ if (::CFURLGetFileSystemRepresentation (url.get(), true, (UInt8*)resolved_path, resolved_path_size))
+ return true;
+ }
+ }
+ }
+
+ if (realpath(path, max_path))
+ {
+ // Found the path relatively...
+ ::strncpy(resolved_path, max_path, resolved_path_size);
+ return strlen(resolved_path) + 1 < resolved_path_size;
+ }
+ else
+ {
+ // Not a relative path, check the PATH environment variable if the
+ const char *PATH = getenv("PATH");
+ if (PATH)
+ {
+ const char *curr_path_start = PATH;
+ const char *curr_path_end;
+ while (curr_path_start && *curr_path_start)
+ {
+ curr_path_end = strchr(curr_path_start, ':');
+ if (curr_path_end == NULL)
+ {
+ result.assign(curr_path_start);
+ curr_path_start = NULL;
+ }
+ else if (curr_path_end > curr_path_start)
+ {
+ size_t len = curr_path_end - curr_path_start;
+ result.assign(curr_path_start, len);
+ curr_path_start += len + 1;
+ }
+ else
+ break;
+
+ result += '/';
+ result += path;
+ struct stat s;
+ if (stat(result.c_str(), &s) == 0)
+ {
+ ::strncpy(resolved_path, result.c_str(), resolved_path_size);
+ return result.size() + 1 < resolved_path_size;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool
+DNBGetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch)
+{
+ return MachProcess::GetOSVersionNumbers (major, minor, patch);
+}
+
+
+void
+DNBInitialize()
+{
+ DNBLogThreadedIf (LOG_PROCESS, "DNBInitialize ()");
+#if defined (__i386__) || defined (__x86_64__)
+ DNBArchImplI386::Initialize();
+ DNBArchImplX86_64::Initialize();
+#elif defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ DNBArchMachARM::Initialize();
+ DNBArchMachARM64::Initialize();
+#endif
+}
+
+void
+DNBTerminate()
+{
+}
+
+nub_bool_t
+DNBSetArchitecture (const char *arch)
+{
+ if (arch && arch[0])
+ {
+ if (strcasecmp (arch, "i386") == 0)
+ return DNBArchProtocol::SetArchitecture (CPU_TYPE_I386);
+ else if ((strcasecmp (arch, "x86_64") == 0) || (strcasecmp (arch, "x86_64h") == 0))
+ return DNBArchProtocol::SetArchitecture (CPU_TYPE_X86_64);
+ else if (strstr (arch, "arm64") == arch || strstr (arch, "armv8") == arch || strstr (arch, "aarch64") == arch)
+ return DNBArchProtocol::SetArchitecture (CPU_TYPE_ARM64);
+ else if (strstr (arch, "arm") == arch)
+ return DNBArchProtocol::SetArchitecture (CPU_TYPE_ARM);
+ }
+ return false;
+}
diff --git a/tools/debugserver/source/DNB.h b/tools/debugserver/source/DNB.h
new file mode 100644
index 000000000000..7b186d38b32a
--- /dev/null
+++ b/tools/debugserver/source/DNB.h
@@ -0,0 +1,173 @@
+//===-- DNB.h ---------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/23/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNB_h__
+#define __DNB_h__
+
+#include "MacOSX/Genealogy.h"
+#include "MacOSX/ThreadInfo.h"
+#include "JSONGenerator.h"
+#include "DNBDefs.h"
+#include <mach/thread_info.h>
+#include <string>
+
+#define DNB_EXPORT __attribute__((visibility("default")))
+
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 ((cpu_type_t) 12 | 0x01000000)
+#endif
+
+typedef bool (*DNBShouldCancelCallback) (void *);
+
+void DNBInitialize ();
+void DNBTerminate ();
+
+nub_bool_t DNBSetArchitecture (const char *arch);
+
+//----------------------------------------------------------------------
+// Process control
+//----------------------------------------------------------------------
+nub_process_t DNBProcessLaunch (const char *path,
+ char const *argv[],
+ const char *envp[],
+ const char *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ bool no_stdio,
+ nub_launch_flavor_t launch_flavor,
+ int disable_aslr,
+ const char *event_data,
+ char *err_str,
+ size_t err_len);
+
+nub_process_t DNBProcessGetPIDByName (const char *name);
+nub_process_t DNBProcessAttach (nub_process_t pid, struct timespec *timeout, char *err_str, size_t err_len);
+nub_process_t DNBProcessAttachByName (const char *name, struct timespec *timeout, char *err_str, size_t err_len);
+nub_process_t DNBProcessAttachWait (const char *wait_name, nub_launch_flavor_t launch_flavor, bool ignore_existing, struct timespec *timeout, useconds_t interval, char *err_str, size_t err_len, DNBShouldCancelCallback should_cancel = NULL, void *callback_data = NULL);
+// Resume a process with exact instructions on what to do with each thread:
+// - If no thread actions are supplied (actions is NULL or num_actions is zero),
+// then all threads are continued.
+// - If any thread actions are supplied, then each thread will do as it is told
+// by the action. A default actions for any threads that don't have an
+// explicit thread action can be made by making a thread action with a tid of
+// INVALID_NUB_THREAD. If there is no default action, those threads will
+// remain stopped.
+nub_bool_t DNBProcessResume (nub_process_t pid, const DNBThreadResumeAction *actions, size_t num_actions) DNB_EXPORT;
+nub_bool_t DNBProcessHalt (nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessDetach (nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSignal (nub_process_t pid, int signal) DNB_EXPORT;
+nub_bool_t DNBProcessInterrupt (nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessKill (nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSendEvent (nub_process_t pid, const char *event) DNB_EXPORT;
+nub_size_t DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void *buf) DNB_EXPORT;
+uint64_t DNBProcessMemoryReadInteger (nub_process_t pid, nub_addr_t addr, nub_size_t integer_size, uint64_t fail_value) DNB_EXPORT;
+nub_addr_t DNBProcessMemoryReadPointer (nub_process_t pid, nub_addr_t addr) DNB_EXPORT;
+std::string DNBProcessMemoryReadCString (nub_process_t pid, nub_addr_t addr) DNB_EXPORT;
+std::string DNBProcessMemoryReadCStringFixed (nub_process_t pid, nub_addr_t addr, nub_size_t fixed_length) DNB_EXPORT;
+nub_size_t DNBProcessMemoryWrite (nub_process_t pid, nub_addr_t addr, nub_size_t size, const void *buf) DNB_EXPORT;
+nub_addr_t DNBProcessMemoryAllocate (nub_process_t pid, nub_size_t size, uint32_t permissions) DNB_EXPORT;
+nub_bool_t DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr) DNB_EXPORT;
+int DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info) DNB_EXPORT;
+std::string DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType) DNB_EXPORT;
+nub_bool_t DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) DNB_EXPORT;
+
+//----------------------------------------------------------------------
+// Process status
+//----------------------------------------------------------------------
+nub_bool_t DNBProcessIsAlive (nub_process_t pid) DNB_EXPORT;
+nub_state_t DNBProcessGetState (nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessGetExitStatus (nub_process_t pid, int *status) DNB_EXPORT;
+nub_bool_t DNBProcessSetExitStatus (nub_process_t pid, int status) DNB_EXPORT;
+const char * DNBProcessGetExitInfo (nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSetExitInfo (nub_process_t pid, const char *info) DNB_EXPORT;
+nub_size_t DNBProcessGetNumThreads (nub_process_t pid) DNB_EXPORT;
+nub_thread_t DNBProcessGetCurrentThread (nub_process_t pid) DNB_EXPORT;
+nub_thread_t DNBProcessGetCurrentThreadMachPort (nub_process_t pid) DNB_EXPORT;
+nub_thread_t DNBProcessSetCurrentThread (nub_process_t pid, nub_thread_t tid) DNB_EXPORT;
+nub_thread_t DNBProcessGetThreadAtIndex (nub_process_t pid, nub_size_t thread_idx) DNB_EXPORT;
+nub_bool_t DNBProcessSyncThreadState (nub_process_t pid, nub_thread_t tid) DNB_EXPORT;
+nub_addr_t DNBProcessGetSharedLibraryInfoAddress (nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSharedLibrariesUpdated (nub_process_t pid) DNB_EXPORT;
+nub_size_t DNBProcessGetSharedLibraryInfo (nub_process_t pid, nub_bool_t only_changed, DNBExecutableImageInfo **image_infos) DNB_EXPORT;
+nub_bool_t DNBProcessSetNameToAddressCallback (nub_process_t pid, DNBCallbackNameToAddress callback, void *baton) DNB_EXPORT;
+nub_bool_t DNBProcessSetSharedLibraryInfoCallback (nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback, void *baton) DNB_EXPORT;
+nub_addr_t DNBProcessLookupAddress (nub_process_t pid, const char *name, const char *shlib) DNB_EXPORT;
+nub_size_t DNBProcessGetAvailableSTDOUT (nub_process_t pid, char *buf, nub_size_t buf_size) DNB_EXPORT;
+nub_size_t DNBProcessGetAvailableSTDERR (nub_process_t pid, char *buf, nub_size_t buf_size) DNB_EXPORT;
+nub_size_t DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_size) DNB_EXPORT;
+nub_size_t DNBProcessGetStopCount (nub_process_t pid) DNB_EXPORT;
+uint32_t DNBProcessGetCPUType (nub_process_t pid) DNB_EXPORT;
+
+//----------------------------------------------------------------------
+// Process executable and arguments
+//----------------------------------------------------------------------
+const char * DNBProcessGetExecutablePath (nub_process_t pid);
+const char * DNBProcessGetArgumentAtIndex (nub_process_t pid, nub_size_t idx);
+nub_size_t DNBProcessGetArgumentCount (nub_process_t pid);
+
+//----------------------------------------------------------------------
+// Process events
+//----------------------------------------------------------------------
+nub_event_t DNBProcessWaitForEvents (nub_process_t pid, nub_event_t event_mask, bool wait_for_set, struct timespec* timeout);
+void DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask);
+
+//----------------------------------------------------------------------
+// Thread functions
+//----------------------------------------------------------------------
+const char * DNBThreadGetName (nub_process_t pid, nub_thread_t tid);
+nub_bool_t DNBThreadGetIdentifierInfo (nub_process_t pid, nub_thread_t tid, thread_identifier_info_data_t *ident_info);
+nub_state_t DNBThreadGetState (nub_process_t pid, nub_thread_t tid);
+nub_bool_t DNBThreadGetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value);
+nub_bool_t DNBThreadSetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value);
+nub_size_t DNBThreadGetRegisterContext (nub_process_t pid, nub_thread_t tid, void *buf, size_t buf_len);
+nub_size_t DNBThreadSetRegisterContext (nub_process_t pid, nub_thread_t tid, const void *buf, size_t buf_len);
+uint32_t DNBThreadSaveRegisterState (nub_process_t pid, nub_thread_t tid);
+nub_bool_t DNBThreadRestoreRegisterState (nub_process_t pid, nub_thread_t tid, uint32_t save_id);
+nub_bool_t DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t set, const char *name, DNBRegisterValue *value);
+nub_bool_t DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, DNBThreadStopInfo *stop_info);
+const char * DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid);
+Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread (nub_process_t pid, nub_thread_t tid, bool &timed_out);
+Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo (nub_process_t pid, size_t idx);
+ThreadInfo::QoS DNBGetRequestedQoSForThread (nub_process_t pid, nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index);
+nub_addr_t DNBGetPThreadT (nub_process_t pid, nub_thread_t tid);
+nub_addr_t DNBGetDispatchQueueT (nub_process_t pid, nub_thread_t tid);
+nub_addr_t DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size);
+JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count);
+//
+//----------------------------------------------------------------------
+// Breakpoint functions
+//----------------------------------------------------------------------
+nub_bool_t DNBBreakpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware);
+nub_bool_t DNBBreakpointClear (nub_process_t pid, nub_addr_t addr);
+
+//----------------------------------------------------------------------
+// Watchpoint functions
+//----------------------------------------------------------------------
+nub_bool_t DNBWatchpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, uint32_t watch_flags, nub_bool_t hardware);
+nub_bool_t DNBWatchpointClear (nub_process_t pid, nub_addr_t addr);
+uint32_t DNBWatchpointGetNumSupportedHWP (nub_process_t pid);
+
+uint32_t DNBGetRegisterCPUType ();
+const DNBRegisterSetInfo *
+ DNBGetRegisterSetInfo (nub_size_t *num_reg_sets);
+nub_bool_t DNBGetRegisterInfoByName (const char *reg_name, DNBRegisterInfo* info);
+
+//----------------------------------------------------------------------
+// Other static nub information calls.
+//----------------------------------------------------------------------
+const char * DNBStateAsString (nub_state_t state);
+nub_bool_t DNBResolveExecutablePath (const char *path, char *resolved_path, size_t resolved_path_size);
+bool DNBGetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch);
+
+#endif
diff --git a/tools/debugserver/source/DNBArch.cpp b/tools/debugserver/source/DNBArch.cpp
new file mode 100644
index 000000000000..f17a719e92ec
--- /dev/null
+++ b/tools/debugserver/source/DNBArch.cpp
@@ -0,0 +1,97 @@
+//===-- DNBArch.cpp ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/24/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBArch.h"
+#include <assert.h>
+#include <mach/mach.h>
+
+#include <map>
+
+#include "DNBLog.h"
+
+typedef std::map<uint32_t, DNBArchPluginInfo> CPUPluginInfoMap;
+
+static uint32_t g_current_cpu_type = 0;
+CPUPluginInfoMap g_arch_plugins;
+
+
+static const DNBArchPluginInfo *
+GetArchInfo ()
+{
+ CPUPluginInfoMap::const_iterator pos = g_arch_plugins.find(g_current_cpu_type);
+ if (pos != g_arch_plugins.end())
+ return &pos->second;
+ return NULL;
+}
+
+
+uint32_t
+DNBArchProtocol::GetArchitecture ()
+{
+ return g_current_cpu_type;
+}
+
+bool
+DNBArchProtocol::SetArchitecture (uint32_t cpu_type)
+{
+ g_current_cpu_type = cpu_type;
+ bool result = g_arch_plugins.find(g_current_cpu_type) != g_arch_plugins.end();
+ DNBLogThreadedIf (LOG_PROCESS, "DNBArchProtocol::SetDefaultArchitecture (cpu_type=0x%8.8x) => %i", cpu_type, result);
+ return result;
+}
+
+void
+DNBArchProtocol::RegisterArchPlugin (const DNBArchPluginInfo &arch_info)
+{
+ if (arch_info.cpu_type)
+ g_arch_plugins[arch_info.cpu_type] = arch_info;
+}
+
+uint32_t
+DNBArchProtocol::GetRegisterCPUType ()
+{
+ const DNBArchPluginInfo *arch_info = GetArchInfo ();
+ if (arch_info)
+ return arch_info->cpu_type;
+ return 0;
+}
+
+const DNBRegisterSetInfo *
+DNBArchProtocol::GetRegisterSetInfo (nub_size_t *num_reg_sets)
+{
+ const DNBArchPluginInfo *arch_info = GetArchInfo ();
+ if (arch_info)
+ return arch_info->GetRegisterSetInfo (num_reg_sets);
+ *num_reg_sets = 0;
+ return NULL;
+}
+
+DNBArchProtocol *
+DNBArchProtocol::Create (MachThread *thread)
+{
+ const DNBArchPluginInfo *arch_info = GetArchInfo ();
+ if (arch_info)
+ return arch_info->Create (thread);
+ return NULL;
+
+}
+
+const uint8_t *
+DNBArchProtocol::GetBreakpointOpcode (nub_size_t byte_size)
+{
+ const DNBArchPluginInfo *arch_info = GetArchInfo ();
+ if (arch_info)
+ return arch_info->GetBreakpointOpcode (byte_size);
+ return NULL;
+}
+
diff --git a/tools/debugserver/source/DNBArch.h b/tools/debugserver/source/DNBArch.h
new file mode 100644
index 000000000000..c07d3a67400d
--- /dev/null
+++ b/tools/debugserver/source/DNBArch.h
@@ -0,0 +1,129 @@
+//===-- DNBArch.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/24/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArch_h__
+#define __DebugNubArch_h__
+
+#include "DNBDefs.h"
+#include "MacOSX/MachException.h"
+
+#include <mach/mach.h>
+#include <stdio.h>
+
+struct DNBRegisterValue;
+struct DNBRegisterSetInfo;
+class DNBArchProtocol;
+class MachThread;
+
+typedef DNBArchProtocol * (* DNBArchCallbackCreate)(MachThread *thread);
+typedef const DNBRegisterSetInfo * (* DNBArchCallbackGetRegisterSetInfo)(nub_size_t *num_reg_sets);
+typedef const uint8_t * (* DNBArchCallbackGetBreakpointOpcode)(nub_size_t byte_size);
+
+typedef struct DNBArchPluginInfoTag
+{
+ uint32_t cpu_type;
+ DNBArchCallbackCreate Create;
+ DNBArchCallbackGetRegisterSetInfo GetRegisterSetInfo;
+ DNBArchCallbackGetBreakpointOpcode GetBreakpointOpcode;
+} DNBArchPluginInfo;
+
+class DNBArchProtocol
+{
+public:
+ static DNBArchProtocol *
+ Create (MachThread *thread);
+
+ static uint32_t
+ GetRegisterCPUType ();
+
+ static const DNBRegisterSetInfo *
+ GetRegisterSetInfo (nub_size_t *num_reg_sets);
+
+ static const uint8_t *
+ GetBreakpointOpcode (nub_size_t byte_size);
+
+ static void
+ RegisterArchPlugin (const DNBArchPluginInfo &arch_info);
+
+ static uint32_t
+ GetArchitecture ();
+
+ static bool
+ SetArchitecture (uint32_t cpu_type);
+
+ DNBArchProtocol () :
+ m_save_id(0)
+ {
+
+ }
+
+ virtual ~DNBArchProtocol ()
+ {
+
+ }
+ virtual bool GetRegisterValue (uint32_t set, uint32_t reg, DNBRegisterValue *value) = 0;
+ virtual bool SetRegisterValue (uint32_t set, uint32_t reg, const DNBRegisterValue *value) = 0;
+ virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len) = 0;
+ virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len) = 0;
+ virtual uint32_t SaveRegisterState () = 0;
+ virtual bool RestoreRegisterState (uint32_t save_id) = 0;
+
+ virtual kern_return_t GetRegisterState (int set, bool force) = 0;
+ virtual kern_return_t SetRegisterState (int set) = 0;
+ virtual bool RegisterSetStateIsValid (int set) const = 0;
+
+ virtual uint64_t GetPC (uint64_t failValue) = 0; // Get program counter
+ virtual kern_return_t SetPC (uint64_t value) = 0;
+ virtual uint64_t GetSP (uint64_t failValue) = 0; // Get stack pointer
+ virtual void ThreadWillResume () = 0;
+ virtual bool ThreadDidStop () = 0;
+ virtual bool NotifyException (MachException::Data& exc) { return false; }
+ virtual uint32_t NumSupportedHardwareBreakpoints() { return 0; }
+ virtual uint32_t NumSupportedHardwareWatchpoints() { return 0; }
+ virtual uint32_t EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size) { return INVALID_NUB_HW_INDEX; }
+ virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task) { return INVALID_NUB_HW_INDEX; }
+ virtual bool DisableHardwareBreakpoint (uint32_t hw_index) { return false; }
+ virtual bool DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task) { return false; }
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr) { return INVALID_NUB_HW_INDEX; }
+ virtual bool StepNotComplete () { return false; }
+
+protected:
+ friend class MachThread;
+
+ uint32_t GetNextRegisterStateSaveID ()
+ {
+ return ++m_save_id;
+ }
+
+ enum
+ {
+ Trans_Pending = 0, // Transaction is pending, and checkpoint state has been snapshotted.
+ Trans_Done = 1, // Transaction is done, the current state is committed, and checkpoint state is irrelevant.
+ Trans_Rolled_Back = 2 // Transaction is done, the current state has been rolled back to the checkpoint state.
+ };
+ virtual bool StartTransForHWP() { return true; }
+ virtual bool RollbackTransForHWP() { return true; }
+ virtual bool FinishTransForHWP() { return true; }
+
+ uint32_t m_save_id; // An always incrementing integer ID used with SaveRegisterState/RestoreRegisterState
+
+};
+
+
+#include "MacOSX/arm/DNBArchImpl.h"
+#include "MacOSX/arm64/DNBArchImplARM64.h"
+#include "MacOSX/i386/DNBArchImplI386.h"
+#include "MacOSX/x86_64/DNBArchImplX86_64.h"
+#include "MacOSX/ppc/DNBArchImpl.h"
+
+#endif
diff --git a/tools/debugserver/source/DNBBreakpoint.cpp b/tools/debugserver/source/DNBBreakpoint.cpp
new file mode 100644
index 000000000000..2645f173306b
--- /dev/null
+++ b/tools/debugserver/source/DNBBreakpoint.cpp
@@ -0,0 +1,225 @@
+//===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/29/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBBreakpoint.h"
+#include "MachProcess.h"
+#include <assert.h>
+#include <algorithm>
+#include <inttypes.h>
+#include "DNBLog.h"
+
+
+#pragma mark -- DNBBreakpoint
+DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size, bool hardware) :
+ m_retain_count (1),
+ m_byte_size (static_cast<uint32_t>(byte_size)),
+ m_opcode(),
+ m_addr(addr),
+ m_enabled(0),
+ m_hw_preferred(hardware),
+ m_is_watchpoint(0),
+ m_watch_read(0),
+ m_watch_write(0),
+ m_hw_index(INVALID_NUB_HW_INDEX)
+{
+}
+
+DNBBreakpoint::~DNBBreakpoint()
+{
+}
+
+void
+DNBBreakpoint::Dump() const
+{
+ if (IsBreakpoint())
+ {
+ DNBLog ("DNBBreakpoint addr = 0x%llx state = %s type = %s breakpoint hw_index = %i",
+ (uint64_t)m_addr,
+ m_enabled ? "enabled " : "disabled",
+ IsHardware() ? "hardware" : "software",
+ GetHardwareIndex());
+ }
+ else
+ {
+ DNBLog ("DNBBreakpoint addr = 0x%llx size = %llu state = %s type = %s watchpoint (%s%s) hw_index = %i",
+ (uint64_t)m_addr,
+ (uint64_t)m_byte_size,
+ m_enabled ? "enabled " : "disabled",
+ IsHardware() ? "hardware" : "software",
+ m_watch_read ? "r" : "",
+ m_watch_write ? "w" : "",
+ GetHardwareIndex());
+ }
+}
+
+#pragma mark -- DNBBreakpointList
+
+DNBBreakpointList::DNBBreakpointList()
+{
+}
+
+DNBBreakpointList::~DNBBreakpointList()
+{
+}
+
+
+DNBBreakpoint *
+DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length, bool hardware)
+{
+ m_breakpoints.insert(std::make_pair(addr, DNBBreakpoint(addr, length, hardware)));
+ iterator pos = m_breakpoints.find (addr);
+ return &pos->second;
+}
+
+bool
+DNBBreakpointList::Remove (nub_addr_t addr)
+{
+ iterator pos = m_breakpoints.find(addr);
+ if (pos != m_breakpoints.end())
+ {
+ m_breakpoints.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+DNBBreakpoint *
+DNBBreakpointList::FindByAddress (nub_addr_t addr)
+{
+ iterator pos = m_breakpoints.find(addr);
+ if (pos != m_breakpoints.end())
+ return &pos->second;
+
+ return NULL;
+}
+
+const DNBBreakpoint *
+DNBBreakpointList::FindByAddress (nub_addr_t addr) const
+{
+ const_iterator pos = m_breakpoints.find(addr);
+ if (pos != m_breakpoints.end())
+ return &pos->second;
+
+ return NULL;
+}
+
+// Finds the next breakpoint at an address greater than or equal to "addr"
+size_t
+DNBBreakpointList::FindBreakpointsThatOverlapRange (nub_addr_t addr,
+ nub_addr_t size,
+ std::vector<DNBBreakpoint *> &bps)
+{
+ bps.clear();
+ iterator end = m_breakpoints.end();
+ // Find the first breakpoint with an address >= to "addr"
+ iterator pos = m_breakpoints.lower_bound(addr);
+ if (pos != end)
+ {
+ if (pos != m_breakpoints.begin())
+ {
+ // Watch out for a breakpoint at an address less than "addr" that might still overlap
+ iterator prev_pos = pos;
+ --prev_pos;
+ if (prev_pos->second.IntersectsRange (addr, size, NULL, NULL, NULL))
+ bps.push_back (&pos->second);
+
+ }
+
+ while (pos != end)
+ {
+ // When we hit a breakpoint whose start address is greater than "addr + size" we are done.
+ // Do the math in a way that doesn't risk unsigned overflow with bad input.
+ if ((pos->second.Address() - addr) >= size)
+ break;
+
+ // Check if this breakpoint overlaps, and if it does, add it to the list
+ if (pos->second.IntersectsRange (addr, size, NULL, NULL, NULL))
+ {
+ bps.push_back (&pos->second);
+ ++pos;
+ }
+ }
+ }
+ return bps.size();
+}
+
+void
+DNBBreakpointList::Dump() const
+{
+ const_iterator pos;
+ const_iterator end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ pos->second.Dump();
+}
+
+void
+DNBBreakpointList::DisableAll ()
+{
+ iterator pos, end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ pos->second.SetEnabled(false);
+}
+
+
+void
+DNBBreakpointList::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, void *p) const
+{
+ uint8_t *buf = (uint8_t *)p;
+ const_iterator end = m_breakpoints.end();
+ const_iterator pos = m_breakpoints.lower_bound(addr);
+ while (pos != end && (pos->first < (addr + size)))
+ {
+ nub_addr_t intersect_addr;
+ nub_size_t intersect_size;
+ nub_size_t opcode_offset;
+ const DNBBreakpoint &bp = pos->second;
+ if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset))
+ {
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp.ByteSize());
+ nub_size_t buf_offset = intersect_addr - addr;
+ ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset, intersect_size);
+ }
+ ++pos;
+ }
+}
+
+void
+DNBBreakpointList::DisableAllBreakpoints(MachProcess *process)
+{
+ iterator pos, end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ process->DisableBreakpoint(pos->second.Address(), false);
+}
+
+void
+DNBBreakpointList::DisableAllWatchpoints(MachProcess *process)
+{
+ iterator pos, end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ process->DisableWatchpoint(pos->second.Address(), false);
+}
+
+void
+DNBBreakpointList::RemoveDisabled()
+{
+ iterator pos = m_breakpoints.begin();
+ while (pos != m_breakpoints.end())
+ {
+ if (!pos->second.IsEnabled())
+ pos = m_breakpoints.erase(pos);
+ else
+ ++pos;
+ }
+}
diff --git a/tools/debugserver/source/DNBBreakpoint.h b/tools/debugserver/source/DNBBreakpoint.h
new file mode 100644
index 000000000000..c764dbd6cf29
--- /dev/null
+++ b/tools/debugserver/source/DNBBreakpoint.h
@@ -0,0 +1,165 @@
+//===-- DNBBreakpoint.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/29/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBBreakpoint_h__
+#define __DNBBreakpoint_h__
+
+#include <mach/mach.h>
+
+#include <map>
+#include <vector>
+
+#include "DNBDefs.h"
+
+class MachProcess;
+
+class DNBBreakpoint
+{
+public:
+ DNBBreakpoint(nub_addr_t m_addr, nub_size_t byte_size, bool hardware);
+ ~DNBBreakpoint();
+
+ nub_size_t ByteSize() const { return m_byte_size; }
+ uint8_t * SavedOpcodeBytes() { return &m_opcode[0]; }
+ const uint8_t *
+ SavedOpcodeBytes() const { return &m_opcode[0]; }
+ nub_addr_t Address() const { return m_addr; }
+// nub_thread_t ThreadID() const { return m_tid; }
+ bool IsEnabled() const { return m_enabled; }
+ bool IntersectsRange(nub_addr_t addr,
+ nub_size_t size,
+ nub_addr_t *intersect_addr,
+ nub_size_t *intersect_size,
+ nub_size_t *opcode_offset) const
+ {
+ // We only use software traps for software breakpoints
+ if (IsBreakpoint() && IsEnabled() && !IsHardware())
+ {
+ if (m_byte_size > 0)
+ {
+ const nub_addr_t bp_end_addr = m_addr + m_byte_size;
+ const nub_addr_t end_addr = addr + size;
+ // Is the breakpoint end address before the passed in start address?
+ if (bp_end_addr <= addr)
+ return false;
+ // Is the breakpoint start address after passed in end address?
+ if (end_addr <= m_addr)
+ return false;
+ if (intersect_addr || intersect_size || opcode_offset)
+ {
+ if (m_addr < addr)
+ {
+ if (intersect_addr)
+ *intersect_addr = addr;
+ if (intersect_size)
+ *intersect_size = std::min<nub_addr_t>(bp_end_addr, end_addr) - addr;
+ if (opcode_offset)
+ *opcode_offset = addr - m_addr;
+ }
+ else
+ {
+ if (intersect_addr)
+ *intersect_addr = m_addr;
+ if (intersect_size)
+ *intersect_size = std::min<nub_addr_t>(bp_end_addr, end_addr) - m_addr;
+ if (opcode_offset)
+ *opcode_offset = 0;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ void SetEnabled(bool enabled)
+ {
+ if (!enabled)
+ SetHardwareIndex(INVALID_NUB_HW_INDEX);
+ m_enabled = enabled;
+ }
+ void SetIsWatchpoint (uint32_t type)
+ {
+ m_is_watchpoint = 1;
+ m_watch_read = (type & WATCH_TYPE_READ) != 0;
+ m_watch_write = (type & WATCH_TYPE_WRITE) != 0;
+ }
+ bool IsBreakpoint() const { return m_is_watchpoint == 0; }
+ bool IsWatchpoint() const { return m_is_watchpoint == 1; }
+ bool WatchpointRead() const { return m_watch_read != 0; }
+ bool WatchpointWrite() const { return m_watch_write != 0; }
+ bool HardwarePreferred() const { return m_hw_preferred; }
+ bool IsHardware() const { return m_hw_index != INVALID_NUB_HW_INDEX; }
+ uint32_t GetHardwareIndex() const { return m_hw_index; }
+ void SetHardwareIndex(uint32_t hw_index) { m_hw_index = hw_index; }
+ void Dump() const;
+ uint32_t Retain ()
+ {
+ return ++m_retain_count;
+ }
+ uint32_t Release ()
+ {
+ if (m_retain_count == 0)
+ return 0;
+ return --m_retain_count;
+ }
+
+private:
+ uint32_t m_retain_count; // Each breakpoint is maintained by address and is ref counted in case multiple people set a breakpoint at the same address
+ uint32_t m_byte_size; // Length in bytes of the breakpoint if set in memory
+ uint8_t m_opcode[8]; // Saved opcode bytes
+ nub_addr_t m_addr; // Address of this breakpoint
+ uint32_t m_enabled:1, // Flags for this breakpoint
+ m_hw_preferred:1, // 1 if this point has been requested to be set using hardware (which may fail due to lack of resources)
+ m_is_watchpoint:1, // 1 if this is a watchpoint
+ m_watch_read:1, // 1 if we stop when the watched data is read from
+ m_watch_write:1; // 1 if we stop when the watched data is written to
+ uint32_t m_hw_index; // The hardware resource index for this breakpoint/watchpoint
+};
+
+
+class DNBBreakpointList
+{
+public:
+ DNBBreakpointList();
+ ~DNBBreakpointList();
+
+ DNBBreakpoint * Add (nub_addr_t addr, nub_size_t length, bool hardware);
+ bool Remove (nub_addr_t addr);
+ DNBBreakpoint * FindByAddress (nub_addr_t addr);
+ const DNBBreakpoint * FindByAddress (nub_addr_t addr) const;
+
+ size_t FindBreakpointsThatOverlapRange (nub_addr_t addr,
+ nub_addr_t size,
+ std::vector<DNBBreakpoint *> &bps);
+
+ void Dump () const;
+
+ size_t Size() const { return m_breakpoints.size(); }
+ void DisableAll ();
+
+ void RemoveTrapsFromBuffer (nub_addr_t addr,
+ nub_size_t size,
+ void *buf) const;
+
+ void DisableAllBreakpoints (MachProcess *process);
+ void DisableAllWatchpoints(MachProcess *process);
+ void RemoveDisabled ();
+protected:
+ typedef std::map<nub_addr_t, DNBBreakpoint> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ collection m_breakpoints;
+};
+
+#endif
+
diff --git a/tools/debugserver/source/DNBDataRef.cpp b/tools/debugserver/source/DNBDataRef.cpp
new file mode 100644
index 000000000000..53e9881dfb94
--- /dev/null
+++ b/tools/debugserver/source/DNBDataRef.cpp
@@ -0,0 +1,386 @@
+//===-- DNBDataRef.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/11/06.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include <assert.h>
+#include <ctype.h>
+#include <libkern/OSByteOrder.h>
+
+//----------------------------------------------------------------------
+// Constructor
+//----------------------------------------------------------------------
+
+DNBDataRef::DNBDataRef() :
+ m_start(NULL),
+ m_end(NULL),
+ m_swap(false),
+ m_ptrSize(0),
+ m_addrPCRelative(INVALID_NUB_ADDRESS),
+ m_addrTEXT(INVALID_NUB_ADDRESS),
+ m_addrDATA(INVALID_NUB_ADDRESS)
+{
+}
+
+
+//----------------------------------------------------------------------
+// Constructor
+//----------------------------------------------------------------------
+
+DNBDataRef::DNBDataRef(const uint8_t *start, size_t size, bool swap) :
+ m_start(start),
+ m_end(start+size),
+ m_swap(swap),
+ m_ptrSize(0),
+ m_addrPCRelative(INVALID_NUB_ADDRESS),
+ m_addrTEXT(INVALID_NUB_ADDRESS),
+ m_addrDATA(INVALID_NUB_ADDRESS)
+{
+}
+
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+
+DNBDataRef::~DNBDataRef()
+{
+}
+
+
+//----------------------------------------------------------------------
+// Get8
+//----------------------------------------------------------------------
+uint8_t
+DNBDataRef::Get8(offset_t *offset_ptr) const
+{
+ uint8_t val = 0;
+ if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) )
+ {
+ val = *(m_start + *offset_ptr);
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+
+//----------------------------------------------------------------------
+// Get16
+//----------------------------------------------------------------------
+uint16_t
+DNBDataRef::Get16(offset_t *offset_ptr) const
+{
+ uint16_t val = 0;
+ if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) )
+ {
+ const uint8_t *p = m_start + *offset_ptr;
+ val = *(uint16_t*)p;
+
+ if (m_swap)
+ val = OSSwapInt16(val);
+
+ // Advance the offset
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+
+//----------------------------------------------------------------------
+// Get32
+//----------------------------------------------------------------------
+uint32_t
+DNBDataRef::Get32(offset_t *offset_ptr) const
+{
+ uint32_t val = 0;
+ if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) )
+ {
+ const uint8_t *p = m_start + *offset_ptr;
+ val = *(uint32_t*)p;
+ if (m_swap)
+ val = OSSwapInt32(val);
+
+ // Advance the offset
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+
+//----------------------------------------------------------------------
+// Get64
+//----------------------------------------------------------------------
+uint64_t
+DNBDataRef::Get64(offset_t *offset_ptr) const
+{
+ uint64_t val = 0;
+ if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) )
+ {
+ const uint8_t *p = m_start + *offset_ptr;
+ val = *(uint64_t*)p;
+ if (m_swap)
+ val = OSSwapInt64(val);
+
+ // Advance the offset
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+
+//----------------------------------------------------------------------
+// GetMax32
+//
+// Used for calls when the size can vary. Fill in extra cases if they
+// are ever needed.
+//----------------------------------------------------------------------
+uint32_t
+DNBDataRef::GetMax32(offset_t *offset_ptr, uint32_t byte_size) const
+{
+ switch (byte_size)
+ {
+ case 1: return Get8 (offset_ptr); break;
+ case 2: return Get16(offset_ptr); break;
+ case 4: return Get32(offset_ptr); break;
+ default:
+ assert(!"GetMax32 unhandled case!");
+ break;
+ }
+ return 0;
+}
+
+
+//----------------------------------------------------------------------
+// GetMax64
+//
+// Used for calls when the size can vary. Fill in extra cases if they
+// are ever needed.
+//----------------------------------------------------------------------
+uint64_t
+DNBDataRef::GetMax64(offset_t *offset_ptr, uint32_t size) const
+{
+ switch (size)
+ {
+ case 1: return Get8 (offset_ptr); break;
+ case 2: return Get16(offset_ptr); break;
+ case 4: return Get32(offset_ptr); break;
+ case 8: return Get64(offset_ptr); break;
+ default:
+ assert(!"GetMax64 unhandled case!");
+ break;
+ }
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// GetPointer
+//
+// Extract a pointer value from the buffer. The pointer size must be
+// set prior to using this using one of the SetPointerSize functions.
+//----------------------------------------------------------------------
+uint64_t
+DNBDataRef::GetPointer(offset_t *offset_ptr) const
+{
+ // Must set pointer size prior to using this call
+ assert(m_ptrSize != 0);
+ return GetMax64(offset_ptr, m_ptrSize);
+}
+//----------------------------------------------------------------------
+// GetCStr
+//----------------------------------------------------------------------
+const char *
+DNBDataRef::GetCStr(offset_t *offset_ptr, uint32_t fixed_length) const
+{
+ const char *s = NULL;
+ if ( m_start < m_end )
+ {
+ s = (char*)m_start + *offset_ptr;
+
+ // Advance the offset
+ if (fixed_length)
+ *offset_ptr += fixed_length;
+ else
+ *offset_ptr += strlen(s) + 1;
+ }
+ return s;
+}
+
+
+//----------------------------------------------------------------------
+// GetData
+//----------------------------------------------------------------------
+const uint8_t *
+DNBDataRef::GetData(offset_t *offset_ptr, uint32_t length) const
+{
+ const uint8_t *data = NULL;
+ if ( length > 0 && ValidOffsetForDataOfSize(*offset_ptr, length) )
+ {
+ data = m_start + *offset_ptr;
+ *offset_ptr += length;
+ }
+ return data;
+}
+
+
+//----------------------------------------------------------------------
+// Get_ULEB128
+//----------------------------------------------------------------------
+uint64_t
+DNBDataRef::Get_ULEB128 (offset_t *offset_ptr) const
+{
+ uint64_t result = 0;
+ if ( m_start < m_end )
+ {
+ int shift = 0;
+ const uint8_t *src = m_start + *offset_ptr;
+ uint8_t byte;
+ int bytecount = 0;
+
+ while (src < m_end)
+ {
+ bytecount++;
+ byte = *src++;
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ if ((byte & 0x80) == 0)
+ break;
+ }
+
+ *offset_ptr += bytecount;
+ }
+ return result;
+}
+
+
+//----------------------------------------------------------------------
+// Get_SLEB128
+//----------------------------------------------------------------------
+int64_t
+DNBDataRef::Get_SLEB128 (offset_t *offset_ptr) const
+{
+ int64_t result = 0;
+
+ if ( m_start < m_end )
+ {
+ int shift = 0;
+ int size = sizeof (uint32_t) * 8;
+ const uint8_t *src = m_start + *offset_ptr;
+
+ uint8_t byte = 0;
+ int bytecount = 0;
+
+ while (src < m_end)
+ {
+ bytecount++;
+ byte = *src++;
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ if ((byte & 0x80) == 0)
+ break;
+ }
+
+ // Sign bit of byte is 2nd high order bit (0x40)
+ if (shift < size && (byte & 0x40))
+ result |= - (1ll << shift);
+
+ *offset_ptr += bytecount;
+ }
+ return result;
+}
+
+
+//----------------------------------------------------------------------
+// Skip_LEB128
+//
+// Skips past ULEB128 and SLEB128 numbers (just updates the offset)
+//----------------------------------------------------------------------
+void
+DNBDataRef::Skip_LEB128 (offset_t *offset_ptr) const
+{
+ if ( m_start < m_end )
+ {
+ const uint8_t *start = m_start + *offset_ptr;
+ const uint8_t *src = start;
+
+ while ((src < m_end) && (*src++ & 0x80))
+ /* Do nothing */;
+
+ *offset_ptr += src - start;
+ }
+}
+
+uint32_t
+DNBDataRef::Dump
+(
+ uint32_t startOffset,
+ uint32_t endOffset,
+ uint64_t offsetBase,
+ DNBDataRef::Type type,
+ uint32_t numPerLine,
+ const char *format
+)
+{
+ uint32_t offset;
+ uint32_t count;
+ char str[1024];
+ str[0] = '\0';
+ size_t str_offset = 0;
+
+ for (offset = startOffset, count = 0; ValidOffset(offset) && offset < endOffset; ++count)
+ {
+ if ((count % numPerLine) == 0)
+ {
+ // Print out any previous string
+ if (str[0] != '\0')
+ DNBLog("%s", str);
+ // Reset string offset and fill the current line string with address:
+ str_offset = 0;
+ str_offset += snprintf(str, sizeof(str), "0x%8.8llx:", (uint64_t)(offsetBase + (offset - startOffset)));
+ }
+
+ // Make sure we don't pass the bounds of our current string buffer on each iteration through this loop
+ if (str_offset >= sizeof(str))
+ {
+ // The last snprintf consumed our string buffer, we will need to dump this out
+ // and reset the string with no address
+ DNBLog("%s", str);
+ str_offset = 0;
+ str[0] = '\0';
+ }
+
+ // We already checked that there is at least some room in the string str above, so it is safe to make
+ // the snprintf call each time through this loop
+ switch (type)
+ {
+ default:
+ case TypeUInt8: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %2.2x", Get8(&offset)); break;
+ case TypeChar:
+ {
+ char ch = Get8(&offset);
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %c", isprint(ch) ? ch : ' ');
+ }
+ break;
+ case TypeUInt16: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %4.4x", Get16(&offset)); break;
+ case TypeUInt32: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %8.8x", Get32(&offset)); break;
+ case TypeUInt64: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %16.16llx", Get64(&offset)); break;
+ case TypePointer: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " 0x%llx", GetPointer(&offset)); break;
+ case TypeULEB128: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " 0x%llx", Get_ULEB128(&offset)); break;
+ case TypeSLEB128: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %lld", Get_SLEB128(&offset)); break;
+ }
+ }
+
+ if (str[0] != '\0')
+ DNBLog("%s", str);
+
+ return offset; // Return the offset at which we ended up
+}
diff --git a/tools/debugserver/source/DNBDataRef.h b/tools/debugserver/source/DNBDataRef.h
new file mode 100644
index 000000000000..d0c34ced6233
--- /dev/null
+++ b/tools/debugserver/source/DNBDataRef.h
@@ -0,0 +1,125 @@
+//===-- DNBDataRef.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/11/06.
+//
+//===----------------------------------------------------------------------===//
+//
+// DNBDataRef is a class that can extract data in normal or byte
+// swapped order from a data buffer that someone else owns. The data
+// buffer needs to remain intact as long as the DNBDataRef object
+// needs the data. Strings returned are pointers into the data buffer
+// and will need to be copied if they are needed after the data buffer
+// is no longer around.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBDataRef_h__
+#define __DNBDataRef_h__
+
+#include "DNBDefs.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+class DNBDataRef
+{
+public:
+ // For use with Dump
+ typedef enum
+ {
+ TypeUInt8 = 0,
+ TypeChar,
+ TypeUInt16,
+ TypeUInt32,
+ TypeUInt64,
+ TypePointer,
+ TypeULEB128,
+ TypeSLEB128
+ } Type;
+ typedef uint32_t offset_t;
+ typedef nub_addr_t addr_t;
+
+ DNBDataRef();
+ DNBDataRef(const uint8_t *start, size_t size, bool swap);
+ ~DNBDataRef();
+ void Clear()
+ {
+ DNBDataRef::SetData(NULL, 0);
+ m_swap = false;
+ }
+
+ size_t BytesLeft (size_t offset) const
+ {
+ const size_t size = GetSize();
+ if (size > offset)
+ return size - offset;
+ return 0;
+ }
+
+ bool ValidOffset(offset_t offset) const
+ {
+ return BytesLeft(offset) > 0;
+ }
+ bool ValidOffsetForDataOfSize(offset_t offset, uint32_t num_bytes) const
+ {
+ return num_bytes <= BytesLeft (offset);
+ }
+ size_t GetSize() const { return m_end - m_start; }
+ const uint8_t * GetDataStart() const { return m_start; }
+ const uint8_t * GetDataEnd() const { return m_end; }
+ bool GetSwap() const { return m_swap; }
+ void SetSwap(bool swap) { m_swap = swap; }
+ void SetData(const uint8_t *start, size_t size)
+ {
+ m_start = start;
+ if (m_start != NULL)
+ m_end = start + size;
+ else
+ m_end = NULL;
+ }
+ uint8_t GetPointerSize() const { return m_ptrSize; }
+ void SetPointerSize(uint8_t size) { m_ptrSize = size; }
+ void SetEHPtrBaseAddrPCRelative(addr_t addr = INVALID_NUB_ADDRESS) { m_addrPCRelative = addr; }
+ void SetEHPtrBaseAddrTEXT(addr_t addr = INVALID_NUB_ADDRESS) { m_addrTEXT = addr; }
+ void SetEHPtrBaseAddrDATA(addr_t addr = INVALID_NUB_ADDRESS) { m_addrDATA = addr; }
+ uint8_t Get8(offset_t *offset_ptr) const;
+ uint16_t Get16(offset_t *offset_ptr) const;
+ uint32_t Get32(offset_t *offset_ptr) const;
+ uint64_t Get64(offset_t *offset_ptr) const;
+ uint32_t GetMax32(offset_t *offset_ptr, uint32_t byte_size) const;
+ uint64_t GetMax64(offset_t *offset_ptr, uint32_t byte_size) const;
+ uint64_t GetPointer(offset_t *offset_ptr) const;
+// uint64_t GetDwarfEHPtr(offset_t *offset_ptr, uint32_t eh_ptr_enc) const;
+ const char * GetCStr(offset_t *offset_ptr, uint32_t fixed_length = 0) const;
+ const char * PeekCStr(offset_t offset) const
+ {
+ if (ValidOffset(offset))
+ return (const char*)m_start + offset;
+ return NULL;
+ }
+
+ const uint8_t * GetData( offset_t *offset_ptr, uint32_t length) const;
+ uint64_t Get_ULEB128 (offset_t *offset_ptr) const;
+ int64_t Get_SLEB128 (offset_t *offset_ptr) const;
+ void Skip_LEB128 (offset_t *offset_ptr) const;
+
+ uint32_t Dump(offset_t startOffset, offset_t endOffset, uint64_t offsetBase, DNBDataRef::Type type, uint32_t numPerLine, const char *typeFormat = NULL);
+protected:
+ const uint8_t * m_start;
+ const uint8_t * m_end;
+ bool m_swap;
+ uint8_t m_ptrSize;
+ addr_t m_addrPCRelative;
+ addr_t m_addrTEXT;
+ addr_t m_addrDATA;
+};
+
+#endif // #ifndef __DNBDataRef_h__
diff --git a/tools/debugserver/source/DNBDefs.h b/tools/debugserver/source/DNBDefs.h
new file mode 100644
index 000000000000..e3757e903e59
--- /dev/null
+++ b/tools/debugserver/source/DNBDefs.h
@@ -0,0 +1,372 @@
+//===-- DNBDefs.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBDefs_h__
+#define __DNBDefs_h__
+
+#include <stdint.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/syslimits.h>
+#include <unistd.h>
+
+//----------------------------------------------------------------------
+// Define nub_addr_t and the invalid address value from the architecture
+//----------------------------------------------------------------------
+#if defined (__x86_64__) || defined (__ppc64__) || defined (__arm64__) || defined (__aarch64__)
+
+//----------------------------------------------------------------------
+// 64 bit address architectures
+//----------------------------------------------------------------------
+typedef uint64_t nub_addr_t;
+#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ull)
+
+#elif defined (__i386__) || defined (__powerpc__) || defined (__ppc__) || defined (__arm__)
+
+//----------------------------------------------------------------------
+// 32 bit address architectures
+//----------------------------------------------------------------------
+
+typedef uint32_t nub_addr_t;
+#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ul)
+
+#else
+
+//----------------------------------------------------------------------
+// Default to 64 bit address for unrecognized architectures.
+//----------------------------------------------------------------------
+
+#warning undefined architecture, defaulting to 8 byte addresses
+typedef uint64_t nub_addr_t;
+#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ull)
+
+
+#endif
+
+typedef size_t nub_size_t;
+typedef ssize_t nub_ssize_t;
+typedef uint32_t nub_index_t;
+typedef pid_t nub_process_t;
+typedef uint64_t nub_thread_t;
+typedef uint32_t nub_event_t;
+typedef uint32_t nub_bool_t;
+
+#define INVALID_NUB_PROCESS ((nub_process_t)0)
+#define INVALID_NUB_THREAD ((nub_thread_t)0)
+#define INVALID_NUB_WATCH_ID ((nub_watch_t)0)
+#define INVALID_NUB_HW_INDEX UINT32_MAX
+#define INVALID_NUB_REGNUM UINT32_MAX
+#define NUB_GENERIC_ERROR UINT32_MAX
+
+// Watchpoint types
+#define WATCH_TYPE_READ (1u << 0)
+#define WATCH_TYPE_WRITE (1u << 1)
+
+typedef enum
+{
+ eStateInvalid = 0,
+ eStateUnloaded,
+ eStateAttaching,
+ eStateLaunching,
+ eStateStopped,
+ eStateRunning,
+ eStateStepping,
+ eStateCrashed,
+ eStateDetached,
+ eStateExited,
+ eStateSuspended
+} nub_state_t;
+
+typedef enum
+{
+ eLaunchFlavorDefault = 0,
+ eLaunchFlavorPosixSpawn = 1,
+ eLaunchFlavorForkExec = 2,
+#ifdef WITH_SPRINGBOARD
+ eLaunchFlavorSpringBoard = 3,
+#endif
+#ifdef WITH_BKS
+ eLaunchFlavorBKS = 4,
+#endif
+#ifdef WITH_FBS
+ eLaunchFlavorFBS = 5
+#endif
+} nub_launch_flavor_t;
+
+#define NUB_STATE_IS_RUNNING(s) ((s) == eStateAttaching ||\
+ (s) == eStateLaunching ||\
+ (s) == eStateRunning ||\
+ (s) == eStateStepping ||\
+ (s) == eStateDetached)
+
+#define NUB_STATE_IS_STOPPED(s) ((s) == eStateUnloaded ||\
+ (s) == eStateStopped ||\
+ (s) == eStateCrashed ||\
+ (s) == eStateExited)
+
+enum
+{
+ eEventProcessRunningStateChanged = 1 << 0, // The process has changed state to running
+ eEventProcessStoppedStateChanged = 1 << 1, // The process has changed state to stopped
+ eEventSharedLibsStateChange = 1 << 2, // Shared libraries loaded/unloaded state has changed
+ eEventStdioAvailable = 1 << 3, // Something is available on stdout/stderr
+ eEventProfileDataAvailable = 1 << 4, // Profile data ready for retrieval
+ kAllEventsMask = eEventProcessRunningStateChanged |
+ eEventProcessStoppedStateChanged |
+ eEventSharedLibsStateChange |
+ eEventStdioAvailable |
+ eEventProfileDataAvailable
+};
+
+#define LOG_VERBOSE (1u << 0)
+#define LOG_PROCESS (1u << 1)
+#define LOG_THREAD (1u << 2)
+#define LOG_EXCEPTIONS (1u << 3)
+#define LOG_SHLIB (1u << 4)
+#define LOG_MEMORY (1u << 5) // Log memory reads/writes calls
+#define LOG_MEMORY_DATA_SHORT (1u << 6) // Log short memory reads/writes bytes
+#define LOG_MEMORY_DATA_LONG (1u << 7) // Log all memory reads/writes bytes
+#define LOG_MEMORY_PROTECTIONS (1u << 8) // Log memory protection changes
+#define LOG_BREAKPOINTS (1u << 9)
+#define LOG_EVENTS (1u << 10)
+#define LOG_WATCHPOINTS (1u << 11)
+#define LOG_STEP (1u << 12)
+#define LOG_TASK (1u << 13)
+#define LOG_LO_USER (1u << 16)
+#define LOG_HI_USER (1u << 31)
+#define LOG_ALL 0xFFFFFFFFu
+#define LOG_DEFAULT ((LOG_PROCESS) |\
+ (LOG_TASK) |\
+ (LOG_THREAD) |\
+ (LOG_EXCEPTIONS) |\
+ (LOG_SHLIB) |\
+ (LOG_MEMORY) |\
+ (LOG_BREAKPOINTS) |\
+ (LOG_WATCHPOINTS) |\
+ (LOG_STEP))
+
+
+#define REGISTER_SET_ALL 0
+// Generic Register set to be defined by each architecture for access to common
+// register values.
+#define REGISTER_SET_GENERIC ((uint32_t)0xFFFFFFFFu)
+#define GENERIC_REGNUM_PC 0 // Program Counter
+#define GENERIC_REGNUM_SP 1 // Stack Pointer
+#define GENERIC_REGNUM_FP 2 // Frame Pointer
+#define GENERIC_REGNUM_RA 3 // Return Address
+#define GENERIC_REGNUM_FLAGS 4 // Processor flags register
+#define GENERIC_REGNUM_ARG1 5 // The register that would contain pointer size or less argument 1 (if any)
+#define GENERIC_REGNUM_ARG2 6 // The register that would contain pointer size or less argument 2 (if any)
+#define GENERIC_REGNUM_ARG3 7 // The register that would contain pointer size or less argument 3 (if any)
+#define GENERIC_REGNUM_ARG4 8 // The register that would contain pointer size or less argument 4 (if any)
+#define GENERIC_REGNUM_ARG5 9 // The register that would contain pointer size or less argument 5 (if any)
+#define GENERIC_REGNUM_ARG6 10 // The register that would contain pointer size or less argument 6 (if any)
+#define GENERIC_REGNUM_ARG7 11 // The register that would contain pointer size or less argument 7 (if any)
+#define GENERIC_REGNUM_ARG8 12 // The register that would contain pointer size or less argument 8 (if any)
+
+enum DNBRegisterType
+{
+ InvalidRegType = 0,
+ Uint, // unsigned integer
+ Sint, // signed integer
+ IEEE754, // float
+ Vector // vector registers
+};
+
+enum DNBRegisterFormat
+{
+ InvalidRegFormat = 0,
+ Binary,
+ Decimal,
+ Hex,
+ Float,
+ VectorOfSInt8,
+ VectorOfUInt8,
+ VectorOfSInt16,
+ VectorOfUInt16,
+ VectorOfSInt32,
+ VectorOfUInt32,
+ VectorOfFloat32,
+ VectorOfUInt128
+};
+
+struct DNBRegisterInfo
+{
+ uint32_t set; // Register set
+ uint32_t reg; // Register number
+ const char *name; // Name of this register
+ const char *alt; // Alternate name
+ uint16_t type; // Type of the register bits (DNBRegisterType)
+ uint16_t format; // Default format for display (DNBRegisterFormat),
+ uint32_t size; // Size in bytes of the register
+ uint32_t offset; // Offset from the beginning of the register context
+ uint32_t reg_ehframe; // eh_frame register number (INVALID_NUB_REGNUM when none)
+ uint32_t reg_dwarf; // DWARF register number (INVALID_NUB_REGNUM when none)
+ uint32_t reg_generic; // Generic register number (INVALID_NUB_REGNUM when none)
+ uint32_t reg_debugserver;// The debugserver register number we'll use over gdb-remote protocol (INVALID_NUB_REGNUM when none)
+ const char **value_regs; // If this register is a part of other registers, list the register names terminated by NULL
+ const char **update_regs; // If modifying this register will invalidate other registers, list the register names terminated by NULL
+};
+
+struct DNBRegisterSetInfo
+{
+ const char *name; // Name of this register set
+ const struct DNBRegisterInfo *registers; // An array of register descriptions
+ nub_size_t num_registers; // The number of registers in REGISTERS array above
+};
+
+struct DNBThreadResumeAction
+{
+ nub_thread_t tid; // The thread ID that this action applies to, INVALID_NUB_THREAD for the default thread action
+ nub_state_t state; // Valid values are eStateStopped/eStateSuspended, eStateRunning, and eStateStepping.
+ int signal; // When resuming this thread, resume it with this signal
+ nub_addr_t addr; // If not INVALID_NUB_ADDRESS, then set the PC for the thread to ADDR before resuming/stepping
+};
+
+enum DNBThreadStopType
+{
+ eStopTypeInvalid = 0,
+ eStopTypeSignal,
+ eStopTypeException,
+ eStopTypeExec
+};
+
+enum DNBMemoryPermissions
+{
+ eMemoryPermissionsWritable = (1 << 0),
+ eMemoryPermissionsReadable = (1 << 1),
+ eMemoryPermissionsExecutable = (1 << 2)
+};
+
+#define DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH 256
+#define DNB_THREAD_STOP_INFO_MAX_EXC_DATA 8
+
+//----------------------------------------------------------------------
+// DNBThreadStopInfo
+//
+// Describes the reason a thread stopped.
+//----------------------------------------------------------------------
+struct DNBThreadStopInfo
+{
+ DNBThreadStopType reason;
+ char description[DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH];
+ union
+ {
+ // eStopTypeSignal
+ struct
+ {
+ uint32_t signo;
+ } signal;
+
+ // eStopTypeException
+ struct
+ {
+ uint32_t type;
+ nub_size_t data_count;
+ nub_addr_t data[DNB_THREAD_STOP_INFO_MAX_EXC_DATA];
+ } exception;
+ } details;
+};
+
+
+struct DNBRegisterValue
+{
+ struct DNBRegisterInfo info; // Register information for this register
+ union
+ {
+ int8_t sint8;
+ int16_t sint16;
+ int32_t sint32;
+ int64_t sint64;
+ uint8_t uint8;
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+ float float32;
+ double float64;
+ int8_t v_sint8[32];
+ int16_t v_sint16[16];
+ int32_t v_sint32[8];
+ int64_t v_sint64[4];
+ uint8_t v_uint8[32];
+ uint16_t v_uint16[16];
+ uint32_t v_uint32[8];
+ uint64_t v_uint64[4];
+ float v_float32[8];
+ double v_float64[4];
+ void *pointer;
+ char *c_str;
+ } value;
+};
+
+enum DNBSharedLibraryState
+{
+ eShlibStateUnloaded = 0,
+ eShlibStateLoaded = 1
+};
+
+#ifndef DNB_MAX_SEGMENT_NAME_LENGTH
+#define DNB_MAX_SEGMENT_NAME_LENGTH 32
+#endif
+
+struct DNBSegment
+{
+ char name[DNB_MAX_SEGMENT_NAME_LENGTH];
+ nub_addr_t addr;
+ nub_addr_t size;
+};
+
+struct DNBExecutableImageInfo
+{
+ char name[PATH_MAX]; // Name of the executable image (usually a full path)
+ uint32_t state; // State of the executable image (see enum DNBSharedLibraryState)
+ nub_addr_t header_addr; // Executable header address
+ uuid_t uuid; // Unique identifier for matching with symbols
+ uint32_t num_segments; // Number of contiguous memory segments to in SEGMENTS array
+ DNBSegment *segments; // Array of contiguous memory segments in executable
+};
+
+struct DNBRegionInfo
+{
+ nub_addr_t addr;
+ nub_addr_t size;
+ uint32_t permissions;
+};
+
+enum DNBProfileDataScanType
+{
+ eProfileHostCPU = (1 << 0),
+ eProfileCPU = (1 << 1),
+
+ eProfileThreadsCPU = (1 << 2), // By default excludes eProfileThreadName and eProfileQueueName.
+ eProfileThreadName = (1 << 3), // Assume eProfileThreadsCPU, get thread name as well.
+ eProfileQueueName = (1 << 4), // Assume eProfileThreadsCPU, get queue name as well.
+
+ eProfileHostMemory = (1 << 5),
+
+ eProfileMemory = (1 << 6), // By default, excludes eProfileMemoryDirtyPage.
+ eProfileMemoryDirtyPage = (1 << 7), // Assume eProfileMemory, get Dirty Page size as well.
+ eProfileMemoryAnonymous = (1 << 8), // Assume eProfileMemory, get Anonymous memory as well.
+
+ eProfileEnergy = (1 << 9),
+
+ eProfileAll = 0xffffffff
+};
+
+typedef nub_addr_t (*DNBCallbackNameToAddress)(nub_process_t pid, const char *name, const char *shlib_regex, void *baton);
+typedef nub_size_t (*DNBCallbackCopyExecutableImageInfos)(nub_process_t pid, struct DNBExecutableImageInfo **image_infos, nub_bool_t only_changed, void *baton);
+typedef void (*DNBCallbackLog)(void *baton, uint32_t flags, const char *format, va_list args);
+
+#define UNUSED_IF_ASSERT_DISABLED(x) ((void)(x))
+
+#endif // #ifndef __DNBDefs_h__
diff --git a/tools/debugserver/source/DNBError.cpp b/tools/debugserver/source/DNBError.cpp
new file mode 100644
index 000000000000..c9d8ebd58d89
--- /dev/null
+++ b/tools/debugserver/source/DNBError.cpp
@@ -0,0 +1,128 @@
+//===-- DNBError.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBError.h"
+#include "CFString.h"
+#include "DNBLog.h"
+#include "PThreadMutex.h"
+
+#ifdef WITH_SPRINGBOARD
+#include <SpringBoardServices/SpringBoardServer.h>
+#endif
+
+const char *
+DNBError::AsString() const
+{
+ if (Success())
+ return NULL;
+
+ if (m_str.empty())
+ {
+ const char *s = NULL;
+ switch (m_flavor)
+ {
+ case MachKernel:
+ s = ::mach_error_string (m_err);
+ break;
+
+ case POSIX:
+ s = ::strerror (m_err);
+ break;
+
+#ifdef WITH_SPRINGBOARD
+ case SpringBoard:
+ {
+ CFStringRef statusStr = SBSApplicationLaunchingErrorString (m_err);
+ if (CFString::UTF8 (statusStr, m_str) == NULL)
+ m_str.clear();
+ }
+ break;
+#endif
+#ifdef WITH_BKS
+ case BackBoard:
+ {
+ // You have to call ObjC routines to get the error string from BackBoardServices.
+ // Not sure I want to make DNBError.cpp an .mm file. For now just make sure you
+ // pre-populate the error string when you make the DNBError of type BackBoard.
+ m_str.assign("Should have set BackBoard error when making the error string.");
+ }
+ break;
+#endif
+#ifdef WITH_FBS
+ case FrontBoard:
+ {
+ // You have to call ObjC routines to get the error string from FrontBoardServices.
+ // Not sure I want to make DNBError.cpp an .mm file. For now just make sure you
+ // pre-populate the error string when you make the DNBError of type FrontBoard.
+ m_str.assign("Should have set FrontBoard error when making the error string.");
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ if (s)
+ m_str.assign(s);
+ }
+ if (m_str.empty())
+ return NULL;
+ return m_str.c_str();
+}
+
+void
+DNBError::LogThreadedIfError(const char *format, ...) const
+{
+ if (Fail())
+ {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ const char *err_str = AsString();
+ if (err_str == NULL)
+ err_str = "???";
+ DNBLogThreaded ("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_err);
+ free (arg_msg);
+ }
+ }
+}
+
+void
+DNBError::LogThreaded(const char *format, ...) const
+{
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ if (Fail())
+ {
+ const char *err_str = AsString();
+ if (err_str == NULL)
+ err_str = "???";
+ DNBLogThreaded ("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_err);
+ }
+ else
+ {
+ DNBLogThreaded ("%s err = 0x%8.8x", arg_msg, m_err);
+ }
+ free (arg_msg);
+ }
+}
diff --git a/tools/debugserver/source/DNBError.h b/tools/debugserver/source/DNBError.h
new file mode 100644
index 000000000000..274ae0d44773
--- /dev/null
+++ b/tools/debugserver/source/DNBError.h
@@ -0,0 +1,102 @@
+//===-- DNBError.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBError_h__
+#define __DNBError_h__
+
+#include <errno.h>
+#include <mach/mach.h>
+#include <stdio.h>
+#include <string>
+
+class DNBError
+{
+public:
+ typedef uint32_t ValueType;
+ typedef enum
+ {
+ Generic = 0,
+ MachKernel = 1,
+ POSIX = 2
+#ifdef WITH_SPRINGBOARD
+ , SpringBoard = 3
+#endif
+#ifdef WITH_BKS
+ , BackBoard = 4
+#endif
+#ifdef WITH_FBS
+ , FrontBoard = 5
+#endif
+ } FlavorType;
+
+ explicit DNBError( ValueType err = 0,
+ FlavorType flavor = Generic) :
+ m_err(err),
+ m_flavor(flavor)
+ {
+ }
+
+ const char * AsString() const;
+ void Clear() { m_err = 0; m_flavor = Generic; m_str.clear(); }
+ ValueType Error() const { return m_err; }
+ FlavorType Flavor() const { return m_flavor; }
+
+ ValueType operator = (kern_return_t err)
+ {
+ m_err = err;
+ m_flavor = MachKernel;
+ m_str.clear();
+ return m_err;
+ }
+
+ void SetError(kern_return_t err)
+ {
+ m_err = err;
+ m_flavor = MachKernel;
+ m_str.clear();
+ }
+
+ void SetErrorToErrno()
+ {
+ m_err = errno;
+ m_flavor = POSIX;
+ m_str.clear();
+ }
+
+ void SetError(ValueType err, FlavorType flavor)
+ {
+ m_err = err;
+ m_flavor = flavor;
+ m_str.clear();
+ }
+
+ // Generic errors can set their own string values
+ void SetErrorString(const char *err_str)
+ {
+ if (err_str && err_str[0])
+ m_str = err_str;
+ else
+ m_str.clear();
+ }
+ bool Success() const { return m_err == 0; }
+ bool Fail() const { return m_err != 0; }
+ void LogThreadedIfError(const char *format, ...) const;
+ void LogThreaded(const char *format, ...) const;
+protected:
+ ValueType m_err;
+ FlavorType m_flavor;
+ mutable std::string m_str;
+};
+
+
+#endif // #ifndef __DNBError_h__
diff --git a/tools/debugserver/source/DNBLog.cpp b/tools/debugserver/source/DNBLog.cpp
new file mode 100644
index 000000000000..18d8d2ad3a67
--- /dev/null
+++ b/tools/debugserver/source/DNBLog.cpp
@@ -0,0 +1,377 @@
+//===-- DNBLog.cpp ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBLog.h"
+
+static int g_debug = 0;
+static int g_verbose = 0;
+
+#if defined (DNBLOG_ENABLED)
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include "PThreadMutex.h"
+
+uint32_t g_log_bits = 0;
+static DNBCallbackLog g_log_callback = NULL;
+static void *g_log_baton = NULL;
+
+
+int
+DNBLogGetDebug ()
+{
+ return g_debug;
+}
+
+
+void
+DNBLogSetDebug (int g)
+{
+ g_debug = g;
+}
+
+int
+DNBLogGetVerbose ()
+{
+ return g_verbose;
+}
+
+void
+DNBLogSetVerbose (int v)
+{
+ g_verbose = v;
+}
+
+bool
+DNBLogCheckLogBit (uint32_t bit)
+{
+ return (g_log_bits & bit) != 0;
+}
+
+uint32_t
+DNBLogSetLogMask (uint32_t mask)
+{
+ uint32_t old = g_log_bits;
+ g_log_bits = mask;
+ return old;
+}
+
+uint32_t
+DNBLogGetLogMask ()
+{
+ return g_log_bits;
+}
+
+void
+DNBLogSetLogCallback (DNBCallbackLog callback, void *baton)
+{
+ g_log_callback = callback;
+ g_log_baton = baton;
+}
+
+DNBCallbackLog
+DNBLogGetLogCallback ()
+{
+ return g_log_callback;
+}
+
+bool
+DNBLogEnabled ()
+{
+ return g_log_callback != NULL;
+}
+
+bool
+DNBLogEnabledForAny (uint32_t mask)
+{
+ if (g_log_callback)
+ return (g_log_bits & mask) != 0;
+ return false;
+}
+static inline void
+_DNBLogVAPrintf(uint32_t flags, const char *format, va_list args)
+{
+ static PThreadMutex g_LogThreadedMutex(PTHREAD_MUTEX_RECURSIVE);
+ PTHREAD_MUTEX_LOCKER(locker, g_LogThreadedMutex);
+
+ if (g_log_callback)
+ g_log_callback(g_log_baton, flags, format, args);
+}
+
+void
+_DNBLog(uint32_t flags, const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ _DNBLogVAPrintf(flags, format, args);
+ va_end (args);
+}
+
+//----------------------------------------------------------------------
+// Print debug strings if and only if the global g_debug is set to
+// a non-zero value.
+//----------------------------------------------------------------------
+void
+_DNBLogDebug (const char *format, ...)
+{
+ if (DNBLogEnabled () && g_debug)
+ {
+ va_list args;
+ va_start (args, format);
+ _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG, format, args);
+ va_end (args);
+ }
+}
+
+
+//----------------------------------------------------------------------
+// Print debug strings if and only if the global g_debug is set to
+// a non-zero value.
+//----------------------------------------------------------------------
+void
+_DNBLogDebugVerbose (const char *format, ...)
+{
+ if (DNBLogEnabled () && g_debug && g_verbose)
+ {
+ va_list args;
+ va_start (args, format);
+ _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG | DNBLOG_FLAG_VERBOSE, format, args);
+ va_end (args);
+ }
+}
+
+
+static uint32_t g_message_id = 0;
+
+//----------------------------------------------------------------------
+// Prefix the formatted log string with process and thread IDs and
+// suffix it with a newline.
+//----------------------------------------------------------------------
+void
+_DNBLogThreaded (const char *format, ...)
+{
+ if (DNBLogEnabled ())
+ {
+ //PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex());
+
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ static struct timeval g_timeval = { 0 , 0 };
+ static struct timeval tv;
+ static struct timeval delta;
+ gettimeofday(&tv, NULL);
+ if (g_timeval.tv_sec == 0)
+ {
+ delta.tv_sec = 0;
+ delta.tv_usec = 0;
+ }
+ else
+ {
+ timersub (&tv, &g_timeval, &delta);
+ }
+ g_timeval = tv;
+
+ // Calling "mach_port_deallocate()" bumps the reference count on the thread
+ // port, so we need to deallocate it. mach_task_self() doesn't bump the ref
+ // count.
+ thread_port_t thread_self = mach_thread_self();
+
+ _DNBLog (DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s",
+ ++g_message_id,
+ delta.tv_sec,
+ delta.tv_usec,
+ getpid(),
+ thread_self,
+ arg_msg);
+
+ mach_port_deallocate(mach_task_self(), thread_self);
+ free (arg_msg);
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+// Prefix the formatted log string with process and thread IDs and
+// suffix it with a newline.
+//----------------------------------------------------------------------
+void
+_DNBLogThreadedIf (uint32_t log_bit, const char *format, ...)
+{
+ if (DNBLogEnabled () && (log_bit & g_log_bits) == log_bit)
+ {
+ //PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex());
+
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ static struct timeval g_timeval = { 0 , 0 };
+ static struct timeval tv;
+ static struct timeval delta;
+ gettimeofday(&tv, NULL);
+ if (g_timeval.tv_sec == 0)
+ {
+ delta.tv_sec = 0;
+ delta.tv_usec = 0;
+ }
+ else
+ {
+ timersub (&tv, &g_timeval, &delta);
+ }
+ g_timeval = tv;
+
+ // Calling "mach_port_deallocate()" bumps the reference count on the thread
+ // port, so we need to deallocate it. mach_task_self() doesn't bump the ref
+ // count.
+ thread_port_t thread_self = mach_thread_self();
+
+ _DNBLog (DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s",
+ ++g_message_id,
+ delta.tv_sec,
+ delta.tv_usec,
+ getpid(),
+ thread_self,
+ arg_msg);
+
+ mach_port_deallocate(mach_task_self(), thread_self);
+
+ free (arg_msg);
+ }
+ }
+}
+
+
+
+//----------------------------------------------------------------------
+// Printing of errors that are not fatal.
+//----------------------------------------------------------------------
+void
+_DNBLogError (const char *format, ...)
+{
+ if (DNBLogEnabled ())
+ {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ _DNBLog (DNBLOG_FLAG_ERROR, "error: %s", arg_msg);
+ free (arg_msg);
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+// Printing of errors that ARE fatal. Exit with ERR exit code
+// immediately.
+//----------------------------------------------------------------------
+void
+_DNBLogFatalError (int err, const char *format, ...)
+{
+ if (DNBLogEnabled ())
+ {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ _DNBLog (DNBLOG_FLAG_ERROR | DNBLOG_FLAG_FATAL, "error: %s", arg_msg);
+ free (arg_msg);
+ }
+ ::exit (err);
+ }
+}
+
+
+//----------------------------------------------------------------------
+// Printing of warnings that are not fatal only if verbose mode is
+// enabled.
+//----------------------------------------------------------------------
+void
+_DNBLogVerbose (const char *format, ...)
+{
+ if (DNBLogEnabled () && g_verbose)
+ {
+ va_list args;
+ va_start (args, format);
+ _DNBLogVAPrintf(DNBLOG_FLAG_VERBOSE, format, args);
+ va_end (args);
+ }
+}
+
+//----------------------------------------------------------------------
+// Printing of warnings that are not fatal only if verbose mode is
+// enabled.
+//----------------------------------------------------------------------
+void
+_DNBLogWarningVerbose (const char *format, ...)
+{
+ if (DNBLogEnabled () && g_verbose)
+ {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ _DNBLog (DNBLOG_FLAG_WARNING | DNBLOG_FLAG_VERBOSE, "warning: %s", arg_msg);
+ free (arg_msg);
+ }
+ }
+}
+//----------------------------------------------------------------------
+// Printing of warnings that are not fatal.
+//----------------------------------------------------------------------
+void
+_DNBLogWarning (const char *format, ...)
+{
+ if (DNBLogEnabled ())
+ {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start (args, format);
+ ::vasprintf (&arg_msg, format, args);
+ va_end (args);
+
+ if (arg_msg != NULL)
+ {
+ _DNBLog (DNBLOG_FLAG_WARNING, "warning: %s", arg_msg);
+ free (arg_msg);
+ }
+ }
+}
+
+#endif
diff --git a/tools/debugserver/source/DNBLog.h b/tools/debugserver/source/DNBLog.h
new file mode 100644
index 000000000000..01add065abcd
--- /dev/null
+++ b/tools/debugserver/source/DNBLog.h
@@ -0,0 +1,96 @@
+//===-- DNBLog.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBLog_h__
+#define __DNBLog_h__
+
+#include <stdio.h>
+#include <stdint.h>
+#include "DNBDefs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Flags that get filled in automatically before calling the log callback function
+#define DNBLOG_FLAG_FATAL (1u << 0)
+#define DNBLOG_FLAG_ERROR (1u << 1)
+#define DNBLOG_FLAG_WARNING (1u << 2)
+#define DNBLOG_FLAG_DEBUG (1u << 3)
+#define DNBLOG_FLAG_VERBOSE (1u << 4)
+#define DNBLOG_FLAG_THREADED (1u << 5)
+
+#define DNBLOG_ENABLED
+
+#if defined (DNBLOG_ENABLED)
+
+void _DNBLog(uint32_t flags, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
+void _DNBLogDebug (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+void _DNBLogDebugVerbose (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))) ;
+void _DNBLogThreaded (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+void _DNBLogThreadedIf (uint32_t mask, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+void _DNBLogError (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+void _DNBLogFatalError (int err, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+void _DNBLogVerbose (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+void _DNBLogWarning (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+void _DNBLogWarningVerbose (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+bool DNBLogCheckLogBit (uint32_t bit);
+uint32_t DNBLogSetLogMask (uint32_t mask);
+uint32_t DNBLogGetLogMask ();
+void DNBLogSetLogCallback (DNBCallbackLog callback, void *baton);
+DNBCallbackLog DNBLogGetLogCallback ();
+bool DNBLogEnabled ();
+bool DNBLogEnabledForAny (uint32_t mask);
+int DNBLogGetDebug ();
+void DNBLogSetDebug (int g);
+int DNBLogGetVerbose ();
+void DNBLogSetVerbose (int g);
+
+#define DNBLog(fmt, ...) do { if (DNBLogEnabled()) { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogDebug(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogDebug(fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogDebugVerbose(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogDebugVerbose(fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogThreaded(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogThreaded(fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogThreadedIf(mask, fmt, ...) do { if (DNBLogEnabledForAny(mask)) { _DNBLogThreaded(fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogError(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogError(fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogFatalError(err, fmt, ...) do { if (DNBLogEnabled()) { _DNBLogFatalError(err, fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogVerbose(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogVerbose(fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogWarning(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogWarning(fmt, ## __VA_ARGS__); } } while (0)
+#define DNBLogWarningVerbose(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogWarningVerbose(fmt, ## __VA_ARGS__); } } while (0)
+
+#else // #if defined(DNBLOG_ENABLED)
+
+#define DNBLogDebug(...) ((void)0)
+#define DNBLogDebugVerbose(...) ((void)0)
+#define DNBLogThreaded(...) ((void)0)
+#define DNBLogThreadedIf(...) ((void)0)
+#define DNBLogError(...) ((void)0)
+#define DNBLogFatalError(...) ((void)0)
+#define DNBLogVerbose(...) ((void)0)
+#define DNBLogWarning(...) ((void)0)
+#define DNBLogWarningVerbose(...) ((void)0)
+#define DNBLogGetLogFile() ((FILE *)NULL)
+#define DNBLogSetLogFile(f) ((void)0)
+#define DNBLogCheckLogBit(bit) ((bool)false)
+#define DNBLogSetLogMask(mask) ((uint32_t)0u)
+#define DNBLogGetLogMask() ((uint32_t)0u)
+#define DNBLogToASL() ((void)0)
+#define DNBLogToFile() ((void)0)
+#define DNBLogCloseLogFile() ((void)0)
+
+#endif // #else defined(DNBLOG_ENABLED)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // #ifndef __DNBLog_h__
diff --git a/tools/debugserver/source/DNBRegisterInfo.cpp b/tools/debugserver/source/DNBRegisterInfo.cpp
new file mode 100644
index 000000000000..acc7ba9946be
--- /dev/null
+++ b/tools/debugserver/source/DNBRegisterInfo.cpp
@@ -0,0 +1,219 @@
+//===-- DNBRegisterInfo.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 8/3/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBRegisterInfo.h"
+#include "DNBLog.h"
+#include <string.h>
+
+DNBRegisterValueClass::DNBRegisterValueClass(const DNBRegisterInfo *regInfo)
+{
+ Clear();
+ if (regInfo)
+ info = *regInfo;
+}
+
+void
+DNBRegisterValueClass::Clear()
+{
+ memset(&info, 0, sizeof(DNBRegisterInfo));
+ memset(&value, 0, sizeof(value));
+}
+
+bool
+DNBRegisterValueClass::IsValid() const
+{
+ return
+ info.name != NULL &&
+ info.type != InvalidRegType &&
+ info.size > 0 && info.size <= sizeof(value);
+}
+
+#define PRINT_COMMA_SEPARATOR do { if (pos < end) { if (i > 0) { strncpy(pos, ", ", end - pos); pos += 2; } } } while (0)
+
+void
+DNBRegisterValueClass::Dump(const char *pre, const char *post) const
+{
+ if (info.name != NULL)
+ {
+ char str[1024];
+ char *pos;
+ char *end = str + sizeof(str);
+ if (info.format == Hex)
+ {
+ switch (info.size)
+ {
+ case 0: snprintf(str, sizeof(str), "%s", "error: invalid register size of zero."); break;
+ case 1: snprintf(str, sizeof(str), "0x%2.2x", value.uint8); break;
+ case 2: snprintf(str, sizeof(str), "0x%4.4x", value.uint16); break;
+ case 4: snprintf(str, sizeof(str), "0x%8.8x", value.uint32); break;
+ case 8: snprintf(str, sizeof(str), "0x%16.16llx", value.uint64); break;
+ case 16: snprintf(str, sizeof(str), "0x%16.16llx%16.16llx", value.v_uint64[0], value.v_uint64[1]); break;
+ default:
+ strncpy(str, "0x", 3);
+ pos = str + 2;
+ for (uint32_t i=0; i<info.size; ++i)
+ {
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%2.2x", (uint32_t)value.v_uint8[i]);
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch (info.type)
+ {
+ case Uint:
+ switch (info.size)
+ {
+ case 1: snprintf(str, sizeof(str), "%u", value.uint8); break;
+ case 2: snprintf(str, sizeof(str), "%u", value.uint16); break;
+ case 4: snprintf(str, sizeof(str), "%u", value.uint32); break;
+ case 8: snprintf(str, sizeof(str), "%llu", value.uint64); break;
+ default: snprintf(str, sizeof(str), "error: unsupported uint byte size %d.", info.size); break;
+ }
+ break;
+
+ case Sint:
+ switch (info.size)
+ {
+ case 1: snprintf(str, sizeof(str), "%d", value.sint8); break;
+ case 2: snprintf(str, sizeof(str), "%d", value.sint16); break;
+ case 4: snprintf(str, sizeof(str), "%d", value.sint32); break;
+ case 8: snprintf(str, sizeof(str), "%lld", value.sint64); break;
+ default: snprintf(str, sizeof(str), "error: unsupported sint byte size %d.", info.size); break;
+ }
+ break;
+
+ case IEEE754:
+ switch (info.size)
+ {
+ case 4: snprintf(str, sizeof(str), "%f", value.float32); break;
+ case 8: snprintf(str, sizeof(str), "%g", value.float64); break;
+ default: snprintf(str, sizeof(str), "error: unsupported float byte size %d.", info.size); break;
+ }
+ break;
+
+ case Vector:
+ if (info.size > 0)
+ {
+ switch (info.format)
+ {
+ case VectorOfSInt8:
+ snprintf(str, sizeof(str), "%s", "sint8 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%d", (int32_t)value.v_sint8[i]);
+ }
+ strlcat(str, " }", sizeof(str));
+ break;
+
+ default:
+ DNBLogError("unsupported vector format %d, defaulting to hex bytes.", info.format);
+ case VectorOfUInt8:
+ snprintf(str, sizeof(str), "%s", "uint8 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%u", (uint32_t)value.v_uint8[i]);
+ }
+ break;
+
+ case VectorOfSInt16:
+ snprintf(str, sizeof(str), "%s", "sint16 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size/2; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%d", (int32_t)value.v_sint16[i]);
+ }
+ break;
+
+ case VectorOfUInt16:
+ snprintf(str, sizeof(str), "%s", "uint16 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size/2; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%u", (uint32_t)value.v_uint16[i]);
+ }
+ break;
+
+ case VectorOfSInt32:
+ snprintf(str, sizeof(str), "%s", "sint32 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size/4; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%d", (int32_t)value.v_sint32[i]);
+ }
+ break;
+
+ case VectorOfUInt32:
+ snprintf(str, sizeof(str), "%s", "uint32 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size/4; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%u", (uint32_t)value.v_uint32[i]);
+ }
+ break;
+
+ case VectorOfFloat32:
+ snprintf(str, sizeof(str), "%s", "float32 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size/4; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%f", value.v_float32[i]);
+ }
+ break;
+
+ case VectorOfUInt128:
+ snprintf(str, sizeof(str), "%s", "uint128 { ");
+ pos = str + strlen(str);
+ for (uint32_t i=0; i<info.size/16; ++i)
+ {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "0x%16.16llx%16.16llx", value.v_uint64[i], value.v_uint64[i+1]);
+ }
+ break;
+ }
+ strlcat(str, " }", sizeof(str));
+ }
+ else
+ {
+ snprintf(str, sizeof(str), "error: unsupported vector size %d.", info.size);
+ }
+ break;
+
+ default:
+ snprintf(str, sizeof(str), "error: unsupported register type %d.", info.type);
+ break;
+ }
+ }
+
+ DNBLog("%s%4s = %s%s", pre ? pre : "", info.name, str, post ? post : "");
+ }
+}
diff --git a/tools/debugserver/source/DNBRegisterInfo.h b/tools/debugserver/source/DNBRegisterInfo.h
new file mode 100644
index 000000000000..666c397e0b5e
--- /dev/null
+++ b/tools/debugserver/source/DNBRegisterInfo.h
@@ -0,0 +1,31 @@
+//===-- DNBRegisterInfo.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 8/3/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBRegisterInfo_h__
+#define __DNBRegisterInfo_h__
+
+#include <stdint.h>
+#include <stdio.h>
+#include "DNBDefs.h"
+
+struct DNBRegisterValueClass : public DNBRegisterValue
+{
+#ifdef __cplusplus
+ DNBRegisterValueClass(const DNBRegisterInfo *regInfo = NULL);
+ void Clear();
+ void Dump(const char *pre, const char *post) const;
+ bool IsValid() const;
+#endif
+};
+
+#endif
diff --git a/tools/debugserver/source/DNBRuntimeAction.h b/tools/debugserver/source/DNBRuntimeAction.h
new file mode 100644
index 000000000000..d77bda8c604d
--- /dev/null
+++ b/tools/debugserver/source/DNBRuntimeAction.h
@@ -0,0 +1,25 @@
+//===-- DNBRuntimeAction.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 10/8/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBRuntimeAction_h__
+#define __DNBRuntimeAction_h__
+
+class DNBRuntimeAction
+{
+ virtual void Initialize (nub_process_t pid) = 0;
+ virtual void ProcessStateChanged (nub_state_t state) = 0;
+ virtual void SharedLibraryStateChanged (DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos) = 0;
+};
+
+
+#endif // #ifndef __DNBRuntimeAction_h__
diff --git a/tools/debugserver/source/DNBThreadResumeActions.cpp b/tools/debugserver/source/DNBThreadResumeActions.cpp
new file mode 100644
index 000000000000..b50dd0617843
--- /dev/null
+++ b/tools/debugserver/source/DNBThreadResumeActions.cpp
@@ -0,0 +1,116 @@
+//===-- DNBThreadResumeActions.cpp ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 03/13/2010
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBThreadResumeActions.h"
+
+DNBThreadResumeActions::DNBThreadResumeActions() :
+ m_actions (),
+ m_signal_handled ()
+{
+}
+
+DNBThreadResumeActions::DNBThreadResumeActions (const DNBThreadResumeAction *actions, size_t num_actions) :
+ m_actions (),
+ m_signal_handled ()
+{
+ if (actions && num_actions)
+ {
+ m_actions.assign (actions, actions + num_actions);
+ m_signal_handled.assign (num_actions, false);
+ }
+}
+
+DNBThreadResumeActions::DNBThreadResumeActions (nub_state_t default_action, int signal) :
+ m_actions(),
+ m_signal_handled ()
+{
+ SetDefaultThreadActionIfNeeded (default_action, signal);
+}
+
+void
+DNBThreadResumeActions::Append (const DNBThreadResumeAction &action)
+{
+ m_actions.push_back (action);
+ m_signal_handled.push_back (false);
+}
+
+void
+DNBThreadResumeActions::AppendAction
+(
+ nub_thread_t tid,
+ nub_state_t state,
+ int signal,
+ nub_addr_t addr
+)
+{
+ DNBThreadResumeAction action = { tid, state, signal, addr };
+ Append (action);
+}
+
+
+const DNBThreadResumeAction *
+DNBThreadResumeActions::GetActionForThread (nub_thread_t tid, bool default_ok) const
+{
+ const size_t num_actions = m_actions.size();
+ for (size_t i=0; i<num_actions; ++i)
+ {
+ if (m_actions[i].tid == tid)
+ return &m_actions[i];
+ }
+ if (default_ok && tid != INVALID_NUB_THREAD)
+ return GetActionForThread (INVALID_NUB_THREAD, false);
+ return NULL;
+}
+
+size_t
+DNBThreadResumeActions::NumActionsWithState (nub_state_t state) const
+{
+ size_t count = 0;
+ const size_t num_actions = m_actions.size();
+ for (size_t i=0; i<num_actions; ++i)
+ {
+ if (m_actions[i].state == state)
+ ++count;
+ }
+ return count;
+}
+
+
+bool
+DNBThreadResumeActions::SetDefaultThreadActionIfNeeded (nub_state_t action, int signal)
+{
+ if (GetActionForThread (INVALID_NUB_THREAD, true) == NULL)
+ {
+ // There isn't a default action so we do need to set it.
+ DNBThreadResumeAction default_action = {INVALID_NUB_THREAD, action, signal, INVALID_NUB_ADDRESS };
+ m_actions.push_back (default_action);
+ m_signal_handled.push_back (false);
+ return true; // Return true as we did add the default action
+ }
+ return false;
+}
+
+
+void
+DNBThreadResumeActions::SetSignalHandledForThread (nub_thread_t tid) const
+{
+ if (tid != INVALID_NUB_THREAD)
+ {
+ const size_t num_actions = m_actions.size();
+ for (size_t i=0; i<num_actions; ++i)
+ {
+ if (m_actions[i].tid == tid)
+ m_signal_handled[i] = true;
+ }
+ }
+}
diff --git a/tools/debugserver/source/DNBThreadResumeActions.h b/tools/debugserver/source/DNBThreadResumeActions.h
new file mode 100644
index 000000000000..81c7c43b7222
--- /dev/null
+++ b/tools/debugserver/source/DNBThreadResumeActions.h
@@ -0,0 +1,102 @@
+//===-- DNBThreadResumeActions.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 03/13/2010
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __DNBThreadResumeActions_h__
+#define __DNBThreadResumeActions_h__
+
+#include <vector>
+
+#include "DNBDefs.h"
+
+
+class DNBThreadResumeActions
+{
+public:
+ DNBThreadResumeActions ();
+
+ DNBThreadResumeActions (nub_state_t default_action, int signal);
+
+ DNBThreadResumeActions (const DNBThreadResumeAction *actions, size_t num_actions);
+
+ bool
+ IsEmpty() const
+ {
+ return m_actions.empty();
+ }
+
+ void
+ Append (const DNBThreadResumeAction &action);
+
+ void
+ AppendAction (nub_thread_t tid,
+ nub_state_t state,
+ int signal = 0,
+ nub_addr_t addr = INVALID_NUB_ADDRESS);
+
+ void
+ AppendResumeAll ()
+ {
+ AppendAction (INVALID_NUB_THREAD, eStateRunning);
+ }
+
+ void
+ AppendSuspendAll ()
+ {
+ AppendAction (INVALID_NUB_THREAD, eStateStopped);
+ }
+
+ void
+ AppendStepAll ()
+ {
+ AppendAction (INVALID_NUB_THREAD, eStateStepping);
+ }
+
+ const DNBThreadResumeAction *
+ GetActionForThread (nub_thread_t tid, bool default_ok) const;
+
+ size_t
+ NumActionsWithState (nub_state_t state) const;
+
+ bool
+ SetDefaultThreadActionIfNeeded (nub_state_t action, int signal);
+
+ void
+ SetSignalHandledForThread (nub_thread_t tid) const;
+
+ const DNBThreadResumeAction *
+ GetFirst() const
+ {
+ return m_actions.data();
+ }
+
+ size_t
+ GetSize () const
+ {
+ return m_actions.size();
+ }
+
+ void
+ Clear()
+ {
+ m_actions.clear();
+ m_signal_handled.clear();
+ }
+
+protected:
+ std::vector<DNBThreadResumeAction> m_actions;
+ mutable std::vector<bool> m_signal_handled;
+};
+
+
+#endif // #ifndef __DNBThreadResumeActions_h__
diff --git a/tools/debugserver/source/DNBTimer.h b/tools/debugserver/source/DNBTimer.h
new file mode 100644
index 000000000000..ca56e30c7090
--- /dev/null
+++ b/tools/debugserver/source/DNBTimer.h
@@ -0,0 +1,163 @@
+//===-- DNBTimer.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/13/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBTimer_h__
+#define __DNBTimer_h__
+
+#include <sys/time.h>
+#include <stdint.h>
+#include <memory>
+#include "DNBDefs.h"
+#include "PThreadMutex.h"
+
+class DNBTimer
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ DNBTimer (bool threadSafe) :
+ m_mutexAP()
+ {
+ if (threadSafe)
+ m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE));
+ Reset();
+ }
+
+ DNBTimer (const DNBTimer& rhs) :
+ m_mutexAP()
+ {
+ // Create a new mutex to make this timer thread safe as well if
+ // the timer we are copying is thread safe
+ if (rhs.IsThreadSafe())
+ m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE));
+ m_timeval = rhs.m_timeval;
+ }
+
+ DNBTimer& operator= (const DNBTimer& rhs)
+ {
+ // Create a new mutex to make this timer thread safe as well if
+ // the timer we are copying is thread safe
+ if (rhs.IsThreadSafe())
+ m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE));
+ m_timeval = rhs.m_timeval;
+ return *this;
+ }
+
+ ~DNBTimer ()
+ {
+ }
+
+ bool
+ IsThreadSafe() const
+ {
+ return m_mutexAP.get() != NULL;
+ }
+ //------------------------------------------------------------------
+ // Reset the time value to now
+ //------------------------------------------------------------------
+ void
+ Reset ()
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get());
+ gettimeofday (&m_timeval, NULL);
+ }
+ //------------------------------------------------------------------
+ // Get the total mircoseconds since Jan 1, 1970
+ //------------------------------------------------------------------
+ uint64_t
+ TotalMicroSeconds () const
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get());
+ return (uint64_t)(m_timeval.tv_sec) * 1000000ull + (uint64_t)m_timeval.tv_usec;
+ }
+
+ void
+ GetTime (uint64_t& sec, uint32_t& usec) const
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get());
+ sec = m_timeval.tv_sec;
+ usec = m_timeval.tv_usec;
+ }
+ //------------------------------------------------------------------
+ // Return the number of microseconds elapsed between now and the
+ // m_timeval
+ //------------------------------------------------------------------
+ uint64_t
+ ElapsedMicroSeconds (bool update)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get());
+ struct timeval now;
+ gettimeofday (&now, NULL);
+ uint64_t now_usec = (uint64_t)(now.tv_sec) * 1000000ull + (uint64_t)now.tv_usec;
+ uint64_t this_usec = (uint64_t)(m_timeval.tv_sec) * 1000000ull + (uint64_t)m_timeval.tv_usec;
+ uint64_t elapsed = now_usec - this_usec;
+ // Update the timer time value if requeseted
+ if (update)
+ m_timeval = now;
+ return elapsed;
+ }
+
+ static uint64_t GetTimeOfDay()
+ {
+ struct timeval now;
+ gettimeofday (&now, NULL);
+ uint64_t now_usec = (uint64_t)(now.tv_sec) * 1000000ull + (uint64_t)now.tv_usec;
+ return now_usec;
+ }
+
+ static void OffsetTimeOfDay (struct timespec* ts, __darwin_time_t sec_offset = 0, long nsec_offset = 0)
+ {
+ if (ts == NULL)
+ return;
+ // Get the current time in a timeval structure
+ struct timeval now;
+ gettimeofday (&now, NULL);
+ // Morph it into a timespec
+ TIMEVAL_TO_TIMESPEC(&now, ts);
+ // Offset the timespec if requested
+ if (sec_offset != 0 || nsec_offset != 0)
+ {
+ // Offset the nano seconds
+ ts->tv_nsec += nsec_offset;
+ // Offset the seconds taking into account a nano-second overflow
+ ts->tv_sec = ts->tv_sec + ts->tv_nsec / 1000000000 + sec_offset;
+ // Trim the nanoseconds back there was an overflow
+ ts->tv_nsec = ts->tv_nsec % 1000000000;
+ }
+ }
+ static bool TimeOfDayLaterThan (struct timespec &ts)
+ {
+ struct timespec now;
+ OffsetTimeOfDay(&now);
+ if (now.tv_sec > ts.tv_sec)
+ return true;
+ else if (now.tv_sec < ts.tv_sec)
+ return false;
+ else
+ {
+ if (now.tv_nsec > ts.tv_nsec)
+ return true;
+ else
+ return false;
+ }
+ }
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from DNBTimer can see and modify these
+ //------------------------------------------------------------------
+ std::unique_ptr<PThreadMutex> m_mutexAP;
+ struct timeval m_timeval;
+};
+
+#endif // #ifndef __DNBTimer_h__
diff --git a/tools/debugserver/source/JSONGenerator.h b/tools/debugserver/source/JSONGenerator.h
new file mode 100644
index 000000000000..423b2bd57927
--- /dev/null
+++ b/tools/debugserver/source/JSONGenerator.h
@@ -0,0 +1,490 @@
+//===-- JSONGenerator.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __JSONGenerator_h_
+#define __JSONGenerator_h_
+
+// C Includes
+// C++ Includes
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+//----------------------------------------------------------------------
+/// @class JSONGenerator JSONGenerator.h
+/// @brief A class which can construct structured data for the sole purpose
+/// of printing it in JSON format.
+///
+/// A stripped down version of lldb's StructuredData objects which are much
+/// general purpose. This variant is intended only for assembling information
+/// and printing it as a JSON string.
+//----------------------------------------------------------------------
+
+class JSONGenerator
+{
+public:
+
+ class Object;
+ class Array;
+ class Integer;
+ class Float;
+ class Boolean;
+ class String;
+ class Dictionary;
+ class Generic;
+
+ typedef std::shared_ptr<Object> ObjectSP;
+ typedef std::shared_ptr<Array> ArraySP;
+ typedef std::shared_ptr<Integer> IntegerSP;
+ typedef std::shared_ptr<Float> FloatSP;
+ typedef std::shared_ptr<Boolean> BooleanSP;
+ typedef std::shared_ptr<String> StringSP;
+ typedef std::shared_ptr<Dictionary> DictionarySP;
+ typedef std::shared_ptr<Generic> GenericSP;
+
+ enum class Type
+ {
+ eTypeInvalid = -1,
+ eTypeNull = 0,
+ eTypeGeneric,
+ eTypeArray,
+ eTypeInteger,
+ eTypeFloat,
+ eTypeBoolean,
+ eTypeString,
+ eTypeDictionary
+ };
+
+ class Object :
+ public std::enable_shared_from_this<Object>
+ {
+ public:
+
+ Object (Type t = Type::eTypeInvalid) :
+ m_type (t)
+ {
+ }
+
+ virtual ~Object ()
+ {
+ }
+
+ virtual bool
+ IsValid() const
+ {
+ return true;
+ }
+
+ virtual void
+ Clear ()
+ {
+ m_type = Type::eTypeInvalid;
+ }
+
+ Type
+ GetType () const
+ {
+ return m_type;
+ }
+
+ void
+ SetType (Type t)
+ {
+ m_type = t;
+ }
+
+ Array *
+ GetAsArray ()
+ {
+ if (m_type == Type::eTypeArray)
+ return (Array *)this;
+ return NULL;
+ }
+
+ Dictionary *
+ GetAsDictionary ()
+ {
+ if (m_type == Type::eTypeDictionary)
+ return (Dictionary *)this;
+ return NULL;
+ }
+
+ Integer *
+ GetAsInteger ()
+ {
+ if (m_type == Type::eTypeInteger)
+ return (Integer *)this;
+ return NULL;
+ }
+
+ Float *
+ GetAsFloat ()
+ {
+ if (m_type == Type::eTypeFloat)
+ return (Float *)this;
+ return NULL;
+ }
+
+ Boolean *
+ GetAsBoolean ()
+ {
+ if (m_type == Type::eTypeBoolean)
+ return (Boolean *)this;
+ return NULL;
+ }
+
+ String *
+ GetAsString ()
+ {
+ if (m_type == Type::eTypeString)
+ return (String *)this;
+ return NULL;
+ }
+
+ Generic *
+ GetAsGeneric()
+ {
+ if (m_type == Type::eTypeGeneric)
+ return (Generic *)this;
+ return NULL;
+ }
+
+ virtual void
+ Dump (std::ostream &s) const = 0;
+
+ private:
+ Type m_type;
+ };
+
+ class Array : public Object
+ {
+ public:
+ Array () :
+ Object (Type::eTypeArray)
+ {
+ }
+
+ virtual
+ ~Array()
+ {
+ }
+
+ void
+ AddItem(ObjectSP item)
+ {
+ m_items.push_back(item);
+ }
+
+ void Dump(std::ostream &s) const override
+ {
+ s << "[";
+ const size_t arrsize = m_items.size();
+ for (size_t i = 0; i < arrsize; ++i)
+ {
+ m_items[i]->Dump(s);
+ if (i + 1 < arrsize)
+ s << ",";
+ }
+ s << "]";
+ }
+
+ protected:
+ typedef std::vector<ObjectSP> collection;
+ collection m_items;
+ };
+
+
+ class Integer : public Object
+ {
+ public:
+ Integer (uint64_t value = 0) :
+ Object (Type::eTypeInteger),
+ m_value (value)
+ {
+ }
+
+ virtual ~Integer()
+ {
+ }
+
+ void
+ SetValue (uint64_t value)
+ {
+ m_value = value;
+ }
+
+ void Dump(std::ostream &s) const override
+ {
+ s << m_value;
+ }
+
+ protected:
+ uint64_t m_value;
+ };
+
+ class Float : public Object
+ {
+ public:
+ Float (double d = 0.0) :
+ Object (Type::eTypeFloat),
+ m_value (d)
+ {
+ }
+
+ virtual ~Float()
+ {
+ }
+
+ void
+ SetValue (double value)
+ {
+ m_value = value;
+ }
+
+ void Dump(std::ostream &s) const override
+ {
+ s << m_value;
+ }
+
+ protected:
+ double m_value;
+ };
+
+ class Boolean : public Object
+ {
+ public:
+ Boolean (bool b = false) :
+ Object (Type::eTypeBoolean),
+ m_value (b)
+ {
+ }
+
+ virtual ~Boolean()
+ {
+ }
+
+ void
+ SetValue (bool value)
+ {
+ m_value = value;
+ }
+
+ void Dump(std::ostream &s) const override
+ {
+ if (m_value == true)
+ s << "true";
+ else
+ s << "false";
+ }
+
+ protected:
+ bool m_value;
+ };
+
+
+
+ class String : public Object
+ {
+ public:
+ String () :
+ Object (Type::eTypeString),
+ m_value ()
+ {
+ }
+
+ String (const std::string &s) :
+ Object (Type::eTypeString),
+ m_value (s)
+ {
+ }
+
+ String (const std::string &&s) :
+ Object (Type::eTypeString),
+ m_value (s)
+ {
+ }
+
+ void
+ SetValue (const std::string &string)
+ {
+ m_value = string;
+ }
+
+ void Dump(std::ostream &s) const override
+ {
+ std::string quoted;
+ const size_t strsize = m_value.size();
+ for (size_t i = 0; i < strsize ; ++i)
+ {
+ char ch = m_value[i];
+ if (ch == '"')
+ quoted.push_back ('\\');
+ quoted.push_back (ch);
+ }
+ s << '"' << quoted.c_str() << '"';
+ }
+
+ protected:
+ std::string m_value;
+ };
+
+ class Dictionary : public Object
+ {
+ public:
+ Dictionary () :
+ Object (Type::eTypeDictionary),
+ m_dict ()
+ {
+ }
+
+ virtual ~Dictionary()
+ {
+ }
+
+ void
+ AddItem (std::string key, ObjectSP value)
+ {
+ m_dict.push_back(Pair(key, value));
+ }
+
+ void
+ AddIntegerItem (std::string key, uint64_t value)
+ {
+ AddItem (key, ObjectSP (new Integer(value)));
+ }
+
+ void
+ AddFloatItem (std::string key, double value)
+ {
+ AddItem (key, ObjectSP (new Float(value)));
+ }
+
+ void
+ AddStringItem (std::string key, std::string value)
+ {
+ AddItem (key, ObjectSP (new String(std::move(value))));
+ }
+
+ void
+ AddBytesAsHexASCIIString (std::string key, const uint8_t *src, size_t src_len)
+ {
+ if (src && src_len)
+ {
+ std::ostringstream strm;
+ for (size_t i = 0; i < src_len; i++)
+ strm << std::setfill('0') << std::hex << std::right << std::setw(2) << ((uint32_t)(src[i]));
+ AddItem (key, ObjectSP (new String(std::move(strm.str()))));
+ }
+ else
+ {
+ AddItem (key, ObjectSP (new String()));
+ }
+ }
+
+ void
+ AddBooleanItem (std::string key, bool value)
+ {
+ AddItem (key, ObjectSP (new Boolean(value)));
+ }
+
+ void Dump(std::ostream &s) const override
+ {
+ bool have_printed_one_elem = false;
+ s << "{";
+ for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter)
+ {
+ if (have_printed_one_elem == false)
+ {
+ have_printed_one_elem = true;
+ }
+ else
+ {
+ s << ",";
+ }
+ s << "\"" << iter->first.c_str() << "\":";
+ iter->second->Dump(s);
+ }
+ s << "}";
+ }
+
+ protected:
+ // Keep the dictionary as a vector so the dictionary doesn't reorder itself when you dump it
+ // We aren't accessing keys by name, so this won't affect performance
+ typedef std::pair<std::string, ObjectSP> Pair;
+ typedef std::vector<Pair> collection;
+ collection m_dict;
+ };
+
+ class Null : public Object
+ {
+ public:
+ Null () :
+ Object (Type::eTypeNull)
+ {
+ }
+
+ virtual ~Null()
+ {
+ }
+
+ bool
+ IsValid() const override
+ {
+ return false;
+ }
+
+ void Dump(std::ostream &s) const override
+ {
+ s << "null";
+ }
+
+ protected:
+ };
+
+ class Generic : public Object
+ {
+ public:
+ explicit Generic(void *object = nullptr)
+ : Object(Type::eTypeGeneric)
+ , m_object(object)
+ {
+ }
+
+ void
+ SetValue(void *value)
+ {
+ m_object = value;
+ }
+
+ void *
+ GetValue() const
+ {
+ return m_object;
+ }
+
+ bool
+ IsValid() const override
+ {
+ return m_object != nullptr;
+ }
+
+ void Dump(std::ostream &s) const override;
+
+ private:
+ void *m_object;
+ };
+
+}; // class JSONGenerator
+
+
+
+#endif // __JSONGenerator_h_
diff --git a/tools/debugserver/source/MacOSX/CFBundle.cpp b/tools/debugserver/source/MacOSX/CFBundle.cpp
new file mode 100644
index 000000000000..fdcb7cc2fcb2
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CFBundle.cpp
@@ -0,0 +1,97 @@
+//===-- CFBundle.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+//----------------------------------------------------------------------
+// CFBundle constructor
+//----------------------------------------------------------------------
+CFBundle::CFBundle(const char *path) :
+ CFReleaser<CFBundleRef>(),
+ m_bundle_url()
+{
+ if (path && path[0])
+ SetPath(path);
+}
+
+//----------------------------------------------------------------------
+// CFBundle copy constructor
+//----------------------------------------------------------------------
+CFBundle::CFBundle(const CFBundle& rhs) :
+ CFReleaser<CFBundleRef>(rhs),
+ m_bundle_url(rhs.m_bundle_url)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFBundle copy constructor
+//----------------------------------------------------------------------
+CFBundle&
+CFBundle::operator=(const CFBundle& rhs)
+{
+ *this = rhs;
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFBundle::~CFBundle()
+{
+}
+
+//----------------------------------------------------------------------
+// Set the path for a bundle by supplying a
+//----------------------------------------------------------------------
+bool
+CFBundle::SetPath (const char *path)
+{
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ // Release our old bundle and ULR
+ reset(); // This class is a CFReleaser<CFBundleRef>
+ m_bundle_url.reset();
+ // Make a CFStringRef from the supplied path
+ CFString cf_path;
+ cf_path.SetFileSystemRepresentation(path);
+ if (cf_path.get())
+ {
+ // Make our Bundle URL
+ m_bundle_url.reset (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true));
+ if (m_bundle_url.get())
+ {
+ reset (::CFBundleCreate (alloc, m_bundle_url.get()));
+ }
+ }
+ return get() != NULL;
+}
+
+CFStringRef
+CFBundle::GetIdentifier () const
+{
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return ::CFBundleGetIdentifier (bundle);
+ return NULL;
+}
+
+
+CFURLRef
+CFBundle::CopyExecutableURL () const
+{
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return CFBundleCopyExecutableURL(bundle);
+ return NULL;
+}
diff --git a/tools/debugserver/source/MacOSX/CFBundle.h b/tools/debugserver/source/MacOSX/CFBundle.h
new file mode 100644
index 000000000000..e08290add731
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CFBundle.h
@@ -0,0 +1,43 @@
+//===-- CFBundle.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFBundle_h__
+#define __CFBundle_h__
+
+#include "CFUtils.h"
+
+class CFBundle : public CFReleaser<CFBundleRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFBundle(const char *path = NULL);
+ CFBundle(const CFBundle& rhs);
+ CFBundle& operator=(const CFBundle& rhs);
+ virtual
+ ~CFBundle();
+ bool
+ SetPath (const char *path);
+
+ CFStringRef
+ GetIdentifier () const;
+
+ CFURLRef
+ CopyExecutableURL () const;
+
+protected:
+ CFReleaser<CFURLRef> m_bundle_url;
+};
+
+#endif // #ifndef __CFBundle_h__
diff --git a/tools/debugserver/source/MacOSX/CFData.cpp b/tools/debugserver/source/MacOSX/CFData.cpp
new file mode 100644
index 000000000000..94c93da544a4
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CFData.cpp
@@ -0,0 +1,85 @@
+//===-- CFData.cpp ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFData.h"
+
+//----------------------------------------------------------------------
+// CFData constructor
+//----------------------------------------------------------------------
+CFData::CFData(CFDataRef data) :
+ CFReleaser<CFDataRef>(data)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFData copy constructor
+//----------------------------------------------------------------------
+CFData::CFData(const CFData& rhs) :
+ CFReleaser<CFDataRef>(rhs)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFData copy constructor
+//----------------------------------------------------------------------
+CFData&
+CFData::operator=(const CFData& rhs)
+
+{
+ *this = rhs;
+ return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFData::~CFData()
+{
+}
+
+
+CFIndex
+CFData::GetLength() const
+{
+ CFDataRef data = get();
+ if (data)
+ return CFDataGetLength (data);
+ return 0;
+}
+
+
+const uint8_t*
+CFData::GetBytePtr() const
+{
+ CFDataRef data = get();
+ if (data)
+ return CFDataGetBytePtr (data);
+ return NULL;
+}
+
+CFDataRef
+CFData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format)
+{
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ reset();
+ CFReleaser<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/tools/debugserver/source/MacOSX/CFData.h b/tools/debugserver/source/MacOSX/CFData.h
new file mode 100644
index 000000000000..2c9d65d3af72
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CFData.h
@@ -0,0 +1,39 @@
+//===-- CFData.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFData_h__
+#define __CFData_h__
+
+#include "CFUtils.h"
+
+class CFData : public CFReleaser<CFDataRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFData(CFDataRef data = NULL);
+ CFData(const CFData& rhs);
+ CFData& operator=(const CFData& rhs);
+ virtual ~CFData();
+
+ CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format);
+ const uint8_t* GetBytePtr () const;
+ CFIndex GetLength () const;
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from CFData can see and modify these
+ //------------------------------------------------------------------
+};
+
+#endif // #ifndef __CFData_h__
diff --git a/tools/debugserver/source/MacOSX/CFString.cpp b/tools/debugserver/source/MacOSX/CFString.cpp
new file mode 100644
index 000000000000..819024ca3bcb
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CFString.cpp
@@ -0,0 +1,201 @@
+//===-- CFString.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFString.h"
+#include <string>
+#include <glob.h>
+
+//----------------------------------------------------------------------
+// CFString constructor
+//----------------------------------------------------------------------
+CFString::CFString(CFStringRef s) :
+ CFReleaser<CFStringRef> (s)
+{
+}
+
+//----------------------------------------------------------------------
+// CFString copy constructor
+//----------------------------------------------------------------------
+CFString::CFString(const CFString& rhs) :
+ CFReleaser<CFStringRef> (rhs)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFString copy constructor
+//----------------------------------------------------------------------
+CFString&
+CFString::operator=(const CFString& rhs)
+{
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+CFString::CFString (const char *cstr, CFStringEncoding cstr_encoding) :
+ CFReleaser<CFStringRef> ()
+{
+ if (cstr && cstr[0])
+ {
+ reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
+ }
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFString::~CFString()
+{
+}
+
+const char *
+CFString::GetFileSystemRepresentation(std::string& s)
+{
+ return CFString::FileSystemRepresentation(get(), s);
+}
+
+CFStringRef
+CFString::SetFileSystemRepresentation (const char *path)
+{
+ CFStringRef new_value = NULL;
+ if (path && path[0])
+ new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path);
+ reset(new_value);
+ return get();
+}
+
+
+CFStringRef
+CFString::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
+CFString::SetFileSystemRepresentationAndExpandTilde (const char *path)
+{
+ std::string expanded_path;
+ if (CFString::GlobPath(path, expanded_path))
+ SetFileSystemRepresentation(expanded_path.c_str());
+ else
+ reset();
+ return get();
+}
+
+const char *
+CFString::UTF8(std::string& str)
+{
+ return CFString::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 *
+CFString::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;
+}
+
+// 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 *
+CFString::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
+CFString::GetLength() const
+{
+ CFStringRef str = get();
+ if (str)
+ return CFStringGetLength (str);
+ return 0;
+}
+
+
+const char*
+CFString::GlobPath(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();
+}
+
diff --git a/tools/debugserver/source/MacOSX/CFString.h b/tools/debugserver/source/MacOSX/CFString.h
new file mode 100644
index 000000000000..73945a28a658
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CFString.h
@@ -0,0 +1,43 @@
+//===-- CFString.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFString_h__
+#define __CFString_h__
+
+#include "CFUtils.h"
+#include <iosfwd>
+
+class CFString : public CFReleaser<CFStringRef>
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ CFString (CFStringRef cf_str = NULL);
+ CFString (const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8);
+ CFString (const CFString& rhs);
+ CFString& operator= (const CFString& rhs);
+ virtual ~CFString ();
+
+ 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* GlobPath(const char* path, std::string &expanded_path);
+};
+
+#endif // #ifndef __CFString_h__
diff --git a/tools/debugserver/source/MacOSX/CFUtils.h b/tools/debugserver/source/MacOSX/CFUtils.h
new file mode 100644
index 000000000000..afa984fa11cd
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CFUtils.h
@@ -0,0 +1,81 @@
+//===-- CFUtils.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/5/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFUtils_h__
+#define __CFUtils_h__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#ifdef __cplusplus
+
+//----------------------------------------------------------------------
+// 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.
+//----------------------------------------------------------------------
+template <class T>
+class CFReleaser
+{
+public:
+ // Type names for the avlue
+ typedef T element_type;
+
+ // Constructors and destructors
+ CFReleaser(T ptr = NULL) : _ptr(ptr) { }
+ CFReleaser(const CFReleaser& copy) : _ptr(copy.get())
+ {
+ if (get())
+ ::CFRetain(get());
+ }
+ virtual ~CFReleaser() { reset(); }
+
+ // Assignments
+ CFReleaser& operator= (const CFReleaser<T>& copy)
+ {
+ if (copy != *this)
+ {
+ // Replace our owned pointer with the new one
+ reset(copy.get());
+ // Retain the current pointer that we own
+ if (get())
+ ::CFRetain(get());
+ }
+ }
+ // Get the address of the contained type
+ T * ptr_address() { return &_ptr; }
+
+ // Access the pointer itself
+ const T get() const { return _ptr; }
+ T get() { 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 != _ptr)
+ {
+ if (_ptr != NULL)
+ ::CFRelease(_ptr);
+ _ptr = ptr;
+ }
+ }
+
+ // Release ownership without calling CFRelease
+ T release() { T tmp = _ptr; _ptr = NULL; return tmp; }
+private:
+ element_type _ptr;
+};
+
+#endif // #ifdef __cplusplus
+#endif // #ifndef __CFUtils_h__
+
diff --git a/tools/debugserver/source/MacOSX/CMakeLists.txt b/tools/debugserver/source/MacOSX/CMakeLists.txt
new file mode 100644
index 000000000000..d319cb7b0bab
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/CMakeLists.txt
@@ -0,0 +1,89 @@
+#add_subdirectory(arm64)
+#add_subdirectory(arm)
+add_subdirectory(i386)
+#add_subdirectory(ppc)
+add_subdirectory(x86_64)
+
+include_directories(..)
+
+set(generated_mach_interfaces
+ ${CMAKE_CURRENT_BINARY_DIR}/mach_exc.h
+ ${CMAKE_CURRENT_BINARY_DIR}/mach_excServer.c
+ ${CMAKE_CURRENT_BINARY_DIR}/mach_excUser.c
+ )
+add_custom_command(OUTPUT ${generated_mach_interfaces}
+ COMMAND mig ${CMAKE_CURRENT_SOURCE_DIR}/dbgnub-mig.defs
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/dbgnub-mig.defs
+ )
+
+set(DEBUGSERVER_VERS_GENERATED_FILE ${CMAKE_CURRENT_BINARY_DIR}/debugserver_vers.c)
+set_source_files_properties(${DEBUGSERVER_VERS_GENERATED_FILE} PROPERTIES GENERATED 1)
+
+add_custom_command(OUTPUT ${DEBUGSERVER_VERS_GENERATED_FILE}
+ COMMAND ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl
+ ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj debugserver
+ > ${DEBUGSERVER_VERS_GENERATED_FILE}
+ DEPENDS ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl
+ ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj
+ )
+
+set(DEBUGSERVER_USED_LIBS
+ lldbDebugserverCommon
+ lldbUtility
+ lldbDebugserverMacOSX_I386
+ lldbDebugserverMacOSX_X86_64
+ )
+
+add_lldb_executable(debugserver
+ HasAVX.s
+ CFBundle.cpp
+ CFData.cpp
+ CFString.cpp
+ Genealogy.cpp
+ MachException.cpp
+ MachProcess.mm
+ MachTask.mm
+ MachThread.cpp
+ MachThreadList.cpp
+ MachVMMemory.cpp
+ MachVMRegion.cpp
+ ${generated_mach_interfaces}
+ ${DEBUGSERVER_VERS_GENERATED_FILE}
+ )
+
+set_source_files_properties(
+ HasAVX.s
+ # Necessary since compilation will fail with stand-alone assembler
+ PROPERTIES LANGUAGE C COMPILE_FLAGS "-x assembler-with-cpp"
+ )
+
+target_link_libraries(debugserver ${DEBUGSERVER_USED_LIBS})
+
+# Sign the debugserver binary
+set (CODESIGN_IDENTITY lldb_codesign)
+execute_process(
+ COMMAND xcrun -f codesign_allocate
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ OUTPUT_VARIABLE CODESIGN_ALLOCATE
+ )
+# Older cmake versions don't support "-E env".
+if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.2)
+ add_custom_command(TARGET debugserver
+ POST_BUILD
+ # Note: --entitlements option removed, as it causes errors when debugging.
+ # was: COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --entitlements ${CMAKE_CURRENT_SOURCE_DIR}/../debugserver-entitlements.plist --force --sign ${CODESIGN_IDENTITY} debugserver
+ COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${CODESIGN_IDENTITY} debugserver
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin
+ )
+else()
+ add_custom_command(TARGET debugserver
+ POST_BUILD
+ # Note: --entitlements option removed (see comment above).
+ COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${CODESIGN_IDENTITY} debugserver
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin
+ )
+endif()
+
+install(TARGETS debugserver
+ RUNTIME DESTINATION bin
+ )
diff --git a/tools/debugserver/source/MacOSX/Genealogy.cpp b/tools/debugserver/source/MacOSX/Genealogy.cpp
new file mode 100644
index 000000000000..a5ee097aa2a6
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/Genealogy.cpp
@@ -0,0 +1,300 @@
+///===-- Activity.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <Availability.h>
+#include <string>
+#include <dlfcn.h>
+#include <uuid/uuid.h>
+
+#include "DNBDefs.h"
+#include "Genealogy.h"
+#include "GenealogySPI.h"
+#include "MachThreadList.h"
+
+//---------------------------
+/// Constructor
+//---------------------------
+
+Genealogy::Genealogy () :
+ m_os_activity_diagnostic_for_pid (nullptr),
+ m_os_activity_iterate_processes (nullptr),
+ m_os_activity_iterate_breadcrumbs (nullptr),
+ m_os_activity_iterate_messages (nullptr),
+ m_os_activity_iterate_activities (nullptr),
+ m_os_trace_get_type (nullptr),
+ m_os_trace_copy_formatted_message (nullptr),
+ m_os_activity_for_thread (nullptr),
+ m_os_activity_for_task_thread (nullptr),
+ m_thread_activities(),
+ m_process_executable_infos(),
+ m_diagnosticd_call_timed_out(false)
+{
+ m_os_activity_diagnostic_for_pid = (bool (*)(pid_t, os_activity_t, uint32_t, os_diagnostic_block_t))dlsym (RTLD_DEFAULT, "os_activity_diagnostic_for_pid");
+ m_os_activity_iterate_processes = (void (*)(os_activity_process_list_t, bool (^)(os_activity_process_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_processes");
+ m_os_activity_iterate_breadcrumbs = (void (*)(os_activity_process_t, bool (^)(os_activity_breadcrumb_t))) dlsym (RTLD_DEFAULT, "os_activity_iterate_breadcrumbs");
+ m_os_activity_iterate_messages = (void (*)(os_trace_message_list_t, os_activity_process_t, bool (^)(os_trace_message_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_messages");
+ m_os_activity_iterate_activities = (void (*)(os_activity_list_t, os_activity_process_t, bool (^)(os_activity_entry_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_activities");
+ m_os_trace_get_type = (uint8_t (*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_get_type");
+ m_os_trace_copy_formatted_message = (char *(*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_copy_formatted_message");
+ m_os_activity_for_thread = (os_activity_t (*)(os_activity_process_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_thread");
+ m_os_activity_for_task_thread = (os_activity_t (*)(task_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_task_thread");
+ m_os_activity_messages_for_thread = (os_trace_message_list_t (*) (os_activity_process_t process, os_activity_t activity, uint64_t thread_id)) dlsym (RTLD_DEFAULT, "os_activity_messages_for_thread");
+}
+
+Genealogy::ThreadActivitySP
+Genealogy::GetGenealogyInfoForThread (pid_t pid, nub_thread_t tid, const MachThreadList &thread_list, task_t task, bool &timed_out)
+{
+ ThreadActivitySP activity;
+ //
+ // if we've timed out trying to get the activities, don't try again at this process stop.
+ // (else we'll need to hit the timeout for every thread we're asked about.)
+ // We'll try again at the next public stop.
+
+ if (m_thread_activities.size() == 0 && m_diagnosticd_call_timed_out == false)
+ {
+ GetActivities(pid, thread_list, task);
+ }
+ std::map<nub_thread_t, ThreadActivitySP>::const_iterator search;
+ search = m_thread_activities.find(tid);
+ if (search != m_thread_activities.end())
+ {
+ activity = search->second;
+ }
+ timed_out = m_diagnosticd_call_timed_out;
+ return activity;
+}
+
+void
+Genealogy::Clear()
+{
+ m_thread_activities.clear();
+ m_diagnosticd_call_timed_out = false;
+}
+
+void
+Genealogy::GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task)
+{
+ if (m_os_activity_diagnostic_for_pid != nullptr
+ && m_os_activity_iterate_processes != nullptr
+ && m_os_activity_iterate_breadcrumbs != nullptr
+ && m_os_activity_iterate_messages != nullptr
+ && m_os_activity_iterate_activities != nullptr
+ && m_os_trace_get_type != nullptr
+ && m_os_trace_copy_formatted_message != nullptr
+ && (m_os_activity_for_thread != nullptr || m_os_activity_for_task_thread != nullptr)
+ )
+ {
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block BreadcrumbList breadcrumbs;
+ __block ActivityList activities;
+ __block MessageList messages;
+ __block std::map<nub_thread_t, uint64_t> thread_activity_mapping;
+
+ os_activity_diagnostic_flag_t flags = OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES | OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY;
+ if (m_os_activity_diagnostic_for_pid (pid, 0, flags, ^(os_activity_process_list_t processes, int error)
+ {
+ if (error == 0)
+ {
+ m_os_activity_iterate_processes (processes, ^bool(os_activity_process_t process_info)
+ {
+ if (pid == process_info->pid)
+ {
+ // Collect all the Breadcrumbs
+ m_os_activity_iterate_breadcrumbs (process_info, ^bool(os_activity_breadcrumb_t breadcrumb)
+ {
+ Breadcrumb bc;
+ bc.breadcrumb_id = breadcrumb->breadcrumb_id;
+ bc.activity_id = breadcrumb->activity_id;
+ bc.timestamp = breadcrumb->timestamp;
+ if (breadcrumb->name)
+ bc.name = breadcrumb->name;
+ breadcrumbs.push_back (bc);
+ return true;
+ });
+
+ // Collect all the Activites
+ m_os_activity_iterate_activities (process_info->activities, process_info, ^bool(os_activity_entry_t activity)
+ {
+ Activity ac;
+ ac.activity_start = activity->activity_start;
+ ac.activity_id = activity->activity_id;
+ ac.parent_id = activity->parent_id;
+ if (activity->activity_name)
+ ac.activity_name = activity->activity_name;
+ if (activity->reason)
+ ac.reason = activity->reason;
+ activities.push_back (ac);
+ return true;
+ });
+
+
+ // Collect all the Messages -- messages not associated with any thread
+ m_os_activity_iterate_messages (process_info->messages, process_info, ^bool(os_trace_message_t trace_msg)
+ {
+ Message msg;
+ msg.timestamp = trace_msg->timestamp;
+ msg.trace_id = trace_msg->trace_id;
+ msg.thread = trace_msg->thread;
+ msg.type = m_os_trace_get_type (trace_msg);
+ msg.activity_id = 0;
+ if (trace_msg->image_uuid && trace_msg->image_path)
+ {
+ ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo());
+ uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid);
+ process_info_sp->image_path = trace_msg->image_path;
+ msg.process_info_index = AddProcessExecutableInfo (process_info_sp);
+ }
+ const char *message_text = m_os_trace_copy_formatted_message (trace_msg);
+ if (message_text)
+ msg.message = message_text;
+ messages.push_back (msg);
+ return true;
+ });
+
+ // Discover which activities are said to be running on threads currently
+ const nub_size_t num_threads = thread_list.NumThreads();
+ for (nub_size_t i = 0; i < num_threads; ++i)
+ {
+ nub_thread_t thread_id = thread_list.ThreadIDAtIndex(i);
+ os_activity_t act = 0;
+ if (m_os_activity_for_task_thread != nullptr)
+ {
+ act = m_os_activity_for_task_thread (task, thread_id);
+ }
+ else if (m_os_activity_for_thread != nullptr)
+ {
+ act = m_os_activity_for_thread (process_info, thread_id);
+ }
+ if (act != 0)
+ thread_activity_mapping[thread_id] = act;
+ }
+
+ // Collect all Messages -- messages associated with a thread
+
+ // When there's no genealogy information, an early version of os_activity_messages_for_thread
+ // can crash in rare circumstances. Check to see if this process has any activities before
+ // making the call to get messages.
+ if (process_info->activities != nullptr && thread_activity_mapping.size() > 0)
+ {
+ std::map<nub_thread_t, uint64_t>::const_iterator iter;
+ for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter)
+ {
+ nub_thread_t thread_id = iter->first;
+ os_activity_t act = iter->second;
+ os_trace_message_list_t this_thread_messages = m_os_activity_messages_for_thread (process_info, act, thread_id);
+ m_os_activity_iterate_messages (this_thread_messages, process_info, ^bool(os_trace_message_t trace_msg)
+ {
+ Message msg;
+ msg.timestamp = trace_msg->timestamp;
+ msg.trace_id = trace_msg->trace_id;
+ msg.thread = trace_msg->thread;
+ msg.type = m_os_trace_get_type (trace_msg);
+ msg.activity_id = act;
+ if (trace_msg->image_uuid && trace_msg->image_path)
+ {
+ ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo());
+ uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid);
+ process_info_sp->image_path = trace_msg->image_path;
+ msg.process_info_index = AddProcessExecutableInfo (process_info_sp);
+ }
+ const char *message_text = m_os_trace_copy_formatted_message (trace_msg);
+ if (message_text)
+ msg.message = message_text;
+ messages.push_back (msg);
+ return true;
+ });
+ }
+ }
+ }
+ return true;
+ });
+ }
+ dispatch_semaphore_signal(semaphore);
+ }) == true)
+ {
+ // Wait for the diagnosticd xpc calls to all finish up -- or half a second to elapse.
+ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
+ bool success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+ if (!success)
+ {
+ m_diagnosticd_call_timed_out = true;
+ return;
+ }
+ }
+
+ // breadcrumbs, activities, and messages have all now been filled in.
+
+ std::map<nub_thread_t, uint64_t>::const_iterator iter;
+ for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter)
+ {
+ nub_thread_t thread_id = iter->first;
+ uint64_t activity_id = iter->second;
+ ActivityList::const_iterator activity_search;
+ for (activity_search = activities.begin(); activity_search != activities.end(); ++activity_search)
+ {
+ if (activity_search->activity_id == activity_id)
+ {
+ ThreadActivitySP thread_activity_sp (new ThreadActivity());
+ thread_activity_sp->current_activity = *activity_search;
+
+ BreadcrumbList::const_iterator breadcrumb_search;
+ for (breadcrumb_search = breadcrumbs.begin(); breadcrumb_search != breadcrumbs.end(); ++breadcrumb_search)
+ {
+ if (breadcrumb_search->activity_id == activity_id)
+ {
+ thread_activity_sp->breadcrumbs.push_back (*breadcrumb_search);
+ }
+ }
+ MessageList::const_iterator message_search;
+ for (message_search = messages.begin(); message_search != messages.end(); ++message_search)
+ {
+ if (message_search->thread == thread_id)
+ {
+ thread_activity_sp->messages.push_back (*message_search);
+ }
+ }
+
+ m_thread_activities[thread_id] = thread_activity_sp;
+ break;
+ }
+ }
+ }
+ }
+}
+
+uint32_t
+Genealogy::AddProcessExecutableInfo (ProcessExecutableInfoSP process_exe_info)
+{
+ const uint32_t info_size = static_cast<uint32_t>(m_process_executable_infos.size());
+ for (uint32_t idx = 0; idx < info_size; ++idx)
+ {
+ if (uuid_compare (m_process_executable_infos[idx]->image_uuid, process_exe_info->image_uuid) == 0)
+ {
+ return idx + 1;
+ }
+ }
+ m_process_executable_infos.push_back (process_exe_info);
+ return info_size + 1;
+}
+
+Genealogy::ProcessExecutableInfoSP
+Genealogy::GetProcessExecutableInfosAtIndex(size_t idx)
+{
+ ProcessExecutableInfoSP info_sp;
+ if (idx > 0)
+ {
+ idx--;
+ if (idx <= m_process_executable_infos.size())
+ {
+ info_sp = m_process_executable_infos[idx];
+ }
+ }
+ return info_sp;
+}
+
diff --git a/tools/debugserver/source/MacOSX/Genealogy.h b/tools/debugserver/source/MacOSX/Genealogy.h
new file mode 100644
index 000000000000..d39145a06f29
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/Genealogy.h
@@ -0,0 +1,116 @@
+//===-- Activity.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __Genealogy_h__
+#define __Genealogy_h__
+
+#include <string>
+#include <vector>
+#include <map>
+#include <pthread.h>
+#include <mach/task.h>
+
+#include "GenealogySPI.h"
+#include "MachThreadList.h"
+
+class Genealogy
+{
+public:
+
+ Genealogy ();
+
+ ~Genealogy ()
+ {
+ }
+
+ void
+ Clear();
+
+ struct Breadcrumb
+ {
+ uint32_t breadcrumb_id;
+ uint64_t activity_id;
+ uint64_t timestamp;
+ std::string name;
+ };
+
+ struct Activity
+ {
+ uint64_t activity_start;
+ uint64_t activity_id;
+ uint64_t parent_id;
+ std::string activity_name;
+ std::string reason;
+ };
+
+ struct Message
+ {
+ uint64_t timestamp;
+ uint64_t activity_id;
+ uint64_t trace_id;
+ uint64_t thread;
+ uint8_t type; // OS_TRACE_TYPE_RELEASE, OS_TRACE_TYPE_DEBUG, OS_TRACE_TYPE_ERROR, OS_TRACE_TYPE_FAULT
+ uint32_t process_info_index; // index # of the image uuid/file path, 0 means unknown
+ std::string message;
+ };
+
+ typedef std::vector<Message> MessageList;
+ typedef std::vector<Breadcrumb> BreadcrumbList;
+ typedef std::vector<Activity> ActivityList;
+
+ struct ThreadActivity
+ {
+ Activity current_activity;
+ MessageList messages;
+ BreadcrumbList breadcrumbs; // should be 0 or 1 breadcrumbs; no more than 1 BC for any given activity
+ };
+
+ typedef std::shared_ptr<ThreadActivity> ThreadActivitySP;
+
+ ThreadActivitySP
+ GetGenealogyInfoForThread (pid_t pid, nub_thread_t tid, const MachThreadList &thread_list, task_t task, bool &timed_out);
+
+ struct ProcessExecutableInfo
+ {
+ std::string image_path;
+ uuid_t image_uuid;
+ };
+
+ typedef std::shared_ptr<ProcessExecutableInfo> ProcessExecutableInfoSP;
+
+ ProcessExecutableInfoSP
+ GetProcessExecutableInfosAtIndex(size_t idx);
+
+ uint32_t
+ AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info);
+
+private:
+
+ void
+ GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task);
+
+ // the spi we need to call into libtrace - look them up via dlsym at runtime
+ bool (*m_os_activity_diagnostic_for_pid) (pid_t pid, os_activity_t activity, uint32_t flags, os_diagnostic_block_t block);
+ void (*m_os_activity_iterate_processes) (os_activity_process_list_t processes, bool (^iterator)(os_activity_process_t process_info));
+ void (*m_os_activity_iterate_breadcrumbs) (os_activity_process_t process_info, bool (^iterator)(os_activity_breadcrumb_t breadcrumb));
+ void (*m_os_activity_iterate_messages) (os_trace_message_list_t messages, os_activity_process_t process_info, bool (^iterator)(os_trace_message_t tracemsg));
+ void (*m_os_activity_iterate_activities) (os_activity_list_t activities, os_activity_process_t process_info, bool (^iterator)(os_activity_entry_t activity));
+ uint8_t (*m_os_trace_get_type) (os_trace_message_t trace_msg);
+ char * (*m_os_trace_copy_formatted_message) (os_trace_message_t trace_msg);
+ os_activity_t (*m_os_activity_for_thread) (os_activity_process_t process, uint64_t thread_id);
+ os_activity_t (*m_os_activity_for_task_thread) (task_t target, uint64_t thread_id);
+ os_trace_message_list_t (*m_os_activity_messages_for_thread) (os_activity_process_t process, os_activity_t activity, uint64_t thread_id);
+
+
+ std::map<nub_thread_t, ThreadActivitySP> m_thread_activities;
+ std::vector<ProcessExecutableInfoSP> m_process_executable_infos;
+ bool m_diagnosticd_call_timed_out;
+};
+
+#endif // __Genealogy_h__
diff --git a/tools/debugserver/source/MacOSX/GenealogySPI.h b/tools/debugserver/source/MacOSX/GenealogySPI.h
new file mode 100644
index 000000000000..f84e930e8725
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/GenealogySPI.h
@@ -0,0 +1,96 @@
+//===-- ActivitySPI.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//===----------------------------------------------------------------------===//
+
+#ifndef __GenealogySPI_h__
+#define __GenealogySPI_h__
+
+#include <xpc/xpc.h>
+
+typedef void *os_activity_process_list_t;
+typedef void *os_activity_list_t;
+typedef void *os_trace_message_list_t;
+typedef struct os_activity_watch_s *os_activity_watch_t;
+typedef uint64_t os_activity_t;
+
+struct os_activity_breadcrumb_s {
+ uint32_t breadcrumb_id;
+ uint64_t activity_id;
+ uint64_t timestamp;
+ const char *name;
+};
+
+typedef struct os_activity_breadcrumb_s *os_activity_breadcrumb_t;
+
+typedef struct os_trace_message_s {
+ uint64_t trace_id;
+ uint64_t thread;
+ uint64_t timestamp;
+ uint32_t offset;
+ xpc_object_t __unsafe_unretained payload;
+ const uint8_t *image_uuid;
+ const char *image_path;
+ const char *format;
+ const void *buffer;
+ size_t bufferLen;
+} *os_trace_message_t;
+
+typedef struct os_activity_process_s {
+ os_activity_process_list_t child_procs;
+ os_trace_message_list_t messages;
+ os_activity_list_t activities;
+ void *breadcrumbs;
+ uint64_t proc_id;
+ const uint8_t *image_uuid;
+ const char *image_path;
+ pid_t pid;
+} *os_activity_process_t;
+
+typedef struct os_activity_entry_s {
+ uint64_t activity_start;
+ os_activity_t activity_id;
+ os_activity_t parent_id;
+ const char *activity_name;
+ const char *reason;
+ os_trace_message_list_t messages;
+} *os_activity_entry_t;
+
+enum
+{
+ OS_ACTIVITY_DIAGNOSTIC_DEFAULT = 0x00000000,
+ OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY = 0x00000001,
+ OS_ACTIVITY_DIAGNOSTIC_SKIP_DECODE = 0x00000002,
+ OS_ACTIVITY_DIAGNOSTIC_FLATTENED = 0x00000004,
+ OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES = 0x00000008,
+ OS_ACTIVITY_DIAGNOSTIC_MAX = 0x0000000f
+};
+typedef uint32_t os_activity_diagnostic_flag_t;
+
+enum
+{
+ OS_ACTIVITY_WATCH_DEFAULT = 0x00000000,
+ OS_ACTIVITY_WATCH_PROCESS_ONLY = 0x00000001,
+ OS_ACTIVITY_WATCH_SKIP_DECODE = 0x00000002,
+ OS_ACTIVITY_WATCH_PAYLOAD = 0x00000004,
+ OS_ACTIVITY_WATCH_ERRORS = 0x00000008,
+ OS_ACTIVITY_WATCH_FAULTS = 0x00000010,
+ OS_ACTIVITY_WATCH_MAX = 0x0000001f
+};
+typedef uint32_t os_activity_watch_flag_t;
+
+// Return values from os_trace_get_type()
+#define OS_TRACE_TYPE_RELEASE (1u << 0)
+#define OS_TRACE_TYPE_DEBUG (1u << 1)
+#define OS_TRACE_TYPE_ERROR ((1u << 6) | (1u << 0))
+#define OS_TRACE_TYPE_FAULT ((1u << 7) | (1u << 6) | (1u << 0))
+
+
+typedef void (^os_activity_watch_block_t)(os_activity_watch_t watch, os_activity_process_t process_info, bool canceled);
+typedef void (^os_diagnostic_block_t)(os_activity_process_list_t processes, int error);
+
+#endif
+
diff --git a/tools/debugserver/source/MacOSX/HasAVX.h b/tools/debugserver/source/MacOSX/HasAVX.h
new file mode 100644
index 000000000000..c7a50fa20b3e
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/HasAVX.h
@@ -0,0 +1,27 @@
+//===-- HasAVX.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HasAVX_h
+#define HasAVX_h
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int HasAVX ();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/tools/debugserver/source/MacOSX/HasAVX.s b/tools/debugserver/source/MacOSX/HasAVX.s
new file mode 100644
index 000000000000..b66ccf14dbee
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/HasAVX.s
@@ -0,0 +1,50 @@
+//===-- HasAVX.s ---------------------------------------*- x86 Assembly -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__i386__) || defined (__x86_64__)
+
+.globl _HasAVX
+
+_HasAVX:
+#if defined (__x86_64__)
+ pushq %rbp
+ movq %rsp, %rbp
+ pushq %rbx
+#else
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+#endif
+ mov $1, %eax
+ cpuid // clobbers ebx
+ and $0x018000000, %ecx
+ cmp $0x018000000, %ecx
+ jne not_supported
+ mov $0, %ecx
+.byte 0x0f, 0x01, 0xd0 // xgetbv, for those assemblers that don't know it
+ and $0x06, %eax
+ cmp $0x06, %eax
+ jne not_supported
+ mov $1, %eax
+ jmp done
+not_supported:
+ mov $0, %eax
+done:
+#if defined (__x86_64__)
+ popq %rbx
+ movq %rbp, %rsp
+ popq %rbp
+#else
+ popl %ebx
+ movl %ebp, %esp
+ popl %ebp
+#endif
+ ret // return
+
+#endif
diff --git a/tools/debugserver/source/MacOSX/MachException.cpp b/tools/debugserver/source/MacOSX/MachException.cpp
new file mode 100644
index 000000000000..b7245796ae4c
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachException.cpp
@@ -0,0 +1,582 @@
+//===-- MachException.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachException.h"
+#include "MachProcess.h"
+#include "DNB.h"
+#include "DNBError.h"
+#include <sys/types.h>
+#include "DNBLog.h"
+#include "PThreadMutex.h"
+#include "SysSignal.h"
+#include <errno.h>
+#include <sys/ptrace.h>
+
+// Routine mach_exception_raise
+extern "C"
+kern_return_t catch_mach_exception_raise
+(
+ mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt
+);
+
+extern "C"
+kern_return_t catch_mach_exception_raise_state
+(
+ mach_port_t exception_port,
+ exception_type_t exception,
+ const mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int *flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+);
+
+// Routine mach_exception_raise_state_identity
+extern "C"
+kern_return_t catch_mach_exception_raise_state_identity
+(
+ mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int *flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+);
+
+extern "C" boolean_t mach_exc_server(
+ mach_msg_header_t *InHeadP,
+ mach_msg_header_t *OutHeadP);
+
+// Any access to the g_message variable should be done by locking the
+// g_message_mutex first, using the g_message variable, then unlocking
+// the g_message_mutex. See MachException::Message::CatchExceptionRaise()
+// for sample code.
+
+static MachException::Data *g_message = NULL;
+//static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state
+(
+ mach_port_t exc_port,
+ exception_type_t exc_type,
+ const mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count,
+ int * flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t * new_stateCnt
+)
+{
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded ("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = 0x%llx, exc_data_count = %d)",
+ __FUNCTION__,
+ exc_port,
+ exc_type, MachException::Name(exc_type),
+ (uint64_t)exc_data,
+ exc_data_count);
+ }
+ return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state_identity
+(
+ mach_port_t exc_port,
+ mach_port_t thread_port,
+ mach_port_t task_port,
+ exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count,
+ int * flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+)
+{
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, 0x%llx })",
+ __FUNCTION__,
+ exc_port,
+ thread_port,
+ task_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data_count,
+ (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+ (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+ }
+ mach_port_deallocate (mach_task_self (), task_port);
+ mach_port_deallocate (mach_task_self (), thread_port);
+
+ return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise
+(
+ mach_port_t exc_port,
+ mach_port_t thread_port,
+ mach_port_t task_port,
+ exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count)
+{
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded ("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, 0x%llx })",
+ __FUNCTION__,
+ exc_port,
+ thread_port,
+ task_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data_count,
+ (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+ (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+ }
+
+ if (task_port == g_message->task_port)
+ {
+ g_message->task_port = task_port;
+ g_message->thread_port = thread_port;
+ g_message->exc_type = exc_type;
+ g_message->exc_data.resize(exc_data_count);
+ ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t));
+ return KERN_SUCCESS;
+ }
+ return KERN_FAILURE;
+}
+
+
+void
+MachException::Message::Dump() const
+{
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ " exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = 0x%8.8x } ",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id);
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ "reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = 0x%8.8x }",
+ reply_msg.hdr.msgh_bits,
+ reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port,
+ reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved,
+ reply_msg.hdr.msgh_id);
+
+ state.Dump();
+}
+
+bool
+MachException::Data::GetStopInfo(struct DNBThreadStopInfo *stop_info) const
+{
+ // Zero out the structure.
+ memset(stop_info, 0, sizeof(struct DNBThreadStopInfo));
+
+ if (exc_type == 0)
+ {
+ stop_info->reason = eStopTypeInvalid;
+ return true;
+ }
+
+ // We always stop with a mach exceptions
+ stop_info->reason = eStopTypeException;
+ // Save the EXC_XXXX exception type
+ stop_info->details.exception.type = exc_type;
+
+ // Fill in a text description
+ const char * exc_name = MachException::Name(exc_type);
+ char *desc = stop_info->description;
+ const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH;
+ if (exc_name)
+ desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s", exc_name);
+ else
+ desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i", exc_type);
+
+ stop_info->details.exception.data_count = exc_data.size();
+
+ int soft_signal = SoftSignal();
+ if (soft_signal)
+ {
+ if (desc < end_desc)
+ {
+ const char *sig_str = SysSignal::Name(soft_signal);
+ snprintf(desc, end_desc - desc, " EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal, sig_str ? sig_str : "unknown signal");
+ }
+ }
+ else
+ {
+ // No special disassembly for exception data, just
+ size_t idx;
+ if (desc < end_desc)
+ {
+ desc += snprintf(desc, end_desc - desc, " data[%llu] = {", (uint64_t)stop_info->details.exception.data_count);
+
+ for (idx = 0; desc < end_desc && idx < stop_info->details.exception.data_count; ++idx)
+ desc += snprintf(desc, end_desc - desc, "0x%llx%c", (uint64_t)exc_data[idx], ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
+ }
+ }
+
+ // Copy the exception data
+ size_t i;
+ for (i=0; i<stop_info->details.exception.data_count; i++)
+ stop_info->details.exception.data[i] = exc_data[i];
+
+ return true;
+}
+
+
+void
+MachException::Data::DumpStopReason() const
+{
+ int soft_signal = SoftSignal();
+ if (soft_signal)
+ {
+ const char *signal_str = SysSignal::Name(soft_signal);
+ if (signal_str)
+ DNBLog("signal(%s)", signal_str);
+ else
+ DNBLog("signal(%i)", soft_signal);
+ return;
+ }
+ DNBLog("%s", Name(exc_type));
+}
+
+kern_return_t
+MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port)
+{
+ DNBError err;
+ const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS);
+ mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0;
+ if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0))
+ {
+ // Dump this log message if we have no timeout in case it never returns
+ DNBLogThreaded ("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = 0, rcv_size = %llu, rcv_name = %#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id,
+ options,
+ (uint64_t)sizeof (exc_msg.data),
+ port,
+ mach_msg_timeout,
+ notify_port);
+ }
+
+ err = ::mach_msg (&exc_msg.hdr,
+ options, // options
+ 0, // Send size
+ sizeof (exc_msg.data), // Receive size
+ port, // exception port to watch for exception on
+ mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter)
+ notify_port);
+
+ // Dump any errors we get
+ if (log_exceptions)
+ {
+ err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id,
+ options,
+ 0,
+ sizeof (exc_msg.data),
+ port,
+ mach_msg_timeout,
+ notify_port);
+ }
+ return err.Error();
+}
+
+bool
+MachException::Message::CatchExceptionRaise(task_t task)
+{
+ bool success = false;
+ // locker will keep a mutex locked until it goes out of scope
+// PThreadMutex::Locker locker(&g_message_mutex);
+ // DNBLogThreaded("calling mach_exc_server");
+ state.task_port = task;
+ g_message = &state;
+ // The exc_server function is the MIG generated server handling function
+ // to handle messages from the kernel relating to the occurrence of an
+ // exception in a thread. Such messages are delivered to the exception port
+ // set via thread_set_exception_ports or task_set_exception_ports. When an
+ // exception occurs in a thread, the thread sends an exception message to
+ // its exception port, blocking in the kernel waiting for the receipt of a
+ // reply. The exc_server function performs all necessary argument handling
+ // for this kernel message and calls catch_exception_raise,
+ // catch_exception_raise_state or catch_exception_raise_state_identity,
+ // which should handle the exception. If the called routine returns
+ // KERN_SUCCESS, a reply message will be sent, allowing the thread to
+ // continue from the point of the exception; otherwise, no reply message
+ // is sent and the called routine must have dealt with the exception
+ // thread directly.
+ if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr))
+ {
+ success = true;
+ }
+ else if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ {
+ DNBLogThreaded("mach_exc_server returned zero...");
+ }
+ g_message = NULL;
+ return success;
+}
+
+
+
+kern_return_t
+MachException::Message::Reply(MachProcess *process, int signal)
+{
+ // Reply to the exception...
+ DNBError err;
+
+ // If we had a soft signal, we need to update the thread first so it can
+ // continue without signaling
+ int soft_signal = state.SoftSignal();
+ if (soft_signal)
+ {
+ int state_pid = -1;
+ if (process->Task().TaskPort() == state.task_port)
+ {
+ // This is our task, so we can update the signal to send to it
+ state_pid = process->ProcessID();
+ soft_signal = signal;
+ }
+ else
+ {
+ err = ::pid_for_task(state.task_port, &state_pid);
+ }
+
+ assert (state_pid != -1);
+ if (state_pid != -1)
+ {
+ errno = 0;
+ if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)((uintptr_t)state.thread_port), soft_signal) != 0)
+ err.SetError(errno, DNBError::POSIX);
+ else
+ err.Clear();
+
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, soft_signal);
+ }
+ }
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
+ reply_msg.hdr.msgh_bits,
+ reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port,
+ reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved,
+ reply_msg.hdr.msgh_id,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ err = ::mach_msg ( &reply_msg.hdr,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ if (err.Fail())
+ {
+ if (err.Error() == MACH_SEND_INTERRUPTED)
+ {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreaded("::mach_msg() - send interrupted");
+ // TODO: keep retrying to reply???
+ }
+ else
+ {
+ if (state.task_port == process->Task().TaskPort())
+ {
+ DNBLogThreaded("error: mach_msg() returned an error when replying to a mach exception: error = %u", err.Error());
+ abort ();
+ }
+ else
+ {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreaded("::mach_msg() - failed (child of task)");
+ }
+ }
+ }
+
+ return err.Error();
+}
+
+
+void
+MachException::Data::Dump() const
+{
+ const char *exc_type_name = MachException::Name(exc_type);
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " state { task_port = 0x%4.4x, thread_port = 0x%4.4x, exc_type = %i (%s) ...", task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???");
+
+ const size_t exc_data_count = exc_data.size();
+ // Dump any special exception data contents
+ int soft_signal = SoftSignal();
+ if (soft_signal != 0)
+ {
+ const char *sig_str = SysSignal::Name(soft_signal);
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal");
+ }
+ else
+ {
+ // No special disassembly for this data, just dump the data
+ size_t idx;
+ for (idx = 0; idx < exc_data_count; ++idx)
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data[%llu]: 0x%llx", (uint64_t)idx, (uint64_t)exc_data[idx]);
+ }
+ }
+}
+
+#define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS | \
+ EXC_MASK_BAD_INSTRUCTION | \
+ EXC_MASK_ARITHMETIC | \
+ EXC_MASK_EMULATION | \
+ EXC_MASK_SOFTWARE | \
+ EXC_MASK_BREAKPOINT | \
+ EXC_MASK_SYSCALL | \
+ EXC_MASK_MACH_SYSCALL | \
+ EXC_MASK_RPC_ALERT | \
+ EXC_MASK_MACHINE)
+
+// Don't listen for EXC_RESOURCE, it should really get handled by the system handler.
+
+#ifndef EXC_RESOURCE
+#define EXC_RESOURCE 11
+#endif
+
+#ifndef EXC_MASK_RESOURCE
+#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)
+#endif
+
+#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE)
+
+kern_return_t
+MachException::PortInfo::Save (task_t task)
+{
+ DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task);
+ // Be careful to be able to have debugserver built on a newer OS than what
+ // it is currently running on by being able to start with all exceptions
+ // and back off to just what is supported on the current system
+ DNBError err;
+
+ mask = LLDB_EXC_MASK;
+
+ count = (sizeof (ports) / sizeof (ports[0]));
+ err = ::task_get_exception_ports (task, mask, masks, &count, ports, behaviors, flavors);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, mask, count);
+
+ if (err.Error() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL)
+ {
+ mask = PREV_EXC_MASK_ALL;
+ count = (sizeof (ports) / sizeof (ports[0]));
+ err = ::task_get_exception_ports (task, mask, masks, &count, ports, behaviors, flavors);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, mask, count);
+ }
+ if (err.Fail())
+ {
+ mask = 0;
+ count = 0;
+ }
+ return err.Error();
+}
+
+kern_return_t
+MachException::PortInfo::Restore (task_t task)
+{
+ DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Restore( task = 0x%4.4x )", task);
+ uint32_t i = 0;
+ DNBError err;
+ if (count > 0)
+ {
+ for (i = 0; i < count; i++)
+ {
+ err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ {
+ err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]);
+ // Bail if we encounter any errors
+ }
+
+ if (err.Fail())
+ break;
+ }
+ }
+ count = 0;
+ return err.Error();
+}
+
+const char *
+MachException::Name(exception_type_t exc_type)
+{
+ switch (exc_type)
+ {
+ case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS";
+ case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION";
+ case EXC_ARITHMETIC: return "EXC_ARITHMETIC";
+ case EXC_EMULATION: return "EXC_EMULATION";
+ case EXC_SOFTWARE: return "EXC_SOFTWARE";
+ case EXC_BREAKPOINT: return "EXC_BREAKPOINT";
+ case EXC_SYSCALL: return "EXC_SYSCALL";
+ case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL";
+ case EXC_RPC_ALERT: return "EXC_RPC_ALERT";
+#ifdef EXC_CRASH
+ case EXC_CRASH: return "EXC_CRASH";
+#endif
+ default:
+ break;
+ }
+ return NULL;
+}
+
+
+
diff --git a/tools/debugserver/source/MacOSX/MachException.h b/tools/debugserver/source/MacOSX/MachException.h
new file mode 100644
index 000000000000..c831479f2b6d
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachException.h
@@ -0,0 +1,133 @@
+//===-- MachException.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __MachException_h__
+#define __MachException_h__
+
+#include <mach/mach.h>
+#include <vector>
+
+class MachProcess;
+class PThreadMutex;
+
+typedef union MachMessageTag
+{
+ mach_msg_header_t hdr;
+ char data[1024];
+} MachMessage;
+
+
+class MachException
+{
+public:
+
+ struct PortInfo
+ {
+ exception_mask_t mask; // the exception mask for this device which may be a subset of EXC_MASK_ALL...
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+ mach_msg_type_number_t count;
+
+ kern_return_t Save(task_t task);
+ kern_return_t Restore(task_t task);
+ };
+
+ struct Data
+ {
+ task_t task_port;
+ thread_t thread_port;
+ exception_type_t exc_type;
+ std::vector<mach_exception_data_type_t> exc_data;
+ Data() :
+ task_port(TASK_NULL),
+ thread_port(THREAD_NULL),
+ exc_type(0),
+ exc_data()
+ {
+ }
+
+ void Clear()
+ {
+ task_port = TASK_NULL;
+ thread_port = THREAD_NULL;
+ exc_type = 0;
+ exc_data.clear();
+ }
+ bool IsValid() const
+ {
+ return task_port != TASK_NULL &&
+ thread_port != THREAD_NULL &&
+ exc_type != 0;
+ }
+ // Return the SoftSignal for this MachException data, or zero if there is none
+ int SoftSignal() const
+ {
+ if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL)
+ return static_cast<int>(exc_data[1]);
+ return 0;
+ }
+ bool IsBreakpoint() const
+ {
+ return (exc_type == EXC_BREAKPOINT || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1));
+ }
+ void Dump() const;
+ void DumpStopReason() const;
+ bool GetStopInfo(struct DNBThreadStopInfo *stop_info) const;
+ };
+
+ struct Message
+ {
+ MachMessage exc_msg;
+ MachMessage reply_msg;
+ Data state;
+
+ Message() :
+ state()
+ {
+ memset(&exc_msg, 0, sizeof(exc_msg));
+ memset(&reply_msg, 0, sizeof(reply_msg));
+ }
+ bool CatchExceptionRaise(task_t task);
+ void Dump() const;
+ kern_return_t Reply (MachProcess *process, int signal);
+ kern_return_t Receive( mach_port_t receive_port,
+ mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port = MACH_PORT_NULL);
+
+ typedef std::vector<Message> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ };
+
+ enum
+ {
+ e_actionForward, // Forward signal to inferior process
+ e_actionStop, // Stop when this signal is received
+ };
+ struct Action
+ {
+ task_t task_port; // Set to TASK_NULL for any TASK
+ thread_t thread_port; // Set to THREAD_NULL for any thread
+ exception_type_t exc_mask; // Mach exception mask to watch for
+ std::vector<mach_exception_data_type_t> exc_data_mask; // Mask to apply to exception data, or empty to ignore exc_data value for exception
+ std::vector<mach_exception_data_type_t> exc_data_value; // Value to compare to exception data after masking, or empty to ignore exc_data value for exception
+ uint8_t flags; // Action flags describing what to do with the exception
+ };
+ static const char *Name(exception_type_t exc_type);
+};
+
+#endif
diff --git a/tools/debugserver/source/MacOSX/MachProcess.h b/tools/debugserver/source/MacOSX/MachProcess.h
new file mode 100644
index 000000000000..3be0b2dbabb3
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachProcess.h
@@ -0,0 +1,364 @@
+//===-- MachProcess.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/15/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachProcess_h__
+#define __MachProcess_h__
+
+#include <mach/mach.h>
+#include <sys/signal.h>
+#include <pthread.h>
+#include <vector>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "DNBDefs.h"
+#include "DNBBreakpoint.h"
+#include "DNBError.h"
+#include "DNBThreadResumeActions.h"
+#include "MachException.h"
+#include "MachVMMemory.h"
+#include "MachTask.h"
+#include "MachThreadList.h"
+#include "PThreadCondition.h"
+#include "PThreadEvent.h"
+#include "PThreadMutex.h"
+#include "Genealogy.h"
+#include "ThreadInfo.h"
+#include "JSONGenerator.h"
+
+class DNBThreadResumeActions;
+
+class MachProcess
+{
+public:
+ //----------------------------------------------------------------------
+ // Constructors and Destructors
+ //----------------------------------------------------------------------
+ MachProcess ();
+ ~MachProcess ();
+
+ //----------------------------------------------------------------------
+ // Child process control
+ //----------------------------------------------------------------------
+ pid_t AttachForDebug (pid_t pid, char *err_str, size_t err_len);
+ pid_t LaunchForDebug (const char *path,
+ char const *argv[],
+ char const *envp[],
+ const char *working_directory,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ bool no_stdio,
+ nub_launch_flavor_t launch_flavor,
+ int disable_aslr,
+ const char *event_data,
+ DNBError &err);
+
+ static uint32_t GetCPUTypeForLocalProcess (pid_t pid);
+ static pid_t ForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], MachProcess* process, DNBError &err);
+ static pid_t PosixSpawnChildForPTraceDebugging (const char *path,
+ cpu_type_t cpu_type,
+ char const *argv[],
+ char const *envp[],
+ const char *working_directory,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ bool no_stdio,
+ MachProcess* process,
+ int disable_aslr,
+ DNBError& err);
+ nub_addr_t GetDYLDAllImageInfosAddress ();
+ static const void * PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str);
+ static void CleanupAfterAttach (const void *attach_token, nub_launch_flavor_t launch_flavor, bool success, DNBError &err_str);
+ static nub_process_t CheckForProcess (const void *attach_token, nub_launch_flavor_t launch_flavor);
+#if defined(WITH_BKS) || defined(WITH_FBS)
+ pid_t BoardServiceLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err);
+ pid_t BoardServiceForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err);
+ bool BoardServiceSendEvent (const char *event, DNBError &error);
+#endif
+ static bool GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch);
+#ifdef WITH_BKS
+ static void BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str);
+#endif // WITH_BKS
+#ifdef WITH_FBS
+ static void FBSCleanupAfterAttach (const void *attach_token, DNBError &err_str);
+#endif // WITH_FBS
+#ifdef WITH_SPRINGBOARD
+ pid_t SBLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, DNBError &launch_err);
+ static pid_t SBForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err);
+#endif // WITH_SPRINGBOARD
+ nub_addr_t LookupSymbol (const char *name, const char *shlib);
+ void SetNameToAddressCallback (DNBCallbackNameToAddress callback, void *baton)
+ {
+ m_name_to_addr_callback = callback;
+ m_name_to_addr_baton = baton;
+ }
+ void SetSharedLibraryInfoCallback (DNBCallbackCopyExecutableImageInfos callback, void *baton)
+ {
+ m_image_infos_callback = callback;
+ m_image_infos_baton = baton;
+ }
+
+ bool Resume (const DNBThreadResumeActions& thread_actions);
+ bool Signal (int signal, const struct timespec *timeout_abstime = NULL);
+ bool Interrupt();
+ bool SendEvent (const char *event, DNBError &send_err);
+ bool Kill (const struct timespec *timeout_abstime = NULL);
+ bool Detach ();
+ nub_size_t ReadMemory (nub_addr_t addr, nub_size_t size, void *buf);
+ nub_size_t WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf);
+
+ //----------------------------------------------------------------------
+ // Path and arg accessors
+ //----------------------------------------------------------------------
+ const char * Path () const { return m_path.c_str(); }
+ size_t ArgumentCount () const { return m_args.size(); }
+ const char * ArgumentAtIndex (size_t arg_idx) const
+ {
+ if (arg_idx < m_args.size())
+ return m_args[arg_idx].c_str();
+ return NULL;
+ }
+
+ //----------------------------------------------------------------------
+ // Breakpoint functions
+ //----------------------------------------------------------------------
+ DNBBreakpoint * CreateBreakpoint (nub_addr_t addr, nub_size_t length, bool hardware);
+ bool DisableBreakpoint (nub_addr_t addr, bool remove);
+ void DisableAllBreakpoints (bool remove);
+ bool EnableBreakpoint (nub_addr_t addr);
+ DNBBreakpointList& Breakpoints() { return m_breakpoints; }
+ const DNBBreakpointList& Breakpoints() const { return m_breakpoints; }
+
+ //----------------------------------------------------------------------
+ // Watchpoint functions
+ //----------------------------------------------------------------------
+ DNBBreakpoint * CreateWatchpoint (nub_addr_t addr, nub_size_t length, uint32_t watch_type, bool hardware);
+ bool DisableWatchpoint (nub_addr_t addr, bool remove);
+ void DisableAllWatchpoints (bool remove);
+ bool EnableWatchpoint (nub_addr_t addr);
+ uint32_t GetNumSupportedHardwareWatchpoints () const;
+ DNBBreakpointList& Watchpoints() { return m_watchpoints; }
+ const DNBBreakpointList& Watchpoints() const { return m_watchpoints; }
+
+ //----------------------------------------------------------------------
+ // Exception thread functions
+ //----------------------------------------------------------------------
+ bool StartSTDIOThread ();
+ static void * STDIOThread (void *arg);
+ void ExceptionMessageReceived (const MachException::Message& exceptionMessage);
+ task_t ExceptionMessageBundleComplete ();
+ void SharedLibrariesUpdated ();
+ nub_size_t CopyImageInfos (struct DNBExecutableImageInfo **image_infos, bool only_changed);
+
+ //----------------------------------------------------------------------
+ // Profile functions
+ //----------------------------------------------------------------------
+ void SetEnableAsyncProfiling (bool enable, uint64_t internal_usec, DNBProfileDataScanType scan_type);
+ bool IsProfilingEnabled () { return m_profile_enabled; }
+ useconds_t ProfileInterval () { return m_profile_interval_usec; }
+ bool StartProfileThread ();
+ static void * ProfileThread (void *arg);
+ void SignalAsyncProfileData (const char *info);
+ size_t GetAsyncProfileData (char *buf, size_t buf_size);
+
+ //----------------------------------------------------------------------
+ // Accessors
+ //----------------------------------------------------------------------
+ pid_t ProcessID () const { return m_pid; }
+ bool ProcessIDIsValid () const { return m_pid > 0; }
+ pid_t SetProcessID (pid_t pid);
+ MachTask& Task() { return m_task; }
+ const MachTask& Task() const { return m_task; }
+
+ PThreadEvent& Events() { return m_events; }
+ const DNBRegisterSetInfo *
+ GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const;
+ bool GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value) const;
+ bool SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value) const;
+ nub_bool_t SyncThreadState (nub_thread_t tid);
+ const char * ThreadGetName (nub_thread_t tid);
+ nub_state_t ThreadGetState (nub_thread_t tid);
+ ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index);
+ nub_addr_t GetPThreadT (nub_thread_t tid);
+ nub_addr_t GetDispatchQueueT (nub_thread_t tid);
+ nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size);
+ JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count);
+
+ nub_size_t GetNumThreads () const;
+ nub_thread_t GetThreadAtIndex (nub_size_t thread_idx) const;
+ nub_thread_t GetCurrentThread ();
+ nub_thread_t GetCurrentThreadMachPort ();
+ nub_thread_t SetCurrentThread (nub_thread_t tid);
+ MachThreadList & GetThreadList() { return m_thread_list; }
+ bool GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info);
+ void DumpThreadStoppedReason(nub_thread_t tid) const;
+ const char * GetThreadInfo (nub_thread_t tid) const;
+
+ nub_thread_t GetThreadIDForMachPortNumber (thread_t mach_port_number) const;
+
+ uint32_t GetCPUType ();
+ nub_state_t GetState ();
+ void SetState (nub_state_t state);
+ bool IsRunning (nub_state_t state)
+ {
+ return state == eStateRunning || IsStepping(state);
+ }
+ bool IsStepping (nub_state_t state)
+ {
+ return state == eStateStepping;
+ }
+ bool CanResume (nub_state_t state)
+ {
+ return state == eStateStopped;
+ }
+
+ bool GetExitStatus(int* status)
+ {
+ if (GetState() == eStateExited)
+ {
+ if (status)
+ *status = m_exit_status;
+ return true;
+ }
+ return false;
+ }
+ void SetExitStatus(int status)
+ {
+ m_exit_status = status;
+ SetState(eStateExited);
+ }
+ const char * GetExitInfo ()
+ {
+ return m_exit_info.c_str();
+ }
+
+ void SetExitInfo (const char *info);
+
+ uint32_t StopCount() const { return m_stop_count; }
+ void SetChildFileDescriptors (int stdin_fileno, int stdout_fileno, int stderr_fileno)
+ {
+ m_child_stdin = stdin_fileno;
+ m_child_stdout = stdout_fileno;
+ m_child_stderr = stderr_fileno;
+ }
+
+ int GetStdinFileDescriptor () const { return m_child_stdin; }
+ int GetStdoutFileDescriptor () const { return m_child_stdout; }
+ int GetStderrFileDescriptor () const { return m_child_stderr; }
+ void AppendSTDOUT (char* s, size_t len);
+ size_t GetAvailableSTDOUT (char *buf, size_t buf_size);
+ size_t GetAvailableSTDERR (char *buf, size_t buf_size);
+ void CloseChildFileDescriptors ()
+ {
+ if (m_child_stdin >= 0)
+ {
+ ::close (m_child_stdin);
+ m_child_stdin = -1;
+ }
+ if (m_child_stdout >= 0)
+ {
+ ::close (m_child_stdout);
+ m_child_stdout = -1;
+ }
+ if (m_child_stderr >= 0)
+ {
+ ::close (m_child_stderr);
+ m_child_stderr = -1;
+ }
+ }
+
+ bool ProcessUsingSpringBoard() const { return (m_flags & eMachProcessFlagsUsingSBS) != 0; }
+ bool ProcessUsingBackBoard() const { return (m_flags & eMachProcessFlagsUsingBKS) != 0; }
+
+ Genealogy::ThreadActivitySP GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out);
+
+ Genealogy::ProcessExecutableInfoSP GetGenealogyImageInfo (size_t idx);
+
+ DNBProfileDataScanType GetProfileScanType () { return m_profile_scan_type; }
+
+private:
+ enum
+ {
+ eMachProcessFlagsNone = 0,
+ eMachProcessFlagsAttached = (1 << 0),
+ eMachProcessFlagsUsingSBS = (1 << 1),
+ eMachProcessFlagsUsingBKS = (1 << 2),
+ eMachProcessFlagsUsingFBS = (1 << 3)
+ };
+ void Clear (bool detaching = false);
+ void ReplyToAllExceptions ();
+ void PrivateResume ();
+
+ uint32_t Flags () const { return m_flags; }
+ nub_state_t DoSIGSTOP (bool clear_bps_and_wps, bool allow_running, uint32_t *thread_idx_ptr);
+
+ pid_t m_pid; // Process ID of child process
+ cpu_type_t m_cpu_type; // The CPU type of this process
+ int m_child_stdin;
+ int m_child_stdout;
+ int m_child_stderr;
+ std::string m_path; // A path to the executable if we have one
+ std::vector<std::string> m_args; // The arguments with which the process was lauched
+ int m_exit_status; // The exit status for the process
+ std::string m_exit_info; // Any extra info that we may have about the exit
+ MachTask m_task; // The mach task for this process
+ uint32_t m_flags; // Process specific flags (see eMachProcessFlags enums)
+ uint32_t m_stop_count; // A count of many times have we stopped
+ pthread_t m_stdio_thread; // Thread ID for the thread that watches for child process stdio
+ PThreadMutex m_stdio_mutex; // Multithreaded protection for stdio
+ std::string m_stdout_data;
+
+ bool m_profile_enabled; // A flag to indicate if profiling is enabled
+ useconds_t m_profile_interval_usec; // If enable, the profiling interval in microseconds
+ DNBProfileDataScanType m_profile_scan_type; // Indicates what needs to be profiled
+ pthread_t m_profile_thread; // Thread ID for the thread that profiles the inferior
+ PThreadMutex m_profile_data_mutex; // Multithreaded protection for profile info data
+ std::vector<std::string> m_profile_data; // Profile data, must be protected by m_profile_data_mutex
+
+ DNBThreadResumeActions m_thread_actions; // The thread actions for the current MachProcess::Resume() call
+ MachException::Message::collection
+ m_exception_messages; // A collection of exception messages caught when listening to the exception port
+ PThreadMutex m_exception_messages_mutex; // Multithreaded protection for m_exception_messages
+
+ MachThreadList m_thread_list; // A list of threads that is maintained/updated after each stop
+ Genealogy m_activities; // A list of activities that is updated after every stop lazily
+ nub_state_t m_state; // The state of our process
+ PThreadMutex m_state_mutex; // Multithreaded protection for m_state
+ PThreadEvent m_events; // Process related events in the child processes lifetime can be waited upon
+ PThreadEvent m_private_events; // Used to coordinate running and stopping the process without affecting m_events
+ DNBBreakpointList m_breakpoints; // Breakpoint list for this process
+ DNBBreakpointList m_watchpoints; // Watchpoint list for this process
+ DNBCallbackNameToAddress m_name_to_addr_callback;
+ void * m_name_to_addr_baton;
+ DNBCallbackCopyExecutableImageInfos
+ m_image_infos_callback;
+ void * m_image_infos_baton;
+ std::string m_bundle_id; // If we are a SB or BKS process, this will be our bundle ID.
+ int m_sent_interrupt_signo; // When we call MachProcess::Interrupt(), we want to send a single signal
+ // to the inferior and only send the signal if we aren't already stopped.
+ // If we end up sending a signal to stop the process we store it until we
+ // receive an exception with this signal. This helps us to verify we got
+ // the signal that interrupted the process. We might stop due to another
+ // reason after an interrupt signal is sent, so this helps us ensure that
+ // we don't report a spurious stop on the next resume.
+ int m_auto_resume_signo; // If we resume the process and still haven't received our interrupt signal
+ // acknownledgement, we will shortly after the next resume. We store the
+ // interrupt signal in this variable so when we get the interrupt signal
+ // as the sole reason for the process being stopped, we can auto resume
+ // the process.
+ bool m_did_exec;
+};
+
+
+#endif // __MachProcess_h__
diff --git a/tools/debugserver/source/MacOSX/MachProcess.mm b/tools/debugserver/source/MacOSX/MachProcess.mm
new file mode 100644
index 000000000000..b9e06307a4aa
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachProcess.mm
@@ -0,0 +1,3685 @@
+//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/15/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNB.h"
+#include <inttypes.h>
+#include <mach/mach.h>
+#include <signal.h>
+#include <spawn.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <mach-o/loader.h>
+#include <uuid/uuid.h>
+#include "MacOSX/CFUtils.h"
+#include "SysSignal.h"
+
+#include <algorithm>
+#include <map>
+
+#import <Foundation/Foundation.h>
+
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "DNBTimer.h"
+#include "MachProcess.h"
+#include "PseudoTerminal.h"
+
+#include "CFBundle.h"
+#include "CFData.h"
+#include "CFString.h"
+
+#ifdef WITH_SPRINGBOARD
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+
+static bool
+IsSBProcess (nub_process_t pid)
+{
+ CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid));
+ return appIdsForPID.get() != NULL;
+}
+
+#endif // WITH_SPRINGBOARD
+
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
+// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
+// or NULL if there was some problem getting the bundle id.
+static CFStringRef CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str);
+#endif
+
+#if defined(WITH_BKS) || defined(WITH_FBS)
+#import <Foundation/Foundation.h>
+static const int OPEN_APPLICATION_TIMEOUT_ERROR = 111;
+typedef void (*SetErrorFunction) (NSInteger, DNBError &);
+typedef bool (*CallOpenApplicationFunction) (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid);
+// This function runs the BKSSystemService (or FBSSystemService) method openApplication:options:clientPort:withResult,
+// messaging the app passed in bundleIDNSStr.
+// The function should be run inside of an NSAutoReleasePool.
+//
+// It will use the "options" dictionary passed in, and fill the error passed in if there is an error.
+// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID.
+// If bundleIDNSStr is NULL, then the system application will be messaged.
+
+template <typename OpenFlavor, typename ErrorFlavor, ErrorFlavor no_error_enum_value, SetErrorFunction error_function>
+static bool
+CallBoardSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid)
+{
+ // Now make our systemService:
+ OpenFlavor *system_service = [[OpenFlavor alloc] init];
+
+ if (bundleIDNSStr == nil)
+ {
+ bundleIDNSStr = [system_service systemApplicationBundleIdentifier];
+ if (bundleIDNSStr == nil)
+ {
+ // Okay, no system app...
+ error.SetErrorString("No system application to message.");
+ return false;
+ }
+ }
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block ErrorFlavor open_app_error = no_error_enum_value;
+ bool wants_pid = (return_pid != NULL);
+ __block pid_t pid_in_block;
+
+ const char *cstr = [bundleIDNSStr UTF8String];
+ if (!cstr)
+ cstr = "<Unknown Bundle ID>";
+
+ DNBLog ("About to launch process for bundle ID: %s", cstr);
+ [system_service openApplication: bundleIDNSStr
+ options: options
+ clientPort: client_port
+ withResult: ^(NSError *bks_error)
+ {
+ // The system service will cleanup the client port we created for us.
+ if (bks_error)
+ open_app_error = (ErrorFlavor)[bks_error code];
+
+ if (open_app_error == no_error_enum_value)
+ {
+ if (wants_pid)
+ {
+ pid_in_block = [system_service pidForApplication: bundleIDNSStr];
+ DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
+ DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
+ }
+ else
+ DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success.");
+ }
+ else
+ {
+ const char *error_str = [(NSString *)[bks_error localizedDescription] UTF8String];
+ DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%ld).",
+ error_str ? error_str : "<unknown error>",
+ open_app_error);
+ // REMOVE ME
+ DNBLogError ("In completion handler for send event, got error \"%s\"(%ld).",
+ error_str ? error_str : "<unknown error>",
+ open_app_error);
+ }
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }
+
+ ];
+
+ const uint32_t timeout_secs = 9;
+
+ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ dispatch_release(semaphore);
+
+ if (!success)
+{
+ DNBLogError("timed out trying to send openApplication to %s.", cstr);
+ error.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ error.SetErrorString ("timed out trying to launch app");
+ }
+ else if (open_app_error != no_error_enum_value)
+ {
+ error_function (open_app_error, error);
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error);
+ success = false;
+ }
+ else if (wants_pid)
+ {
+ *return_pid = pid_in_block;
+ DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid);
+}
+
+
+ return success;
+}
+#endif
+
+#ifdef WITH_BKS
+#import <Foundation/Foundation.h>
+extern "C"
+{
+#import <BackBoardServices/BackBoardServices.h>
+#import <BackBoardServices/BKSSystemService_LaunchServices.h>
+#import <BackBoardServices/BKSOpenApplicationConstants_Private.h>
+}
+
+static bool
+IsBKSProcess (nub_process_t pid)
+{
+ BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init];
+ BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid];
+ return app_state != BKSApplicationStateUnknown;
+}
+
+static void
+SetBKSError (NSInteger error_code, DNBError &error)
+{
+ error.SetError (error_code, DNBError::BackBoard);
+ NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString((BKSOpenApplicationErrorCode) error_code);
+ const char *err_str = NULL;
+ if (err_nsstr == NULL)
+ err_str = "unknown BKS error";
+ else
+ {
+ err_str = [err_nsstr UTF8String];
+ if (err_str == NULL)
+ err_str = "unknown BKS error";
+ }
+ error.SetErrorString(err_str);
+}
+
+static bool
+BKSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error)
+{
+ if (strcmp (event_data, "BackgroundContentFetching") == 0)
+ {
+ DNBLog("Setting ActivateForEvent key in options dictionary.");
+ NSDictionary *event_details = [NSDictionary dictionary];
+ NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching];
+ [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent];
+ return true;
+ }
+ else
+ {
+ DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data);
+ option_error.SetErrorString("Unrecognized event data.");
+ return false;
+ }
+
+}
+
+static NSMutableDictionary *
+BKSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSMutableDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data)
+{
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ if (launch_argv != nil)
+ [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments];
+ if (launch_envp != nil)
+ [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment];
+
+ [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath];
+ [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger];
+ if (disable_aslr)
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
+ // And there are some other options at the top level in this dictionary:
+ [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice];
+
+ DNBError error;
+ BKSAddEventDataToOptions (options, event_data, error);
+
+ return options;
+}
+
+static CallOpenApplicationFunction BKSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication<BKSSystemService, BKSOpenApplicationErrorCode, BKSOpenApplicationErrorCodeNone, SetBKSError>;
+#endif // WITH_BKS
+
+#ifdef WITH_FBS
+#import <Foundation/Foundation.h>
+extern "C"
+{
+#import <FrontBoardServices/FrontBoardServices.h>
+#import <FrontBoardServices/FBSSystemService_LaunchServices.h>
+#import <FrontBoardServices/FBSOpenApplicationConstants_Private.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+#import <MobileCoreServices/LSResourceProxy.h>
+}
+
+#ifdef WITH_BKS
+static bool
+IsFBSProcess (nub_process_t pid)
+{
+ BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init];
+ BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid];
+ return app_state != BKSApplicationStateUnknown;
+}
+#else
+static bool
+IsFBSProcess (nub_process_t pid)
+{
+ // FIXME: What is the FBS equivalent of BKSApplicationStateMonitor
+ return true;
+}
+#endif
+
+static void
+SetFBSError (NSInteger error_code, DNBError &error)
+{
+ error.SetError ((DNBError::ValueType) error_code, DNBError::FrontBoard);
+ NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString((FBSOpenApplicationErrorCode) error_code);
+ const char *err_str = NULL;
+ if (err_nsstr == NULL)
+ err_str = "unknown FBS error";
+ else
+ {
+ err_str = [err_nsstr UTF8String];
+ if (err_str == NULL)
+ err_str = "unknown FBS error";
+ }
+ error.SetErrorString(err_str);
+}
+
+static bool
+FBSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error)
+{
+ if (strcmp (event_data, "BackgroundContentFetching") == 0)
+ {
+ DNBLog("Setting ActivateForEvent key in options dictionary.");
+ NSDictionary *event_details = [NSDictionary dictionary];
+ NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:FBSActivateForEventOptionTypeBackgroundContentFetching];
+ [options setObject: event_dictionary forKey: FBSOpenApplicationOptionKeyActivateForEvent];
+ return true;
+ }
+ else
+ {
+ DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data);
+ option_error.SetErrorString("Unrecognized event data.");
+ return false;
+ }
+
+}
+
+static NSMutableDictionary *
+FBSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data)
+{
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+
+ if (launch_argv != nil)
+ [debug_options setObject: launch_argv forKey: FBSDebugOptionKeyArguments];
+ if (launch_envp != nil)
+ [debug_options setObject: launch_envp forKey: FBSDebugOptionKeyEnvironment];
+
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath];
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger];
+ if (disable_aslr)
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDisableASLR];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions];
+ // And there are some other options at the top level in this dictionary:
+ [options setObject: [NSNumber numberWithBool: YES] forKey: FBSOpenApplicationOptionKeyUnlockDevice];
+
+ // We have to get the "sequence ID & UUID" for this app bundle path and send them to FBS:
+
+ NSURL *app_bundle_url = [NSURL fileURLWithPath: [NSString stringWithUTF8String: app_bundle_path] isDirectory: YES];
+ LSApplicationProxy *app_proxy = [LSApplicationProxy applicationProxyForBundleURL: app_bundle_url];
+ if (app_proxy)
+ {
+ DNBLog("Sending AppProxy info: sequence no: %lu, GUID: %s.", app_proxy.sequenceNumber, [app_proxy.cacheGUID.UUIDString UTF8String]);
+ [options setObject: [NSNumber numberWithUnsignedInteger: app_proxy.sequenceNumber] forKey: FBSOpenApplicationOptionKeyLSSequenceNumber];
+ [options setObject: app_proxy.cacheGUID.UUIDString forKey: FBSOpenApplicationOptionKeyLSCacheGUID];
+ }
+
+ DNBError error;
+ FBSAddEventDataToOptions (options, event_data, error);
+
+ return options;
+}
+static CallOpenApplicationFunction FBSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication<FBSSystemService, FBSOpenApplicationErrorCode, FBSOpenApplicationErrorCodeNone, SetFBSError>;
+#endif // WITH_FBS
+
+#if 0
+#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_LOG(fmt, ...)
+#endif
+
+#ifndef MACH_PROCESS_USE_POSIX_SPAWN
+#define MACH_PROCESS_USE_POSIX_SPAWN 1
+#endif
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+MachProcess::MachProcess() :
+ m_pid (0),
+ m_cpu_type (0),
+ m_child_stdin (-1),
+ m_child_stdout (-1),
+ m_child_stderr (-1),
+ m_path (),
+ m_args (),
+ m_task (this),
+ m_flags (eMachProcessFlagsNone),
+ m_stdio_thread (0),
+ m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE),
+ m_stdout_data (),
+ m_profile_enabled (false),
+ m_profile_interval_usec (0),
+ m_profile_thread (0),
+ m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE),
+ m_profile_data (),
+ m_thread_actions (),
+ m_exception_messages (),
+ m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE),
+ m_thread_list (),
+ m_activities (),
+ m_state (eStateUnloaded),
+ m_state_mutex (PTHREAD_MUTEX_RECURSIVE),
+ m_events (0, kAllEventsMask),
+ m_private_events (0, kAllEventsMask),
+ m_breakpoints (),
+ m_watchpoints (),
+ m_name_to_addr_callback(NULL),
+ m_name_to_addr_baton(NULL),
+ m_image_infos_callback(NULL),
+ m_image_infos_baton(NULL),
+ m_sent_interrupt_signo (0),
+ m_auto_resume_signo (0),
+ m_did_exec (false)
+{
+ DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
+}
+
+MachProcess::~MachProcess()
+{
+ DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
+ Clear();
+}
+
+pid_t
+MachProcess::SetProcessID(pid_t pid)
+{
+ // Free any previous process specific data or resources
+ Clear();
+ // Set the current PID appropriately
+ if (pid == 0)
+ m_pid = ::getpid ();
+ else
+ m_pid = pid;
+ return m_pid; // Return actually PID in case a zero pid was passed in
+}
+
+nub_state_t
+MachProcess::GetState()
+{
+ // If any other threads access this we will need a mutex for it
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ return m_state;
+}
+
+const char *
+MachProcess::ThreadGetName(nub_thread_t tid)
+{
+ return m_thread_list.GetName(tid);
+}
+
+nub_state_t
+MachProcess::ThreadGetState(nub_thread_t tid)
+{
+ return m_thread_list.GetState(tid);
+}
+
+
+nub_size_t
+MachProcess::GetNumThreads () const
+{
+ return m_thread_list.NumThreads();
+}
+
+nub_thread_t
+MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const
+{
+ return m_thread_list.ThreadIDAtIndex(thread_idx);
+}
+
+nub_thread_t
+MachProcess::GetThreadIDForMachPortNumber (thread_t mach_port_number) const
+{
+ return m_thread_list.GetThreadIDByMachPortNumber (mach_port_number);
+}
+
+nub_bool_t
+MachProcess::SyncThreadState (nub_thread_t tid)
+{
+ MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid));
+ if (!thread_sp)
+ return false;
+ kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber());
+ DNBLogThreadedIf (LOG_THREAD, "thread = 0x%8.8" PRIx32 " calling thread_abort_safely (tid) => %u (GetGPRState() for stop_count = %u)", thread_sp->MachPortNumber(), kret, thread_sp->Process()->StopCount());
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else
+ return false;
+
+}
+
+ThreadInfo::QoS
+MachProcess::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index)
+{
+ return m_thread_list.GetRequestedQoS (tid, tsd, dti_qos_class_index);
+}
+
+nub_addr_t
+MachProcess::GetPThreadT (nub_thread_t tid)
+{
+ return m_thread_list.GetPThreadT (tid);
+}
+
+nub_addr_t
+MachProcess::GetDispatchQueueT (nub_thread_t tid)
+{
+ return m_thread_list.GetDispatchQueueT (tid);
+}
+
+nub_addr_t
+MachProcess::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size)
+{
+ return m_thread_list.GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
+}
+
+
+
+JSONGenerator::ObjectSP
+MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count)
+{
+ JSONGenerator::DictionarySP reply_sp;
+
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ struct kinfo_proc processInfo;
+ size_t bufsize = sizeof(processInfo);
+ if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0)
+ {
+ uint32_t pointer_size = 4;
+ if (processInfo.kp_proc.p_flag & P_LP64)
+ pointer_size = 8;
+
+ struct segment
+ {
+ std::string name;
+ uint64_t vmaddr;
+ uint64_t vmsize;
+ uint64_t fileoff;
+ uint64_t filesize;
+ uint64_t maxprot;
+ uint64_t initprot;
+ uint64_t nsects;
+ uint64_t flags;
+ };
+
+ struct image_info
+ {
+ uint64_t load_address;
+ std::string pathname;
+ uint64_t mod_date;
+ struct mach_header_64 mach_header;
+ std::vector<struct segment> segments;
+ uuid_t uuid;
+ };
+ std::vector<image_info> image_infos;
+ size_t image_infos_size = image_count * 3 * pointer_size;
+
+ uint8_t *image_info_buf = (uint8_t *) malloc (image_infos_size);
+ if (image_info_buf == NULL)
+ {
+ return reply_sp;
+ }
+ if (ReadMemory (image_list_address, image_infos_size, image_info_buf) != image_infos_size)
+ {
+ return reply_sp;
+ }
+
+
+ //// First the image_infos array with (load addr, pathname, mod date) tuples
+
+
+ for (size_t i = 0; i < image_count; i++)
+ {
+ struct image_info info;
+ nub_addr_t pathname_address;
+ if (pointer_size == 4)
+ {
+ uint32_t load_address_32;
+ uint32_t pathname_address_32;
+ uint32_t mod_date_32;
+ ::memcpy (&load_address_32, image_info_buf + (i * 3 * pointer_size), 4);
+ ::memcpy (&pathname_address_32, image_info_buf + (i * 3 * pointer_size) + pointer_size, 4);
+ ::memcpy (&mod_date_32, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 4);
+ info.load_address = load_address_32;
+ info.mod_date = mod_date_32;
+ pathname_address = pathname_address_32;
+ }
+ else
+ {
+ uint64_t load_address_64;
+ uint64_t pathname_address_64;
+ uint64_t mod_date_64;
+ ::memcpy (&load_address_64, image_info_buf + (i * 3 * pointer_size), 8);
+ ::memcpy (&pathname_address_64, image_info_buf + (i * 3 * pointer_size) + pointer_size, 8);
+ ::memcpy (&mod_date_64, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 8);
+ info.load_address = load_address_64;
+ info.mod_date = mod_date_64;
+ pathname_address = pathname_address_64;
+ }
+ char strbuf[17];
+ info.pathname = "";
+ uint64_t pathname_ptr = pathname_address;
+ bool still_reading = true;
+ while (still_reading && ReadMemory (pathname_ptr, sizeof (strbuf) - 1, strbuf) == sizeof (strbuf) - 1)
+ {
+ strbuf[sizeof(strbuf) - 1] = '\0';
+ info.pathname += strbuf;
+ pathname_ptr += sizeof (strbuf) - 1;
+ // Stop if we found nul byte indicating the end of the string
+ for (size_t i = 0; i < sizeof(strbuf) - 1; i++)
+ {
+ if (strbuf[i] == '\0')
+ {
+ still_reading = false;
+ break;
+ }
+ }
+ }
+ uuid_clear (info.uuid);
+ image_infos.push_back (info);
+ }
+ if (image_infos.size() == 0)
+ {
+ return reply_sp;
+ }
+
+
+ //// Second, read the mach header / load commands for all the dylibs
+
+
+ for (size_t i = 0; i < image_count; i++)
+ {
+ uint64_t load_cmds_p;
+ if (pointer_size == 4)
+ {
+ struct mach_header header;
+ if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header), &header) != sizeof (struct mach_header))
+ {
+ return reply_sp;
+ }
+ load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header);
+ image_infos[i].mach_header.magic = header.magic;
+ image_infos[i].mach_header.cputype = header.cputype;
+ image_infos[i].mach_header.cpusubtype = header.cpusubtype;
+ image_infos[i].mach_header.filetype = header.filetype;
+ image_infos[i].mach_header.ncmds = header.ncmds;
+ image_infos[i].mach_header.sizeofcmds = header.sizeofcmds;
+ image_infos[i].mach_header.flags = header.flags;
+ }
+ else
+ {
+ struct mach_header_64 header;
+ if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header_64), &header) != sizeof (struct mach_header_64))
+ {
+ return reply_sp;
+ }
+ load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header_64);
+ image_infos[i].mach_header.magic = header.magic;
+ image_infos[i].mach_header.cputype = header.cputype;
+ image_infos[i].mach_header.cpusubtype = header.cpusubtype;
+ image_infos[i].mach_header.filetype = header.filetype;
+ image_infos[i].mach_header.ncmds = header.ncmds;
+ image_infos[i].mach_header.sizeofcmds = header.sizeofcmds;
+ image_infos[i].mach_header.flags = header.flags;
+ }
+ for (uint32_t j = 0; j < image_infos[i].mach_header.ncmds; j++)
+ {
+ struct load_command lc;
+ if (ReadMemory (load_cmds_p, sizeof (struct load_command), &lc) != sizeof (struct load_command))
+ {
+ return reply_sp;
+ }
+ if (lc.cmd == LC_SEGMENT)
+ {
+ struct segment_command seg;
+ if (ReadMemory (load_cmds_p, sizeof (struct segment_command), &seg) != sizeof (struct segment_command))
+ {
+ return reply_sp;
+ }
+ struct segment this_seg;
+ char name[17];
+ ::memset (name, 0, sizeof (name));
+ memcpy (name, seg.segname, sizeof (seg.segname));
+ this_seg.name = name;
+ this_seg.vmaddr = seg.vmaddr;
+ this_seg.vmsize = seg.vmsize;
+ this_seg.fileoff = seg.fileoff;
+ this_seg.filesize = seg.filesize;
+ this_seg.maxprot = seg.maxprot;
+ this_seg.initprot = seg.initprot;
+ this_seg.nsects = seg.nsects;
+ this_seg.flags = seg.flags;
+ image_infos[i].segments.push_back(this_seg);
+ }
+ if (lc.cmd == LC_SEGMENT_64)
+ {
+ struct segment_command_64 seg;
+ if (ReadMemory (load_cmds_p, sizeof (struct segment_command_64), &seg) != sizeof (struct segment_command_64))
+ {
+ return reply_sp;
+ }
+ struct segment this_seg;
+ char name[17];
+ ::memset (name, 0, sizeof (name));
+ memcpy (name, seg.segname, sizeof (seg.segname));
+ this_seg.name = name;
+ this_seg.vmaddr = seg.vmaddr;
+ this_seg.vmsize = seg.vmsize;
+ this_seg.fileoff = seg.fileoff;
+ this_seg.filesize = seg.filesize;
+ this_seg.maxprot = seg.maxprot;
+ this_seg.initprot = seg.initprot;
+ this_seg.nsects = seg.nsects;
+ this_seg.flags = seg.flags;
+ image_infos[i].segments.push_back(this_seg);
+ }
+ if (lc.cmd == LC_UUID)
+ {
+ struct uuid_command uuidcmd;
+ if (ReadMemory (load_cmds_p, sizeof (struct uuid_command), &uuidcmd) == sizeof (struct uuid_command))
+ uuid_copy (image_infos[i].uuid, uuidcmd.uuid);
+ }
+ load_cmds_p += lc.cmdsize;
+ }
+ }
+
+
+ //// Thrid, format all of the above in the JSONGenerator object.
+
+
+ JSONGenerator::ArraySP image_infos_array_sp (new JSONGenerator::Array());
+ for (size_t i = 0; i < image_count; i++)
+ {
+ JSONGenerator::DictionarySP image_info_dict_sp (new JSONGenerator::Dictionary());
+ image_info_dict_sp->AddIntegerItem ("load_address", image_infos[i].load_address);
+ image_info_dict_sp->AddIntegerItem ("mod_date", image_infos[i].mod_date);
+ image_info_dict_sp->AddStringItem ("pathname", image_infos[i].pathname);
+
+ uuid_string_t uuidstr;
+ uuid_unparse_upper (image_infos[i].uuid, uuidstr);
+ image_info_dict_sp->AddStringItem ("uuid", uuidstr);
+
+ JSONGenerator::DictionarySP mach_header_dict_sp (new JSONGenerator::Dictionary());
+ mach_header_dict_sp->AddIntegerItem ("magic", image_infos[i].mach_header.magic);
+ mach_header_dict_sp->AddIntegerItem ("cputype", image_infos[i].mach_header.cputype);
+ mach_header_dict_sp->AddIntegerItem ("cpusubtype", image_infos[i].mach_header.cpusubtype);
+ mach_header_dict_sp->AddIntegerItem ("filetype", image_infos[i].mach_header.filetype);
+
+// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them.
+// mach_header_dict_sp->AddIntegerItem ("ncmds", image_infos[i].mach_header.ncmds);
+// mach_header_dict_sp->AddIntegerItem ("sizeofcmds", image_infos[i].mach_header.sizeofcmds);
+// mach_header_dict_sp->AddIntegerItem ("flags", image_infos[i].mach_header.flags);
+ image_info_dict_sp->AddItem ("mach_header", mach_header_dict_sp);
+
+ JSONGenerator::ArraySP segments_sp (new JSONGenerator::Array());
+ for (size_t j = 0; j < image_infos[i].segments.size(); j++)
+ {
+ JSONGenerator::DictionarySP segment_sp (new JSONGenerator::Dictionary());
+ segment_sp->AddStringItem ("name", image_infos[i].segments[j].name);
+ segment_sp->AddIntegerItem ("vmaddr", image_infos[i].segments[j].vmaddr);
+ segment_sp->AddIntegerItem ("vmsize", image_infos[i].segments[j].vmsize);
+ segment_sp->AddIntegerItem ("fileoff", image_infos[i].segments[j].fileoff);
+ segment_sp->AddIntegerItem ("filesize", image_infos[i].segments[j].filesize);
+ segment_sp->AddIntegerItem ("maxprot", image_infos[i].segments[j].maxprot);
+
+// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them.
+// segment_sp->AddIntegerItem ("initprot", image_infos[i].segments[j].initprot);
+// segment_sp->AddIntegerItem ("nsects", image_infos[i].segments[j].nsects);
+// segment_sp->AddIntegerItem ("flags", image_infos[i].segments[j].flags);
+ segments_sp->AddItem (segment_sp);
+ }
+ image_info_dict_sp->AddItem ("segments", segments_sp);
+
+ image_infos_array_sp->AddItem (image_info_dict_sp);
+ }
+ reply_sp.reset (new JSONGenerator::Dictionary());
+ reply_sp->AddItem ("images", image_infos_array_sp);
+ }
+ return reply_sp;
+}
+
+nub_thread_t
+MachProcess::GetCurrentThread ()
+{
+ return m_thread_list.CurrentThreadID();
+}
+
+nub_thread_t
+MachProcess::GetCurrentThreadMachPort ()
+{
+ return m_thread_list.GetMachPortNumberByThreadID(m_thread_list.CurrentThreadID());
+}
+
+nub_thread_t
+MachProcess::SetCurrentThread(nub_thread_t tid)
+{
+ return m_thread_list.SetCurrentThread(tid);
+}
+
+bool
+MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info)
+{
+ if (m_thread_list.GetThreadStoppedReason(tid, stop_info))
+ {
+ if (m_did_exec)
+ stop_info->reason = eStopTypeExec;
+ return true;
+ }
+ return false;
+}
+
+void
+MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const
+{
+ return m_thread_list.DumpThreadStoppedReason(tid);
+}
+
+const char *
+MachProcess::GetThreadInfo(nub_thread_t tid) const
+{
+ return m_thread_list.GetThreadInfo(tid);
+}
+
+uint32_t
+MachProcess::GetCPUType ()
+{
+ if (m_cpu_type == 0 && m_pid != 0)
+ m_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid);
+ return m_cpu_type;
+}
+
+const DNBRegisterSetInfo *
+MachProcess::GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const
+{
+ MachThreadSP thread_sp (m_thread_list.GetThreadByID (tid));
+ if (thread_sp)
+ {
+ DNBArchProtocol *arch = thread_sp->GetArchProtocol();
+ if (arch)
+ return arch->GetRegisterSetInfo (num_reg_sets);
+ }
+ *num_reg_sets = 0;
+ return NULL;
+}
+
+bool
+MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const
+{
+ return m_thread_list.GetRegisterValue(tid, set, reg, value);
+}
+
+bool
+MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const
+{
+ return m_thread_list.SetRegisterValue(tid, set, reg, value);
+}
+
+void
+MachProcess::SetState(nub_state_t new_state)
+{
+ // If any other threads access this we will need a mutex for it
+ uint32_t event_mask = 0;
+
+ // Scope for mutex locker
+ {
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ const nub_state_t old_state = m_state;
+
+ if (old_state == eStateExited)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new state since current state is exited", DNBStateAsString(new_state));
+ }
+ else if (old_state == new_state)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring redundant state change...", DNBStateAsString(new_state));
+ }
+ else
+ {
+ if (NUB_STATE_IS_STOPPED(new_state))
+ event_mask = eEventProcessStoppedStateChanged;
+ else
+ event_mask = eEventProcessRunningStateChanged;
+
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous state was %s), event_mask = 0x%8.8x", DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask);
+
+ m_state = new_state;
+ if (new_state == eStateStopped)
+ m_stop_count++;
+ }
+ }
+
+ if (event_mask != 0)
+ {
+ m_events.SetEvents (event_mask);
+ m_private_events.SetEvents (event_mask);
+ if (event_mask == eEventProcessStoppedStateChanged)
+ m_private_events.ResetEvents (eEventProcessRunningStateChanged);
+ else
+ m_private_events.ResetEvents (eEventProcessStoppedStateChanged);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(event_mask);
+ }
+
+}
+
+void
+MachProcess::Clear(bool detaching)
+{
+ // Clear any cached thread list while the pid and task are still valid
+
+ m_task.Clear();
+ // Now clear out all member variables
+ m_pid = INVALID_NUB_PROCESS;
+ if (!detaching)
+ CloseChildFileDescriptors();
+
+ m_path.clear();
+ m_args.clear();
+ SetState(eStateUnloaded);
+ m_flags = eMachProcessFlagsNone;
+ m_stop_count = 0;
+ m_thread_list.Clear();
+ {
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+ m_exception_messages.clear();
+ }
+ m_activities.Clear();
+ if (m_profile_thread)
+ {
+ pthread_join(m_profile_thread, NULL);
+ m_profile_thread = NULL;
+ }
+}
+
+
+bool
+MachProcess::StartSTDIOThread()
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
+ // Create the thread that watches for the child STDIO
+ return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0;
+}
+
+void
+MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec, DNBProfileDataScanType scan_type)
+{
+ m_profile_enabled = enable;
+ m_profile_interval_usec = static_cast<useconds_t>(interval_usec);
+ m_profile_scan_type = scan_type;
+
+ if (m_profile_enabled && (m_profile_thread == NULL))
+ {
+ StartProfileThread();
+ }
+ else if (!m_profile_enabled && m_profile_thread)
+ {
+ pthread_join(m_profile_thread, NULL);
+ m_profile_thread = NULL;
+ }
+}
+
+bool
+MachProcess::StartProfileThread()
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
+ // Create the thread that profiles the inferior and reports back if enabled
+ return ::pthread_create (&m_profile_thread, NULL, MachProcess::ProfileThread, this) == 0;
+}
+
+
+nub_addr_t
+MachProcess::LookupSymbol(const char *name, const char *shlib)
+{
+ if (m_name_to_addr_callback != NULL && name && name[0])
+ return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton);
+ return INVALID_NUB_ADDRESS;
+}
+
+bool
+MachProcess::Resume (const DNBThreadResumeActions& thread_actions)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
+ nub_state_t state = GetState();
+
+ if (CanResume(state))
+ {
+ m_thread_actions = thread_actions;
+ PrivateResume();
+ return true;
+ }
+ else if (state == eStateRunning)
+ {
+ DNBLog("Resume() - task 0x%x is already running, ignoring...", m_task.TaskPort());
+ return true;
+ }
+ DNBLog("Resume() - task 0x%x has state %s, can't continue...", m_task.TaskPort(), DNBStateAsString(state));
+ return false;
+}
+
+bool
+MachProcess::Kill (const struct timespec *timeout_abstime)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()");
+ nub_state_t state = DoSIGSTOP(true, false, NULL);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state));
+ errno = 0;
+ DNBLog ("Sending ptrace PT_KILL to terminate inferior process.");
+ ::ptrace (PT_KILL, m_pid, 0, 0);
+ DNBError err;
+ err.SetErrorToErrno();
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_pid, err.Error(), err.AsString());
+ m_thread_actions = DNBThreadResumeActions (eStateRunning, 0);
+ PrivateResume ();
+
+ // Try and reap the process without touching our m_events since
+ // we want the code above this to still get the eStateExited event
+ const uint32_t reap_timeout_usec = 1000000; // Wait 1 second and try to reap the process
+ const uint32_t reap_interval_usec = 10000; //
+ uint32_t reap_time_elapsed;
+ for (reap_time_elapsed = 0;
+ reap_time_elapsed < reap_timeout_usec;
+ reap_time_elapsed += reap_interval_usec)
+ {
+ if (GetState() == eStateExited)
+ break;
+ usleep(reap_interval_usec);
+ }
+ DNBLog ("Waited %u ms for process to be reaped (state = %s)", reap_time_elapsed/1000, DNBStateAsString(GetState()));
+ return true;
+}
+
+bool
+MachProcess::Interrupt()
+{
+ nub_state_t state = GetState();
+ if (IsRunning(state))
+ {
+ if (m_sent_interrupt_signo == 0)
+ {
+ m_sent_interrupt_signo = SIGSTOP;
+ if (Signal (m_sent_interrupt_signo))
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - sent %i signal to interrupt process", m_sent_interrupt_signo);
+ }
+ else
+ {
+ m_sent_interrupt_signo = 0;
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to send %i signal to interrupt process", m_sent_interrupt_signo);
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously sent an interrupt signal %i that hasn't been received yet, interrupt aborted", m_sent_interrupt_signo);
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already stopped, no interrupt sent");
+ }
+ return false;
+}
+
+bool
+MachProcess::Signal (int signal, const struct timespec *timeout_abstime)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime);
+ nub_state_t state = GetState();
+ if (::kill (ProcessID(), signal) == 0)
+ {
+ // If we were running and we have a timeout, wait for the signal to stop
+ if (IsRunning(state) && timeout_abstime)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime);
+ m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime);
+ state = GetState();
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state));
+ return !IsRunning (state);
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime);
+ return true;
+ }
+ DNBError err(errno, DNBError::POSIX);
+ err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal);
+ return false;
+
+}
+
+bool
+MachProcess::SendEvent (const char *event, DNBError &send_err)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SendEvent (event = %s) to pid: %d", event, m_pid);
+ if (m_pid == INVALID_NUB_PROCESS)
+ return false;
+ // FIXME: Shouldn't we use the launch flavor we were started with?
+#if defined(WITH_FBS) || defined(WITH_BKS)
+ return BoardServiceSendEvent (event, send_err);
+#endif
+ return true;
+}
+
+nub_state_t
+MachProcess::DoSIGSTOP (bool clear_bps_and_wps, bool allow_running, uint32_t *thread_idx_ptr)
+{
+ nub_state_t state = GetState();
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state));
+
+ if (!IsRunning(state))
+ {
+ if (clear_bps_and_wps)
+ {
+ DisableAllBreakpoints (true);
+ DisableAllWatchpoints (true);
+ clear_bps_and_wps = false;
+ }
+
+ // If we already have a thread stopped due to a SIGSTOP, we don't have
+ // to do anything...
+ uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP);
+ if (thread_idx_ptr)
+ *thread_idx_ptr = thread_idx;
+ if (thread_idx != UINT32_MAX)
+ return GetState();
+
+ // No threads were stopped with a SIGSTOP, we need to run and halt the
+ // process with a signal
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state));
+ if (allow_running)
+ m_thread_actions = DNBThreadResumeActions (eStateRunning, 0);
+ else
+ m_thread_actions = DNBThreadResumeActions (eStateSuspended, 0);
+
+ PrivateResume ();
+
+ // Reset the event that says we were indeed running
+ m_events.ResetEvents(eEventProcessRunningStateChanged);
+ state = GetState();
+ }
+
+ // We need to be stopped in order to be able to detach, so we need
+ // to send ourselves a SIGSTOP
+
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state));
+ struct timespec sigstop_timeout;
+ DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0);
+ Signal (SIGSTOP, &sigstop_timeout);
+ if (clear_bps_and_wps)
+ {
+ DisableAllBreakpoints (true);
+ DisableAllWatchpoints (true);
+ //clear_bps_and_wps = false;
+ }
+ uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP);
+ if (thread_idx_ptr)
+ *thread_idx_ptr = thread_idx;
+ return GetState();
+}
+
+bool
+MachProcess::Detach()
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()");
+
+ uint32_t thread_idx = UINT32_MAX;
+ nub_state_t state = DoSIGSTOP(true, true, &thread_idx);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state));
+
+ {
+ m_thread_actions.Clear();
+ m_activities.Clear();
+ DNBThreadResumeAction thread_action;
+ thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx);
+ thread_action.state = eStateRunning;
+ thread_action.signal = -1;
+ thread_action.addr = INVALID_NUB_ADDRESS;
+
+ m_thread_actions.Append (thread_action);
+ m_thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0);
+
+ PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
+
+ ReplyToAllExceptions ();
+
+ }
+
+ m_task.ShutDownExcecptionThread();
+
+ // Detach from our process
+ errno = 0;
+ nub_process_t pid = m_pid;
+ int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
+ DNBError err(errno, DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0))
+ err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
+
+ // Resume our task
+ m_task.Resume();
+
+ // NULL our task out as we have already retored all exception ports
+ m_task.Clear();
+
+ // Clear out any notion of the process we once were
+ const bool detaching = true;
+ Clear(detaching);
+
+ SetState(eStateDetached);
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+// ReadMemory from the MachProcess level will always remove any software
+// breakpoints from the memory buffer before returning. If you wish to
+// read memory and see those traps, read from the MachTask
+// (m_task.ReadMemory()) as that version will give you what is actually
+// in inferior memory.
+//----------------------------------------------------------------------
+nub_size_t
+MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
+{
+ // We need to remove any current software traps (enabled software
+ // breakpoints) that we may have placed in our tasks memory.
+
+ // First just read the memory as is
+ nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf);
+
+ // Then place any opcodes that fall into this range back into the buffer
+ // before we return this to callers.
+ if (bytes_read > 0)
+ m_breakpoints.RemoveTrapsFromBuffer (addr, bytes_read, buf);
+ return bytes_read;
+}
+
+//----------------------------------------------------------------------
+// WriteMemory from the MachProcess level will always write memory around
+// any software breakpoints. Any software breakpoints will have their
+// opcodes modified if they are enabled. Any memory that doesn't overlap
+// with software breakpoints will be written to. If you wish to write to
+// inferior memory without this interference, then write to the MachTask
+// (m_task.WriteMemory()) as that version will always modify inferior
+// memory.
+//----------------------------------------------------------------------
+nub_size_t
+MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
+{
+ // We need to write any data that would go where any current software traps
+ // (enabled software breakpoints) any software traps (breakpoints) that we
+ // may have placed in our tasks memory.
+
+ std::vector<DNBBreakpoint *> bps;
+
+ const size_t num_bps = m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps);
+ if (num_bps == 0)
+ return m_task.WriteMemory(addr, size, buf);
+
+ nub_size_t bytes_written = 0;
+ nub_addr_t intersect_addr;
+ nub_size_t intersect_size;
+ nub_size_t opcode_offset;
+ const uint8_t *ubuf = (const uint8_t *)buf;
+
+ for (size_t i=0; i<num_bps; ++i)
+ {
+ DNBBreakpoint *bp = bps[i];
+
+ const bool intersects = bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset);
+ UNUSED_IF_ASSERT_DISABLED(intersects);
+ assert(intersects);
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp->ByteSize());
+
+ // Check for bytes before this breakpoint
+ const nub_addr_t curr_addr = addr + bytes_written;
+ if (intersect_addr > curr_addr)
+ {
+ // There are some bytes before this breakpoint that we need to
+ // just write to memory
+ nub_size_t curr_size = intersect_addr - curr_addr;
+ nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written);
+ bytes_written += curr_bytes_written;
+ if (curr_bytes_written != curr_size)
+ {
+ // We weren't able to write all of the requested bytes, we
+ // are done looping and will return the number of bytes that
+ // we have written so far.
+ break;
+ }
+ }
+
+ // Now write any bytes that would cover up any software breakpoints
+ // directly into the breakpoint opcode buffer
+ ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size);
+ bytes_written += intersect_size;
+ }
+
+ // Write any remaining bytes after the last breakpoint if we have any left
+ if (bytes_written < size)
+ bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written);
+
+ return bytes_written;
+}
+
+void
+MachProcess::ReplyToAllExceptions ()
+{
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+ if (m_exception_messages.empty() == false)
+ {
+ MachException::Message::iterator pos;
+ MachException::Message::iterator begin = m_exception_messages.begin();
+ MachException::Message::iterator end = m_exception_messages.end();
+ for (pos = begin; pos != end; ++pos)
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...", (uint32_t)std::distance(begin, pos));
+ int thread_reply_signal = 0;
+
+ nub_thread_t tid = m_thread_list.GetThreadIDByMachPortNumber (pos->state.thread_port);
+ const DNBThreadResumeAction *action = NULL;
+ if (tid != INVALID_NUB_THREAD)
+ {
+ action = m_thread_actions.GetActionForThread (tid, false);
+ }
+
+ if (action)
+ {
+ thread_reply_signal = action->signal;
+ if (thread_reply_signal)
+ m_thread_actions.SetSignalHandledForThread (tid);
+ }
+
+ DNBError err (pos->Reply(this, thread_reply_signal));
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreadedIfError("Error replying to exception");
+ }
+
+ // Erase all exception message as we should have used and replied
+ // to them all already.
+ m_exception_messages.clear();
+ }
+}
+void
+MachProcess::PrivateResume ()
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
+
+ m_auto_resume_signo = m_sent_interrupt_signo;
+ if (m_auto_resume_signo)
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming (with unhandled interrupt signal %i)...", m_task.TaskPort(), m_auto_resume_signo);
+ else
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming...", m_task.TaskPort());
+
+ ReplyToAllExceptions ();
+// bool stepOverBreakInstruction = step;
+
+ // Let the thread prepare to resume and see if any threads want us to
+ // step over a breakpoint instruction (ProcessWillResume will modify
+ // the value of stepOverBreakInstruction).
+ m_thread_list.ProcessWillResume (this, m_thread_actions);
+
+ // Set our state accordingly
+ if (m_thread_actions.NumActionsWithState(eStateStepping))
+ SetState (eStateStepping);
+ else
+ SetState (eStateRunning);
+
+ // Now resume our task.
+ m_task.Resume();
+}
+
+DNBBreakpoint *
+MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware)
+{
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu, hardware = %i)", (uint64_t)addr, (uint64_t)length, hardware);
+
+ DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
+ if (bp)
+ bp->Retain();
+ else
+ bp = m_breakpoints.Add(addr, length, hardware);
+
+ if (EnableBreakpoint(addr))
+ {
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, bp);
+ return bp;
+ }
+ else if (bp->Release() == 0)
+ {
+ m_breakpoints.Remove(addr);
+ }
+ // We failed to enable the breakpoint
+ return NULL;
+}
+
+DNBBreakpoint *
+MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu, flags = 0x%8.8x, hardware = %i)", (uint64_t)addr, (uint64_t)length, watch_flags, hardware);
+
+ DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
+ // since the Z packets only send an address, we can only have one watchpoint at
+ // an address. If there is already one, we must refuse to create another watchpoint
+ if (wp)
+ return NULL;
+
+ wp = m_watchpoints.Add(addr, length, hardware);
+ wp->SetIsWatchpoint(watch_flags);
+
+ if (EnableWatchpoint(addr))
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, wp);
+ return wp;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => FAILED", (uint64_t)addr, (uint64_t)length);
+ m_watchpoints.Remove(addr);
+ }
+ // We failed to enable the watchpoint
+ return NULL;
+}
+
+void
+MachProcess::DisableAllBreakpoints (bool remove)
+{
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
+
+ m_breakpoints.DisableAllBreakpoints (this);
+
+ if (remove)
+ m_breakpoints.RemoveDisabled();
+}
+
+void
+MachProcess::DisableAllWatchpoints(bool remove)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
+
+ m_watchpoints.DisableAllWatchpoints(this);
+
+ if (remove)
+ m_watchpoints.RemoveDisabled();
+}
+
+bool
+MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove)
+{
+ DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
+ if (bp)
+ {
+ // After "exec" we might end up with a bunch of breakpoints that were disabled
+ // manually, just ignore them
+ if (!bp->IsEnabled())
+ {
+ // Breakpoint might have been disabled by an exec
+ if (remove && bp->Release() == 0)
+ {
+ m_thread_list.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(addr);
+ }
+ return true;
+ }
+
+ // We have multiple references to this breakpoint, decrement the ref count
+ // and if it isn't zero, then return true;
+ if (remove && bp->Release() > 0)
+ return true;
+
+ DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove);
+
+ if (bp->IsHardware())
+ {
+ bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp);
+
+ if (hw_disable_result == true)
+ {
+ bp->SetEnabled(false);
+ // Let the thread list know that a breakpoint has been modified
+ if (remove)
+ {
+ m_thread_list.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(addr);
+ }
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove);
+ return true;
+ }
+
+ return false;
+ }
+
+ const nub_size_t break_op_size = bp->ByteSize();
+ assert (break_op_size > 0);
+ const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (bp->ByteSize());
+ if (break_op_size > 0)
+ {
+ // Clear a software breakpoint instruction
+ uint8_t curr_break_op[break_op_size];
+ bool break_op_found = false;
+
+ // Read the breakpoint opcode
+ if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size)
+ {
+ bool verify = false;
+ if (bp->IsEnabled())
+ {
+ // Make sure we have the a breakpoint opcode exists at this address
+ if (memcmp(curr_break_op, break_op, break_op_size) == 0)
+ {
+ break_op_found = true;
+ // We found a valid breakpoint opcode at this address, now restore
+ // the saved opcode.
+ if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
+ {
+ verify = true;
+ }
+ else
+ {
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) memory write failed when restoring original opcode", (uint64_t)addr, remove);
+ }
+ }
+ else
+ {
+ DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) expected a breakpoint opcode but didn't find one.", (uint64_t)addr, remove);
+ // Set verify to true and so we can check if the original opcode has already been restored
+ verify = true;
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) is not enabled", (uint64_t)addr, remove);
+ // Set verify to true and so we can check if the original opcode is there
+ verify = true;
+ }
+
+ if (verify)
+ {
+ uint8_t verify_opcode[break_op_size];
+ // Verify that our original opcode made it back to the inferior
+ if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size)
+ {
+ // compare the memory we just read with the original opcode
+ if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0)
+ {
+ // SUCCESS
+ bp->SetEnabled(false);
+ // Let the thread list know that a breakpoint has been modified
+ if (remove && bp->Release() == 0)
+ {
+ m_thread_list.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(addr);
+ }
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) => success", (uint64_t)addr, remove);
+ return true;
+ }
+ else
+ {
+ if (break_op_found)
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : failed to restore original opcode", (uint64_t)addr, remove);
+ else
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : opcode changed", (uint64_t)addr, remove);
+ }
+ }
+ else
+ {
+ DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr);
+ }
+ }
+ }
+ else
+ {
+ DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr);
+ }
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) invalid breakpoint address", (uint64_t)addr, remove);
+ }
+ return false;
+}
+
+bool
+MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(addr = 0x%8.8llx, remove = %d)", __FUNCTION__, (uint64_t)addr, remove);
+ DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
+ if (wp)
+ {
+ // If we have multiple references to a watchpoint, removing the watchpoint shouldn't clear it
+ if (remove && wp->Release() > 0)
+ return true;
+
+ nub_addr_t addr = wp->Address();
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove);
+
+ if (wp->IsHardware())
+ {
+ bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp);
+
+ if (hw_disable_result == true)
+ {
+ wp->SetEnabled(false);
+ if (remove)
+ m_watchpoints.Remove(addr);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove);
+ return true;
+ }
+ }
+
+ // TODO: clear software watchpoints if we implement them
+ }
+ else
+ {
+ DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d ) invalid watchpoint ID", (uint64_t)addr, remove);
+ }
+ return false;
+}
+
+
+uint32_t
+MachProcess::GetNumSupportedHardwareWatchpoints () const
+{
+ return m_thread_list.NumSupportedHardwareWatchpoints();
+}
+
+bool
+MachProcess::EnableBreakpoint(nub_addr_t addr)
+{
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )", (uint64_t)addr);
+ DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
+ if (bp)
+ {
+ if (bp->IsEnabled())
+ {
+ DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint already enabled.", (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ if (bp->HardwarePreferred())
+ {
+ bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp));
+ if (bp->IsHardware())
+ {
+ bp->SetEnabled(true);
+ return true;
+ }
+ }
+
+ const nub_size_t break_op_size = bp->ByteSize();
+ assert (break_op_size != 0);
+ const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (break_op_size);
+ if (break_op_size > 0)
+ {
+ // Save the original opcode by reading it
+ if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
+ {
+ // Write a software breakpoint in place of the original opcode
+ if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size)
+ {
+ uint8_t verify_break_op[4];
+ if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size)
+ {
+ if (memcmp(break_op, verify_break_op, break_op_size) == 0)
+ {
+ bp->SetEnabled(true);
+ // Let the thread list know that a breakpoint has been modified
+ m_thread_list.NotifyBreakpointChanged(bp);
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) : SUCCESS.", (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint opcode verification failed.", (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory to verify breakpoint opcode.", (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to write breakpoint opcode to memory.", (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory at breakpoint address.", (uint64_t)addr);
+ }
+ }
+ else
+ {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no software breakpoint opcode for current architecture.", (uint64_t)addr);
+ }
+ }
+ }
+ return false;
+}
+
+bool
+MachProcess::EnableWatchpoint(nub_addr_t addr)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)", (uint64_t)addr);
+ DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
+ if (wp)
+ {
+ nub_addr_t addr = wp->Address();
+ if (wp->IsEnabled())
+ {
+ DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): watchpoint already enabled.", (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ // Currently only try and set hardware watchpoints.
+ wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp));
+ if (wp->IsHardware())
+ {
+ wp->SetEnabled(true);
+ return true;
+ }
+ // TODO: Add software watchpoints by doing page protection tricks.
+ }
+ }
+ return false;
+}
+
+// Called by the exception thread when an exception has been received from
+// our process. The exception message is completely filled and the exception
+// data has already been copied.
+void
+MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
+
+ if (m_exception_messages.empty())
+ m_task.Suspend();
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
+
+ // Use a locker to automatically unlock our mutex in case of exceptions
+ // Add the exception to our internal exception stack
+ m_exception_messages.push_back(exceptionMessage);
+}
+
+task_t
+MachProcess::ExceptionMessageBundleComplete()
+{
+ // We have a complete bundle of exceptions for our child process.
+ PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
+ bool auto_resume = false;
+ if (!m_exception_messages.empty())
+ {
+ m_did_exec = false;
+ // First check for any SIGTRAP and make sure we didn't exec
+ const task_t task = m_task.TaskPort();
+ size_t i;
+ if (m_pid != 0)
+ {
+ bool received_interrupt = false;
+ uint32_t num_task_exceptions = 0;
+ for (i=0; i<m_exception_messages.size(); ++i)
+ {
+ if (m_exception_messages[i].state.task_port == task)
+ {
+ ++num_task_exceptions;
+ const int signo = m_exception_messages[i].state.SoftSignal();
+ if (signo == SIGTRAP)
+ {
+ // SIGTRAP could mean that we exec'ed. We need to check the
+ // dyld all_image_infos.infoArray to see if it is NULL and if
+ // so, say that we exec'ed.
+ const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress();
+ if (aii_addr != INVALID_NUB_ADDRESS)
+ {
+ const nub_addr_t info_array_count_addr = aii_addr + 4;
+ uint32_t info_array_count = 0;
+ if (m_task.ReadMemory(info_array_count_addr, 4, &info_array_count) == 4)
+ {
+ if (info_array_count == 0)
+ {
+ m_did_exec = true;
+ // Force the task port to update itself in case the task port changed after exec
+ DNBError err;
+ const task_t old_task = m_task.TaskPort();
+ const task_t new_task = m_task.TaskPortForProcessID (err, true);
+ if (old_task != new_task)
+ DNBLogThreadedIf(LOG_PROCESS, "exec: task changed from 0x%4.4x to 0x%4.4x", old_task, new_task);
+ }
+ }
+ else
+ {
+ DNBLog ("error: failed to read all_image_infos.infoArrayCount from 0x%8.8llx", (uint64_t)info_array_count_addr);
+ }
+ }
+ break;
+ }
+ else if (m_sent_interrupt_signo != 0 && signo == m_sent_interrupt_signo)
+ {
+ received_interrupt = true;
+ }
+ }
+ }
+
+ if (m_did_exec)
+ {
+ cpu_type_t process_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid);
+ if (m_cpu_type != process_cpu_type)
+ {
+ DNBLog ("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type, process_cpu_type);
+ m_cpu_type = process_cpu_type;
+ DNBArchProtocol::SetArchitecture (process_cpu_type);
+ }
+ m_thread_list.Clear();
+ m_activities.Clear();
+ m_breakpoints.DisableAll();
+ }
+
+ if (m_sent_interrupt_signo != 0)
+ {
+ if (received_interrupt)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): process successfully interrupted with signal %i", m_sent_interrupt_signo);
+
+ // Mark that we received the interrupt signal
+ m_sent_interrupt_signo = 0;
+ // Not check if we had a case where:
+ // 1 - We called MachProcess::Interrupt() but we stopped for another reason
+ // 2 - We called MachProcess::Resume() (but still haven't gotten the interrupt signal)
+ // 3 - We are now incorrectly stopped because we are handling the interrupt signal we missed
+ // 4 - We might need to resume if we stopped only with the interrupt signal that we never handled
+ if (m_auto_resume_signo != 0)
+ {
+ // Only auto_resume if we stopped with _only_ the interrupt signal
+ if (num_task_exceptions == 1)
+ {
+ auto_resume = true;
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): auto resuming due to unhandled interrupt signal %i", m_auto_resume_signo);
+ }
+ m_auto_resume_signo = 0;
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): didn't get signal %i after MachProcess::Interrupt()",
+ m_sent_interrupt_signo);
+ }
+ }
+ }
+
+ // Let all threads recover from stopping and do any clean up based
+ // on the previous thread state (if any).
+ m_thread_list.ProcessDidStop(this);
+ m_activities.Clear();
+
+ // Let each thread know of any exceptions
+ for (i=0; i<m_exception_messages.size(); ++i)
+ {
+ // Let the thread list figure use the MachProcess to forward all exceptions
+ // on down to each thread.
+ if (m_exception_messages[i].state.task_port == task)
+ m_thread_list.NotifyException(m_exception_messages[i].state);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ m_exception_messages[i].Dump();
+ }
+
+ if (DNBLogCheckLogBit(LOG_THREAD))
+ m_thread_list.Dump();
+
+ bool step_more = false;
+ if (m_thread_list.ShouldStop(step_more) && auto_resume == false)
+ {
+ // Wait for the eEventProcessRunningStateChanged event to be reset
+ // before changing state to stopped to avoid race condition with
+ // very fast start/stops
+ struct timespec timeout;
+ //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms
+ DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms
+ m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout);
+ SetState(eStateStopped);
+ }
+ else
+ {
+ // Resume without checking our current state.
+ PrivateResume ();
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle (%llu exceptions).", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
+ }
+ return m_task.TaskPort();
+}
+
+nub_size_t
+MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed)
+{
+ if (m_image_infos_callback != NULL)
+ return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton);
+ return 0;
+}
+
+void
+MachProcess::SharedLibrariesUpdated ( )
+{
+ uint32_t event_bits = eEventSharedLibsStateChange;
+ // Set the shared library event bit to let clients know of shared library
+ // changes
+ m_events.SetEvents(event_bits);
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(event_bits);
+}
+
+void
+MachProcess::SetExitInfo (const char *info)
+{
+ if (info && info[0])
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(\"%s\")", __FUNCTION__, info);
+ m_exit_info.assign(info);
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(NULL)", __FUNCTION__);
+ m_exit_info.clear();
+ }
+}
+
+void
+MachProcess::AppendSTDOUT (char* s, size_t len)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%llu> %s) ...", __FUNCTION__, (uint64_t)len, s);
+ PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
+ m_stdout_data.append(s, len);
+ m_events.SetEvents(eEventStdioAvailable);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(eEventStdioAvailable);
+}
+
+size_t
+MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size);
+ PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0)
+ {
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_stdout_data.data(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_stdout_data.data(), bytes_available);
+ m_stdout_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+nub_addr_t
+MachProcess::GetDYLDAllImageInfosAddress ()
+{
+ DNBError err;
+ return m_task.GetDYLDAllImageInfosAddress(err);
+}
+
+size_t
+MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size)
+{
+ return 0;
+}
+
+void *
+MachProcess::STDIOThread(void *arg)
+{
+ MachProcess *proc = (MachProcess*) arg;
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg);
+
+#if defined (__APPLE__)
+ pthread_setname_np ("stdio monitoring thread");
+#endif
+
+ // We start use a base and more options so we can control if we
+ // are currently using a timeout on the mach_msg. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main thread loop
+ // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle available. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ DNBError err;
+ int stdout_fd = proc->GetStdoutFileDescriptor();
+ int stderr_fd = proc->GetStderrFileDescriptor();
+ if (stdout_fd == stderr_fd)
+ stderr_fd = -1;
+
+ while (stdout_fd >= 0 || stderr_fd >= 0)
+ {
+ ::pthread_testcancel ();
+
+ fd_set read_fds;
+ FD_ZERO (&read_fds);
+ if (stdout_fd >= 0)
+ FD_SET (stdout_fd, &read_fds);
+ if (stderr_fd >= 0)
+ FD_SET (stderr_fd, &read_fds);
+ int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
+
+ int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL);
+ DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+
+ if (num_set_fds < 0)
+ {
+ int select_errno = errno;
+ if (DNBLogCheckLogBit(LOG_PROCESS))
+ {
+ err.SetError (select_errno, DNBError::POSIX);
+ err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+ }
+
+ switch (select_errno)
+ {
+ case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO
+ break;
+ case EBADF: // One of the descriptor sets specified an invalid descriptor.
+ return NULL;
+ break;
+ case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred.
+ case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
+ default: // Other unknown error
+ break;
+ }
+ }
+ else if (num_set_fds == 0)
+ {
+ }
+ else
+ {
+ char s[1024];
+ s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination
+ ssize_t bytes_read = 0;
+ if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stdout_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %zd errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %zd (reached EOF for child STDOUT)", bytes_read);
+ stdout_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+
+ if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stderr_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %zd errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %zd (reached EOF for child STDERR)", bytes_read);
+ stderr_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+ }
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg);
+ return NULL;
+}
+
+
+void
+MachProcess::SignalAsyncProfileData (const char *info)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info);
+ PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex);
+ m_profile_data.push_back(info);
+ m_events.SetEvents(eEventProfileDataAvailable);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(eEventProfileDataAvailable);
+}
+
+
+size_t
+MachProcess::GetAsyncProfileData (char *buf, size_t buf_size)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size);
+ PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex);
+ if (m_profile_data.empty())
+ return 0;
+
+ size_t bytes_available = m_profile_data.front().size();
+ if (bytes_available > 0)
+ {
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_profile_data.front().data(), buf_size);
+ m_profile_data.front().erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_profile_data.front().data(), bytes_available);
+ m_profile_data.erase(m_profile_data.begin());
+ }
+ }
+ return bytes_available;
+}
+
+
+void *
+MachProcess::ProfileThread(void *arg)
+{
+ MachProcess *proc = (MachProcess*) arg;
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg);
+
+#if defined (__APPLE__)
+ pthread_setname_np ("performance profiling thread");
+#endif
+
+ while (proc->IsProfilingEnabled())
+ {
+ nub_state_t state = proc->GetState();
+ if (state == eStateRunning)
+ {
+ std::string data = proc->Task().GetProfileData(proc->GetProfileScanType());
+ if (!data.empty())
+ {
+ proc->SignalAsyncProfileData(data.c_str());
+ }
+ }
+ else if ((state == eStateUnloaded) || (state == eStateDetached) || (state == eStateUnloaded))
+ {
+ // Done. Get out of this thread.
+ break;
+ }
+
+ // A simple way to set up the profile interval. We can also use select() or dispatch timer source if necessary.
+ usleep(proc->ProfileInterval());
+ }
+ return NULL;
+}
+
+
+pid_t
+MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len)
+{
+ // Clear out and clean up from any current state
+ Clear();
+ if (pid != 0)
+ {
+ DNBError err;
+ // Make sure the process exists...
+ if (::getpgid (pid) < 0)
+ {
+ err.SetErrorToErrno();
+ const char *err_cstr = err.AsString();
+ ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process");
+ return INVALID_NUB_PROCESS;
+ }
+
+ SetState(eStateAttaching);
+ m_pid = pid;
+ // Let ourselves know we are going to be using SBS or BKS if the correct flag bit is set...
+#if defined (WITH_FBS) || defined (WITH_BKS)
+ bool found_app_flavor = false;
+#endif
+
+#if defined (WITH_FBS)
+ if (!found_app_flavor && IsFBSProcess (pid))
+ {
+ found_app_flavor = true;
+ m_flags |= eMachProcessFlagsUsingFBS;
+ }
+#elif defined (WITH_BKS)
+ if (!found_app_flavor && IsBKSProcess (pid))
+ {
+ found_app_flavor = true;
+ m_flags |= eMachProcessFlagsUsingBKS;
+ }
+#elif defined (WITH_SPRINGBOARD)
+ if (IsSBProcess(pid))
+ m_flags |= eMachProcessFlagsUsingSBS;
+#endif
+ if (!m_task.StartExceptionThread(err))
+ {
+ const char *err_cstr = err.AsString();
+ ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread");
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ errno = 0;
+ if (::ptrace (PT_ATTACHEXC, pid, 0, 0))
+ err.SetError(errno);
+ else
+ err.Clear();
+
+ if (err.Success())
+ {
+ m_flags |= eMachProcessFlagsAttached;
+ // Sleep a bit to let the exception get received and set our process status
+ // to stopped.
+ ::usleep(250000);
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
+ return m_pid;
+ }
+ else
+ {
+ ::snprintf (err_str, err_len, "%s", err.AsString());
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
+ }
+ }
+ return INVALID_NUB_PROCESS;
+}
+
+Genealogy::ThreadActivitySP
+MachProcess::GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out)
+{
+ return m_activities.GetGenealogyInfoForThread (m_pid, tid, m_thread_list, m_task.TaskPort(), timed_out);
+}
+
+Genealogy::ProcessExecutableInfoSP
+MachProcess::GetGenealogyImageInfo (size_t idx)
+{
+ return m_activities.GetProcessExecutableInfosAtIndex (idx);
+}
+
+bool
+MachProcess::GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch)
+{
+ bool success = false;
+
+#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000)
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSOperatingSystemVersion vers = [[NSProcessInfo processInfo] operatingSystemVersion];
+ if (major)
+ *major = vers.majorVersion;
+ if (minor)
+ *minor = vers.minorVersion;
+ if (patch)
+ *patch = vers.patchVersion;
+
+ success = true;
+
+ [pool drain];
+#endif
+
+ return success;
+}
+
+// Do the process specific setup for attach. If this returns NULL, then there's no
+// platform specific stuff to be done to wait for the attach. If you get non-null,
+// pass that token to the CheckForProcess method, and then to CleanupAfterAttach.
+
+// Call PrepareForAttach before attaching to a process that has not yet launched
+// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach.
+// You should call CleanupAfterAttach to free the token, and do whatever other
+// cleanup seems good.
+
+const void *
+MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &attach_err)
+{
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
+ // Tell SpringBoard to halt the next launch of this application on startup.
+
+ if (!waitfor)
+ return NULL;
+
+ const char *app_ext = strstr(path, ".app");
+ const bool is_app = app_ext != NULL && (app_ext[4] == '\0' || app_ext[4] == '/');
+ if (!is_app)
+ {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, "
+ "we can't tell springboard to wait for launch...",
+ path);
+ return NULL;
+ }
+
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorDefault)
+ launch_flavor = eLaunchFlavorFBS;
+ if (launch_flavor != eLaunchFlavorFBS)
+ return NULL;
+#elif defined (WITH_BKS)
+ if (launch_flavor == eLaunchFlavorDefault)
+ launch_flavor = eLaunchFlavorBKS;
+ if (launch_flavor != eLaunchFlavorBKS)
+ return NULL;
+#elif defined (WITH_SPRINGBOARD)
+ if (launch_flavor == eLaunchFlavorDefault)
+ launch_flavor = eLaunchFlavorSpringBoard;
+ if (launch_flavor != eLaunchFlavorSpringBoard)
+ return NULL;
+#endif
+
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+
+ CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), attach_err);
+ std::string bundleIDStr;
+ CFString::UTF8(bundleIDCFStr, bundleIDStr);
+ DNBLogThreadedIf(LOG_PROCESS,
+ "CopyBundleIDForPath (%s, err_str) returned @\"%s\"",
+ app_bundle_path.c_str (),
+ bundleIDStr.c_str());
+
+ if (bundleIDCFStr == NULL)
+ {
+ return NULL;
+ }
+
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS)
+ {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *stdio_path = nil;
+ NSFileManager *file_manager = [NSFileManager defaultManager];
+ const char *null_path = "/dev/null";
+ stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)];
+
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+
+ DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", "
+ "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )",
+ bundleIDStr.c_str(),
+ null_path);
+
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath];
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDebugOnNextLaunch];
+
+ [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions];
+
+ FBSSystemService *system_service = [[FBSSystemService alloc] init];
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block FBSOpenApplicationErrorCode attach_error_code = FBSOpenApplicationErrorCodeNone;
+
+ NSString *bundleIDNSStr = (NSString *) bundleIDCFStr;
+
+ [system_service openApplication: bundleIDNSStr
+ options: options
+ clientPort: client_port
+ withResult: ^(NSError *error)
+ {
+ // The system service will cleanup the client port we created for us.
+ if (error)
+ attach_error_code = (FBSOpenApplicationErrorCode)[error code];
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }
+ ];
+
+ const uint32_t timeout_secs = 9;
+
+ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ if (!success)
+ {
+ DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
+ attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete.");
+ attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ }
+ else if (attach_error_code != FBSOpenApplicationErrorCodeNone)
+ {
+ SetFBSError (attach_error_code, attach_err);
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld",
+ bundleIDStr.c_str(),
+ (NSInteger) attach_error_code);
+ }
+ dispatch_release(semaphore);
+ [pool drain];
+ }
+#endif
+#if defined (WITH_BKS)
+ if (launch_flavor == eLaunchFlavorBKS)
+ {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *stdio_path = nil;
+ NSFileManager *file_manager = [NSFileManager defaultManager];
+ const char *null_path = "/dev/null";
+ stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)];
+
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+
+ DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", "
+ "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )",
+ bundleIDStr.c_str(),
+ null_path);
+
+ [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath];
+ [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDebugOnNextLaunch];
+
+ [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
+
+ BKSSystemService *system_service = [[BKSSystemService alloc] init];
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block BKSOpenApplicationErrorCode attach_error_code = BKSOpenApplicationErrorCodeNone;
+
+ NSString *bundleIDNSStr = (NSString *) bundleIDCFStr;
+
+ [system_service openApplication: bundleIDNSStr
+ options: options
+ clientPort: client_port
+ withResult: ^(NSError *error)
+ {
+ // The system service will cleanup the client port we created for us.
+ if (error)
+ attach_error_code = (BKSOpenApplicationErrorCode)[error code];
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }
+ ];
+
+ const uint32_t timeout_secs = 9;
+
+ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ if (!success)
+ {
+ DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
+ attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete.");
+ attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ }
+ else if (attach_error_code != BKSOpenApplicationErrorCodeNone)
+ {
+ SetBKSError (attach_error_code, attach_err);
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld",
+ bundleIDStr.c_str(),
+ attach_error_code);
+ }
+ dispatch_release(semaphore);
+ [pool drain];
+ }
+#endif
+
+#if defined (WITH_SPRINGBOARD)
+ if (launch_flavor == eLaunchFlavorSpringBoard)
+ {
+ SBSApplicationLaunchError sbs_error = 0;
+
+ const char *stdout_err = "/dev/null";
+ CFString stdio_path;
+ stdio_path.SetFileSystemRepresentation (stdout_err);
+
+ DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", "
+ "SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )",
+ bundleIDStr.c_str(),
+ stdout_err,
+ stdout_err);
+
+ sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ NULL, // launch_argv.get(),
+ NULL, // launch_envp.get(), // CFDictionaryRef environment
+ stdio_path.get(),
+ stdio_path.get(),
+ SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger);
+
+ if (sbs_error != SBSApplicationLaunchErrorSuccess)
+ {
+ attach_err.SetError(sbs_error, DNBError::SpringBoard);
+ return NULL;
+ }
+ }
+#endif // WITH_SPRINGBOARD
+
+ DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
+ return bundleIDCFStr;
+# else // !(defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS))
+ return NULL;
+#endif
+}
+
+// Pass in the token you got from PrepareForAttach. If there is a process
+// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS
+// will be returned.
+
+nub_process_t
+MachProcess::CheckForProcess (const void *attach_token, nub_launch_flavor_t launch_flavor)
+{
+ if (attach_token == NULL)
+ return INVALID_NUB_PROCESS;
+
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS)
+ {
+ NSString *bundleIDNSStr = (NSString *) attach_token;
+ FBSSystemService *systemService = [[FBSSystemService alloc] init];
+ pid_t pid = [systemService pidForApplication: bundleIDNSStr];
+ [systemService release];
+ if (pid == 0)
+ return INVALID_NUB_PROCESS;
+ else
+ return pid;
+ }
+#endif
+
+#if defined (WITH_BKS)
+ if (launch_flavor == eLaunchFlavorBKS)
+ {
+ NSString *bundleIDNSStr = (NSString *) attach_token;
+ BKSSystemService *systemService = [[BKSSystemService alloc] init];
+ pid_t pid = [systemService pidForApplication: bundleIDNSStr];
+ [systemService release];
+ if (pid == 0)
+ return INVALID_NUB_PROCESS;
+ else
+ return pid;
+ }
+#endif
+
+#if defined (WITH_SPRINGBOARD)
+ if (launch_flavor == eLaunchFlavorSpringBoard)
+ {
+ CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
+ Boolean got_it;
+ nub_process_t attach_pid;
+ got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid);
+ if (got_it)
+ return attach_pid;
+ else
+ return INVALID_NUB_PROCESS;
+ }
+#endif
+ return INVALID_NUB_PROCESS;
+}
+
+// Call this to clean up after you have either attached or given up on the attach.
+// Pass true for success if you have attached, false if you have not.
+// The token will also be freed at this point, so you can't use it after calling
+// this method.
+
+void
+MachProcess::CleanupAfterAttach (const void *attach_token, nub_launch_flavor_t launch_flavor, bool success, DNBError &err_str)
+{
+ if (attach_token == NULL)
+ return;
+
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS)
+ {
+ if (!success)
+ {
+ FBSCleanupAfterAttach (attach_token, err_str);
+ }
+ CFRelease((CFStringRef) attach_token);
+ }
+#endif
+
+#if defined (WITH_BKS)
+
+ if (launch_flavor == eLaunchFlavorBKS)
+ {
+ if (!success)
+ {
+ BKSCleanupAfterAttach (attach_token, err_str);
+ }
+ CFRelease((CFStringRef) attach_token);
+ }
+#endif
+
+#if defined (WITH_SPRINGBOARD)
+ // Tell SpringBoard to cancel the debug on next launch of this application
+ // if we failed to attach
+ if (launch_flavor == eMachProcessFlagsUsingSpringBoard)
+ {
+ if (!success)
+ {
+ SBSApplicationLaunchError sbs_error = 0;
+ CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
+
+ sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
+ (CFURLRef)NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SBSApplicationCancelDebugOnNextLaunch);
+
+ if (sbs_error != SBSApplicationLaunchErrorSuccess)
+ {
+ err_str.SetError(sbs_error, DNBError::SpringBoard);
+ return;
+ }
+ }
+
+ CFRelease((CFStringRef) attach_token);
+ }
+#endif
+}
+
+pid_t
+MachProcess::LaunchForDebug
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ const char *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ bool no_stdio,
+ nub_launch_flavor_t launch_flavor,
+ int disable_aslr,
+ const char *event_data,
+ DNBError &launch_err
+)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+
+ switch (launch_flavor)
+ {
+ case eLaunchFlavorForkExec:
+ m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err);
+ break;
+#ifdef WITH_FBS
+ case eLaunchFlavorFBS:
+ {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/'))
+ {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ m_flags |= eMachProcessFlagsUsingFBS;
+ if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
+ else
+ break; // We tried a FBS launch, but didn't succeed lets get out
+ }
+ }
+ break;
+#endif
+#ifdef WITH_BKS
+ case eLaunchFlavorBKS:
+ {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/'))
+ {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ m_flags |= eMachProcessFlagsUsingBKS;
+ if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
+ else
+ break; // We tried a BKS launch, but didn't succeed lets get out
+ }
+ }
+ break;
+#endif
+#ifdef WITH_SPRINGBOARD
+
+ case eLaunchFlavorSpringBoard:
+ {
+ // .../whatever.app/whatever ?
+ // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in "com.apple.whatever" here
+ const char *app_ext = strstr (path, ".app/");
+ if (app_ext == NULL)
+ {
+ // .../whatever.app ?
+ int len = strlen (path);
+ if (len > 5)
+ {
+ if (strcmp (path + len - 4, ".app") == 0)
+ {
+ app_ext = path + len - 4;
+ }
+ }
+ }
+ if (app_ext)
+ {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ if (SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
+ else
+ break; // We tried a springboard launch, but didn't succeed lets get out
+ }
+ }
+ break;
+
+#endif
+
+ case eLaunchFlavorPosixSpawn:
+ m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path,
+ DNBArchProtocol::GetArchitecture (),
+ argv,
+ envp,
+ working_directory,
+ stdin_path,
+ stdout_path,
+ stderr_path,
+ no_stdio,
+ this,
+ disable_aslr,
+ launch_err);
+ break;
+
+ default:
+ // Invalid launch
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ return INVALID_NUB_PROCESS;
+ }
+
+ if (m_pid == INVALID_NUB_PROCESS)
+ {
+ // If we don't have a valid process ID and no one has set the error,
+ // then return a generic error
+ if (launch_err.Success())
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ }
+ else
+ {
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i=0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+
+ m_task.StartExceptionThread(launch_err);
+ if (launch_err.Fail())
+ {
+ if (launch_err.AsString() == NULL)
+ launch_err.SetErrorString("unable to start the exception thread");
+ DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting.");
+ ::ptrace (PT_KILL, m_pid, 0, 0);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ StartSTDIOThread();
+
+ if (launch_flavor == eLaunchFlavorPosixSpawn)
+ {
+
+ SetState (eStateAttaching);
+ errno = 0;
+ int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0)
+ {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid);
+ launch_err.Clear();
+ }
+ else
+ {
+ SetState (eStateExited);
+ DNBError ptrace_err(errno, DNBError::POSIX);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid %d (err = %i, errno = %i (%s))", m_pid, err, ptrace_err.Error(), ptrace_err.AsString());
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ }
+ }
+ else
+ {
+ launch_err.Clear();
+ }
+ }
+ return m_pid;
+}
+
+pid_t
+MachProcess::PosixSpawnChildForPTraceDebugging
+(
+ const char *path,
+ cpu_type_t cpu_type,
+ char const *argv[],
+ char const *envp[],
+ const char *working_directory,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ bool no_stdio,
+ MachProcess* process,
+ int disable_aslr,
+ DNBError& err
+)
+{
+ posix_spawnattr_t attr;
+ short flags;
+ DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, working_dir=%s, stdin=%s, stdout=%s stderr=%s, no-stdio=%i)",
+ __FUNCTION__,
+ path,
+ argv,
+ envp,
+ working_directory,
+ stdin_path,
+ stdout_path,
+ stderr_path,
+ no_stdio);
+
+ err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_init ( &attr )");
+ if (err.Fail())
+ return INVALID_NUB_PROCESS;
+
+ flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
+ if (disable_aslr)
+ flags |= _POSIX_SPAWN_DISABLE_ASLR;
+
+ sigset_t no_signals;
+ sigset_t all_signals;
+ sigemptyset (&no_signals);
+ sigfillset (&all_signals);
+ ::posix_spawnattr_setsigmask(&attr, &no_signals);
+ ::posix_spawnattr_setsigdefault(&attr, &all_signals);
+
+ err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : "");
+ if (err.Fail())
+ return INVALID_NUB_PROCESS;
+
+ // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail
+ // and we will fail to continue with our process...
+
+ // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment....
+
+#if !defined(__arm__)
+
+ // We don't need to do this for ARM, and we really shouldn't now that we
+ // have multiple CPU subtypes and no posix_spawnattr call that allows us
+ // to set which CPU subtype to launch...
+ if (cpu_type != 0)
+ {
+ size_t ocount = 0;
+ err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu_type, (uint64_t)ocount);
+
+ if (err.Fail() != 0 || ocount != 1)
+ return INVALID_NUB_PROCESS;
+ }
+#endif
+
+ PseudoTerminal pty;
+
+ posix_spawn_file_actions_t file_actions;
+ err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX);
+ int file_actions_valid = err.Success();
+ if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )");
+ int pty_error = -1;
+ pid_t pid = INVALID_NUB_PROCESS;
+ if (file_actions_valid)
+ {
+ if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && !no_stdio)
+ {
+ pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
+ if (pty_error == PseudoTerminal::success)
+ {
+ stdin_path = stdout_path = stderr_path = pty.SlaveName();
+ }
+ }
+
+ // if no_stdio or std paths not supplied, then route to "/dev/null".
+ if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0')
+ stdin_path = "/dev/null";
+ if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0')
+ stdout_path = "/dev/null";
+ if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0')
+ stderr_path = "/dev/null";
+
+ err.SetError( ::posix_spawn_file_actions_addopen (&file_actions,
+ STDIN_FILENO,
+ stdin_path,
+ O_RDONLY | O_NOCTTY,
+ 0),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS))
+ err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDIN_FILENO, path='%s')", stdin_path);
+
+ err.SetError( ::posix_spawn_file_actions_addopen (&file_actions,
+ STDOUT_FILENO,
+ stdout_path,
+ O_WRONLY | O_NOCTTY | O_CREAT,
+ 0640),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS))
+ err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDOUT_FILENO, path='%s')", stdout_path);
+
+ err.SetError( ::posix_spawn_file_actions_addopen (&file_actions,
+ STDERR_FILENO,
+ stderr_path,
+ O_WRONLY | O_NOCTTY | O_CREAT,
+ 0640),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS))
+ err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDERR_FILENO, path='%s')", stderr_path);
+
+ // TODO: Verify if we can set the working directory back immediately
+ // after the posix_spawnp call without creating a race condition???
+ if (working_directory)
+ ::chdir (working_directory);
+
+ err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp);
+ }
+ else
+ {
+ // TODO: Verify if we can set the working directory back immediately
+ // after the posix_spawnp call without creating a race condition???
+ if (working_directory)
+ ::chdir (working_directory);
+
+ err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp);
+ }
+
+ // We have seen some cases where posix_spawnp was returning a valid
+ // looking pid even when an error was returned, so clear it out
+ if (err.Fail())
+ pid = INVALID_NUB_PROCESS;
+
+ if (pty_error == 0)
+ {
+ if (process != NULL)
+ {
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ }
+ ::posix_spawnattr_destroy (&attr);
+
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess (pid);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x", __FUNCTION__, pid, pid_cpu_type);
+ if (pid_cpu_type)
+ DNBArchProtocol::SetArchitecture (pid_cpu_type);
+ }
+
+ if (file_actions_valid)
+ {
+ DNBError err2;
+ err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX);
+ if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )");
+ }
+
+ return pid;
+}
+
+uint32_t
+MachProcess::GetCPUTypeForLocalProcess (pid_t pid)
+{
+ int mib[CTL_MAXNAME]={0,};
+ size_t len = CTL_MAXNAME;
+ if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
+ return 0;
+
+ mib[len] = pid;
+ len++;
+
+ cpu_type_t cpu;
+ size_t cpu_len = sizeof(cpu);
+ if (::sysctl (mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
+ cpu = 0;
+ return cpu;
+}
+
+pid_t
+MachProcess::ForkChildForPTraceDebugging
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ MachProcess* process,
+ DNBError& launch_err
+)
+{
+ PseudoTerminal::Error pty_error = PseudoTerminal::success;
+
+ // Use a fork that ties the child process's stdin/out/err to a pseudo
+ // terminal so we can read it in our MachProcess::STDIOThread
+ // as unbuffered io.
+ PseudoTerminal pty;
+ pid_t pid = pty.Fork(pty_error);
+
+ if (pid < 0)
+ {
+ //--------------------------------------------------------------
+ // Error during fork.
+ //--------------------------------------------------------------
+ return pid;
+ }
+ else if (pid == 0)
+ {
+ //--------------------------------------------------------------
+ // Child process
+ //--------------------------------------------------------------
+ ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process
+ ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions
+
+ // If our parent is setgid, lets make sure we don't inherit those
+ // extra powers due to nepotism.
+ if (::setgid (getgid ()) == 0)
+ {
+
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (0, 0); // Set the child process group to match its pid
+
+ // Sleep a bit to before the exec call
+ ::sleep (1);
+
+ // Turn this process into
+ ::execv (path, (char * const *)argv);
+ }
+ // Exit with error code. Child process should have taken
+ // over in above exec call and if the exec fails it will
+ // exit the child process below.
+ ::exit (127);
+ }
+ else
+ {
+ //--------------------------------------------------------------
+ // Parent process
+ //--------------------------------------------------------------
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (pid, pid); // Set the child process group to match its pid
+
+ if (process != NULL)
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ }
+ return pid;
+}
+
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
+// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
+// or NULL if there was some problem getting the bundle id.
+static CFStringRef
+CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str)
+{
+ CFBundle bundle(app_bundle_path);
+ CFStringRef bundleIDCFStr = bundle.GetIdentifier();
+ std::string bundleID;
+ if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
+ {
+ struct stat app_bundle_stat;
+ char err_msg[PATH_MAX];
+
+ if (::stat (app_bundle_path, &app_bundle_stat) < 0)
+ {
+ err_str.SetError(errno, DNBError::POSIX);
+ snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
+ }
+ else
+ {
+ err_str.SetError(-1, DNBError::Generic);
+ snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
+ }
+ return NULL;
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
+ CFRetain (bundleIDCFStr);
+
+ return bundleIDCFStr;
+}
+#endif // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
+#ifdef WITH_SPRINGBOARD
+
+pid_t
+MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, DNBError &launch_err)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio, this, launch_err);
+ if (m_pid != 0)
+ {
+ m_flags |= eMachProcessFlagsUsingSBS;
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i=0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+ m_task.StartExceptionThread(launch_err);
+
+ if (launch_err.Fail())
+ {
+ if (launch_err.AsString() == NULL)
+ launch_err.SetErrorString("unable to start the exception thread");
+ DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting.");
+ ::ptrace (PT_KILL, m_pid, 0, 0);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ StartSTDIOThread();
+ SetState (eStateAttaching);
+ int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0)
+ {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
+ }
+ else
+ {
+ SetState (eStateExited);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
+ }
+ }
+ return m_pid;
+}
+
+#include <servers/bootstrap.h>
+
+pid_t
+MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process);
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+
+ if (argv[0] == NULL)
+ return INVALID_NUB_PROCESS;
+
+ size_t argc = 0;
+ // Count the number of arguments
+ while (argv[argc] != NULL)
+ argc++;
+
+ // Enumerate the arguments
+ size_t first_launch_arg_idx = 1;
+ CFReleaser<CFMutableArrayRef> launch_argv;
+
+ if (argv[first_launch_arg_idx])
+ {
+ size_t launch_argc = argc > 0 ? argc - 1 : 0;
+ launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks));
+ size_t i;
+ char const *arg;
+ CFString launch_arg;
+ for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
+ {
+ launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8));
+ if (launch_arg.get() != NULL)
+ CFArrayAppendValue(launch_argv.get(), launch_arg.get());
+ else
+ break;
+ }
+ }
+
+ // Next fill in the arguments dictionary. Note, the envp array is of the form
+ // Variable=value but SpringBoard wants a CF dictionary. So we have to convert
+ // this here.
+
+ CFReleaser<CFMutableDictionaryRef> launch_envp;
+
+ if (envp[0])
+ {
+ launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ const char *value;
+ int name_len;
+ CFString name_string, value_string;
+
+ for (int i = 0; envp[i] != NULL; i++)
+ {
+ value = strstr (envp[i], "=");
+
+ // If the name field is empty or there's no =, skip it. Somebody's messing with us.
+ if (value == NULL || value == envp[i])
+ continue;
+
+ name_len = value - envp[i];
+
+ // Now move value over the "="
+ value++;
+
+ name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false));
+ value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
+ CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get());
+ }
+ }
+
+ CFString stdio_path;
+
+ PseudoTerminal pty;
+ if (!no_stdio)
+ {
+ PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
+ if (pty_err == PseudoTerminal::success)
+ {
+ const char* slave_name = pty.SlaveName();
+ DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
+ if (slave_name && slave_name[0])
+ {
+ ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
+ stdio_path.SetFileSystemRepresentation (slave_name);
+ }
+ }
+ }
+
+ if (stdio_path.get() == NULL)
+ {
+ stdio_path.SetFileSystemRepresentation ("/dev/null");
+ }
+
+ CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err);
+ if (bundleIDCFStr == NULL)
+ return INVALID_NUB_PROCESS;
+
+ // This is just for logging:
+ std::string bundleID;
+ CFString::UTF8(bundleIDCFStr, bundleID);
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__);
+
+ // Find SpringBoard
+ SBSApplicationLaunchError sbs_error = 0;
+ sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ launch_argv.get(),
+ launch_envp.get(), // CFDictionaryRef environment
+ stdio_path.get(),
+ stdio_path.get(),
+ SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
+
+
+ launch_err.SetError(sbs_error, DNBError::SpringBoard);
+
+ if (sbs_error == SBSApplicationLaunchErrorSuccess)
+ {
+ static const useconds_t pid_poll_interval = 200000;
+ static const useconds_t pid_poll_timeout = 30000000;
+
+ useconds_t pid_poll_total = 0;
+
+ nub_process_t pid = INVALID_NUB_PROCESS;
+ Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired
+ // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started
+ // yet, or that it died very quickly (if you weren't using waitForDebugger).
+ while (!pid_found && pid_poll_total < pid_poll_timeout)
+ {
+ usleep (pid_poll_interval);
+ pid_poll_total += pid_poll_interval;
+ DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str());
+ pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ }
+
+ CFRelease (bundleIDCFStr);
+ if (pid_found)
+ {
+ if (process != NULL)
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
+ }
+ else
+ {
+ DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str());
+ }
+ return pid;
+ }
+
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error);
+ return INVALID_NUB_PROCESS;
+}
+
+#endif // #ifdef WITH_SPRINGBOARD
+
+
+
+#if defined (WITH_BKS) || defined (WITH_FBS)
+pid_t
+MachProcess::BoardServiceLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err)
+{
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ m_pid = BoardServiceForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err);
+ if (m_pid != 0)
+ {
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i=0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+ m_task.StartExceptionThread(launch_err);
+
+ if (launch_err.Fail())
+ {
+ if (launch_err.AsString() == NULL)
+ launch_err.SetErrorString("unable to start the exception thread");
+ DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting.");
+ ::ptrace (PT_KILL, m_pid, 0, 0);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ StartSTDIOThread();
+ SetState (eStateAttaching);
+ int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0)
+ {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
+ }
+ else
+ {
+ SetState (eStateExited);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
+ }
+ }
+ return m_pid;
+}
+
+pid_t
+MachProcess::BoardServiceForkChildForPTraceDebugging (const char *app_bundle_path,
+ char const *argv[],
+ char const *envp[],
+ bool no_stdio,
+ bool disable_aslr,
+ const char *event_data,
+ DNBError &launch_err)
+{
+ if (argv[0] == NULL)
+ return INVALID_NUB_PROCESS;
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, this);
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ size_t argc = 0;
+ // Count the number of arguments
+ while (argv[argc] != NULL)
+ argc++;
+
+ // Enumerate the arguments
+ size_t first_launch_arg_idx = 1;
+
+ NSMutableArray *launch_argv = nil;
+
+ if (argv[first_launch_arg_idx])
+ {
+ size_t launch_argc = argc > 0 ? argc - 1 : 0;
+ launch_argv = [NSMutableArray arrayWithCapacity: launch_argc];
+ size_t i;
+ char const *arg;
+ NSString *launch_arg;
+ for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
+ {
+ launch_arg = [NSString stringWithUTF8String: arg];
+ // FIXME: Should we silently eat an argument that we can't convert into a UTF8 string?
+ if (launch_arg != nil)
+ [launch_argv addObject: launch_arg];
+ else
+ break;
+ }
+ }
+
+ NSMutableDictionary *launch_envp = nil;
+ if (envp[0])
+ {
+ launch_envp = [[NSMutableDictionary alloc] init];
+ const char *value;
+ int name_len;
+ NSString *name_string, *value_string;
+
+ for (int i = 0; envp[i] != NULL; i++)
+ {
+ value = strstr (envp[i], "=");
+
+ // If the name field is empty or there's no =, skip it. Somebody's messing with us.
+ if (value == NULL || value == envp[i])
+ continue;
+
+ name_len = value - envp[i];
+
+ // Now move value over the "="
+ value++;
+ name_string = [[NSString alloc] initWithBytes: envp[i] length: name_len encoding: NSUTF8StringEncoding];
+ value_string = [NSString stringWithUTF8String: value];
+ [launch_envp setObject: value_string forKey: name_string];
+ }
+ }
+
+ NSString *stdio_path = nil;
+ NSFileManager *file_manager = [NSFileManager defaultManager];
+
+ PseudoTerminal pty;
+ if (!no_stdio)
+ {
+ PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
+ if (pty_err == PseudoTerminal::success)
+ {
+ const char* slave_name = pty.SlaveName();
+ DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
+ if (slave_name && slave_name[0])
+ {
+ ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
+ stdio_path = [file_manager stringWithFileSystemRepresentation: slave_name length: strlen(slave_name)];
+ }
+ }
+ }
+
+ if (stdio_path == nil)
+ {
+ const char *null_path = "/dev/null";
+ stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)];
+ }
+
+ CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err);
+ if (bundleIDCFStr == NULL)
+ {
+ [pool drain];
+ return INVALID_NUB_PROCESS;
+ }
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *) bundleIDCFStr;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
+
+ NSMutableDictionary *options = nullptr;
+ pid_t return_pid = INVALID_NUB_PROCESS;
+ bool success = false;
+
+#ifdef WITH_BKS
+ if (m_flags & eMachProcessFlagsUsingBKS)
+ {
+ options = BKSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data);
+ success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid);
+ }
+#endif
+#ifdef WITH_FBS
+ if (m_flags & eMachProcessFlagsUsingFBS)
+ {
+ options = FBSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data);
+ success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid);
+ }
+#endif
+
+ if (success)
+ {
+ int master_fd = pty.ReleaseMasterFD();
+ SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ CFString::UTF8(bundleIDCFStr, m_bundle_id);
+ }
+
+ [pool drain];
+
+ return return_pid;
+}
+
+bool
+MachProcess::BoardServiceSendEvent (const char *event_data, DNBError &send_err)
+{
+ bool return_value = true;
+
+ if (event_data == NULL || *event_data == '\0')
+ {
+ DNBLogError ("SendEvent called with NULL event data.");
+ send_err.SetErrorString("SendEvent called with empty event data");
+ return false;
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (strcmp (event_data, "BackgroundApplication") == 0)
+ {
+ // This is an event I cooked up. What you actually do is foreground the system app, so:
+#ifdef WITH_BKS
+ if (m_flags & eMachProcessFlagsUsingBKS)
+ {
+ return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL);
+ }
+#endif
+#ifdef WITH_FBS
+ if (m_flags & eMachProcessFlagsUsingFBS)
+ {
+ return_value = FBSCallOpenApplicationFunction(nil, nil, send_err, NULL);
+ }
+#endif
+ if (!return_value)
+ {
+ DNBLogError ("Failed to background application, error: %s.", send_err.AsString());
+ }
+ }
+ else
+ {
+ if (m_bundle_id.empty())
+ {
+ // See if we can figure out the bundle ID for this PID:
+
+ DNBLogError ("Tried to send event \"%s\" to a process that has no bundle ID.", event_data);
+ return false;
+ }
+
+ NSString *bundleIDNSStr = [NSString stringWithUTF8String:m_bundle_id.c_str()];
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+
+#ifdef WITH_BKS
+ if (m_flags & eMachProcessFlagsUsingBKS)
+ {
+ if (!BKSAddEventDataToOptions(options, event_data, send_err))
+ {
+ [pool drain];
+ return false;
+ }
+ return_value = BKSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL);
+ DNBLogThreadedIf (LOG_PROCESS, "Called BKSCallOpenApplicationFunction to send event.");
+
+ }
+#endif
+#ifdef WITH_FBS
+ if (m_flags & eMachProcessFlagsUsingFBS)
+ {
+ if (!FBSAddEventDataToOptions(options, event_data, send_err))
+ {
+ [pool drain];
+ return false;
+ }
+ return_value = FBSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL);
+ DNBLogThreadedIf (LOG_PROCESS, "Called FBSCallOpenApplicationFunction to send event.");
+ }
+#endif
+
+ if (!return_value)
+ {
+ DNBLogError ("Failed to send event: %s, error: %s.", event_data, send_err.AsString());
+ }
+ }
+
+ [pool drain];
+ return return_value;
+}
+#endif // defined(WITH_BKS) || defined (WITH_FBS)
+
+#ifdef WITH_BKS
+void
+MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str)
+{
+ bool success;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *) attach_token;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
+
+ // First we have the debug sub-dictionary:
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
+
+ success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL);
+
+ if (!success)
+ {
+ DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString());
+ }
+
+ [pool drain];
+}
+#endif // WITH_BKS
+
+#ifdef WITH_FBS
+void
+MachProcess::FBSCleanupAfterAttach (const void *attach_token, DNBError &err_str)
+{
+ bool success;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *) attach_token;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
+
+ // First we have the debug sub-dictionary:
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyCancelDebugOnNextLaunch];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions];
+
+ success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL);
+
+ if (!success)
+ {
+ DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString());
+ }
+
+ [pool drain];
+}
+#endif // WITH_FBS
diff --git a/tools/debugserver/source/MacOSX/MachTask.h b/tools/debugserver/source/MacOSX/MachTask.h
new file mode 100644
index 000000000000..96b991478c78
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachTask.h
@@ -0,0 +1,134 @@
+//===-- MachTask.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// MachTask.h
+// debugserver
+//
+// Created by Greg Clayton on 12/5/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachTask_h__
+#define __MachTask_h__
+
+// C Includes
+#include <mach/mach.h>
+#include <sys/socket.h>
+// C++ Includes
+#include <map>
+#include <string>
+// Other libraries and framework includes
+// Project includes
+#include "DNBDefs.h"
+#include "MachException.h"
+#include "MachVMMemory.h"
+#include "PThreadMutex.h"
+
+class MachProcess;
+
+typedef uint64_t MachMallocEventId;
+
+enum MachMallocEventType
+{
+ eMachMallocEventTypeAlloc = 2,
+ eMachMallocEventTypeDealloc = 4,
+ eMachMallocEventTypeOther = 1
+};
+
+struct MachMallocEvent
+{
+ mach_vm_address_t m_base_address;
+ uint64_t m_size;
+ MachMallocEventType m_event_type;
+ MachMallocEventId m_event_id;
+};
+
+class MachTask
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ MachTask (MachProcess *process);
+ virtual ~MachTask ();
+
+ void Clear ();
+
+ kern_return_t Suspend ();
+ kern_return_t Resume ();
+
+ nub_size_t ReadMemory (nub_addr_t addr, nub_size_t size, void *buf);
+ nub_size_t WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf);
+ int GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info);
+ std::string GetProfileData (DNBProfileDataScanType scanType);
+
+ nub_addr_t AllocateMemory (nub_size_t size, uint32_t permissions);
+ nub_bool_t DeallocateMemory (nub_addr_t addr);
+
+ mach_port_t ExceptionPort () const;
+ bool ExceptionPortIsValid () const;
+ kern_return_t SaveExceptionPortInfo ();
+ kern_return_t RestoreExceptionPortInfo ();
+ kern_return_t ShutDownExcecptionThread ();
+
+ bool StartExceptionThread (DNBError &err);
+ nub_addr_t GetDYLDAllImageInfosAddress (DNBError& err);
+ kern_return_t BasicInfo (struct task_basic_info *info);
+ static kern_return_t BasicInfo (task_t task, struct task_basic_info *info);
+ bool IsValid () const;
+ static bool IsValid (task_t task);
+ static void * ExceptionThread (void *arg);
+ task_t TaskPort () const { return m_task; }
+ task_t TaskPortForProcessID (DNBError &err, bool force = false);
+ static task_t TaskPortForProcessID (pid_t pid, DNBError &err, uint32_t num_retries = 10, uint32_t usec_interval = 10000);
+
+ MachProcess * Process () { return m_process; }
+ const MachProcess * Process () const { return m_process; }
+
+ nub_size_t PageSize ();
+
+ bool HasMallocLoggingEnabled ();
+
+ // enumerate the malloc records for a given address (starting with Mac OS X 10.6 Snow Leopard it should include
+ // all allocations that *include* address, rather than just those *starting* at address)
+ bool EnumerateMallocRecords (mach_vm_address_t address,
+ MachMallocEvent *event_buffer,
+ uint32_t buffer_size,
+ uint32_t *count);
+
+ // enumerate every malloc record generated by this task, no matter what the address
+ bool EnumerateMallocRecords (MachMallocEvent *event_buffer,
+ uint32_t buffer_size,
+ uint32_t *count);
+
+ // given a malloc event, report every stack frame that led to this event
+ bool EnumerateMallocFrames (MachMallocEventId event_id,
+ mach_vm_address_t *function_addresses_buffer,
+ uint32_t buffer_size,
+ uint32_t *count);
+
+protected:
+ MachProcess * m_process; // The mach process that owns this MachTask
+ task_t m_task;
+ MachVMMemory m_vm_memory; // Special mach memory reading class that will take care of watching for page and region boundaries
+ MachException::PortInfo
+ m_exc_port_info; // Saved settings for all exception ports
+ pthread_t m_exception_thread; // Thread ID for the exception thread in case we need it
+ mach_port_t m_exception_port; // Exception port on which we will receive child exceptions
+
+ typedef std::map <mach_vm_address_t, size_t> allocation_collection;
+ allocation_collection m_allocations;
+
+private:
+ MachTask(const MachTask&); // Outlaw
+ MachTask& operator=(const MachTask& rhs);// Outlaw
+};
+
+#endif // __MachTask_h__
diff --git a/tools/debugserver/source/MacOSX/MachTask.mm b/tools/debugserver/source/MacOSX/MachTask.mm
new file mode 100644
index 000000000000..9c725663d559
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachTask.mm
@@ -0,0 +1,1144 @@
+//===-- MachTask.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// MachTask.cpp
+// debugserver
+//
+// Created by Greg Clayton on 12/5/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachTask.h"
+
+// C Includes
+
+#include <mach-o/dyld_images.h>
+#include <mach/mach_vm.h>
+#import <sys/sysctl.h>
+
+#if defined (__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+// C++ Includes
+#include <iomanip>
+#include <sstream>
+
+// Other libraries and framework includes
+// Project includes
+#include "CFUtils.h"
+#include "DNB.h"
+#include "DNBError.h"
+#include "DNBLog.h"
+#include "MachProcess.h"
+#include "DNBDataRef.h"
+#include "stack_logging.h"
+
+#ifdef WITH_SPRINGBOARD
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+
+#endif
+
+#ifdef WITH_BKS
+extern "C"
+{
+ #import <Foundation/Foundation.h>
+ #import <BackBoardServices/BackBoardServices.h>
+ #import <BackBoardServices/BKSWatchdogAssertion.h>
+}
+#endif
+
+#include <AvailabilityMacros.h>
+
+#ifdef LLDB_ENERGY
+#include <mach/mach_time.h>
+#include <pmenergy.h>
+#include <pmsample.h>
+#endif
+
+
+//----------------------------------------------------------------------
+// MachTask constructor
+//----------------------------------------------------------------------
+MachTask::MachTask(MachProcess *process) :
+ m_process (process),
+ m_task (TASK_NULL),
+ m_vm_memory (),
+ m_exception_thread (0),
+ m_exception_port (MACH_PORT_NULL)
+{
+ memset(&m_exc_port_info, 0, sizeof(m_exc_port_info));
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+MachTask::~MachTask()
+{
+ Clear();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Suspend
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::Suspend()
+{
+ DNBError err;
+ task_t task = TaskPort();
+ err = ::task_suspend (task);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task);
+ return err.Error();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Resume
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::Resume()
+{
+ struct task_basic_info task_info;
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ DNBError err;
+ err = BasicInfo(task, &task_info);
+
+ if (err.Success())
+ {
+ // task_resume isn't counted like task_suspend calls are, are, so if the
+ // task is not suspended, don't try and resume it since it is already
+ // running
+ if (task_info.suspend_count > 0)
+ {
+ err = ::task_resume (task);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task);
+ }
+ }
+ return err.Error();
+}
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPort
+//----------------------------------------------------------------------
+mach_port_t
+MachTask::ExceptionPort() const
+{
+ return m_exception_port;
+}
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPortIsValid
+//----------------------------------------------------------------------
+bool
+MachTask::ExceptionPortIsValid() const
+{
+ return MACH_PORT_VALID(m_exception_port);
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Clear
+//----------------------------------------------------------------------
+void
+MachTask::Clear()
+{
+ // Do any cleanup needed for this task
+ m_task = TASK_NULL;
+ m_exception_thread = 0;
+ m_exception_port = MACH_PORT_NULL;
+
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::SaveExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::SaveExceptionPortInfo()
+{
+ return m_exc_port_info.Save(TaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::RestoreExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::RestoreExceptionPortInfo()
+{
+ return m_exc_port_info.Restore(TaskPort());
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::ReadMemory
+//----------------------------------------------------------------------
+nub_size_t
+MachTask::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
+{
+ nub_size_t n = 0;
+ task_t task = TaskPort();
+ if (task != TASK_NULL)
+ {
+ n = m_vm_memory.Read(task, addr, buf, size);
+
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::ReadMemory ( addr = 0x%8.8llx, size = %llu, buf = %p) => %llu bytes read", (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n);
+ if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
+ {
+ DNBDataRef data((uint8_t*)buf, n, false);
+ data.Dump(0, static_cast<DNBDataRef::offset_t>(n), addr, DNBDataRef::TypeUInt8, 16);
+ }
+ }
+ return n;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::WriteMemory
+//----------------------------------------------------------------------
+nub_size_t
+MachTask::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
+{
+ nub_size_t n = 0;
+ task_t task = TaskPort();
+ if (task != TASK_NULL)
+ {
+ n = m_vm_memory.Write(task, addr, buf, size);
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, size = %llu, buf = %p) => %llu bytes written", (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n);
+ if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
+ {
+ DNBDataRef data((uint8_t*)buf, n, false);
+ data.Dump(0, static_cast<DNBDataRef::offset_t>(n), addr, DNBDataRef::TypeUInt8, 16);
+ }
+ }
+ return n;
+}
+
+//----------------------------------------------------------------------
+// MachTask::MemoryRegionInfo
+//----------------------------------------------------------------------
+int
+MachTask::GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info)
+{
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return -1;
+
+ int ret = m_vm_memory.GetMemoryRegionInfo(task, addr, region_info);
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::MemoryRegionInfo ( addr = 0x%8.8llx ) => %i (start = 0x%8.8llx, size = 0x%8.8llx, permissions = %u)",
+ (uint64_t)addr,
+ ret,
+ (uint64_t)region_info->addr,
+ (uint64_t)region_info->size,
+ region_info->permissions);
+ return ret;
+}
+
+#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
+(r)->tv_sec = (a)->seconds; \
+(r)->tv_usec = (a)->microseconds; \
+} while (0)
+
+// We should consider moving this into each MacThread.
+static void get_threads_profile_data(DNBProfileDataScanType scanType, task_t task, nub_process_t pid, std::vector<uint64_t> &threads_id, std::vector<std::string> &threads_name, std::vector<uint64_t> &threads_used_usec)
+{
+ kern_return_t kr;
+ thread_act_array_t threads;
+ mach_msg_type_number_t tcnt;
+
+ kr = task_threads(task, &threads, &tcnt);
+ if (kr != KERN_SUCCESS)
+ return;
+
+ for (mach_msg_type_number_t i = 0; i < tcnt; i++)
+ {
+ thread_identifier_info_data_t identifier_info;
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = ::thread_info(threads[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&identifier_info, &count);
+ if (kr != KERN_SUCCESS) continue;
+
+ thread_basic_info_data_t basic_info;
+ count = THREAD_BASIC_INFO_COUNT;
+ kr = ::thread_info(threads[i], THREAD_BASIC_INFO, (thread_info_t)&basic_info, &count);
+ if (kr != KERN_SUCCESS) continue;
+
+ if ((basic_info.flags & TH_FLAGS_IDLE) == 0)
+ {
+ nub_thread_t tid = MachThread::GetGloballyUniqueThreadIDForMachPortID (threads[i]);
+ threads_id.push_back(tid);
+
+ if ((scanType & eProfileThreadName) && (identifier_info.thread_handle != 0))
+ {
+ struct proc_threadinfo proc_threadinfo;
+ int len = ::proc_pidinfo(pid, PROC_PIDTHREADINFO, identifier_info.thread_handle, &proc_threadinfo, PROC_PIDTHREADINFO_SIZE);
+ if (len && proc_threadinfo.pth_name[0])
+ {
+ threads_name.push_back(proc_threadinfo.pth_name);
+ }
+ else
+ {
+ threads_name.push_back("");
+ }
+ }
+ else
+ {
+ threads_name.push_back("");
+ }
+ struct timeval tv;
+ struct timeval thread_tv;
+ TIME_VALUE_TO_TIMEVAL(&basic_info.user_time, &thread_tv);
+ TIME_VALUE_TO_TIMEVAL(&basic_info.system_time, &tv);
+ timeradd(&thread_tv, &tv, &thread_tv);
+ uint64_t used_usec = thread_tv.tv_sec * 1000000ULL + thread_tv.tv_usec;
+ threads_used_usec.push_back(used_usec);
+ }
+
+ mach_port_deallocate(mach_task_self(), threads[i]);
+ }
+ mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads, tcnt * sizeof(*threads));
+}
+
+#define RAW_HEXBASE std::setfill('0') << std::hex << std::right
+#define DECIMAL std::dec << std::setfill(' ')
+std::string
+MachTask::GetProfileData (DNBProfileDataScanType scanType)
+{
+ std::string result;
+
+ static int32_t numCPU = -1;
+ struct host_cpu_load_info host_info;
+ if (scanType & eProfileHostCPU)
+ {
+ int32_t mib[] = {CTL_HW, HW_AVAILCPU};
+ size_t len = sizeof(numCPU);
+ if (numCPU == -1)
+ {
+ if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) != 0)
+ return result;
+ }
+
+ mach_port_t localHost = mach_host_self();
+ mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
+ kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO, (host_info_t)&host_info, &count);
+ if (kr != KERN_SUCCESS)
+ return result;
+ }
+
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return result;
+
+ pid_t pid = m_process->ProcessID();
+
+ struct task_basic_info task_info;
+ DNBError err;
+ err = BasicInfo(task, &task_info);
+
+ if (!err.Success())
+ return result;
+
+ uint64_t elapsed_usec = 0;
+ uint64_t task_used_usec = 0;
+ if (scanType & eProfileCPU)
+ {
+ // Get current used time.
+ struct timeval current_used_time;
+ struct timeval tv;
+ TIME_VALUE_TO_TIMEVAL(&task_info.user_time, &current_used_time);
+ TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv);
+ timeradd(&current_used_time, &tv, &current_used_time);
+ task_used_usec = current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec;
+
+ struct timeval current_elapsed_time;
+ int res = gettimeofday(&current_elapsed_time, NULL);
+ if (res == 0)
+ {
+ elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL + current_elapsed_time.tv_usec;
+ }
+ }
+
+ std::vector<uint64_t> threads_id;
+ std::vector<std::string> threads_name;
+ std::vector<uint64_t> threads_used_usec;
+
+ if (scanType & eProfileThreadsCPU)
+ {
+ get_threads_profile_data(scanType, task, pid, threads_id, threads_name, threads_used_usec);
+ }
+
+#if defined (HOST_VM_INFO64_COUNT)
+ vm_statistics64_data_t vminfo;
+#else
+ struct vm_statistics vminfo;
+#endif
+ uint64_t physical_memory;
+ mach_vm_size_t rprvt = 0;
+ mach_vm_size_t rsize = 0;
+ mach_vm_size_t vprvt = 0;
+ mach_vm_size_t vsize = 0;
+ mach_vm_size_t dirty_size = 0;
+ mach_vm_size_t purgeable = 0;
+ mach_vm_size_t anonymous = 0;
+ if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), pid, vminfo, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size, purgeable, anonymous))
+ {
+ std::ostringstream profile_data_stream;
+
+ if (scanType & eProfileHostCPU)
+ {
+ profile_data_stream << "num_cpu:" << numCPU << ';';
+ profile_data_stream << "host_user_ticks:" << host_info.cpu_ticks[CPU_STATE_USER] << ';';
+ profile_data_stream << "host_sys_ticks:" << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';';
+ profile_data_stream << "host_idle_ticks:" << host_info.cpu_ticks[CPU_STATE_IDLE] << ';';
+ }
+
+ if (scanType & eProfileCPU)
+ {
+ profile_data_stream << "elapsed_usec:" << elapsed_usec << ';';
+ profile_data_stream << "task_used_usec:" << task_used_usec << ';';
+ }
+
+ if (scanType & eProfileThreadsCPU)
+ {
+ const size_t num_threads = threads_id.size();
+ for (size_t i=0; i<num_threads; i++)
+ {
+ profile_data_stream << "thread_used_id:" << std::hex << threads_id[i] << std::dec << ';';
+ profile_data_stream << "thread_used_usec:" << threads_used_usec[i] << ';';
+
+ if (scanType & eProfileThreadName)
+ {
+ profile_data_stream << "thread_used_name:";
+ const size_t len = threads_name[i].size();
+ if (len)
+ {
+ const char *thread_name = threads_name[i].c_str();
+ // Make sure that thread name doesn't interfere with our delimiter.
+ profile_data_stream << RAW_HEXBASE << std::setw(2);
+ const uint8_t *ubuf8 = (const uint8_t *)(thread_name);
+ for (size_t j=0; j<len; j++)
+ {
+ profile_data_stream << (uint32_t)(ubuf8[j]);
+ }
+ // Reset back to DECIMAL.
+ profile_data_stream << DECIMAL;
+ }
+ profile_data_stream << ';';
+ }
+ }
+ }
+
+ if (scanType & eProfileHostMemory)
+ profile_data_stream << "total:" << physical_memory << ';';
+
+ if (scanType & eProfileMemory)
+ {
+#if defined (HOST_VM_INFO64_COUNT) && defined (_VM_PAGE_SIZE_H_)
+ static vm_size_t pagesize = vm_kernel_page_size;
+#else
+ static vm_size_t pagesize;
+ static bool calculated = false;
+ if (!calculated)
+ {
+ calculated = true;
+ pagesize = PageSize();
+ }
+#endif
+
+ /* Unused values. Optimized out for transfer performance.
+ profile_data_stream << "wired:" << vminfo.wire_count * pagesize << ';';
+ profile_data_stream << "active:" << vminfo.active_count * pagesize << ';';
+ profile_data_stream << "inactive:" << vminfo.inactive_count * pagesize << ';';
+ */
+#if defined (HOST_VM_INFO64_COUNT)
+ // This mimicks Activity Monitor.
+ uint64_t total_used_count = (physical_memory / pagesize) - (vminfo.free_count - vminfo.speculative_count) - vminfo.external_page_count - vminfo.purgeable_count;
+#else
+ uint64_t total_used_count = vminfo.wire_count + vminfo.inactive_count + vminfo.active_count;
+#endif
+ profile_data_stream << "used:" << total_used_count * pagesize << ';';
+ /* Unused values. Optimized out for transfer performance.
+ profile_data_stream << "free:" << vminfo.free_count * pagesize << ';';
+ */
+
+ profile_data_stream << "rprvt:" << rprvt << ';';
+ /* Unused values. Optimized out for transfer performance.
+ profile_data_stream << "rsize:" << rsize << ';';
+ profile_data_stream << "vprvt:" << vprvt << ';';
+ profile_data_stream << "vsize:" << vsize << ';';
+ */
+
+ if (scanType & eProfileMemoryDirtyPage)
+ profile_data_stream << "dirty:" << dirty_size << ';';
+
+ if (scanType & eProfileMemoryAnonymous)
+ {
+ profile_data_stream << "purgeable:" << purgeable << ';';
+ profile_data_stream << "anonymous:" << anonymous << ';';
+ }
+ }
+
+ // proc_pid_rusage pm_sample_task_and_pid pm_energy_impact needs to be tested for weakness in Cab
+#ifdef LLDB_ENERGY
+ if ((scanType & eProfileEnergy) && (pm_sample_task_and_pid != NULL))
+ {
+ struct rusage_info_v2 info;
+ int rc = proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t *)&info);
+ if (rc == 0)
+ {
+ uint64_t now = mach_absolute_time();
+ pm_task_energy_data_t pm_energy;
+ memset(&pm_energy, 0, sizeof(pm_energy));
+ /*
+ * Disable most features of pm_sample_pid. It will gather
+ * network/GPU/WindowServer information; fill in the rest.
+ */
+ pm_sample_task_and_pid(task, pid, &pm_energy, now, PM_SAMPLE_ALL & ~PM_SAMPLE_NAME & ~PM_SAMPLE_INTERVAL & ~PM_SAMPLE_CPU & ~PM_SAMPLE_DISK);
+ pm_energy.sti.total_user = info.ri_user_time;
+ pm_energy.sti.total_system = info.ri_system_time;
+ pm_energy.sti.task_interrupt_wakeups = info.ri_interrupt_wkups;
+ pm_energy.sti.task_platform_idle_wakeups = info.ri_pkg_idle_wkups;
+ pm_energy.diskio_bytesread = info.ri_diskio_bytesread;
+ pm_energy.diskio_byteswritten = info.ri_diskio_byteswritten;
+ pm_energy.pageins = info.ri_pageins;
+
+ uint64_t total_energy = (uint64_t)(pm_energy_impact(&pm_energy) * NSEC_PER_SEC);
+ //uint64_t process_age = now - info.ri_proc_start_abstime;
+ //uint64_t avg_energy = 100.0 * (double)total_energy / (double)process_age;
+
+ profile_data_stream << "energy:" << total_energy << ';';
+ }
+ }
+#endif
+
+ profile_data_stream << "--end--;";
+
+ result = profile_data_stream.str();
+ }
+
+ return result;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t
+MachTask::TaskPortForProcessID (DNBError &err, bool force)
+{
+ if (((m_task == TASK_NULL) || force) && m_process != NULL)
+ m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err);
+ return m_task;
+}
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t
+MachTask::TaskPortForProcessID (pid_t pid, DNBError &err, uint32_t num_retries, uint32_t usec_interval)
+{
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ DNBError err;
+ mach_port_t task_self = mach_task_self ();
+ task_t task = TASK_NULL;
+ for (uint32_t i=0; i<num_retries; i++)
+ {
+ err = ::task_for_pid ( task_self, pid, &task);
+
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ {
+ char str[1024];
+ ::snprintf (str,
+ sizeof(str),
+ "::task_for_pid ( target_tport = 0x%4.4x, pid = %d, &task ) => err = 0x%8.8x (%s)",
+ task_self,
+ pid,
+ err.Error(),
+ err.AsString() ? err.AsString() : "success");
+ if (err.Fail())
+ err.SetErrorString(str);
+ err.LogThreaded(str);
+ }
+
+ if (err.Success())
+ return task;
+
+ // Sleep a bit and try again
+ ::usleep (usec_interval);
+ }
+ }
+ return TASK_NULL;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::BasicInfo(struct task_basic_info *info)
+{
+ return BasicInfo (TaskPort(), info);
+}
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::BasicInfo(task_t task, struct task_basic_info *info)
+{
+ if (info == NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ DNBError err;
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count);
+ const bool log_process = DNBLogCheckLogBit(LOG_TASK);
+ if (log_process || err.Fail())
+ err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count);
+ if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) && err.Success())
+ {
+ float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
+ float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
+ DNBLogThreaded ("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8llx, resident_size = 0x%8.8llx, user_time = %f, system_time = %f }",
+ info->suspend_count,
+ (uint64_t)info->virtual_size,
+ (uint64_t)info->resident_size,
+ user,
+ system);
+ }
+ return err.Error();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool
+MachTask::IsValid () const
+{
+ return MachTask::IsValid(TaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool
+MachTask::IsValid (task_t task)
+{
+ if (task != TASK_NULL)
+ {
+ struct task_basic_info task_info;
+ return BasicInfo(task, &task_info) == KERN_SUCCESS;
+ }
+ return false;
+}
+
+
+bool
+MachTask::StartExceptionThread(DNBError &err)
+{
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__);
+
+ task_t task = TaskPortForProcessID(err);
+ if (MachTask::IsValid(task))
+ {
+ // Got the mach port for the current process
+ mach_port_t task_self = mach_task_self ();
+
+ // Allocate an exception port that we will use to track our child process
+ err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port);
+ if (err.Fail())
+ return false;
+
+ // Add the ability to send messages on the new exception port
+ err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
+ if (err.Fail())
+ return false;
+
+ // Save the original state of the exception ports for our child process
+ SaveExceptionPortInfo();
+
+ // We weren't able to save the info for our exception ports, we must stop...
+ if (m_exc_port_info.mask == 0)
+ {
+ err.SetErrorString("failed to get exception port info");
+ return false;
+ }
+
+ // Set the ability to get all exceptions on this port
+ err = ::task_set_exception_ports (task, m_exc_port_info.mask, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ {
+ err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )",
+ task,
+ m_exc_port_info.mask,
+ m_exception_port,
+ (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES),
+ THREAD_STATE_NONE);
+ }
+
+ if (err.Fail())
+ return false;
+
+ // Create the exception thread
+ err = ::pthread_create (&m_exception_thread, NULL, MachTask::ExceptionThread, this);
+ return err.Success();
+ }
+ else
+ {
+ DNBLogError("MachTask::%s (): task invalid, exception thread start failed.", __FUNCTION__);
+ }
+ return false;
+}
+
+kern_return_t
+MachTask::ShutDownExcecptionThread()
+{
+ DNBError err;
+
+ err = RestoreExceptionPortInfo();
+
+ // NULL our our exception port and let our exception thread exit
+ mach_port_t exception_port = m_exception_port;
+ m_exception_port = 0;
+
+ err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread);
+
+ err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)", m_exception_thread);
+
+ // Deallocate our exception port that we used to track our child process
+ mach_port_t task_self = mach_task_self ();
+ err = ::mach_port_deallocate (task_self, exception_port);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port);
+
+ return err.Error();
+}
+
+
+void *
+MachTask::ExceptionThread (void *arg)
+{
+ if (arg == NULL)
+ return NULL;
+
+ MachTask *mach_task = (MachTask*) arg;
+ MachProcess *mach_proc = mach_task->Process();
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__, arg);
+
+#if defined (__APPLE__)
+ pthread_setname_np ("exception monitoring thread");
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0)
+ {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ // We keep a count of the number of consecutive exceptions received so
+ // we know to grab all exceptions without a timeout. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main loop in this
+ // thread can stop periodically if needed to service things related to this
+ // process.
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle available. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ uint32_t num_exceptions_received = 0;
+ DNBError err;
+ task_t task = mach_task->TaskPort();
+ mach_msg_timeout_t periodic_timeout = 0;
+
+#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+ mach_msg_timeout_t watchdog_elapsed = 0;
+ mach_msg_timeout_t watchdog_timeout = 60 * 1000;
+ pid_t pid = mach_proc->ProcessID();
+ CFReleaser<SBSWatchdogAssertionRef> watchdog;
+
+ if (mach_proc->ProcessUsingSpringBoard())
+ {
+ // Request a renewal for every 60 seconds if we attached using SpringBoard
+ watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60));
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get());
+
+ if (watchdog.get())
+ {
+ ::SBSWatchdogAssertionRenew (watchdog.get());
+
+ CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get());
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval);
+ if (watchdogRenewalInterval > 0.0)
+ {
+ watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
+ if (watchdog_timeout > 3000)
+ watchdog_timeout -= 1000; // Give us a second to renew our timeout
+ else if (watchdog_timeout > 1000)
+ watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout
+ }
+ }
+ if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
+ periodic_timeout = watchdog_timeout;
+ }
+#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+
+#ifdef WITH_BKS
+ CFReleaser<BKSWatchdogAssertionRef> watchdog;
+ if (mach_proc->ProcessUsingBackBoard())
+ {
+ pid_t pid = mach_proc->ProcessID();
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid));
+ }
+#endif // #ifdef WITH_BKS
+
+ while (mach_task->ExceptionPortIsValid())
+ {
+ ::pthread_testcancel ();
+
+ MachException::Message exception_message;
+
+
+ if (num_exceptions_received > 0)
+ {
+ // No timeout, just receive as many exceptions as we can since we already have one and we want
+ // to get all currently available exceptions for this task
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0);
+ }
+ else if (periodic_timeout > 0)
+ {
+ // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms)
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout);
+ }
+ else
+ {
+ // We don't need to parse all current exceptions or stop periodically,
+ // just wait for an exception forever.
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
+ }
+
+ if (err.Error() == MACH_RCV_INTERRUPTED)
+ {
+ // If we have no task port we should exit this thread
+ if (!mach_task->ExceptionPortIsValid())
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled...");
+ break;
+ }
+
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task))
+ {
+ // Task is still ok
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "interrupted, but task still valid, continuing...");
+ continue;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
+ mach_proc->SetState(eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ }
+ else if (err.Error() == MACH_RCV_TIMED_OUT)
+ {
+ if (num_exceptions_received > 0)
+ {
+ // We were receiving all current exceptions with a timeout of zero
+ // it is time to go back to our normal looping mode
+ num_exceptions_received = 0;
+
+ // Notify our main thread we have a complete exception message
+ // bundle available and get the possibly updated task port back
+ // from the process in case we exec'ed and our task port changed
+ task = mach_proc->ExceptionMessageBundleComplete();
+
+ // in case we use a timeout value when getting exceptions...
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task))
+ {
+ // Task is still ok
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing...");
+ continue;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
+ mach_proc->SetState(eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ }
+
+#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+ if (watchdog.get())
+ {
+ watchdog_elapsed += periodic_timeout;
+ if (watchdog_elapsed >= watchdog_timeout)
+ {
+ DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get());
+ ::SBSWatchdogAssertionRenew (watchdog.get());
+ watchdog_elapsed = 0;
+ }
+ }
+#endif
+ }
+ else if (err.Error() != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something about it??? nah, continuing for now...");
+ // TODO: notify of error?
+ }
+ else
+ {
+ if (exception_message.CatchExceptionRaise(task))
+ {
+ ++num_exceptions_received;
+ mach_proc->ExceptionMessageReceived(exception_message);
+ }
+ }
+ }
+
+#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+ if (watchdog.get())
+ {
+ // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we
+ // all are up and running on systems that support it. The SBS framework has a #define
+ // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now
+ // so it should still build either way.
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get());
+ ::SBSWatchdogAssertionRelease (watchdog.get());
+ }
+#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...", __FUNCTION__, arg);
+ return NULL;
+}
+
+
+// So the TASK_DYLD_INFO used to just return the address of the all image infos
+// as a single member called "all_image_info". Then someone decided it would be
+// a good idea to rename this first member to "all_image_info_addr" and add a
+// size member called "all_image_info_size". This of course can not be detected
+// using code or #defines. So to hack around this problem, we define our own
+// version of the TASK_DYLD_INFO structure so we can guarantee what is inside it.
+
+struct hack_task_dyld_info {
+ mach_vm_address_t all_image_info_addr;
+ mach_vm_size_t all_image_info_size;
+};
+
+nub_addr_t
+MachTask::GetDYLDAllImageInfosAddress (DNBError& err)
+{
+ struct hack_task_dyld_info dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ // Make sure that COUNT isn't bigger than our hacked up struct hack_task_dyld_info.
+ // If it is, then make COUNT smaller to match.
+ if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)))
+ count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t));
+
+ task_t task = TaskPortForProcessID (err);
+ if (err.Success())
+ {
+ err = ::task_info (task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
+ if (err.Success())
+ {
+ // We now have the address of the all image infos structure
+ return dyld_info.all_image_info_addr;
+ }
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::AllocateMemory
+//----------------------------------------------------------------------
+nub_addr_t
+MachTask::AllocateMemory (size_t size, uint32_t permissions)
+{
+ mach_vm_address_t addr;
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return INVALID_NUB_ADDRESS;
+
+ DNBError err;
+ err = ::mach_vm_allocate (task, &addr, size, TRUE);
+ if (err.Error() == KERN_SUCCESS)
+ {
+ // Set the protections:
+ vm_prot_t mach_prot = VM_PROT_NONE;
+ if (permissions & eMemoryPermissionsReadable)
+ mach_prot |= VM_PROT_READ;
+ if (permissions & eMemoryPermissionsWritable)
+ mach_prot |= VM_PROT_WRITE;
+ if (permissions & eMemoryPermissionsExecutable)
+ mach_prot |= VM_PROT_EXECUTE;
+
+
+ err = ::mach_vm_protect (task, addr, size, 0, mach_prot);
+ if (err.Error() == KERN_SUCCESS)
+ {
+ m_allocations.insert (std::make_pair(addr, size));
+ return addr;
+ }
+ ::mach_vm_deallocate (task, addr, size);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// MachTask::DeallocateMemory
+//----------------------------------------------------------------------
+nub_bool_t
+MachTask::DeallocateMemory (nub_addr_t addr)
+{
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return false;
+
+ // We have to stash away sizes for the allocations...
+ allocation_collection::iterator pos, end = m_allocations.end();
+ for (pos = m_allocations.begin(); pos != end; pos++)
+ {
+ if ((*pos).first == addr)
+ {
+ m_allocations.erase(pos);
+#define ALWAYS_ZOMBIE_ALLOCATIONS 0
+ if (ALWAYS_ZOMBIE_ALLOCATIONS || getenv ("DEBUGSERVER_ZOMBIE_ALLOCATIONS"))
+ {
+ ::mach_vm_protect (task, (*pos).first, (*pos).second, 0, VM_PROT_NONE);
+ return true;
+ }
+ else
+ return ::mach_vm_deallocate (task, (*pos).first, (*pos).second) == KERN_SUCCESS;
+ }
+
+ }
+ return false;
+}
+
+static void foundStackLog(mach_stack_logging_record_t record, void *context) {
+ *((bool*)context) = true;
+}
+
+bool
+MachTask::HasMallocLoggingEnabled ()
+{
+ bool found = false;
+
+ __mach_stack_logging_enumerate_records(m_task, 0x0, foundStackLog, &found);
+ return found;
+}
+
+struct history_enumerator_impl_data
+{
+ MachMallocEvent *buffer;
+ uint32_t *position;
+ uint32_t count;
+};
+
+static void history_enumerator_impl(mach_stack_logging_record_t record, void* enum_obj)
+{
+ history_enumerator_impl_data *data = (history_enumerator_impl_data*)enum_obj;
+
+ if (*data->position >= data->count)
+ return;
+
+ data->buffer[*data->position].m_base_address = record.address;
+ data->buffer[*data->position].m_size = record.argument;
+ data->buffer[*data->position].m_event_id = record.stack_identifier;
+ data->buffer[*data->position].m_event_type = record.type_flags == stack_logging_type_alloc ? eMachMallocEventTypeAlloc :
+ record.type_flags == stack_logging_type_dealloc ? eMachMallocEventTypeDealloc :
+ eMachMallocEventTypeOther;
+ *data->position+=1;
+}
+
+bool
+MachTask::EnumerateMallocRecords (MachMallocEvent *event_buffer,
+ uint32_t buffer_size,
+ uint32_t *count)
+{
+ return EnumerateMallocRecords(0,
+ event_buffer,
+ buffer_size,
+ count);
+}
+
+bool
+MachTask::EnumerateMallocRecords (mach_vm_address_t address,
+ MachMallocEvent *event_buffer,
+ uint32_t buffer_size,
+ uint32_t *count)
+{
+ if (!event_buffer || !count)
+ return false;
+
+ if (buffer_size == 0)
+ return false;
+
+ *count = 0;
+ history_enumerator_impl_data data = { event_buffer, count, buffer_size };
+ __mach_stack_logging_enumerate_records(m_task, address, history_enumerator_impl, &data);
+ return (*count > 0);
+}
+
+bool
+MachTask::EnumerateMallocFrames (MachMallocEventId event_id,
+ mach_vm_address_t *function_addresses_buffer,
+ uint32_t buffer_size,
+ uint32_t *count)
+{
+ if (!function_addresses_buffer || !count)
+ return false;
+
+ if (buffer_size == 0)
+ return false;
+
+ __mach_stack_logging_frames_for_uniqued_stack(m_task, event_id, &function_addresses_buffer[0], buffer_size, count);
+ *count -= 1;
+ if (function_addresses_buffer[*count-1] < PageSize())
+ *count -= 1;
+ return (*count > 0);
+}
+
+nub_size_t
+MachTask::PageSize ()
+{
+ return m_vm_memory.PageSize (m_task);
+}
diff --git a/tools/debugserver/source/MacOSX/MachThread.cpp b/tools/debugserver/source/MacOSX/MachThread.cpp
new file mode 100644
index 000000000000..897484156080
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachThread.cpp
@@ -0,0 +1,922 @@
+//===-- MachThread.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include <inttypes.h>
+#include <mach/thread_policy.h>
+#include <dlfcn.h>
+#include "MachThread.h"
+#include "MachProcess.h"
+#include "DNBLog.h"
+#include "DNB.h"
+#include "ThreadInfo.h"
+
+static uint32_t
+GetSequenceID()
+{
+ static uint32_t g_nextID = 0;
+ return ++g_nextID;
+}
+
+MachThread::MachThread (MachProcess *process, bool is_64_bit, uint64_t unique_thread_id, thread_t mach_port_num) :
+ m_process (process),
+ m_unique_id (unique_thread_id),
+ m_mach_port_number (mach_port_num),
+ m_seq_id (GetSequenceID()),
+ m_state (eStateUnloaded),
+ m_state_mutex (PTHREAD_MUTEX_RECURSIVE),
+ m_suspend_count (0),
+ m_stop_exception (),
+ m_arch_ap (DNBArchProtocol::Create (this)),
+ m_reg_sets (NULL),
+ m_num_reg_sets (0),
+ m_ident_info(),
+ m_proc_threadinfo(),
+ m_dispatch_queue_name(),
+ m_is_64_bit(is_64_bit),
+ m_pthread_qos_class_decode (nullptr)
+{
+ nub_size_t num_reg_sets = 0;
+ m_reg_sets = m_arch_ap->GetRegisterSetInfo (&num_reg_sets);
+ m_num_reg_sets = num_reg_sets;
+
+ m_pthread_qos_class_decode = (unsigned int (*)(unsigned long, int*, unsigned long*)) dlsym (RTLD_DEFAULT, "_pthread_qos_class_decode");
+
+ // Get the thread state so we know if a thread is in a state where we can't
+ // muck with it and also so we get the suspend count correct in case it was
+ // already suspended
+ GetBasicInfo();
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::MachThread ( process = %p, tid = 0x%8.8" PRIx64 ", seq_id = %u )", &m_process, m_unique_id, m_seq_id);
+}
+
+MachThread::~MachThread()
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::~MachThread() for tid = 0x%8.8" PRIx64 " (%u)", m_unique_id, m_seq_id);
+}
+
+
+
+void
+MachThread::Suspend()
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
+ if (MachPortNumberIsValid(m_mach_port_number))
+ {
+ DNBError err(::thread_suspend (m_mach_port_number), DNBError::MachKernel);
+ if (err.Success())
+ m_suspend_count++;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number);
+ }
+}
+
+void
+MachThread::Resume(bool others_stopped)
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
+ if (MachPortNumberIsValid(m_mach_port_number))
+ {
+ SetSuspendCountBeforeResume(others_stopped);
+ }
+}
+
+bool
+MachThread::SetSuspendCountBeforeResume(bool others_stopped)
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
+ DNBError err;
+ if (MachPortNumberIsValid(m_mach_port_number) == false)
+ return false;
+
+ integer_t times_to_resume;
+
+ if (others_stopped)
+ {
+ if (GetBasicInfo())
+ {
+ times_to_resume = m_basic_info.suspend_count;
+ m_suspend_count = - (times_to_resume - m_suspend_count);
+ }
+ else
+ times_to_resume = 0;
+ }
+ else
+ {
+ times_to_resume = m_suspend_count;
+ m_suspend_count = 0;
+ }
+
+ if (times_to_resume > 0)
+ {
+ while (times_to_resume > 0)
+ {
+ err = ::thread_resume (m_mach_port_number);
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
+ if (err.Success())
+ --times_to_resume;
+ else
+ {
+ if (GetBasicInfo())
+ times_to_resume = m_basic_info.suspend_count;
+ else
+ times_to_resume = 0;
+ }
+ }
+ }
+ return true;
+}
+
+bool
+MachThread::RestoreSuspendCountAfterStop ()
+{
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__);
+ DNBError err;
+ if (MachPortNumberIsValid(m_mach_port_number) == false)
+ return false;
+
+ if (m_suspend_count > 0)
+ {
+ while (m_suspend_count > 0)
+ {
+ err = ::thread_resume (m_mach_port_number);
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
+ if (err.Success())
+ --m_suspend_count;
+ else
+ {
+ if (GetBasicInfo())
+ m_suspend_count = m_basic_info.suspend_count;
+ else
+ m_suspend_count = 0;
+ return false; // ???
+ }
+ }
+ }
+ else if (m_suspend_count < 0)
+ {
+ while (m_suspend_count < 0)
+ {
+ err = ::thread_suspend (m_mach_port_number);
+ if (err.Success())
+ ++m_suspend_count;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ {
+ err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+const char *
+MachThread::GetBasicInfoAsString () const
+{
+ static char g_basic_info_string[1024];
+ struct thread_basic_info basicInfo;
+
+ if (GetBasicInfo(m_mach_port_number, &basicInfo))
+ {
+
+// char run_state_str[32];
+// size_t run_state_str_size = sizeof(run_state_str);
+// switch (basicInfo.run_state)
+// {
+// case TH_STATE_RUNNING: strncpy(run_state_str, "running", run_state_str_size); break;
+// case TH_STATE_STOPPED: strncpy(run_state_str, "stopped", run_state_str_size); break;
+// case TH_STATE_WAITING: strncpy(run_state_str, "waiting", run_state_str_size); break;
+// case TH_STATE_UNINTERRUPTIBLE: strncpy(run_state_str, "uninterruptible", run_state_str_size); break;
+// case TH_STATE_HALTED: strncpy(run_state_str, "halted", run_state_str_size); break;
+// default: snprintf(run_state_str, run_state_str_size, "%d", basicInfo.run_state); break; // ???
+// }
+ float user = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
+ float system = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
+ snprintf(g_basic_info_string, sizeof(g_basic_info_string), "Thread 0x%8.8" PRIx64 ": user=%f system=%f cpu=%d sleep_time=%d",
+ m_unique_id,
+ user,
+ system,
+ basicInfo.cpu_usage,
+ basicInfo.sleep_time);
+
+ return g_basic_info_string;
+ }
+ return NULL;
+}
+
+// Finds the Mach port number for a given thread in the inferior process' port namespace.
+thread_t
+MachThread::InferiorThreadID() const
+{
+ mach_msg_type_number_t i;
+ mach_port_name_array_t names;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t ncount, tcount;
+ thread_t inferior_tid = INVALID_NUB_THREAD;
+ task_t my_task = ::mach_task_self();
+ task_t task = m_process->Task().TaskPort();
+
+ kern_return_t kret = ::mach_port_names (task, &names, &ncount, &types, &tcount);
+ if (kret == KERN_SUCCESS)
+ {
+
+ for (i = 0; i < ncount; i++)
+ {
+ mach_port_t my_name;
+ mach_msg_type_name_t my_type;
+
+ kret = ::mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &my_name, &my_type);
+ if (kret == KERN_SUCCESS)
+ {
+ ::mach_port_deallocate (my_task, my_name);
+ if (my_name == m_mach_port_number)
+ {
+ inferior_tid = names[i];
+ break;
+ }
+ }
+ }
+ // Free up the names and types
+ ::vm_deallocate (my_task, (vm_address_t) names, ncount * sizeof (mach_port_name_t));
+ ::vm_deallocate (my_task, (vm_address_t) types, tcount * sizeof (mach_port_type_t));
+ }
+ return inferior_tid;
+}
+
+bool
+MachThread::IsUserReady()
+{
+ if (m_basic_info.run_state == 0)
+ GetBasicInfo ();
+
+ switch (m_basic_info.run_state)
+ {
+ default:
+ case TH_STATE_UNINTERRUPTIBLE:
+ break;
+
+ case TH_STATE_RUNNING:
+ case TH_STATE_STOPPED:
+ case TH_STATE_WAITING:
+ case TH_STATE_HALTED:
+ return true;
+ }
+ return false;
+}
+
+struct thread_basic_info *
+MachThread::GetBasicInfo ()
+{
+ if (MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info))
+ return &m_basic_info;
+ return NULL;
+}
+
+
+bool
+MachThread::GetBasicInfo(thread_t thread, struct thread_basic_info *basicInfoPtr)
+{
+ if (MachPortNumberIsValid(thread))
+ {
+ unsigned int info_count = THREAD_BASIC_INFO_COUNT;
+ kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count);
+ if (err == KERN_SUCCESS)
+ return true;
+ }
+ ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info));
+ return false;
+}
+
+
+bool
+MachThread::ThreadIDIsValid(uint64_t thread)
+{
+ return thread != 0;
+}
+
+bool
+MachThread::MachPortNumberIsValid(thread_t thread)
+{
+ return thread != THREAD_NULL;
+}
+
+bool
+MachThread::GetRegisterState(int flavor, bool force)
+{
+ return m_arch_ap->GetRegisterState(flavor, force) == KERN_SUCCESS;
+}
+
+bool
+MachThread::SetRegisterState(int flavor)
+{
+ return m_arch_ap->SetRegisterState(flavor) == KERN_SUCCESS;
+}
+
+uint64_t
+MachThread::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ return m_arch_ap->GetPC(failValue);
+}
+
+bool
+MachThread::SetPC(uint64_t value)
+{
+ // Set program counter
+ return m_arch_ap->SetPC(value);
+}
+
+uint64_t
+MachThread::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ return m_arch_ap->GetSP(failValue);
+}
+
+nub_process_t
+MachThread::ProcessID() const
+{
+ if (m_process)
+ return m_process->ProcessID();
+ return INVALID_NUB_PROCESS;
+}
+
+void
+MachThread::Dump(uint32_t index)
+{
+ const char * thread_run_state = NULL;
+
+ switch (m_basic_info.run_state)
+ {
+ case TH_STATE_RUNNING: thread_run_state = "running"; break; // 1 thread is running normally
+ case TH_STATE_STOPPED: thread_run_state = "stopped"; break; // 2 thread is stopped
+ case TH_STATE_WAITING: thread_run_state = "waiting"; break; // 3 thread is waiting normally
+ case TH_STATE_UNINTERRUPTIBLE: thread_run_state = "uninter"; break; // 4 thread is in an uninterruptible wait
+ case TH_STATE_HALTED: thread_run_state = "halted "; break; // 5 thread is halted at a
+ default: thread_run_state = "???"; break;
+ }
+
+ DNBLogThreaded("[%3u] #%3u tid: 0x%8.8" PRIx64 ", pc: 0x%16.16" PRIx64 ", sp: 0x%16.16" PRIx64 ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: %2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
+ index,
+ m_seq_id,
+ m_unique_id,
+ GetPC(INVALID_NUB_ADDRESS),
+ GetSP(INVALID_NUB_ADDRESS),
+ m_basic_info.user_time.seconds, m_basic_info.user_time.microseconds,
+ m_basic_info.system_time.seconds, m_basic_info.system_time.microseconds,
+ m_basic_info.cpu_usage,
+ m_basic_info.policy,
+ m_basic_info.run_state,
+ thread_run_state,
+ m_basic_info.flags,
+ m_basic_info.suspend_count, m_suspend_count,
+ m_basic_info.sleep_time);
+ //DumpRegisterState(0);
+}
+
+void
+MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action, bool others_stopped)
+{
+ if (thread_action->addr != INVALID_NUB_ADDRESS)
+ SetPC (thread_action->addr);
+
+ SetState (thread_action->state);
+ switch (thread_action->state)
+ {
+ case eStateStopped:
+ case eStateSuspended:
+ assert (others_stopped == false);
+ Suspend();
+ break;
+
+ case eStateRunning:
+ case eStateStepping:
+ Resume(others_stopped);
+ break;
+ default:
+ break;
+ }
+ m_arch_ap->ThreadWillResume();
+ m_stop_exception.Clear();
+}
+
+DNBBreakpoint *
+MachThread::CurrentBreakpoint()
+{
+ return m_process->Breakpoints().FindByAddress(GetPC());
+}
+
+bool
+MachThread::ShouldStop(bool &step_more)
+{
+ // See if this thread is at a breakpoint?
+ DNBBreakpoint *bp = CurrentBreakpoint();
+
+ if (bp)
+ {
+ // This thread is sitting at a breakpoint, ask the breakpoint
+ // if we should be stopping here.
+ return true;
+ }
+ else
+ {
+ if (m_arch_ap->StepNotComplete())
+ {
+ step_more = true;
+ return false;
+ }
+ // The thread state is used to let us know what the thread was
+ // trying to do. MachThread::ThreadWillResume() will set the
+ // thread state to various values depending if the thread was
+ // the current thread and if it was to be single stepped, or
+ // resumed.
+ if (GetState() == eStateRunning)
+ {
+ // If our state is running, then we should continue as we are in
+ // the process of stepping over a breakpoint.
+ return false;
+ }
+ else
+ {
+ // Stop if we have any kind of valid exception for this
+ // thread.
+ if (GetStopException().IsValid())
+ return true;
+ }
+ }
+ return false;
+}
+bool
+MachThread::IsStepping()
+{
+ return GetState() == eStateStepping;
+}
+
+
+bool
+MachThread::ThreadDidStop()
+{
+ // This thread has existed prior to resuming under debug nub control,
+ // and has just been stopped. Do any cleanup that needs to be done
+ // after running.
+
+ // The thread state and breakpoint will still have the same values
+ // as they had prior to resuming the thread, so it makes it easy to check
+ // if we were trying to step a thread, or we tried to resume while being
+ // at a breakpoint.
+
+ // When this method gets called, the process state is still in the
+ // state it was in while running so we can act accordingly.
+ m_arch_ap->ThreadDidStop();
+
+
+ // We may have suspended this thread so the primary thread could step
+ // without worrying about race conditions, so lets restore our suspend
+ // count.
+ RestoreSuspendCountAfterStop();
+
+ // Update the basic information for a thread
+ MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info);
+
+ if (m_basic_info.suspend_count > 0)
+ SetState(eStateSuspended);
+ else
+ SetState(eStateStopped);
+ return true;
+}
+
+bool
+MachThread::NotifyException(MachException::Data& exc)
+{
+ // Allow the arch specific protocol to process (MachException::Data &)exc
+ // first before possible reassignment of m_stop_exception with exc.
+ // See also MachThread::GetStopException().
+ bool handled = m_arch_ap->NotifyException(exc);
+
+ if (m_stop_exception.IsValid())
+ {
+ // We may have more than one exception for a thread, but we need to
+ // only remember the one that we will say is the reason we stopped.
+ // We may have been single stepping and also gotten a signal exception,
+ // so just remember the most pertinent one.
+ if (m_stop_exception.IsBreakpoint())
+ m_stop_exception = exc;
+ }
+ else
+ {
+ m_stop_exception = exc;
+ }
+
+ return handled;
+}
+
+
+nub_state_t
+MachThread::GetState()
+{
+ // If any other threads access this we will need a mutex for it
+ PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
+ return m_state;
+}
+
+void
+MachThread::SetState(nub_state_t state)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_state_mutex);
+ m_state = state;
+ DNBLogThreadedIf(LOG_THREAD, "MachThread::SetState ( %s ) for tid = 0x%8.8" PRIx64 "", DNBStateAsString(state), m_unique_id);
+}
+
+nub_size_t
+MachThread::GetNumRegistersInSet(nub_size_t regSet) const
+{
+ if (regSet < m_num_reg_sets)
+ return m_reg_sets[regSet].num_registers;
+ return 0;
+}
+
+const char *
+MachThread::GetRegisterSetName(nub_size_t regSet) const
+{
+ if (regSet < m_num_reg_sets)
+ return m_reg_sets[regSet].name;
+ return NULL;
+}
+
+const DNBRegisterInfo *
+MachThread::GetRegisterInfo(nub_size_t regSet, nub_size_t regIndex) const
+{
+ if (regSet < m_num_reg_sets)
+ if (regIndex < m_reg_sets[regSet].num_registers)
+ return &m_reg_sets[regSet].registers[regIndex];
+ return NULL;
+}
+void
+MachThread::DumpRegisterState(nub_size_t regSet)
+{
+ if (regSet == REGISTER_SET_ALL)
+ {
+ for (regSet = 1; regSet < m_num_reg_sets; regSet++)
+ DumpRegisterState(regSet);
+ }
+ else
+ {
+ if (m_arch_ap->RegisterSetStateIsValid((int)regSet))
+ {
+ const size_t numRegisters = GetNumRegistersInSet(regSet);
+ uint32_t regIndex = 0;
+ DNBRegisterValueClass reg;
+ for (regIndex = 0; regIndex < numRegisters; ++regIndex)
+ {
+ if (m_arch_ap->GetRegisterValue((uint32_t)regSet, regIndex, &reg))
+ {
+ reg.Dump(NULL, NULL);
+ }
+ }
+ }
+ else
+ {
+ DNBLog("%s: registers are not currently valid.", GetRegisterSetName(regSet));
+ }
+ }
+}
+
+const DNBRegisterSetInfo *
+MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets ) const
+{
+ *num_reg_sets = m_num_reg_sets;
+ return &m_reg_sets[0];
+}
+
+bool
+MachThread::GetRegisterValue ( uint32_t set, uint32_t reg, DNBRegisterValue *value )
+{
+ return m_arch_ap->GetRegisterValue(set, reg, value);
+}
+
+bool
+MachThread::SetRegisterValue ( uint32_t set, uint32_t reg, const DNBRegisterValue *value )
+{
+ return m_arch_ap->SetRegisterValue(set, reg, value);
+}
+
+nub_size_t
+MachThread::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ return m_arch_ap->GetRegisterContext(buf, buf_len);
+}
+
+nub_size_t
+MachThread::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ return m_arch_ap->SetRegisterContext(buf, buf_len);
+}
+
+uint32_t
+MachThread::SaveRegisterState ()
+{
+ return m_arch_ap->SaveRegisterState();
+
+}
+bool
+MachThread::RestoreRegisterState (uint32_t save_id)
+{
+ return m_arch_ap->RestoreRegisterState(save_id);
+}
+
+uint32_t
+MachThread::EnableHardwareBreakpoint (const DNBBreakpoint *bp)
+{
+ if (bp != NULL && bp->IsBreakpoint())
+ return m_arch_ap->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize());
+ return INVALID_NUB_HW_INDEX;
+}
+
+uint32_t
+MachThread::EnableHardwareWatchpoint (const DNBBreakpoint *wp, bool also_set_on_task)
+{
+ if (wp != NULL && wp->IsWatchpoint())
+ return m_arch_ap->EnableHardwareWatchpoint(wp->Address(), wp->ByteSize(), wp->WatchpointRead(), wp->WatchpointWrite(), also_set_on_task);
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+MachThread::RollbackTransForHWP()
+{
+ return m_arch_ap->RollbackTransForHWP();
+}
+
+bool
+MachThread::FinishTransForHWP()
+{
+ return m_arch_ap->FinishTransForHWP();
+}
+
+bool
+MachThread::DisableHardwareBreakpoint (const DNBBreakpoint *bp)
+{
+ if (bp != NULL && bp->IsHardware())
+ return m_arch_ap->DisableHardwareBreakpoint(bp->GetHardwareIndex());
+ return false;
+}
+
+bool
+MachThread::DisableHardwareWatchpoint (const DNBBreakpoint *wp, bool also_set_on_task)
+{
+ if (wp != NULL && wp->IsHardware())
+ return m_arch_ap->DisableHardwareWatchpoint(wp->GetHardwareIndex(), also_set_on_task);
+ return false;
+}
+
+uint32_t
+MachThread::NumSupportedHardwareWatchpoints () const
+{
+ return m_arch_ap->NumSupportedHardwareWatchpoints();
+}
+
+bool
+MachThread::GetIdentifierInfo ()
+{
+ // Don't try to get the thread info once and cache it for the life of the thread. It changes over time, for instance
+ // if the thread name changes, then the thread_handle also changes... So you have to refetch it every time.
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kern_return_t kret = ::thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count);
+ return kret == KERN_SUCCESS;
+
+ return false;
+}
+
+
+const char *
+MachThread::GetName ()
+{
+ if (GetIdentifierInfo ())
+ {
+ int len = ::proc_pidinfo (m_process->ProcessID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo));
+
+ if (len && m_proc_threadinfo.pth_name[0])
+ return m_proc_threadinfo.pth_name;
+ }
+ return NULL;
+}
+
+
+uint64_t
+MachThread::GetGloballyUniqueThreadIDForMachPortID (thread_t mach_port_id)
+{
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info (mach_port_id, THREAD_IDENTIFIER_INFO,
+ (thread_info_t) &tident, &tident_count);
+ if (kr != KERN_SUCCESS)
+ {
+ return mach_port_id;
+ }
+ return tident.thread_id;
+}
+
+nub_addr_t
+MachThread::GetPThreadT ()
+{
+ nub_addr_t pthread_t_value = INVALID_NUB_ADDRESS;
+ if (MachPortNumberIsValid (m_mach_port_number))
+ {
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO,
+ (thread_info_t) &tident, &tident_count);
+ if (kr == KERN_SUCCESS)
+ {
+ // Dereference thread_handle to get the pthread_t value for this thread.
+ if (m_is_64_bit)
+ {
+ uint64_t addr;
+ if (m_process->ReadMemory (tident.thread_handle, 8, &addr) == 8)
+ {
+ if (addr != 0)
+ {
+ pthread_t_value = addr;
+ }
+ }
+ }
+ else
+ {
+ uint32_t addr;
+ if (m_process->ReadMemory (tident.thread_handle, 4, &addr) == 4)
+ {
+ if (addr != 0)
+ {
+ pthread_t_value = addr;
+ }
+ }
+ }
+ }
+ }
+ return pthread_t_value;
+}
+
+// Return this thread's TSD (Thread Specific Data) address.
+// This is computed based on this thread's pthread_t value.
+//
+// We compute the TSD from the pthread_t by one of two methods.
+//
+// If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we add to
+// the pthread_t to get the TSD base address.
+//
+// Else we read a pointer from memory at pthread_t + plo_pthread_tsd_base_address_offset and
+// that gives us the TSD address.
+//
+// These plo_pthread_tsd_base values must be read out of libpthread by lldb & provided to debugserver.
+
+nub_addr_t
+MachThread::GetTSDAddressForThread (uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size)
+{
+ nub_addr_t tsd_addr = INVALID_NUB_ADDRESS;
+ nub_addr_t pthread_t_value = GetPThreadT();
+ if (plo_pthread_tsd_base_offset != 0 && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS)
+ {
+ tsd_addr = pthread_t_value + plo_pthread_tsd_base_offset;
+ }
+ else
+ {
+ if (plo_pthread_tsd_entry_size == 4)
+ {
+ uint32_t addr = 0;
+ if (m_process->ReadMemory (pthread_t_value + plo_pthread_tsd_base_address_offset, 4, &addr) == 4)
+ {
+ if (addr != 0)
+ {
+ tsd_addr = addr;
+ }
+ }
+ }
+ if (plo_pthread_tsd_entry_size == 4)
+ {
+ uint64_t addr = 0;
+ if (m_process->ReadMemory (pthread_t_value + plo_pthread_tsd_base_address_offset, 8, &addr) == 8)
+ {
+ if (addr != 0)
+ {
+ tsd_addr = addr;
+ }
+ }
+ }
+ }
+ return tsd_addr;
+}
+
+
+nub_addr_t
+MachThread::GetDispatchQueueT ()
+{
+ nub_addr_t dispatch_queue_t_value = INVALID_NUB_ADDRESS;
+ if (MachPortNumberIsValid (m_mach_port_number))
+ {
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO,
+ (thread_info_t) &tident, &tident_count);
+ if (kr == KERN_SUCCESS && tident.dispatch_qaddr != 0 && tident.dispatch_qaddr != INVALID_NUB_ADDRESS)
+ {
+ // Dereference dispatch_qaddr to get the dispatch_queue_t value for this thread's queue, if any.
+ if (m_is_64_bit)
+ {
+ uint64_t addr;
+ if (m_process->ReadMemory (tident.dispatch_qaddr, 8, &addr) == 8)
+ {
+ if (addr != 0)
+ dispatch_queue_t_value = addr;
+ }
+ }
+ else
+ {
+ uint32_t addr;
+ if (m_process->ReadMemory (tident.dispatch_qaddr, 4, &addr) == 4)
+ {
+ if (addr != 0)
+ dispatch_queue_t_value = addr;
+ }
+ }
+ }
+ }
+ return dispatch_queue_t_value;
+}
+
+
+ThreadInfo::QoS
+MachThread::GetRequestedQoS (nub_addr_t tsd, uint64_t dti_qos_class_index)
+{
+ ThreadInfo::QoS qos_value;
+ if (MachPortNumberIsValid (m_mach_port_number) && m_pthread_qos_class_decode != nullptr)
+ {
+ uint64_t pthread_priority_value = 0;
+ if (m_is_64_bit)
+ {
+ uint64_t pri;
+ if (m_process->ReadMemory (tsd + (dti_qos_class_index * 8), 8, &pri) == 8)
+ {
+ pthread_priority_value = pri;
+ }
+ }
+ else
+ {
+ uint32_t pri;
+ if (m_process->ReadMemory (tsd + (dti_qos_class_index * 4), 4, &pri) == 4)
+ {
+ pthread_priority_value = pri;
+ }
+ }
+
+ uint32_t requested_qos = m_pthread_qos_class_decode (pthread_priority_value, NULL, NULL);
+
+ switch (requested_qos)
+ {
+ // These constants from <pthread/qos.h>
+ case 0x21:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_USER_INTERACTIVE";
+ qos_value.printable_name = "User Interactive";
+ break;
+ case 0x19:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_USER_INITIATED";
+ qos_value.printable_name = "User Initiated";
+ break;
+ case 0x15:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_DEFAULT";
+ qos_value.printable_name = "Default";
+ break;
+ case 0x11:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_UTILITY";
+ qos_value.printable_name = "Utility";
+ break;
+ case 0x09:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_BACKGROUND";
+ qos_value.printable_name = "Background";
+ break;
+ case 0x00:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_UNSPECIFIED";
+ qos_value.printable_name = "Unspecified";
+ break;
+ }
+ }
+ return qos_value;
+}
diff --git a/tools/debugserver/source/MacOSX/MachThread.h b/tools/debugserver/source/MacOSX/MachThread.h
new file mode 100644
index 000000000000..a2a318172588
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachThread.h
@@ -0,0 +1,159 @@
+//===-- MachThread.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachThread_h__
+#define __MachThread_h__
+
+#include <string>
+#include <vector>
+
+#include <libproc.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include <sys/signal.h>
+
+#include "PThreadCondition.h"
+#include "PThreadMutex.h"
+#include "MachException.h"
+#include "DNBArch.h"
+#include "DNBRegisterInfo.h"
+
+#include "ThreadInfo.h"
+
+class DNBBreakpoint;
+class MachProcess;
+class MachThreadList;
+
+class MachThread
+{
+public:
+
+ MachThread (MachProcess *process, bool is_64_bit, uint64_t unique_thread_id = 0, thread_t mach_port_number = 0);
+ ~MachThread ();
+
+ MachProcess * Process() { return m_process; }
+ const MachProcess *
+ Process() const { return m_process; }
+ nub_process_t ProcessID() const;
+ void Dump(uint32_t index);
+ uint64_t ThreadID() const { return m_unique_id; }
+ thread_t MachPortNumber() const { return m_mach_port_number; }
+ thread_t InferiorThreadID() const;
+
+ uint32_t SequenceID() const { return m_seq_id; }
+ static bool ThreadIDIsValid(uint64_t thread); // The 64-bit system-wide unique thread identifier
+ static bool MachPortNumberIsValid(thread_t thread); // The mach port # for this thread in debugserver namespace
+ void Resume(bool others_stopped);
+ void Suspend();
+ bool SetSuspendCountBeforeResume(bool others_stopped);
+ bool RestoreSuspendCountAfterStop();
+
+ bool GetRegisterState(int flavor, bool force);
+ bool SetRegisterState(int flavor);
+ uint64_t GetPC(uint64_t failValue = INVALID_NUB_ADDRESS); // Get program counter
+ bool SetPC(uint64_t value); // Set program counter
+ uint64_t GetSP(uint64_t failValue = INVALID_NUB_ADDRESS); // Get stack pointer
+
+ DNBBreakpoint * CurrentBreakpoint();
+ uint32_t EnableHardwareBreakpoint (const DNBBreakpoint *breakpoint);
+ uint32_t EnableHardwareWatchpoint (const DNBBreakpoint *watchpoint, bool also_set_on_task);
+ bool DisableHardwareBreakpoint (const DNBBreakpoint *breakpoint);
+ bool DisableHardwareWatchpoint (const DNBBreakpoint *watchpoint, bool also_set_on_task);
+ uint32_t NumSupportedHardwareWatchpoints () const;
+ bool RollbackTransForHWP();
+ bool FinishTransForHWP();
+
+ nub_state_t GetState();
+ void SetState(nub_state_t state);
+
+ void ThreadWillResume (const DNBThreadResumeAction *thread_action, bool others_stopped = false);
+ bool ShouldStop(bool &step_more);
+ bool IsStepping();
+ bool ThreadDidStop();
+ bool NotifyException(MachException::Data& exc);
+ const MachException::Data& GetStopException() { return m_stop_exception; }
+
+ nub_size_t GetNumRegistersInSet(nub_size_t regSet) const;
+ const char * GetRegisterSetName(nub_size_t regSet) const;
+ const DNBRegisterInfo *
+ GetRegisterInfo(nub_size_t regSet, nub_size_t regIndex) const;
+ void DumpRegisterState(nub_size_t regSet);
+ const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets ) const;
+ bool GetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value );
+ bool SetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value );
+ nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+ uint32_t SaveRegisterState ();
+ bool RestoreRegisterState (uint32_t save_id);
+
+ void NotifyBreakpointChanged (const DNBBreakpoint *bp)
+ {
+ }
+
+ bool IsUserReady();
+ struct thread_basic_info *
+ GetBasicInfo ();
+ const char * GetBasicInfoAsString () const;
+ const char * GetName ();
+
+ DNBArchProtocol*
+ GetArchProtocol()
+ {
+ return m_arch_ap.get();
+ }
+
+ ThreadInfo::QoS GetRequestedQoS (nub_addr_t tsd, uint64_t dti_qos_class_index);
+ nub_addr_t GetPThreadT();
+ nub_addr_t GetDispatchQueueT();
+ nub_addr_t GetTSDAddressForThread (uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size);
+
+ static uint64_t GetGloballyUniqueThreadIDForMachPortID (thread_t mach_port_id);
+
+protected:
+ static bool GetBasicInfo(thread_t threadID, struct thread_basic_info *basic_info);
+
+ bool
+ GetIdentifierInfo ();
+
+// const char *
+// GetDispatchQueueName();
+//
+ MachProcess * m_process; // The process that owns this thread
+ uint64_t m_unique_id; // The globally unique ID for this thread (nub_thread_t)
+ thread_t m_mach_port_number; // The mach port # for this thread in debugserver namesp.
+ uint32_t m_seq_id; // A Sequential ID that increments with each new thread
+ nub_state_t m_state; // The state of our process
+ PThreadMutex m_state_mutex; // Multithreaded protection for m_state
+ struct thread_basic_info m_basic_info; // Basic information for a thread used to see if a thread is valid
+ int32_t m_suspend_count; // The current suspend count > 0 means we have suspended m_suspendCount times,
+ // < 0 means we have resumed it m_suspendCount times.
+ MachException::Data m_stop_exception; // The best exception that describes why this thread is stopped
+ std::unique_ptr<DNBArchProtocol> m_arch_ap; // Arch specific information for register state and more
+ const DNBRegisterSetInfo * m_reg_sets; // Register set information for this thread
+ nub_size_t m_num_reg_sets;
+ thread_identifier_info_data_t m_ident_info;
+ struct proc_threadinfo m_proc_threadinfo;
+ std::string m_dispatch_queue_name;
+ bool m_is_64_bit;
+
+ // qos_class_t _pthread_qos_class_decode(pthread_priority_t priority, int *, unsigned long *);
+ unsigned int (*m_pthread_qos_class_decode) (unsigned long priority, int*, unsigned long *);
+
+private:
+ friend class MachThreadList;
+};
+
+typedef std::shared_ptr<MachThread> MachThreadSP;
+
+#endif
diff --git a/tools/debugserver/source/MacOSX/MachThreadList.cpp b/tools/debugserver/source/MacOSX/MachThreadList.cpp
new file mode 100644
index 000000000000..8a7da6f4531e
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachThreadList.cpp
@@ -0,0 +1,668 @@
+//===-- MachThreadList.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachThreadList.h"
+
+#include <inttypes.h>
+#include <sys/sysctl.h>
+
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "MachProcess.h"
+
+MachThreadList::MachThreadList() :
+ m_threads(),
+ m_threads_mutex(PTHREAD_MUTEX_RECURSIVE),
+ m_is_64_bit(false)
+{
+}
+
+MachThreadList::~MachThreadList()
+{
+}
+
+nub_state_t
+MachThreadList::GetState(nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetState();
+ return eStateInvalid;
+}
+
+const char *
+MachThreadList::GetName (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetName();
+ return NULL;
+}
+
+ThreadInfo::QoS
+MachThreadList::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index);
+ return ThreadInfo::QoS();
+}
+
+nub_addr_t
+MachThreadList::GetPThreadT (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetPThreadT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+MachThreadList::GetDispatchQueueT (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetDispatchQueueT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+MachThreadList::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetTSDAddressForThread(plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_thread_t
+MachThreadList::SetCurrentThread(nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ {
+ m_current_thread = thread_sp;
+ return tid;
+ }
+ return INVALID_NUB_THREAD;
+}
+
+
+bool
+MachThreadList::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetStopException().GetStopInfo(stop_info);
+ return false;
+}
+
+bool
+MachThreadList::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info)
+{
+ thread_t mach_port_number = GetMachPortNumberByThreadID (tid);
+
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ return ::thread_info (mach_port_number, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS;
+}
+
+void
+MachThreadList::DumpThreadStoppedReason (nub_thread_t tid) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ thread_sp->GetStopException().DumpStopReason();
+}
+
+const char *
+MachThreadList::GetThreadInfo (nub_thread_t tid) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetBasicInfoAsString();
+ return NULL;
+}
+
+MachThreadSP
+MachThreadList::GetThreadByID (nub_thread_t tid) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->ThreadID() == tid)
+ {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+MachThreadSP
+MachThreadList::GetThreadByMachPortNumber (thread_t mach_port_number) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->MachPortNumber() == mach_port_number)
+ {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+nub_thread_t
+MachThreadList::GetThreadIDByMachPortNumber (thread_t mach_port_number) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->MachPortNumber() == mach_port_number)
+ {
+ return m_threads[idx]->ThreadID();
+ }
+ }
+ return INVALID_NUB_THREAD;
+}
+
+thread_t
+MachThreadList::GetMachPortNumberByThreadID (nub_thread_t globally_unique_id) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->ThreadID() == globally_unique_id)
+ {
+ return m_threads[idx]->MachPortNumber();
+ }
+ }
+ return 0;
+}
+
+bool
+MachThreadList::GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value ) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetRegisterValue(set, reg, reg_value);
+
+ return false;
+}
+
+bool
+MachThreadList::SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value ) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->SetRegisterValue(set, reg, reg_value);
+
+ return false;
+}
+
+nub_size_t
+MachThreadList::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetRegisterContext (buf, buf_len);
+ return 0;
+}
+
+nub_size_t
+MachThreadList::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->SetRegisterContext (buf, buf_len);
+ return 0;
+}
+
+uint32_t
+MachThreadList::SaveRegisterState (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->SaveRegisterState ();
+ return 0;
+}
+
+bool
+MachThreadList::RestoreRegisterState (nub_thread_t tid, uint32_t save_id)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->RestoreRegisterState (save_id);
+ return 0;
+}
+
+
+nub_size_t
+MachThreadList::NumThreads () const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ return m_threads.size();
+}
+
+nub_thread_t
+MachThreadList::ThreadIDAtIndex (nub_size_t idx) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ if (idx < m_threads.size())
+ return m_threads[idx]->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+nub_thread_t
+MachThreadList::CurrentThreadID ( )
+{
+ MachThreadSP thread_sp;
+ CurrentThread(thread_sp);
+ if (thread_sp.get())
+ return thread_sp->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+bool
+MachThreadList::NotifyException(MachException::Data& exc)
+{
+ MachThreadSP thread_sp (GetThreadByMachPortNumber (exc.thread_port));
+ if (thread_sp)
+ {
+ thread_sp->NotifyException(exc);
+ return true;
+ }
+ return false;
+}
+
+void
+MachThreadList::Clear()
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ m_threads.clear();
+}
+
+uint32_t
+MachThreadList::UpdateThreadList(MachProcess *process, bool update, MachThreadList::collection *new_threads)
+{
+ // locker will keep a mutex locked until it goes out of scope
+ DNBLogThreadedIf (LOG_THREAD, "MachThreadList::UpdateThreadList (pid = %4.4x, update = %u) process stop count = %u", process->ProcessID(), update, process->StopCount());
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+
+ if (process->StopCount() == 0)
+ {
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process->ProcessID() };
+ struct kinfo_proc processInfo;
+ size_t bufsize = sizeof(processInfo);
+ if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0)
+ {
+ if (processInfo.kp_proc.p_flag & P_LP64)
+ m_is_64_bit = true;
+ }
+#if defined (__i386__) || defined (__x86_64__)
+ if (m_is_64_bit)
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64);
+ else
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
+#elif defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ if (m_is_64_bit)
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
+ else
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
+#endif
+ }
+
+ if (m_threads.empty() || update)
+ {
+ thread_array_t thread_list = NULL;
+ mach_msg_type_number_t thread_list_count = 0;
+ task_t task = process->Task().TaskPort();
+ DNBError err(::task_threads (task, &thread_list, &thread_list_count), DNBError::MachKernel);
+
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count);
+
+ if (err.Error() == KERN_SUCCESS && thread_list_count > 0)
+ {
+ MachThreadList::collection currThreads;
+ size_t idx;
+ // Iterator through the current thread list and see which threads
+ // we already have in our list (keep them), which ones we don't
+ // (add them), and which ones are not around anymore (remove them).
+ for (idx = 0; idx < thread_list_count; ++idx)
+ {
+ const thread_t mach_port_num = thread_list[idx];
+
+ uint64_t unique_thread_id = MachThread::GetGloballyUniqueThreadIDForMachPortID (mach_port_num);
+ MachThreadSP thread_sp (GetThreadByID (unique_thread_id));
+ if (thread_sp)
+ {
+ // Keep the existing thread class
+ currThreads.push_back(thread_sp);
+ }
+ else
+ {
+ // We don't have this thread, lets add it.
+ thread_sp.reset(new MachThread(process, m_is_64_bit, unique_thread_id, mach_port_num));
+
+ // Add the new thread regardless of its is user ready state...
+ // Make sure the thread is ready to be displayed and shown to users
+ // before we add this thread to our list...
+ if (thread_sp->IsUserReady())
+ {
+ if (new_threads)
+ new_threads->push_back(thread_sp);
+
+ currThreads.push_back(thread_sp);
+ }
+ }
+ }
+
+ m_threads.swap(currThreads);
+ m_current_thread.reset();
+
+ // Free the vm memory given to us by ::task_threads()
+ vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t));
+ ::vm_deallocate (::mach_task_self(),
+ (vm_address_t)thread_list,
+ thread_list_size);
+ }
+ }
+ return static_cast<uint32_t>(m_threads.size());
+}
+
+
+void
+MachThreadList::CurrentThread (MachThreadSP& thread_sp)
+{
+ // locker will keep a mutex locked until it goes out of scope
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ if (m_current_thread.get() == NULL)
+ {
+ // Figure out which thread is going to be our current thread.
+ // This is currently done by finding the first thread in the list
+ // that has a valid exception.
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetStopException().IsValid())
+ {
+ m_current_thread = m_threads[idx];
+ break;
+ }
+ }
+ }
+ thread_sp = m_current_thread;
+}
+
+void
+MachThreadList::Dump() const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->Dump(idx);
+ }
+}
+
+
+void
+MachThreadList::ProcessWillResume(MachProcess *process, const DNBThreadResumeActions &thread_actions)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+
+ // Update our thread list, because sometimes libdispatch or the kernel
+ // will spawn threads while a task is suspended.
+ MachThreadList::collection new_threads;
+
+ // First figure out if we were planning on running only one thread, and if so force that thread to resume.
+ bool run_one_thread;
+ nub_thread_t solo_thread = INVALID_NUB_THREAD;
+ if (thread_actions.GetSize() > 0
+ && thread_actions.NumActionsWithState(eStateStepping) + thread_actions.NumActionsWithState (eStateRunning) == 1)
+ {
+ run_one_thread = true;
+ const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst();
+ size_t num_actions = thread_actions.GetSize();
+ for (size_t i = 0; i < num_actions; i++, action_ptr++)
+ {
+ if (action_ptr->state == eStateStepping || action_ptr->state == eStateRunning)
+ {
+ solo_thread = action_ptr->tid;
+ break;
+ }
+ }
+ }
+ else
+ run_one_thread = false;
+
+ UpdateThreadList(process, true, &new_threads);
+
+ DNBThreadResumeAction resume_new_threads = { -1U, eStateRunning, 0, INVALID_NUB_ADDRESS };
+ // If we are planning to run only one thread, any new threads should be suspended.
+ if (run_one_thread)
+ resume_new_threads.state = eStateSuspended;
+
+ const size_t num_new_threads = new_threads.size();
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ MachThread *thread = m_threads[idx].get();
+ bool handled = false;
+ for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx)
+ {
+ if (thread == new_threads[new_idx].get())
+ {
+ thread->ThreadWillResume(&resume_new_threads);
+ handled = true;
+ break;
+ }
+ }
+
+ if (!handled)
+ {
+ const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true);
+ // There must always be a thread action for every thread.
+ assert (thread_action);
+ bool others_stopped = false;
+ if (solo_thread == thread->ThreadID())
+ others_stopped = true;
+ thread->ThreadWillResume (thread_action, others_stopped);
+ }
+ }
+
+ if (new_threads.size())
+ {
+ for (uint32_t idx = 0; idx < num_new_threads; ++idx)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "MachThreadList::ProcessWillResume (pid = %4.4x) stop-id=%u, resuming newly discovered thread: 0x%8.8" PRIx64 ", thread-is-user-ready=%i)",
+ process->ProcessID(),
+ process->StopCount(),
+ new_threads[idx]->ThreadID(),
+ new_threads[idx]->IsUserReady());
+ }
+ }
+}
+
+uint32_t
+MachThreadList::ProcessDidStop(MachProcess *process)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ // Update our thread list
+ const uint32_t num_threads = UpdateThreadList(process, true);
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->ThreadDidStop();
+ }
+ return num_threads;
+}
+
+//----------------------------------------------------------------------
+// Check each thread in our thread list to see if we should notify our
+// client of the current halt in execution.
+//
+// Breakpoints can have callback functions associated with them than
+// can return true to stop, or false to continue executing the inferior.
+//
+// RETURNS
+// true if we should stop and notify our clients
+// false if we should resume our child process and skip notification
+//----------------------------------------------------------------------
+bool
+MachThreadList::ShouldStop(bool &step_more)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ uint32_t should_stop = false;
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx)
+ {
+ should_stop = m_threads[idx]->ShouldStop(step_more);
+ }
+ return should_stop;
+}
+
+
+void
+MachThreadList::NotifyBreakpointChanged (const DNBBreakpoint *bp)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->NotifyBreakpointChanged(bp);
+ }
+}
+
+
+uint32_t
+MachThreadList::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+ if (bp != NULL)
+ {
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->EnableHardwareBreakpoint(bp);
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+MachThreadList::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+ if (bp != NULL)
+ {
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->DisableHardwareBreakpoint(bp);
+ }
+ return false;
+}
+
+// DNBWatchpointSet() -> MachProcess::CreateWatchpoint() -> MachProcess::EnableWatchpoint()
+// -> MachThreadList::EnableHardwareWatchpoint().
+uint32_t
+MachThreadList::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+ uint32_t hw_index = INVALID_NUB_HW_INDEX;
+ if (wp != NULL)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ // On Mac OS X we have to prime the control registers for new threads. We do this
+ // using the control register data for the first thread, for lack of a better way of choosing.
+ bool also_set_on_task = true;
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if ((hw_index = m_threads[idx]->EnableHardwareWatchpoint(wp, also_set_on_task)) == INVALID_NUB_HW_INDEX)
+ {
+ // We know that idx failed for some reason. Let's rollback the transaction for [0, idx).
+ for (uint32_t i = 0; i < idx; ++i)
+ m_threads[i]->RollbackTransForHWP();
+ return INVALID_NUB_HW_INDEX;
+ }
+ also_set_on_task = false;
+ }
+ // Notify each thread to commit the pending transaction.
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->FinishTransForHWP();
+
+ }
+ return hw_index;
+}
+
+bool
+MachThreadList::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+ if (wp != NULL)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+
+ // On Mac OS X we have to prime the control registers for new threads. We do this
+ // using the control register data for the first thread, for lack of a better way of choosing.
+ bool also_set_on_task = true;
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (!m_threads[idx]->DisableHardwareWatchpoint(wp, also_set_on_task))
+ {
+ // We know that idx failed for some reason. Let's rollback the transaction for [0, idx).
+ for (uint32_t i = 0; i < idx; ++i)
+ m_threads[i]->RollbackTransForHWP();
+ return false;
+ }
+ also_set_on_task = false;
+ }
+ // Notify each thread to commit the pending transaction.
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->FinishTransForHWP();
+
+ return true;
+ }
+ return false;
+}
+
+uint32_t
+MachThreadList::NumSupportedHardwareWatchpoints () const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ // Use an arbitrary thread to retrieve the number of supported hardware watchpoints.
+ if (num_threads)
+ return m_threads[0]->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t
+MachThreadList::GetThreadIndexForThreadStoppedWithSignal (const int signo) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ uint32_t should_stop = false;
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetStopException().SoftSignal () == signo)
+ return idx;
+ }
+ return UINT32_MAX;
+}
+
diff --git a/tools/debugserver/source/MacOSX/MachThreadList.h b/tools/debugserver/source/MacOSX/MachThreadList.h
new file mode 100644
index 000000000000..0ab550e83fc6
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachThreadList.h
@@ -0,0 +1,87 @@
+//===-- MachThreadList.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachThreadList_h__
+#define __MachThreadList_h__
+
+#include "MachThread.h"
+#include "ThreadInfo.h"
+
+class DNBThreadResumeActions;
+
+class MachThreadList
+{
+public:
+ MachThreadList ();
+ ~MachThreadList ();
+
+ void Clear ();
+ void Dump () const;
+ bool GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value) const;
+ bool SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value) const;
+ nub_size_t GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len);
+ nub_size_t SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len);
+ uint32_t SaveRegisterState (nub_thread_t tid);
+ bool RestoreRegisterState (nub_thread_t tid, uint32_t save_id);
+ const char * GetThreadInfo (nub_thread_t tid) const;
+ void ProcessWillResume (MachProcess *process, const DNBThreadResumeActions &thread_actions);
+ uint32_t ProcessDidStop (MachProcess *process);
+ bool NotifyException (MachException::Data& exc);
+ bool ShouldStop (bool &step_more);
+ const char * GetName (nub_thread_t tid);
+ nub_state_t GetState (nub_thread_t tid);
+ nub_thread_t SetCurrentThread (nub_thread_t tid);
+
+ ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index);
+ nub_addr_t GetPThreadT (nub_thread_t tid);
+ nub_addr_t GetDispatchQueueT (nub_thread_t tid);
+ nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size);
+
+ bool GetThreadStoppedReason (nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const;
+ void DumpThreadStoppedReason (nub_thread_t tid) const;
+ bool GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info);
+ nub_size_t NumThreads () const;
+ nub_thread_t ThreadIDAtIndex (nub_size_t idx) const;
+ nub_thread_t CurrentThreadID ();
+ void CurrentThread (MachThreadSP& threadSP);
+ void NotifyBreakpointChanged (const DNBBreakpoint *bp);
+ uint32_t EnableHardwareBreakpoint (const DNBBreakpoint *bp) const;
+ bool DisableHardwareBreakpoint (const DNBBreakpoint *bp) const;
+ uint32_t EnableHardwareWatchpoint (const DNBBreakpoint *wp) const;
+ bool DisableHardwareWatchpoint (const DNBBreakpoint *wp) const;
+ uint32_t NumSupportedHardwareWatchpoints () const;
+
+ uint32_t GetThreadIndexForThreadStoppedWithSignal (const int signo) const;
+
+ MachThreadSP GetThreadByID (nub_thread_t tid) const;
+
+ MachThreadSP GetThreadByMachPortNumber (thread_t mach_port_number) const;
+ nub_thread_t GetThreadIDByMachPortNumber (thread_t mach_port_number) const;
+ thread_t GetMachPortNumberByThreadID (nub_thread_t globally_unique_id) const;
+
+protected:
+ typedef std::vector<MachThreadSP> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ uint32_t UpdateThreadList (MachProcess *process, bool update, collection *num_threads = NULL);
+// const_iterator FindThreadByID (thread_t tid) const;
+
+ collection m_threads;
+ mutable PThreadMutex m_threads_mutex;
+ MachThreadSP m_current_thread;
+ bool m_is_64_bit;
+};
+
+#endif // #ifndef __MachThreadList_h__
+
diff --git a/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/tools/debugserver/source/MacOSX/MachVMMemory.cpp
new file mode 100644
index 000000000000..3b86a83024d9
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachVMMemory.cpp
@@ -0,0 +1,598 @@
+//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachVMMemory.h"
+#include "MachVMRegion.h"
+#include "DNBLog.h"
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
+#include <sys/sysctl.h>
+#include <dlfcn.h>
+
+static const vm_size_t kInvalidPageSize = ~0;
+
+MachVMMemory::MachVMMemory() :
+ m_page_size (kInvalidPageSize),
+ m_err (0)
+{
+}
+
+MachVMMemory::~MachVMMemory()
+{
+}
+
+nub_size_t
+MachVMMemory::PageSize(task_t task)
+{
+ if (m_page_size == kInvalidPageSize)
+ {
+#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
+ if (task != TASK_NULL)
+ {
+ kern_return_t kr;
+ mach_msg_type_number_t info_count = TASK_VM_INFO_COUNT;
+ task_vm_info_data_t vm_info;
+ kr = task_info (task, TASK_VM_INFO, (task_info_t) &vm_info, &info_count);
+ if (kr == KERN_SUCCESS)
+ {
+ DNBLogThreadedIf(LOG_TASK, "MachVMMemory::PageSize task_info returned page size of 0x%x", (int) vm_info.page_size);
+ m_page_size = vm_info.page_size;
+ return m_page_size;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_TASK, "MachVMMemory::PageSize task_info call failed to get page size, TASK_VM_INFO %d, TASK_VM_INFO_COUNT %d, kern return %d", TASK_VM_INFO, TASK_VM_INFO_COUNT, kr);
+ }
+ }
+#endif
+ m_err = ::host_page_size( ::mach_host_self(), &m_page_size);
+ if (m_err.Fail())
+ m_page_size = 0;
+ }
+ return m_page_size;
+}
+
+nub_size_t
+MachVMMemory::MaxBytesLeftInPage(task_t task, nub_addr_t addr, nub_size_t count)
+{
+ const nub_size_t page_size = PageSize(task);
+ if (page_size > 0)
+ {
+ nub_size_t page_offset = (addr % page_size);
+ nub_size_t bytes_left_in_page = page_size - page_offset;
+ if (count > bytes_left_in_page)
+ count = bytes_left_in_page;
+ }
+ return count;
+}
+
+nub_bool_t
+MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info)
+{
+ MachVMRegion vmRegion(task);
+
+ if (vmRegion.GetRegionForAddress(address))
+ {
+ region_info->addr = vmRegion.StartAddress();
+ region_info->size = vmRegion.GetByteSize();
+ region_info->permissions = vmRegion.GetDNBPermissions();
+ }
+ else
+ {
+ region_info->addr = address;
+ region_info->size = 0;
+ if (vmRegion.GetError().Success())
+ {
+ // vmRegion.GetRegionForAddress() return false, indicating that "address"
+ // wasn't in a valid region, but the "vmRegion" info was successfully
+ // read from the task which means the info describes the next valid
+ // region from which we can infer the size of this invalid region
+ mach_vm_address_t start_addr = vmRegion.StartAddress();
+ if (address < start_addr)
+ region_info->size = start_addr - address;
+ }
+ // If we can't get any info about the size from the next region it means
+ // we asked about an address that was past all mappings, so the size
+ // of this region will take up all remaining address space.
+ if (region_info->size == 0)
+ region_info->size = INVALID_NUB_ADDRESS - region_info->addr;
+
+ // Not readable, writeable or executable
+ region_info->permissions = 0;
+ }
+ return true;
+}
+
+// For integrated graphics chip, this makes the accounting info for 'wired' memory more like top.
+uint64_t
+MachVMMemory::GetStolenPages(task_t task)
+{
+ static uint64_t stolenPages = 0;
+ static bool calculated = false;
+ if (calculated) return stolenPages;
+
+ static int mib_reserved[CTL_MAXNAME];
+ static int mib_unusable[CTL_MAXNAME];
+ static int mib_other[CTL_MAXNAME];
+ static size_t mib_reserved_len = 0;
+ static size_t mib_unusable_len = 0;
+ static size_t mib_other_len = 0;
+ int r;
+
+ /* This can be used for testing: */
+ //tsamp->pages_stolen = (256 * 1024 * 1024ULL) / tsamp->pagesize;
+
+ if(0 == mib_reserved_len)
+ {
+ mib_reserved_len = CTL_MAXNAME;
+
+ r = sysctlnametomib("machdep.memmap.Reserved", mib_reserved,
+ &mib_reserved_len);
+
+ if(-1 == r)
+ {
+ mib_reserved_len = 0;
+ return 0;
+ }
+
+ mib_unusable_len = CTL_MAXNAME;
+
+ r = sysctlnametomib("machdep.memmap.Unusable", mib_unusable,
+ &mib_unusable_len);
+
+ if(-1 == r)
+ {
+ mib_reserved_len = 0;
+ return 0;
+ }
+
+
+ mib_other_len = CTL_MAXNAME;
+
+ r = sysctlnametomib("machdep.memmap.Other", mib_other,
+ &mib_other_len);
+
+ if(-1 == r)
+ {
+ mib_reserved_len = 0;
+ return 0;
+ }
+ }
+
+ if(mib_reserved_len > 0 && mib_unusable_len > 0 && mib_other_len > 0)
+ {
+ uint64_t reserved = 0, unusable = 0, other = 0;
+ size_t reserved_len;
+ size_t unusable_len;
+ size_t other_len;
+
+ reserved_len = sizeof(reserved);
+ unusable_len = sizeof(unusable);
+ other_len = sizeof(other);
+
+ /* These are all declared as QUAD/uint64_t sysctls in the kernel. */
+
+ if (sysctl (mib_reserved,
+ static_cast<u_int>(mib_reserved_len),
+ &reserved,
+ &reserved_len,
+ NULL,
+ 0))
+ {
+ return 0;
+ }
+
+ if (sysctl (mib_unusable,
+ static_cast<u_int>(mib_unusable_len),
+ &unusable,
+ &unusable_len,
+ NULL,
+ 0))
+ {
+ return 0;
+ }
+
+ if (sysctl (mib_other,
+ static_cast<u_int>(mib_other_len),
+ &other,
+ &other_len,
+ NULL,
+ 0))
+ {
+ return 0;
+ }
+
+ if (reserved_len == sizeof(reserved) &&
+ unusable_len == sizeof(unusable) &&
+ other_len == sizeof(other))
+ {
+ uint64_t stolen = reserved + unusable + other;
+ uint64_t mb128 = 128 * 1024 * 1024ULL;
+
+ if(stolen >= mb128)
+ {
+ stolen = (stolen & ~((128 * 1024 * 1024ULL) - 1)); // rounding down
+ stolenPages = stolen / PageSize (task);
+ }
+ }
+ }
+
+ calculated = true;
+ return stolenPages;
+}
+
+static uint64_t GetPhysicalMemory()
+{
+ // This doesn't change often at all. No need to poll each time.
+ static uint64_t physical_memory = 0;
+ static bool calculated = false;
+ if (calculated) return physical_memory;
+
+ size_t len = sizeof(physical_memory);
+ sysctlbyname("hw.memsize", &physical_memory, &len, NULL, 0);
+
+ calculated = true;
+ return physical_memory;
+}
+
+// rsize and dirty_size is not adjusted for dyld shared cache and multiple __LINKEDIT segment, as in vmmap. In practice, dirty_size doesn't differ much but rsize may. There is performance penalty for the adjustment. Right now, only use the dirty_size.
+void
+MachVMMemory::GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t &dirty_size)
+{
+#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
+
+ task_vm_info_data_t vm_info;
+ mach_msg_type_number_t info_count;
+ kern_return_t kr;
+
+ info_count = TASK_VM_INFO_COUNT;
+ kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count);
+ if (kr == KERN_SUCCESS)
+ dirty_size = vm_info.internal;
+#endif
+}
+
+// Test whether the virtual address is within the architecture's shared region.
+static bool InSharedRegion(mach_vm_address_t addr, cpu_type_t type)
+{
+ mach_vm_address_t base = 0, size = 0;
+
+ switch(type) {
+#if defined (CPU_TYPE_ARM64) && defined (SHARED_REGION_BASE_ARM64)
+ case CPU_TYPE_ARM64:
+ base = SHARED_REGION_BASE_ARM64;
+ size = SHARED_REGION_SIZE_ARM64;
+ break;
+#endif
+
+ case CPU_TYPE_ARM:
+ base = SHARED_REGION_BASE_ARM;
+ size = SHARED_REGION_SIZE_ARM;
+ break;
+
+ case CPU_TYPE_X86_64:
+ base = SHARED_REGION_BASE_X86_64;
+ size = SHARED_REGION_SIZE_X86_64;
+ break;
+
+ case CPU_TYPE_I386:
+ base = SHARED_REGION_BASE_I386;
+ size = SHARED_REGION_SIZE_I386;
+ break;
+
+ default: {
+ // Log error abut unknown CPU type
+ break;
+ }
+ }
+
+
+ return(addr >= base && addr < (base + size));
+}
+
+void
+MachVMMemory::GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, mach_vm_size_t &rprvt, mach_vm_size_t &vprvt)
+{
+ // Collecting some other info cheaply but not reporting for now.
+ mach_vm_size_t empty = 0;
+ mach_vm_size_t fw_private = 0;
+
+ mach_vm_size_t aliased = 0;
+ bool global_shared_text_data_mapped = false;
+ vm_size_t pagesize = PageSize (task);
+
+ for (mach_vm_address_t addr=0, size=0; ; addr += size)
+ {
+ vm_region_top_info_data_t info;
+ mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT;
+ mach_port_t object_name;
+
+ kern_return_t kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name);
+ if (kr != KERN_SUCCESS) break;
+
+ if (InSharedRegion(addr, cputype))
+ {
+ // Private Shared
+ fw_private += info.private_pages_resident * pagesize;
+
+ // Check if this process has the globally shared text and data regions mapped in. If so, set global_shared_text_data_mapped to TRUE and avoid checking again.
+ if (global_shared_text_data_mapped == FALSE && info.share_mode == SM_EMPTY) {
+ vm_region_basic_info_data_64_t b_info;
+ mach_vm_address_t b_addr = addr;
+ mach_vm_size_t b_size = size;
+ count = VM_REGION_BASIC_INFO_COUNT_64;
+
+ kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name);
+ if (kr != KERN_SUCCESS) break;
+
+ if (b_info.reserved) {
+ global_shared_text_data_mapped = TRUE;
+ }
+ }
+
+ // Short circuit the loop if this isn't a shared private region, since that's the only region type we care about within the current address range.
+ if (info.share_mode != SM_PRIVATE)
+ {
+ continue;
+ }
+ }
+
+ // Update counters according to the region type.
+ if (info.share_mode == SM_COW && info.ref_count == 1)
+ {
+ // Treat single reference SM_COW as SM_PRIVATE
+ info.share_mode = SM_PRIVATE;
+ }
+
+ switch (info.share_mode)
+ {
+ case SM_LARGE_PAGE:
+ // Treat SM_LARGE_PAGE the same as SM_PRIVATE
+ // since they are not shareable and are wired.
+ case SM_PRIVATE:
+ rprvt += info.private_pages_resident * pagesize;
+ rprvt += info.shared_pages_resident * pagesize;
+ vprvt += size;
+ break;
+
+ case SM_EMPTY:
+ empty += size;
+ break;
+
+ case SM_COW:
+ case SM_SHARED:
+ {
+ if (pid == 0)
+ {
+ // Treat kernel_task specially
+ if (info.share_mode == SM_COW)
+ {
+ rprvt += info.private_pages_resident * pagesize;
+ vprvt += size;
+ }
+ break;
+ }
+
+ if (info.share_mode == SM_COW)
+ {
+ rprvt += info.private_pages_resident * pagesize;
+ vprvt += info.private_pages_resident * pagesize;
+ }
+ break;
+ }
+ default:
+ // log that something is really bad.
+ break;
+ }
+ }
+
+ rprvt += aliased;
+}
+
+static void
+GetPurgeableAndAnonymous(task_t task, uint64_t &purgeable, uint64_t &anonymous)
+{
+#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22
+
+ kern_return_t kr;
+ mach_msg_type_number_t info_count;
+ task_vm_info_data_t vm_info;
+
+ info_count = TASK_VM_INFO_COUNT;
+ kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count);
+ if (kr == KERN_SUCCESS)
+ {
+ purgeable = vm_info.purgeable_volatile_resident;
+ anonymous = vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap;
+ }
+
+#endif
+}
+
+#if defined (HOST_VM_INFO64_COUNT)
+nub_bool_t
+MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics64_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous)
+#else
+nub_bool_t
+MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous)
+#endif
+{
+ if (scanType & eProfileHostMemory)
+ physical_memory = GetPhysicalMemory();
+
+ if (scanType & eProfileMemory)
+ {
+ static mach_port_t localHost = mach_host_self();
+#if defined (HOST_VM_INFO64_COUNT)
+ mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
+ host_statistics64(localHost, HOST_VM_INFO64, (host_info64_t)&vminfo, &count);
+#else
+ mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
+ host_statistics(localHost, HOST_VM_INFO, (host_info_t)&vminfo, &count);
+ vminfo.wire_count += GetStolenPages(task);
+#endif
+
+ /* We are no longer reporting these. Let's not waste time.
+ GetMemorySizes(task, cputype, pid, rprvt, vprvt);
+ rsize = ti.resident_size;
+ vsize = ti.virtual_size;
+
+ if (scanType & eProfileMemoryDirtyPage)
+ {
+ // This uses vmmap strategy. We don't use the returned rsize for now. We prefer to match top's version since that's what we do for the rest of the metrics.
+ GetRegionSizes(task, rsize, dirty_size);
+ }
+ */
+
+ if (scanType & eProfileMemoryAnonymous)
+ {
+ GetPurgeableAndAnonymous(task, purgeable, anonymous);
+ }
+ }
+
+ return true;
+}
+
+nub_size_t
+MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count)
+{
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ nub_size_t total_bytes_read = 0;
+ nub_addr_t curr_addr = address;
+ uint8_t *curr_data = (uint8_t*)data;
+ while (total_bytes_read < data_count)
+ {
+ mach_vm_size_t curr_size = MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_read);
+ mach_msg_type_number_t curr_bytes_read = 0;
+ vm_offset_t vm_memory = 0;
+ m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read);
+
+ if (DNBLogCheckLogBit(LOG_MEMORY))
+ m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i )", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read);
+
+ if (m_err.Success())
+ {
+ if (curr_bytes_read != curr_size)
+ {
+ if (DNBLogCheckLogBit(LOG_MEMORY))
+ m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i ) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size);
+ }
+ ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read);
+ ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read);
+ total_bytes_read += curr_bytes_read;
+ curr_addr += curr_bytes_read;
+ curr_data += curr_bytes_read;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return total_bytes_read;
+}
+
+
+nub_size_t
+MachVMMemory::Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count)
+{
+ MachVMRegion vmRegion(task);
+
+ nub_size_t total_bytes_written = 0;
+ nub_addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t*)data;
+
+
+ while (total_bytes_written < data_count)
+ {
+ if (vmRegion.GetRegionForAddress(curr_addr))
+ {
+ mach_vm_size_t curr_data_count = data_count - total_bytes_written;
+ mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
+ if (region_bytes_left == 0)
+ {
+ break;
+ }
+ if (curr_data_count > region_bytes_left)
+ curr_data_count = region_bytes_left;
+
+ if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE))
+ {
+ nub_size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count);
+ if (bytes_written <= 0)
+ {
+ // Error should have already be posted by WriteRegion...
+ break;
+ }
+ else
+ {
+ total_bytes_written += bytes_written;
+ curr_addr += bytes_written;
+ curr_data += bytes_written;
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count));
+ break;
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address);
+ break;
+ }
+ }
+
+ return total_bytes_written;
+}
+
+
+nub_size_t
+MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count)
+{
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ nub_size_t total_bytes_written = 0;
+ nub_addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t*)data;
+ while (total_bytes_written < data_count)
+ {
+ mach_msg_type_number_t curr_data_count = static_cast<mach_msg_type_number_t>(MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_written));
+ m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count);
+ if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count);
+
+#if !defined (__i386__) && !defined (__x86_64__)
+ vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
+
+ m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value);
+ if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
+ m_err.LogThreaded("::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count);
+#endif
+
+ if (m_err.Success())
+ {
+ total_bytes_written += curr_data_count;
+ curr_addr += curr_data_count;
+ curr_data += curr_data_count;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return total_bytes_written;
+}
diff --git a/tools/debugserver/source/MacOSX/MachVMMemory.h b/tools/debugserver/source/MacOSX/MachVMMemory.h
new file mode 100644
index 000000000000..abaa20368a26
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachVMMemory.h
@@ -0,0 +1,51 @@
+//===-- MachVMMemory.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachVMMemory_h__
+#define __MachVMMemory_h__
+
+#include "DNBDefs.h"
+#include "DNBError.h"
+#include <mach/mach.h>
+
+class MachVMMemory
+{
+public:
+ MachVMMemory();
+ ~MachVMMemory();
+ nub_size_t Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count);
+ nub_size_t Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count);
+ nub_size_t PageSize(task_t task);
+ nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info);
+#if defined (HOST_VM_INFO64_COUNT)
+ nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics64_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous);
+#else
+ nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous);
+#endif
+
+protected:
+ nub_size_t MaxBytesLeftInPage(task_t task, nub_addr_t addr, nub_size_t count);
+
+ uint64_t GetStolenPages(task_t task);
+ void GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t &dirty_size);
+ void GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, mach_vm_size_t &rprvt, mach_vm_size_t &vprvt);
+
+
+ nub_size_t WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count);
+
+ vm_size_t m_page_size;
+ DNBError m_err;
+};
+
+
+#endif // #ifndef __MachVMMemory_h__
diff --git a/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/tools/debugserver/source/MacOSX/MachVMRegion.cpp
new file mode 100644
index 000000000000..38757595cfed
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachVMRegion.cpp
@@ -0,0 +1,202 @@
+//===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachVMRegion.h"
+#include <mach/mach_vm.h>
+#include "DNBLog.h"
+#include <assert.h>
+
+MachVMRegion::MachVMRegion(task_t task) :
+ m_task(task),
+ m_addr(INVALID_NUB_ADDRESS),
+ m_err(),
+ m_start(INVALID_NUB_ADDRESS),
+ m_size(0),
+ m_depth(-1),
+ m_curr_protection(0),
+ m_protection_addr(INVALID_NUB_ADDRESS),
+ m_protection_size(0)
+{
+ memset(&m_data, 0, sizeof(m_data));
+}
+
+MachVMRegion::~MachVMRegion()
+{
+ // Restore any original protections and clear our vars
+ Clear();
+}
+
+void
+MachVMRegion::Clear()
+{
+ RestoreProtections();
+ m_addr = INVALID_NUB_ADDRESS;
+ m_err.Clear();
+ m_start = INVALID_NUB_ADDRESS;
+ m_size = 0;
+ m_depth = -1;
+ memset(&m_data, 0, sizeof(m_data));
+ m_curr_protection = 0;
+ m_protection_addr = INVALID_NUB_ADDRESS;
+ m_protection_size = 0;
+}
+
+bool
+MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot)
+{
+ if (ContainsAddress(addr))
+ {
+ mach_vm_size_t prot_size = size;
+ mach_vm_address_t end_addr = EndAddress();
+ if (prot_size > (end_addr - addr))
+ prot_size = end_addr - addr;
+
+ if (prot_size > 0)
+ {
+ if (prot == (m_curr_protection & VM_PROT_ALL))
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE, "MachVMRegion::%s: protections (%u) already sufficient for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, prot, m_task, (uint64_t)addr);
+ // Protections are already set as requested...
+ return true;
+ }
+ else
+ {
+ m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS))
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot);
+ if (m_err.Fail())
+ {
+ // Try again with the ability to create a copy on write region
+ m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot | VM_PROT_COPY);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot | VM_PROT_COPY);
+ }
+ if (m_err.Success())
+ {
+ m_curr_protection = prot;
+ m_protection_addr = addr;
+ m_protection_size = prot_size;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE, "%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, m_task, (uint64_t)addr);
+ }
+ }
+ return false;
+}
+
+bool
+MachVMRegion::RestoreProtections()
+{
+ if (m_curr_protection != m_data.protection && m_protection_size > 0)
+ {
+ m_err = ::mach_vm_protect (m_task, m_protection_addr, m_protection_size, 0, m_data.protection);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)m_protection_addr, (uint64_t)m_protection_size, 0, m_data.protection);
+ if (m_err.Success())
+ {
+ m_protection_size = 0;
+ m_protection_addr = INVALID_NUB_ADDRESS;
+ m_curr_protection = m_data.protection;
+ return true;
+ }
+ }
+ else
+ {
+ m_err.Clear();
+ return true;
+ }
+
+ return false;
+}
+
+bool
+MachVMRegion::GetRegionForAddress(nub_addr_t addr)
+{
+ // Restore any original protections and clear our vars
+ Clear();
+ m_err.Clear();
+ m_addr = addr;
+ m_start = addr;
+ m_depth = 1024;
+ mach_msg_type_number_t info_size = kRegionInfoSize;
+ assert(sizeof(info_size) == 4);
+ m_err = ::mach_vm_region_recurse (m_task, &m_start, &m_size, &m_depth, (vm_region_recurse_info_t)&m_data, &info_size);
+
+ const bool failed = m_err.Fail();
+ const bool log_protections = DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS);
+
+ if (log_protections || failed)
+ m_err.LogThreaded("::mach_vm_region_recurse ( task = 0x%4.4x, address => 0x%8.8llx, size => %llu, nesting_depth => %d, info => %p, infoCnt => %d) addr = 0x%8.8llx ", m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth, &m_data, info_size, (uint64_t)addr);
+
+ if (failed)
+ return false;
+ if (log_protections)
+ {
+ DNBLogThreaded("info = { prot = %u, "
+ "max_prot = %u, "
+ "inheritance = 0x%8.8x, "
+ "offset = 0x%8.8llx, "
+ "user_tag = 0x%8.8x, "
+ "ref_count = %u, "
+ "shadow_depth = %u, "
+ "ext_pager = %u, "
+ "share_mode = %u, "
+ "is_submap = %d, "
+ "behavior = %d, "
+ "object_id = 0x%8.8x, "
+ "user_wired_count = 0x%4.4x }",
+ m_data.protection,
+ m_data.max_protection,
+ m_data.inheritance,
+ (uint64_t)m_data.offset,
+ m_data.user_tag,
+ m_data.ref_count,
+ m_data.shadow_depth,
+ m_data.external_pager,
+ m_data.share_mode,
+ m_data.is_submap,
+ m_data.behavior,
+ m_data.object_id,
+ m_data.user_wired_count);
+ }
+ m_curr_protection = m_data.protection;
+
+ // We make a request for an address and got no error back, but this
+ // doesn't mean that "addr" is in the range. The data in this object will
+ // be valid though, so you could see where the next region begins. So we
+ // return false, yet leave "m_err" with a successfull return code.
+ if ((addr < m_start) || (addr >= (m_start + m_size)))
+ return false;
+
+ return true;
+}
+
+uint32_t
+MachVMRegion::GetDNBPermissions () const
+{
+ if (m_addr == INVALID_NUB_ADDRESS || m_start == INVALID_NUB_ADDRESS || m_size == 0)
+ return 0;
+ uint32_t dnb_permissions = 0;
+
+ if ((m_data.protection & VM_PROT_READ) == VM_PROT_READ)
+ dnb_permissions |= eMemoryPermissionsReadable;
+ if ((m_data.protection & VM_PROT_WRITE) == VM_PROT_WRITE)
+ dnb_permissions |= eMemoryPermissionsWritable;
+ if ((m_data.protection & VM_PROT_EXECUTE) == VM_PROT_EXECUTE)
+ dnb_permissions |= eMemoryPermissionsExecutable;
+ return dnb_permissions;
+}
diff --git a/tools/debugserver/source/MacOSX/MachVMRegion.h b/tools/debugserver/source/MacOSX/MachVMRegion.h
new file mode 100644
index 000000000000..bcac60b83182
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/MachVMRegion.h
@@ -0,0 +1,77 @@
+//===-- MachVMRegion.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachVMRegion_h__
+#define __MachVMRegion_h__
+
+#include "DNBDefs.h"
+#include "DNBError.h"
+#include <mach/mach.h>
+
+class MachVMRegion
+{
+public:
+ MachVMRegion(task_t task);
+ ~MachVMRegion();
+
+ void Clear();
+ mach_vm_address_t StartAddress() const { return m_start; }
+ mach_vm_address_t EndAddress() const { return m_start + m_size; }
+ mach_vm_size_t GetByteSize () const { return m_size; }
+ mach_vm_address_t BytesRemaining(mach_vm_address_t addr) const
+ {
+ if (ContainsAddress(addr))
+ return m_size - (addr - m_start);
+ else
+ return 0;
+ }
+ bool ContainsAddress(mach_vm_address_t addr) const
+ {
+ return addr >= StartAddress() && addr < EndAddress();
+ }
+
+ bool SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot);
+ bool RestoreProtections();
+ bool GetRegionForAddress(nub_addr_t addr);
+
+ uint32_t
+ GetDNBPermissions () const;
+
+ const DNBError &
+ GetError ()
+ {
+ return m_err;
+ }
+protected:
+#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64)
+ typedef vm_region_submap_short_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
+#else
+ typedef vm_region_submap_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 };
+#endif
+
+ task_t m_task;
+ mach_vm_address_t m_addr;
+ DNBError m_err;
+ mach_vm_address_t m_start;
+ mach_vm_size_t m_size;
+ natural_t m_depth;
+ RegionInfo m_data;
+ vm_prot_t m_curr_protection; // The current, possibly modified protections. Original value is saved in m_data.protections.
+ mach_vm_address_t m_protection_addr; // The start address at which protections were changed
+ mach_vm_size_t m_protection_size; // The size of memory that had its protections changed
+
+};
+
+#endif // #ifndef __MachVMRegion_h__
diff --git a/tools/debugserver/source/MacOSX/Makefile b/tools/debugserver/source/MacOSX/Makefile
new file mode 100644
index 000000000000..d047444a9c81
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/Makefile
@@ -0,0 +1,54 @@
+##===- tools/debugserver/source/MacOSX/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 := ../../../..
+
+DIRS := i386 x86_64
+
+TOOLNAME = debugserver
+
+CODESIGN_TOOLS := 1
+
+TOOL_CODESIGN_IDENTITY := lldb_codesign
+
+LLVMLibsOptions += -llldbDebugserverCommon -llldbUtility -llldbDebugserverMacOSX_I386 -llldbDebugserverMacOSX_X86_64 \
+ -framework Foundation -framework CoreFoundation
+
+GENERATED_MACH_SOURCES = $(PROJ_OBJ_DIR)/mach_excServer.c $(PROJ_OBJ_DIR)/mach_excUser.c
+
+SOURCES := CFBundle.cpp \
+ CFData.cpp \
+ CFString.cpp \
+ MachException.cpp \
+ MachProcess.cpp \
+ MachTask.cpp \
+ MachThread.cpp \
+ MachThreadList.cpp \
+ MachVMMemory.cpp \
+ MachVMRegion.cpp
+
+BUILT_SOURCES = $(GENERATED_MACH_SOURCES) $(PROJ_OBJ_DIR)/HasAVX.o
+
+CPP.Flags += -I$(PROJ_OBJ_DIR)/../.. -I$(PROJ_SRC_DIR)/..
+
+LD.Flags += -Wl,-sectcreate,__TEXT,__info_plist,$(PROJ_SRC_DIR)/../../resources/lldb-debugserver-Info.plist
+
+include $(LLDB_LEVEL)/Makefile
+
+ObjectsO += $(PROJ_OBJ_DIR)/HasAVX.o
+
+$(PROJ_OBJ_DIR)/HasAVX.o: $(PROJ_SRC_DIR)/HasAVX.s
+ $(Echo) "Compiling HasAVX.s for $(BuildMode) build" $(PIC_FLAG)
+ $(CC) $(TargetCommonOpts) $(CompileCommonOpts) -c $< -o $@
+
+ifeq ($(HOST_OS),Darwin)
+LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/
+endif
+
+$(GENERATED_MACH_SOURCES):
+ mig -I$(PROJ_OBJ_DIR)/../.. $(PROJ_SRC_DIR)/dbgnub-mig.defs \ No newline at end of file
diff --git a/tools/debugserver/source/MacOSX/ThreadInfo.h b/tools/debugserver/source/MacOSX/ThreadInfo.h
new file mode 100644
index 000000000000..1fd9d5790cf0
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/ThreadInfo.h
@@ -0,0 +1,26 @@
+//===-- ThreadInfo.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __ThreadInfo_h__
+#define __ThreadInfo_h__
+
+namespace ThreadInfo {
+
+class QoS {
+public:
+ QoS () : constant_name(), printable_name(), enum_value(UINT32_MAX) { }
+ bool IsValid () { return enum_value != UINT32_MAX; }
+ std::string constant_name;
+ std::string printable_name;
+ uint32_t enum_value;
+};
+
+};
+
+#endif // __ThreadInfo_h__
diff --git a/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp
new file mode 100644
index 000000000000..2eac47b045c3
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp
@@ -0,0 +1,2162 @@
+//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+
+#include "MacOSX/arm/DNBArchImpl.h"
+#include "MacOSX/MachProcess.h"
+#include "MacOSX/MachThread.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+#include "DNB.h"
+#include "ARM_ehframe_Registers.h"
+#include "ARM_DWARF_Registers.h"
+
+#include <inttypes.h>
+#include <sys/sysctl.h>
+
+// BCR address match type
+#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21))
+#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21))
+#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21))
+#define BCR_M_RESERVED ((uint32_t)(3u << 21))
+
+// Link a BVR/BCR or WVR/WCR pair to another
+#define E_ENABLE_LINKING ((uint32_t)(1u << 20))
+
+// Byte Address Select
+#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5))
+#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6))
+#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7))
+#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8))
+#define BAS_IMVA_0_1 ((uint32_t)(3u << 5))
+#define BAS_IMVA_2_3 ((uint32_t)(3u << 7))
+#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5))
+
+// Break only in privileged or user mode
+#define S_RSVD ((uint32_t)(0u << 1))
+#define S_PRIV ((uint32_t)(1u << 1))
+#define S_USER ((uint32_t)(2u << 1))
+#define S_PRIV_USER ((S_PRIV) | (S_USER))
+
+#define BCR_ENABLE ((uint32_t)(1u))
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+// Definitions for the Debug Status and Control Register fields:
+// [5:2] => Method of debug entry
+//#define WATCHPOINT_OCCURRED ((uint32_t)(2u))
+// I'm seeing this, instead.
+#define WATCHPOINT_OCCURRED ((uint32_t)(10u))
+
+// 0xE120BE70
+static const uint8_t g_arm_breakpoint_opcode[] = { 0x70, 0xBE, 0x20, 0xE1 };
+static const uint8_t g_thumb_breakpoint_opcode[] = { 0x70, 0xBE };
+
+// A watchpoint may need to be implemented using two watchpoint registers.
+// e.g. watching an 8-byte region when the device can only watch 4-bytes.
+//
+// This stores the lo->hi mappings. It's safe to initialize to all 0's
+// since hi > lo and therefore LoHi[i] cannot be 0.
+static uint32_t LoHi[16] = { 0 };
+
+// ARM constants used during decoding
+#define REG_RD 0
+#define LDM_REGLIST 1
+#define PC_REG 15
+#define PC_REGLIST_BIT 0x8000
+
+// ARM conditions
+#define COND_EQ 0x0
+#define COND_NE 0x1
+#define COND_CS 0x2
+#define COND_HS 0x2
+#define COND_CC 0x3
+#define COND_LO 0x3
+#define COND_MI 0x4
+#define COND_PL 0x5
+#define COND_VS 0x6
+#define COND_VC 0x7
+#define COND_HI 0x8
+#define COND_LS 0x9
+#define COND_GE 0xA
+#define COND_LT 0xB
+#define COND_GT 0xC
+#define COND_LE 0xD
+#define COND_AL 0xE
+#define COND_UNCOND 0xF
+
+#define MASK_CPSR_T (1u << 5)
+#define MASK_CPSR_J (1u << 24)
+
+#define MNEMONIC_STRING_SIZE 32
+#define OPERAND_STRING_SIZE 128
+
+// Returns true if the first 16 bit opcode of a thumb instruction indicates
+// the instruction will be a 32 bit thumb opcode
+static bool
+IsThumb32Opcode (uint16_t opcode)
+{
+ if (((opcode & 0xE000) == 0xE000) && (opcode & 0x1800))
+ return true;
+ return false;
+}
+
+void
+DNBArchMachARM::Initialize()
+{
+ DNBArchPluginInfo arch_plugin_info =
+ {
+ CPU_TYPE_ARM,
+ DNBArchMachARM::Create,
+ DNBArchMachARM::GetRegisterSetInfo,
+ DNBArchMachARM::SoftwareBreakpointOpcode
+ };
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin (arch_plugin_info);
+}
+
+
+DNBArchProtocol *
+DNBArchMachARM::Create (MachThread *thread)
+{
+ DNBArchMachARM *obj = new DNBArchMachARM (thread);
+ return obj;
+}
+
+const uint8_t *
+DNBArchMachARM::SoftwareBreakpointOpcode (nub_size_t byte_size)
+{
+ switch (byte_size)
+ {
+ case 2: return g_thumb_breakpoint_opcode;
+ case 4: return g_arm_breakpoint_opcode;
+ }
+ return NULL;
+}
+
+uint32_t
+DNBArchMachARM::GetCPUType()
+{
+ return CPU_TYPE_ARM;
+}
+
+uint64_t
+DNBArchMachARM::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__pc;
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachARM::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.context.gpr.__pc = (uint32_t) value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchMachARM::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__sp;
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachARM::GetGPRState(bool force)
+{
+ int set = e_regSetGPR;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_THREAD_STATE_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_THREAD_STATE, (thread_state_t)&m_state.context.gpr, &count);
+ uint32_t *r = &m_state.context.gpr.__r[0];
+ DNBLogThreadedIf(LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count = %u) regs r0=%8.8x r1=%8.8x r2=%8.8x r3=%8.8x r4=%8.8x r5=%8.8x r6=%8.8x r7=%8.8x r8=%8.8x r9=%8.8x r10=%8.8x r11=%8.8x s12=%8.8x sp=%8.8x lr=%8.8x pc=%8.8x cpsr=%8.8x",
+ m_thread->MachPortNumber(),
+ ARM_THREAD_STATE,
+ ARM_THREAD_STATE_COUNT,
+ kret,
+ count,
+ r[0],
+ r[1],
+ r[2],
+ r[3],
+ r[4],
+ r[5],
+ r[6],
+ r[7],
+ r[8],
+ r[9],
+ r[10],
+ r[11],
+ r[12],
+ r[13],
+ r[14],
+ r[15],
+ r[16]);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM::GetVFPState(bool force)
+{
+ int set = e_regSetVFP;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ kern_return_t kret;
+
+#if defined (__arm64__) || defined (__aarch64__)
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_NEON_STATE_COUNT;
+ kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_NEON_STATE, (thread_state_t)&m_state.context.vfp, &count);
+ if (DNBLogEnabledForAny (LOG_THREAD))
+ {
+ DNBLogThreaded("thread_get_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs"
+ "\n q0 = 0x%16.16llx%16.16llx"
+ "\n q1 = 0x%16.16llx%16.16llx"
+ "\n q2 = 0x%16.16llx%16.16llx"
+ "\n q3 = 0x%16.16llx%16.16llx"
+ "\n q4 = 0x%16.16llx%16.16llx"
+ "\n q5 = 0x%16.16llx%16.16llx"
+ "\n q6 = 0x%16.16llx%16.16llx"
+ "\n q7 = 0x%16.16llx%16.16llx"
+ "\n q8 = 0x%16.16llx%16.16llx"
+ "\n q9 = 0x%16.16llx%16.16llx"
+ "\n q10 = 0x%16.16llx%16.16llx"
+ "\n q11 = 0x%16.16llx%16.16llx"
+ "\n q12 = 0x%16.16llx%16.16llx"
+ "\n q13 = 0x%16.16llx%16.16llx"
+ "\n q14 = 0x%16.16llx%16.16llx"
+ "\n q15 = 0x%16.16llx%16.16llx"
+ "\n fpsr = 0x%8.8x"
+ "\n fpcr = 0x%8.8x\n\n",
+ m_thread->MachPortNumber(),
+ ARM_NEON_STATE,
+ ARM_NEON_STATE_COUNT,
+ kret,
+ count,
+ ((uint64_t *)&m_state.context.vfp.__v[0])[0] , ((uint64_t *)&m_state.context.vfp.__v[0])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[0] , ((uint64_t *)&m_state.context.vfp.__v[1])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[0] , ((uint64_t *)&m_state.context.vfp.__v[2])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[0] , ((uint64_t *)&m_state.context.vfp.__v[3])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[0] , ((uint64_t *)&m_state.context.vfp.__v[4])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[0] , ((uint64_t *)&m_state.context.vfp.__v[5])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[0] , ((uint64_t *)&m_state.context.vfp.__v[6])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[0] , ((uint64_t *)&m_state.context.vfp.__v[7])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[0] , ((uint64_t *)&m_state.context.vfp.__v[8])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[0] , ((uint64_t *)&m_state.context.vfp.__v[9])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[0], ((uint64_t *)&m_state.context.vfp.__v[10])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[0], ((uint64_t *)&m_state.context.vfp.__v[11])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[0], ((uint64_t *)&m_state.context.vfp.__v[12])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[0], ((uint64_t *)&m_state.context.vfp.__v[13])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[0], ((uint64_t *)&m_state.context.vfp.__v[14])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[0], ((uint64_t *)&m_state.context.vfp.__v[15])[1],
+ m_state.context.vfp.__fpsr,
+ m_state.context.vfp.__fpcr);
+
+ }
+#else
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_VFP_STATE_COUNT;
+ kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_VFP_STATE, (thread_state_t)&m_state.context.vfp, &count);
+
+ if (DNBLogEnabledForAny (LOG_THREAD))
+ {
+ uint32_t *r = &m_state.context.vfp.__r[0];
+ DNBLogThreaded ("thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count => %u)",
+ m_thread->MachPortNumber(),
+ ARM_THREAD_STATE,
+ ARM_THREAD_STATE_COUNT,
+ kret,
+ count);
+ DNBLogThreaded(" s0=%8.8x s1=%8.8x s2=%8.8x s3=%8.8x s4=%8.8x s5=%8.8x s6=%8.8x s7=%8.8x",r[ 0],r[ 1],r[ 2],r[ 3],r[ 4],r[ 5],r[ 6],r[ 7]);
+ DNBLogThreaded(" s8=%8.8x s9=%8.8x s10=%8.8x s11=%8.8x s12=%8.8x s13=%8.8x s14=%8.8x s15=%8.8x",r[ 8],r[ 9],r[10],r[11],r[12],r[13],r[14],r[15]);
+ DNBLogThreaded(" s16=%8.8x s17=%8.8x s18=%8.8x s19=%8.8x s20=%8.8x s21=%8.8x s22=%8.8x s23=%8.8x",r[16],r[17],r[18],r[19],r[20],r[21],r[22],r[23]);
+ DNBLogThreaded(" s24=%8.8x s25=%8.8x s26=%8.8x s27=%8.8x s28=%8.8x s29=%8.8x s30=%8.8x s31=%8.8x",r[24],r[25],r[26],r[27],r[28],r[29],r[30],r[31]);
+ DNBLogThreaded(" s32=%8.8x s33=%8.8x s34=%8.8x s35=%8.8x s36=%8.8x s37=%8.8x s38=%8.8x s39=%8.8x",r[32],r[33],r[34],r[35],r[36],r[37],r[38],r[39]);
+ DNBLogThreaded(" s40=%8.8x s41=%8.8x s42=%8.8x s43=%8.8x s44=%8.8x s45=%8.8x s46=%8.8x s47=%8.8x",r[40],r[41],r[42],r[43],r[44],r[45],r[46],r[47]);
+ DNBLogThreaded(" s48=%8.8x s49=%8.8x s50=%8.8x s51=%8.8x s52=%8.8x s53=%8.8x s54=%8.8x s55=%8.8x",r[48],r[49],r[50],r[51],r[52],r[53],r[54],r[55]);
+ DNBLogThreaded(" s56=%8.8x s57=%8.8x s58=%8.8x s59=%8.8x s60=%8.8x s61=%8.8x s62=%8.8x s63=%8.8x fpscr=%8.8x",r[56],r[57],r[58],r[59],r[60],r[61],r[62],r[63],r[64]);
+ }
+
+#endif
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM::GetEXCState(bool force)
+{
+ int set = e_regSetEXC;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_EXCEPTION_STATE_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, &count);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+static void
+DumpDBGState(const DNBArchMachARM::DBG& dbg)
+{
+ uint32_t i = 0;
+ for (i=0; i<16; i++)
+ {
+ DNBLogThreadedIf(LOG_STEP, "BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }",
+ i, i, dbg.__bvr[i], dbg.__bcr[i],
+ i, i, dbg.__wvr[i], dbg.__wcr[i]);
+ }
+}
+
+kern_return_t
+DNBArchMachARM::GetDBGState(bool force)
+{
+ int set = e_regSetDBG;
+
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+#if defined (ARM_DEBUG_STATE32) && (defined (__arm64__) || defined (__aarch64__))
+ mach_msg_type_number_t count = ARM_DEBUG_STATE32_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE32, (thread_state_t)&m_state.dbg, &count);
+#else
+ mach_msg_type_number_t count = ARM_DEBUG_STATE_COUNT;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, &count);
+#endif
+ m_state.SetError(set, Read, kret);
+
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM::SetGPRState()
+{
+ int set = e_regSetGPR;
+ kern_return_t kret = ::thread_set_state(m_thread->MachPortNumber(), ARM_THREAD_STATE, (thread_state_t)&m_state.context.gpr, ARM_THREAD_STATE_COUNT);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM::SetVFPState()
+{
+ int set = e_regSetVFP;
+ kern_return_t kret;
+ mach_msg_type_number_t count;
+
+#if defined (__arm64__) || defined (__aarch64__)
+ count = ARM_NEON_STATE_COUNT;
+ kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_NEON_STATE, (thread_state_t)&m_state.context.vfp, count);
+#else
+ count = ARM_VFP_STATE_COUNT;
+ kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_VFP_STATE, (thread_state_t)&m_state.context.vfp, count);
+#endif
+
+#if defined (__arm64__) || defined (__aarch64__)
+ if (DNBLogEnabledForAny (LOG_THREAD))
+ {
+ DNBLogThreaded("thread_set_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs"
+ "\n q0 = 0x%16.16llx%16.16llx"
+ "\n q1 = 0x%16.16llx%16.16llx"
+ "\n q2 = 0x%16.16llx%16.16llx"
+ "\n q3 = 0x%16.16llx%16.16llx"
+ "\n q4 = 0x%16.16llx%16.16llx"
+ "\n q5 = 0x%16.16llx%16.16llx"
+ "\n q6 = 0x%16.16llx%16.16llx"
+ "\n q7 = 0x%16.16llx%16.16llx"
+ "\n q8 = 0x%16.16llx%16.16llx"
+ "\n q9 = 0x%16.16llx%16.16llx"
+ "\n q10 = 0x%16.16llx%16.16llx"
+ "\n q11 = 0x%16.16llx%16.16llx"
+ "\n q12 = 0x%16.16llx%16.16llx"
+ "\n q13 = 0x%16.16llx%16.16llx"
+ "\n q14 = 0x%16.16llx%16.16llx"
+ "\n q15 = 0x%16.16llx%16.16llx"
+ "\n fpsr = 0x%8.8x"
+ "\n fpcr = 0x%8.8x\n\n",
+ m_thread->MachPortNumber(),
+ ARM_NEON_STATE,
+ ARM_NEON_STATE_COUNT,
+ kret,
+ count,
+ ((uint64_t *)&m_state.context.vfp.__v[0])[0] , ((uint64_t *)&m_state.context.vfp.__v[0])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[0] , ((uint64_t *)&m_state.context.vfp.__v[1])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[0] , ((uint64_t *)&m_state.context.vfp.__v[2])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[0] , ((uint64_t *)&m_state.context.vfp.__v[3])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[0] , ((uint64_t *)&m_state.context.vfp.__v[4])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[0] , ((uint64_t *)&m_state.context.vfp.__v[5])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[0] , ((uint64_t *)&m_state.context.vfp.__v[6])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[0] , ((uint64_t *)&m_state.context.vfp.__v[7])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[0] , ((uint64_t *)&m_state.context.vfp.__v[8])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[0] , ((uint64_t *)&m_state.context.vfp.__v[9])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[0], ((uint64_t *)&m_state.context.vfp.__v[10])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[0], ((uint64_t *)&m_state.context.vfp.__v[11])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[0], ((uint64_t *)&m_state.context.vfp.__v[12])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[0], ((uint64_t *)&m_state.context.vfp.__v[13])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[0], ((uint64_t *)&m_state.context.vfp.__v[14])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[0], ((uint64_t *)&m_state.context.vfp.__v[15])[1],
+ m_state.context.vfp.__fpsr,
+ m_state.context.vfp.__fpcr);
+ }
+#else
+ if (DNBLogEnabledForAny (LOG_THREAD))
+ {
+ uint32_t *r = &m_state.context.vfp.__r[0];
+ DNBLogThreaded ("thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count => %u)",
+ m_thread->MachPortNumber(),
+ ARM_THREAD_STATE,
+ ARM_THREAD_STATE_COUNT,
+ kret,
+ count);
+ DNBLogThreaded(" s0=%8.8x s1=%8.8x s2=%8.8x s3=%8.8x s4=%8.8x s5=%8.8x s6=%8.8x s7=%8.8x",r[ 0],r[ 1],r[ 2],r[ 3],r[ 4],r[ 5],r[ 6],r[ 7]);
+ DNBLogThreaded(" s8=%8.8x s9=%8.8x s10=%8.8x s11=%8.8x s12=%8.8x s13=%8.8x s14=%8.8x s15=%8.8x",r[ 8],r[ 9],r[10],r[11],r[12],r[13],r[14],r[15]);
+ DNBLogThreaded(" s16=%8.8x s17=%8.8x s18=%8.8x s19=%8.8x s20=%8.8x s21=%8.8x s22=%8.8x s23=%8.8x",r[16],r[17],r[18],r[19],r[20],r[21],r[22],r[23]);
+ DNBLogThreaded(" s24=%8.8x s25=%8.8x s26=%8.8x s27=%8.8x s28=%8.8x s29=%8.8x s30=%8.8x s31=%8.8x",r[24],r[25],r[26],r[27],r[28],r[29],r[30],r[31]);
+ DNBLogThreaded(" s32=%8.8x s33=%8.8x s34=%8.8x s35=%8.8x s36=%8.8x s37=%8.8x s38=%8.8x s39=%8.8x",r[32],r[33],r[34],r[35],r[36],r[37],r[38],r[39]);
+ DNBLogThreaded(" s40=%8.8x s41=%8.8x s42=%8.8x s43=%8.8x s44=%8.8x s45=%8.8x s46=%8.8x s47=%8.8x",r[40],r[41],r[42],r[43],r[44],r[45],r[46],r[47]);
+ DNBLogThreaded(" s48=%8.8x s49=%8.8x s50=%8.8x s51=%8.8x s52=%8.8x s53=%8.8x s54=%8.8x s55=%8.8x",r[48],r[49],r[50],r[51],r[52],r[53],r[54],r[55]);
+ DNBLogThreaded(" s56=%8.8x s57=%8.8x s58=%8.8x s59=%8.8x s60=%8.8x s61=%8.8x s62=%8.8x s63=%8.8x fpscr=%8.8x",r[56],r[57],r[58],r[59],r[60],r[61],r[62],r[63],r[64]);
+ }
+#endif
+
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM::SetEXCState()
+{
+ int set = e_regSetEXC;
+ kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, ARM_EXCEPTION_STATE_COUNT);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM::SetDBGState(bool also_set_on_task)
+{
+ int set = e_regSetDBG;
+#if defined (ARM_DEBUG_STATE32) && (defined (__arm64__) || defined (__aarch64__))
+ kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_DEBUG_STATE32, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE32_COUNT);
+ if (also_set_on_task)
+ {
+ kern_return_t task_kret = ::task_set_state (m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE32, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE32_COUNT);
+ if (task_kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::SetDBGState failed to set debug control register state: 0x%8.8x.", kret);
+ }
+#else
+ kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT);
+ if (also_set_on_task)
+ {
+ kern_return_t task_kret = ::task_set_state (m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT);
+ if (task_kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::SetDBGState failed to set debug control register state: 0x%8.8x.", kret);
+ }
+#endif
+
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+void
+DNBArchMachARM::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ // This is the primary thread, let the arch do anything it needs
+ if (NumSupportedHardwareBreakpoints() > 0)
+ {
+ if (EnableHardwareSingleStep(true) != KERN_SUCCESS)
+ {
+ DNBLogThreaded("DNBArchMachARM::ThreadWillResume() failed to enable hardware single step");
+ }
+ }
+ }
+
+ // Disable the triggered watchpoint temporarily before we resume.
+ // Plus, we try to enable hardware single step to execute past the instruction which triggered our watchpoint.
+ if (m_watchpoint_did_occur)
+ {
+ if (m_watchpoint_hw_index >= 0)
+ {
+ kern_return_t kret = GetDBGState(false);
+ if (kret == KERN_SUCCESS && !IsWatchpointEnabled(m_state.dbg, m_watchpoint_hw_index)) {
+ // The watchpoint might have been disabled by the user. We don't need to do anything at all
+ // to enable hardware single stepping.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ return;
+ }
+
+ DisableHardwareWatchpoint(m_watchpoint_hw_index, false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() DisableHardwareWatchpoint(%d) called",
+ m_watchpoint_hw_index);
+
+ // Enable hardware single step to move past the watchpoint-triggering instruction.
+ m_watchpoint_resume_single_step_enabled = (EnableHardwareSingleStep(true) == KERN_SUCCESS);
+
+ // If we are not able to enable single step to move past the watchpoint-triggering instruction,
+ // at least we should reset the two watchpoint member variables so that the next time around
+ // this callback function is invoked, the enclosing logical branch is skipped.
+ if (!m_watchpoint_resume_single_step_enabled) {
+ // Reset the two watchpoint member variables.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() failed to enable single step");
+ }
+ else
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() succeeded to enable single step");
+ }
+ }
+}
+
+bool
+DNBArchMachARM::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateRegisterSetState (e_regSetALL);
+
+ if (m_watchpoint_resume_single_step_enabled)
+ {
+ // Great! We now disable the hardware single step as well as re-enable the hardware watchpoint.
+ // See also ThreadWillResume().
+ if (EnableHardwareSingleStep(false) == KERN_SUCCESS)
+ {
+ if (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0)
+ {
+ ReenableHardwareWatchpoint(m_watchpoint_hw_index);
+ m_watchpoint_resume_single_step_enabled = false;
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ }
+ else
+ {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) does not hold!");
+ }
+ }
+ else
+ {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but unable to disable single step!");
+ }
+ }
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool
+DNBArchMachARM::NotifyException(MachException::Data& exc)
+{
+ switch (exc.exc_type)
+ {
+ default:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() == 2 && exc.exc_data[0] == EXC_ARM_DA_DEBUG)
+ {
+ // The data break address is passed as exc_data[1].
+ nub_addr_t addr = exc.exc_data[1];
+ // Find the hardware index with the side effect of possibly massaging the
+ // addr to return the starting address as seen from the debugger side.
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException watchpoint %d was hit on address 0x%llx", hw_index, (uint64_t) addr);
+ const int num_watchpoints = NumSupportedHardwareWatchpoints ();
+ for (int i = 0; i < num_watchpoints; i++)
+ {
+ if (LoHi[i] != 0
+ && LoHi[i] == hw_index
+ && LoHi[i] != i
+ && GetWatchpointAddressByIndex (i) != INVALID_NUB_ADDRESS)
+ {
+ addr = GetWatchpointAddressByIndex (i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException It is a linked watchpoint; rewritten to index %d addr 0x%llx", LoHi[i], (uint64_t) addr);
+ }
+ }
+ if (hw_index != INVALID_NUB_HW_INDEX)
+ {
+ m_watchpoint_did_occur = true;
+ m_watchpoint_hw_index = hw_index;
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+bool
+DNBArchMachARM::StepNotComplete ()
+{
+ if (m_hw_single_chained_step_addr != INVALID_NUB_ADDRESS)
+ {
+ kern_return_t kret = KERN_INVALID_ARGUMENT;
+ kret = GetGPRState(false);
+ if (kret == KERN_SUCCESS)
+ {
+ if (m_state.context.gpr.__pc == m_hw_single_chained_step_addr)
+ {
+ DNBLogThreadedIf(LOG_STEP, "Need to step some more at 0x%8.8llx", (uint64_t) m_hw_single_chained_step_addr);
+ return true;
+ }
+ }
+ }
+
+ m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS;
+ return false;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchMachARM::EnableHardwareSingleStep (bool enable)
+{
+ DNBError err;
+ DNBLogThreadedIf(LOG_STEP, "%s( enable = %d )", __FUNCTION__, enable);
+
+ err = GetGPRState(false);
+
+ if (err.Fail())
+ {
+ err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__);
+ return err.Error();
+ }
+
+ err = GetDBGState(false);
+
+ if (err.Fail())
+ {
+ err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__);
+ return err.Error();
+ }
+
+// The use of __arm64__ here is not ideal. If debugserver is running on
+// an armv8 device, regardless of whether it was built for arch arm or arch arm64,
+// it needs to use the MDSCR_EL1 SS bit to single instruction step.
+
+#if defined (__arm64__) || defined (__aarch64__)
+ if (enable)
+ {
+ DNBLogThreadedIf(LOG_STEP, "%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc);
+ m_state.dbg.__mdscr_el1 |= 1; // Set bit 0 (single step, SS) in the MDSCR_EL1.
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_STEP, "%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc);
+ m_state.dbg.__mdscr_el1 &= ~(1ULL); // Clear bit 0 (single step, SS) in the MDSCR_EL1.
+ }
+#else
+ const uint32_t i = 0;
+ if (enable)
+ {
+ m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS;
+
+ // Save our previous state
+ m_dbg_save = m_state.dbg;
+ // Set a breakpoint that will stop when the PC doesn't match the current one!
+ m_state.dbg.__bvr[i] = m_state.context.gpr.__pc & 0xFFFFFFFCu; // Set the current PC as the breakpoint address
+ m_state.dbg.__bcr[i] = BCR_M_IMVA_MISMATCH | // Stop on address mismatch
+ S_USER | // Stop only in user mode
+ BCR_ENABLE; // Enable this breakpoint
+ if (m_state.context.gpr.__cpsr & 0x20)
+ {
+ // Thumb breakpoint
+ if (m_state.context.gpr.__pc & 2)
+ m_state.dbg.__bcr[i] |= BAS_IMVA_2_3;
+ else
+ m_state.dbg.__bcr[i] |= BAS_IMVA_0_1;
+
+ uint16_t opcode;
+ if (sizeof(opcode) == m_thread->Process()->Task().ReadMemory(m_state.context.gpr.__pc, sizeof(opcode), &opcode))
+ {
+ if (IsThumb32Opcode(opcode))
+ {
+ // 32 bit thumb opcode...
+ if (m_state.context.gpr.__pc & 2)
+ {
+ // We can't take care of a 32 bit thumb instruction single step
+ // with just IVA mismatching. We will need to chain an extra
+ // hardware single step in order to complete this single step...
+ m_hw_single_chained_step_addr = m_state.context.gpr.__pc + 2;
+ }
+ else
+ {
+ // Extend the number of bits to ignore for the mismatch
+ m_state.dbg.__bcr[i] |= BAS_IMVA_ALL;
+ }
+ }
+ }
+ }
+ else
+ {
+ // ARM breakpoint
+ m_state.dbg.__bcr[i] |= BAS_IMVA_ALL; // Stop when any address bits change
+ }
+
+ DNBLogThreadedIf(LOG_STEP, "%s: BVR%u=0x%8.8x BCR%u=0x%8.8x", __FUNCTION__, i, m_state.dbg.__bvr[i], i, m_state.dbg.__bcr[i]);
+
+ for (uint32_t j=i+1; j<16; ++j)
+ {
+ // Disable all others
+ m_state.dbg.__bvr[j] = 0;
+ m_state.dbg.__bcr[j] = 0;
+ }
+ }
+ else
+ {
+ // Just restore the state we had before we did single stepping
+ m_state.dbg = m_dbg_save;
+ }
+#endif
+
+ return SetDBGState(false);
+}
+
+// return 1 if bit "BIT" is set in "value"
+static inline uint32_t bit(uint32_t value, uint32_t bit)
+{
+ return (value >> bit) & 1u;
+}
+
+// return the bitfield "value[msbit:lsbit]".
+static inline uint32_t bits(uint32_t value, uint32_t msbit, uint32_t lsbit)
+{
+ assert(msbit >= lsbit);
+ uint32_t shift_left = sizeof(value) * 8 - 1 - msbit;
+ value <<= shift_left; // shift anything above the msbit off of the unsigned edge
+ value >>= (shift_left + lsbit); // shift it back again down to the lsbit (including undoing any shift from above)
+ return value; // return our result
+}
+
+bool
+DNBArchMachARM::ConditionPassed(uint8_t condition, uint32_t cpsr)
+{
+ uint32_t cpsr_n = bit(cpsr, 31); // Negative condition code flag
+ uint32_t cpsr_z = bit(cpsr, 30); // Zero condition code flag
+ uint32_t cpsr_c = bit(cpsr, 29); // Carry condition code flag
+ uint32_t cpsr_v = bit(cpsr, 28); // Overflow condition code flag
+
+ switch (condition) {
+ case COND_EQ: // (0x0)
+ if (cpsr_z == 1) return true;
+ break;
+ case COND_NE: // (0x1)
+ if (cpsr_z == 0) return true;
+ break;
+ case COND_CS: // (0x2)
+ if (cpsr_c == 1) return true;
+ break;
+ case COND_CC: // (0x3)
+ if (cpsr_c == 0) return true;
+ break;
+ case COND_MI: // (0x4)
+ if (cpsr_n == 1) return true;
+ break;
+ case COND_PL: // (0x5)
+ if (cpsr_n == 0) return true;
+ break;
+ case COND_VS: // (0x6)
+ if (cpsr_v == 1) return true;
+ break;
+ case COND_VC: // (0x7)
+ if (cpsr_v == 0) return true;
+ break;
+ case COND_HI: // (0x8)
+ if ((cpsr_c == 1) && (cpsr_z == 0)) return true;
+ break;
+ case COND_LS: // (0x9)
+ if ((cpsr_c == 0) || (cpsr_z == 1)) return true;
+ break;
+ case COND_GE: // (0xA)
+ if (cpsr_n == cpsr_v) return true;
+ break;
+ case COND_LT: // (0xB)
+ if (cpsr_n != cpsr_v) return true;
+ break;
+ case COND_GT: // (0xC)
+ if ((cpsr_z == 0) && (cpsr_n == cpsr_v)) return true;
+ break;
+ case COND_LE: // (0xD)
+ if ((cpsr_z == 1) || (cpsr_n != cpsr_v)) return true;
+ break;
+ default:
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+uint32_t
+DNBArchMachARM::NumSupportedHardwareBreakpoints()
+{
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many breakpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_breakpoints = UINT_MAX;
+ if (g_num_supported_hw_breakpoints == UINT_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_breakpoints = 0;
+
+ size_t len;
+ uint32_t n = 0;
+ len = sizeof (n);
+ if (::sysctlbyname("hw.optional.breakpoint", &n, &len, NULL, 0) == 0)
+ {
+ g_num_supported_hw_breakpoints = n;
+ DNBLogThreadedIf(LOG_THREAD, "hw.optional.breakpoint=%u", n);
+ }
+ else
+ {
+#if !defined (__arm64__) && !defined (__aarch64__)
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many BRPs
+ // are available to us directly without having to read DBGDIDR.
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ uint32_t numBRPs = bits(register_DBGDIDR, 27, 24);
+ // Zero is reserved for the BRP count, so don't increment it if it is zero
+ if (numBRPs > 0)
+ numBRPs++;
+ DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, numBRPs);
+
+ if (numBRPs > 0)
+ {
+ uint32_t cpusubtype;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
+ {
+ DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=%d", cpusubtype);
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ DNBLogThreadedIf(LOG_THREAD, "Hardware breakpoints disabled for armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_breakpoints = numBRPs;
+ }
+ }
+#endif
+ }
+ }
+ return g_num_supported_hw_breakpoints;
+}
+
+
+uint32_t
+DNBArchMachARM::NumSupportedHardwareWatchpoints()
+{
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT_MAX;
+ if (g_num_supported_hw_watchpoints == UINT_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+
+
+ size_t len;
+ uint32_t n = 0;
+ len = sizeof (n);
+ if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0)
+ {
+ g_num_supported_hw_watchpoints = n;
+ DNBLogThreadedIf(LOG_THREAD, "hw.optional.watchpoint=%u", n);
+ }
+ else
+ {
+#if !defined (__arm64__) && !defined (__aarch64__)
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many WRPs
+ // are available to us directly without having to read DBGDIDR.
+
+ uint32_t register_DBGDIDR;
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ uint32_t numWRPs = bits(register_DBGDIDR, 31, 28) + 1;
+ DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, numWRPs);
+
+ if (numWRPs > 0)
+ {
+ uint32_t cpusubtype;
+ size_t len;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
+ {
+ DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype);
+
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ DNBLogThreadedIf(LOG_THREAD, "Hardware watchpoints disabled for armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_watchpoints = numWRPs;
+ }
+ }
+#endif
+ }
+ }
+ return g_num_supported_hw_watchpoints;
+}
+
+
+uint32_t
+DNBArchMachARM::EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size)
+{
+ // Make sure our address isn't bogus
+ if (addr & 1)
+ return INVALID_NUB_HW_INDEX;
+
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ uint32_t i;
+ for (i=0; i<num_hw_breakpoints; ++i)
+ {
+ if ((m_state.dbg.__bcr[i] & BCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_breakpoints)
+ {
+ // Make sure bits 1:0 are clear in our address
+ m_state.dbg.__bvr[i] = addr & ~((nub_addr_t)3);
+
+ if (size == 2 || addr & 2)
+ {
+ uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1;
+
+ // We have a thumb breakpoint
+ // We have an ARM breakpoint
+ m_state.dbg.__bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch
+ byte_addr_select | // Set the correct byte address select so we only trigger on the correct opcode
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ DNBLogThreadedIf (LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint( addr = 0x%8.8llx, size = %llu ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (Thumb)",
+ (uint64_t)addr,
+ (uint64_t)size,
+ i,
+ i,
+ m_state.dbg.__bvr[i],
+ m_state.dbg.__bcr[i]);
+ }
+ else if (size == 4)
+ {
+ // We have an ARM breakpoint
+ m_state.dbg.__bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch
+ BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ DNBLogThreadedIf (LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint( addr = 0x%8.8llx, size = %llu ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (ARM)",
+ (uint64_t)addr,
+ (uint64_t)size,
+ i,
+ i,
+ m_state.dbg.__bvr[i],
+ m_state.dbg.__bcr[i]);
+ }
+
+ kret = SetDBGState(false);
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint(addr = 0x%8.8llx, size = %llu) => all hardware breakpoint resources are being used.", (uint64_t)addr, (uint64_t)size);
+ }
+ }
+
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+DNBArchMachARM::DisableHardwareBreakpoint (uint32_t hw_index)
+{
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ if (hw_index < num_hw_points)
+ {
+ m_state.dbg.__bcr[hw_index] = 0;
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x",
+ hw_index,
+ hw_index,
+ m_state.dbg.__bvr[hw_index],
+ hw_index,
+ m_state.dbg.__bcr[hw_index]);
+
+ kret = SetDBGState(false);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+// ARM v7 watchpoints may be either word-size or double-word-size.
+// It's implementation defined which they can handle. It looks like on an
+// armv8 device, armv7 processes can watch dwords. But on a genuine armv7
+// device I tried, only word watchpoints are supported.
+
+#if defined (__arm64__) || defined (__aarch64__)
+#define WATCHPOINTS_ARE_DWORD 1
+#else
+#undef WATCHPOINTS_ARE_DWORD
+#endif
+
+uint32_t
+DNBArchMachARM::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task)
+{
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu, read = %u, write = %u)", (uint64_t)addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return INVALID_NUB_HW_INDEX;
+
+ // Otherwise, can't watch more than 8 bytes per WVR/WCR pair
+ if (size > 8)
+ return INVALID_NUB_HW_INDEX;
+
+ // Treat arm watchpoints as having an 8-byte alignment requirement. You can put a watchpoint on a 4-byte
+ // offset address but you can only watch 4 bytes with that watchpoint.
+
+ // arm watchpoints on an 8-byte (double word) aligned addr can watch any bytes in that
+ // 8-byte long region of memory. They can watch the 1st byte, the 2nd byte, 3rd byte, etc, or any
+ // combination therein by setting the bits in the BAS [12:5] (Byte Address Select) field of
+ // the DBGWCRn_EL1 reg for the watchpoint.
+
+ // If the MASK [28:24] bits in the DBGWCRn_EL1 allow a single watchpoint to monitor a larger region
+ // of memory (16 bytes, 32 bytes, or 2GB) but the Byte Address Select bitfield then selects a larger
+ // range of bytes, instead of individual bytes. See the ARMv8 Debug Architecture manual for details.
+ // This implementation does not currently use the MASK bits; the largest single region watched by a single
+ // watchpoint right now is 8-bytes.
+
+#if defined (WATCHPOINTS_ARE_DWORD)
+ nub_addr_t aligned_wp_address = addr & ~0x7;
+ uint32_t addr_dword_offset = addr & 0x7;
+ const int max_watchpoint_size = 8;
+#else
+ nub_addr_t aligned_wp_address = addr & ~0x3;
+ uint32_t addr_dword_offset = addr & 0x3;
+ const int max_watchpoint_size = 4;
+#endif
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint aligned_wp_address is 0x%llx and addr_dword_offset is 0x%x", (uint64_t)aligned_wp_address, addr_dword_offset);
+
+ // Do we need to split up this logical watchpoint into two hardware watchpoint
+ // registers?
+ // e.g. a watchpoint of length 4 on address 6. We need do this with
+ // one watchpoint on address 0 with bytes 6 & 7 being monitored
+ // one watchpoint on address 8 with bytes 0, 1, 2, 3 being monitored
+
+ if (addr_dword_offset + size > max_watchpoint_size)
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu) needs two hardware watchpoints slots to monitor", (uint64_t)addr, size);
+ int low_watchpoint_size = max_watchpoint_size - addr_dword_offset;
+ int high_watchpoint_size = addr_dword_offset + size - max_watchpoint_size;
+
+ uint32_t lo = EnableHardwareWatchpoint(addr, low_watchpoint_size, read, write, also_set_on_task);
+ if (lo == INVALID_NUB_HW_INDEX)
+ return INVALID_NUB_HW_INDEX;
+ uint32_t hi = EnableHardwareWatchpoint (aligned_wp_address + max_watchpoint_size, high_watchpoint_size, read, write, also_set_on_task);
+ if (hi == INVALID_NUB_HW_INDEX)
+ {
+ DisableHardwareWatchpoint (lo, also_set_on_task);
+ return INVALID_NUB_HW_INDEX;
+ }
+ // Tag this lo->hi mapping in our database.
+ LoHi[lo] = hi;
+ return lo;
+ }
+
+ // At this point
+ // 1 aligned_wp_address is the requested address rounded down to 8-byte alignment
+ // 2 addr_dword_offset is the offset into that double word (8-byte) region that we are watching
+ // 3 size is the number of bytes within that 8-byte region that we are watching
+
+ // Set the Byte Address Selects bits DBGWCRn_EL1 bits [12:5] based on the above.
+ // The bit shift and negation operation will give us 0b11 for 2, 0b1111 for 4, etc, up to 0b11111111 for 8.
+ // then we shift those bits left by the offset into this dword that we are interested in.
+ // e.g. if we are watching bytes 4,5,6,7 in a dword we want a BAS of 0b11110000.
+ uint32_t byte_address_select = ((1 << size) - 1) << addr_dword_offset;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+
+ if (kret == KERN_SUCCESS)
+ {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i=0; i<num_hw_watchpoints; ++i)
+ {
+ if ((m_state.dbg.__wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw watchpoint slot (in i)
+ }
+
+ // See if we found an available hw watchpoint slot above
+ if (i < num_hw_watchpoints)
+ {
+ //DumpDBGState(m_state.dbg);
+
+ // Clear any previous LoHi joined-watchpoint that may have been in use
+ LoHi[i] = 0;
+
+ // shift our Byte Address Select bits up to the correct bit range for the DBGWCRn_EL1
+ byte_address_select = byte_address_select << 5;
+
+ // Make sure bits 1:0 are clear in our address
+ m_state.dbg.__wvr[i] = aligned_wp_address; // DVA (Data Virtual Address)
+ m_state.dbg.__wcr[i] = byte_address_select | // Which bytes that follow the DVA that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() adding watchpoint on address 0x%llx with control register value 0x%x", (uint64_t) m_state.dbg.__wvr[i], (uint32_t) m_state.dbg.__wcr[i]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us automatically, don't need to do it here.
+
+ kret = SetDBGState(also_set_on_task);
+ //DumpDBGState(m_state.dbg);
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+DNBArchMachARM::ReenableHardwareWatchpoint (uint32_t hw_index)
+{
+ // If this logical watchpoint # is actually implemented using
+ // two hardware watchpoint registers, re-enable both of them.
+
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index])
+ {
+ return ReenableHardwareWatchpoint_helper (hw_index) && ReenableHardwareWatchpoint_helper (LoHi[hw_index]);
+ }
+ else
+ {
+ return ReenableHardwareWatchpoint_helper (hw_index);
+ }
+}
+
+bool
+DNBArchMachARM::ReenableHardwareWatchpoint_helper (uint32_t hw_index)
+{
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_state.dbg.__wvr[hw_index] = m_disabled_watchpoints[hw_index].addr;
+ m_state.dbg.__wcr[hw_index] = m_disabled_watchpoints[hw_index].control;
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx",
+ hw_index,
+ hw_index,
+ (uint64_t) m_state.dbg.__wvr[hw_index],
+ hw_index,
+ (uint64_t) m_state.dbg.__wcr[hw_index]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us automatically, don't need to do it here.
+
+ kret = SetDBGState(false);
+
+ return (kret == KERN_SUCCESS);
+}
+
+bool
+DNBArchMachARM::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task)
+{
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index])
+ {
+ return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task) && DisableHardwareWatchpoint_helper (LoHi[hw_index], also_set_on_task);
+ }
+ else
+ {
+ return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task);
+ }
+}
+
+bool
+DNBArchMachARM::DisableHardwareWatchpoint_helper (uint32_t hw_index, bool also_set_on_task)
+{
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_disabled_watchpoints[hw_index].addr = m_state.dbg.__wvr[hw_index];
+ m_disabled_watchpoints[hw_index].control = m_state.dbg.__wcr[hw_index];
+
+ m_state.dbg.__wvr[hw_index] = 0;
+ m_state.dbg.__wcr[hw_index] = 0;
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::DisableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx",
+ hw_index,
+ hw_index,
+ (uint64_t) m_state.dbg.__wvr[hw_index],
+ hw_index,
+ (uint64_t) m_state.dbg.__wcr[hw_index]);
+
+ kret = SetDBGState(also_set_on_task);
+
+ return (kret == KERN_SUCCESS);
+}
+
+// Returns -1 if the trailing bit patterns are not one of:
+// { 0b???1, 0b??10, 0b?100, 0b1000 }.
+static inline
+int32_t
+LowestBitSet(uint32_t val)
+{
+ for (unsigned i = 0; i < 4; ++i) {
+ if (bit(val, i))
+ return i;
+ }
+ return -1;
+}
+
+// Iterate through the debug registers; return the index of the first watchpoint whose address matches.
+// As a side effect, the starting address as understood by the debugger is returned which could be
+// different from 'addr' passed as an in/out argument.
+uint32_t
+DNBArchMachARM::GetHardwareWatchpointHit(nub_addr_t &addr)
+{
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ //DumpDBGState(m_state.dbg);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::GetHardwareWatchpointHit() addr = 0x%llx", (uint64_t)addr);
+
+ // This is the watchpoint value to match against, i.e., word address.
+#if defined (WATCHPOINTS_ARE_DWORD)
+ nub_addr_t wp_val = addr & ~((nub_addr_t)7);
+#else
+ nub_addr_t wp_val = addr & ~((nub_addr_t)3);
+#endif
+ if (kret == KERN_SUCCESS)
+ {
+ DBG &debug_state = m_state.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ {
+ nub_addr_t wp_addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchMachARM::GetHardwareWatchpointHit() slot: %u (addr = 0x%llx).",
+ i, (uint64_t)wp_addr);
+ if (wp_val == wp_addr) {
+#if defined (WATCHPOINTS_ARE_DWORD)
+ uint32_t byte_mask = bits(debug_state.__wcr[i], 12, 5);
+#else
+ uint32_t byte_mask = bits(debug_state.__wcr[i], 8, 5);
+#endif
+
+ // Sanity check the byte_mask, first.
+ if (LowestBitSet(byte_mask) < 0)
+ continue;
+
+ // Compute the starting address (from the point of view of the debugger).
+ addr = wp_addr + LowestBitSet(byte_mask);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+nub_addr_t
+DNBArchMachARM::GetWatchpointAddressByIndex (uint32_t hw_index)
+{
+ kern_return_t kret = GetDBGState(true);
+ if (kret != KERN_SUCCESS)
+ return INVALID_NUB_ADDRESS;
+ const uint32_t num = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num)
+ return INVALID_NUB_ADDRESS;
+ if (IsWatchpointEnabled (m_state.dbg, hw_index))
+ return GetWatchAddress (m_state.dbg, hw_index);
+ return INVALID_NUB_ADDRESS;
+}
+
+bool
+DNBArchMachARM::IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index)
+{
+ // Watchpoint Control Registers, bitfield definitions
+ // ...
+ // Bits Value Description
+ // [0] 0 Watchpoint disabled
+ // 1 Watchpoint enabled.
+ return (debug_state.__wcr[hw_index] & 1u);
+}
+
+nub_addr_t
+DNBArchMachARM::GetWatchAddress(const DBG &debug_state, uint32_t hw_index)
+{
+ // Watchpoint Value Registers, bitfield definitions
+ // Bits Description
+ // [31:2] Watchpoint value (word address, i.e., 4-byte aligned)
+ // [1:0] RAZ/SBZP
+ return bits(debug_state.__wvr[hw_index], 31, 0);
+}
+
+//----------------------------------------------------------------------
+// Register information definitions for 32 bit ARMV7.
+//----------------------------------------------------------------------
+enum gpr_regnums
+{
+ gpr_r0 = 0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_sp,
+ gpr_lr,
+ gpr_pc,
+ gpr_cpsr
+};
+
+enum
+{
+ vfp_s0 = 0,
+ vfp_s1,
+ vfp_s2,
+ vfp_s3,
+ vfp_s4,
+ vfp_s5,
+ vfp_s6,
+ vfp_s7,
+ vfp_s8,
+ vfp_s9,
+ vfp_s10,
+ vfp_s11,
+ vfp_s12,
+ vfp_s13,
+ vfp_s14,
+ vfp_s15,
+ vfp_s16,
+ vfp_s17,
+ vfp_s18,
+ vfp_s19,
+ vfp_s20,
+ vfp_s21,
+ vfp_s22,
+ vfp_s23,
+ vfp_s24,
+ vfp_s25,
+ vfp_s26,
+ vfp_s27,
+ vfp_s28,
+ vfp_s29,
+ vfp_s30,
+ vfp_s31,
+ vfp_d0,
+ vfp_d1,
+ vfp_d2,
+ vfp_d3,
+ vfp_d4,
+ vfp_d5,
+ vfp_d6,
+ vfp_d7,
+ vfp_d8,
+ vfp_d9,
+ vfp_d10,
+ vfp_d11,
+ vfp_d12,
+ vfp_d13,
+ vfp_d14,
+ vfp_d15,
+ vfp_d16,
+ vfp_d17,
+ vfp_d18,
+ vfp_d19,
+ vfp_d20,
+ vfp_d21,
+ vfp_d22,
+ vfp_d23,
+ vfp_d24,
+ vfp_d25,
+ vfp_d26,
+ vfp_d27,
+ vfp_d28,
+ vfp_d29,
+ vfp_d30,
+ vfp_d31,
+ vfp_q0,
+ vfp_q1,
+ vfp_q2,
+ vfp_q3,
+ vfp_q4,
+ vfp_q5,
+ vfp_q6,
+ vfp_q7,
+ vfp_q8,
+ vfp_q9,
+ vfp_q10,
+ vfp_q11,
+ vfp_q12,
+ vfp_q13,
+ vfp_q14,
+ vfp_q15,
+#if defined (__arm64__) || defined (__aarch64__)
+ vfp_fpsr,
+ vfp_fpcr,
+#else
+ vfp_fpscr
+#endif
+};
+
+enum
+{
+ exc_exception,
+ exc_fsr,
+ exc_far,
+};
+
+#define GPR_OFFSET_IDX(idx) (offsetof (DNBArchMachARM::GPR, __r[idx]))
+#define GPR_OFFSET_NAME(reg) (offsetof (DNBArchMachARM::GPR, __##reg))
+
+#define EXC_OFFSET(reg) (offsetof (DNBArchMachARM::EXC, __##reg) + offsetof (DNBArchMachARM::Context, exc))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR_IDX(idx, reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 4, GPR_OFFSET_IDX(idx), ehframe_##reg, dwarf_##reg, gen, INVALID_NUB_REGNUM, NULL, NULL}
+#define DEFINE_GPR_NAME(reg, alt, gen, inval) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 4, GPR_OFFSET_NAME(reg), ehframe_##reg, dwarf_##reg, gen, INVALID_NUB_REGNUM, NULL, inval}
+
+// In case we are debugging to a debug target that the ability to
+// change into the protected modes with folded registers (ABT, IRQ,
+// FIQ, SYS, USR, etc..), we should invalidate r8-r14 if the CPSR
+// gets modified.
+
+const char * g_invalidate_cpsr[] = { "r8", "r9", "r10", "r11", "r12", "sp", "lr", NULL };
+
+// General purpose registers
+const DNBRegisterInfo
+DNBArchMachARM::g_gpr_registers[] =
+{
+ DEFINE_GPR_IDX ( 0, r0,"arg1", GENERIC_REGNUM_ARG1 ),
+ DEFINE_GPR_IDX ( 1, r1,"arg2", GENERIC_REGNUM_ARG2 ),
+ DEFINE_GPR_IDX ( 2, r2,"arg3", GENERIC_REGNUM_ARG3 ),
+ DEFINE_GPR_IDX ( 3, r3,"arg4", GENERIC_REGNUM_ARG4 ),
+ DEFINE_GPR_IDX ( 4, r4, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX ( 5, r5, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX ( 6, r6, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX ( 7, r7, "fp", GENERIC_REGNUM_FP ),
+ DEFINE_GPR_IDX ( 8, r8, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX ( 9, r9, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (10, r10, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (11, r11, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (12, r12, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_NAME (sp, "r13", GENERIC_REGNUM_SP, NULL),
+ DEFINE_GPR_NAME (lr, "r14", GENERIC_REGNUM_RA, NULL),
+ DEFINE_GPR_NAME (pc, "r15", GENERIC_REGNUM_PC, NULL),
+ DEFINE_GPR_NAME (cpsr, "flags", GENERIC_REGNUM_FLAGS, g_invalidate_cpsr)
+};
+
+const char *g_contained_q0 [] { "q0", NULL };
+const char *g_contained_q1 [] { "q1", NULL };
+const char *g_contained_q2 [] { "q2", NULL };
+const char *g_contained_q3 [] { "q3", NULL };
+const char *g_contained_q4 [] { "q4", NULL };
+const char *g_contained_q5 [] { "q5", NULL };
+const char *g_contained_q6 [] { "q6", NULL };
+const char *g_contained_q7 [] { "q7", NULL };
+const char *g_contained_q8 [] { "q8", NULL };
+const char *g_contained_q9 [] { "q9", NULL };
+const char *g_contained_q10[] { "q10", NULL };
+const char *g_contained_q11[] { "q11", NULL };
+const char *g_contained_q12[] { "q12", NULL };
+const char *g_contained_q13[] { "q13", NULL };
+const char *g_contained_q14[] { "q14", NULL };
+const char *g_contained_q15[] { "q15", NULL };
+
+const char *g_invalidate_q0[] { "q0", "d0" , "d1" , "s0" , "s1" , "s2" , "s3" , NULL };
+const char *g_invalidate_q1[] { "q1", "d2" , "d3" , "s4" , "s5" , "s6" , "s7" , NULL };
+const char *g_invalidate_q2[] { "q2", "d4" , "d5" , "s8" , "s9" , "s10", "s11", NULL };
+const char *g_invalidate_q3[] { "q3", "d6" , "d7" , "s12", "s13", "s14", "s15", NULL };
+const char *g_invalidate_q4[] { "q4", "d8" , "d9" , "s16", "s17", "s18", "s19", NULL };
+const char *g_invalidate_q5[] { "q5", "d10", "d11", "s20", "s21", "s22", "s23", NULL };
+const char *g_invalidate_q6[] { "q6", "d12", "d13", "s24", "s25", "s26", "s27", NULL };
+const char *g_invalidate_q7[] { "q7", "d14", "d15", "s28", "s29", "s30", "s31", NULL };
+const char *g_invalidate_q8[] { "q8", "d16", "d17", NULL };
+const char *g_invalidate_q9[] { "q9", "d18", "d19", NULL };
+const char *g_invalidate_q10[] { "q10", "d20", "d21", NULL };
+const char *g_invalidate_q11[] { "q11", "d22", "d23", NULL };
+const char *g_invalidate_q12[] { "q12", "d24", "d25", NULL };
+const char *g_invalidate_q13[] { "q13", "d26", "d27", NULL };
+const char *g_invalidate_q14[] { "q14", "d28", "d29", NULL };
+const char *g_invalidate_q15[] { "q15", "d30", "d31", NULL };
+
+#define VFP_S_OFFSET_IDX(idx) (((idx) % 4) * 4) // offset into q reg: 0, 4, 8, 12
+#define VFP_D_OFFSET_IDX(idx) (((idx) % 2) * 8) // offset into q reg: 0, 8
+#define VFP_Q_OFFSET_IDX(idx) (VFP_S_OFFSET_IDX ((idx) * 4))
+
+#define VFP_OFFSET_NAME(reg) (offsetof (DNBArchMachARM::FPU, __##reg) + offsetof (DNBArchMachARM::Context, vfp))
+
+#define FLOAT_FORMAT Float
+
+#define DEFINE_VFP_S_IDX(idx) e_regSetVFP, vfp_s##idx, "s" #idx, NULL, IEEE754, FLOAT_FORMAT, 4, VFP_S_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_s##idx, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM
+#define DEFINE_VFP_D_IDX(idx) e_regSetVFP, vfp_d##idx, "d" #idx, NULL, IEEE754, FLOAT_FORMAT, 8, VFP_D_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_d##idx, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM
+#define DEFINE_VFP_Q_IDX(idx) e_regSetVFP, vfp_q##idx, "q" #idx, NULL, Vector, VectorOfUInt8, 16, VFP_Q_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_q##idx, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM
+
+// Floating point registers
+const DNBRegisterInfo
+DNBArchMachARM::g_vfp_registers[] =
+{
+ { DEFINE_VFP_S_IDX ( 0), g_contained_q0, g_invalidate_q0 },
+ { DEFINE_VFP_S_IDX ( 1), g_contained_q0, g_invalidate_q0 },
+ { DEFINE_VFP_S_IDX ( 2), g_contained_q0, g_invalidate_q0 },
+ { DEFINE_VFP_S_IDX ( 3), g_contained_q0, g_invalidate_q0 },
+ { DEFINE_VFP_S_IDX ( 4), g_contained_q1, g_invalidate_q1 },
+ { DEFINE_VFP_S_IDX ( 5), g_contained_q1, g_invalidate_q1 },
+ { DEFINE_VFP_S_IDX ( 6), g_contained_q1, g_invalidate_q1 },
+ { DEFINE_VFP_S_IDX ( 7), g_contained_q1, g_invalidate_q1 },
+ { DEFINE_VFP_S_IDX ( 8), g_contained_q2, g_invalidate_q2 },
+ { DEFINE_VFP_S_IDX ( 9), g_contained_q2, g_invalidate_q2 },
+ { DEFINE_VFP_S_IDX (10), g_contained_q2, g_invalidate_q2 },
+ { DEFINE_VFP_S_IDX (11), g_contained_q2, g_invalidate_q2 },
+ { DEFINE_VFP_S_IDX (12), g_contained_q3, g_invalidate_q3 },
+ { DEFINE_VFP_S_IDX (13), g_contained_q3, g_invalidate_q3 },
+ { DEFINE_VFP_S_IDX (14), g_contained_q3, g_invalidate_q3 },
+ { DEFINE_VFP_S_IDX (15), g_contained_q3, g_invalidate_q3 },
+ { DEFINE_VFP_S_IDX (16), g_contained_q4, g_invalidate_q4 },
+ { DEFINE_VFP_S_IDX (17), g_contained_q4, g_invalidate_q4 },
+ { DEFINE_VFP_S_IDX (18), g_contained_q4, g_invalidate_q4 },
+ { DEFINE_VFP_S_IDX (19), g_contained_q4, g_invalidate_q4 },
+ { DEFINE_VFP_S_IDX (20), g_contained_q5, g_invalidate_q5 },
+ { DEFINE_VFP_S_IDX (21), g_contained_q5, g_invalidate_q5 },
+ { DEFINE_VFP_S_IDX (22), g_contained_q5, g_invalidate_q5 },
+ { DEFINE_VFP_S_IDX (23), g_contained_q5, g_invalidate_q5 },
+ { DEFINE_VFP_S_IDX (24), g_contained_q6, g_invalidate_q6 },
+ { DEFINE_VFP_S_IDX (25), g_contained_q6, g_invalidate_q6 },
+ { DEFINE_VFP_S_IDX (26), g_contained_q6, g_invalidate_q6 },
+ { DEFINE_VFP_S_IDX (27), g_contained_q6, g_invalidate_q6 },
+ { DEFINE_VFP_S_IDX (28), g_contained_q7, g_invalidate_q7 },
+ { DEFINE_VFP_S_IDX (29), g_contained_q7, g_invalidate_q7 },
+ { DEFINE_VFP_S_IDX (30), g_contained_q7, g_invalidate_q7 },
+ { DEFINE_VFP_S_IDX (31), g_contained_q7, g_invalidate_q7 },
+
+ { DEFINE_VFP_D_IDX (0), g_contained_q0, g_invalidate_q0 },
+ { DEFINE_VFP_D_IDX (1), g_contained_q0, g_invalidate_q0 },
+ { DEFINE_VFP_D_IDX (2), g_contained_q1, g_invalidate_q1 },
+ { DEFINE_VFP_D_IDX (3), g_contained_q1, g_invalidate_q1 },
+ { DEFINE_VFP_D_IDX (4), g_contained_q2, g_invalidate_q2 },
+ { DEFINE_VFP_D_IDX (5), g_contained_q2, g_invalidate_q2 },
+ { DEFINE_VFP_D_IDX (6), g_contained_q3, g_invalidate_q3 },
+ { DEFINE_VFP_D_IDX (7), g_contained_q3, g_invalidate_q3 },
+ { DEFINE_VFP_D_IDX (8), g_contained_q4, g_invalidate_q4 },
+ { DEFINE_VFP_D_IDX (9), g_contained_q4, g_invalidate_q4 },
+ { DEFINE_VFP_D_IDX (10), g_contained_q5, g_invalidate_q5 },
+ { DEFINE_VFP_D_IDX (11), g_contained_q5, g_invalidate_q5 },
+ { DEFINE_VFP_D_IDX (12), g_contained_q6, g_invalidate_q6 },
+ { DEFINE_VFP_D_IDX (13), g_contained_q6, g_invalidate_q6 },
+ { DEFINE_VFP_D_IDX (14), g_contained_q7, g_invalidate_q7 },
+ { DEFINE_VFP_D_IDX (15), g_contained_q7, g_invalidate_q7 },
+ { DEFINE_VFP_D_IDX (16), g_contained_q8, g_invalidate_q8 },
+ { DEFINE_VFP_D_IDX (17), g_contained_q8, g_invalidate_q8 },
+ { DEFINE_VFP_D_IDX (18), g_contained_q9, g_invalidate_q9 },
+ { DEFINE_VFP_D_IDX (19), g_contained_q9, g_invalidate_q9 },
+ { DEFINE_VFP_D_IDX (20), g_contained_q10, g_invalidate_q10 },
+ { DEFINE_VFP_D_IDX (21), g_contained_q10, g_invalidate_q10 },
+ { DEFINE_VFP_D_IDX (22), g_contained_q11, g_invalidate_q11 },
+ { DEFINE_VFP_D_IDX (23), g_contained_q11, g_invalidate_q11 },
+ { DEFINE_VFP_D_IDX (24), g_contained_q12, g_invalidate_q12 },
+ { DEFINE_VFP_D_IDX (25), g_contained_q12, g_invalidate_q12 },
+ { DEFINE_VFP_D_IDX (26), g_contained_q13, g_invalidate_q13 },
+ { DEFINE_VFP_D_IDX (27), g_contained_q13, g_invalidate_q13 },
+ { DEFINE_VFP_D_IDX (28), g_contained_q14, g_invalidate_q14 },
+ { DEFINE_VFP_D_IDX (29), g_contained_q14, g_invalidate_q14 },
+ { DEFINE_VFP_D_IDX (30), g_contained_q15, g_invalidate_q15 },
+ { DEFINE_VFP_D_IDX (31), g_contained_q15, g_invalidate_q15 },
+
+ { DEFINE_VFP_Q_IDX (0), NULL, g_invalidate_q0 },
+ { DEFINE_VFP_Q_IDX (1), NULL, g_invalidate_q1 },
+ { DEFINE_VFP_Q_IDX (2), NULL, g_invalidate_q2 },
+ { DEFINE_VFP_Q_IDX (3), NULL, g_invalidate_q3 },
+ { DEFINE_VFP_Q_IDX (4), NULL, g_invalidate_q4 },
+ { DEFINE_VFP_Q_IDX (5), NULL, g_invalidate_q5 },
+ { DEFINE_VFP_Q_IDX (6), NULL, g_invalidate_q6 },
+ { DEFINE_VFP_Q_IDX (7), NULL, g_invalidate_q7 },
+ { DEFINE_VFP_Q_IDX (8), NULL, g_invalidate_q8 },
+ { DEFINE_VFP_Q_IDX (9), NULL, g_invalidate_q9 },
+ { DEFINE_VFP_Q_IDX (10), NULL, g_invalidate_q10 },
+ { DEFINE_VFP_Q_IDX (11), NULL, g_invalidate_q11 },
+ { DEFINE_VFP_Q_IDX (12), NULL, g_invalidate_q12 },
+ { DEFINE_VFP_Q_IDX (13), NULL, g_invalidate_q13 },
+ { DEFINE_VFP_Q_IDX (14), NULL, g_invalidate_q14 },
+ { DEFINE_VFP_Q_IDX (15), NULL, g_invalidate_q15 },
+
+#if defined (__arm64__) || defined (__aarch64__)
+ { e_regSetVFP, vfp_fpsr, "fpsr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpsr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+ { e_regSetVFP, vfp_fpcr, "fpcr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpcr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }
+#else
+ { e_regSetVFP, vfp_fpscr, "fpscr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpscr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }
+#endif
+};
+
+// Exception registers
+
+const DNBRegisterInfo
+DNBArchMachARM::g_exc_registers[] =
+{
+ { e_regSetVFP, exc_exception , "exception" , NULL, Uint, Hex, 4, EXC_OFFSET(exception) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM },
+ { e_regSetVFP, exc_fsr , "fsr" , NULL, Uint, Hex, 4, EXC_OFFSET(fsr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM },
+ { e_regSetVFP, exc_far , "far" , NULL, Uint, Hex, 4, EXC_OFFSET(far) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM }
+};
+
+// Number of registers in each register set
+const size_t DNBArchMachARM::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM::k_num_vfp_registers = sizeof(g_vfp_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM::k_num_all_registers = k_num_gpr_registers + k_num_vfp_registers + k_num_exc_registers;
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+const DNBRegisterSetInfo
+DNBArchMachARM::g_reg_sets[] =
+{
+ { "ARM Registers", NULL, k_num_all_registers },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_vfp_registers, k_num_vfp_registers },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+// Total number of register sets for this architecture
+const size_t DNBArchMachARM::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo);
+
+
+const DNBRegisterSetInfo *
+DNBArchMachARM::GetRegisterSetInfo(nub_size_t *num_reg_sets)
+{
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool
+DNBArchMachARM::GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_r7; // is this the right reg?
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = gpr_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_cpsr;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint32 = m_state.context.gpr.__r[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetVFP:
+ // "reg" is an index into the floating point register set at this point.
+ // We need to translate it up so entry 0 in the fp reg set is the same as vfp_s0
+ // in the enumerated values for case statement below.
+ if (reg >= vfp_s0 && reg <= vfp_s31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ uint32_t *s_reg = ((uint32_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_s0);
+ memcpy (&value->value.v_uint8, s_reg, 4);
+#else
+ value->value.uint32 = m_state.context.vfp.__r[reg];
+#endif
+ return true;
+ }
+ else if (reg >= vfp_d0 && reg <= vfp_d31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ uint64_t *d_reg = ((uint64_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_d0);
+ memcpy (&value->value.v_uint8, d_reg, 8);
+#else
+ uint32_t d_reg_idx = reg - vfp_d0;
+ uint32_t s_reg_idx = d_reg_idx * 2;
+ value->value.v_sint32[0] = m_state.context.vfp.__r[s_reg_idx + 0];
+ value->value.v_sint32[1] = m_state.context.vfp.__r[s_reg_idx + 1];
+#endif
+ return true;
+ }
+ else if (reg >= vfp_q0 && reg <= vfp_q15)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&value->value.v_uint8, (uint8_t *) &m_state.context.vfp.__v[reg - vfp_q0], 16);
+#else
+ uint32_t s_reg_idx = (reg - vfp_q0) * 4;
+ memcpy (&value->value.v_uint8, (uint8_t *) &m_state.context.vfp.__r[s_reg_idx], 16);
+#endif
+ return true;
+ }
+#if defined (__arm64__) || defined (__aarch64__)
+ else if (reg == vfp_fpsr)
+ {
+ value->value.uint32 = m_state.context.vfp.__fpsr;
+ return true;
+ }
+ else if (reg == vfp_fpcr)
+ {
+ value->value.uint32 = m_state.context.vfp.__fpcr;
+ return true;
+ }
+#else
+ else if (reg == vfp_fpscr)
+ {
+ value->value.uint32 = m_state.context.vfp.__fpscr;
+ return true;
+ }
+#endif
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ value->value.uint32 = (&m_state.context.exc.__exception)[reg];
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool
+DNBArchMachARM::SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_r7;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = gpr_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_cpsr;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ m_state.context.gpr.__r[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetVFP:
+ // "reg" is an index into the floating point register set at this point.
+ // We need to translate it up so entry 0 in the fp reg set is the same as vfp_s0
+ // in the enumerated values for case statement below.
+ if (reg >= vfp_s0 && reg <= vfp_s31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ uint32_t *s_reg = ((uint32_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_s0);
+ memcpy (s_reg, &value->value.v_uint8, 4);
+#else
+ m_state.context.vfp.__r[reg] = value->value.uint32;
+#endif
+ success = true;
+ }
+ else if (reg >= vfp_d0 && reg <= vfp_d31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ uint64_t *d_reg = ((uint64_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_d0);
+ memcpy (d_reg, &value->value.v_uint8, 8);
+#else
+ uint32_t d_reg_idx = reg - vfp_d0;
+ uint32_t s_reg_idx = d_reg_idx * 2;
+ m_state.context.vfp.__r[s_reg_idx + 0] = value->value.v_sint32[0];
+ m_state.context.vfp.__r[s_reg_idx + 1] = value->value.v_sint32[1];
+#endif
+ success = true;
+ }
+ else if (reg >= vfp_q0 && reg <= vfp_q15)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy ((uint8_t *) &m_state.context.vfp.__v[reg - vfp_q0], &value->value.v_uint8, 16);
+#else
+ uint32_t s_reg_idx = (reg - vfp_q0) * 4;
+ memcpy ((uint8_t *) &m_state.context.vfp.__r[s_reg_idx], &value->value.v_uint8, 16);
+#endif
+ success = true;
+ }
+#if defined (__arm64__) || defined (__aarch64__)
+ else if (reg == vfp_fpsr)
+ {
+ m_state.context.vfp.__fpsr = value->value.uint32;
+ success = true;
+ }
+ else if (reg == vfp_fpcr)
+ {
+ m_state.context.vfp.__fpcr = value->value.uint32;
+ success = true;
+ }
+#else
+ else if (reg == vfp_fpscr)
+ {
+ m_state.context.vfp.__fpscr = value->value.uint32;
+ success = true;
+ }
+#endif
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ (&m_state.context.exc.__exception)[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+
+ }
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+kern_return_t
+DNBArchMachARM::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL: return GetGPRState(force) |
+ GetVFPState(force) |
+ GetEXCState(force) |
+ GetDBGState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetVFP: return GetVFPState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ case e_regSetDBG: return GetDBGState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchMachARM::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() |
+ SetVFPState() |
+ SetEXCState() |
+ SetDBGState(false);
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetVFP: return SetVFPState();
+ case e_regSetEXC: return SetEXCState();
+ case e_regSetDBG: return SetDBGState(false);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchMachARM::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+nub_size_t
+DNBArchMachARM::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context.gpr) +
+ sizeof (m_state.context.vfp) +
+ sizeof (m_state.context.exc);
+
+ if (buf && buf_len)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ bool force = false;
+ if (GetGPRState(force) | GetVFPState(force) | GetEXCState(force))
+ return 0;
+
+ // Copy each struct individually to avoid any padding that might be between the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy (p, &m_state.context.gpr, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy (p, &m_state.context.vfp, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy (p, &m_state.context.exc, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::GetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t
+DNBArchMachARM::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context.gpr) +
+ sizeof (m_state.context.vfp) +
+ sizeof (m_state.context.exc);
+
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ // Copy each struct individually to avoid any padding that might be between the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy (&m_state.context.gpr, p, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy (&m_state.context.vfp, p, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy (&m_state.context.exc, p, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+
+ if (SetGPRState() | SetVFPState() | SetEXCState())
+ return 0;
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::SetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size);
+ return size;
+}
+
+
+uint32_t
+DNBArchMachARM::SaveRegisterState ()
+{
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ // Always re-read the registers because above we call thread_abort_safely();
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::SaveRegisterState () error: GPR regs failed to read: %u ", kret);
+ }
+ else if ((kret = GetVFPState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::SaveRegisterState () error: %s regs failed to read: %u", "VFP", kret);
+ }
+ else
+ {
+ const uint32_t save_id = GetNextRegisterStateSaveID ();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return UINT32_MAX;
+}
+
+bool
+DNBArchMachARM::RestoreRegisterState (uint32_t save_id)
+{
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end())
+ {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.vfp = pos->second.vfp;
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret);
+ success = false;
+ }
+ else if ((kret = SetVFPState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, "VFP", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+
+#endif // #if defined (__arm__)
+
diff --git a/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h
new file mode 100644
index 000000000000..ae8974855238
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h
@@ -0,0 +1,282 @@
+//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArchMachARM_h__
+#define __DebugNubArchMachARM_h__
+
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+
+#include "DNBArch.h"
+
+#include <map>
+
+class MachThread;
+
+class DNBArchMachARM : public DNBArchProtocol
+{
+public:
+ enum { kMaxNumThumbITBreakpoints = 4 };
+
+ DNBArchMachARM(MachThread *thread) :
+ m_thread(thread),
+ m_state(),
+ m_disabled_watchpoints(),
+ m_hw_single_chained_step_addr(INVALID_NUB_ADDRESS),
+ m_last_decode_pc(INVALID_NUB_ADDRESS),
+ m_watchpoint_hw_index(-1),
+ m_watchpoint_did_occur(false),
+ m_watchpoint_resume_single_step_enabled(false),
+ m_saved_register_states()
+ {
+ m_disabled_watchpoints.resize (16);
+ memset(&m_dbg_save, 0, sizeof(m_dbg_save));
+#if defined (USE_ARM_DISASSEMBLER_FRAMEWORK)
+ ThumbStaticsInit(&m_last_decode_thumb);
+#endif
+ }
+
+ virtual ~DNBArchMachARM()
+ {
+ }
+
+ static void Initialize();
+ static const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState ();
+ virtual bool RestoreRegisterState (uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data& exc);
+
+ static DNBArchProtocol *Create (MachThread *thread);
+ static const uint8_t * SoftwareBreakpointOpcode (nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+ virtual uint32_t NumSupportedHardwareBreakpoints();
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size);
+ virtual bool DisableHardwareBreakpoint (uint32_t hw_break_index);
+
+ virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint_helper (uint32_t hw_break_index, bool also_set_on_task);
+ virtual bool ReenableHardwareWatchpoint (uint32_t hw_break_index);
+ virtual bool ReenableHardwareWatchpoint_helper (uint32_t hw_break_index);
+
+ virtual bool StepNotComplete ();
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+#if defined (ARM_DEBUG_STATE32) && (defined (__arm64__) || defined (__aarch64__))
+ typedef arm_debug_state32_t DBG;
+#else
+ typedef arm_debug_state_t DBG;
+#endif
+
+protected:
+
+
+ kern_return_t EnableHardwareSingleStep (bool enable);
+ kern_return_t SetSingleStepSoftwareBreakpoints ();
+
+ bool ConditionPassed(uint8_t condition, uint32_t cpsr);
+#if defined (USE_ARM_DISASSEMBLER_FRAMEWORK)
+ bool ComputeNextPC(nub_addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, nub_addr_t *targetPC);
+ arm_error_t DecodeInstructionUsingDisassembler(nub_addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, nub_addr_t *next_pc);
+ void DecodeITBlockInstructions(nub_addr_t curr_pc);
+#endif
+ void EvaluateNextInstructionForSoftwareBreakpointSetup(nub_addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, nub_addr_t *nextPC, bool *nextPCIsThumb);
+
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR, // ARM_THREAD_STATE
+ e_regSetVFP, // ARM_VFP_STATE (ARM_NEON_STATE if defined __arm64__)
+ e_regSetEXC, // ARM_EXCEPTION_STATE
+ e_regSetDBG, // ARM_DEBUG_STATE (ARM_DEBUG_STATE32 if defined __arm64__)
+ kNumRegisterSets
+ } RegisterSet;
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ typedef arm_thread_state_t GPR;
+#if defined (__arm64__) || defined (__aarch64__)
+ typedef arm_neon_state_t FPU;
+#else
+ typedef arm_vfp_state_t FPU;
+#endif
+ typedef arm_exception_state_t EXC;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_vfp_registers[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets[];
+
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_vfp_registers;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers;
+ static const size_t k_num_register_sets;
+
+ struct Context
+ {
+ GPR gpr;
+ FPU vfp;
+ EXC exc;
+ };
+
+ struct State
+ {
+ Context context;
+ DBG dbg;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t vfp_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ vfp_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+ void InvalidateRegisterSetState(int set)
+ {
+ SetError (set, Read, -1);
+ }
+ kern_return_t GetError (int set, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL: return gpr_errs[err_idx] |
+ vfp_errs[err_idx] |
+ exc_errs[err_idx] |
+ dbg_errs[err_idx] ;
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetVFP: return vfp_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ case e_regSetDBG: return dbg_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+ bool SetError (int set, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] = err;
+ vfp_errs[err_idx] = err;
+ dbg_errs[err_idx] = err;
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVFP:
+ vfp_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetDBG:
+ dbg_errs[err_idx] = err;
+ return true;
+ default: break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetVFPState (bool force);
+ kern_return_t GetEXCState (bool force);
+ kern_return_t GetDBGState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetVFPState ();
+ kern_return_t SetEXCState ();
+ kern_return_t SetDBGState (bool also_set_on_task);
+
+ bool IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index);
+ nub_addr_t GetWatchpointAddressByIndex (uint32_t hw_index);
+ nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+
+ class disabled_watchpoint {
+ public:
+ disabled_watchpoint () { addr = 0; control = 0; }
+ nub_addr_t addr;
+ uint32_t control;
+ };
+
+protected:
+ MachThread * m_thread;
+ State m_state;
+ DBG m_dbg_save;
+
+ // armv8 doesn't keep the disabled watchpoint values in the debug register context like armv7;
+ // we need to save them aside when we disable them temporarily.
+ std::vector<disabled_watchpoint> m_disabled_watchpoints;
+
+ nub_addr_t m_hw_single_chained_step_addr;
+ nub_addr_t m_last_decode_pc;
+
+ // The following member variables should be updated atomically.
+ int32_t m_watchpoint_hw_index;
+ bool m_watchpoint_did_occur;
+ bool m_watchpoint_resume_single_step_enabled;
+
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (__arm__)
+#endif // #ifndef __DebugNubArchMachARM_h__
diff --git a/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
new file mode 100644
index 000000000000..e79d3d52e8f8
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
@@ -0,0 +1,2095 @@
+//===-- DNBArchMachARM64.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+
+#include "MacOSX/arm64/DNBArchImplARM64.h"
+
+#if defined (ARM_THREAD_STATE64_COUNT)
+
+#include "MacOSX/MachProcess.h"
+#include "MacOSX/MachThread.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+#include "DNB.h"
+
+#include <inttypes.h>
+#include <sys/sysctl.h>
+
+// Break only in privileged or user mode
+// (PAC bits in the DBGWVRn_EL1 watchpoint control register)
+#define S_USER ((uint32_t)(2u << 1))
+
+#define BCR_ENABLE ((uint32_t)(1u))
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+// (LSC bits in the DBGWVRn_EL1 watchpoint control register)
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+// Enable breakpoint, watchpoint, and vector catch debug exceptions.
+// (MDE bit in the MDSCR_EL1 register. Equivalent to the MDBGen bit in DBGDSCRext in Aarch32)
+#define MDE_ENABLE ((uint32_t)(1u << 15))
+
+// Single instruction step
+// (SS bit in the MDSCR_EL1 register)
+#define SS_ENABLE ((uint32_t)(1u))
+
+static const uint8_t g_arm64_breakpoint_opcode[] = { 0x00, 0x00, 0x20, 0xD4 }; // "brk #0", 0xd4200000 in BE byte order
+static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; // this armv7 insn also works in arm64
+
+// If we need to set one logical watchpoint by using
+// two hardware watchpoint registers, the watchpoint
+// will be split into a "high" and "low" watchpoint.
+// Record both of them in the LoHi array.
+
+// It's safe to initialize to all 0's since
+// hi > lo and therefore LoHi[i] cannot be 0.
+static uint32_t LoHi[16] = { 0 };
+
+
+void
+DNBArchMachARM64::Initialize()
+{
+ DNBArchPluginInfo arch_plugin_info =
+ {
+ CPU_TYPE_ARM64,
+ DNBArchMachARM64::Create,
+ DNBArchMachARM64::GetRegisterSetInfo,
+ DNBArchMachARM64::SoftwareBreakpointOpcode
+ };
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin (arch_plugin_info);
+}
+
+
+DNBArchProtocol *
+DNBArchMachARM64::Create (MachThread *thread)
+{
+ DNBArchMachARM64 *obj = new DNBArchMachARM64 (thread);
+
+ return obj;
+}
+
+const uint8_t *
+DNBArchMachARM64::SoftwareBreakpointOpcode (nub_size_t byte_size)
+{
+ return g_arm_breakpoint_opcode;
+}
+
+uint32_t
+DNBArchMachARM64::GetCPUType()
+{
+ return CPU_TYPE_ARM64;
+}
+
+uint64_t
+DNBArchMachARM64::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__pc;
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachARM64::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.context.gpr.__pc = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchMachARM64::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__sp;
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachARM64::GetGPRState(bool force)
+{
+ int set = e_regSetGPR;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetGPRCount;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, &count);
+ if (DNBLogEnabledForAny (LOG_THREAD))
+ {
+ uint64_t *x = &m_state.context.gpr.__x[0];
+ DNBLogThreaded("thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count = %u) regs"
+ "\n x0=%16.16llx"
+ "\n x1=%16.16llx"
+ "\n x2=%16.16llx"
+ "\n x3=%16.16llx"
+ "\n x4=%16.16llx"
+ "\n x5=%16.16llx"
+ "\n x6=%16.16llx"
+ "\n x7=%16.16llx"
+ "\n x8=%16.16llx"
+ "\n x9=%16.16llx"
+ "\n x10=%16.16llx"
+ "\n x11=%16.16llx"
+ "\n x12=%16.16llx"
+ "\n x13=%16.16llx"
+ "\n x14=%16.16llx"
+ "\n x15=%16.16llx"
+ "\n x16=%16.16llx"
+ "\n x17=%16.16llx"
+ "\n x18=%16.16llx"
+ "\n x19=%16.16llx"
+ "\n x20=%16.16llx"
+ "\n x21=%16.16llx"
+ "\n x22=%16.16llx"
+ "\n x23=%16.16llx"
+ "\n x24=%16.16llx"
+ "\n x25=%16.16llx"
+ "\n x26=%16.16llx"
+ "\n x27=%16.16llx"
+ "\n x28=%16.16llx"
+ "\n fp=%16.16llx"
+ "\n lr=%16.16llx"
+ "\n sp=%16.16llx"
+ "\n pc=%16.16llx"
+ "\n cpsr=%8.8x",
+ m_thread->MachPortNumber(),
+ e_regSetGPR,
+ e_regSetGPRCount,
+ kret,
+ count,
+ x[0],
+ x[1],
+ x[2],
+ x[3],
+ x[4],
+ x[5],
+ x[6],
+ x[7],
+ x[8],
+ x[9],
+ x[0],
+ x[11],
+ x[12],
+ x[13],
+ x[14],
+ x[15],
+ x[16],
+ x[17],
+ x[18],
+ x[19],
+ x[20],
+ x[21],
+ x[22],
+ x[23],
+ x[24],
+ x[25],
+ x[26],
+ x[27],
+ x[28],
+ m_state.context.gpr.__fp,
+ m_state.context.gpr.__lr,
+ m_state.context.gpr.__sp,
+ m_state.context.gpr.__pc,
+ m_state.context.gpr.__cpsr);
+ }
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM64::GetVFPState(bool force)
+{
+ int set = e_regSetVFP;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetVFPCount;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_NEON_STATE64, (thread_state_t)&m_state.context.vfp, &count);
+ if (DNBLogEnabledForAny (LOG_THREAD))
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ DNBLogThreaded("thread_get_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs"
+ "\n q0 = 0x%16.16llx%16.16llx"
+ "\n q1 = 0x%16.16llx%16.16llx"
+ "\n q2 = 0x%16.16llx%16.16llx"
+ "\n q3 = 0x%16.16llx%16.16llx"
+ "\n q4 = 0x%16.16llx%16.16llx"
+ "\n q5 = 0x%16.16llx%16.16llx"
+ "\n q6 = 0x%16.16llx%16.16llx"
+ "\n q7 = 0x%16.16llx%16.16llx"
+ "\n q8 = 0x%16.16llx%16.16llx"
+ "\n q9 = 0x%16.16llx%16.16llx"
+ "\n q10 = 0x%16.16llx%16.16llx"
+ "\n q11 = 0x%16.16llx%16.16llx"
+ "\n q12 = 0x%16.16llx%16.16llx"
+ "\n q13 = 0x%16.16llx%16.16llx"
+ "\n q14 = 0x%16.16llx%16.16llx"
+ "\n q15 = 0x%16.16llx%16.16llx"
+ "\n q16 = 0x%16.16llx%16.16llx"
+ "\n q17 = 0x%16.16llx%16.16llx"
+ "\n q18 = 0x%16.16llx%16.16llx"
+ "\n q19 = 0x%16.16llx%16.16llx"
+ "\n q20 = 0x%16.16llx%16.16llx"
+ "\n q21 = 0x%16.16llx%16.16llx"
+ "\n q22 = 0x%16.16llx%16.16llx"
+ "\n q23 = 0x%16.16llx%16.16llx"
+ "\n q24 = 0x%16.16llx%16.16llx"
+ "\n q25 = 0x%16.16llx%16.16llx"
+ "\n q26 = 0x%16.16llx%16.16llx"
+ "\n q27 = 0x%16.16llx%16.16llx"
+ "\n q28 = 0x%16.16llx%16.16llx"
+ "\n q29 = 0x%16.16llx%16.16llx"
+ "\n q30 = 0x%16.16llx%16.16llx"
+ "\n q31 = 0x%16.16llx%16.16llx"
+ "\n fpsr = 0x%8.8x"
+ "\n fpcr = 0x%8.8x\n\n",
+ m_thread->MachPortNumber(),
+ e_regSetVFP,
+ e_regSetVFPCount,
+ kret,
+ count,
+ ((uint64_t *)&m_state.context.vfp.__v[0])[0] , ((uint64_t *)&m_state.context.vfp.__v[0])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[0] , ((uint64_t *)&m_state.context.vfp.__v[1])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[0] , ((uint64_t *)&m_state.context.vfp.__v[2])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[0] , ((uint64_t *)&m_state.context.vfp.__v[3])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[0] , ((uint64_t *)&m_state.context.vfp.__v[4])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[0] , ((uint64_t *)&m_state.context.vfp.__v[5])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[0] , ((uint64_t *)&m_state.context.vfp.__v[6])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[0] , ((uint64_t *)&m_state.context.vfp.__v[7])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[0] , ((uint64_t *)&m_state.context.vfp.__v[8])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[0] , ((uint64_t *)&m_state.context.vfp.__v[9])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[0], ((uint64_t *)&m_state.context.vfp.__v[10])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[0], ((uint64_t *)&m_state.context.vfp.__v[11])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[0], ((uint64_t *)&m_state.context.vfp.__v[12])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[0], ((uint64_t *)&m_state.context.vfp.__v[13])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[0], ((uint64_t *)&m_state.context.vfp.__v[14])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[0], ((uint64_t *)&m_state.context.vfp.__v[15])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[16])[0], ((uint64_t *)&m_state.context.vfp.__v[16])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[17])[0], ((uint64_t *)&m_state.context.vfp.__v[17])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[18])[0], ((uint64_t *)&m_state.context.vfp.__v[18])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[19])[0], ((uint64_t *)&m_state.context.vfp.__v[19])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[20])[0], ((uint64_t *)&m_state.context.vfp.__v[20])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[21])[0], ((uint64_t *)&m_state.context.vfp.__v[21])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[22])[0], ((uint64_t *)&m_state.context.vfp.__v[22])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[23])[0], ((uint64_t *)&m_state.context.vfp.__v[23])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[24])[0], ((uint64_t *)&m_state.context.vfp.__v[24])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[25])[0], ((uint64_t *)&m_state.context.vfp.__v[25])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[26])[0], ((uint64_t *)&m_state.context.vfp.__v[26])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[27])[0], ((uint64_t *)&m_state.context.vfp.__v[27])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[28])[0], ((uint64_t *)&m_state.context.vfp.__v[28])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[29])[0], ((uint64_t *)&m_state.context.vfp.__v[29])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[30])[0], ((uint64_t *)&m_state.context.vfp.__v[30])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[31])[0], ((uint64_t *)&m_state.context.vfp.__v[31])[1],
+ m_state.context.vfp.__fpsr,
+ m_state.context.vfp.__fpcr);
+#endif
+ }
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM64::GetEXCState(bool force)
+{
+ int set = e_regSetEXC;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetEXCCount;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_EXCEPTION_STATE64, (thread_state_t)&m_state.context.exc, &count);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+static void
+DumpDBGState(const arm_debug_state_t& dbg)
+{
+ uint32_t i = 0;
+ for (i=0; i<16; i++)
+ DNBLogThreadedIf(LOG_STEP, "BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }",
+ i, i, dbg.__bvr[i], dbg.__bcr[i],
+ i, i, dbg.__wvr[i], dbg.__wcr[i]);
+}
+
+kern_return_t
+DNBArchMachARM64::GetDBGState(bool force)
+{
+ int set = e_regSetDBG;
+
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetDBGCount;
+ kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE64, (thread_state_t)&m_state.dbg, &count);
+ m_state.SetError(set, Read, kret);
+
+ return kret;
+}
+
+kern_return_t
+DNBArchMachARM64::SetGPRState()
+{
+ int set = e_regSetGPR;
+ kern_return_t kret = ::thread_set_state(m_thread->MachPortNumber(), ARM_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, e_regSetGPRCount);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM64::SetVFPState()
+{
+ int set = e_regSetVFP;
+ kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_NEON_STATE64, (thread_state_t)&m_state.context.vfp, e_regSetVFPCount);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM64::SetEXCState()
+{
+ int set = e_regSetEXC;
+ kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_EXCEPTION_STATE64, (thread_state_t)&m_state.context.exc, e_regSetEXCCount);
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+ return kret; // Return the error code
+}
+
+kern_return_t
+DNBArchMachARM64::SetDBGState(bool also_set_on_task)
+{
+ int set = e_regSetDBG;
+ kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_DEBUG_STATE64, (thread_state_t)&m_state.dbg, e_regSetDBGCount);
+ if (also_set_on_task)
+ {
+ kern_return_t task_kret = task_set_state (m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE64, (thread_state_t)&m_state.dbg, e_regSetDBGCount);
+ if (task_kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::SetDBGState failed to set debug control register state: 0x%8.8x.", task_kret);
+ }
+ m_state.SetError(set, Write, kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently
+
+ return kret; // Return the error code
+}
+
+void
+DNBArchMachARM64::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ EnableHardwareSingleStep(true);
+ }
+
+ // Disable the triggered watchpoint temporarily before we resume.
+ // Plus, we try to enable hardware single step to execute past the instruction which triggered our watchpoint.
+ if (m_watchpoint_did_occur)
+ {
+ if (m_watchpoint_hw_index >= 0)
+ {
+ kern_return_t kret = GetDBGState(false);
+ if (kret == KERN_SUCCESS && !IsWatchpointEnabled(m_state.dbg, m_watchpoint_hw_index)) {
+ // The watchpoint might have been disabled by the user. We don't need to do anything at all
+ // to enable hardware single stepping.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ return;
+ }
+
+ DisableHardwareWatchpoint(m_watchpoint_hw_index, false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() DisableHardwareWatchpoint(%d) called",
+ m_watchpoint_hw_index);
+
+ // Enable hardware single step to move past the watchpoint-triggering instruction.
+ m_watchpoint_resume_single_step_enabled = (EnableHardwareSingleStep(true) == KERN_SUCCESS);
+
+ // If we are not able to enable single step to move past the watchpoint-triggering instruction,
+ // at least we should reset the two watchpoint member variables so that the next time around
+ // this callback function is invoked, the enclosing logical branch is skipped.
+ if (!m_watchpoint_resume_single_step_enabled) {
+ // Reset the two watchpoint member variables.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() failed to enable single step");
+ }
+ else
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() succeeded to enable single step");
+ }
+ }
+}
+
+bool
+DNBArchMachARM64::NotifyException(MachException::Data& exc)
+{
+
+ switch (exc.exc_type)
+ {
+ default:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() == 2 && exc.exc_data[0] == EXC_ARM_DA_DEBUG)
+ {
+ // The data break address is passed as exc_data[1].
+ nub_addr_t addr = exc.exc_data[1];
+ // Find the hardware index with the side effect of possibly massaging the
+ // addr to return the starting address as seen from the debugger side.
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+
+ // One logical watchpoint was split into two watchpoint locations because
+ // it was too big. If the watchpoint exception is indicating the 2nd half
+ // of the two-parter, find the address of the 1st half and report that --
+ // that's what lldb is going to expect to see.
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException watchpoint %d was hit on address 0x%llx", hw_index, (uint64_t) addr);
+ const int num_watchpoints = NumSupportedHardwareWatchpoints ();
+ for (int i = 0; i < num_watchpoints; i++)
+ {
+ if (LoHi[i] != 0
+ && LoHi[i] == hw_index
+ && LoHi[i] != i
+ && GetWatchpointAddressByIndex (i) != INVALID_NUB_ADDRESS)
+ {
+ addr = GetWatchpointAddressByIndex (i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException It is a linked watchpoint; rewritten to index %d addr 0x%llx", LoHi[i], (uint64_t) addr);
+ }
+ }
+
+ if (hw_index != INVALID_NUB_HW_INDEX)
+ {
+ m_watchpoint_did_occur = true;
+ m_watchpoint_hw_index = hw_index;
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+bool
+DNBArchMachARM64::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ if (m_watchpoint_resume_single_step_enabled)
+ {
+ // Great! We now disable the hardware single step as well as re-enable the hardware watchpoint.
+ // See also ThreadWillResume().
+ if (EnableHardwareSingleStep(false) == KERN_SUCCESS)
+ {
+ if (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0)
+ {
+ ReenableHardwareWatchpoint(m_watchpoint_hw_index);
+ m_watchpoint_resume_single_step_enabled = false;
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ }
+ else
+ {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) does not hold!");
+ }
+ }
+ else
+ {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but unable to disable single step!");
+ }
+ }
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchMachARM64::EnableHardwareSingleStep (bool enable)
+{
+ DNBError err;
+ DNBLogThreadedIf(LOG_STEP, "%s( enable = %d )", __FUNCTION__, enable);
+
+ err = GetGPRState(false);
+
+ if (err.Fail())
+ {
+ err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__);
+ return err.Error();
+ }
+
+ err = GetDBGState(false);
+
+ if (err.Fail())
+ {
+ err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__);
+ return err.Error();
+ }
+
+ if (enable)
+ {
+ DNBLogThreadedIf(LOG_STEP, "%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc);
+ m_state.dbg.__mdscr_el1 |= SS_ENABLE;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_STEP, "%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc);
+ m_state.dbg.__mdscr_el1 &= ~(SS_ENABLE);
+ }
+
+ return SetDBGState(false);
+}
+
+// return 1 if bit "BIT" is set in "value"
+static inline uint32_t bit(uint32_t value, uint32_t bit)
+{
+ return (value >> bit) & 1u;
+}
+
+// return the bitfield "value[msbit:lsbit]".
+static inline uint64_t bits(uint64_t value, uint32_t msbit, uint32_t lsbit)
+{
+ assert(msbit >= lsbit);
+ uint64_t shift_left = sizeof(value) * 8 - 1 - msbit;
+ value <<= shift_left; // shift anything above the msbit off of the unsigned edge
+ value >>= shift_left + lsbit; // shift it back again down to the lsbit (including undoing any shift from above)
+ return value; // return our result
+}
+
+uint32_t
+DNBArchMachARM64::NumSupportedHardwareWatchpoints()
+{
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT_MAX;
+ if (g_num_supported_hw_watchpoints == UINT_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+
+
+ size_t len;
+ uint32_t n = 0;
+ len = sizeof (n);
+ if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0)
+ {
+ g_num_supported_hw_watchpoints = n;
+ DNBLogThreadedIf(LOG_THREAD, "hw.optional.watchpoint=%u", n);
+ }
+ else
+ {
+ // For AArch64 we would need to look at ID_AA64DFR0_EL1 but debugserver runs in EL0 so it can't
+ // access that reg. The kernel should have filled in the sysctls based on it though.
+#if defined (__arm__)
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ uint32_t numWRPs = bits(register_DBGDIDR, 31, 28);
+ // Zero is reserved for the WRP count, so don't increment it if it is zero
+ if (numWRPs > 0)
+ numWRPs++;
+ g_num_supported_hw_watchpoints = numWRPs;
+ DNBLogThreadedIf(LOG_THREAD, "Number of supported hw watchpoints via asm(): %d", g_num_supported_hw_watchpoints);
+#endif
+ }
+ }
+ return g_num_supported_hw_watchpoints;
+}
+
+uint32_t
+DNBArchMachARM64::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu, read = %u, write = %u)", (uint64_t)addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return INVALID_NUB_HW_INDEX;
+
+ // Otherwise, can't watch more than 8 bytes per WVR/WCR pair
+ if (size > 8)
+ return INVALID_NUB_HW_INDEX;
+
+ // arm64 watchpoints really have an 8-byte alignment requirement. You can put a watchpoint on a 4-byte
+ // offset address but you can only watch 4 bytes with that watchpoint.
+
+ // arm64 watchpoints on an 8-byte (double word) aligned addr can watch any bytes in that
+ // 8-byte long region of memory. They can watch the 1st byte, the 2nd byte, 3rd byte, etc, or any
+ // combination therein by setting the bits in the BAS [12:5] (Byte Address Select) field of
+ // the DBGWCRn_EL1 reg for the watchpoint.
+
+ // If the MASK [28:24] bits in the DBGWCRn_EL1 allow a single watchpoint to monitor a larger region
+ // of memory (16 bytes, 32 bytes, or 2GB) but the Byte Address Select bitfield then selects a larger
+ // range of bytes, instead of individual bytes. See the ARMv8 Debug Architecture manual for details.
+ // This implementation does not currently use the MASK bits; the largest single region watched by a single
+ // watchpoint right now is 8-bytes.
+
+ nub_addr_t aligned_wp_address = addr & ~0x7;
+ uint32_t addr_dword_offset = addr & 0x7;
+
+ // Do we need to split up this logical watchpoint into two hardware watchpoint
+ // registers?
+ // e.g. a watchpoint of length 4 on address 6. We need do this with
+ // one watchpoint on address 0 with bytes 6 & 7 being monitored
+ // one watchpoint on address 8 with bytes 0, 1, 2, 3 being monitored
+
+ if (addr_dword_offset + size > 8)
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu) needs two hardware watchpoints slots to monitor", (uint64_t)addr, size);
+ int low_watchpoint_size = 8 - addr_dword_offset;
+ int high_watchpoint_size = addr_dword_offset + size - 8;
+
+ uint32_t lo = EnableHardwareWatchpoint(addr, low_watchpoint_size, read, write, also_set_on_task);
+ if (lo == INVALID_NUB_HW_INDEX)
+ return INVALID_NUB_HW_INDEX;
+ uint32_t hi = EnableHardwareWatchpoint (aligned_wp_address + 8, high_watchpoint_size, read, write, also_set_on_task);
+ if (hi == INVALID_NUB_HW_INDEX)
+ {
+ DisableHardwareWatchpoint (lo, also_set_on_task);
+ return INVALID_NUB_HW_INDEX;
+ }
+ // Tag this lo->hi mapping in our database.
+ LoHi[lo] = hi;
+ return lo;
+ }
+
+ // At this point
+ // 1 aligned_wp_address is the requested address rounded down to 8-byte alignment
+ // 2 addr_dword_offset is the offset into that double word (8-byte) region that we are watching
+ // 3 size is the number of bytes within that 8-byte region that we are watching
+
+ // Set the Byte Address Selects bits DBGWCRn_EL1 bits [12:5] based on the above.
+ // The bit shift and negation operation will give us 0b11 for 2, 0b1111 for 4, etc, up to 0b11111111 for 8.
+ // then we shift those bits left by the offset into this dword that we are interested in.
+ // e.g. if we are watching bytes 4,5,6,7 in a dword we want a BAS of 0b11110000.
+ uint32_t byte_address_select = ((1 << size) - 1) << addr_dword_offset;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i=0; i<num_hw_watchpoints; ++i)
+ {
+ if ((m_state.dbg.__wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw watchpoint slot (in i)
+ }
+
+ // See if we found an available hw watchpoint slot above
+ if (i < num_hw_watchpoints)
+ {
+ //DumpDBGState(m_state.dbg);
+
+ // Clear any previous LoHi joined-watchpoint that may have been in use
+ LoHi[i] = 0;
+
+ // shift our Byte Address Select bits up to the correct bit range for the DBGWCRn_EL1
+ byte_address_select = byte_address_select << 5;
+
+ // Make sure bits 1:0 are clear in our address
+ m_state.dbg.__wvr[i] = aligned_wp_address; // DVA (Data Virtual Address)
+ m_state.dbg.__wcr[i] = byte_address_select | // Which bytes that follow the DVA that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint() adding watchpoint on address 0x%llx with control register value 0x%x", (uint64_t) m_state.dbg.__wvr[i], (uint32_t) m_state.dbg.__wcr[i]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us automatically, don't need to do it here.
+
+ kret = SetDBGState(also_set_on_task);
+ //DumpDBGState(m_state.dbg);
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+DNBArchMachARM64::ReenableHardwareWatchpoint (uint32_t hw_index)
+{
+ // If this logical watchpoint # is actually implemented using
+ // two hardware watchpoint registers, re-enable both of them.
+
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index])
+ {
+ return ReenableHardwareWatchpoint_helper (hw_index) && ReenableHardwareWatchpoint_helper (LoHi[hw_index]);
+ }
+ else
+ {
+ return ReenableHardwareWatchpoint_helper (hw_index);
+ }
+}
+
+bool
+DNBArchMachARM64::ReenableHardwareWatchpoint_helper (uint32_t hw_index)
+{
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_state.dbg.__wvr[hw_index] = m_disabled_watchpoints[hw_index].addr;
+ m_state.dbg.__wcr[hw_index] = m_disabled_watchpoints[hw_index].control;
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx",
+ hw_index,
+ hw_index,
+ (uint64_t) m_state.dbg.__wvr[hw_index],
+ hw_index,
+ (uint64_t) m_state.dbg.__wcr[hw_index]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us automatically, don't need to do it here.
+
+ kret = SetDBGState(false);
+
+ return (kret == KERN_SUCCESS);
+}
+
+bool
+DNBArchMachARM64::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task)
+{
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index])
+ {
+ return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task) && DisableHardwareWatchpoint_helper (LoHi[hw_index], also_set_on_task);
+ }
+ else
+ {
+ return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task);
+ }
+}
+
+bool
+DNBArchMachARM64::DisableHardwareWatchpoint_helper (uint32_t hw_index, bool also_set_on_task)
+{
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_disabled_watchpoints[hw_index].addr = m_state.dbg.__wvr[hw_index];
+ m_disabled_watchpoints[hw_index].control = m_state.dbg.__wcr[hw_index];
+
+ m_state.dbg.__wcr[hw_index] &= ~((nub_addr_t)WCR_ENABLE);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::DisableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx",
+ hw_index,
+ hw_index,
+ (uint64_t) m_state.dbg.__wvr[hw_index],
+ hw_index,
+ (uint64_t) m_state.dbg.__wcr[hw_index]);
+
+ kret = SetDBGState(also_set_on_task);
+
+ return (kret == KERN_SUCCESS);
+}
+
+// This is for checking the Byte Address Select bits in the DBRWCRn_EL1 control register.
+// Returns -1 if the trailing bit patterns are not one of:
+// { 0b???????1, 0b??????10, 0b?????100, 0b????1000, 0b???10000, 0b??100000, 0b?1000000, 0b10000000 }.
+static inline
+int32_t
+LowestBitSet(uint32_t val)
+{
+ for (unsigned i = 0; i < 8; ++i) {
+ if (bit(val, i))
+ return i;
+ }
+ return -1;
+}
+
+// Iterate through the debug registers; return the index of the first watchpoint whose address matches.
+// As a side effect, the starting address as understood by the debugger is returned which could be
+// different from 'addr' passed as an in/out argument.
+uint32_t
+DNBArchMachARM64::GetHardwareWatchpointHit(nub_addr_t &addr)
+{
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ //DumpDBGState(m_state.dbg);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::GetHardwareWatchpointHit() addr = 0x%llx", (uint64_t)addr);
+
+ // This is the watchpoint value to match against, i.e., word address.
+ nub_addr_t wp_val = addr & ~((nub_addr_t)3);
+ if (kret == KERN_SUCCESS)
+ {
+ DBG &debug_state = m_state.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ {
+ nub_addr_t wp_addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchMachARM64::GetHardwareWatchpointHit() slot: %u (addr = 0x%llx).",
+ i, (uint64_t)wp_addr);
+ if (wp_val == wp_addr) {
+ uint32_t byte_mask = bits(debug_state.__wcr[i], 12, 5);
+
+ // Sanity check the byte_mask, first.
+ if (LowestBitSet(byte_mask) < 0)
+ continue;
+
+ // Check that the watchpoint is enabled.
+ if (!IsWatchpointEnabled(debug_state, i))
+ continue;
+
+ // Compute the starting address (from the point of view of the debugger).
+ addr = wp_addr + LowestBitSet(byte_mask);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+nub_addr_t
+DNBArchMachARM64::GetWatchpointAddressByIndex (uint32_t hw_index)
+{
+ kern_return_t kret = GetDBGState(true);
+ if (kret != KERN_SUCCESS)
+ return INVALID_NUB_ADDRESS;
+ const uint32_t num = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num)
+ return INVALID_NUB_ADDRESS;
+ if (IsWatchpointEnabled (m_state.dbg, hw_index))
+ return GetWatchAddress (m_state.dbg, hw_index);
+ return INVALID_NUB_ADDRESS;
+}
+
+bool
+DNBArchMachARM64::IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index)
+{
+ // Watchpoint Control Registers, bitfield definitions
+ // ...
+ // Bits Value Description
+ // [0] 0 Watchpoint disabled
+ // 1 Watchpoint enabled.
+ return (debug_state.__wcr[hw_index] & 1u);
+}
+
+nub_addr_t
+DNBArchMachARM64::GetWatchAddress(const DBG &debug_state, uint32_t hw_index)
+{
+ // Watchpoint Value Registers, bitfield definitions
+ // Bits Description
+ // [31:2] Watchpoint value (word address, i.e., 4-byte aligned)
+ // [1:0] RAZ/SBZP
+ return bits(debug_state.__wvr[hw_index], 63, 0);
+}
+
+//----------------------------------------------------------------------
+// Register information definitions for 64 bit ARMv8.
+//----------------------------------------------------------------------
+enum gpr_regnums
+{
+ gpr_x0 = 0,
+ gpr_x1,
+ gpr_x2,
+ gpr_x3,
+ gpr_x4,
+ gpr_x5,
+ gpr_x6,
+ gpr_x7,
+ gpr_x8,
+ gpr_x9,
+ gpr_x10,
+ gpr_x11,
+ gpr_x12,
+ gpr_x13,
+ gpr_x14,
+ gpr_x15,
+ gpr_x16,
+ gpr_x17,
+ gpr_x18,
+ gpr_x19,
+ gpr_x20,
+ gpr_x21,
+ gpr_x22,
+ gpr_x23,
+ gpr_x24,
+ gpr_x25,
+ gpr_x26,
+ gpr_x27,
+ gpr_x28,
+ gpr_fp, gpr_x29 = gpr_fp,
+ gpr_lr, gpr_x30 = gpr_lr,
+ gpr_sp, gpr_x31 = gpr_sp,
+ gpr_pc,
+ gpr_cpsr,
+ gpr_w0,
+ gpr_w1,
+ gpr_w2,
+ gpr_w3,
+ gpr_w4,
+ gpr_w5,
+ gpr_w6,
+ gpr_w7,
+ gpr_w8,
+ gpr_w9,
+ gpr_w10,
+ gpr_w11,
+ gpr_w12,
+ gpr_w13,
+ gpr_w14,
+ gpr_w15,
+ gpr_w16,
+ gpr_w17,
+ gpr_w18,
+ gpr_w19,
+ gpr_w20,
+ gpr_w21,
+ gpr_w22,
+ gpr_w23,
+ gpr_w24,
+ gpr_w25,
+ gpr_w26,
+ gpr_w27,
+ gpr_w28
+
+};
+
+enum
+{
+ vfp_v0 = 0,
+ vfp_v1,
+ vfp_v2,
+ vfp_v3,
+ vfp_v4,
+ vfp_v5,
+ vfp_v6,
+ vfp_v7,
+ vfp_v8,
+ vfp_v9,
+ vfp_v10,
+ vfp_v11,
+ vfp_v12,
+ vfp_v13,
+ vfp_v14,
+ vfp_v15,
+ vfp_v16,
+ vfp_v17,
+ vfp_v18,
+ vfp_v19,
+ vfp_v20,
+ vfp_v21,
+ vfp_v22,
+ vfp_v23,
+ vfp_v24,
+ vfp_v25,
+ vfp_v26,
+ vfp_v27,
+ vfp_v28,
+ vfp_v29,
+ vfp_v30,
+ vfp_v31,
+ vfp_fpsr,
+ vfp_fpcr,
+
+ // lower 32 bits of the corresponding vfp_v<n> reg.
+ vfp_s0,
+ vfp_s1,
+ vfp_s2,
+ vfp_s3,
+ vfp_s4,
+ vfp_s5,
+ vfp_s6,
+ vfp_s7,
+ vfp_s8,
+ vfp_s9,
+ vfp_s10,
+ vfp_s11,
+ vfp_s12,
+ vfp_s13,
+ vfp_s14,
+ vfp_s15,
+ vfp_s16,
+ vfp_s17,
+ vfp_s18,
+ vfp_s19,
+ vfp_s20,
+ vfp_s21,
+ vfp_s22,
+ vfp_s23,
+ vfp_s24,
+ vfp_s25,
+ vfp_s26,
+ vfp_s27,
+ vfp_s28,
+ vfp_s29,
+ vfp_s30,
+ vfp_s31,
+
+ // lower 64 bits of the corresponding vfp_v<n> reg.
+ vfp_d0,
+ vfp_d1,
+ vfp_d2,
+ vfp_d3,
+ vfp_d4,
+ vfp_d5,
+ vfp_d6,
+ vfp_d7,
+ vfp_d8,
+ vfp_d9,
+ vfp_d10,
+ vfp_d11,
+ vfp_d12,
+ vfp_d13,
+ vfp_d14,
+ vfp_d15,
+ vfp_d16,
+ vfp_d17,
+ vfp_d18,
+ vfp_d19,
+ vfp_d20,
+ vfp_d21,
+ vfp_d22,
+ vfp_d23,
+ vfp_d24,
+ vfp_d25,
+ vfp_d26,
+ vfp_d27,
+ vfp_d28,
+ vfp_d29,
+ vfp_d30,
+ vfp_d31
+};
+
+enum
+{
+ exc_far = 0,
+ exc_esr,
+ exc_exception
+};
+
+// These numbers from the "DWARF for the ARM 64-bit Architecture (AArch64)" document.
+
+enum
+{
+ dwarf_x0 = 0,
+ dwarf_x1,
+ dwarf_x2,
+ dwarf_x3,
+ dwarf_x4,
+ dwarf_x5,
+ dwarf_x6,
+ dwarf_x7,
+ dwarf_x8,
+ dwarf_x9,
+ dwarf_x10,
+ dwarf_x11,
+ dwarf_x12,
+ dwarf_x13,
+ dwarf_x14,
+ dwarf_x15,
+ dwarf_x16,
+ dwarf_x17,
+ dwarf_x18,
+ dwarf_x19,
+ dwarf_x20,
+ dwarf_x21,
+ dwarf_x22,
+ dwarf_x23,
+ dwarf_x24,
+ dwarf_x25,
+ dwarf_x26,
+ dwarf_x27,
+ dwarf_x28,
+ dwarf_x29,
+ dwarf_x30,
+ dwarf_x31,
+ dwarf_pc = 32,
+ dwarf_elr_mode = 33,
+ dwarf_fp = dwarf_x29,
+ dwarf_lr = dwarf_x30,
+ dwarf_sp = dwarf_x31,
+ // 34-63 reserved
+
+ // V0-V31 (128 bit vector registers)
+ dwarf_v0 = 64,
+ dwarf_v1,
+ dwarf_v2,
+ dwarf_v3,
+ dwarf_v4,
+ dwarf_v5,
+ dwarf_v6,
+ dwarf_v7,
+ dwarf_v8,
+ dwarf_v9,
+ dwarf_v10,
+ dwarf_v11,
+ dwarf_v12,
+ dwarf_v13,
+ dwarf_v14,
+ dwarf_v15,
+ dwarf_v16,
+ dwarf_v17,
+ dwarf_v18,
+ dwarf_v19,
+ dwarf_v20,
+ dwarf_v21,
+ dwarf_v22,
+ dwarf_v23,
+ dwarf_v24,
+ dwarf_v25,
+ dwarf_v26,
+ dwarf_v27,
+ dwarf_v28,
+ dwarf_v29,
+ dwarf_v30,
+ dwarf_v31
+
+ // 96-127 reserved
+};
+
+enum
+{
+ debugserver_gpr_x0 = 0,
+ debugserver_gpr_x1,
+ debugserver_gpr_x2,
+ debugserver_gpr_x3,
+ debugserver_gpr_x4,
+ debugserver_gpr_x5,
+ debugserver_gpr_x6,
+ debugserver_gpr_x7,
+ debugserver_gpr_x8,
+ debugserver_gpr_x9,
+ debugserver_gpr_x10,
+ debugserver_gpr_x11,
+ debugserver_gpr_x12,
+ debugserver_gpr_x13,
+ debugserver_gpr_x14,
+ debugserver_gpr_x15,
+ debugserver_gpr_x16,
+ debugserver_gpr_x17,
+ debugserver_gpr_x18,
+ debugserver_gpr_x19,
+ debugserver_gpr_x20,
+ debugserver_gpr_x21,
+ debugserver_gpr_x22,
+ debugserver_gpr_x23,
+ debugserver_gpr_x24,
+ debugserver_gpr_x25,
+ debugserver_gpr_x26,
+ debugserver_gpr_x27,
+ debugserver_gpr_x28,
+ debugserver_gpr_fp, // x29
+ debugserver_gpr_lr, // x30
+ debugserver_gpr_sp, // sp aka xsp
+ debugserver_gpr_pc,
+ debugserver_gpr_cpsr,
+ debugserver_vfp_v0,
+ debugserver_vfp_v1,
+ debugserver_vfp_v2,
+ debugserver_vfp_v3,
+ debugserver_vfp_v4,
+ debugserver_vfp_v5,
+ debugserver_vfp_v6,
+ debugserver_vfp_v7,
+ debugserver_vfp_v8,
+ debugserver_vfp_v9,
+ debugserver_vfp_v10,
+ debugserver_vfp_v11,
+ debugserver_vfp_v12,
+ debugserver_vfp_v13,
+ debugserver_vfp_v14,
+ debugserver_vfp_v15,
+ debugserver_vfp_v16,
+ debugserver_vfp_v17,
+ debugserver_vfp_v18,
+ debugserver_vfp_v19,
+ debugserver_vfp_v20,
+ debugserver_vfp_v21,
+ debugserver_vfp_v22,
+ debugserver_vfp_v23,
+ debugserver_vfp_v24,
+ debugserver_vfp_v25,
+ debugserver_vfp_v26,
+ debugserver_vfp_v27,
+ debugserver_vfp_v28,
+ debugserver_vfp_v29,
+ debugserver_vfp_v30,
+ debugserver_vfp_v31,
+ debugserver_vfp_fpsr,
+ debugserver_vfp_fpcr
+};
+
+const char *g_contained_x0[] {"x0", NULL };
+const char *g_contained_x1[] {"x1", NULL };
+const char *g_contained_x2[] {"x2", NULL };
+const char *g_contained_x3[] {"x3", NULL };
+const char *g_contained_x4[] {"x4", NULL };
+const char *g_contained_x5[] {"x5", NULL };
+const char *g_contained_x6[] {"x6", NULL };
+const char *g_contained_x7[] {"x7", NULL };
+const char *g_contained_x8[] {"x8", NULL };
+const char *g_contained_x9[] {"x9", NULL };
+const char *g_contained_x10[] {"x10", NULL };
+const char *g_contained_x11[] {"x11", NULL };
+const char *g_contained_x12[] {"x12", NULL };
+const char *g_contained_x13[] {"x13", NULL };
+const char *g_contained_x14[] {"x14", NULL };
+const char *g_contained_x15[] {"x15", NULL };
+const char *g_contained_x16[] {"x16", NULL };
+const char *g_contained_x17[] {"x17", NULL };
+const char *g_contained_x18[] {"x18", NULL };
+const char *g_contained_x19[] {"x19", NULL };
+const char *g_contained_x20[] {"x20", NULL };
+const char *g_contained_x21[] {"x21", NULL };
+const char *g_contained_x22[] {"x22", NULL };
+const char *g_contained_x23[] {"x23", NULL };
+const char *g_contained_x24[] {"x24", NULL };
+const char *g_contained_x25[] {"x25", NULL };
+const char *g_contained_x26[] {"x26", NULL };
+const char *g_contained_x27[] {"x27", NULL };
+const char *g_contained_x28[] {"x28", NULL };
+
+const char *g_invalidate_x0[] {"x0", "w0", NULL };
+const char *g_invalidate_x1[] {"x1", "w1", NULL };
+const char *g_invalidate_x2[] {"x2", "w2", NULL };
+const char *g_invalidate_x3[] {"x3", "w3", NULL };
+const char *g_invalidate_x4[] {"x4", "w4", NULL };
+const char *g_invalidate_x5[] {"x5", "w5", NULL };
+const char *g_invalidate_x6[] {"x6", "w6", NULL };
+const char *g_invalidate_x7[] {"x7", "w7", NULL };
+const char *g_invalidate_x8[] {"x8", "w8", NULL };
+const char *g_invalidate_x9[] {"x9", "w9", NULL };
+const char *g_invalidate_x10[] {"x10", "w10", NULL };
+const char *g_invalidate_x11[] {"x11", "w11", NULL };
+const char *g_invalidate_x12[] {"x12", "w12", NULL };
+const char *g_invalidate_x13[] {"x13", "w13", NULL };
+const char *g_invalidate_x14[] {"x14", "w14", NULL };
+const char *g_invalidate_x15[] {"x15", "w15", NULL };
+const char *g_invalidate_x16[] {"x16", "w16", NULL };
+const char *g_invalidate_x17[] {"x17", "w17", NULL };
+const char *g_invalidate_x18[] {"x18", "w18", NULL };
+const char *g_invalidate_x19[] {"x19", "w19", NULL };
+const char *g_invalidate_x20[] {"x20", "w20", NULL };
+const char *g_invalidate_x21[] {"x21", "w21", NULL };
+const char *g_invalidate_x22[] {"x22", "w22", NULL };
+const char *g_invalidate_x23[] {"x23", "w23", NULL };
+const char *g_invalidate_x24[] {"x24", "w24", NULL };
+const char *g_invalidate_x25[] {"x25", "w25", NULL };
+const char *g_invalidate_x26[] {"x26", "w26", NULL };
+const char *g_invalidate_x27[] {"x27", "w27", NULL };
+const char *g_invalidate_x28[] {"x28", "w28", NULL };
+
+#define GPR_OFFSET_IDX(idx) (offsetof (DNBArchMachARM64::GPR, __x[idx]))
+
+#define GPR_OFFSET_NAME(reg) (offsetof (DNBArchMachARM64::GPR , __##reg))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR_IDX(idx, reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 8, GPR_OFFSET_IDX(idx) , dwarf_##reg, dwarf_##reg, gen, debugserver_gpr_##reg, NULL, g_invalidate_x##idx }
+#define DEFINE_GPR_NAME(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 8, GPR_OFFSET_NAME(reg), dwarf_##reg, dwarf_##reg, gen, debugserver_gpr_##reg, NULL, NULL }
+#define DEFINE_PSEUDO_GPR_IDX(idx, reg) { e_regSetGPR, gpr_##reg, #reg, NULL, Uint, Hex, 4, 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_x##idx, g_invalidate_x##idx }
+
+//_STRUCT_ARM_THREAD_STATE64
+//{
+// uint64_t x[29]; /* General purpose registers x0-x28 */
+// uint64_t fp; /* Frame pointer x29 */
+// uint64_t lr; /* Link register x30 */
+// uint64_t sp; /* Stack pointer x31 */
+// uint64_t pc; /* Program counter */
+// uint32_t cpsr; /* Current program status register */
+//};
+
+
+// General purpose registers
+const DNBRegisterInfo
+DNBArchMachARM64::g_gpr_registers[] =
+{
+ DEFINE_GPR_IDX ( 0, x0, "arg1", GENERIC_REGNUM_ARG1 ),
+ DEFINE_GPR_IDX ( 1, x1, "arg2", GENERIC_REGNUM_ARG2 ),
+ DEFINE_GPR_IDX ( 2, x2, "arg3", GENERIC_REGNUM_ARG3 ),
+ DEFINE_GPR_IDX ( 3, x3, "arg4", GENERIC_REGNUM_ARG4 ),
+ DEFINE_GPR_IDX ( 4, x4, "arg5", GENERIC_REGNUM_ARG5 ),
+ DEFINE_GPR_IDX ( 5, x5, "arg6", GENERIC_REGNUM_ARG6 ),
+ DEFINE_GPR_IDX ( 6, x6, "arg7", GENERIC_REGNUM_ARG7 ),
+ DEFINE_GPR_IDX ( 7, x7, "arg8", GENERIC_REGNUM_ARG8 ),
+ DEFINE_GPR_IDX ( 8, x8, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX ( 9, x9, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (10, x10, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (11, x11, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (12, x12, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (13, x13, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (14, x14, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (15, x15, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (16, x16, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (17, x17, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (18, x18, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (19, x19, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (20, x20, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (21, x21, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (22, x22, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (23, x23, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (24, x24, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (25, x25, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (26, x26, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (27, x27, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_IDX (28, x28, NULL, INVALID_NUB_REGNUM ),
+ DEFINE_GPR_NAME (fp, "x29", GENERIC_REGNUM_FP),
+ DEFINE_GPR_NAME (lr, "x30", GENERIC_REGNUM_RA),
+ DEFINE_GPR_NAME (sp, "xsp", GENERIC_REGNUM_SP),
+ DEFINE_GPR_NAME (pc, NULL, GENERIC_REGNUM_PC),
+
+ // in armv7 we specify that writing to the CPSR should invalidate r8-12, sp, lr.
+ // this should be specified for arm64 too even though debugserver is only used for
+ // userland debugging.
+ { e_regSetGPR, gpr_cpsr, "cpsr", "flags", Uint, Hex, 4, GPR_OFFSET_NAME(cpsr), dwarf_elr_mode, dwarf_elr_mode, INVALID_NUB_REGNUM, debugserver_gpr_cpsr, NULL, NULL },
+
+ DEFINE_PSEUDO_GPR_IDX ( 0, w0),
+ DEFINE_PSEUDO_GPR_IDX ( 1, w1),
+ DEFINE_PSEUDO_GPR_IDX ( 2, w2),
+ DEFINE_PSEUDO_GPR_IDX ( 3, w3),
+ DEFINE_PSEUDO_GPR_IDX ( 4, w4),
+ DEFINE_PSEUDO_GPR_IDX ( 5, w5),
+ DEFINE_PSEUDO_GPR_IDX ( 6, w6),
+ DEFINE_PSEUDO_GPR_IDX ( 7, w7),
+ DEFINE_PSEUDO_GPR_IDX ( 8, w8),
+ DEFINE_PSEUDO_GPR_IDX ( 9, w9),
+ DEFINE_PSEUDO_GPR_IDX (10, w10),
+ DEFINE_PSEUDO_GPR_IDX (11, w11),
+ DEFINE_PSEUDO_GPR_IDX (12, w12),
+ DEFINE_PSEUDO_GPR_IDX (13, w13),
+ DEFINE_PSEUDO_GPR_IDX (14, w14),
+ DEFINE_PSEUDO_GPR_IDX (15, w15),
+ DEFINE_PSEUDO_GPR_IDX (16, w16),
+ DEFINE_PSEUDO_GPR_IDX (17, w17),
+ DEFINE_PSEUDO_GPR_IDX (18, w18),
+ DEFINE_PSEUDO_GPR_IDX (19, w19),
+ DEFINE_PSEUDO_GPR_IDX (20, w20),
+ DEFINE_PSEUDO_GPR_IDX (21, w21),
+ DEFINE_PSEUDO_GPR_IDX (22, w22),
+ DEFINE_PSEUDO_GPR_IDX (23, w23),
+ DEFINE_PSEUDO_GPR_IDX (24, w24),
+ DEFINE_PSEUDO_GPR_IDX (25, w25),
+ DEFINE_PSEUDO_GPR_IDX (26, w26),
+ DEFINE_PSEUDO_GPR_IDX (27, w27),
+ DEFINE_PSEUDO_GPR_IDX (28, w28)
+};
+
+const char *g_contained_v0[] {"v0", NULL };
+const char *g_contained_v1[] {"v1", NULL };
+const char *g_contained_v2[] {"v2", NULL };
+const char *g_contained_v3[] {"v3", NULL };
+const char *g_contained_v4[] {"v4", NULL };
+const char *g_contained_v5[] {"v5", NULL };
+const char *g_contained_v6[] {"v6", NULL };
+const char *g_contained_v7[] {"v7", NULL };
+const char *g_contained_v8[] {"v8", NULL };
+const char *g_contained_v9[] {"v9", NULL };
+const char *g_contained_v10[] {"v10", NULL };
+const char *g_contained_v11[] {"v11", NULL };
+const char *g_contained_v12[] {"v12", NULL };
+const char *g_contained_v13[] {"v13", NULL };
+const char *g_contained_v14[] {"v14", NULL };
+const char *g_contained_v15[] {"v15", NULL };
+const char *g_contained_v16[] {"v16", NULL };
+const char *g_contained_v17[] {"v17", NULL };
+const char *g_contained_v18[] {"v18", NULL };
+const char *g_contained_v19[] {"v19", NULL };
+const char *g_contained_v20[] {"v20", NULL };
+const char *g_contained_v21[] {"v21", NULL };
+const char *g_contained_v22[] {"v22", NULL };
+const char *g_contained_v23[] {"v23", NULL };
+const char *g_contained_v24[] {"v24", NULL };
+const char *g_contained_v25[] {"v25", NULL };
+const char *g_contained_v26[] {"v26", NULL };
+const char *g_contained_v27[] {"v27", NULL };
+const char *g_contained_v28[] {"v28", NULL };
+const char *g_contained_v29[] {"v29", NULL };
+const char *g_contained_v30[] {"v30", NULL };
+const char *g_contained_v31[] {"v31", NULL };
+
+const char *g_invalidate_v0[] {"v0", "d0", "s0", NULL };
+const char *g_invalidate_v1[] {"v1", "d1", "s1", NULL };
+const char *g_invalidate_v2[] {"v2", "d2", "s2", NULL };
+const char *g_invalidate_v3[] {"v3", "d3", "s3", NULL };
+const char *g_invalidate_v4[] {"v4", "d4", "s4", NULL };
+const char *g_invalidate_v5[] {"v5", "d5", "s5", NULL };
+const char *g_invalidate_v6[] {"v6", "d6", "s6", NULL };
+const char *g_invalidate_v7[] {"v7", "d7", "s7", NULL };
+const char *g_invalidate_v8[] {"v8", "d8", "s8", NULL };
+const char *g_invalidate_v9[] {"v9", "d9", "s9", NULL };
+const char *g_invalidate_v10[] {"v10", "d10", "s10", NULL };
+const char *g_invalidate_v11[] {"v11", "d11", "s11", NULL };
+const char *g_invalidate_v12[] {"v12", "d12", "s12", NULL };
+const char *g_invalidate_v13[] {"v13", "d13", "s13", NULL };
+const char *g_invalidate_v14[] {"v14", "d14", "s14", NULL };
+const char *g_invalidate_v15[] {"v15", "d15", "s15", NULL };
+const char *g_invalidate_v16[] {"v16", "d16", "s16", NULL };
+const char *g_invalidate_v17[] {"v17", "d17", "s17", NULL };
+const char *g_invalidate_v18[] {"v18", "d18", "s18", NULL };
+const char *g_invalidate_v19[] {"v19", "d19", "s19", NULL };
+const char *g_invalidate_v20[] {"v20", "d20", "s20", NULL };
+const char *g_invalidate_v21[] {"v21", "d21", "s21", NULL };
+const char *g_invalidate_v22[] {"v22", "d22", "s22", NULL };
+const char *g_invalidate_v23[] {"v23", "d23", "s23", NULL };
+const char *g_invalidate_v24[] {"v24", "d24", "s24", NULL };
+const char *g_invalidate_v25[] {"v25", "d25", "s25", NULL };
+const char *g_invalidate_v26[] {"v26", "d26", "s26", NULL };
+const char *g_invalidate_v27[] {"v27", "d27", "s27", NULL };
+const char *g_invalidate_v28[] {"v28", "d28", "s28", NULL };
+const char *g_invalidate_v29[] {"v29", "d29", "s29", NULL };
+const char *g_invalidate_v30[] {"v30", "d30", "s30", NULL };
+const char *g_invalidate_v31[] {"v31", "d31", "s31", NULL };
+
+#if defined (__arm64__) || defined (__aarch64__)
+#define VFP_V_OFFSET_IDX(idx) (offsetof (DNBArchMachARM64::FPU, __v) + (idx * 16) + offsetof (DNBArchMachARM64::Context, vfp))
+#else
+#define VFP_V_OFFSET_IDX(idx) (offsetof (DNBArchMachARM64::FPU, opaque) + (idx * 16) + offsetof (DNBArchMachARM64::Context, vfp))
+#endif
+#define VFP_OFFSET_NAME(reg) (offsetof (DNBArchMachARM64::FPU, reg) + offsetof (DNBArchMachARM64::Context, vfp))
+#define EXC_OFFSET(reg) (offsetof (DNBArchMachARM64::EXC, reg) + offsetof (DNBArchMachARM64::Context, exc))
+
+//#define FLOAT_FORMAT Float
+#define DEFINE_VFP_V_IDX(idx) { e_regSetVFP, vfp_v##idx, "v" #idx, "q" #idx, Vector, VectorOfUInt8, 16, VFP_V_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_v##idx, INVALID_NUB_REGNUM, debugserver_vfp_v##idx, NULL, g_invalidate_v##idx }
+#define DEFINE_PSEUDO_VFP_S_IDX(idx) { e_regSetVFP, vfp_s##idx, "s" #idx, NULL, IEEE754, Float, 4, 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_v##idx, g_invalidate_v##idx }
+#define DEFINE_PSEUDO_VFP_D_IDX(idx) { e_regSetVFP, vfp_d##idx, "d" #idx, NULL, IEEE754, Float, 8, 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_v##idx, g_invalidate_v##idx }
+
+// Floating point registers
+const DNBRegisterInfo
+DNBArchMachARM64::g_vfp_registers[] =
+{
+ DEFINE_VFP_V_IDX ( 0),
+ DEFINE_VFP_V_IDX ( 1),
+ DEFINE_VFP_V_IDX ( 2),
+ DEFINE_VFP_V_IDX ( 3),
+ DEFINE_VFP_V_IDX ( 4),
+ DEFINE_VFP_V_IDX ( 5),
+ DEFINE_VFP_V_IDX ( 6),
+ DEFINE_VFP_V_IDX ( 7),
+ DEFINE_VFP_V_IDX ( 8),
+ DEFINE_VFP_V_IDX ( 9),
+ DEFINE_VFP_V_IDX (10),
+ DEFINE_VFP_V_IDX (11),
+ DEFINE_VFP_V_IDX (12),
+ DEFINE_VFP_V_IDX (13),
+ DEFINE_VFP_V_IDX (14),
+ DEFINE_VFP_V_IDX (15),
+ DEFINE_VFP_V_IDX (16),
+ DEFINE_VFP_V_IDX (17),
+ DEFINE_VFP_V_IDX (18),
+ DEFINE_VFP_V_IDX (19),
+ DEFINE_VFP_V_IDX (20),
+ DEFINE_VFP_V_IDX (21),
+ DEFINE_VFP_V_IDX (22),
+ DEFINE_VFP_V_IDX (23),
+ DEFINE_VFP_V_IDX (24),
+ DEFINE_VFP_V_IDX (25),
+ DEFINE_VFP_V_IDX (26),
+ DEFINE_VFP_V_IDX (27),
+ DEFINE_VFP_V_IDX (28),
+ DEFINE_VFP_V_IDX (29),
+ DEFINE_VFP_V_IDX (30),
+ DEFINE_VFP_V_IDX (31),
+ { e_regSetVFP, vfp_fpsr, "fpsr", NULL, Uint, Hex, 4, VFP_V_OFFSET_IDX (32) + 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+ { e_regSetVFP, vfp_fpcr, "fpcr", NULL, Uint, Hex, 4, VFP_V_OFFSET_IDX (32) + 4, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+
+ DEFINE_PSEUDO_VFP_S_IDX (0),
+ DEFINE_PSEUDO_VFP_S_IDX (1),
+ DEFINE_PSEUDO_VFP_S_IDX (2),
+ DEFINE_PSEUDO_VFP_S_IDX (3),
+ DEFINE_PSEUDO_VFP_S_IDX (4),
+ DEFINE_PSEUDO_VFP_S_IDX (5),
+ DEFINE_PSEUDO_VFP_S_IDX (6),
+ DEFINE_PSEUDO_VFP_S_IDX (7),
+ DEFINE_PSEUDO_VFP_S_IDX (8),
+ DEFINE_PSEUDO_VFP_S_IDX (9),
+ DEFINE_PSEUDO_VFP_S_IDX (10),
+ DEFINE_PSEUDO_VFP_S_IDX (11),
+ DEFINE_PSEUDO_VFP_S_IDX (12),
+ DEFINE_PSEUDO_VFP_S_IDX (13),
+ DEFINE_PSEUDO_VFP_S_IDX (14),
+ DEFINE_PSEUDO_VFP_S_IDX (15),
+ DEFINE_PSEUDO_VFP_S_IDX (16),
+ DEFINE_PSEUDO_VFP_S_IDX (17),
+ DEFINE_PSEUDO_VFP_S_IDX (18),
+ DEFINE_PSEUDO_VFP_S_IDX (19),
+ DEFINE_PSEUDO_VFP_S_IDX (20),
+ DEFINE_PSEUDO_VFP_S_IDX (21),
+ DEFINE_PSEUDO_VFP_S_IDX (22),
+ DEFINE_PSEUDO_VFP_S_IDX (23),
+ DEFINE_PSEUDO_VFP_S_IDX (24),
+ DEFINE_PSEUDO_VFP_S_IDX (25),
+ DEFINE_PSEUDO_VFP_S_IDX (26),
+ DEFINE_PSEUDO_VFP_S_IDX (27),
+ DEFINE_PSEUDO_VFP_S_IDX (28),
+ DEFINE_PSEUDO_VFP_S_IDX (29),
+ DEFINE_PSEUDO_VFP_S_IDX (30),
+ DEFINE_PSEUDO_VFP_S_IDX (31),
+
+ DEFINE_PSEUDO_VFP_D_IDX (0),
+ DEFINE_PSEUDO_VFP_D_IDX (1),
+ DEFINE_PSEUDO_VFP_D_IDX (2),
+ DEFINE_PSEUDO_VFP_D_IDX (3),
+ DEFINE_PSEUDO_VFP_D_IDX (4),
+ DEFINE_PSEUDO_VFP_D_IDX (5),
+ DEFINE_PSEUDO_VFP_D_IDX (6),
+ DEFINE_PSEUDO_VFP_D_IDX (7),
+ DEFINE_PSEUDO_VFP_D_IDX (8),
+ DEFINE_PSEUDO_VFP_D_IDX (9),
+ DEFINE_PSEUDO_VFP_D_IDX (10),
+ DEFINE_PSEUDO_VFP_D_IDX (11),
+ DEFINE_PSEUDO_VFP_D_IDX (12),
+ DEFINE_PSEUDO_VFP_D_IDX (13),
+ DEFINE_PSEUDO_VFP_D_IDX (14),
+ DEFINE_PSEUDO_VFP_D_IDX (15),
+ DEFINE_PSEUDO_VFP_D_IDX (16),
+ DEFINE_PSEUDO_VFP_D_IDX (17),
+ DEFINE_PSEUDO_VFP_D_IDX (18),
+ DEFINE_PSEUDO_VFP_D_IDX (19),
+ DEFINE_PSEUDO_VFP_D_IDX (20),
+ DEFINE_PSEUDO_VFP_D_IDX (21),
+ DEFINE_PSEUDO_VFP_D_IDX (22),
+ DEFINE_PSEUDO_VFP_D_IDX (23),
+ DEFINE_PSEUDO_VFP_D_IDX (24),
+ DEFINE_PSEUDO_VFP_D_IDX (25),
+ DEFINE_PSEUDO_VFP_D_IDX (26),
+ DEFINE_PSEUDO_VFP_D_IDX (27),
+ DEFINE_PSEUDO_VFP_D_IDX (28),
+ DEFINE_PSEUDO_VFP_D_IDX (29),
+ DEFINE_PSEUDO_VFP_D_IDX (30),
+ DEFINE_PSEUDO_VFP_D_IDX (31)
+
+};
+
+
+//_STRUCT_ARM_EXCEPTION_STATE64
+//{
+// uint64_t far; /* Virtual Fault Address */
+// uint32_t esr; /* Exception syndrome */
+// uint32_t exception; /* number of arm exception taken */
+//};
+
+// Exception registers
+const DNBRegisterInfo
+DNBArchMachARM64::g_exc_registers[] =
+{
+ { e_regSetEXC, exc_far , "far" , NULL, Uint, Hex, 8, EXC_OFFSET(__far) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+ { e_regSetEXC, exc_esr , "esr" , NULL, Uint, Hex, 4, EXC_OFFSET(__esr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+ { e_regSetEXC, exc_exception , "exception" , NULL, Uint, Hex, 4, EXC_OFFSET(__exception) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }
+};
+
+// Number of registers in each register set
+const size_t DNBArchMachARM64::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM64::k_num_vfp_registers = sizeof(g_vfp_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM64::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM64::k_num_all_registers = k_num_gpr_registers + k_num_vfp_registers + k_num_exc_registers;
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+const DNBRegisterSetInfo
+DNBArchMachARM64::g_reg_sets[] =
+{
+ { "ARM64 Registers", NULL, k_num_all_registers },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_vfp_registers, k_num_vfp_registers },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+// Total number of register sets for this architecture
+const size_t DNBArchMachARM64::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo);
+
+
+const DNBRegisterSetInfo *
+DNBArchMachARM64::GetRegisterSetInfo(nub_size_t *num_reg_sets)
+{
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool
+DNBArchMachARM64::FixGenericRegisterNumber (uint32_t &set, uint32_t &reg)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_fp;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = gpr_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_cpsr;
+ break;
+
+ case GENERIC_REGNUM_ARG1:
+ case GENERIC_REGNUM_ARG2:
+ case GENERIC_REGNUM_ARG3:
+ case GENERIC_REGNUM_ARG4:
+ case GENERIC_REGNUM_ARG5:
+ case GENERIC_REGNUM_ARG6:
+ set = e_regSetGPR;
+ reg = gpr_x0 + reg - GENERIC_REGNUM_ARG1;
+ break;
+
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+bool
+DNBArchMachARM64::GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value)
+{
+ if (!FixGenericRegisterNumber (set, reg))
+ return false;
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg <= gpr_pc)
+ {
+ value->value.uint64 = m_state.context.gpr.__x[reg];
+ return true;
+ }
+ else if (reg == gpr_cpsr)
+ {
+ value->value.uint32 = m_state.context.gpr.__cpsr;
+ return true;
+ }
+ break;
+
+ case e_regSetVFP:
+
+ if (reg >= vfp_v0 && reg <= vfp_v31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_v0], 16);
+#else
+ memcpy (&value->value.v_uint8, ((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_v0) * 16), 16);
+#endif
+ return true;
+ }
+ else if (reg == vfp_fpsr)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&value->value.uint32, &m_state.context.vfp.__fpsr, 4);
+#else
+ memcpy (&value->value.uint32, ((uint8_t *) &m_state.context.vfp.opaque) + (32 * 16) + 0, 4);
+#endif
+ return true;
+ }
+ else if (reg == vfp_fpcr)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&value->value.uint32, &m_state.context.vfp.__fpcr, 4);
+#else
+ memcpy (&value->value.uint32, ((uint8_t *) &m_state.context.vfp.opaque) + (32 * 16) + 4, 4);
+#endif
+ return true;
+ }
+ else if (reg >= vfp_s0 && reg <= vfp_s31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_s0], 4);
+#else
+ memcpy (&value->value.v_uint8, ((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_s0) * 16), 4);
+#endif
+ return true;
+ }
+ else if (reg >= vfp_d0 && reg <= vfp_d31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_d0], 8);
+#else
+ memcpy (&value->value.v_uint8, ((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_d0) * 16), 8);
+#endif
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg == exc_far)
+ {
+ value->value.uint64 = m_state.context.exc.__far;
+ return true;
+ }
+ else if (reg == exc_esr)
+ {
+ value->value.uint32 = m_state.context.exc.__esr;
+ return true;
+ }
+ else if (reg == exc_exception)
+ {
+ value->value.uint32 = m_state.context.exc.__exception;
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool
+DNBArchMachARM64::SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value)
+{
+ if (!FixGenericRegisterNumber (set, reg))
+ return false;
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg <= gpr_pc)
+ {
+ m_state.context.gpr.__x[reg] = value->value.uint64;
+ success = true;
+ }
+ else if (reg == gpr_cpsr)
+ {
+ m_state.context.gpr.__cpsr = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetVFP:
+ if (reg >= vfp_v0 && reg <= vfp_v31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&m_state.context.vfp.__v[reg - vfp_v0], &value->value.v_uint8, 16);
+#else
+ memcpy (((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_v0) * 16), &value->value.v_uint8, 16);
+#endif
+ success = true;
+ }
+ else if (reg == vfp_fpsr)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&m_state.context.vfp.__fpsr, &value->value.uint32, 4);
+#else
+ memcpy (((uint8_t *) &m_state.context.vfp.opaque) + (32 * 16) + 0, &value->value.uint32, 4);
+#endif
+ success = true;
+ }
+ else if (reg == vfp_fpcr)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&m_state.context.vfp.__fpcr, &value->value.uint32, 4);
+#else
+ memcpy (((uint8_t *) m_state.context.vfp.opaque) + (32 * 16) + 4, &value->value.uint32, 4);
+#endif
+ success = true;
+ }
+ else if (reg >= vfp_s0 && reg <= vfp_s31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&m_state.context.vfp.__v[reg - vfp_s0], &value->value.v_uint8, 4);
+#else
+ memcpy (((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_s0) * 16), &value->value.v_uint8, 4);
+#endif
+ success = true;
+ }
+ else if (reg >= vfp_d0 && reg <= vfp_d31)
+ {
+#if defined (__arm64__) || defined (__aarch64__)
+ memcpy (&m_state.context.vfp.__v[reg - vfp_d0], &value->value.v_uint8, 8);
+#else
+ memcpy (((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_d0) * 16), &value->value.v_uint8, 8);
+#endif
+ success = true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg == exc_far)
+ {
+ m_state.context.exc.__far = value->value.uint64;
+ success = true;
+ }
+ else if (reg == exc_esr)
+ {
+ m_state.context.exc.__esr = value->value.uint32;
+ success = true;
+ }
+ else if (reg == exc_exception)
+ {
+ m_state.context.exc.__exception = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+
+ }
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+kern_return_t
+DNBArchMachARM64::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL: return GetGPRState(force) |
+ GetVFPState(force) |
+ GetEXCState(force) |
+ GetDBGState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetVFP: return GetVFPState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ case e_regSetDBG: return GetDBGState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchMachARM64::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() |
+ SetVFPState() |
+ SetEXCState() |
+ SetDBGState(false);
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetVFP: return SetVFPState();
+ case e_regSetEXC: return SetEXCState();
+ case e_regSetDBG: return SetDBGState(false);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchMachARM64::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+nub_size_t
+DNBArchMachARM64::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context.gpr) +
+ sizeof (m_state.context.vfp) +
+ sizeof (m_state.context.exc);
+
+ if (buf && buf_len)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ bool force = false;
+ if (GetGPRState(force) | GetVFPState(force) | GetEXCState(force))
+ return 0;
+
+ // Copy each struct individually to avoid any padding that might be between the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy (p, &m_state.context.gpr, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy (p, &m_state.context.vfp, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy (p, &m_state.context.exc, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::GetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t
+DNBArchMachARM64::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context.gpr) +
+ sizeof (m_state.context.vfp) +
+ sizeof (m_state.context.exc);
+
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ // Copy each struct individually to avoid any padding that might be between the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy (&m_state.context.gpr, p, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy (&m_state.context.vfp, p, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy (&m_state.context.exc, p, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+ SetGPRState();
+ SetVFPState();
+ SetEXCState();
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::SetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size);
+ return size;
+}
+
+uint32_t
+DNBArchMachARM64::SaveRegisterState ()
+{
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ // Always re-read the registers because above we call thread_abort_safely();
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::SaveRegisterState () error: GPR regs failed to read: %u ", kret);
+ }
+ else if ((kret = GetVFPState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::SaveRegisterState () error: %s regs failed to read: %u", "VFP", kret);
+ }
+ else
+ {
+ const uint32_t save_id = GetNextRegisterStateSaveID ();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return UINT32_MAX;
+}
+
+bool
+DNBArchMachARM64::RestoreRegisterState (uint32_t save_id)
+{
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end())
+ {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.vfp = pos->second.vfp;
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret);
+ success = false;
+ }
+ else if ((kret = SetVFPState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, "VFP", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+
+#endif // #if defined (ARM_THREAD_STATE64_COUNT)
+#endif // #if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
diff --git a/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h
new file mode 100644
index 000000000000..7e68e411a765
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h
@@ -0,0 +1,272 @@
+//===-- DNBArchMachARM64.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __DNBArchImplARM64_h__
+#define __DNBArchImplARM64_h__
+
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+
+#include <map>
+#include <mach/thread_status.h>
+
+#if defined (ARM_THREAD_STATE64_COUNT)
+
+#include "DNBArch.h"
+
+class MachThread;
+
+class DNBArchMachARM64 : public DNBArchProtocol
+{
+public:
+ enum { kMaxNumThumbITBreakpoints = 4 };
+
+ DNBArchMachARM64(MachThread *thread) :
+ m_thread(thread),
+ m_state(),
+ m_disabled_watchpoints(),
+ m_watchpoint_hw_index(-1),
+ m_watchpoint_did_occur(false),
+ m_watchpoint_resume_single_step_enabled(false),
+ m_saved_register_states()
+ {
+ m_disabled_watchpoints.resize (16);
+ memset(&m_dbg_save, 0, sizeof(m_dbg_save));
+ }
+
+ virtual ~DNBArchMachARM64()
+ {
+ }
+
+ static void Initialize();
+ static const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState ();
+ virtual bool RestoreRegisterState (uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data& exc);
+
+ static DNBArchProtocol *Create (MachThread *thread);
+ static const uint8_t * SoftwareBreakpointOpcode (nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint_helper (uint32_t hw_break_index, bool also_set_on_task);
+
+protected:
+
+
+ kern_return_t EnableHardwareSingleStep (bool enable);
+ static bool FixGenericRegisterNumber (uint32_t &set, uint32_t &reg);
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR, // ARM_THREAD_STATE64,
+ e_regSetVFP, // ARM_NEON_STATE64,
+ e_regSetEXC, // ARM_EXCEPTION_STATE64,
+ e_regSetDBG, // ARM_DEBUG_STATE64,
+ kNumRegisterSets
+ } RegisterSet;
+
+ enum
+ {
+ e_regSetGPRCount = ARM_THREAD_STATE64_COUNT,
+ e_regSetVFPCount = ARM_NEON_STATE64_COUNT,
+ e_regSetEXCCount = ARM_EXCEPTION_STATE64_COUNT,
+ e_regSetDBGCount = ARM_DEBUG_STATE64_COUNT,
+ };
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ typedef arm_thread_state64_t GPR;
+ typedef arm_neon_state64_t FPU;
+ typedef arm_exception_state64_t EXC;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_vfp_registers[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets[];
+
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_vfp_registers;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers;
+ static const size_t k_num_register_sets;
+
+ struct Context
+ {
+ GPR gpr;
+ FPU vfp;
+ EXC exc;
+ };
+
+ struct State
+ {
+ Context context;
+ arm_debug_state64_t dbg;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t vfp_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ vfp_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+ void InvalidateRegisterSetState(int set)
+ {
+ SetError (set, Read, -1);
+ }
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (e_regSetALL, Read, -1);
+ }
+
+ kern_return_t GetError (int set, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL: return gpr_errs[err_idx] |
+ vfp_errs[err_idx] |
+ exc_errs[err_idx] |
+ dbg_errs[err_idx] ;
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetVFP: return vfp_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ //case e_regSetDBG: return dbg_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+ bool SetError (int set, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] = err;
+ vfp_errs[err_idx] = err;
+ dbg_errs[err_idx] = err;
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVFP:
+ vfp_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+// case e_regSetDBG:
+// dbg_errs[err_idx] = err;
+// return true;
+ default: break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetVFPState (bool force);
+ kern_return_t GetEXCState (bool force);
+ kern_return_t GetDBGState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetVFPState ();
+ kern_return_t SetEXCState ();
+ kern_return_t SetDBGState (bool also_set_on_task);
+
+ // Helper functions for watchpoint implementaions.
+
+ typedef arm_debug_state64_t DBG;
+
+ void ClearWatchpointOccurred();
+ bool HasWatchpointOccurred();
+ bool IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index);
+ nub_addr_t GetWatchpointAddressByIndex (uint32_t hw_index);
+ nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+ virtual bool ReenableHardwareWatchpoint (uint32_t hw_break_index);
+ virtual bool ReenableHardwareWatchpoint_helper (uint32_t hw_break_index);
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+
+ class disabled_watchpoint {
+ public:
+ disabled_watchpoint () { addr = 0; control = 0; }
+ nub_addr_t addr;
+ uint32_t control;
+ };
+
+protected:
+ MachThread * m_thread;
+ State m_state;
+ arm_debug_state64_t m_dbg_save;
+
+ // arm64 doesn't keep the disabled watchpoint values in the debug register context like armv7;
+ // we need to save them aside when we disable them temporarily.
+ std::vector<disabled_watchpoint> m_disabled_watchpoints;
+
+ // The following member variables should be updated atomically.
+ int32_t m_watchpoint_hw_index;
+ bool m_watchpoint_did_occur;
+ bool m_watchpoint_resume_single_step_enabled;
+
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (ARM_THREAD_STATE64_COUNT)
+#endif // #if defined (__arm__)
+#endif // #ifndef __DNBArchImplARM64_h__
diff --git a/tools/debugserver/source/MacOSX/dbgnub-mig.defs b/tools/debugserver/source/MacOSX/dbgnub-mig.defs
new file mode 100644
index 000000000000..cd5be1700704
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/dbgnub-mig.defs
@@ -0,0 +1,5 @@
+/*
+ * nub.defs
+ */
+
+#import <mach/mach_exc.defs>
diff --git a/tools/debugserver/source/MacOSX/i386/CMakeLists.txt b/tools/debugserver/source/MacOSX/i386/CMakeLists.txt
new file mode 100644
index 000000000000..dee2c1ea96b0
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/i386/CMakeLists.txt
@@ -0,0 +1,4 @@
+include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source)
+add_library(lldbDebugserverMacOSX_I386
+ DNBArchImplI386.cpp
+ )
diff --git a/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp
new file mode 100644
index 000000000000..93d4d894300b
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp
@@ -0,0 +1,1901 @@
+//===-- DNBArchImplI386.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#include <sys/cdefs.h>
+
+#include "MacOSX/i386/DNBArchImplI386.h"
+#include "DNBLog.h"
+#include "MachThread.h"
+#include "MachProcess.h"
+
+extern "C" bool CPUHasAVX(); // Defined over in DNBArchImplX86_64.cpp
+
+#if defined (LLDB_DEBUGSERVER_RELEASE) || defined (LLDB_DEBUGSERVER_DEBUG)
+enum debugState {
+ debugStateUnknown,
+ debugStateOff,
+ debugStateOn
+};
+
+static debugState sFPUDebugState = debugStateUnknown;
+static debugState sAVXForceState = debugStateUnknown;
+
+static bool DebugFPURegs ()
+{
+ if (sFPUDebugState == debugStateUnknown)
+ {
+ if (getenv("DNB_DEBUG_FPU_REGS"))
+ sFPUDebugState = debugStateOn;
+ else
+ sFPUDebugState = debugStateOff;
+ }
+
+ return (sFPUDebugState == debugStateOn);
+}
+
+static bool ForceAVXRegs ()
+{
+ if (sFPUDebugState == debugStateUnknown)
+ {
+ if (getenv("DNB_DEBUG_X86_FORCE_AVX_REGS"))
+ sAVXForceState = debugStateOn;
+ else
+ sAVXForceState = debugStateOff;
+ }
+
+ return (sAVXForceState == debugStateOn);
+}
+
+#define DEBUG_FPU_REGS (DebugFPURegs())
+#define FORCE_AVX_REGS (ForceAVXRegs())
+#else
+#define DEBUG_FPU_REGS (0)
+#define FORCE_AVX_REGS (0)
+#endif
+
+enum
+{
+ gpr_eax = 0,
+ gpr_ebx = 1,
+ gpr_ecx = 2,
+ gpr_edx = 3,
+ gpr_edi = 4,
+ gpr_esi = 5,
+ gpr_ebp = 6,
+ gpr_esp = 7,
+ gpr_ss = 8,
+ gpr_eflags = 9,
+ gpr_eip = 10,
+ gpr_cs = 11,
+ gpr_ds = 12,
+ gpr_es = 13,
+ gpr_fs = 14,
+ gpr_gs = 15,
+ gpr_ax ,
+ gpr_bx ,
+ gpr_cx ,
+ gpr_dx ,
+ gpr_di ,
+ gpr_si ,
+ gpr_bp ,
+ gpr_sp ,
+ gpr_ah ,
+ gpr_bh ,
+ gpr_ch ,
+ gpr_dh ,
+ gpr_al ,
+ gpr_bl ,
+ gpr_cl ,
+ gpr_dl ,
+ gpr_dil,
+ gpr_sil,
+ gpr_bpl,
+ gpr_spl,
+ k_num_gpr_regs
+};
+
+enum {
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_ymm0,
+ fpu_ymm1,
+ fpu_ymm2,
+ fpu_ymm3,
+ fpu_ymm4,
+ fpu_ymm5,
+ fpu_ymm6,
+ fpu_ymm7,
+ k_num_fpu_regs,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum {
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+ k_num_exc_regs,
+};
+
+
+enum
+{
+ ehframe_eax = 0,
+ ehframe_ecx,
+ ehframe_edx,
+ ehframe_ebx,
+
+ // On i386 Darwin the eh_frame register numbers for ebp and esp are reversed from DWARF.
+ // It's due to an ancient compiler bug in the output of the eh_frame.
+ // Specifically, on i386 darwin eh_frame, 4 is ebp, 5 is esp.
+ // On i386 darwin debug_frame (and debug_info), 4 is esp, 5 is ebp.
+ ehframe_ebp,
+ ehframe_esp,
+ ehframe_esi,
+ ehframe_edi,
+ ehframe_eip,
+ ehframe_eflags
+};
+
+enum
+{
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+ dwarf_stmm0 = 11,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_ymm0 = dwarf_xmm0,
+ dwarf_ymm1 = dwarf_xmm1,
+ dwarf_ymm2 = dwarf_xmm2,
+ dwarf_ymm3 = dwarf_xmm3,
+ dwarf_ymm4 = dwarf_xmm4,
+ dwarf_ymm5 = dwarf_xmm5,
+ dwarf_ymm6 = dwarf_xmm6,
+ dwarf_ymm7 = dwarf_xmm7,
+};
+
+enum
+{
+ debugserver_eax = 0,
+ debugserver_ecx = 1,
+ debugserver_edx = 2,
+ debugserver_ebx = 3,
+ debugserver_esp = 4,
+ debugserver_ebp = 5,
+ debugserver_esi = 6,
+ debugserver_edi = 7,
+ debugserver_eip = 8,
+ debugserver_eflags = 9,
+ debugserver_cs = 10,
+ debugserver_ss = 11,
+ debugserver_ds = 12,
+ debugserver_es = 13,
+ debugserver_fs = 14,
+ debugserver_gs = 15,
+ debugserver_stmm0 = 16,
+ debugserver_stmm1 = 17,
+ debugserver_stmm2 = 18,
+ debugserver_stmm3 = 19,
+ debugserver_stmm4 = 20,
+ debugserver_stmm5 = 21,
+ debugserver_stmm6 = 22,
+ debugserver_stmm7 = 23,
+ debugserver_fctrl = 24, debugserver_fcw = debugserver_fctrl,
+ debugserver_fstat = 25, debugserver_fsw = debugserver_fstat,
+ debugserver_ftag = 26, debugserver_ftw = debugserver_ftag,
+ debugserver_fiseg = 27, debugserver_fpu_cs = debugserver_fiseg,
+ debugserver_fioff = 28, debugserver_ip = debugserver_fioff,
+ debugserver_foseg = 29, debugserver_fpu_ds = debugserver_foseg,
+ debugserver_fooff = 30, debugserver_dp = debugserver_fooff,
+ debugserver_fop = 31,
+ debugserver_xmm0 = 32,
+ debugserver_xmm1 = 33,
+ debugserver_xmm2 = 34,
+ debugserver_xmm3 = 35,
+ debugserver_xmm4 = 36,
+ debugserver_xmm5 = 37,
+ debugserver_xmm6 = 38,
+ debugserver_xmm7 = 39,
+ debugserver_mxcsr = 40,
+ debugserver_mm0 = 41,
+ debugserver_mm1 = 42,
+ debugserver_mm2 = 43,
+ debugserver_mm3 = 44,
+ debugserver_mm4 = 45,
+ debugserver_mm5 = 46,
+ debugserver_mm6 = 47,
+ debugserver_mm7 = 48,
+ debugserver_ymm0 = debugserver_xmm0,
+ debugserver_ymm1 = debugserver_xmm1,
+ debugserver_ymm2 = debugserver_xmm2,
+ debugserver_ymm3 = debugserver_xmm3,
+ debugserver_ymm4 = debugserver_xmm4,
+ debugserver_ymm5 = debugserver_xmm5,
+ debugserver_ymm6 = debugserver_xmm6,
+ debugserver_ymm7 = debugserver_xmm7
+};
+
+uint64_t
+DNBArchImplI386::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__eip;
+ return failValue;
+}
+
+kern_return_t
+DNBArchImplI386::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.context.gpr.__eip = static_cast<uint32_t>(value);
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchImplI386::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__esp;
+ return failValue;
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+//#define SET_GPR(reg) m_state.context.gpr.__##reg = gpr_##reg
+
+kern_return_t
+DNBArchImplI386::GetGPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetGPR, Read))
+ {
+#if DEBUG_GPR_VALUES
+ SET_GPR(eax);
+ SET_GPR(ebx);
+ SET_GPR(ecx);
+ SET_GPR(edx);
+ SET_GPR(edi);
+ SET_GPR(esi);
+ SET_GPR(ebp);
+ SET_GPR(esp);
+ SET_GPR(ss);
+ SET_GPR(eflags);
+ SET_GPR(eip);
+ SET_GPR(cs);
+ SET_GPR(ds);
+ SET_GPR(es);
+ SET_GPR(fs);
+ SET_GPR(gs);
+ m_state.SetError(e_regSetGPR, Read, 0);
+#else
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->MachPortNumber(), __i386_THREAD_STATE, (thread_state_t)&m_state.context.gpr, &count));
+#endif
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_FPU_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t
+DNBArchImplI386::GetFPUState(bool force)
+{
+ if (force || m_state.GetError(e_regSetFPU, Read))
+ {
+ if (DEBUG_FPU_REGS)
+ {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ m_state.context.fpu.avx.__fpu_reserved[0] = -1;
+ m_state.context.fpu.avx.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.avx.__fpu_ftw = 1;
+ m_state.context.fpu.avx.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.avx.__fpu_fop = 2;
+ m_state.context.fpu.avx.__fpu_ip = 3;
+ m_state.context.fpu.avx.__fpu_cs = 4;
+ m_state.context.fpu.avx.__fpu_rsrv2 = 5;
+ m_state.context.fpu.avx.__fpu_dp = 6;
+ m_state.context.fpu.avx.__fpu_ds = 7;
+ m_state.context.fpu.avx.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.avx.__fpu_mxcsr = 8;
+ m_state.context.fpu.avx.__fpu_mxcsrmask = 9;
+ int i;
+ for (i=0; i<16; ++i)
+ {
+ if (i<10)
+ {
+ m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = 'h';
+ }
+ else
+ {
+ m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg[i] = '0';
+ m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg[i] = '1';
+ m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg[i] = '2';
+ m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg[i] = '3';
+ m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg[i] = '4';
+ m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg[i] = '5';
+ m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg[i] = '6';
+ m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg[i] = '7';
+ }
+ for (i=0; i<sizeof(m_state.context.fpu.avx.__fpu_rsrv4); ++i)
+ m_state.context.fpu.avx.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_reserved1 = -1;
+ for (i=0; i<sizeof(m_state.context.fpu.avx.__avx_reserved1); ++i)
+ m_state.context.fpu.avx.__avx_reserved1[i] = INT8_MIN;
+
+ for (i = 0; i < 16; ++i)
+ {
+ m_state.context.fpu.avx.__fpu_ymmh0.__xmm_reg[i] = '0';
+ m_state.context.fpu.avx.__fpu_ymmh1.__xmm_reg[i] = '1';
+ m_state.context.fpu.avx.__fpu_ymmh2.__xmm_reg[i] = '2';
+ m_state.context.fpu.avx.__fpu_ymmh3.__xmm_reg[i] = '3';
+ m_state.context.fpu.avx.__fpu_ymmh4.__xmm_reg[i] = '4';
+ m_state.context.fpu.avx.__fpu_ymmh5.__xmm_reg[i] = '5';
+ m_state.context.fpu.avx.__fpu_ymmh6.__xmm_reg[i] = '6';
+ m_state.context.fpu.avx.__fpu_ymmh7.__xmm_reg[i] = '7';
+ }
+ }
+ else
+ {
+ m_state.context.fpu.no_avx.__fpu_reserved[0] = -1;
+ m_state.context.fpu.no_avx.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.no_avx.__fpu_ftw = 1;
+ m_state.context.fpu.no_avx.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.no_avx.__fpu_fop = 2;
+ m_state.context.fpu.no_avx.__fpu_ip = 3;
+ m_state.context.fpu.no_avx.__fpu_cs = 4;
+ m_state.context.fpu.no_avx.__fpu_rsrv2 = 5;
+ m_state.context.fpu.no_avx.__fpu_dp = 6;
+ m_state.context.fpu.no_avx.__fpu_ds = 7;
+ m_state.context.fpu.no_avx.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.no_avx.__fpu_mxcsr = 8;
+ m_state.context.fpu.no_avx.__fpu_mxcsrmask = 9;
+ int i;
+ for (i=0; i<16; ++i)
+ {
+ if (i<10)
+ {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = 'h';
+ }
+ else
+ {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg[i] = '0';
+ m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg[i] = '1';
+ m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg[i] = '2';
+ m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg[i] = '3';
+ m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg[i] = '4';
+ m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg[i] = '5';
+ m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg[i] = '6';
+ m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg[i] = '7';
+ }
+ for (i=0; i<sizeof(m_state.context.fpu.avx.__fpu_rsrv4); ++i)
+ m_state.context.fpu.no_avx.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_reserved1 = -1;
+ }
+ m_state.SetError(e_regSetFPU, Read, 0);
+ }
+ else
+ {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeAVX;
+ m_state.SetError (e_regSetFPU, Read, ::thread_get_state(m_thread->MachPortNumber(), __i386_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, &count));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &avx, %u (%u passed in)) => 0x%8.8x",
+ m_thread->MachPortNumber(), __i386_AVX_STATE, count, e_regSetWordSizeAVX,
+ m_state.GetError(e_regSetFPU, Read));
+ }
+ else
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeFPU;
+ m_state.SetError(e_regSetFPU, Read, ::thread_get_state(m_thread->MachPortNumber(), __i386_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, &count));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &fpu, %u (%u passed in) => 0x%8.8x",
+ m_thread->MachPortNumber(), __i386_FLOAT_STATE, count, e_regSetWordSizeFPU,
+ m_state.GetError(e_regSetFPU, Read));
+ }
+ }
+ }
+ return m_state.GetError(e_regSetFPU, Read);
+}
+
+kern_return_t
+DNBArchImplI386::GetEXCState(bool force)
+{
+ if (force || m_state.GetError(e_regSetEXC, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->MachPortNumber(), __i386_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t
+DNBArchImplI386::SetGPRState()
+{
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+
+ m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_THREAD_STATE, (thread_state_t)&m_state.context.gpr, e_regSetWordSizeGPR));
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t
+DNBArchImplI386::SetFPUState()
+{
+ if (DEBUG_FPU_REGS)
+ {
+ m_state.SetError(e_regSetFPU, Write, 0);
+ return m_state.GetError(e_regSetFPU, Write);
+ }
+ else
+ {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, e_regSetWordSizeAVX));
+ else
+ m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, e_regSetWordSizeFPU));
+ return m_state.GetError(e_regSetFPU, Write);
+ }
+}
+
+kern_return_t
+DNBArchImplI386::SetEXCState()
+{
+ m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+kern_return_t
+DNBArchImplI386::GetDBGState(bool force)
+{
+ if (force || m_state.GetError(e_regSetDBG, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeDBG;
+ m_state.SetError(e_regSetDBG, Read, ::thread_get_state(m_thread->MachPortNumber(), __i386_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, &count));
+ }
+ return m_state.GetError(e_regSetDBG, Read);
+}
+
+kern_return_t
+DNBArchImplI386::SetDBGState(bool also_set_on_task)
+{
+ m_state.SetError(e_regSetDBG, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG));
+ if (also_set_on_task)
+ {
+ kern_return_t kret = ::task_set_state(m_thread->Process()->Task().TaskPort(), __i386_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG);
+ if (kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::SetDBGState failed to set debug control register state: 0x%8.8x.", kret);
+
+ }
+ return m_state.GetError(e_regSetDBG, Write);
+}
+
+void
+DNBArchImplI386::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ // This is the primary thread, let the arch do anything it needs
+ EnableHardwareSingleStep(true);
+ }
+
+ // Reset the debug status register, if necessary, before we resume.
+ kern_return_t kret = GetDBGState(false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::ThreadWillResume() GetDBGState() => 0x%8.8x.", kret);
+ if (kret != KERN_SUCCESS)
+ return;
+
+ DBG &debug_state = m_state.context.dbg;
+ bool need_reset = false;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ if (IsWatchpointHit(debug_state, i))
+ need_reset = true;
+
+ if (need_reset)
+ {
+ ClearWatchpointHits(debug_state);
+ kret = SetDBGState(false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,"DNBArchImplI386::ThreadWillResume() SetDBGState() => 0x%8.8x.", kret);
+ }
+}
+
+bool
+DNBArchImplI386::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool
+DNBArchImplI386::NotifyException(MachException::Data& exc)
+{
+ switch (exc.exc_type)
+ {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2)
+ {
+ // exc_code = EXC_I386_BPT
+ //
+ nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS);
+ if (pc != INVALID_NUB_ADDRESS && pc > 0)
+ {
+ pc -= 1;
+ // Check for a breakpoint at one byte prior to the current PC value
+ // since the PC will be just past the trap.
+
+ DNBBreakpoint *bp = m_thread->Process()->Breakpoints().FindByAddress(pc);
+ if (bp)
+ {
+ // Backup the PC for i386 since the trap was taken and the PC
+ // is at the address following the single byte trap instruction.
+ if (m_state.context.gpr.__eip > 0)
+ {
+ m_state.context.gpr.__eip = static_cast<uint32_t>(pc);
+ // Write the new PC back out
+ SetGPRState ();
+ }
+ }
+ return true;
+ }
+ }
+ else if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 1)
+ {
+ // exc_code = EXC_I386_SGL
+ //
+ // Check whether this corresponds to a watchpoint hit event.
+ // If yes, set the exc_sub_code to the data break address.
+ nub_addr_t addr = 0;
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+ if (hw_index != INVALID_NUB_HW_INDEX)
+ {
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+uint32_t
+DNBArchImplI386::NumSupportedHardwareWatchpoints()
+{
+ // Available debug address registers: dr0, dr1, dr2, dr3.
+ return 4;
+}
+
+static uint32_t
+size_and_rw_bits(nub_size_t size, bool read, bool write)
+{
+ uint32_t rw;
+ if (read) {
+ rw = 0x3; // READ or READ/WRITE
+ } else if (write) {
+ rw = 0x1; // WRITE
+ } else {
+ assert(0 && "read and write cannot both be false");
+ }
+
+ switch (size) {
+ case 1:
+ return rw;
+ case 2:
+ return (0x1 << 2) | rw;
+ case 4:
+ return (0x3 << 2) | rw;
+ case 8:
+ return (0x2 << 2) | rw;
+ }
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ return 0;
+}
+
+void
+DNBArchImplI386::SetWatchpoint(DBG &debug_state, uint32_t hw_index, nub_addr_t addr, nub_size_t size, bool read, bool write)
+{
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ debug_state.__dr7 |= (1 << (2*hw_index) |
+ size_and_rw_bits(size, read, write) << (16+4*hw_index));
+ uint32_t addr_32 = addr & 0xffffffff;
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = addr_32; break;
+ case 1:
+ debug_state.__dr1 = addr_32; break;
+ case 2:
+ debug_state.__dr2 = addr_32; break;
+ case 3:
+ debug_state.__dr3 = addr_32; break;
+ default:
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+void
+DNBArchImplI386::ClearWatchpoint(DBG &debug_state, uint32_t hw_index)
+{
+ debug_state.__dr7 &= ~(3 << (2*hw_index));
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = 0; break;
+ case 1:
+ debug_state.__dr1 = 0; break;
+ case 2:
+ debug_state.__dr2 = 0; break;
+ case 3:
+ debug_state.__dr3 = 0; break;
+ default:
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+bool
+DNBArchImplI386::IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index)
+{
+ // Check dr7 (debug control register) for local/global enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ return (debug_state.__dr7 & (3 << (2*hw_index))) == 0;
+}
+
+// Resets local copy of debug status register to wait for the next debug exception.
+void
+DNBArchImplI386::ClearWatchpointHits(DBG &debug_state)
+{
+ // See also IsWatchpointHit().
+ debug_state.__dr6 = 0;
+ return;
+}
+
+bool
+DNBArchImplI386::IsWatchpointHit(const DBG &debug_state, uint32_t hw_index)
+{
+ // Check dr6 (debug status register) whether a watchpoint hits:
+ // is watchpoint hit?
+ // |
+ // v
+ // dr0 -> bits{0}
+ // dr1 -> bits{1}
+ // dr2 -> bits{2}
+ // dr3 -> bits{3}
+ return (debug_state.__dr6 & (1 << hw_index));
+}
+
+nub_addr_t
+DNBArchImplI386::GetWatchAddress(const DBG &debug_state, uint32_t hw_index)
+{
+ switch (hw_index) {
+ case 0:
+ return debug_state.__dr0;
+ case 1:
+ return debug_state.__dr1;
+ case 2:
+ return debug_state.__dr2;
+ case 3:
+ return debug_state.__dr3;
+ }
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ return 0;
+}
+
+bool
+DNBArchImplI386::StartTransForHWP()
+{
+ if (m_2pc_trans_state != Trans_Done && m_2pc_trans_state != Trans_Rolled_Back)
+ DNBLogError ("%s inconsistent state detected, expected %d or %d, got: %d", __FUNCTION__, Trans_Done, Trans_Rolled_Back, m_2pc_trans_state);
+ m_2pc_dbg_checkpoint = m_state.context.dbg;
+ m_2pc_trans_state = Trans_Pending;
+ return true;
+}
+bool
+DNBArchImplI386::RollbackTransForHWP()
+{
+ m_state.context.dbg = m_2pc_dbg_checkpoint;
+ if (m_2pc_trans_state != Trans_Pending)
+ DNBLogError ("%s inconsistent state detected, expected %d, got: %d", __FUNCTION__, Trans_Pending, m_2pc_trans_state);
+ m_2pc_trans_state = Trans_Rolled_Back;
+ kern_return_t kret = SetDBGState(false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::RollbackTransForHWP() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else
+ return false;
+}
+bool
+DNBArchImplI386::FinishTransForHWP()
+{
+ m_2pc_trans_state = Trans_Done;
+ return true;
+}
+DNBArchImplI386::DBG
+DNBArchImplI386::GetDBGCheckpoint()
+{
+ return m_2pc_dbg_checkpoint;
+}
+
+uint32_t
+DNBArchImplI386::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::EnableHardwareWatchpoint(addr = 0x%llx, size = %llu, read = %u, write = %u)", (uint64_t)addr, (uint64_t)size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can only watch 1, 2, 4, or 8 bytes.
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return INVALID_NUB_HW_INDEX;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ DBG &debug_state = m_state.context.dbg;
+ for (i = 0; i < num_hw_watchpoints; ++i)
+ {
+ if (IsWatchpointVacant(debug_state, i))
+ break;
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints)
+ {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ SetWatchpoint(debug_state, i, addr, size, read, write);
+ // Now set the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::EnableHardwareWatchpoint() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+DNBArchImplI386::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task)
+{
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ DBG &debug_state = m_state.context.dbg;
+ if (hw_index < num_hw_points && !IsWatchpointVacant(debug_state, hw_index))
+ {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ ClearWatchpoint(debug_state, hw_index);
+ // Now disable the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::DisableHardwareWatchpoint( %u )",
+ hw_index);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ }
+ return false;
+}
+
+// Iterate through the debug status register; return the index of the first hit.
+uint32_t
+DNBArchImplI386::GetHardwareWatchpointHit(nub_addr_t &addr)
+{
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret);
+ if (kret == KERN_SUCCESS)
+ {
+ DBG &debug_state = m_state.context.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ {
+ if (IsWatchpointHit(debug_state, i))
+ {
+ addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchImplI386::GetHardwareWatchpointHit() found => %u (addr = 0x%llx).",
+ i, (uint64_t)addr);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchImplI386::EnableHardwareSingleStep (bool enable)
+{
+ if (GetGPRState(false) == KERN_SUCCESS)
+ {
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ m_state.context.gpr.__eflags |= trace_bit;
+ else
+ m_state.context.gpr.__eflags &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+
+//----------------------------------------------------------------------
+// Register information definitions
+//----------------------------------------------------------------------
+
+#define DEFINE_GPR_PSEUDO_16(reg16,reg32) { e_regSetGPR, gpr_##reg16, #reg16, NULL, Uint, Hex, 2, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg32, g_invalidate_##reg32 }
+#define DEFINE_GPR_PSEUDO_8H(reg8,reg32) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 1,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg32, g_invalidate_##reg32 }
+#define DEFINE_GPR_PSEUDO_8L(reg8,reg32) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg32, g_invalidate_##reg32 }
+
+
+#define GPR_OFFSET(reg) (offsetof (DNBArchImplI386::GPR, __##reg))
+#define FPU_OFFSET(reg) (offsetof (DNBArchImplI386::FPU, __fpu_##reg) + offsetof (DNBArchImplI386::Context, fpu.no_avx))
+#define AVX_OFFSET(reg) (offsetof (DNBArchImplI386::AVX, __fpu_##reg) + offsetof (DNBArchImplI386::Context, fpu.avx))
+#define EXC_OFFSET(reg) (offsetof (DNBArchImplI386::EXC, __##reg) + offsetof (DNBArchImplI386::Context, exc))
+
+#define GPR_SIZE(reg) (sizeof(((DNBArchImplI386::GPR *)NULL)->__##reg))
+#define FPU_SIZE_UINT(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg))
+#define FPU_SIZE_MMST(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__mmst_reg))
+#define FPU_SIZE_XMM(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__xmm_reg))
+#define FPU_SIZE_YMM(reg) (32)
+#define EXC_SIZE(reg) (sizeof(((DNBArchImplI386::EXC *)NULL)->__##reg))
+
+// This does not accurately identify the location of ymm0...7 in
+// Context.fpu.avx. That is because there is a bunch of padding
+// in Context.fpu.avx that we don't need. Offset macros lay out
+// the register state that Debugserver transmits to the debugger
+// -- not to interpret the thread_get_state info.
+#define AVX_OFFSET_YMM(n) (AVX_OFFSET(xmm7) + FPU_SIZE_XMM(xmm7) + (32 * n))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+
+const char * g_contained_eax[] = { "eax", NULL };
+const char * g_contained_ebx[] = { "ebx", NULL };
+const char * g_contained_ecx[] = { "ecx", NULL };
+const char * g_contained_edx[] = { "edx", NULL };
+const char * g_contained_edi[] = { "edi", NULL };
+const char * g_contained_esi[] = { "esi", NULL };
+const char * g_contained_ebp[] = { "ebp", NULL };
+const char * g_contained_esp[] = { "esp", NULL };
+
+const char * g_invalidate_eax[] = { "eax", "ax", "ah", "al", NULL };
+const char * g_invalidate_ebx[] = { "ebx", "bx", "bh", "bl", NULL };
+const char * g_invalidate_ecx[] = { "ecx", "cx", "ch", "cl", NULL };
+const char * g_invalidate_edx[] = { "edx", "dx", "dh", "dl", NULL };
+const char * g_invalidate_edi[] = { "edi", "di", "dil", NULL };
+const char * g_invalidate_esi[] = { "esi", "si", "sil", NULL };
+const char * g_invalidate_ebp[] = { "ebp", "bp", "bpl", NULL };
+const char * g_invalidate_esp[] = { "esp", "sp", "spl", NULL };
+
+// General purpose registers for 64 bit
+const DNBRegisterInfo
+DNBArchImplI386::g_gpr_registers[] =
+{
+{ e_regSetGPR, gpr_eax, "eax" , NULL , Uint, Hex, GPR_SIZE(eax), GPR_OFFSET(eax) , ehframe_eax , dwarf_eax , INVALID_NUB_REGNUM , debugserver_eax , NULL, g_invalidate_eax },
+{ e_regSetGPR, gpr_ebx, "ebx" , NULL , Uint, Hex, GPR_SIZE(ebx), GPR_OFFSET(ebx) , ehframe_ebx , dwarf_ebx , INVALID_NUB_REGNUM , debugserver_ebx , NULL, g_invalidate_ebx },
+{ e_regSetGPR, gpr_ecx, "ecx" , NULL , Uint, Hex, GPR_SIZE(ecx), GPR_OFFSET(ecx) , ehframe_ecx , dwarf_ecx , INVALID_NUB_REGNUM , debugserver_ecx , NULL, g_invalidate_ecx },
+{ e_regSetGPR, gpr_edx, "edx" , NULL , Uint, Hex, GPR_SIZE(edx), GPR_OFFSET(edx) , ehframe_edx , dwarf_edx , INVALID_NUB_REGNUM , debugserver_edx , NULL, g_invalidate_edx },
+{ e_regSetGPR, gpr_edi, "edi" , NULL , Uint, Hex, GPR_SIZE(edi), GPR_OFFSET(edi) , ehframe_edi , dwarf_edi , INVALID_NUB_REGNUM , debugserver_edi , NULL, g_invalidate_edi },
+{ e_regSetGPR, gpr_esi, "esi" , NULL , Uint, Hex, GPR_SIZE(esi), GPR_OFFSET(esi) , ehframe_esi , dwarf_esi , INVALID_NUB_REGNUM , debugserver_esi , NULL, g_invalidate_esi },
+{ e_regSetGPR, gpr_ebp, "ebp" , "fp" , Uint, Hex, GPR_SIZE(ebp), GPR_OFFSET(ebp) , ehframe_ebp , dwarf_ebp , GENERIC_REGNUM_FP , debugserver_ebp , NULL, g_invalidate_ebp },
+{ e_regSetGPR, gpr_esp, "esp" , "sp" , Uint, Hex, GPR_SIZE(esp), GPR_OFFSET(esp) , ehframe_esp , dwarf_esp , GENERIC_REGNUM_SP , debugserver_esp , NULL, g_invalidate_esp },
+{ e_regSetGPR, gpr_ss, "ss" , NULL , Uint, Hex, GPR_SIZE(ss), GPR_OFFSET(ss) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_ss , NULL, NULL},
+{ e_regSetGPR, gpr_eflags, "eflags", "flags" , Uint, Hex, GPR_SIZE(eflags), GPR_OFFSET(eflags) , ehframe_eflags , dwarf_eflags , GENERIC_REGNUM_FLAGS , debugserver_eflags, NULL, NULL},
+{ e_regSetGPR, gpr_eip, "eip" , "pc" , Uint, Hex, GPR_SIZE(eip), GPR_OFFSET(eip) , ehframe_eip , dwarf_eip , GENERIC_REGNUM_PC , debugserver_eip , NULL, NULL},
+{ e_regSetGPR, gpr_cs, "cs" , NULL , Uint, Hex, GPR_SIZE(cs), GPR_OFFSET(cs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_cs , NULL, NULL},
+{ e_regSetGPR, gpr_ds, "ds" , NULL , Uint, Hex, GPR_SIZE(ds), GPR_OFFSET(ds) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_ds , NULL, NULL},
+{ e_regSetGPR, gpr_es, "es" , NULL , Uint, Hex, GPR_SIZE(es), GPR_OFFSET(es) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_es , NULL, NULL},
+{ e_regSetGPR, gpr_fs, "fs" , NULL , Uint, Hex, GPR_SIZE(fs), GPR_OFFSET(fs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_fs , NULL, NULL},
+{ e_regSetGPR, gpr_gs, "gs" , NULL , Uint, Hex, GPR_SIZE(gs), GPR_OFFSET(gs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_gs , NULL, NULL},
+DEFINE_GPR_PSEUDO_16 (ax , eax),
+DEFINE_GPR_PSEUDO_16 (bx , ebx),
+DEFINE_GPR_PSEUDO_16 (cx , ecx),
+DEFINE_GPR_PSEUDO_16 (dx , edx),
+DEFINE_GPR_PSEUDO_16 (di , edi),
+DEFINE_GPR_PSEUDO_16 (si , esi),
+DEFINE_GPR_PSEUDO_16 (bp , ebp),
+DEFINE_GPR_PSEUDO_16 (sp , esp),
+DEFINE_GPR_PSEUDO_8H (ah , eax),
+DEFINE_GPR_PSEUDO_8H (bh , ebx),
+DEFINE_GPR_PSEUDO_8H (ch , ecx),
+DEFINE_GPR_PSEUDO_8H (dh , edx),
+DEFINE_GPR_PSEUDO_8L (al , eax),
+DEFINE_GPR_PSEUDO_8L (bl , ebx),
+DEFINE_GPR_PSEUDO_8L (cl , ecx),
+DEFINE_GPR_PSEUDO_8L (dl , edx),
+DEFINE_GPR_PSEUDO_8L (dil, edi),
+DEFINE_GPR_PSEUDO_8L (sil, esi),
+DEFINE_GPR_PSEUDO_8L (bpl, ebp),
+DEFINE_GPR_PSEUDO_8L (spl, esp)
+};
+
+
+const DNBRegisterInfo
+DNBArchImplI386::g_fpu_registers_no_avx[] =
+{
+{ e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+
+{ e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), INVALID_NUB_REGNUM, dwarf_stmm0, INVALID_NUB_REGNUM, debugserver_stmm0, NULL, NULL },
+{ e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), INVALID_NUB_REGNUM, dwarf_stmm1, INVALID_NUB_REGNUM, debugserver_stmm1, NULL, NULL },
+{ e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), INVALID_NUB_REGNUM, dwarf_stmm2, INVALID_NUB_REGNUM, debugserver_stmm2, NULL, NULL },
+{ e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), INVALID_NUB_REGNUM, dwarf_stmm3, INVALID_NUB_REGNUM, debugserver_stmm3, NULL, NULL },
+{ e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), INVALID_NUB_REGNUM, dwarf_stmm4, INVALID_NUB_REGNUM, debugserver_stmm4, NULL, NULL },
+{ e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), INVALID_NUB_REGNUM, dwarf_stmm5, INVALID_NUB_REGNUM, debugserver_stmm5, NULL, NULL },
+{ e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), INVALID_NUB_REGNUM, dwarf_stmm6, INVALID_NUB_REGNUM, debugserver_stmm6, NULL, NULL },
+{ e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), INVALID_NUB_REGNUM, dwarf_stmm7, INVALID_NUB_REGNUM, debugserver_stmm7, NULL, NULL },
+
+{ e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0), FPU_OFFSET(xmm0), INVALID_NUB_REGNUM, dwarf_xmm0, INVALID_NUB_REGNUM, debugserver_xmm0, NULL, NULL },
+{ e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1), FPU_OFFSET(xmm1), INVALID_NUB_REGNUM, dwarf_xmm1, INVALID_NUB_REGNUM, debugserver_xmm1, NULL, NULL },
+{ e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2), FPU_OFFSET(xmm2), INVALID_NUB_REGNUM, dwarf_xmm2, INVALID_NUB_REGNUM, debugserver_xmm2, NULL, NULL },
+{ e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3), FPU_OFFSET(xmm3), INVALID_NUB_REGNUM, dwarf_xmm3, INVALID_NUB_REGNUM, debugserver_xmm3, NULL, NULL },
+{ e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4), FPU_OFFSET(xmm4), INVALID_NUB_REGNUM, dwarf_xmm4, INVALID_NUB_REGNUM, debugserver_xmm4, NULL, NULL },
+{ e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5), FPU_OFFSET(xmm5), INVALID_NUB_REGNUM, dwarf_xmm5, INVALID_NUB_REGNUM, debugserver_xmm5, NULL, NULL },
+{ e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6), FPU_OFFSET(xmm6), INVALID_NUB_REGNUM, dwarf_xmm6, INVALID_NUB_REGNUM, debugserver_xmm6, NULL, NULL },
+{ e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7), FPU_OFFSET(xmm7), INVALID_NUB_REGNUM, dwarf_xmm7, INVALID_NUB_REGNUM, debugserver_xmm7, NULL, NULL }
+};
+
+
+static const char *g_contained_ymm0 [] = { "ymm0", NULL };
+static const char *g_contained_ymm1 [] = { "ymm1", NULL };
+static const char *g_contained_ymm2 [] = { "ymm2", NULL };
+static const char *g_contained_ymm3 [] = { "ymm3", NULL };
+static const char *g_contained_ymm4 [] = { "ymm4", NULL };
+static const char *g_contained_ymm5 [] = { "ymm5", NULL };
+static const char *g_contained_ymm6 [] = { "ymm6", NULL };
+static const char *g_contained_ymm7 [] = { "ymm7", NULL };
+
+
+const DNBRegisterInfo
+DNBArchImplI386::g_fpu_registers_avx[] =
+{
+{ e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , AVX_OFFSET(fcw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , AVX_OFFSET(fsw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , AVX_OFFSET(ftw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , AVX_OFFSET(fop) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , AVX_OFFSET(ip) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , AVX_OFFSET(cs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , AVX_OFFSET(dp) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , AVX_OFFSET(ds) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , AVX_OFFSET(mxcsr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , AVX_OFFSET(mxcsrmask) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+
+{ e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), INVALID_NUB_REGNUM, dwarf_stmm0, INVALID_NUB_REGNUM, debugserver_stmm0, NULL, NULL },
+{ e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), INVALID_NUB_REGNUM, dwarf_stmm1, INVALID_NUB_REGNUM, debugserver_stmm1, NULL, NULL },
+{ e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), INVALID_NUB_REGNUM, dwarf_stmm2, INVALID_NUB_REGNUM, debugserver_stmm2, NULL, NULL },
+{ e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), INVALID_NUB_REGNUM, dwarf_stmm3, INVALID_NUB_REGNUM, debugserver_stmm3, NULL, NULL },
+{ e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), INVALID_NUB_REGNUM, dwarf_stmm4, INVALID_NUB_REGNUM, debugserver_stmm4, NULL, NULL },
+{ e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), INVALID_NUB_REGNUM, dwarf_stmm5, INVALID_NUB_REGNUM, debugserver_stmm5, NULL, NULL },
+{ e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), INVALID_NUB_REGNUM, dwarf_stmm6, INVALID_NUB_REGNUM, debugserver_stmm6, NULL, NULL },
+{ e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), INVALID_NUB_REGNUM, dwarf_stmm7, INVALID_NUB_REGNUM, debugserver_stmm7, NULL, NULL },
+
+{ e_regSetFPU, fpu_ymm0, "ymm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm0), AVX_OFFSET_YMM(0), INVALID_NUB_REGNUM, dwarf_ymm0, INVALID_NUB_REGNUM, debugserver_ymm0, NULL, NULL },
+{ e_regSetFPU, fpu_ymm1, "ymm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm1), AVX_OFFSET_YMM(1), INVALID_NUB_REGNUM, dwarf_ymm1, INVALID_NUB_REGNUM, debugserver_ymm1, NULL, NULL },
+{ e_regSetFPU, fpu_ymm2, "ymm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm2), AVX_OFFSET_YMM(2), INVALID_NUB_REGNUM, dwarf_ymm2, INVALID_NUB_REGNUM, debugserver_ymm2, NULL, NULL },
+{ e_regSetFPU, fpu_ymm3, "ymm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm3), AVX_OFFSET_YMM(3), INVALID_NUB_REGNUM, dwarf_ymm3, INVALID_NUB_REGNUM, debugserver_ymm3, NULL, NULL },
+{ e_regSetFPU, fpu_ymm4, "ymm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm4), AVX_OFFSET_YMM(4), INVALID_NUB_REGNUM, dwarf_ymm4, INVALID_NUB_REGNUM, debugserver_ymm4, NULL, NULL },
+{ e_regSetFPU, fpu_ymm5, "ymm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm5), AVX_OFFSET_YMM(5), INVALID_NUB_REGNUM, dwarf_ymm5, INVALID_NUB_REGNUM, debugserver_ymm5, NULL, NULL },
+{ e_regSetFPU, fpu_ymm6, "ymm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm6), AVX_OFFSET_YMM(6), INVALID_NUB_REGNUM, dwarf_ymm6, INVALID_NUB_REGNUM, debugserver_ymm6, NULL, NULL },
+{ e_regSetFPU, fpu_ymm7, "ymm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm7), AVX_OFFSET_YMM(7), INVALID_NUB_REGNUM, dwarf_ymm7, INVALID_NUB_REGNUM, debugserver_ymm7, NULL, NULL },
+
+{ e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0), 0, INVALID_NUB_REGNUM, dwarf_xmm0, INVALID_NUB_REGNUM, debugserver_xmm0, g_contained_ymm0, NULL },
+{ e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1), 0, INVALID_NUB_REGNUM, dwarf_xmm1, INVALID_NUB_REGNUM, debugserver_xmm1, g_contained_ymm1, NULL },
+{ e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2), 0, INVALID_NUB_REGNUM, dwarf_xmm2, INVALID_NUB_REGNUM, debugserver_xmm2, g_contained_ymm2, NULL },
+{ e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3), 0, INVALID_NUB_REGNUM, dwarf_xmm3, INVALID_NUB_REGNUM, debugserver_xmm3, g_contained_ymm3, NULL },
+{ e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4), 0, INVALID_NUB_REGNUM, dwarf_xmm4, INVALID_NUB_REGNUM, debugserver_xmm4, g_contained_ymm4, NULL },
+{ e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5), 0, INVALID_NUB_REGNUM, dwarf_xmm5, INVALID_NUB_REGNUM, debugserver_xmm5, g_contained_ymm5, NULL },
+{ e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6), 0, INVALID_NUB_REGNUM, dwarf_xmm6, INVALID_NUB_REGNUM, debugserver_xmm6, g_contained_ymm6, NULL },
+{ e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7), 0, INVALID_NUB_REGNUM, dwarf_xmm7, INVALID_NUB_REGNUM, debugserver_xmm7, g_contained_ymm7, NULL },
+
+};
+
+const DNBRegisterInfo
+DNBArchImplI386::g_exc_registers[] =
+{
+{ e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL },
+{ e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }
+};
+
+// Number of registers in each register set
+const size_t DNBArchImplI386::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_fpu_registers_no_avx = sizeof(g_fpu_registers_no_avx)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_fpu_registers_avx = sizeof(g_fpu_registers_avx)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_all_registers_no_avx = k_num_gpr_registers + k_num_fpu_registers_no_avx + k_num_exc_registers;
+const size_t DNBArchImplI386::k_num_all_registers_avx = k_num_gpr_registers + k_num_fpu_registers_avx + k_num_exc_registers;
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+const DNBRegisterSetInfo
+DNBArchImplI386::g_reg_sets_no_avx[] =
+{
+ { "i386 Registers", NULL, k_num_all_registers_no_avx },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpu_registers_no_avx, k_num_fpu_registers_no_avx },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+
+const DNBRegisterSetInfo
+DNBArchImplI386::g_reg_sets_avx[] =
+{
+ { "i386 Registers", NULL, k_num_all_registers_avx },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpu_registers_avx, k_num_fpu_registers_avx },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+
+// Total number of register sets for this architecture
+const size_t DNBArchImplI386::k_num_register_sets = sizeof(g_reg_sets_no_avx)/sizeof(DNBRegisterSetInfo);
+
+DNBArchProtocol *
+DNBArchImplI386::Create (MachThread *thread)
+{
+ DNBArchImplI386 *obj = new DNBArchImplI386 (thread);
+ return obj;
+}
+
+const uint8_t *
+DNBArchImplI386::SoftwareBreakpointOpcode (nub_size_t byte_size)
+{
+ static const uint8_t g_breakpoint_opcode[] = { 0xCC };
+ if (byte_size == 1)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+const DNBRegisterSetInfo *
+DNBArchImplI386::GetRegisterSetInfo(nub_size_t *num_reg_sets)
+{
+ *num_reg_sets = k_num_register_sets;
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ return g_reg_sets_avx;
+ else
+ return g_reg_sets_no_avx;
+}
+
+
+void
+DNBArchImplI386::Initialize()
+{
+ DNBArchPluginInfo arch_plugin_info =
+ {
+ CPU_TYPE_I386,
+ DNBArchImplI386::Create,
+ DNBArchImplI386::GetRegisterSetInfo,
+ DNBArchImplI386::SoftwareBreakpointOpcode
+ };
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin (arch_plugin_info);
+}
+
+bool
+DNBArchImplI386::GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_eip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_esp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_ebp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_eflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint32 = ((uint32_t*)(&m_state.context.gpr))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPU:
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ switch (reg)
+ {
+ case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)); return true;
+ case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)); return true;
+ case fpu_ftw: value->value.uint8 = m_state.context.fpu.avx.__fpu_ftw; return true;
+ case fpu_fop: value->value.uint16 = m_state.context.fpu.avx.__fpu_fop; return true;
+ case fpu_ip: value->value.uint32 = m_state.context.fpu.avx.__fpu_ip; return true;
+ case fpu_cs: value->value.uint16 = m_state.context.fpu.avx.__fpu_cs; return true;
+ case fpu_dp: value->value.uint32 = m_state.context.fpu.avx.__fpu_dp; return true;
+ case fpu_ds: value->value.uint16 = m_state.context.fpu.avx.__fpu_ds; return true;
+ case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.avx.__fpu_mxcsr; return true;
+ case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.avx.__fpu_mxcsrmask; return true;
+
+ case fpu_stmm0: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg, 10); return true;
+ case fpu_stmm1: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg, 10); return true;
+ case fpu_stmm2: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg, 10); return true;
+ case fpu_stmm3: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg, 10); return true;
+ case fpu_stmm4: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg, 10); return true;
+ case fpu_stmm5: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg, 10); return true;
+ case fpu_stmm6: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg, 10); return true;
+ case fpu_stmm7: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg, 10); return true;
+
+ case fpu_xmm0: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg, 16); return true;
+ case fpu_xmm1: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg, 16); return true;
+ case fpu_xmm2: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg, 16); return true;
+ case fpu_xmm3: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg, 16); return true;
+ case fpu_xmm4: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg, 16); return true;
+ case fpu_xmm5: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg, 16); return true;
+ case fpu_xmm6: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg, 16); return true;
+ case fpu_xmm7: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg, 16); return true;
+
+#define MEMCPY_YMM(n) \
+ memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm##n.__xmm_reg, 16); \
+ memcpy((&value->value.uint8) + 16, m_state.context.fpu.avx.__fpu_ymmh##n.__xmm_reg, 16);
+ case fpu_ymm0: MEMCPY_YMM(0); return true;
+ case fpu_ymm1: MEMCPY_YMM(1); return true;
+ case fpu_ymm2: MEMCPY_YMM(2); return true;
+ case fpu_ymm3: MEMCPY_YMM(3); return true;
+ case fpu_ymm4: MEMCPY_YMM(4); return true;
+ case fpu_ymm5: MEMCPY_YMM(5); return true;
+ case fpu_ymm6: MEMCPY_YMM(6); return true;
+ case fpu_ymm7: MEMCPY_YMM(7); return true;
+#undef MEMCPY_YMM
+ }
+ }
+ else
+ {
+ switch (reg)
+ {
+ case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)); return true;
+ case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)); return true;
+ case fpu_ftw: value->value.uint8 = m_state.context.fpu.no_avx.__fpu_ftw; return true;
+ case fpu_fop: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop; return true;
+ case fpu_ip: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_ip; return true;
+ case fpu_cs: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_cs; return true;
+ case fpu_dp: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_dp; return true;
+ case fpu_ds: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_ds; return true;
+ case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsr; return true;
+ case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsrmask; return true;
+
+ case fpu_stmm0: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg, 10); return true;
+ case fpu_stmm1: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg, 10); return true;
+ case fpu_stmm2: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg, 10); return true;
+ case fpu_stmm3: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg, 10); return true;
+ case fpu_stmm4: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg, 10); return true;
+ case fpu_stmm5: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg, 10); return true;
+ case fpu_stmm6: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg, 10); return true;
+ case fpu_stmm7: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg, 10); return true;
+
+ case fpu_xmm0: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg, 16); return true;
+ case fpu_xmm1: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg, 16); return true;
+ case fpu_xmm2: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg, 16); return true;
+ case fpu_xmm3: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg, 16); return true;
+ case fpu_xmm4: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg, 16); return true;
+ case fpu_xmm5: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg, 16); return true;
+ case fpu_xmm6: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg, 16); return true;
+ case fpu_xmm7: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg, 16); return true;
+ }
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ value->value.uint32 = (&m_state.context.exc.__trapno)[reg];
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+bool
+DNBArchImplI386::SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_eip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_esp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_ebp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_eflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ ((uint32_t*)(&m_state.context.gpr))[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetFPU:
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ switch (reg)
+ {
+ case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)) = value->value.uint16; success = true; break;
+ case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)) = value->value.uint16; success = true; break;
+ case fpu_ftw: m_state.context.fpu.avx.__fpu_ftw = value->value.uint8; success = true; break;
+ case fpu_fop: m_state.context.fpu.avx.__fpu_fop = value->value.uint16; success = true; break;
+ case fpu_ip: m_state.context.fpu.avx.__fpu_ip = value->value.uint32; success = true; break;
+ case fpu_cs: m_state.context.fpu.avx.__fpu_cs = value->value.uint16; success = true; break;
+ case fpu_dp: m_state.context.fpu.avx.__fpu_dp = value->value.uint32; success = true; break;
+ case fpu_ds: m_state.context.fpu.avx.__fpu_ds = value->value.uint16; success = true; break;
+ case fpu_mxcsr: m_state.context.fpu.avx.__fpu_mxcsr = value->value.uint32; success = true; break;
+ case fpu_mxcsrmask: m_state.context.fpu.avx.__fpu_mxcsrmask = value->value.uint32; success = true; break;
+
+ case fpu_stmm0: memcpy (m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm1: memcpy (m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm2: memcpy (m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm3: memcpy (m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm4: memcpy (m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm5: memcpy (m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm6: memcpy (m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm7: memcpy (m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg, &value->value.uint8, 10); success = true; break;
+
+ case fpu_xmm0: memcpy(m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm1: memcpy(m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm2: memcpy(m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm3: memcpy(m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm4: memcpy(m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm5: memcpy(m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm6: memcpy(m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm7: memcpy(m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg, &value->value.uint8, 16); success = true; break;
+
+#define MEMCPY_YMM(n) \
+ memcpy(m_state.context.fpu.avx.__fpu_xmm##n.__xmm_reg, &value->value.uint8, 16); \
+ memcpy(m_state.context.fpu.avx.__fpu_ymmh##n.__xmm_reg, (&value->value.uint8) + 16, 16);
+ case fpu_ymm0: MEMCPY_YMM(0); return true;
+ case fpu_ymm1: MEMCPY_YMM(1); return true;
+ case fpu_ymm2: MEMCPY_YMM(2); return true;
+ case fpu_ymm3: MEMCPY_YMM(3); return true;
+ case fpu_ymm4: MEMCPY_YMM(4); return true;
+ case fpu_ymm5: MEMCPY_YMM(5); return true;
+ case fpu_ymm6: MEMCPY_YMM(6); return true;
+ case fpu_ymm7: MEMCPY_YMM(7); return true;
+#undef MEMCPY_YMM
+ }
+ }
+ else
+ {
+ switch (reg)
+ {
+ case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)) = value->value.uint16; success = true; break;
+ case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)) = value->value.uint16; success = true; break;
+ case fpu_ftw: m_state.context.fpu.no_avx.__fpu_ftw = value->value.uint8; success = true; break;
+ case fpu_fop: m_state.context.fpu.no_avx.__fpu_fop = value->value.uint16; success = true; break;
+ case fpu_ip: m_state.context.fpu.no_avx.__fpu_ip = value->value.uint32; success = true; break;
+ case fpu_cs: m_state.context.fpu.no_avx.__fpu_cs = value->value.uint16; success = true; break;
+ case fpu_dp: m_state.context.fpu.no_avx.__fpu_dp = value->value.uint32; success = true; break;
+ case fpu_ds: m_state.context.fpu.no_avx.__fpu_ds = value->value.uint16; success = true; break;
+ case fpu_mxcsr: m_state.context.fpu.no_avx.__fpu_mxcsr = value->value.uint32; success = true; break;
+ case fpu_mxcsrmask: m_state.context.fpu.no_avx.__fpu_mxcsrmask = value->value.uint32; success = true; break;
+
+ case fpu_stmm0: memcpy (m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm1: memcpy (m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm2: memcpy (m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm3: memcpy (m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm4: memcpy (m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm5: memcpy (m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm6: memcpy (m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg, &value->value.uint8, 10); success = true; break;
+ case fpu_stmm7: memcpy (m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg, &value->value.uint8, 10); success = true; break;
+
+ case fpu_xmm0: memcpy(m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm1: memcpy(m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm2: memcpy(m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm3: memcpy(m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm4: memcpy(m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm5: memcpy(m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm6: memcpy(m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ case fpu_xmm7: memcpy(m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg, &value->value.uint8, 16); success = true; break;
+ }
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ (&m_state.context.exc.__trapno)[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+ }
+
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+
+uint32_t
+DNBArchImplI386::GetRegisterContextSize()
+{
+ static uint32_t g_cached_size = 0;
+ if (g_cached_size == 0)
+ {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ for (size_t i=0; i<k_num_fpu_registers_avx; ++i)
+ {
+ if (g_fpu_registers_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_avx[i].size;
+ }
+ }
+ else
+ {
+ for (size_t i=0; i<k_num_fpu_registers_no_avx; ++i)
+ {
+ if (g_fpu_registers_no_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_no_avx[i].size;
+ }
+ }
+ DNBLogThreaded ("DNBArchImplX86_64::GetRegisterContextSize() - GPR = %zu, FPU = %u, EXC = %zu", sizeof(GPR), g_cached_size, sizeof(EXC));
+ g_cached_size += sizeof(GPR);
+ g_cached_size += sizeof(EXC);
+ DNBLogThreaded ("DNBArchImplX86_64::GetRegisterContextSize() - GPR + FPU + EXC = %u", g_cached_size);
+ }
+ return g_cached_size;
+}
+
+
+nub_size_t
+DNBArchImplI386::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ uint32_t size = GetRegisterContextSize();
+
+ if (buf && buf_len)
+ {
+ if (size > buf_len)
+ size = static_cast<uint32_t>(buf_len);
+
+ bool force = false;
+ kern_return_t kret;
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) error: GPR regs failed to read: %u ", buf, (uint64_t)buf_len, kret);
+ size = 0;
+ }
+ else if ((kret = GetFPUState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) error: %s regs failed to read: %u", buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ size = 0;
+ }
+ else if ((kret = GetEXCState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) error: EXC regs failed to read: %u", buf, (uint64_t)buf_len, kret);
+ size = 0;
+ }
+ else
+ {
+ uint8_t *p = (uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(p, &m_state.context.gpr, sizeof(GPR));
+ p += sizeof(GPR);
+
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ // Walk around the gaps in the FPU regs
+ memcpy(p, &m_state.context.fpu.avx.__fpu_fcw, 5);
+ p += 5;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_fop, 8);
+ p += 8;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_dp, 6);
+ p += 6;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_mxcsr, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(p, &m_state.context.fpu.avx.__fpu_stmm0 + i, 10);
+ p += 10;
+ }
+
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(p, &m_state.context.fpu.avx.__fpu_xmm0 + i, 16);
+ p += 16;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_ymmh0 + i, 16);
+ p += 16;
+ }
+ }
+ else
+ {
+ // Walk around the gaps in the FPU regs
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fcw, 5);
+ p += 5;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fop, 8);
+ p += 8;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_dp, 6);
+ p += 6;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_mxcsr, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_stmm0 + i, 10);
+ p += 10;
+ }
+
+ // Copy the XMM registers in a single block
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_xmm0, 8 * 16);
+ p += 8 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(p, &m_state.context.exc, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+ }
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t
+DNBArchImplI386::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ nub_size_t size = sizeof (m_state.context);
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size)
+ {
+ if (size > buf_len)
+ size = buf_len;
+
+ uint8_t *p = (uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(&m_state.context.gpr, p, sizeof(GPR));
+ p += sizeof(GPR);
+
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ // Walk around the gaps in the FPU regs
+ memcpy(&m_state.context.fpu.avx.__fpu_fcw, p, 5);
+ p += 5;
+ memcpy(&m_state.context.fpu.avx.__fpu_fop, p, 8);
+ p += 8;
+ memcpy(&m_state.context.fpu.avx.__fpu_dp, p, 6);
+ p += 6;
+ memcpy(&m_state.context.fpu.avx.__fpu_mxcsr, p, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(&m_state.context.fpu.avx.__fpu_stmm0 + i, p, 10);
+ p += 10;
+ }
+
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + i, p, 16);
+ p += 16;
+ memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + i, p, 16);
+ p += 16;
+ }
+ }
+ else
+ {
+ // Copy fcw through mxcsrmask as there is no padding
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fcw, p, 5);
+ p += 5;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fop, p, 8);
+ p += 8;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_dp, p, 6);
+ p += 6;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_mxcsr, p, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(&m_state.context.fpu.no_avx.__fpu_stmm0 + i, p, 10);
+ p += 10;
+ }
+
+ // Copy the XMM registers in a single block
+ memcpy(&m_state.context.fpu.no_avx.__fpu_xmm0, p, 8 * 16);
+ p += 8 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(&m_state.context.exc, p, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+ kern_return_t kret;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) error: GPR regs failed to write: %u", buf, (uint64_t)buf_len, kret);
+ if ((kret = SetFPUState()) != KERN_SUCCESS)
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) error: %s regs failed to write: %u", buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ if ((kret = SetEXCState()) != KERN_SUCCESS)
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) error: EXP regs failed to write: %u", buf, (uint64_t)buf_len, kret);
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size);
+ return size;
+}
+
+
+uint32_t
+DNBArchImplI386::SaveRegisterState ()
+{
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SaveRegisterState () error: GPR regs failed to read: %u ", kret);
+ }
+ else if ((kret = GetFPUState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SaveRegisterState () error: %s regs failed to read: %u", CPUHasAVX() ? "AVX" : "FPU", kret);
+ }
+ else
+ {
+ const uint32_t save_id = GetNextRegisterStateSaveID ();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return 0;
+}
+bool
+DNBArchImplI386::RestoreRegisterState (uint32_t save_id)
+{
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end())
+ {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.fpu = pos->second.fpu;
+ m_state.context.exc = pos->second.exc;
+ m_state.SetError(e_regSetGPR, Read, 0);
+ m_state.SetError(e_regSetFPU, Read, 0);
+ m_state.SetError(e_regSetEXC, Read, 0);
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret);
+ success = false;
+ }
+ else if ((kret = SetFPUState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, CPUHasAVX() ? "AVX" : "FPU", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+
+kern_return_t
+DNBArchImplI386::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL: return GetGPRState(force) | GetFPUState(force) | GetEXCState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetFPU: return GetFPUState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchImplI386::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetStateIsValid(set))
+ {
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() | SetFPUState() | SetEXCState();
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetFPU: return SetFPUState();
+ case e_regSetEXC: return SetEXCState();
+ default: break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchImplI386::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+#endif // #if defined (__i386__)
diff --git a/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h
new file mode 100644
index 000000000000..6b4252151feb
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h
@@ -0,0 +1,255 @@
+//===-- DNBArchImplI386.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBArchImplI386_h__
+#define __DNBArchImplI386_h__
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#include "DNBArch.h"
+#include "../HasAVX.h"
+#include "MachRegisterStatesI386.h"
+
+#include <map>
+
+class MachThread;
+
+class DNBArchImplI386 : public DNBArchProtocol
+{
+public:
+ DNBArchImplI386(MachThread *thread) :
+ DNBArchProtocol(),
+ m_thread(thread),
+ m_state(),
+ m_2pc_dbg_checkpoint(),
+ m_2pc_trans_state(Trans_Done),
+ m_saved_register_states()
+ {
+ }
+ virtual ~DNBArchImplI386()
+ {
+ }
+
+ static void Initialize();
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState ();
+ virtual bool RestoreRegisterState (uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data& exc);
+
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task);
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+protected:
+ kern_return_t EnableHardwareSingleStep (bool enable);
+
+ typedef __i386_thread_state_t GPR;
+ typedef __i386_float_state_t FPU;
+ typedef __i386_exception_state_t EXC;
+ typedef __i386_avx_state_t AVX;
+ typedef __i386_debug_state_t DBG;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_fpu_registers_no_avx[];
+ static const DNBRegisterInfo g_fpu_registers_avx[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets_no_avx[];
+ static const DNBRegisterSetInfo g_reg_sets_avx[];
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_fpu_registers_no_avx;
+ static const size_t k_num_fpu_registers_avx;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers_no_avx;
+ static const size_t k_num_all_registers_avx;
+ static const size_t k_num_register_sets;
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPU,
+ e_regSetEXC,
+ e_regSetDBG,
+ kNumRegisterSets
+ } RegisterSet;
+
+ typedef enum RegisterSetWordSizeTag
+ {
+ e_regSetWordSizeGPR = sizeof(GPR) / sizeof(int),
+ e_regSetWordSizeFPU = sizeof(FPU) / sizeof(int),
+ e_regSetWordSizeEXC = sizeof(EXC) / sizeof(int),
+ e_regSetWordSizeAVX = sizeof(AVX) / sizeof(int),
+ e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int)
+ } RegisterSetWordSize;
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ struct Context
+ {
+ GPR gpr;
+ union {
+ FPU no_avx;
+ AVX avx;
+ } fpu;
+ EXC exc;
+ DBG dbg;
+ };
+
+ struct State
+ {
+ Context context;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+ void InvalidateAllRegisterStates()
+ {
+ SetError (e_regSetALL, Read, -1);
+ }
+ kern_return_t GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL: return gpr_errs[err_idx] |
+ fpu_errs[err_idx] |
+ exc_errs[err_idx];
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetFPU: return fpu_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ case e_regSetDBG: return dbg_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+ bool SetError (int flavor, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] =
+ fpu_errs[err_idx] =
+ exc_errs[err_idx] =
+ dbg_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPU:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetDBG:
+ dbg_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid (int flavor) const
+ {
+ return GetError(flavor, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetFPUState (bool force);
+ kern_return_t GetEXCState (bool force);
+ kern_return_t GetDBGState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetFPUState ();
+ kern_return_t SetEXCState ();
+ kern_return_t SetDBGState (bool also_set_on_task);
+
+ static DNBArchProtocol *
+ Create (MachThread *thread);
+
+ static const uint8_t *
+ SoftwareBreakpointOpcode (nub_size_t byte_size);
+
+ static const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ static uint32_t
+ GetRegisterContextSize();
+
+ // Helper functions for watchpoint manipulations.
+ static void SetWatchpoint(DBG &debug_state, uint32_t hw_index, nub_addr_t addr, nub_size_t size, bool read, bool write);
+ static void ClearWatchpoint(DBG &debug_state, uint32_t hw_index);
+ static bool IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index);
+ static void ClearWatchpointHits(DBG &debug_state);
+ static bool IsWatchpointHit(const DBG &debug_state, uint32_t hw_index);
+ static nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+
+ virtual bool StartTransForHWP();
+ virtual bool RollbackTransForHWP();
+ virtual bool FinishTransForHWP();
+ DBG GetDBGCheckpoint();
+
+ MachThread *m_thread;
+ State m_state;
+ DBG m_2pc_dbg_checkpoint;
+ uint32_t m_2pc_trans_state; // Is transaction of DBG state change: Pedning (0), Done (1), or Rolled Back (2)?
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (__i386__) || defined (__x86_64__)
+#endif // #ifndef __DNBArchImplI386_h__
diff --git a/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h b/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h
new file mode 100644
index 000000000000..59cfbe055a36
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h
@@ -0,0 +1,180 @@
+//===-- MachRegisterStatesI386.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Sean Callanan on 3/16/11.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachRegisterStatesI386_h__
+#define __MachRegisterStatesI386_h__
+
+#include <inttypes.h>
+
+#define __i386_THREAD_STATE 1
+#define __i386_FLOAT_STATE 2
+#define __i386_EXCEPTION_STATE 3
+#define __i386_DEBUG_STATE 10
+#define __i386_AVX_STATE 16
+
+typedef struct {
+ uint32_t __eax;
+ uint32_t __ebx;
+ uint32_t __ecx;
+ uint32_t __edx;
+ uint32_t __edi;
+ uint32_t __esi;
+ uint32_t __ebp;
+ uint32_t __esp;
+ uint32_t __ss;
+ uint32_t __eflags;
+ uint32_t __eip;
+ uint32_t __cs;
+ uint32_t __ds;
+ uint32_t __es;
+ uint32_t __fs;
+ uint32_t __gs;
+} __i386_thread_state_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __PAD1 : 2;
+ uint16_t __pc : 2;
+ uint16_t __rc : 2;
+ uint16_t __PAD2 : 1;
+ uint16_t __PAD3 : 3;
+} __i386_fp_control_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __stkflt : 1;
+ uint16_t __errsumm : 1;
+ uint16_t __c0 : 1;
+ uint16_t __c1 : 1;
+ uint16_t __c2 : 1;
+ uint16_t __tos : 3;
+ uint16_t __c3 : 1;
+ uint16_t __busy : 1;
+} __i386_fp_status_t;
+
+typedef struct {
+ uint8_t __mmst_reg[10];
+ uint8_t __mmst_rsrv[6];
+} __i386_mmst_reg;
+
+typedef struct {
+ uint8_t __xmm_reg[16];
+} __i386_xmm_reg;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __i386_fp_control_t __fpu_fcw;
+ __i386_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __i386_mmst_reg __fpu_stmm0;
+ __i386_mmst_reg __fpu_stmm1;
+ __i386_mmst_reg __fpu_stmm2;
+ __i386_mmst_reg __fpu_stmm3;
+ __i386_mmst_reg __fpu_stmm4;
+ __i386_mmst_reg __fpu_stmm5;
+ __i386_mmst_reg __fpu_stmm6;
+ __i386_mmst_reg __fpu_stmm7;
+ __i386_xmm_reg __fpu_xmm0;
+ __i386_xmm_reg __fpu_xmm1;
+ __i386_xmm_reg __fpu_xmm2;
+ __i386_xmm_reg __fpu_xmm3;
+ __i386_xmm_reg __fpu_xmm4;
+ __i386_xmm_reg __fpu_xmm5;
+ __i386_xmm_reg __fpu_xmm6;
+ __i386_xmm_reg __fpu_xmm7;
+ uint8_t __fpu_rsrv4[14*16];
+ uint32_t __fpu_reserved1;
+} __i386_float_state_t;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __i386_fp_control_t __fpu_fcw;
+ __i386_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __i386_mmst_reg __fpu_stmm0;
+ __i386_mmst_reg __fpu_stmm1;
+ __i386_mmst_reg __fpu_stmm2;
+ __i386_mmst_reg __fpu_stmm3;
+ __i386_mmst_reg __fpu_stmm4;
+ __i386_mmst_reg __fpu_stmm5;
+ __i386_mmst_reg __fpu_stmm6;
+ __i386_mmst_reg __fpu_stmm7;
+ __i386_xmm_reg __fpu_xmm0;
+ __i386_xmm_reg __fpu_xmm1;
+ __i386_xmm_reg __fpu_xmm2;
+ __i386_xmm_reg __fpu_xmm3;
+ __i386_xmm_reg __fpu_xmm4;
+ __i386_xmm_reg __fpu_xmm5;
+ __i386_xmm_reg __fpu_xmm6;
+ __i386_xmm_reg __fpu_xmm7;
+ uint8_t __fpu_rsrv4[14*16];
+ uint32_t __fpu_reserved1;
+ uint8_t __avx_reserved1[64];
+ __i386_xmm_reg __fpu_ymmh0;
+ __i386_xmm_reg __fpu_ymmh1;
+ __i386_xmm_reg __fpu_ymmh2;
+ __i386_xmm_reg __fpu_ymmh3;
+ __i386_xmm_reg __fpu_ymmh4;
+ __i386_xmm_reg __fpu_ymmh5;
+ __i386_xmm_reg __fpu_ymmh6;
+ __i386_xmm_reg __fpu_ymmh7;
+} __i386_avx_state_t;
+
+typedef struct {
+ uint32_t __trapno;
+ uint32_t __err;
+ uint32_t __faultvaddr;
+} __i386_exception_state_t;
+
+typedef struct {
+ uint32_t __dr0;
+ uint32_t __dr1;
+ uint32_t __dr2;
+ uint32_t __dr3;
+ uint32_t __dr4;
+ uint32_t __dr5;
+ uint32_t __dr6;
+ uint32_t __dr7;
+} __i386_debug_state_t;
+
+#endif
diff --git a/tools/debugserver/source/MacOSX/i386/Makefile b/tools/debugserver/source/MacOSX/i386/Makefile
new file mode 100644
index 000000000000..f770b19834bb
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/i386/Makefile
@@ -0,0 +1,19 @@
+##===- tools/debugserver/source/MacOSX/i386/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 := lldbDebugserverMacOSX_I386
+BUILD_ARCHIVE = 1
+
+SOURCES := DNBArchImplI386.cpp
+
+include $(LLDB_LEVEL)/Makefile
+
+CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../.. -I$(PROJ_OBJ_DIR)/../../.. \ No newline at end of file
diff --git a/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp
new file mode 100644
index 000000000000..c6f1a718ac92
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp
@@ -0,0 +1,569 @@
+//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+
+#if __DARWIN_UNIX03
+#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) __##reg
+#else
+#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) reg
+#endif
+
+#include "MacOSX/ppc/DNBArchImpl.h"
+#include "MacOSX/MachThread.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+
+static const uint8_t g_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 };
+
+const uint8_t *
+DNBArchMachPPC::SoftwareBreakpointOpcode (nub_size_t size)
+{
+ if (size == 4)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+uint32_t
+DNBArchMachPPC::GetCPUType()
+{
+ return CPU_TYPE_POWERPC;
+}
+
+uint64_t
+DNBArchMachPPC::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0);
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachPPC::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0) = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchMachPPC::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(r1);
+ return failValue;
+}
+
+kern_return_t
+DNBArchMachPPC::GetGPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetGPR, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->MachPortNumber(), e_regSetGPR, (thread_state_t)&m_state.gpr, &count));
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::GetFPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetFPR, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeFPR;
+ m_state.SetError(e_regSetFPR, Read, ::thread_get_state(m_thread->MachPortNumber(), e_regSetFPR, (thread_state_t)&m_state.fpr, &count));
+ }
+ return m_state.GetError(e_regSetFPR, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::GetEXCState(bool force)
+{
+ if (force || m_state.GetError(e_regSetEXC, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->MachPortNumber(), e_regSetEXC, (thread_state_t)&m_state.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::GetVECState(bool force)
+{
+ if (force || m_state.GetError(e_regSetVEC, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeVEC;
+ m_state.SetError(e_regSetVEC, Read, ::thread_get_state(m_thread->MachPortNumber(), e_regSetVEC, (thread_state_t)&m_state.vec, &count));
+ }
+ return m_state.GetError(e_regSetVEC, Read);
+}
+
+kern_return_t
+DNBArchMachPPC::SetGPRState()
+{
+ m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->MachPortNumber(), e_regSetGPR, (thread_state_t)&m_state.gpr, e_regSetWordSizeGPR));
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t
+DNBArchMachPPC::SetFPRState()
+{
+ m_state.SetError(e_regSetFPR, Write, ::thread_set_state(m_thread->MachPortNumber(), e_regSetFPR, (thread_state_t)&m_state.fpr, e_regSetWordSizeFPR));
+ return m_state.GetError(e_regSetFPR, Write);
+}
+
+kern_return_t
+DNBArchMachPPC::SetEXCState()
+{
+ m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->MachPortNumber(), e_regSetEXC, (thread_state_t)&m_state.exc, e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+kern_return_t
+DNBArchMachPPC::SetVECState()
+{
+ m_state.SetError(e_regSetVEC, Write, ::thread_set_state(m_thread->MachPortNumber(), e_regSetVEC, (thread_state_t)&m_state.vec, e_regSetWordSizeVEC));
+ return m_state.GetError(e_regSetVEC, Write);
+}
+
+bool
+DNBArchMachPPC::ThreadWillResume()
+{
+ bool success = true;
+
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ // This is the primary thread, let the arch do anything it needs
+ success = EnableHardwareSingleStep(true) == KERN_SUCCESS;
+ }
+ return success;
+}
+
+bool
+DNBArchMachPPC::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchMachPPC::EnableHardwareSingleStep (bool enable)
+{
+ DNBLogThreadedIf(LOG_STEP, "DNBArchMachPPC::EnableHardwareSingleStep( enable = %d )", enable);
+ if (GetGPRState(false) == KERN_SUCCESS)
+ {
+ const uint32_t trace_bit = 0x400;
+ if (enable)
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) |= trace_bit;
+ else
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+//----------------------------------------------------------------------
+// Register information definitions for 32 bit PowerPC.
+//----------------------------------------------------------------------
+
+enum gpr_regnums
+{
+ e_regNumGPR_srr0,
+ e_regNumGPR_srr1,
+ e_regNumGPR_r0,
+ e_regNumGPR_r1,
+ e_regNumGPR_r2,
+ e_regNumGPR_r3,
+ e_regNumGPR_r4,
+ e_regNumGPR_r5,
+ e_regNumGPR_r6,
+ e_regNumGPR_r7,
+ e_regNumGPR_r8,
+ e_regNumGPR_r9,
+ e_regNumGPR_r10,
+ e_regNumGPR_r11,
+ e_regNumGPR_r12,
+ e_regNumGPR_r13,
+ e_regNumGPR_r14,
+ e_regNumGPR_r15,
+ e_regNumGPR_r16,
+ e_regNumGPR_r17,
+ e_regNumGPR_r18,
+ e_regNumGPR_r19,
+ e_regNumGPR_r20,
+ e_regNumGPR_r21,
+ e_regNumGPR_r22,
+ e_regNumGPR_r23,
+ e_regNumGPR_r24,
+ e_regNumGPR_r25,
+ e_regNumGPR_r26,
+ e_regNumGPR_r27,
+ e_regNumGPR_r28,
+ e_regNumGPR_r29,
+ e_regNumGPR_r30,
+ e_regNumGPR_r31,
+ e_regNumGPR_cr,
+ e_regNumGPR_xer,
+ e_regNumGPR_lr,
+ e_regNumGPR_ctr,
+ e_regNumGPR_mq,
+ e_regNumGPR_vrsave
+};
+
+
+
+
+// General purpose registers
+static DNBRegisterInfo g_gpr_registers[] =
+{
+ { "srr0" , Uint, 4, Hex },
+ { "srr1" , Uint, 4, Hex },
+ { "r0" , Uint, 4, Hex },
+ { "r1" , Uint, 4, Hex },
+ { "r2" , Uint, 4, Hex },
+ { "r3" , Uint, 4, Hex },
+ { "r4" , Uint, 4, Hex },
+ { "r5" , Uint, 4, Hex },
+ { "r6" , Uint, 4, Hex },
+ { "r7" , Uint, 4, Hex },
+ { "r8" , Uint, 4, Hex },
+ { "r9" , Uint, 4, Hex },
+ { "r10" , Uint, 4, Hex },
+ { "r11" , Uint, 4, Hex },
+ { "r12" , Uint, 4, Hex },
+ { "r13" , Uint, 4, Hex },
+ { "r14" , Uint, 4, Hex },
+ { "r15" , Uint, 4, Hex },
+ { "r16" , Uint, 4, Hex },
+ { "r17" , Uint, 4, Hex },
+ { "r18" , Uint, 4, Hex },
+ { "r19" , Uint, 4, Hex },
+ { "r20" , Uint, 4, Hex },
+ { "r21" , Uint, 4, Hex },
+ { "r22" , Uint, 4, Hex },
+ { "r23" , Uint, 4, Hex },
+ { "r24" , Uint, 4, Hex },
+ { "r25" , Uint, 4, Hex },
+ { "r26" , Uint, 4, Hex },
+ { "r27" , Uint, 4, Hex },
+ { "r28" , Uint, 4, Hex },
+ { "r29" , Uint, 4, Hex },
+ { "r30" , Uint, 4, Hex },
+ { "r31" , Uint, 4, Hex },
+ { "cr" , Uint, 4, Hex },
+ { "xer" , Uint, 4, Hex },
+ { "lr" , Uint, 4, Hex },
+ { "ctr" , Uint, 4, Hex },
+ { "mq" , Uint, 4, Hex },
+ { "vrsave", Uint, 4, Hex },
+};
+
+// Floating point registers
+static DNBRegisterInfo g_fpr_registers[] =
+{
+ { "fp0" , IEEE754, 8, Float },
+ { "fp1" , IEEE754, 8, Float },
+ { "fp2" , IEEE754, 8, Float },
+ { "fp3" , IEEE754, 8, Float },
+ { "fp4" , IEEE754, 8, Float },
+ { "fp5" , IEEE754, 8, Float },
+ { "fp6" , IEEE754, 8, Float },
+ { "fp7" , IEEE754, 8, Float },
+ { "fp8" , IEEE754, 8, Float },
+ { "fp9" , IEEE754, 8, Float },
+ { "fp10" , IEEE754, 8, Float },
+ { "fp11" , IEEE754, 8, Float },
+ { "fp12" , IEEE754, 8, Float },
+ { "fp13" , IEEE754, 8, Float },
+ { "fp14" , IEEE754, 8, Float },
+ { "fp15" , IEEE754, 8, Float },
+ { "fp16" , IEEE754, 8, Float },
+ { "fp17" , IEEE754, 8, Float },
+ { "fp18" , IEEE754, 8, Float },
+ { "fp19" , IEEE754, 8, Float },
+ { "fp20" , IEEE754, 8, Float },
+ { "fp21" , IEEE754, 8, Float },
+ { "fp22" , IEEE754, 8, Float },
+ { "fp23" , IEEE754, 8, Float },
+ { "fp24" , IEEE754, 8, Float },
+ { "fp25" , IEEE754, 8, Float },
+ { "fp26" , IEEE754, 8, Float },
+ { "fp27" , IEEE754, 8, Float },
+ { "fp28" , IEEE754, 8, Float },
+ { "fp29" , IEEE754, 8, Float },
+ { "fp30" , IEEE754, 8, Float },
+ { "fp31" , IEEE754, 8, Float },
+ { "fpscr" , Uint, 4, Hex }
+};
+
+// Exception registers
+
+static DNBRegisterInfo g_exc_registers[] =
+{
+ { "dar" , Uint, 4, Hex },
+ { "dsisr" , Uint, 4, Hex },
+ { "exception" , Uint, 4, Hex }
+};
+
+// Altivec registers
+static DNBRegisterInfo g_vec_registers[] =
+{
+ { "vr0" , Vector, 16, VectorOfFloat32 },
+ { "vr1" , Vector, 16, VectorOfFloat32 },
+ { "vr2" , Vector, 16, VectorOfFloat32 },
+ { "vr3" , Vector, 16, VectorOfFloat32 },
+ { "vr4" , Vector, 16, VectorOfFloat32 },
+ { "vr5" , Vector, 16, VectorOfFloat32 },
+ { "vr6" , Vector, 16, VectorOfFloat32 },
+ { "vr7" , Vector, 16, VectorOfFloat32 },
+ { "vr8" , Vector, 16, VectorOfFloat32 },
+ { "vr9" , Vector, 16, VectorOfFloat32 },
+ { "vr10" , Vector, 16, VectorOfFloat32 },
+ { "vr11" , Vector, 16, VectorOfFloat32 },
+ { "vr12" , Vector, 16, VectorOfFloat32 },
+ { "vr13" , Vector, 16, VectorOfFloat32 },
+ { "vr14" , Vector, 16, VectorOfFloat32 },
+ { "vr15" , Vector, 16, VectorOfFloat32 },
+ { "vr16" , Vector, 16, VectorOfFloat32 },
+ { "vr17" , Vector, 16, VectorOfFloat32 },
+ { "vr18" , Vector, 16, VectorOfFloat32 },
+ { "vr19" , Vector, 16, VectorOfFloat32 },
+ { "vr20" , Vector, 16, VectorOfFloat32 },
+ { "vr21" , Vector, 16, VectorOfFloat32 },
+ { "vr22" , Vector, 16, VectorOfFloat32 },
+ { "vr23" , Vector, 16, VectorOfFloat32 },
+ { "vr24" , Vector, 16, VectorOfFloat32 },
+ { "vr25" , Vector, 16, VectorOfFloat32 },
+ { "vr26" , Vector, 16, VectorOfFloat32 },
+ { "vr27" , Vector, 16, VectorOfFloat32 },
+ { "vr28" , Vector, 16, VectorOfFloat32 },
+ { "vr29" , Vector, 16, VectorOfFloat32 },
+ { "vr30" , Vector, 16, VectorOfFloat32 },
+ { "vr31" , Vector, 16, VectorOfFloat32 },
+ { "vscr" , Uint, 16, Hex },
+ { "vrvalid" , Uint, 4, Hex }
+};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_fpr_registers = sizeof(g_fpr_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t k_num_vec_registers = sizeof(g_vec_registers)/sizeof(DNBRegisterInfo);
+// Total number of registers for this architecture
+const size_t k_num_ppc_registers = k_num_gpr_registers + k_num_fpr_registers + k_num_exc_registers + k_num_vec_registers;
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+static const DNBRegisterSetInfo g_reg_sets[] =
+{
+ { "PowerPC Registers", NULL, k_num_ppc_registers },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpr_registers, k_num_fpr_registers },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers },
+ { "Altivec Registers", g_vec_registers, k_num_vec_registers }
+};
+// Total number of register sets for this architecture
+const size_t k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo);
+
+
+const DNBRegisterSetInfo *
+DNBArchMachPPC::GetRegisterSetInfo(nub_size_t *num_reg_sets) const
+{
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool
+DNBArchMachPPC::GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value) const
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = e_regNumGPR_srr0;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = e_regNumGPR_r1;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ // Return false for now instead of returning r30 as gcc 3.x would
+ // use a variety of registers for the FP and it takes inspecting
+ // the stack to make sure there is a frame pointer before we can
+ // determine the FP.
+ return false;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = e_regNumGPR_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = e_regNumGPR_srr1;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (!m_state.RegsAreValid(set))
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint32 = (&m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPR:
+ if (reg < 32)
+ {
+ value->value.float64 = m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpregs)[reg];
+ return true;
+ }
+ else if (reg == 32)
+ {
+ value->value.uint32 = m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpscr);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers)
+ {
+ value->value.uint32 = (&m_state.exc.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(dar))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetVEC:
+ if (reg < k_num_vec_registers)
+ {
+ if (reg < 33) // FP0 - FP31 and VSCR
+ {
+ // Copy all 4 uint32 values for this vector register
+ value->value.v_uint32[0] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][0];
+ value->value.v_uint32[1] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][1];
+ value->value.v_uint32[2] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][2];
+ value->value.v_uint32[3] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][3];
+ return true;
+ }
+ else if (reg == 34) // VRVALID
+ {
+ value->value.uint32 = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vrvalid);
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+kern_return_t
+DNBArchMachPPC::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL:
+ return GetGPRState(force) |
+ GetFPRState(force) |
+ GetEXCState(force) |
+ GetVECState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetFPR: return GetFPRState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ case e_regSetVEC: return GetVECState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchMachPPC::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() | SetFPRState() | SetEXCState() | SetVECState();
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetFPR: return SetFPRState();
+ case e_regSetEXC: return SetEXCState();
+ case e_regSetVEC: return SetVECState();
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchMachPPC::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+#endif // #if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+
diff --git a/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h
new file mode 100644
index 000000000000..8ea81538dc48
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h
@@ -0,0 +1,179 @@
+//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArchMachPPC_h__
+#define __DebugNubArchMachPPC_h__
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+
+#include "DNBArch.h"
+
+class MachThread;
+
+class DNBArchMachPPC : public DNBArchProtocol
+{
+public:
+ DNBArchMachPPC(MachThread *thread) :
+ m_thread(thread),
+ m_state()
+ {
+ }
+
+ virtual ~DNBArchMachPPC()
+ {
+ }
+
+ virtual const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets) const;
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value) const;
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual bool ThreadWillResume();
+ virtual bool ThreadDidStop();
+
+ static const uint8_t * SoftwareBreakpointOpcode (nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+protected:
+
+
+ kern_return_t EnableHardwareSingleStep (bool enable);
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPR,
+ e_regSetEXC,
+ e_regSetVEC,
+ kNumRegisterSets
+ } RegisterSet;
+
+ typedef enum RegisterSetWordSizeTag
+ {
+ e_regSetWordSizeGPR = PPC_THREAD_STATE_COUNT,
+ e_regSetWordSizeFPR = PPC_FLOAT_STATE_COUNT,
+ e_regSetWordSizeEXC = PPC_EXCEPTION_STATE_COUNT,
+ e_regSetWordSizeVEC = PPC_VECTOR_STATE_COUNT
+ } RegisterSetWordSize;
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ struct State
+ {
+ ppc_thread_state_t gpr;
+ ppc_float_state_t fpr;
+ ppc_exception_state_t exc;
+ ppc_vector_state_t vec;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpr_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t vec_errs[2]; // Read/Write errors
+
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpr_errs[i] = -1;
+ exc_errs[i] = -1;
+ vec_errs[i] = -1;
+ }
+ }
+ void InvalidateAllRegisterStates()
+ {
+ SetError (e_regSetALL, Read, -1);
+ }
+ kern_return_t GetError (int set, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL: return gpr_errs[err_idx] | fpr_errs[err_idx] | exc_errs[err_idx] | vec_errs[err_idx];
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetFPR: return fpr_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ case e_regSetVEC: return vec_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+ bool SetError (int set, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (set)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] = fpr_errs[err_idx] = exc_errs[err_idx] = vec_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPR:
+ fpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVEC:
+ vec_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetFPRState (bool force);
+ kern_return_t GetEXCState (bool force);
+ kern_return_t GetVECState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetFPRState ();
+ kern_return_t SetEXCState ();
+ kern_return_t SetVECState ();
+
+protected:
+ MachThread * m_thread;
+ State m_state;
+};
+
+#endif // #if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+#endif // #ifndef __DebugNubArchMachPPC_h__
diff --git a/tools/debugserver/source/MacOSX/stack_logging.h b/tools/debugserver/source/MacOSX/stack_logging.h
new file mode 100644
index 000000000000..5b0a3080349b
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/stack_logging.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef malloc_history_test_stack_logging_h
+#define malloc_history_test_stack_logging_h
+
+#import <malloc/malloc.h>
+
+#define stack_logging_type_free 0
+#define stack_logging_type_generic 1 /* anything that is not allocation/deallocation */
+#define stack_logging_type_alloc 2 /* malloc, realloc, etc... */
+#define stack_logging_type_dealloc 4 /* free, realloc, etc... */
+
+// Following flags are absorbed by stack_logging_log_stack()
+#define stack_logging_flag_zone 8 /* NSZoneMalloc, etc... */
+#define stack_logging_flag_calloc 16 /* multiply arguments to get the size */
+#define stack_logging_flag_object 32 /* NSAllocateObject(Class, extraBytes, zone) */
+#define stack_logging_flag_cleared 64 /* for NewEmptyHandle */
+#define stack_logging_flag_handle 128 /* for Handle (de-)allocation routines */
+#define stack_logging_flag_set_handle_size 256 /* (Handle, newSize) treated specially */
+
+/* Macro used to disguise addresses so that leak finding can work */
+#define STACK_LOGGING_DISGUISE(address) ((address) ^ 0x00005555) /* nicely idempotent */
+
+extern "C" int stack_logging_enable_logging; /* when clear, no logging takes place */
+extern "C" int stack_logging_dontcompact; /* default is to compact; when set does not compact alloc/free logs; useful for tracing history */
+
+
+extern "C" void stack_logging_log_stack(unsigned type, unsigned arg1, unsigned arg2, unsigned arg3, unsigned result, unsigned num_hot_to_skip);
+/* This is the old log-to-memory logger, which is now deprecated. It remains for compatibility with performance tools that haven't been updated to disk_stack_logging_log_stack() yet. */
+
+extern "C" void __disk_stack_logging_log_stack(uint32_t type_flags, uintptr_t zone_ptr, uintptr_t size, uintptr_t ptr_arg, uintptr_t return_val, uint32_t num_hot_to_skip);
+/* Fits as the malloc_logger; logs malloc/free/realloc events and can log custom events if called directly */
+
+
+/* 64-bit-aware stack log access. */
+typedef struct {
+ uint32_t type_flags;
+ uint64_t stack_identifier;
+ uint64_t argument;
+ mach_vm_address_t address;
+} mach_stack_logging_record_t;
+
+extern "C" kern_return_t __mach_stack_logging_get_frames(task_t task, mach_vm_address_t address, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count);
+/* Gets the last allocation record (malloc, realloc, or free) about address */
+
+extern "C" kern_return_t __mach_stack_logging_enumerate_records(task_t task, mach_vm_address_t address, void enumerator(mach_stack_logging_record_t, void *), void *context);
+/* Applies enumerator to all records involving address sending context as enumerator's second parameter; if !address, applies enumerator to all records */
+
+extern "C" kern_return_t __mach_stack_logging_frames_for_uniqued_stack(task_t task, uint64_t stack_identifier, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count);
+/* Given a uniqued_stack fills stack_frames_buffer */
+
+
+#pragma mark -
+#pragma mark Legacy
+
+/* The following is the old 32-bit-only, in-process-memory stack logging. This is deprecated and clients should move to the above 64-bit-aware disk stack logging SPI. */
+
+typedef struct {
+ unsigned type;
+ unsigned uniqued_stack;
+ unsigned argument;
+ unsigned address; /* disguised, to avoid confusing leaks */
+} stack_logging_record_t;
+
+typedef struct {
+ unsigned overall_num_bytes;
+ unsigned num_records;
+ unsigned lock; /* 0 means OK to lock; used for inter-process locking */
+ unsigned *uniquing_table; /* allocated using vm_allocate() */
+ /* hashtable organized as (PC, uniqued parent)
+ Only the second half of the table is active
+ To enable us to grow dynamically */
+ unsigned uniquing_table_num_pages; /* number of pages of the table */
+ unsigned extra_retain_count; /* not used by stack_logging_log_stack */
+ unsigned filler[2]; /* align to cache lines for better performance */
+ stack_logging_record_t records[0]; /* records follow here */
+} stack_logging_record_list_t;
+
+extern "C" stack_logging_record_list_t *stack_logging_the_record_list;
+/* This is the global variable containing all logs */
+
+extern "C" kern_return_t stack_logging_get_frames(task_t task, memory_reader_t reader, vm_address_t address, vm_address_t *stack_frames_buffer, unsigned max_stack_frames, unsigned *num_frames);
+/* Gets the last record in stack_logging_the_record_list about address */
+
+#define STACK_LOGGING_ENUMERATION_PROVIDED 1 // temporary to avoid dependencies between projects
+
+extern "C" kern_return_t stack_logging_enumerate_records(task_t task, memory_reader_t reader, vm_address_t address, void enumerator(stack_logging_record_t, void *), void *context);
+/* Gets all the records about address;
+ If !address, gets all records */
+
+extern "C" kern_return_t stack_logging_frames_for_uniqued_stack(task_t task, memory_reader_t reader, unsigned uniqued_stack, vm_address_t *stack_frames_buffer, unsigned max_stack_frames, unsigned *num_frames);
+/* Given a uniqued_stack fills stack_frames_buffer */
+
+
+
+extern "C" void thread_stack_pcs(vm_address_t *buffer, unsigned max, unsigned *num);
+/* Convenience to fill buffer with the PCs of the frames, starting with the hot frames;
+ num: returned number of frames
+ */
+
+#endif
diff --git a/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt b/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt
new file mode 100644
index 000000000000..bb41b04d9d9e
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Due to sources including headers like:
+# #include "MacOSX/i386/DNBArchImplI386.h"
+# we must include the grandparent directory...
+include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source)
+
+add_library(lldbDebugserverMacOSX_X86_64
+ DNBArchImplX86_64.cpp
+ )
diff --git a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp
new file mode 100644
index 000000000000..3d2805cddb91
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp
@@ -0,0 +1,2289 @@
+//===-- DNBArchImplX86_64.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include "MacOSX/x86_64/DNBArchImplX86_64.h"
+#include "../HasAVX.h"
+#include "DNBLog.h"
+#include "MachThread.h"
+#include "MachProcess.h"
+#include <mach/mach.h>
+#include <stdlib.h>
+
+#if defined (LLDB_DEBUGSERVER_RELEASE) || defined (LLDB_DEBUGSERVER_DEBUG)
+enum debugState {
+ debugStateUnknown,
+ debugStateOff,
+ debugStateOn
+};
+
+static debugState sFPUDebugState = debugStateUnknown;
+static debugState sAVXForceState = debugStateUnknown;
+
+static bool DebugFPURegs ()
+{
+ if (sFPUDebugState == debugStateUnknown)
+ {
+ if (getenv("DNB_DEBUG_FPU_REGS"))
+ sFPUDebugState = debugStateOn;
+ else
+ sFPUDebugState = debugStateOff;
+ }
+
+ return (sFPUDebugState == debugStateOn);
+}
+
+static bool ForceAVXRegs ()
+{
+ if (sFPUDebugState == debugStateUnknown)
+ {
+ if (getenv("DNB_DEBUG_X86_FORCE_AVX_REGS"))
+ sAVXForceState = debugStateOn;
+ else
+ sAVXForceState = debugStateOff;
+ }
+
+ return (sAVXForceState == debugStateOn);
+}
+
+#define DEBUG_FPU_REGS (DebugFPURegs())
+#define FORCE_AVX_REGS (ForceAVXRegs())
+#else
+#define DEBUG_FPU_REGS (0)
+#define FORCE_AVX_REGS (0)
+#endif
+
+
+extern "C" bool
+CPUHasAVX()
+{
+ enum AVXPresence
+ {
+ eAVXUnknown = -1,
+ eAVXNotPresent = 0,
+ eAVXPresent = 1
+ };
+
+ static AVXPresence g_has_avx = eAVXUnknown;
+ if (g_has_avx == eAVXUnknown)
+ {
+ g_has_avx = eAVXNotPresent;
+
+ // Only xnu-2020 or later has AVX support, any versions before
+ // this have a busted thread_get_state RPC where it would truncate
+ // the thread state buffer (<rdar://problem/10122874>). So we need to
+ // verify the kernel version number manually or disable AVX support.
+ int mib[2];
+ char buffer[1024];
+ size_t length = sizeof(buffer);
+ uint64_t xnu_version = 0;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_VERSION;
+ int err = ::sysctl(mib, 2, &buffer, &length, NULL, 0);
+ if (err == 0)
+ {
+ const char *xnu = strstr (buffer, "xnu-");
+ if (xnu)
+ {
+ const char *xnu_version_cstr = xnu + 4;
+ xnu_version = strtoull (xnu_version_cstr, NULL, 0);
+ if (xnu_version >= 2020 && xnu_version != ULLONG_MAX)
+ {
+ if (::HasAVX())
+ {
+ g_has_avx = eAVXPresent;
+ }
+ }
+ }
+ }
+ DNBLogThreadedIf (LOG_THREAD, "CPUHasAVX(): g_has_avx = %i (err = %i, errno = %i, xnu_version = %llu)", g_has_avx, err, errno, xnu_version);
+ }
+
+ return (g_has_avx == eAVXPresent);
+}
+
+uint64_t
+DNBArchImplX86_64::GetPC(uint64_t failValue)
+{
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__rip;
+ return failValue;
+}
+
+kern_return_t
+DNBArchImplX86_64::SetPC(uint64_t value)
+{
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS)
+ {
+ m_state.context.gpr.__rip = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t
+DNBArchImplX86_64::GetSP(uint64_t failValue)
+{
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__rsp;
+ return failValue;
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t
+DNBArchImplX86_64::GetGPRState(bool force)
+{
+ if (force || m_state.GetError(e_regSetGPR, Read))
+ {
+#if DEBUG_GPR_VALUES
+ m_state.context.gpr.__rax = ('a' << 8) + 'x';
+ m_state.context.gpr.__rbx = ('b' << 8) + 'x';
+ m_state.context.gpr.__rcx = ('c' << 8) + 'x';
+ m_state.context.gpr.__rdx = ('d' << 8) + 'x';
+ m_state.context.gpr.__rdi = ('d' << 8) + 'i';
+ m_state.context.gpr.__rsi = ('s' << 8) + 'i';
+ m_state.context.gpr.__rbp = ('b' << 8) + 'p';
+ m_state.context.gpr.__rsp = ('s' << 8) + 'p';
+ m_state.context.gpr.__r8 = ('r' << 8) + '8';
+ m_state.context.gpr.__r9 = ('r' << 8) + '9';
+ m_state.context.gpr.__r10 = ('r' << 8) + 'a';
+ m_state.context.gpr.__r11 = ('r' << 8) + 'b';
+ m_state.context.gpr.__r12 = ('r' << 8) + 'c';
+ m_state.context.gpr.__r13 = ('r' << 8) + 'd';
+ m_state.context.gpr.__r14 = ('r' << 8) + 'e';
+ m_state.context.gpr.__r15 = ('r' << 8) + 'f';
+ m_state.context.gpr.__rip = ('i' << 8) + 'p';
+ m_state.context.gpr.__rflags = ('f' << 8) + 'l';
+ m_state.context.gpr.__cs = ('c' << 8) + 's';
+ m_state.context.gpr.__fs = ('f' << 8) + 's';
+ m_state.context.gpr.__gs = ('g' << 8) + 's';
+ m_state.SetError(e_regSetGPR, Read, 0);
+#else
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_THREAD_STATE, (thread_state_t)&m_state.context.gpr, &count));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx"
+ "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx"
+ "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx"
+ "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx"
+ "\n\trip = %16.16llx"
+ "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx",
+ m_thread->MachPortNumber(), x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT,
+ m_state.GetError(e_regSetGPR, Read),
+ m_state.context.gpr.__rax,m_state.context.gpr.__rbx,m_state.context.gpr.__rcx,
+ m_state.context.gpr.__rdx,m_state.context.gpr.__rdi,m_state.context.gpr.__rsi,
+ m_state.context.gpr.__rbp,m_state.context.gpr.__rsp,m_state.context.gpr.__r8,
+ m_state.context.gpr.__r9, m_state.context.gpr.__r10,m_state.context.gpr.__r11,
+ m_state.context.gpr.__r12,m_state.context.gpr.__r13,m_state.context.gpr.__r14,
+ m_state.context.gpr.__r15,m_state.context.gpr.__rip,m_state.context.gpr.__rflags,
+ m_state.context.gpr.__cs,m_state.context.gpr.__fs, m_state.context.gpr.__gs);
+
+ // DNBLogThreadedIf (LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ // "\n\trax = %16.16llx"
+ // "\n\trbx = %16.16llx"
+ // "\n\trcx = %16.16llx"
+ // "\n\trdx = %16.16llx"
+ // "\n\trdi = %16.16llx"
+ // "\n\trsi = %16.16llx"
+ // "\n\trbp = %16.16llx"
+ // "\n\trsp = %16.16llx"
+ // "\n\t r8 = %16.16llx"
+ // "\n\t r9 = %16.16llx"
+ // "\n\tr10 = %16.16llx"
+ // "\n\tr11 = %16.16llx"
+ // "\n\tr12 = %16.16llx"
+ // "\n\tr13 = %16.16llx"
+ // "\n\tr14 = %16.16llx"
+ // "\n\tr15 = %16.16llx"
+ // "\n\trip = %16.16llx"
+ // "\n\tflg = %16.16llx"
+ // "\n\t cs = %16.16llx"
+ // "\n\t fs = %16.16llx"
+ // "\n\t gs = %16.16llx",
+ // m_thread->MachPortNumber(),
+ // x86_THREAD_STATE64,
+ // x86_THREAD_STATE64_COUNT,
+ // m_state.GetError(e_regSetGPR, Read),
+ // m_state.context.gpr.__rax,
+ // m_state.context.gpr.__rbx,
+ // m_state.context.gpr.__rcx,
+ // m_state.context.gpr.__rdx,
+ // m_state.context.gpr.__rdi,
+ // m_state.context.gpr.__rsi,
+ // m_state.context.gpr.__rbp,
+ // m_state.context.gpr.__rsp,
+ // m_state.context.gpr.__r8,
+ // m_state.context.gpr.__r9,
+ // m_state.context.gpr.__r10,
+ // m_state.context.gpr.__r11,
+ // m_state.context.gpr.__r12,
+ // m_state.context.gpr.__r13,
+ // m_state.context.gpr.__r14,
+ // m_state.context.gpr.__r15,
+ // m_state.context.gpr.__rip,
+ // m_state.context.gpr.__rflags,
+ // m_state.context.gpr.__cs,
+ // m_state.context.gpr.__fs,
+ // m_state.context.gpr.__gs);
+#endif
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_FPU_REGS 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t
+DNBArchImplX86_64::GetFPUState(bool force)
+{
+ if (force || m_state.GetError(e_regSetFPU, Read))
+ {
+ if (DEBUG_FPU_REGS) {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ m_state.context.fpu.avx.__fpu_reserved[0] = -1;
+ m_state.context.fpu.avx.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.avx.__fpu_ftw = 1;
+ m_state.context.fpu.avx.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.avx.__fpu_fop = 2;
+ m_state.context.fpu.avx.__fpu_ip = 3;
+ m_state.context.fpu.avx.__fpu_cs = 4;
+ m_state.context.fpu.avx.__fpu_rsrv2 = UINT8_MAX;
+ m_state.context.fpu.avx.__fpu_dp = 5;
+ m_state.context.fpu.avx.__fpu_ds = 6;
+ m_state.context.fpu.avx.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.avx.__fpu_mxcsr = 8;
+ m_state.context.fpu.avx.__fpu_mxcsrmask = 9;
+ int i;
+ for (i=0; i<16; ++i)
+ {
+ if (i<10)
+ {
+ m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = 'h';
+ }
+ else
+ {
+ m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg[i] = '0' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg[i] = '1' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg[i] = '2' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg[i] = '3' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg[i] = '4' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg[i] = '5' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg[i] = '6' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg[i] = '7' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm8.__xmm_reg[i] = '8' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm9.__xmm_reg[i] = '9' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm10.__xmm_reg[i] = 'A' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm11.__xmm_reg[i] = 'B' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm12.__xmm_reg[i] = 'C' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm13.__xmm_reg[i] = 'D' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm14.__xmm_reg[i] = 'E' + 2 * i;
+ m_state.context.fpu.avx.__fpu_xmm15.__xmm_reg[i] = 'F' + 2 * i;
+
+ m_state.context.fpu.avx.__fpu_ymmh0.__xmm_reg[i] = '0' + i;
+ m_state.context.fpu.avx.__fpu_ymmh1.__xmm_reg[i] = '1' + i;
+ m_state.context.fpu.avx.__fpu_ymmh2.__xmm_reg[i] = '2' + i;
+ m_state.context.fpu.avx.__fpu_ymmh3.__xmm_reg[i] = '3' + i;
+ m_state.context.fpu.avx.__fpu_ymmh4.__xmm_reg[i] = '4' + i;
+ m_state.context.fpu.avx.__fpu_ymmh5.__xmm_reg[i] = '5' + i;
+ m_state.context.fpu.avx.__fpu_ymmh6.__xmm_reg[i] = '6' + i;
+ m_state.context.fpu.avx.__fpu_ymmh7.__xmm_reg[i] = '7' + i;
+ m_state.context.fpu.avx.__fpu_ymmh8.__xmm_reg[i] = '8' + i;
+ m_state.context.fpu.avx.__fpu_ymmh9.__xmm_reg[i] = '9' + i;
+ m_state.context.fpu.avx.__fpu_ymmh10.__xmm_reg[i] = 'A' + i;
+ m_state.context.fpu.avx.__fpu_ymmh11.__xmm_reg[i] = 'B' + i;
+ m_state.context.fpu.avx.__fpu_ymmh12.__xmm_reg[i] = 'C' + i;
+ m_state.context.fpu.avx.__fpu_ymmh13.__xmm_reg[i] = 'D' + i;
+ m_state.context.fpu.avx.__fpu_ymmh14.__xmm_reg[i] = 'E' + i;
+ m_state.context.fpu.avx.__fpu_ymmh15.__xmm_reg[i] = 'F' + i;
+ }
+ for (i=0; i<sizeof(m_state.context.fpu.avx.__fpu_rsrv4); ++i)
+ m_state.context.fpu.avx.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.avx.__fpu_reserved1 = -1;
+ for (i=0; i<sizeof(m_state.context.fpu.avx.__avx_reserved1); ++i)
+ m_state.context.fpu.avx.__avx_reserved1[i] = INT8_MIN;
+ m_state.SetError(e_regSetFPU, Read, 0);
+ }
+ else
+ {
+ m_state.context.fpu.no_avx.__fpu_reserved[0] = -1;
+ m_state.context.fpu.no_avx.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.no_avx.__fpu_ftw = 1;
+ m_state.context.fpu.no_avx.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.no_avx.__fpu_fop = 2;
+ m_state.context.fpu.no_avx.__fpu_ip = 3;
+ m_state.context.fpu.no_avx.__fpu_cs = 4;
+ m_state.context.fpu.no_avx.__fpu_rsrv2 = 5;
+ m_state.context.fpu.no_avx.__fpu_dp = 6;
+ m_state.context.fpu.no_avx.__fpu_ds = 7;
+ m_state.context.fpu.no_avx.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.no_avx.__fpu_mxcsr = 8;
+ m_state.context.fpu.no_avx.__fpu_mxcsrmask = 9;
+ int i;
+ for (i=0; i<16; ++i)
+ {
+ if (i<10)
+ {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = 'h';
+ }
+ else
+ {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg[i] = '0';
+ m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg[i] = '1';
+ m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg[i] = '2';
+ m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg[i] = '3';
+ m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg[i] = '4';
+ m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg[i] = '5';
+ m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg[i] = '6';
+ m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg[i] = '7';
+ m_state.context.fpu.no_avx.__fpu_xmm8.__xmm_reg[i] = '8';
+ m_state.context.fpu.no_avx.__fpu_xmm9.__xmm_reg[i] = '9';
+ m_state.context.fpu.no_avx.__fpu_xmm10.__xmm_reg[i] = 'A';
+ m_state.context.fpu.no_avx.__fpu_xmm11.__xmm_reg[i] = 'B';
+ m_state.context.fpu.no_avx.__fpu_xmm12.__xmm_reg[i] = 'C';
+ m_state.context.fpu.no_avx.__fpu_xmm13.__xmm_reg[i] = 'D';
+ m_state.context.fpu.no_avx.__fpu_xmm14.__xmm_reg[i] = 'E';
+ m_state.context.fpu.no_avx.__fpu_xmm15.__xmm_reg[i] = 'F';
+ }
+ for (i=0; i<sizeof(m_state.context.fpu.no_avx.__fpu_rsrv4); ++i)
+ m_state.context.fpu.no_avx.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_reserved1 = -1;
+ m_state.SetError(e_regSetFPU, Read, 0);
+ }
+ }
+ else
+ {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeAVX;
+ m_state.SetError(e_regSetFPU, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, &count));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &avx, %u (%u passed in) carp) => 0x%8.8x",
+ m_thread->MachPortNumber(), __x86_64_AVX_STATE, (uint32_t)count,
+ e_regSetWordSizeAVX, m_state.GetError(e_regSetFPU, Read));
+ }
+ else
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeFPU;
+ m_state.SetError(e_regSetFPU, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, &count));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &fpu, %u (%u passed in) => 0x%8.8x",
+ m_thread->MachPortNumber(), __x86_64_FLOAT_STATE, (uint32_t)count,
+ e_regSetWordSizeFPU, m_state.GetError(e_regSetFPU, Read));
+ }
+ }
+ }
+ return m_state.GetError(e_regSetFPU, Read);
+}
+
+kern_return_t
+DNBArchImplX86_64::GetEXCState(bool force)
+{
+ if (force || m_state.GetError(e_regSetEXC, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t
+DNBArchImplX86_64::SetGPRState()
+{
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_THREAD_STATE, (thread_state_t)&m_state.context.gpr, e_regSetWordSizeGPR));
+ DNBLogThreadedIf (LOG_THREAD, "::thread_set_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx"
+ "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx"
+ "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx"
+ "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx"
+ "\n\trip = %16.16llx"
+ "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx",
+ m_thread->MachPortNumber(), __x86_64_THREAD_STATE, e_regSetWordSizeGPR,
+ m_state.GetError(e_regSetGPR, Write),
+ m_state.context.gpr.__rax,m_state.context.gpr.__rbx,m_state.context.gpr.__rcx,
+ m_state.context.gpr.__rdx,m_state.context.gpr.__rdi,m_state.context.gpr.__rsi,
+ m_state.context.gpr.__rbp,m_state.context.gpr.__rsp,m_state.context.gpr.__r8,
+ m_state.context.gpr.__r9, m_state.context.gpr.__r10,m_state.context.gpr.__r11,
+ m_state.context.gpr.__r12,m_state.context.gpr.__r13,m_state.context.gpr.__r14,
+ m_state.context.gpr.__r15,m_state.context.gpr.__rip,m_state.context.gpr.__rflags,
+ m_state.context.gpr.__cs, m_state.context.gpr.__fs, m_state.context.gpr.__gs);
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t
+DNBArchImplX86_64::SetFPUState()
+{
+ if (DEBUG_FPU_REGS)
+ {
+ m_state.SetError(e_regSetFPU, Write, 0);
+ return m_state.GetError(e_regSetFPU, Write);
+ }
+ else
+ {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, e_regSetWordSizeAVX));
+ return m_state.GetError(e_regSetFPU, Write);
+ }
+ else
+ {
+ m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, e_regSetWordSizeFPU));
+ return m_state.GetError(e_regSetFPU, Write);
+ }
+ }
+}
+
+kern_return_t
+DNBArchImplX86_64::SetEXCState()
+{
+ m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+kern_return_t
+DNBArchImplX86_64::GetDBGState(bool force)
+{
+ if (force || m_state.GetError(e_regSetDBG, Read))
+ {
+ mach_msg_type_number_t count = e_regSetWordSizeDBG;
+ m_state.SetError(e_regSetDBG, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, &count));
+ }
+ return m_state.GetError(e_regSetDBG, Read);
+}
+
+kern_return_t
+DNBArchImplX86_64::SetDBGState(bool also_set_on_task)
+{
+ m_state.SetError(e_regSetDBG, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG));
+ if (also_set_on_task)
+ {
+ kern_return_t kret = ::task_set_state(m_thread->Process()->Task().TaskPort(), __x86_64_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG);
+ if (kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::SetDBGState failed to set debug control register state: 0x%8.8x.", kret);
+ }
+ return m_state.GetError(e_regSetDBG, Write);
+}
+
+void
+DNBArchImplX86_64::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping())
+ {
+ // This is the primary thread, let the arch do anything it needs
+ EnableHardwareSingleStep(true);
+ }
+
+ // Reset the debug status register, if necessary, before we resume.
+ kern_return_t kret = GetDBGState(false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::ThreadWillResume() GetDBGState() => 0x%8.8x.", kret);
+ if (kret != KERN_SUCCESS)
+ return;
+
+ DBG &debug_state = m_state.context.dbg;
+ bool need_reset = false;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ if (IsWatchpointHit(debug_state, i))
+ need_reset = true;
+
+ if (need_reset)
+ {
+ ClearWatchpointHits(debug_state);
+ kret = SetDBGState(false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::ThreadWillResume() SetDBGState() => 0x%8.8x.", kret);
+ }
+}
+
+bool
+DNBArchImplX86_64::ThreadDidStop()
+{
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping())
+ {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool
+DNBArchImplX86_64::NotifyException(MachException::Data& exc)
+{
+ switch (exc.exc_type)
+ {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2)
+ {
+ // exc_code = EXC_I386_BPT
+ //
+ nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS);
+ if (pc != INVALID_NUB_ADDRESS && pc > 0)
+ {
+ pc -= 1;
+ // Check for a breakpoint at one byte prior to the current PC value
+ // since the PC will be just past the trap.
+
+ DNBBreakpoint *bp = m_thread->Process()->Breakpoints().FindByAddress(pc);
+ if (bp)
+ {
+ // Backup the PC for i386 since the trap was taken and the PC
+ // is at the address following the single byte trap instruction.
+ if (m_state.context.gpr.__rip > 0)
+ {
+ m_state.context.gpr.__rip = pc;
+ // Write the new PC back out
+ SetGPRState ();
+ }
+ }
+ return true;
+ }
+ }
+ else if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 1)
+ {
+ // exc_code = EXC_I386_SGL
+ //
+ // Check whether this corresponds to a watchpoint hit event.
+ // If yes, set the exc_sub_code to the data break address.
+ nub_addr_t addr = 0;
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+ if (hw_index != INVALID_NUB_HW_INDEX)
+ {
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+uint32_t
+DNBArchImplX86_64::NumSupportedHardwareWatchpoints()
+{
+ // Available debug address registers: dr0, dr1, dr2, dr3.
+ return 4;
+}
+
+static uint32_t
+size_and_rw_bits(nub_size_t size, bool read, bool write)
+{
+ uint32_t rw;
+ if (read) {
+ rw = 0x3; // READ or READ/WRITE
+ } else if (write) {
+ rw = 0x1; // WRITE
+ } else {
+ assert(0 && "read and write cannot both be false");
+ }
+
+ switch (size) {
+ case 1:
+ return rw;
+ case 2:
+ return (0x1 << 2) | rw;
+ case 4:
+ return (0x3 << 2) | rw;
+ case 8:
+ return (0x2 << 2) | rw;
+ }
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ return 0;
+}
+void
+DNBArchImplX86_64::SetWatchpoint(DBG &debug_state, uint32_t hw_index, nub_addr_t addr, nub_size_t size, bool read, bool write)
+{
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ debug_state.__dr7 |= (1 << (2*hw_index) |
+ size_and_rw_bits(size, read, write) << (16+4*hw_index));
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = addr; break;
+ case 1:
+ debug_state.__dr1 = addr; break;
+ case 2:
+ debug_state.__dr2 = addr; break;
+ case 3:
+ debug_state.__dr3 = addr; break;
+ default:
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+void
+DNBArchImplX86_64::ClearWatchpoint(DBG &debug_state, uint32_t hw_index)
+{
+ debug_state.__dr7 &= ~(3 << (2*hw_index));
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = 0; break;
+ case 1:
+ debug_state.__dr1 = 0; break;
+ case 2:
+ debug_state.__dr2 = 0; break;
+ case 3:
+ debug_state.__dr3 = 0; break;
+ default:
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+bool
+DNBArchImplX86_64::IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index)
+{
+ // Check dr7 (debug control register) for local/global enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ return (debug_state.__dr7 & (3 << (2*hw_index))) == 0;
+}
+
+// Resets local copy of debug status register to wait for the next debug exception.
+void
+DNBArchImplX86_64::ClearWatchpointHits(DBG &debug_state)
+{
+ // See also IsWatchpointHit().
+ debug_state.__dr6 = 0;
+ return;
+}
+
+bool
+DNBArchImplX86_64::IsWatchpointHit(const DBG &debug_state, uint32_t hw_index)
+{
+ // Check dr6 (debug status register) whether a watchpoint hits:
+ // is watchpoint hit?
+ // |
+ // v
+ // dr0 -> bits{0}
+ // dr1 -> bits{1}
+ // dr2 -> bits{2}
+ // dr3 -> bits{3}
+ return (debug_state.__dr6 & (1 << hw_index));
+}
+
+nub_addr_t
+DNBArchImplX86_64::GetWatchAddress(const DBG &debug_state, uint32_t hw_index)
+{
+ switch (hw_index) {
+ case 0:
+ return debug_state.__dr0;
+ case 1:
+ return debug_state.__dr1;
+ case 2:
+ return debug_state.__dr2;
+ case 3:
+ return debug_state.__dr3;
+ }
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ return 0;
+}
+
+bool
+DNBArchImplX86_64::StartTransForHWP()
+{
+ if (m_2pc_trans_state != Trans_Done && m_2pc_trans_state != Trans_Rolled_Back)
+ DNBLogError ("%s inconsistent state detected, expected %d or %d, got: %d", __FUNCTION__, Trans_Done, Trans_Rolled_Back, m_2pc_trans_state);
+ m_2pc_dbg_checkpoint = m_state.context.dbg;
+ m_2pc_trans_state = Trans_Pending;
+ return true;
+}
+bool
+DNBArchImplX86_64::RollbackTransForHWP()
+{
+ m_state.context.dbg = m_2pc_dbg_checkpoint;
+ if (m_2pc_trans_state != Trans_Pending)
+ DNBLogError ("%s inconsistent state detected, expected %d, got: %d", __FUNCTION__, Trans_Pending, m_2pc_trans_state);
+ m_2pc_trans_state = Trans_Rolled_Back;
+ kern_return_t kret = SetDBGState(false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::RollbackTransForHWP() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else
+ return false;
+}
+bool
+DNBArchImplX86_64::FinishTransForHWP()
+{
+ m_2pc_trans_state = Trans_Done;
+ return true;
+}
+DNBArchImplX86_64::DBG
+DNBArchImplX86_64::GetDBGCheckpoint()
+{
+ return m_2pc_dbg_checkpoint;
+}
+
+uint32_t
+DNBArchImplX86_64::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task)
+{
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::EnableHardwareWatchpoint(addr = 0x%llx, size = %llu, read = %u, write = %u)", (uint64_t)addr, (uint64_t)size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can only watch 1, 2, 4, or 8 bytes.
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return INVALID_NUB_HW_INDEX;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ DBG &debug_state = m_state.context.dbg;
+ for (i = 0; i < num_hw_watchpoints; ++i)
+ {
+ if (IsWatchpointVacant(debug_state, i))
+ break;
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints)
+ {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ SetWatchpoint(debug_state, i, addr, size, read, write);
+ // Now set the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::EnableHardwareWatchpoint() SetDBGState() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+DNBArchImplX86_64::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task)
+{
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ DBG &debug_state = m_state.context.dbg;
+ if (hw_index < num_hw_points && !IsWatchpointVacant(debug_state, hw_index))
+ {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ ClearWatchpoint(debug_state, hw_index);
+ // Now disable the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::DisableHardwareWatchpoint( %u )",
+ hw_index);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ }
+ return false;
+}
+
+// Iterate through the debug status register; return the index of the first hit.
+uint32_t
+DNBArchImplX86_64::GetHardwareWatchpointHit(nub_addr_t &addr)
+{
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret);
+ if (kret == KERN_SUCCESS)
+ {
+ DBG &debug_state = m_state.context.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ {
+ if (IsWatchpointHit(debug_state, i))
+ {
+ addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchImplX86_64::GetHardwareWatchpointHit() found => %u (addr = 0x%llx).",
+ i,
+ (uint64_t)addr);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t
+DNBArchImplX86_64::EnableHardwareSingleStep (bool enable)
+{
+ if (GetGPRState(false) == KERN_SUCCESS)
+ {
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ m_state.context.gpr.__rflags |= trace_bit;
+ else
+ m_state.context.gpr.__rflags &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+
+//----------------------------------------------------------------------
+// Register information definitions
+//----------------------------------------------------------------------
+
+enum
+{
+ gpr_rax = 0,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs,
+ gpr_eax,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_r8d, // Low 32 bits or r8
+ gpr_r9d, // Low 32 bits or r9
+ gpr_r10d, // Low 32 bits or r10
+ gpr_r11d, // Low 32 bits or r11
+ gpr_r12d, // Low 32 bits or r12
+ gpr_r13d, // Low 32 bits or r13
+ gpr_r14d, // Low 32 bits or r14
+ gpr_r15d, // Low 32 bits or r15
+ gpr_ax ,
+ gpr_bx ,
+ gpr_cx ,
+ gpr_dx ,
+ gpr_di ,
+ gpr_si ,
+ gpr_bp ,
+ gpr_sp ,
+ gpr_r8w, // Low 16 bits or r8
+ gpr_r9w, // Low 16 bits or r9
+ gpr_r10w, // Low 16 bits or r10
+ gpr_r11w, // Low 16 bits or r11
+ gpr_r12w, // Low 16 bits or r12
+ gpr_r13w, // Low 16 bits or r13
+ gpr_r14w, // Low 16 bits or r14
+ gpr_r15w, // Low 16 bits or r15
+ gpr_ah ,
+ gpr_bh ,
+ gpr_ch ,
+ gpr_dh ,
+ gpr_al ,
+ gpr_bl ,
+ gpr_cl ,
+ gpr_dl ,
+ gpr_dil,
+ gpr_sil,
+ gpr_bpl,
+ gpr_spl,
+ gpr_r8l, // Low 8 bits or r8
+ gpr_r9l, // Low 8 bits or r9
+ gpr_r10l, // Low 8 bits or r10
+ gpr_r11l, // Low 8 bits or r11
+ gpr_r12l, // Low 8 bits or r12
+ gpr_r13l, // Low 8 bits or r13
+ gpr_r14l, // Low 8 bits or r14
+ gpr_r15l, // Low 8 bits or r15
+ k_num_gpr_regs
+};
+
+enum {
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15,
+ fpu_ymm0,
+ fpu_ymm1,
+ fpu_ymm2,
+ fpu_ymm3,
+ fpu_ymm4,
+ fpu_ymm5,
+ fpu_ymm6,
+ fpu_ymm7,
+ fpu_ymm8,
+ fpu_ymm9,
+ fpu_ymm10,
+ fpu_ymm11,
+ fpu_ymm12,
+ fpu_ymm13,
+ fpu_ymm14,
+ fpu_ymm15,
+ k_num_fpu_regs,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum {
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+ k_num_exc_regs,
+};
+
+
+enum ehframe_dwarf_regnums
+{
+ ehframe_dwarf_rax = 0,
+ ehframe_dwarf_rdx = 1,
+ ehframe_dwarf_rcx = 2,
+ ehframe_dwarf_rbx = 3,
+ ehframe_dwarf_rsi = 4,
+ ehframe_dwarf_rdi = 5,
+ ehframe_dwarf_rbp = 6,
+ ehframe_dwarf_rsp = 7,
+ ehframe_dwarf_r8,
+ ehframe_dwarf_r9,
+ ehframe_dwarf_r10,
+ ehframe_dwarf_r11,
+ ehframe_dwarf_r12,
+ ehframe_dwarf_r13,
+ ehframe_dwarf_r14,
+ ehframe_dwarf_r15,
+ ehframe_dwarf_rip,
+ ehframe_dwarf_xmm0,
+ ehframe_dwarf_xmm1,
+ ehframe_dwarf_xmm2,
+ ehframe_dwarf_xmm3,
+ ehframe_dwarf_xmm4,
+ ehframe_dwarf_xmm5,
+ ehframe_dwarf_xmm6,
+ ehframe_dwarf_xmm7,
+ ehframe_dwarf_xmm8,
+ ehframe_dwarf_xmm9,
+ ehframe_dwarf_xmm10,
+ ehframe_dwarf_xmm11,
+ ehframe_dwarf_xmm12,
+ ehframe_dwarf_xmm13,
+ ehframe_dwarf_xmm14,
+ ehframe_dwarf_xmm15,
+ ehframe_dwarf_stmm0,
+ ehframe_dwarf_stmm1,
+ ehframe_dwarf_stmm2,
+ ehframe_dwarf_stmm3,
+ ehframe_dwarf_stmm4,
+ ehframe_dwarf_stmm5,
+ ehframe_dwarf_stmm6,
+ ehframe_dwarf_stmm7,
+ ehframe_dwarf_ymm0 = ehframe_dwarf_xmm0,
+ ehframe_dwarf_ymm1 = ehframe_dwarf_xmm1,
+ ehframe_dwarf_ymm2 = ehframe_dwarf_xmm2,
+ ehframe_dwarf_ymm3 = ehframe_dwarf_xmm3,
+ ehframe_dwarf_ymm4 = ehframe_dwarf_xmm4,
+ ehframe_dwarf_ymm5 = ehframe_dwarf_xmm5,
+ ehframe_dwarf_ymm6 = ehframe_dwarf_xmm6,
+ ehframe_dwarf_ymm7 = ehframe_dwarf_xmm7,
+ ehframe_dwarf_ymm8 = ehframe_dwarf_xmm8,
+ ehframe_dwarf_ymm9 = ehframe_dwarf_xmm9,
+ ehframe_dwarf_ymm10 = ehframe_dwarf_xmm10,
+ ehframe_dwarf_ymm11 = ehframe_dwarf_xmm11,
+ ehframe_dwarf_ymm12 = ehframe_dwarf_xmm12,
+ ehframe_dwarf_ymm13 = ehframe_dwarf_xmm13,
+ ehframe_dwarf_ymm14 = ehframe_dwarf_xmm14,
+ ehframe_dwarf_ymm15 = ehframe_dwarf_xmm15
+};
+
+enum debugserver_regnums
+{
+ debugserver_rax = 0,
+ debugserver_rbx = 1,
+ debugserver_rcx = 2,
+ debugserver_rdx = 3,
+ debugserver_rsi = 4,
+ debugserver_rdi = 5,
+ debugserver_rbp = 6,
+ debugserver_rsp = 7,
+ debugserver_r8 = 8,
+ debugserver_r9 = 9,
+ debugserver_r10 = 10,
+ debugserver_r11 = 11,
+ debugserver_r12 = 12,
+ debugserver_r13 = 13,
+ debugserver_r14 = 14,
+ debugserver_r15 = 15,
+ debugserver_rip = 16,
+ debugserver_rflags = 17,
+ debugserver_cs = 18,
+ debugserver_ss = 19,
+ debugserver_ds = 20,
+ debugserver_es = 21,
+ debugserver_fs = 22,
+ debugserver_gs = 23,
+ debugserver_stmm0 = 24,
+ debugserver_stmm1 = 25,
+ debugserver_stmm2 = 26,
+ debugserver_stmm3 = 27,
+ debugserver_stmm4 = 28,
+ debugserver_stmm5 = 29,
+ debugserver_stmm6 = 30,
+ debugserver_stmm7 = 31,
+ debugserver_fctrl = 32, debugserver_fcw = debugserver_fctrl,
+ debugserver_fstat = 33, debugserver_fsw = debugserver_fstat,
+ debugserver_ftag = 34, debugserver_ftw = debugserver_ftag,
+ debugserver_fiseg = 35, debugserver_fpu_cs = debugserver_fiseg,
+ debugserver_fioff = 36, debugserver_ip = debugserver_fioff,
+ debugserver_foseg = 37, debugserver_fpu_ds = debugserver_foseg,
+ debugserver_fooff = 38, debugserver_dp = debugserver_fooff,
+ debugserver_fop = 39,
+ debugserver_xmm0 = 40,
+ debugserver_xmm1 = 41,
+ debugserver_xmm2 = 42,
+ debugserver_xmm3 = 43,
+ debugserver_xmm4 = 44,
+ debugserver_xmm5 = 45,
+ debugserver_xmm6 = 46,
+ debugserver_xmm7 = 47,
+ debugserver_xmm8 = 48,
+ debugserver_xmm9 = 49,
+ debugserver_xmm10 = 50,
+ debugserver_xmm11 = 51,
+ debugserver_xmm12 = 52,
+ debugserver_xmm13 = 53,
+ debugserver_xmm14 = 54,
+ debugserver_xmm15 = 55,
+ debugserver_mxcsr = 56,
+ debugserver_ymm0 = debugserver_xmm0,
+ debugserver_ymm1 = debugserver_xmm1,
+ debugserver_ymm2 = debugserver_xmm2,
+ debugserver_ymm3 = debugserver_xmm3,
+ debugserver_ymm4 = debugserver_xmm4,
+ debugserver_ymm5 = debugserver_xmm5,
+ debugserver_ymm6 = debugserver_xmm6,
+ debugserver_ymm7 = debugserver_xmm7,
+ debugserver_ymm8 = debugserver_xmm8,
+ debugserver_ymm9 = debugserver_xmm9,
+ debugserver_ymm10 = debugserver_xmm10,
+ debugserver_ymm11 = debugserver_xmm11,
+ debugserver_ymm12 = debugserver_xmm12,
+ debugserver_ymm13 = debugserver_xmm13,
+ debugserver_ymm14 = debugserver_xmm14,
+ debugserver_ymm15 = debugserver_xmm15
+};
+
+#define GPR_OFFSET(reg) (offsetof (DNBArchImplX86_64::GPR, __##reg))
+#define FPU_OFFSET(reg) (offsetof (DNBArchImplX86_64::FPU, __fpu_##reg) + offsetof (DNBArchImplX86_64::Context, fpu.no_avx))
+#define AVX_OFFSET(reg) (offsetof (DNBArchImplX86_64::AVX, __fpu_##reg) + offsetof (DNBArchImplX86_64::Context, fpu.avx))
+#define EXC_OFFSET(reg) (offsetof (DNBArchImplX86_64::EXC, __##reg) + offsetof (DNBArchImplX86_64::Context, exc))
+#define AVX_OFFSET_YMM(n) (AVX_OFFSET(ymmh0) + (32 * n))
+
+#define GPR_SIZE(reg) (sizeof(((DNBArchImplX86_64::GPR *)NULL)->__##reg))
+#define FPU_SIZE_UINT(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg))
+#define FPU_SIZE_MMST(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__mmst_reg))
+#define FPU_SIZE_XMM(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__xmm_reg))
+#define FPU_SIZE_YMM(reg) (32)
+#define EXC_SIZE(reg) (sizeof(((DNBArchImplX86_64::EXC *)NULL)->__##reg))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR(reg) { e_regSetGPR, gpr_##reg, #reg, NULL, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, INVALID_NUB_REGNUM, debugserver_##reg, NULL, g_invalidate_##reg }
+#define DEFINE_GPR_ALT(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, gen, debugserver_##reg, NULL, g_invalidate_##reg }
+#define DEFINE_GPR_ALT2(reg, alt) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, debugserver_##reg, NULL, NULL }
+#define DEFINE_GPR_ALT3(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, gen, debugserver_##reg, NULL, NULL }
+#define DEFINE_GPR_ALT4(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, gen, debugserver_##reg, NULL, NULL }
+
+#define DEFINE_GPR_PSEUDO_32(reg32,reg64) { e_regSetGPR, gpr_##reg32, #reg32, NULL, Uint, Hex, 4, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 }
+#define DEFINE_GPR_PSEUDO_16(reg16,reg64) { e_regSetGPR, gpr_##reg16, #reg16, NULL, Uint, Hex, 2, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 }
+#define DEFINE_GPR_PSEUDO_8H(reg8,reg64) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 1,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 }
+#define DEFINE_GPR_PSEUDO_8L(reg8,reg64) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 }
+
+// General purpose registers for 64 bit
+
+const char *g_contained_rax[] = { "rax", NULL };
+const char *g_contained_rbx[] = { "rbx", NULL };
+const char *g_contained_rcx[] = { "rcx", NULL };
+const char *g_contained_rdx[] = { "rdx", NULL };
+const char *g_contained_rdi[] = { "rdi", NULL };
+const char *g_contained_rsi[] = { "rsi", NULL };
+const char *g_contained_rbp[] = { "rbp", NULL };
+const char *g_contained_rsp[] = { "rsp", NULL };
+const char *g_contained_r8[] = { "r8", NULL };
+const char *g_contained_r9[] = { "r9", NULL };
+const char *g_contained_r10[] = { "r10", NULL };
+const char *g_contained_r11[] = { "r11", NULL };
+const char *g_contained_r12[] = { "r12", NULL };
+const char *g_contained_r13[] = { "r13", NULL };
+const char *g_contained_r14[] = { "r14", NULL };
+const char *g_contained_r15[] = { "r15", NULL };
+
+const char *g_invalidate_rax[] = { "rax", "eax", "ax", "ah", "al", NULL };
+const char *g_invalidate_rbx[] = { "rbx", "ebx", "bx", "bh", "bl", NULL };
+const char *g_invalidate_rcx[] = { "rcx", "ecx", "cx", "ch", "cl", NULL };
+const char *g_invalidate_rdx[] = { "rdx", "edx", "dx", "dh", "dl", NULL };
+const char *g_invalidate_rdi[] = { "rdi", "edi", "di", "dil", NULL };
+const char *g_invalidate_rsi[] = { "rsi", "esi", "si", "sil", NULL };
+const char *g_invalidate_rbp[] = { "rbp", "ebp", "bp", "bpl", NULL };
+const char *g_invalidate_rsp[] = { "rsp", "esp", "sp", "spl", NULL };
+const char *g_invalidate_r8 [] = { "r8", "r8d", "r8w", "r8l", NULL };
+const char *g_invalidate_r9 [] = { "r9", "r9d", "r9w", "r9l", NULL };
+const char *g_invalidate_r10[] = { "r10", "r10d", "r10w", "r10l", NULL };
+const char *g_invalidate_r11[] = { "r11", "r11d", "r11w", "r11l", NULL };
+const char *g_invalidate_r12[] = { "r12", "r12d", "r12w", "r12l", NULL };
+const char *g_invalidate_r13[] = { "r13", "r13d", "r13w", "r13l", NULL };
+const char *g_invalidate_r14[] = { "r14", "r14d", "r14w", "r14l", NULL };
+const char *g_invalidate_r15[] = { "r15", "r15d", "r15w", "r15l", NULL };
+
+const DNBRegisterInfo
+DNBArchImplX86_64::g_gpr_registers[] =
+{
+ DEFINE_GPR (rax),
+ DEFINE_GPR (rbx),
+ DEFINE_GPR_ALT (rcx , "arg4", GENERIC_REGNUM_ARG4),
+ DEFINE_GPR_ALT (rdx , "arg3", GENERIC_REGNUM_ARG3),
+ DEFINE_GPR_ALT (rdi , "arg1", GENERIC_REGNUM_ARG1),
+ DEFINE_GPR_ALT (rsi , "arg2", GENERIC_REGNUM_ARG2),
+ DEFINE_GPR_ALT (rbp , "fp" , GENERIC_REGNUM_FP),
+ DEFINE_GPR_ALT (rsp , "sp" , GENERIC_REGNUM_SP),
+ DEFINE_GPR_ALT (r8 , "arg5", GENERIC_REGNUM_ARG5),
+ DEFINE_GPR_ALT (r9 , "arg6", GENERIC_REGNUM_ARG6),
+ DEFINE_GPR (r10),
+ DEFINE_GPR (r11),
+ DEFINE_GPR (r12),
+ DEFINE_GPR (r13),
+ DEFINE_GPR (r14),
+ DEFINE_GPR (r15),
+ DEFINE_GPR_ALT4 (rip , "pc", GENERIC_REGNUM_PC),
+ DEFINE_GPR_ALT3 (rflags, "flags", GENERIC_REGNUM_FLAGS),
+ DEFINE_GPR_ALT2 (cs, NULL),
+ DEFINE_GPR_ALT2 (fs, NULL),
+ DEFINE_GPR_ALT2 (gs, NULL),
+ DEFINE_GPR_PSEUDO_32 (eax, rax),
+ DEFINE_GPR_PSEUDO_32 (ebx, rbx),
+ DEFINE_GPR_PSEUDO_32 (ecx, rcx),
+ DEFINE_GPR_PSEUDO_32 (edx, rdx),
+ DEFINE_GPR_PSEUDO_32 (edi, rdi),
+ DEFINE_GPR_PSEUDO_32 (esi, rsi),
+ DEFINE_GPR_PSEUDO_32 (ebp, rbp),
+ DEFINE_GPR_PSEUDO_32 (esp, rsp),
+ DEFINE_GPR_PSEUDO_32 (r8d, r8),
+ DEFINE_GPR_PSEUDO_32 (r9d, r9),
+ DEFINE_GPR_PSEUDO_32 (r10d, r10),
+ DEFINE_GPR_PSEUDO_32 (r11d, r11),
+ DEFINE_GPR_PSEUDO_32 (r12d, r12),
+ DEFINE_GPR_PSEUDO_32 (r13d, r13),
+ DEFINE_GPR_PSEUDO_32 (r14d, r14),
+ DEFINE_GPR_PSEUDO_32 (r15d, r15),
+ DEFINE_GPR_PSEUDO_16 (ax , rax),
+ DEFINE_GPR_PSEUDO_16 (bx , rbx),
+ DEFINE_GPR_PSEUDO_16 (cx , rcx),
+ DEFINE_GPR_PSEUDO_16 (dx , rdx),
+ DEFINE_GPR_PSEUDO_16 (di , rdi),
+ DEFINE_GPR_PSEUDO_16 (si , rsi),
+ DEFINE_GPR_PSEUDO_16 (bp , rbp),
+ DEFINE_GPR_PSEUDO_16 (sp , rsp),
+ DEFINE_GPR_PSEUDO_16 (r8w, r8),
+ DEFINE_GPR_PSEUDO_16 (r9w, r9),
+ DEFINE_GPR_PSEUDO_16 (r10w, r10),
+ DEFINE_GPR_PSEUDO_16 (r11w, r11),
+ DEFINE_GPR_PSEUDO_16 (r12w, r12),
+ DEFINE_GPR_PSEUDO_16 (r13w, r13),
+ DEFINE_GPR_PSEUDO_16 (r14w, r14),
+ DEFINE_GPR_PSEUDO_16 (r15w, r15),
+ DEFINE_GPR_PSEUDO_8H (ah , rax),
+ DEFINE_GPR_PSEUDO_8H (bh , rbx),
+ DEFINE_GPR_PSEUDO_8H (ch , rcx),
+ DEFINE_GPR_PSEUDO_8H (dh , rdx),
+ DEFINE_GPR_PSEUDO_8L (al , rax),
+ DEFINE_GPR_PSEUDO_8L (bl , rbx),
+ DEFINE_GPR_PSEUDO_8L (cl , rcx),
+ DEFINE_GPR_PSEUDO_8L (dl , rdx),
+ DEFINE_GPR_PSEUDO_8L (dil, rdi),
+ DEFINE_GPR_PSEUDO_8L (sil, rsi),
+ DEFINE_GPR_PSEUDO_8L (bpl, rbp),
+ DEFINE_GPR_PSEUDO_8L (spl, rsp),
+ DEFINE_GPR_PSEUDO_8L (r8l, r8),
+ DEFINE_GPR_PSEUDO_8L (r9l, r9),
+ DEFINE_GPR_PSEUDO_8L (r10l, r10),
+ DEFINE_GPR_PSEUDO_8L (r11l, r11),
+ DEFINE_GPR_PSEUDO_8L (r12l, r12),
+ DEFINE_GPR_PSEUDO_8L (r13l, r13),
+ DEFINE_GPR_PSEUDO_8L (r14l, r14),
+ DEFINE_GPR_PSEUDO_8L (r15l, r15)
+};
+
+// Floating point registers 64 bit
+const DNBRegisterInfo
+DNBArchImplX86_64::g_fpu_registers_no_avx[] =
+{
+ { e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , -1U, -1U, -1U, -1U, NULL, NULL },
+
+ { e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), ehframe_dwarf_stmm0, ehframe_dwarf_stmm0, -1U, debugserver_stmm0, NULL, NULL },
+ { e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), ehframe_dwarf_stmm1, ehframe_dwarf_stmm1, -1U, debugserver_stmm1, NULL, NULL },
+ { e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), ehframe_dwarf_stmm2, ehframe_dwarf_stmm2, -1U, debugserver_stmm2, NULL, NULL },
+ { e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), ehframe_dwarf_stmm3, ehframe_dwarf_stmm3, -1U, debugserver_stmm3, NULL, NULL },
+ { e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), ehframe_dwarf_stmm4, ehframe_dwarf_stmm4, -1U, debugserver_stmm4, NULL, NULL },
+ { e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), ehframe_dwarf_stmm5, ehframe_dwarf_stmm5, -1U, debugserver_stmm5, NULL, NULL },
+ { e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), ehframe_dwarf_stmm6, ehframe_dwarf_stmm6, -1U, debugserver_stmm6, NULL, NULL },
+ { e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), ehframe_dwarf_stmm7, ehframe_dwarf_stmm7, -1U, debugserver_stmm7, NULL, NULL },
+
+ { e_regSetFPU, fpu_xmm0 , "xmm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0) , FPU_OFFSET(xmm0) , ehframe_dwarf_xmm0 , ehframe_dwarf_xmm0 , -1U, debugserver_xmm0 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm1 , "xmm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1) , FPU_OFFSET(xmm1) , ehframe_dwarf_xmm1 , ehframe_dwarf_xmm1 , -1U, debugserver_xmm1 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm2 , "xmm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2) , FPU_OFFSET(xmm2) , ehframe_dwarf_xmm2 , ehframe_dwarf_xmm2 , -1U, debugserver_xmm2 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm3 , "xmm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3) , FPU_OFFSET(xmm3) , ehframe_dwarf_xmm3 , ehframe_dwarf_xmm3 , -1U, debugserver_xmm3 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm4 , "xmm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4) , FPU_OFFSET(xmm4) , ehframe_dwarf_xmm4 , ehframe_dwarf_xmm4 , -1U, debugserver_xmm4 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm5 , "xmm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5) , FPU_OFFSET(xmm5) , ehframe_dwarf_xmm5 , ehframe_dwarf_xmm5 , -1U, debugserver_xmm5 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm6 , "xmm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6) , FPU_OFFSET(xmm6) , ehframe_dwarf_xmm6 , ehframe_dwarf_xmm6 , -1U, debugserver_xmm6 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm7 , "xmm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7) , FPU_OFFSET(xmm7) , ehframe_dwarf_xmm7 , ehframe_dwarf_xmm7 , -1U, debugserver_xmm7 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm8 , "xmm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm8) , FPU_OFFSET(xmm8) , ehframe_dwarf_xmm8 , ehframe_dwarf_xmm8 , -1U, debugserver_xmm8 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm9 , "xmm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm9) , FPU_OFFSET(xmm9) , ehframe_dwarf_xmm9 , ehframe_dwarf_xmm9 , -1U, debugserver_xmm9 , NULL, NULL },
+ { e_regSetFPU, fpu_xmm10, "xmm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm10) , FPU_OFFSET(xmm10), ehframe_dwarf_xmm10, ehframe_dwarf_xmm10, -1U, debugserver_xmm10, NULL, NULL },
+ { e_regSetFPU, fpu_xmm11, "xmm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm11) , FPU_OFFSET(xmm11), ehframe_dwarf_xmm11, ehframe_dwarf_xmm11, -1U, debugserver_xmm11, NULL, NULL },
+ { e_regSetFPU, fpu_xmm12, "xmm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm12) , FPU_OFFSET(xmm12), ehframe_dwarf_xmm12, ehframe_dwarf_xmm12, -1U, debugserver_xmm12, NULL, NULL },
+ { e_regSetFPU, fpu_xmm13, "xmm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm13) , FPU_OFFSET(xmm13), ehframe_dwarf_xmm13, ehframe_dwarf_xmm13, -1U, debugserver_xmm13, NULL, NULL },
+ { e_regSetFPU, fpu_xmm14, "xmm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm14) , FPU_OFFSET(xmm14), ehframe_dwarf_xmm14, ehframe_dwarf_xmm14, -1U, debugserver_xmm14, NULL, NULL },
+ { e_regSetFPU, fpu_xmm15, "xmm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm15) , FPU_OFFSET(xmm15), ehframe_dwarf_xmm15, ehframe_dwarf_xmm15, -1U, debugserver_xmm15, NULL, NULL },
+};
+
+static const char *g_contained_ymm0 [] = { "ymm0", NULL };
+static const char *g_contained_ymm1 [] = { "ymm1", NULL };
+static const char *g_contained_ymm2 [] = { "ymm2", NULL };
+static const char *g_contained_ymm3 [] = { "ymm3", NULL };
+static const char *g_contained_ymm4 [] = { "ymm4", NULL };
+static const char *g_contained_ymm5 [] = { "ymm5", NULL };
+static const char *g_contained_ymm6 [] = { "ymm6", NULL };
+static const char *g_contained_ymm7 [] = { "ymm7", NULL };
+static const char *g_contained_ymm8 [] = { "ymm8", NULL };
+static const char *g_contained_ymm9 [] = { "ymm9", NULL };
+static const char *g_contained_ymm10[] = { "ymm10", NULL };
+static const char *g_contained_ymm11[] = { "ymm11", NULL };
+static const char *g_contained_ymm12[] = { "ymm12", NULL };
+static const char *g_contained_ymm13[] = { "ymm13", NULL };
+static const char *g_contained_ymm14[] = { "ymm14", NULL };
+static const char *g_contained_ymm15[] = { "ymm15", NULL };
+
+const DNBRegisterInfo
+DNBArchImplX86_64::g_fpu_registers_avx[] =
+{
+ { e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , AVX_OFFSET(fcw) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , AVX_OFFSET(fsw) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , AVX_OFFSET(ftw) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , AVX_OFFSET(fop) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , AVX_OFFSET(ip) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , AVX_OFFSET(cs) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , AVX_OFFSET(dp) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , AVX_OFFSET(ds) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , AVX_OFFSET(mxcsr) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , AVX_OFFSET(mxcsrmask) , -1U, -1U, -1U, -1U, NULL, NULL },
+
+ { e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), ehframe_dwarf_stmm0, ehframe_dwarf_stmm0, -1U, debugserver_stmm0, NULL, NULL },
+ { e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), ehframe_dwarf_stmm1, ehframe_dwarf_stmm1, -1U, debugserver_stmm1, NULL, NULL },
+ { e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), ehframe_dwarf_stmm2, ehframe_dwarf_stmm2, -1U, debugserver_stmm2, NULL, NULL },
+ { e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), ehframe_dwarf_stmm3, ehframe_dwarf_stmm3, -1U, debugserver_stmm3, NULL, NULL },
+ { e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), ehframe_dwarf_stmm4, ehframe_dwarf_stmm4, -1U, debugserver_stmm4, NULL, NULL },
+ { e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), ehframe_dwarf_stmm5, ehframe_dwarf_stmm5, -1U, debugserver_stmm5, NULL, NULL },
+ { e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), ehframe_dwarf_stmm6, ehframe_dwarf_stmm6, -1U, debugserver_stmm6, NULL, NULL },
+ { e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), ehframe_dwarf_stmm7, ehframe_dwarf_stmm7, -1U, debugserver_stmm7, NULL, NULL },
+
+ { e_regSetFPU, fpu_ymm0 , "ymm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm0) , AVX_OFFSET_YMM(0) , ehframe_dwarf_ymm0 , ehframe_dwarf_ymm0 , -1U, debugserver_ymm0, NULL, NULL },
+ { e_regSetFPU, fpu_ymm1 , "ymm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm1) , AVX_OFFSET_YMM(1) , ehframe_dwarf_ymm1 , ehframe_dwarf_ymm1 , -1U, debugserver_ymm1, NULL, NULL },
+ { e_regSetFPU, fpu_ymm2 , "ymm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm2) , AVX_OFFSET_YMM(2) , ehframe_dwarf_ymm2 , ehframe_dwarf_ymm2 , -1U, debugserver_ymm2, NULL, NULL },
+ { e_regSetFPU, fpu_ymm3 , "ymm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm3) , AVX_OFFSET_YMM(3) , ehframe_dwarf_ymm3 , ehframe_dwarf_ymm3 , -1U, debugserver_ymm3, NULL, NULL },
+ { e_regSetFPU, fpu_ymm4 , "ymm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm4) , AVX_OFFSET_YMM(4) , ehframe_dwarf_ymm4 , ehframe_dwarf_ymm4 , -1U, debugserver_ymm4, NULL, NULL },
+ { e_regSetFPU, fpu_ymm5 , "ymm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm5) , AVX_OFFSET_YMM(5) , ehframe_dwarf_ymm5 , ehframe_dwarf_ymm5 , -1U, debugserver_ymm5, NULL, NULL },
+ { e_regSetFPU, fpu_ymm6 , "ymm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm6) , AVX_OFFSET_YMM(6) , ehframe_dwarf_ymm6 , ehframe_dwarf_ymm6 , -1U, debugserver_ymm6, NULL, NULL },
+ { e_regSetFPU, fpu_ymm7 , "ymm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm7) , AVX_OFFSET_YMM(7) , ehframe_dwarf_ymm7 , ehframe_dwarf_ymm7 , -1U, debugserver_ymm7, NULL, NULL },
+ { e_regSetFPU, fpu_ymm8 , "ymm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm8) , AVX_OFFSET_YMM(8) , ehframe_dwarf_ymm8 , ehframe_dwarf_ymm8 , -1U, debugserver_ymm8 , NULL, NULL },
+ { e_regSetFPU, fpu_ymm9 , "ymm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm9) , AVX_OFFSET_YMM(9) , ehframe_dwarf_ymm9 , ehframe_dwarf_ymm9 , -1U, debugserver_ymm9 , NULL, NULL },
+ { e_regSetFPU, fpu_ymm10, "ymm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm10) , AVX_OFFSET_YMM(10), ehframe_dwarf_ymm10, ehframe_dwarf_ymm10, -1U, debugserver_ymm10, NULL, NULL },
+ { e_regSetFPU, fpu_ymm11, "ymm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm11) , AVX_OFFSET_YMM(11), ehframe_dwarf_ymm11, ehframe_dwarf_ymm11, -1U, debugserver_ymm11, NULL, NULL },
+ { e_regSetFPU, fpu_ymm12, "ymm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm12) , AVX_OFFSET_YMM(12), ehframe_dwarf_ymm12, ehframe_dwarf_ymm12, -1U, debugserver_ymm12, NULL, NULL },
+ { e_regSetFPU, fpu_ymm13, "ymm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm13) , AVX_OFFSET_YMM(13), ehframe_dwarf_ymm13, ehframe_dwarf_ymm13, -1U, debugserver_ymm13, NULL, NULL },
+ { e_regSetFPU, fpu_ymm14, "ymm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm14) , AVX_OFFSET_YMM(14), ehframe_dwarf_ymm14, ehframe_dwarf_ymm14, -1U, debugserver_ymm14, NULL, NULL },
+ { e_regSetFPU, fpu_ymm15, "ymm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm15) , AVX_OFFSET_YMM(15), ehframe_dwarf_ymm15, ehframe_dwarf_ymm15, -1U, debugserver_ymm15, NULL, NULL },
+
+ { e_regSetFPU, fpu_xmm0 , "xmm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0) , 0, ehframe_dwarf_xmm0 , ehframe_dwarf_xmm0 , -1U, debugserver_xmm0 , g_contained_ymm0 , NULL },
+ { e_regSetFPU, fpu_xmm1 , "xmm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1) , 0, ehframe_dwarf_xmm1 , ehframe_dwarf_xmm1 , -1U, debugserver_xmm1 , g_contained_ymm1 , NULL },
+ { e_regSetFPU, fpu_xmm2 , "xmm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2) , 0, ehframe_dwarf_xmm2 , ehframe_dwarf_xmm2 , -1U, debugserver_xmm2 , g_contained_ymm2 , NULL },
+ { e_regSetFPU, fpu_xmm3 , "xmm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3) , 0, ehframe_dwarf_xmm3 , ehframe_dwarf_xmm3 , -1U, debugserver_xmm3 , g_contained_ymm3 , NULL },
+ { e_regSetFPU, fpu_xmm4 , "xmm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4) , 0, ehframe_dwarf_xmm4 , ehframe_dwarf_xmm4 , -1U, debugserver_xmm4 , g_contained_ymm4 , NULL },
+ { e_regSetFPU, fpu_xmm5 , "xmm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5) , 0, ehframe_dwarf_xmm5 , ehframe_dwarf_xmm5 , -1U, debugserver_xmm5 , g_contained_ymm5 , NULL },
+ { e_regSetFPU, fpu_xmm6 , "xmm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6) , 0, ehframe_dwarf_xmm6 , ehframe_dwarf_xmm6 , -1U, debugserver_xmm6 , g_contained_ymm6 , NULL },
+ { e_regSetFPU, fpu_xmm7 , "xmm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7) , 0, ehframe_dwarf_xmm7 , ehframe_dwarf_xmm7 , -1U, debugserver_xmm7 , g_contained_ymm7 , NULL },
+ { e_regSetFPU, fpu_xmm8 , "xmm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm8) , 0, ehframe_dwarf_xmm8 , ehframe_dwarf_xmm8 , -1U, debugserver_xmm8 , g_contained_ymm8 , NULL },
+ { e_regSetFPU, fpu_xmm9 , "xmm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm9) , 0, ehframe_dwarf_xmm9 , ehframe_dwarf_xmm9 , -1U, debugserver_xmm9 , g_contained_ymm9 , NULL },
+ { e_regSetFPU, fpu_xmm10, "xmm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm10) , 0, ehframe_dwarf_xmm10, ehframe_dwarf_xmm10, -1U, debugserver_xmm10, g_contained_ymm10, NULL },
+ { e_regSetFPU, fpu_xmm11, "xmm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm11) , 0, ehframe_dwarf_xmm11, ehframe_dwarf_xmm11, -1U, debugserver_xmm11, g_contained_ymm11, NULL },
+ { e_regSetFPU, fpu_xmm12, "xmm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm12) , 0, ehframe_dwarf_xmm12, ehframe_dwarf_xmm12, -1U, debugserver_xmm12, g_contained_ymm12, NULL },
+ { e_regSetFPU, fpu_xmm13, "xmm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm13) , 0, ehframe_dwarf_xmm13, ehframe_dwarf_xmm13, -1U, debugserver_xmm13, g_contained_ymm13, NULL },
+ { e_regSetFPU, fpu_xmm14, "xmm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm14) , 0, ehframe_dwarf_xmm14, ehframe_dwarf_xmm14, -1U, debugserver_xmm14, g_contained_ymm14, NULL },
+ { e_regSetFPU, fpu_xmm15, "xmm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm15) , 0, ehframe_dwarf_xmm15, ehframe_dwarf_xmm15, -1U, debugserver_xmm15, g_contained_ymm15, NULL }
+
+
+};
+
+// Exception registers
+
+const DNBRegisterInfo
+DNBArchImplX86_64::g_exc_registers[] =
+{
+ { e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , -1U, -1U, -1U, -1U, NULL, NULL },
+ { e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , -1U, -1U, -1U, -1U, NULL, NULL }
+};
+
+// Number of registers in each register set
+const size_t DNBArchImplX86_64::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_fpu_registers_no_avx = sizeof(g_fpu_registers_no_avx)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_fpu_registers_avx = sizeof(g_fpu_registers_avx)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_all_registers_no_avx = k_num_gpr_registers + k_num_fpu_registers_no_avx + k_num_exc_registers;
+const size_t DNBArchImplX86_64::k_num_all_registers_avx = k_num_gpr_registers + k_num_fpu_registers_avx + k_num_exc_registers;
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+const DNBRegisterSetInfo
+DNBArchImplX86_64::g_reg_sets_no_avx[] =
+{
+ { "x86_64 Registers", NULL, k_num_all_registers_no_avx },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpu_registers_no_avx, k_num_fpu_registers_no_avx },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+
+const DNBRegisterSetInfo
+DNBArchImplX86_64::g_reg_sets_avx[] =
+{
+ { "x86_64 Registers", NULL, k_num_all_registers_avx },
+ { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers },
+ { "Floating Point Registers", g_fpu_registers_avx, k_num_fpu_registers_avx },
+ { "Exception State Registers", g_exc_registers, k_num_exc_registers }
+};
+
+// Total number of register sets for this architecture
+const size_t DNBArchImplX86_64::k_num_register_sets = sizeof(g_reg_sets_avx)/sizeof(DNBRegisterSetInfo);
+
+
+DNBArchProtocol *
+DNBArchImplX86_64::Create (MachThread *thread)
+{
+ DNBArchImplX86_64 *obj = new DNBArchImplX86_64 (thread);
+ return obj;
+}
+
+const uint8_t *
+DNBArchImplX86_64::SoftwareBreakpointOpcode (nub_size_t byte_size)
+{
+ static const uint8_t g_breakpoint_opcode[] = { 0xCC };
+ if (byte_size == 1)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+const DNBRegisterSetInfo *
+DNBArchImplX86_64::GetRegisterSetInfo(nub_size_t *num_reg_sets)
+{
+ *num_reg_sets = k_num_register_sets;
+
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ return g_reg_sets_avx;
+ else
+ return g_reg_sets_no_avx;
+}
+
+void
+DNBArchImplX86_64::Initialize()
+{
+ DNBArchPluginInfo arch_plugin_info =
+ {
+ CPU_TYPE_X86_64,
+ DNBArchImplX86_64::Create,
+ DNBArchImplX86_64::GetRegisterSetInfo,
+ DNBArchImplX86_64::SoftwareBreakpointOpcode
+ };
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin (arch_plugin_info);
+}
+
+bool
+DNBArchImplX86_64::GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_rip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_rsp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_rbp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_rflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ value->info = *regInfo;
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ value->value.uint64 = ((uint64_t*)(&m_state.context.gpr))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPU:
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ switch (reg)
+ {
+ case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)); return true;
+ case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)); return true;
+ case fpu_ftw: value->value.uint8 = m_state.context.fpu.avx.__fpu_ftw; return true;
+ case fpu_fop: value->value.uint16 = m_state.context.fpu.avx.__fpu_fop; return true;
+ case fpu_ip: value->value.uint32 = m_state.context.fpu.avx.__fpu_ip; return true;
+ case fpu_cs: value->value.uint16 = m_state.context.fpu.avx.__fpu_cs; return true;
+ case fpu_dp: value->value.uint32 = m_state.context.fpu.avx.__fpu_dp; return true;
+ case fpu_ds: value->value.uint16 = m_state.context.fpu.avx.__fpu_ds; return true;
+ case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.avx.__fpu_mxcsr; return true;
+ case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.avx.__fpu_mxcsrmask; return true;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ memcpy(&value->value.uint8, &m_state.context.fpu.avx.__fpu_stmm0 + (reg - fpu_stmm0), 10);
+ return true;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ memcpy(&value->value.uint8, &m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_xmm0), 16);
+ return true;
+
+ case fpu_ymm0:
+ case fpu_ymm1:
+ case fpu_ymm2:
+ case fpu_ymm3:
+ case fpu_ymm4:
+ case fpu_ymm5:
+ case fpu_ymm6:
+ case fpu_ymm7:
+ case fpu_ymm8:
+ case fpu_ymm9:
+ case fpu_ymm10:
+ case fpu_ymm11:
+ case fpu_ymm12:
+ case fpu_ymm13:
+ case fpu_ymm14:
+ case fpu_ymm15:
+ memcpy(&value->value.uint8, &m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_ymm0), 16);
+ memcpy((&value->value.uint8) + 16, &m_state.context.fpu.avx.__fpu_ymmh0 + (reg - fpu_ymm0), 16);
+ return true;
+ }
+ }
+ else
+ {
+ switch (reg)
+ {
+ case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)); return true;
+ case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)); return true;
+ case fpu_ftw: value->value.uint8 = m_state.context.fpu.no_avx.__fpu_ftw; return true;
+ case fpu_fop: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop; return true;
+ case fpu_ip: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_ip; return true;
+ case fpu_cs: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_cs; return true;
+ case fpu_dp: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_dp; return true;
+ case fpu_ds: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_ds; return true;
+ case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsr; return true;
+ case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsrmask; return true;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ memcpy(&value->value.uint8, &m_state.context.fpu.no_avx.__fpu_stmm0 + (reg - fpu_stmm0), 10);
+ return true;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ memcpy(&value->value.uint8, &m_state.context.fpu.no_avx.__fpu_xmm0 + (reg - fpu_xmm0), 16);
+ return true;
+ }
+ }
+ break;
+
+ case e_regSetEXC:
+ switch (reg)
+ {
+ case exc_trapno: value->value.uint32 = m_state.context.exc.__trapno; return true;
+ case exc_err: value->value.uint32 = m_state.context.exc.__err; return true;
+ case exc_faultvaddr:value->value.uint64 = m_state.context.exc.__faultvaddr; return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+bool
+DNBArchImplX86_64::SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value)
+{
+ if (set == REGISTER_SET_GENERIC)
+ {
+ switch (reg)
+ {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_rip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_rsp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_rbp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_rflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo)
+ {
+ switch (set)
+ {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers)
+ {
+ ((uint64_t*)(&m_state.context.gpr))[reg] = value->value.uint64;
+ success = true;
+ }
+ break;
+
+ case e_regSetFPU:
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ switch (reg)
+ {
+ case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)) = value->value.uint16; success = true; break;
+ case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)) = value->value.uint16; success = true; break;
+ case fpu_ftw: m_state.context.fpu.avx.__fpu_ftw = value->value.uint8; success = true; break;
+ case fpu_fop: m_state.context.fpu.avx.__fpu_fop = value->value.uint16; success = true; break;
+ case fpu_ip: m_state.context.fpu.avx.__fpu_ip = value->value.uint32; success = true; break;
+ case fpu_cs: m_state.context.fpu.avx.__fpu_cs = value->value.uint16; success = true; break;
+ case fpu_dp: m_state.context.fpu.avx.__fpu_dp = value->value.uint32; success = true; break;
+ case fpu_ds: m_state.context.fpu.avx.__fpu_ds = value->value.uint16; success = true; break;
+ case fpu_mxcsr: m_state.context.fpu.avx.__fpu_mxcsr = value->value.uint32; success = true; break;
+ case fpu_mxcsrmask: m_state.context.fpu.avx.__fpu_mxcsrmask = value->value.uint32; success = true; break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ memcpy (&m_state.context.fpu.avx.__fpu_stmm0 + (reg - fpu_stmm0), &value->value.uint8, 10);
+ success = true;
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ memcpy (&m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_xmm0), &value->value.uint8, 16);
+ success = true;
+ break;
+
+ case fpu_ymm0:
+ case fpu_ymm1:
+ case fpu_ymm2:
+ case fpu_ymm3:
+ case fpu_ymm4:
+ case fpu_ymm5:
+ case fpu_ymm6:
+ case fpu_ymm7:
+ case fpu_ymm8:
+ case fpu_ymm9:
+ case fpu_ymm10:
+ case fpu_ymm11:
+ case fpu_ymm12:
+ case fpu_ymm13:
+ case fpu_ymm14:
+ case fpu_ymm15:
+ memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_ymm0), &value->value.uint8, 16);
+ memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + (reg - fpu_ymm0), (&value->value.uint8) + 16, 16);
+ return true;
+ }
+ }
+ else
+ {
+ switch (reg)
+ {
+ case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)) = value->value.uint16; success = true; break;
+ case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)) = value->value.uint16; success = true; break;
+ case fpu_ftw: m_state.context.fpu.no_avx.__fpu_ftw = value->value.uint8; success = true; break;
+ case fpu_fop: m_state.context.fpu.no_avx.__fpu_fop = value->value.uint16; success = true; break;
+ case fpu_ip: m_state.context.fpu.no_avx.__fpu_ip = value->value.uint32; success = true; break;
+ case fpu_cs: m_state.context.fpu.no_avx.__fpu_cs = value->value.uint16; success = true; break;
+ case fpu_dp: m_state.context.fpu.no_avx.__fpu_dp = value->value.uint32; success = true; break;
+ case fpu_ds: m_state.context.fpu.no_avx.__fpu_ds = value->value.uint16; success = true; break;
+ case fpu_mxcsr: m_state.context.fpu.no_avx.__fpu_mxcsr = value->value.uint32; success = true; break;
+ case fpu_mxcsrmask: m_state.context.fpu.no_avx.__fpu_mxcsrmask = value->value.uint32; success = true; break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ memcpy (&m_state.context.fpu.no_avx.__fpu_stmm0 + (reg - fpu_stmm0), &value->value.uint8, 10);
+ success = true;
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ memcpy (&m_state.context.fpu.no_avx.__fpu_xmm0 + (reg - fpu_xmm0), &value->value.uint8, 16);
+ success = true;
+ break;
+ }
+ }
+ break;
+
+ case e_regSetEXC:
+ switch (reg)
+ {
+ case exc_trapno: m_state.context.exc.__trapno = value->value.uint32; success = true; break;
+ case exc_err: m_state.context.exc.__err = value->value.uint32; success = true; break;
+ case exc_faultvaddr:m_state.context.exc.__faultvaddr = value->value.uint64; success = true; break;
+ }
+ break;
+ }
+ }
+
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+uint32_t
+DNBArchImplX86_64::GetRegisterContextSize()
+{
+ static uint32_t g_cached_size = 0;
+ if (g_cached_size == 0)
+ {
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ for (size_t i=0; i<k_num_fpu_registers_avx; ++i)
+ {
+ if (g_fpu_registers_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_avx[i].size;
+ }
+ }
+ else
+ {
+ for (size_t i=0; i<k_num_fpu_registers_no_avx; ++i)
+ {
+ if (g_fpu_registers_no_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_no_avx[i].size;
+ }
+ }
+ DNBLogThreaded ("DNBArchImplX86_64::GetRegisterContextSize() - GPR = %zu, FPU = %u, EXC = %zu", sizeof(GPR), g_cached_size, sizeof(EXC));
+ g_cached_size += sizeof(GPR);
+ g_cached_size += sizeof(EXC);
+ DNBLogThreaded ("DNBArchImplX86_64::GetRegisterContextSize() - GPR + FPU + EXC = %u", g_cached_size);
+ }
+ return g_cached_size;
+}
+
+nub_size_t
+DNBArchImplX86_64::GetRegisterContext (void *buf, nub_size_t buf_len)
+{
+ uint32_t size = GetRegisterContextSize();
+
+ if (buf && buf_len)
+ {
+ bool force = false;
+ kern_return_t kret;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = %llu) error: GPR regs failed to read: %u ", buf, (uint64_t)buf_len, kret);
+ size = 0;
+ }
+ else
+ if ((kret = GetFPUState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = %llu) error: %s regs failed to read: %u", buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ size = 0;
+ }
+ else
+ if ((kret = GetEXCState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = %llu) error: EXC regs failed to read: %u", buf, (uint64_t)buf_len, kret);
+ size = 0;
+ }
+ else
+ {
+ uint8_t *p = (uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(p, &m_state.context.gpr, sizeof(GPR));
+ p += sizeof(GPR);
+
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ // Walk around the gaps in the FPU regs
+ memcpy(p, &m_state.context.fpu.avx.__fpu_fcw, 5);
+ p += 5;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_fop, 8);
+ p += 8;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_dp, 6);
+ p += 6;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_mxcsr, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(p, &m_state.context.fpu.avx.__fpu_stmm0 + i, 10);
+ p += 10;
+ }
+
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i=0; i<16; ++i)
+ {
+ memcpy(p, &m_state.context.fpu.avx.__fpu_xmm0 + i, 16);
+ p += 16;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_ymmh0 + i, 16);
+ p += 16;
+ }
+ }
+ else
+ {
+ // Walk around the gaps in the FPU regs
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fcw, 5);
+ p += 5;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fop, 8);
+ p += 8;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_dp, 6);
+ p += 6;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_mxcsr, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_stmm0 + i, 10);
+ p += 10;
+ }
+
+ // Copy the XMM registers in a single block
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_xmm0, 16 * 16);
+ p += 16 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(p, &m_state.context.exc, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+ }
+
+ }
+
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = %llu) => %u", buf, (uint64_t)buf_len, size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t
+DNBArchImplX86_64::SetRegisterContext (const void *buf, nub_size_t buf_len)
+{
+ uint32_t size = GetRegisterContextSize();
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size)
+ {
+ if (size > buf_len)
+ size = static_cast<uint32_t>(buf_len);
+
+ uint8_t *p = (uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(&m_state.context.gpr, p, sizeof(GPR));
+ p += sizeof(GPR);
+
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ {
+ // Walk around the gaps in the FPU regs
+ memcpy(&m_state.context.fpu.avx.__fpu_fcw, p, 5);
+ p += 5;
+ memcpy(&m_state.context.fpu.avx.__fpu_fop, p, 8);
+ p += 8;
+ memcpy(&m_state.context.fpu.avx.__fpu_dp, p, 6);
+ p += 6;
+ memcpy(&m_state.context.fpu.avx.__fpu_mxcsr, p, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(&m_state.context.fpu.avx.__fpu_stmm0 + i, p, 10);
+ p += 10;
+ }
+
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i=0; i<16; ++i)
+ {
+ memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + i, p, 16);
+ p += 16;
+ memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + i, p, 16);
+ p += 16;
+ }
+ }
+ else
+ {
+ // Copy fcw through mxcsrmask as there is no padding
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fcw, p, 5);
+ p += 5;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fop, p, 8);
+ p += 8;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_dp, p, 6);
+ p += 6;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_mxcsr, p, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i=0; i<8; ++i)
+ {
+ memcpy(&m_state.context.fpu.no_avx.__fpu_stmm0 + i, p, 10);
+ p += 10;
+ }
+
+ // Copy the XMM registers in a single block
+ memcpy(&m_state.context.fpu.no_avx.__fpu_xmm0, p, 16 * 16);
+ p += 16 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(&m_state.context.exc, p, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert (bytes_written == size);
+
+ kern_return_t kret;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) error: GPR regs failed to write: %u", buf, (uint64_t)buf_len, kret);
+ if ((kret = SetFPUState()) != KERN_SUCCESS)
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) error: %s regs failed to write: %u", buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ if ((kret = SetEXCState()) != KERN_SUCCESS)
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) error: EXP regs failed to write: %u", buf, (uint64_t)buf_len, kret);
+ }
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size);
+ return size;
+}
+
+uint32_t
+DNBArchImplX86_64::SaveRegisterState ()
+{
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ // Always re-read the registers because above we call thread_abort_safely();
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SaveRegisterState () error: GPR regs failed to read: %u ", kret);
+ }
+ else if ((kret = GetFPUState(force)) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SaveRegisterState () error: %s regs failed to read: %u", CPUHasAVX() ? "AVX" : "FPU", kret);
+ }
+ else
+ {
+ const uint32_t save_id = GetNextRegisterStateSaveID ();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return 0;
+}
+bool
+DNBArchImplX86_64::RestoreRegisterState (uint32_t save_id)
+{
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end())
+ {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.fpu = pos->second.fpu;
+ m_state.SetError(e_regSetGPR, Read, 0);
+ m_state.SetError(e_regSetFPU, Read, 0);
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret);
+ success = false;
+ }
+ else if ((kret = SetFPUState()) != KERN_SUCCESS)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, CPUHasAVX() ? "AVX" : "FPU", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+
+kern_return_t
+DNBArchImplX86_64::GetRegisterState(int set, bool force)
+{
+ switch (set)
+ {
+ case e_regSetALL: return GetGPRState(force) | GetFPUState(force) | GetEXCState(force);
+ case e_regSetGPR: return GetGPRState(force);
+ case e_regSetFPU: return GetFPUState(force);
+ case e_regSetEXC: return GetEXCState(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+DNBArchImplX86_64::SetRegisterState(int set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetStateIsValid(set))
+ {
+ switch (set)
+ {
+ case e_regSetALL: return SetGPRState() | SetFPUState() | SetEXCState();
+ case e_regSetGPR: return SetGPRState();
+ case e_regSetFPU: return SetFPUState();
+ case e_regSetEXC: return SetEXCState();
+ default: break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+DNBArchImplX86_64::RegisterSetStateIsValid (int set) const
+{
+ return m_state.RegsAreValid(set);
+}
+
+
+
+#endif // #if defined (__i386__) || defined (__x86_64__)
diff --git a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h
new file mode 100644
index 000000000000..20844951261b
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h
@@ -0,0 +1,260 @@
+//===-- DNBArchImplX86_64.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBArchImplX86_64_h__
+#define __DNBArchImplX86_64_h__
+
+#if defined (__i386__) || defined (__x86_64__)
+#include "DNBArch.h"
+#include "MachRegisterStatesX86_64.h"
+
+#include <map>
+
+class MachThread;
+
+class DNBArchImplX86_64 : public DNBArchProtocol
+{
+public:
+ DNBArchImplX86_64(MachThread *thread) :
+ DNBArchProtocol(),
+ m_thread(thread),
+ m_state(),
+ m_2pc_dbg_checkpoint(),
+ m_2pc_trans_state(Trans_Done),
+ m_saved_register_states()
+ {
+ }
+ virtual ~DNBArchImplX86_64()
+ {
+ }
+
+ static void Initialize();
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState ();
+ virtual bool RestoreRegisterState (uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState (int set, bool force);
+ virtual kern_return_t SetRegisterState (int set);
+ virtual bool RegisterSetStateIsValid (int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data& exc);
+
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task);
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+protected:
+ kern_return_t EnableHardwareSingleStep (bool enable);
+
+ typedef __x86_64_thread_state_t GPR;
+ typedef __x86_64_float_state_t FPU;
+ typedef __x86_64_exception_state_t EXC;
+ typedef __x86_64_avx_state_t AVX;
+ typedef __x86_64_debug_state_t DBG;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_fpu_registers_no_avx[];
+ static const DNBRegisterInfo g_fpu_registers_avx[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets_no_avx[];
+ static const DNBRegisterSetInfo g_reg_sets_avx[];
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_fpu_registers_no_avx;
+ static const size_t k_num_fpu_registers_avx;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers_no_avx;
+ static const size_t k_num_all_registers_avx;
+ static const size_t k_num_register_sets;
+
+ typedef enum RegisterSetTag
+ {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPU,
+ e_regSetEXC,
+ e_regSetDBG,
+ kNumRegisterSets
+ } RegisterSet;
+
+ typedef enum RegisterSetWordSizeTag
+ {
+ e_regSetWordSizeGPR = sizeof(GPR) / sizeof(int),
+ e_regSetWordSizeFPU = sizeof(FPU) / sizeof(int),
+ e_regSetWordSizeEXC = sizeof(EXC) / sizeof(int),
+ e_regSetWordSizeAVX = sizeof(AVX) / sizeof(int),
+ e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int)
+ } RegisterSetWordSize;
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ struct Context
+ {
+ GPR gpr;
+ union {
+ FPU no_avx;
+ AVX avx;
+ } fpu;
+ EXC exc;
+ DBG dbg;
+ };
+
+ struct State
+ {
+ Context context;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+
+ State()
+ {
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (e_regSetALL, Read, -1);
+ }
+
+ kern_return_t
+ GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL: return gpr_errs[err_idx] |
+ fpu_errs[err_idx] |
+ exc_errs[err_idx];
+ case e_regSetGPR: return gpr_errs[err_idx];
+ case e_regSetFPU: return fpu_errs[err_idx];
+ case e_regSetEXC: return exc_errs[err_idx];
+ case e_regSetDBG: return dbg_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case e_regSetALL:
+ gpr_errs[err_idx] =
+ fpu_errs[err_idx] =
+ exc_errs[err_idx] =
+ dbg_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPU:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetDBG:
+ dbg_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegsAreValid (int flavor) const
+ {
+ return GetError(flavor, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState (bool force);
+ kern_return_t GetFPUState (bool force);
+ kern_return_t GetEXCState (bool force);
+ kern_return_t GetDBGState (bool force);
+
+ kern_return_t SetGPRState ();
+ kern_return_t SetFPUState ();
+ kern_return_t SetEXCState ();
+ kern_return_t SetDBGState (bool also_set_on_task);
+
+ static DNBArchProtocol *
+ Create (MachThread *thread);
+
+ static const uint8_t *
+ SoftwareBreakpointOpcode (nub_size_t byte_size);
+
+ static const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ static uint32_t GetRegisterContextSize();
+
+ // Helper functions for watchpoint manipulations.
+ static void SetWatchpoint(DBG &debug_state, uint32_t hw_index, nub_addr_t addr, nub_size_t size, bool read, bool write);
+ static void ClearWatchpoint(DBG &debug_state, uint32_t hw_index);
+ static bool IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index);
+ static void ClearWatchpointHits(DBG &debug_state);
+ static bool IsWatchpointHit(const DBG &debug_state, uint32_t hw_index);
+ static nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+
+ virtual bool StartTransForHWP();
+ virtual bool RollbackTransForHWP();
+ virtual bool FinishTransForHWP();
+ DBG GetDBGCheckpoint();
+
+ MachThread *m_thread;
+ State m_state;
+ DBG m_2pc_dbg_checkpoint;
+ uint32_t m_2pc_trans_state; // Is transaction of DBG state change: Pedning (0), Done (1), or Rolled Back (2)?
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (__i386__) || defined (__x86_64__)
+#endif // #ifndef __DNBArchImplX86_64_h__
diff --git a/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h b/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h
new file mode 100644
index 000000000000..4e48e9645dd5
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h
@@ -0,0 +1,210 @@
+//===-- MachRegisterStatesX86_64.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Sean Callanan on 3/16/11.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachRegisterStatesX86_64_h__
+#define __MachRegisterStatesX86_64_h__
+
+#include <inttypes.h>
+
+#define __x86_64_THREAD_STATE 4
+#define __x86_64_FLOAT_STATE 5
+#define __x86_64_EXCEPTION_STATE 6
+#define __x86_64_DEBUG_STATE 11
+#define __x86_64_AVX_STATE 17
+
+typedef struct {
+ uint64_t __rax;
+ uint64_t __rbx;
+ uint64_t __rcx;
+ uint64_t __rdx;
+ uint64_t __rdi;
+ uint64_t __rsi;
+ uint64_t __rbp;
+ uint64_t __rsp;
+ uint64_t __r8;
+ uint64_t __r9;
+ uint64_t __r10;
+ uint64_t __r11;
+ uint64_t __r12;
+ uint64_t __r13;
+ uint64_t __r14;
+ uint64_t __r15;
+ uint64_t __rip;
+ uint64_t __rflags;
+ uint64_t __cs;
+ uint64_t __fs;
+ uint64_t __gs;
+} __x86_64_thread_state_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __PAD1 : 2;
+ uint16_t __pc : 2;
+ uint16_t __rc : 2;
+ uint16_t __PAD2 : 1;
+ uint16_t __PAD3 : 3;
+} __x86_64_fp_control_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __stkflt : 1;
+ uint16_t __errsumm : 1;
+ uint16_t __c0 : 1;
+ uint16_t __c1 : 1;
+ uint16_t __c2 : 1;
+ uint16_t __tos : 3;
+ uint16_t __c3 : 1;
+ uint16_t __busy : 1;
+} __x86_64_fp_status_t;
+
+typedef struct {
+ uint8_t __mmst_reg[10];
+ uint8_t __mmst_rsrv[6];
+} __x86_64_mmst_reg;
+
+typedef struct {
+ uint8_t __xmm_reg[16];
+} __x86_64_xmm_reg;
+
+typedef struct {
+ int32_t __fpu_reserved[2];
+ __x86_64_fp_control_t __fpu_fcw;
+ __x86_64_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __x86_64_mmst_reg __fpu_stmm0;
+ __x86_64_mmst_reg __fpu_stmm1;
+ __x86_64_mmst_reg __fpu_stmm2;
+ __x86_64_mmst_reg __fpu_stmm3;
+ __x86_64_mmst_reg __fpu_stmm4;
+ __x86_64_mmst_reg __fpu_stmm5;
+ __x86_64_mmst_reg __fpu_stmm6;
+ __x86_64_mmst_reg __fpu_stmm7;
+ __x86_64_xmm_reg __fpu_xmm0;
+ __x86_64_xmm_reg __fpu_xmm1;
+ __x86_64_xmm_reg __fpu_xmm2;
+ __x86_64_xmm_reg __fpu_xmm3;
+ __x86_64_xmm_reg __fpu_xmm4;
+ __x86_64_xmm_reg __fpu_xmm5;
+ __x86_64_xmm_reg __fpu_xmm6;
+ __x86_64_xmm_reg __fpu_xmm7;
+ __x86_64_xmm_reg __fpu_xmm8;
+ __x86_64_xmm_reg __fpu_xmm9;
+ __x86_64_xmm_reg __fpu_xmm10;
+ __x86_64_xmm_reg __fpu_xmm11;
+ __x86_64_xmm_reg __fpu_xmm12;
+ __x86_64_xmm_reg __fpu_xmm13;
+ __x86_64_xmm_reg __fpu_xmm14;
+ __x86_64_xmm_reg __fpu_xmm15;
+ uint8_t __fpu_rsrv4[6*16];
+ int32_t __fpu_reserved1;
+} __x86_64_float_state_t;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __x86_64_fp_control_t __fpu_fcw;
+ __x86_64_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __x86_64_mmst_reg __fpu_stmm0;
+ __x86_64_mmst_reg __fpu_stmm1;
+ __x86_64_mmst_reg __fpu_stmm2;
+ __x86_64_mmst_reg __fpu_stmm3;
+ __x86_64_mmst_reg __fpu_stmm4;
+ __x86_64_mmst_reg __fpu_stmm5;
+ __x86_64_mmst_reg __fpu_stmm6;
+ __x86_64_mmst_reg __fpu_stmm7;
+ __x86_64_xmm_reg __fpu_xmm0;
+ __x86_64_xmm_reg __fpu_xmm1;
+ __x86_64_xmm_reg __fpu_xmm2;
+ __x86_64_xmm_reg __fpu_xmm3;
+ __x86_64_xmm_reg __fpu_xmm4;
+ __x86_64_xmm_reg __fpu_xmm5;
+ __x86_64_xmm_reg __fpu_xmm6;
+ __x86_64_xmm_reg __fpu_xmm7;
+ __x86_64_xmm_reg __fpu_xmm8;
+ __x86_64_xmm_reg __fpu_xmm9;
+ __x86_64_xmm_reg __fpu_xmm10;
+ __x86_64_xmm_reg __fpu_xmm11;
+ __x86_64_xmm_reg __fpu_xmm12;
+ __x86_64_xmm_reg __fpu_xmm13;
+ __x86_64_xmm_reg __fpu_xmm14;
+ __x86_64_xmm_reg __fpu_xmm15;
+ uint8_t __fpu_rsrv4[6*16];
+ uint32_t __fpu_reserved1;
+ uint8_t __avx_reserved1[64];
+ __x86_64_xmm_reg __fpu_ymmh0;
+ __x86_64_xmm_reg __fpu_ymmh1;
+ __x86_64_xmm_reg __fpu_ymmh2;
+ __x86_64_xmm_reg __fpu_ymmh3;
+ __x86_64_xmm_reg __fpu_ymmh4;
+ __x86_64_xmm_reg __fpu_ymmh5;
+ __x86_64_xmm_reg __fpu_ymmh6;
+ __x86_64_xmm_reg __fpu_ymmh7;
+ __x86_64_xmm_reg __fpu_ymmh8;
+ __x86_64_xmm_reg __fpu_ymmh9;
+ __x86_64_xmm_reg __fpu_ymmh10;
+ __x86_64_xmm_reg __fpu_ymmh11;
+ __x86_64_xmm_reg __fpu_ymmh12;
+ __x86_64_xmm_reg __fpu_ymmh13;
+ __x86_64_xmm_reg __fpu_ymmh14;
+ __x86_64_xmm_reg __fpu_ymmh15;
+} __x86_64_avx_state_t;
+
+typedef struct {
+ uint32_t __trapno;
+ uint32_t __err;
+ uint64_t __faultvaddr;
+} __x86_64_exception_state_t;
+
+
+typedef struct {
+ uint64_t __dr0;
+ uint64_t __dr1;
+ uint64_t __dr2;
+ uint64_t __dr3;
+ uint64_t __dr4;
+ uint64_t __dr5;
+ uint64_t __dr6;
+ uint64_t __dr7;
+} __x86_64_debug_state_t;
+
+#endif
diff --git a/tools/debugserver/source/MacOSX/x86_64/Makefile b/tools/debugserver/source/MacOSX/x86_64/Makefile
new file mode 100644
index 000000000000..bf488c859242
--- /dev/null
+++ b/tools/debugserver/source/MacOSX/x86_64/Makefile
@@ -0,0 +1,19 @@
+##===- tools/debugserver/source/MacOSX/i386/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 := lldbDebugserverMacOSX_X86_64
+BUILD_ARCHIVE = 1
+
+SOURCES := DNBArchImplX86_64.cpp
+
+include $(LLDB_LEVEL)/Makefile
+
+CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../.. -I$(PROJ_OBJ_DIR)/../../.. \ No newline at end of file
diff --git a/tools/debugserver/source/Makefile b/tools/debugserver/source/Makefile
new file mode 100644
index 000000000000..9eaeab4d3827
--- /dev/null
+++ b/tools/debugserver/source/Makefile
@@ -0,0 +1,46 @@
+##===- tools/debugserver/source/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 := lldbDebugserverCommon
+BUILD_ARCHIVE = 1
+
+SOURCES := debugserver.cpp \
+ DNBArch.cpp \
+ DNBBreakpoint.cpp \
+ DNB.cpp \
+ DNBDataRef.cpp \
+ DNBError.cpp \
+ DNBLog.cpp \
+ DNBRegisterInfo.cpp \
+ DNBThreadResumeActions.cpp \
+ libdebugserver.cpp \
+ PseudoTerminal.cpp \
+ PThreadEvent.cpp \
+ PThreadMutex.cpp \
+ RNBContext.cpp \
+ RNBRemote.cpp \
+ RNBServices.cpp \
+ RNBSocket.cpp \
+ SysSignal.cpp \
+ TTYState.cpp
+
+include $(LLDB_LEVEL)/Makefile
+
+ifeq ($(HOST_OS),Darwin)
+DIRS := MacOSX/i386 MacOSX/x86_64 MacOSX
+CPP.Flags += -I$(PROJ_SRC_DIR)/MacOSX
+CPP.Flags += -I$(PROJ_OBJ_DIR)/..
+BUILT_SOURCES = debugserver_vers.c
+endif
+
+ifeq ($(HOST_OS),Darwin)
+debugserver_vers.c: $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl $(PROJ_SRC_DIR)/../debugserver.xcodeproj/project.pbxproj
+ "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl" "$(PROJ_SRC_DIR)/../debugserver.xcodeproj/project.pbxproj" debugserver > debugserver_vers.c
+endif
diff --git a/tools/debugserver/source/PThreadCondition.h b/tools/debugserver/source/PThreadCondition.h
new file mode 100644
index 000000000000..787cc7941d52
--- /dev/null
+++ b/tools/debugserver/source/PThreadCondition.h
@@ -0,0 +1,53 @@
+//===-- PThreadCondition.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PThreadCondition_h__
+#define __PThreadCondition_h__
+
+#include <pthread.h>
+
+class PThreadCondition
+{
+public:
+
+ PThreadCondition()
+ {
+ ::pthread_cond_init (&m_condition, NULL);
+ }
+
+ ~PThreadCondition()
+ {
+ ::pthread_cond_destroy (&m_condition);
+ }
+
+ pthread_cond_t *Condition()
+ {
+ return &m_condition;
+ }
+
+ int Broadcast()
+ {
+ return ::pthread_cond_broadcast (&m_condition);
+ }
+
+ int Signal()
+ {
+ return ::pthread_cond_signal (&m_condition);
+ }
+
+protected:
+ pthread_cond_t m_condition;
+};
+
+#endif
+
diff --git a/tools/debugserver/source/PThreadEvent.cpp b/tools/debugserver/source/PThreadEvent.cpp
new file mode 100644
index 000000000000..b087bfc7d481
--- /dev/null
+++ b/tools/debugserver/source/PThreadEvent.cpp
@@ -0,0 +1,227 @@
+//===-- PThreadEvent.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PThreadEvent.h"
+#include "errno.h"
+#include "DNBLog.h"
+
+PThreadEvent::PThreadEvent(uint32_t bits, uint32_t validBits) :
+ m_mutex(),
+ m_set_condition(),
+ m_reset_condition(),
+ m_bits(bits),
+ m_validBits(validBits),
+ m_reset_ack_mask(0)
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, 0x%8.8x)", this, __FUNCTION__, bits, validBits);
+}
+
+PThreadEvent::~PThreadEvent()
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__);
+}
+
+
+uint32_t
+PThreadEvent::NewEventBit()
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__);
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+ uint32_t mask = 1;
+ while (mask & m_validBits)
+ mask <<= 1;
+ m_validBits |= mask;
+ return mask;
+}
+
+void
+PThreadEvent::FreeEventBits(const uint32_t mask)
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask);
+ if (mask)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+ m_bits &= ~mask;
+ m_validBits &= ~mask;
+ }
+}
+
+
+uint32_t
+PThreadEvent::GetEventBits() const
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__);
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+ uint32_t bits = m_bits;
+ return bits;
+}
+
+// Replace the event bits with a new bitmask value
+void
+PThreadEvent::ReplaceEventBits(const uint32_t bits)
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, bits);
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+ // Make sure we have some bits and that they aren't already set...
+ if (m_bits != bits)
+ {
+ // Figure out which bits are changing
+ uint32_t changed_bits = m_bits ^ bits;
+ // Set the new bit values
+ m_bits = bits;
+ // If any new bits are set, then broadcast
+ if (changed_bits & m_bits)
+ m_set_condition.Broadcast();
+ }
+}
+
+// Set one or more event bits and broadcast if any new event bits get set
+// that weren't already set.
+
+void
+PThreadEvent::SetEvents(const uint32_t mask)
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask);
+ // Make sure we have some bits to set
+ if (mask)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+ // Save the old event bit state so we can tell if things change
+ uint32_t old = m_bits;
+ // Set the all event bits that are set in 'mask'
+ m_bits |= mask;
+ // Broadcast only if any extra bits got set.
+ if (old != m_bits)
+ m_set_condition.Broadcast();
+ }
+}
+
+// Reset one or more event bits
+void
+PThreadEvent::ResetEvents(const uint32_t mask)
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask);
+ if (mask)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+
+ // Save the old event bit state so we can tell if things change
+ uint32_t old = m_bits;
+ // Clear the all event bits that are set in 'mask'
+ m_bits &= ~mask;
+ // Broadcast only if any extra bits got reset.
+ if (old != m_bits)
+ m_reset_condition.Broadcast();
+ }
+}
+
+//----------------------------------------------------------------------
+// Wait until 'timeout_abstime' for any events that are set in
+// 'mask'. If 'timeout_abstime' is NULL, then wait forever.
+//----------------------------------------------------------------------
+uint32_t
+PThreadEvent::WaitForSetEvents(const uint32_t mask, const struct timespec *timeout_abstime) const
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime);
+ int err = 0;
+ // pthread_cond_timedwait() or pthread_cond_wait() will atomically
+ // unlock the mutex and wait for the condition to be set. When either
+ // function returns, they will re-lock the mutex. We use an auto lock/unlock
+ // class (PThreadMutex::Locker) to allow us to return at any point in this
+ // function and not have to worry about unlocking the mutex.
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+ do
+ {
+ // Check our predicate (event bits) in case any are already set
+ if (mask & m_bits)
+ {
+ uint32_t bits_set = mask & m_bits;
+ // Our PThreadMutex::Locker will automatically unlock our mutex
+ return bits_set;
+ }
+ if (timeout_abstime)
+ {
+ // Wait for condition to get broadcast, or for a timeout. If we get
+ // a timeout we will drop out of the do loop and return false which
+ // is what we want.
+ err = ::pthread_cond_timedwait (m_set_condition.Condition(), m_mutex.Mutex(), timeout_abstime);
+ // Retest our predicate in case of a race condition right at the end
+ // of the timeout.
+ if (err == ETIMEDOUT)
+ {
+ uint32_t bits_set = mask & m_bits;
+ return bits_set;
+ }
+ }
+ else
+ {
+ // Wait for condition to get broadcast. The only error this function
+ // should return is if
+ err = ::pthread_cond_wait (m_set_condition.Condition(), m_mutex.Mutex());
+ }
+ } while (err == 0);
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Wait until 'timeout_abstime' for any events in 'mask' to reset.
+// If 'timeout_abstime' is NULL, then wait forever.
+//----------------------------------------------------------------------
+uint32_t
+PThreadEvent::WaitForEventsToReset(const uint32_t mask, const struct timespec *timeout_abstime) const
+{
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime);
+ int err = 0;
+ // pthread_cond_timedwait() or pthread_cond_wait() will atomically
+ // unlock the mutex and wait for the condition to be set. When either
+ // function returns, they will re-lock the mutex. We use an auto lock/unlock
+ // class (PThreadMutex::Locker) to allow us to return at any point in this
+ // function and not have to worry about unlocking the mutex.
+ PTHREAD_MUTEX_LOCKER (locker, m_mutex);
+ do
+ {
+ // Check our predicate (event bits) each time through this do loop
+ if ((mask & m_bits) == 0)
+ {
+ // All the bits requested have been reset, return zero indicating
+ // which bits from the mask were still set (none of them)
+ return 0;
+ }
+ if (timeout_abstime)
+ {
+ // Wait for condition to get broadcast, or for a timeout. If we get
+ // a timeout we will drop out of the do loop and return false which
+ // is what we want.
+ err = ::pthread_cond_timedwait (m_reset_condition.Condition(), m_mutex.Mutex(), timeout_abstime);
+ }
+ else
+ {
+ // Wait for condition to get broadcast. The only error this function
+ // should return is if
+ err = ::pthread_cond_wait (m_reset_condition.Condition(), m_mutex.Mutex());
+ }
+ } while (err == 0);
+ // Return a mask indicating which bits (if any) were still set
+ return mask & m_bits;
+}
+
+uint32_t
+PThreadEvent::WaitForResetAck (const uint32_t mask, const struct timespec *timeout_abstime) const
+{
+ if (mask & m_reset_ack_mask)
+ {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime);
+ return WaitForEventsToReset (mask & m_reset_ack_mask, timeout_abstime);
+ }
+ return 0;
+}
diff --git a/tools/debugserver/source/PThreadEvent.h b/tools/debugserver/source/PThreadEvent.h
new file mode 100644
index 000000000000..46c7cc09b121
--- /dev/null
+++ b/tools/debugserver/source/PThreadEvent.h
@@ -0,0 +1,59 @@
+//===-- PThreadEvent.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PThreadEvent_h__
+#define __PThreadEvent_h__
+#include "PThreadMutex.h"
+#include "PThreadCondition.h"
+#include <stdint.h>
+#include <time.h>
+
+class PThreadEvent
+{
+public:
+ PThreadEvent (uint32_t bits = 0, uint32_t validBits = 0);
+ ~PThreadEvent ();
+
+ uint32_t NewEventBit ();
+ void FreeEventBits (const uint32_t mask);
+
+ void ReplaceEventBits (const uint32_t bits);
+ uint32_t GetEventBits () const;
+ void SetEvents (const uint32_t mask);
+ void ResetEvents (const uint32_t mask);
+ // Wait for events to be set or reset. These functions take an optional
+ // timeout value. If timeout is NULL an infinite timeout will be used.
+ uint32_t WaitForSetEvents (const uint32_t mask, const struct timespec *timeout_abstime = NULL) const;
+ uint32_t WaitForEventsToReset(const uint32_t mask, const struct timespec *timeout_abstime = NULL) const;
+
+ uint32_t GetResetAckMask () const { return m_reset_ack_mask; }
+ uint32_t SetResetAckMask (uint32_t mask) { return m_reset_ack_mask = mask; }
+ uint32_t WaitForResetAck (const uint32_t mask, const struct timespec *timeout_abstime = NULL) const;
+protected:
+ //----------------------------------------------------------------------
+ // pthread condition and mutex variable to control access and allow
+ // blocking between the main thread and the spotlight index thread.
+ //----------------------------------------------------------------------
+ mutable PThreadMutex m_mutex;
+ mutable PThreadCondition m_set_condition;
+ mutable PThreadCondition m_reset_condition;
+ uint32_t m_bits;
+ uint32_t m_validBits;
+ uint32_t m_reset_ack_mask;
+private:
+ PThreadEvent(const PThreadEvent&); // Outlaw copy constructor
+ PThreadEvent& operator=(const PThreadEvent& rhs);
+
+};
+
+#endif // #ifndef __PThreadEvent_h__
diff --git a/tools/debugserver/source/PThreadMutex.cpp b/tools/debugserver/source/PThreadMutex.cpp
new file mode 100644
index 000000000000..bd91ed0154b1
--- /dev/null
+++ b/tools/debugserver/source/PThreadMutex.cpp
@@ -0,0 +1,84 @@
+//===-- PThreadMutex.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/9/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PThreadMutex.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "DNBTimer.h"
+
+#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+
+PThreadMutex::Locker::Locker(PThreadMutex& m, const char *function, const char *file, const int line) :
+ m_pMutex(m.Mutex()),
+ m_function(function),
+ m_file(file),
+ m_line(line),
+ m_lock_time(0)
+{
+ Lock();
+}
+
+PThreadMutex::Locker::Locker(PThreadMutex* m, const char *function, const char *file, const int line) :
+ m_pMutex(m ? m->Mutex() : NULL),
+ m_function(function),
+ m_file(file),
+ m_line(line),
+ m_lock_time(0)
+{
+ Lock();
+}
+
+PThreadMutex::Locker::Locker(pthread_mutex_t *mutex, const char *function, const char *file, const int line) :
+ m_pMutex(mutex),
+ m_function(function),
+ m_file(file),
+ m_line(line),
+ m_lock_time(0)
+{
+ Lock();
+}
+
+
+PThreadMutex::Locker::~Locker()
+{
+ Unlock();
+}
+
+
+void
+PThreadMutex::Locker::Lock()
+{
+ if (m_pMutex)
+ {
+ m_lock_time = DNBTimer::GetTimeOfDay();
+ if (::pthread_mutex_trylock (m_pMutex) != 0)
+ {
+ fprintf(stdout, "::pthread_mutex_trylock (%8.8p) mutex is locked (function %s in %s:%i), waiting...\n", m_pMutex, m_function, m_file, m_line);
+ ::pthread_mutex_lock (m_pMutex);
+ fprintf(stdout, "::pthread_mutex_lock (%8.8p) succeeded after %6llu usecs (function %s in %s:%i)\n", m_pMutex, DNBTimer::GetTimeOfDay() - m_lock_time, m_function, m_file, m_line);
+ }
+ }
+}
+
+
+void
+PThreadMutex::Locker::Unlock()
+{
+ fprintf(stdout, "::pthread_mutex_unlock (%8.8p) had lock for %6llu usecs in %s in %s:%i\n", m_pMutex, DNBTimer::GetTimeOfDay() - m_lock_time, m_function, m_file, m_line);
+ ::pthread_mutex_unlock (m_pMutex);
+}
+
+#endif
diff --git a/tools/debugserver/source/PThreadMutex.h b/tools/debugserver/source/PThreadMutex.h
new file mode 100644
index 000000000000..9a12f6e8e034
--- /dev/null
+++ b/tools/debugserver/source/PThreadMutex.h
@@ -0,0 +1,148 @@
+//===-- PThreadMutex.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PThreadMutex_h__
+#define __PThreadMutex_h__
+
+#include <pthread.h>
+#include <assert.h>
+#include <stdint.h>
+
+//#define DEBUG_PTHREAD_MUTEX_DEADLOCKS 1
+
+#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+#define PTHREAD_MUTEX_LOCKER(var, mutex) PThreadMutex::Locker var(mutex, __FUNCTION__, __FILE__, __LINE__)
+
+#else
+#define PTHREAD_MUTEX_LOCKER(var, mutex) PThreadMutex::Locker var(mutex)
+#endif
+
+class PThreadMutex
+{
+public:
+
+ class Locker
+ {
+ public:
+#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+
+ Locker(PThreadMutex& m, const char *function, const char *file, int line);
+ Locker(PThreadMutex* m, const char *function, const char *file, int line);
+ Locker(pthread_mutex_t *mutex, const char *function, const char *file, int line);
+ ~Locker();
+ void Lock();
+ void Unlock();
+
+#else
+ Locker(PThreadMutex& m) :
+ m_pMutex(m.Mutex())
+ {
+ Lock();
+ }
+
+ Locker(PThreadMutex* m) :
+ m_pMutex(m ? m->Mutex() : NULL)
+ {
+ Lock();
+ }
+
+ Locker(pthread_mutex_t *mutex) :
+ m_pMutex(mutex)
+ {
+ Lock();
+ }
+
+ void Lock()
+ {
+ if (m_pMutex)
+ ::pthread_mutex_lock (m_pMutex);
+ }
+
+ void Unlock()
+ {
+ if (m_pMutex)
+ ::pthread_mutex_unlock (m_pMutex);
+ }
+
+ ~Locker()
+ {
+ Unlock();
+ }
+
+#endif
+
+ // unlock any the current mutex and lock the new one if it is valid
+ void Reset(pthread_mutex_t *pMutex = NULL)
+ {
+ Unlock();
+ m_pMutex = pMutex;
+ Lock();
+ }
+ pthread_mutex_t *m_pMutex;
+#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+ const char *m_function;
+ const char *m_file;
+ int m_line;
+ uint64_t m_lock_time;
+#endif
+ };
+
+
+ PThreadMutex()
+ {
+ int err;
+ err = ::pthread_mutex_init (&m_mutex, NULL); assert(err == 0);
+ }
+
+ PThreadMutex(int type)
+ {
+ int err;
+ ::pthread_mutexattr_t attr;
+ err = ::pthread_mutexattr_init (&attr); assert(err == 0);
+ err = ::pthread_mutexattr_settype (&attr, type); assert(err == 0);
+ err = ::pthread_mutex_init (&m_mutex, &attr); assert(err == 0);
+ err = ::pthread_mutexattr_destroy (&attr); assert(err == 0);
+ }
+
+ ~PThreadMutex()
+ {
+ int err;
+ err = ::pthread_mutex_destroy (&m_mutex);
+ if (err != 0)
+ {
+ err = Unlock();
+ if (err == 0)
+ ::pthread_mutex_destroy (&m_mutex);
+ }
+ }
+
+ pthread_mutex_t *Mutex()
+ {
+ return &m_mutex;
+ }
+
+ int Lock()
+ {
+ return ::pthread_mutex_lock (&m_mutex);
+ }
+
+ int Unlock()
+ {
+ return ::pthread_mutex_unlock (&m_mutex);
+ }
+
+protected:
+ pthread_mutex_t m_mutex;
+};
+
+#endif
diff --git a/tools/debugserver/source/PseudoTerminal.cpp b/tools/debugserver/source/PseudoTerminal.cpp
new file mode 100644
index 000000000000..f1b505cabd4a
--- /dev/null
+++ b/tools/debugserver/source/PseudoTerminal.cpp
@@ -0,0 +1,227 @@
+//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/8/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PseudoTerminal.h"
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+//----------------------------------------------------------------------
+// PseudoTerminal constructor
+//----------------------------------------------------------------------
+PseudoTerminal::PseudoTerminal() :
+ m_master_fd(invalid_fd),
+ m_slave_fd(invalid_fd)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+// The master and slave file descriptors will get closed if they are
+// valid. Call the ReleaseMasterFD()/ReleaseSlaveFD() member functions
+// to release any file descriptors that are needed beyond the lifespan
+// of this object.
+//----------------------------------------------------------------------
+PseudoTerminal::~PseudoTerminal()
+{
+ CloseMaster();
+ CloseSlave();
+}
+
+//----------------------------------------------------------------------
+// Close the master file descriptor if it is valid.
+//----------------------------------------------------------------------
+void
+PseudoTerminal::CloseMaster()
+{
+ if (m_master_fd > 0)
+ {
+ ::close (m_master_fd);
+ m_master_fd = invalid_fd;
+ }
+}
+
+//----------------------------------------------------------------------
+// Close the slave file descriptor if it is valid.
+//----------------------------------------------------------------------
+void
+PseudoTerminal::CloseSlave()
+{
+ if (m_slave_fd > 0)
+ {
+ ::close (m_slave_fd);
+ m_slave_fd = invalid_fd;
+ }
+}
+
+//----------------------------------------------------------------------
+// Open the first available pseudo terminal with OFLAG as the
+// permissions. The file descriptor is store in the m_master_fd member
+// variable and can be accessed via the MasterFD() or ReleaseMasterFD()
+// accessors.
+//
+// Suggested value for oflag is O_RDWR|O_NOCTTY
+//
+// RETURNS:
+// Zero when successful, non-zero indicating an error occurred.
+//----------------------------------------------------------------------
+PseudoTerminal::Error
+PseudoTerminal::OpenFirstAvailableMaster(int oflag)
+{
+ // Open the master side of a pseudo terminal
+ m_master_fd = ::posix_openpt (oflag);
+ if (m_master_fd < 0)
+ {
+ return err_posix_openpt_failed;
+ }
+
+ // Grant access to the slave pseudo terminal
+ if (::grantpt (m_master_fd) < 0)
+ {
+ CloseMaster();
+ return err_grantpt_failed;
+ }
+
+ // Clear the lock flag on the slave pseudo terminal
+ if (::unlockpt (m_master_fd) < 0)
+ {
+ CloseMaster();
+ return err_unlockpt_failed;
+ }
+
+ return success;
+}
+
+//----------------------------------------------------------------------
+// Open the slave pseudo terminal for the current master pseudo
+// terminal. A master pseudo terminal should already be valid prior to
+// calling this function (see PseudoTerminal::OpenFirstAvailableMaster()).
+// The file descriptor is stored in the m_slave_fd member variable and
+// can be accessed via the SlaveFD() or ReleaseSlaveFD() accessors.
+//
+// RETURNS:
+// Zero when successful, non-zero indicating an error occurred.
+//----------------------------------------------------------------------
+PseudoTerminal::Error
+PseudoTerminal::OpenSlave(int oflag)
+{
+ CloseSlave();
+
+ // Open the master side of a pseudo terminal
+ const char *slave_name = SlaveName();
+
+ if (slave_name == NULL)
+ return err_ptsname_failed;
+
+ m_slave_fd = ::open (slave_name, oflag);
+
+ if (m_slave_fd < 0)
+ return err_open_slave_failed;
+
+ return success;
+}
+
+
+
+//----------------------------------------------------------------------
+// Get the name of the slave pseudo terminal. A master pseudo terminal
+// should already be valid prior to calling this function (see
+// PseudoTerminal::OpenFirstAvailableMaster()).
+//
+// RETURNS:
+// NULL if no valid master pseudo terminal or if ptsname() fails.
+// The name of the slave pseudo terminal as a NULL terminated C string
+// that comes from static memory, so a copy of the string should be
+// made as subsequent calls can change this value.
+//----------------------------------------------------------------------
+const char*
+PseudoTerminal::SlaveName() const
+{
+ if (m_master_fd < 0)
+ return NULL;
+ return ::ptsname (m_master_fd);
+}
+
+
+//----------------------------------------------------------------------
+// Fork a child process that and have its stdio routed to a pseudo
+// terminal.
+//
+// In the parent process when a valid pid is returned, the master file
+// descriptor can be used as a read/write access to stdio of the
+// child process.
+//
+// In the child process the stdin/stdout/stderr will already be routed
+// to the slave pseudo terminal and the master file descriptor will be
+// closed as it is no longer needed by the child process.
+//
+// This class will close the file descriptors for the master/slave
+// when the destructor is called, so be sure to call ReleaseMasterFD()
+// or ReleaseSlaveFD() if any file descriptors are going to be used
+// past the lifespan of this object.
+//
+// RETURNS:
+// in the parent process: the pid of the child, or -1 if fork fails
+// in the child process: zero
+//----------------------------------------------------------------------
+
+pid_t
+PseudoTerminal::Fork(PseudoTerminal::Error& error)
+{
+ pid_t pid = invalid_pid;
+ error = OpenFirstAvailableMaster (O_RDWR|O_NOCTTY);
+
+ if (error == 0)
+ {
+ // Successfully opened our master pseudo terminal
+
+ pid = ::fork ();
+ if (pid < 0)
+ {
+ // Fork failed
+ error = err_fork_failed;
+ }
+ else if (pid == 0)
+ {
+ // Child Process
+ ::setsid();
+
+ error = OpenSlave (O_RDWR);
+ if (error == 0)
+ {
+ // Successfully opened slave
+ // We are done with the master in the child process so lets close it
+ CloseMaster ();
+
+#if defined (TIOCSCTTY)
+ // Acquire the controlling terminal
+ if (::ioctl (m_slave_fd, TIOCSCTTY, (char *)0) < 0)
+ error = err_failed_to_acquire_controlling_terminal;
+#endif
+ // Duplicate all stdio file descriptors to the slave pseudo terminal
+ if (::dup2 (m_slave_fd, STDIN_FILENO) != STDIN_FILENO)
+ error = error ? error : err_dup2_failed_on_stdin;
+ if (::dup2 (m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO)
+ error = error ? error : err_dup2_failed_on_stdout;
+ if (::dup2 (m_slave_fd, STDERR_FILENO) != STDERR_FILENO)
+ error = error ? error : err_dup2_failed_on_stderr;
+ }
+ }
+ else
+ {
+ // Parent Process
+ // Do nothing and let the pid get returned!
+ }
+ }
+ return pid;
+}
diff --git a/tools/debugserver/source/PseudoTerminal.h b/tools/debugserver/source/PseudoTerminal.h
new file mode 100644
index 000000000000..da6b79307b9c
--- /dev/null
+++ b/tools/debugserver/source/PseudoTerminal.h
@@ -0,0 +1,94 @@
+//===-- PseudoTerminal.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/8/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PseudoTerminal_h__
+#define __PseudoTerminal_h__
+
+#include <fcntl.h>
+#include <termios.h>
+#include <string>
+
+class PseudoTerminal
+{
+public:
+ enum {
+ invalid_fd = -1,
+ invalid_pid = -1
+ };
+
+ enum Error
+ {
+ success = 0,
+ err_posix_openpt_failed = -2,
+ err_grantpt_failed = -3,
+ err_unlockpt_failed = -4,
+ err_ptsname_failed = -5,
+ err_open_slave_failed = -6,
+ err_fork_failed = -7,
+ err_setsid_failed = -8,
+ err_failed_to_acquire_controlling_terminal = -9,
+ err_dup2_failed_on_stdin = -10,
+ err_dup2_failed_on_stdout = -11,
+ err_dup2_failed_on_stderr = -12
+ };
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ PseudoTerminal ();
+ ~PseudoTerminal ();
+
+ void CloseMaster ();
+ void CloseSlave ();
+ Error OpenFirstAvailableMaster (int oflag);
+ Error OpenSlave (int oflag);
+ int MasterFD () const { return m_master_fd; }
+ int SlaveFD () const { return m_slave_fd; }
+ int ReleaseMasterFD ()
+ {
+ // Release ownership of the master pseudo terminal file
+ // descriptor without closing it. (the destructor for this
+ // class will close it otherwise!)
+ int fd = m_master_fd;
+ m_master_fd = invalid_fd;
+ return fd;
+ }
+ int ReleaseSlaveFD ()
+ {
+ // Release ownership of the slave pseudo terminal file
+ // descriptor without closing it (the destructor for this
+ // class will close it otherwise!)
+ int fd = m_slave_fd;
+ m_slave_fd = invalid_fd;
+ return fd;
+ }
+
+ const char* SlaveName () const;
+
+ pid_t Fork(Error& error);
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from PseudoTerminal can see and modify these
+ //------------------------------------------------------------------
+ int m_master_fd;
+ int m_slave_fd;
+
+private:
+ //------------------------------------------------------------------
+ // Outlaw copy and assignment constructors
+ //------------------------------------------------------------------
+ PseudoTerminal(const PseudoTerminal& rhs);
+ PseudoTerminal& operator=(const PseudoTerminal& rhs);
+
+};
+
+#endif // #ifndef __PseudoTerminal_h__
diff --git a/tools/debugserver/source/RNBContext.cpp b/tools/debugserver/source/RNBContext.cpp
new file mode 100644
index 000000000000..74ba5c45cb4b
--- /dev/null
+++ b/tools/debugserver/source/RNBContext.cpp
@@ -0,0 +1,279 @@
+//===-- RNBContext.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBContext.h"
+
+#include <sys/stat.h>
+#include <sstream>
+
+#if defined (__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#include "RNBRemote.h"
+#include "DNB.h"
+#include "DNBLog.h"
+#include "CFString.h"
+
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+RNBContext::~RNBContext()
+{
+ SetProcessID (INVALID_NUB_PROCESS);
+}
+
+//----------------------------------------------------------------------
+// RNBContext constructor
+//----------------------------------------------------------------------
+
+const char *
+RNBContext::EnvironmentAtIndex (size_t index)
+{
+ if (index < m_env_vec.size())
+ return m_env_vec[index].c_str();
+ else
+ return NULL;
+}
+
+
+const char *
+RNBContext::ArgumentAtIndex (size_t index)
+{
+ if (index < m_arg_vec.size())
+ return m_arg_vec[index].c_str();
+ else
+ return NULL;
+}
+
+bool
+RNBContext::SetWorkingDirectory (const char *path)
+{
+ struct stat working_directory_stat;
+ if (::stat (path, &working_directory_stat) != 0)
+ {
+ m_working_directory.clear();
+ return false;
+ }
+ m_working_directory.assign(path);
+ return true;
+}
+
+
+void
+RNBContext::SetProcessID (nub_process_t pid)
+{
+ // Delete and events we created
+ if (m_pid != INVALID_NUB_PROCESS)
+ {
+ StopProcessStatusThread ();
+ // Unregister this context as a client of the process's events.
+ }
+ // Assign our new process ID
+ m_pid = pid;
+
+ if (pid != INVALID_NUB_PROCESS)
+ {
+ StartProcessStatusThread ();
+ }
+}
+
+void
+RNBContext::StartProcessStatusThread()
+{
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
+ if ((m_events.GetEventBits() & event_proc_thread_running) == 0)
+ {
+ int err = ::pthread_create (&m_pid_pthread, NULL, ThreadFunctionProcessStatus, this);
+ if (err == 0)
+ {
+ // Our thread was successfully kicked off, wait for it to
+ // set the started event so we can safely continue
+ m_events.WaitForSetEvents (event_proc_thread_running);
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread got started!", __FUNCTION__);
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread failed to start: err = %i", __FUNCTION__, err);
+ m_events.ResetEvents (event_proc_thread_running);
+ m_events.SetEvents (event_proc_thread_exiting);
+ }
+ }
+}
+
+void
+RNBContext::StopProcessStatusThread()
+{
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
+ if ((m_events.GetEventBits() & event_proc_thread_running) == event_proc_thread_running)
+ {
+ struct timespec timeout_abstime;
+ DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
+ // Wait for 2 seconds for the rx thread to exit
+ if (m_events.WaitForSetEvents(RNBContext::event_proc_thread_exiting, &timeout_abstime) == RNBContext::event_proc_thread_exiting)
+ {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread stopped as requeseted", __FUNCTION__);
+ }
+ else
+ {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread did not stop in 2 seconds...", __FUNCTION__);
+ // Kill the RX thread???
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+// This thread's sole purpose is to watch for any status changes in the
+// child process.
+//----------------------------------------------------------------------
+void*
+RNBContext::ThreadFunctionProcessStatus(void *arg)
+{
+ RNBRemoteSP remoteSP(g_remoteSP);
+ RNBRemote* remote = remoteSP.get();
+ if (remote == NULL)
+ return NULL;
+ RNBContext& ctx = remote->Context();
+
+ nub_process_t pid = ctx.ProcessID();
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread starting...", __FUNCTION__, arg, pid);
+ ctx.Events().SetEvents (RNBContext::event_proc_thread_running);
+
+#if defined (__APPLE__)
+ pthread_setname_np ("child process status watcher thread");
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0)
+ {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ bool done = false;
+ while (!done)
+ {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, true)...", __FUNCTION__);
+ nub_event_t pid_status_event = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, true, NULL);
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, true) => 0x%8.8x", __FUNCTION__, pid_status_event);
+
+ if (pid_status_event == 0)
+ {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got ZERO back from DNBProcessWaitForEvent....", __FUNCTION__, pid);
+ // done = true;
+ }
+ else
+ {
+ if (pid_status_event & eEventStdioAvailable)
+ {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got stdio available event....", __FUNCTION__, pid);
+ ctx.Events().SetEvents (RNBContext::event_proc_stdio_available);
+ // Wait for the main thread to consume this notification if it requested we wait for it
+ ctx.Events().WaitForResetAck(RNBContext::event_proc_stdio_available);
+ }
+
+ if (pid_status_event & eEventProfileDataAvailable)
+ {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got profile data event....", __FUNCTION__, pid);
+ ctx.Events().SetEvents (RNBContext::event_proc_profile_data);
+ // Wait for the main thread to consume this notification if it requested we wait for it
+ ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data);
+ }
+
+ if (pid_status_event & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
+ {
+ nub_state_t pid_state = DNBProcessGetState(pid);
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got process state change: %s", __FUNCTION__, pid, DNBStateAsString(pid_state));
+
+ // Let the main thread know there is a process state change to see
+ ctx.Events().SetEvents (RNBContext::event_proc_state_changed);
+ // Wait for the main thread to consume this notification if it requested we wait for it
+ ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed);
+
+ switch (pid_state)
+ {
+ case eStateStopped:
+ break;
+
+ case eStateInvalid:
+ case eStateExited:
+ case eStateDetached:
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Reset any events that we consumed.
+ DNBProcessResetEvents(pid, pid_status_event);
+
+ }
+ }
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...", __FUNCTION__, arg, pid);
+ ctx.Events().ResetEvents(event_proc_thread_running);
+ ctx.Events().SetEvents(event_proc_thread_exiting);
+ return NULL;
+}
+
+
+const char*
+RNBContext::EventsAsString (nub_event_t events, std::string& s)
+{
+ s.clear();
+ if (events & event_proc_state_changed)
+ s += "proc_state_changed ";
+ if (events & event_proc_thread_running)
+ s += "proc_thread_running ";
+ if (events & event_proc_thread_exiting)
+ s += "proc_thread_exiting ";
+ if (events & event_proc_stdio_available)
+ s += "proc_stdio_available ";
+ if (events & event_proc_profile_data)
+ s += "proc_profile_data ";
+ if (events & event_read_packet_available)
+ s += "read_packet_available ";
+ if (events & event_read_thread_running)
+ s += "read_thread_running ";
+ if (events & event_read_thread_running)
+ s += "read_thread_running ";
+ return s.c_str();
+}
+
+const char *
+RNBContext::LaunchStatusAsString (std::string& s)
+{
+ s.clear();
+
+ const char *err_str = m_launch_status.AsString();
+ if (err_str)
+ s = err_str;
+ else
+ {
+ char error_num_str[64];
+ snprintf(error_num_str, sizeof(error_num_str), "%u", m_launch_status.Error());
+ s = error_num_str;
+ }
+ return s.c_str();
+}
+
+bool
+RNBContext::ProcessStateRunning() const
+{
+ nub_state_t pid_state = DNBProcessGetState(m_pid);
+ return pid_state == eStateRunning || pid_state == eStateStepping;
+}
diff --git a/tools/debugserver/source/RNBContext.h b/tools/debugserver/source/RNBContext.h
new file mode 100644
index 000000000000..34fb9796ebeb
--- /dev/null
+++ b/tools/debugserver/source/RNBContext.h
@@ -0,0 +1,159 @@
+//===-- RNBContext.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBContext_h__
+#define __RNBContext_h__
+
+#include "RNBDefs.h"
+#include "DNBError.h"
+#include "PThreadEvent.h"
+#include <vector>
+#include <string>
+
+class RNBContext
+{
+public:
+ enum
+ {
+ event_proc_state_changed = 0x01,
+ event_proc_thread_running = 0x02, // Sticky
+ event_proc_thread_exiting = 0x04,
+ event_proc_stdio_available = 0x08,
+ event_proc_profile_data = 0x10,
+ event_read_packet_available = 0x20,
+ event_read_thread_running = 0x40, // Sticky
+ event_read_thread_exiting = 0x80,
+
+ normal_event_bits = event_proc_state_changed |
+ event_proc_thread_exiting |
+ event_proc_stdio_available |
+ event_proc_profile_data |
+ event_read_packet_available |
+ event_read_thread_exiting,
+
+ sticky_event_bits = event_proc_thread_running |
+ event_read_thread_running,
+
+
+ all_event_bits = sticky_event_bits | normal_event_bits
+ } event_t;
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ RNBContext () :
+ m_pid(INVALID_NUB_PROCESS),
+ m_pid_stop_count(0),
+ m_events(0, all_event_bits),
+ m_pid_pthread(),
+ m_launch_status(),
+ m_arg_vec (),
+ m_env_vec (),
+ m_detach_on_error(false)
+ {
+ }
+
+ virtual ~RNBContext();
+
+
+ nub_process_t ProcessID() const { return m_pid; }
+ bool HasValidProcessID() const { return m_pid != INVALID_NUB_PROCESS; }
+ void SetProcessID (nub_process_t pid);
+ nub_size_t GetProcessStopCount () const { return m_pid_stop_count; }
+ bool SetProcessStopCount (nub_size_t count)
+ {
+ // Returns true if this class' notion of the PID state changed
+ if (m_pid_stop_count == count)
+ return false; // Didn't change
+ m_pid_stop_count = count;
+ return true; // The stop count has changed.
+ }
+
+ bool ProcessStateRunning() const;
+ PThreadEvent& Events( ) { return m_events; }
+ nub_event_t AllEventBits() const { return all_event_bits; }
+ nub_event_t NormalEventBits() const { return normal_event_bits; }
+ nub_event_t StickyEventBits() const { return sticky_event_bits; }
+ const char* EventsAsString (nub_event_t events, std::string& s);
+
+ size_t ArgumentCount () const { return m_arg_vec.size(); }
+ const char * ArgumentAtIndex (size_t index);
+ void PushArgument (const char *arg) { if (arg) m_arg_vec.push_back (arg); }
+ void ClearArgv () { m_arg_vec.erase (m_arg_vec.begin(), m_arg_vec.end()); }
+
+ size_t EnvironmentCount () const { return m_env_vec.size(); }
+ const char * EnvironmentAtIndex (size_t index);
+ void PushEnvironment (const char *arg) { if (arg) m_env_vec.push_back (arg); }
+ void ClearEnvironment () { m_env_vec.erase (m_env_vec.begin(), m_env_vec.end()); }
+ DNBError& LaunchStatus () { return m_launch_status; }
+ const char * LaunchStatusAsString (std::string& s);
+ nub_launch_flavor_t LaunchFlavor () const { return m_launch_flavor; }
+ void SetLaunchFlavor (nub_launch_flavor_t flavor) { m_launch_flavor = flavor; }
+
+ const char * GetWorkingDirectory () const
+ {
+ if (!m_working_directory.empty())
+ return m_working_directory.c_str();
+ return NULL;
+ }
+
+ bool SetWorkingDirectory (const char *path);
+
+ std::string& GetSTDIN () { return m_stdin; }
+ std::string& GetSTDOUT () { return m_stdout; }
+ std::string& GetSTDERR () { return m_stderr; }
+ std::string& GetWorkingDir () { return m_working_dir; }
+
+ const char * GetSTDINPath() { return m_stdin.empty() ? NULL : m_stdin.c_str(); }
+ const char * GetSTDOUTPath() { return m_stdout.empty() ? NULL : m_stdout.c_str(); }
+ const char * GetSTDERRPath() { return m_stderr.empty() ? NULL : m_stderr.c_str(); }
+ const char * GetWorkingDirPath() { return m_working_dir.empty() ? NULL : m_working_dir.c_str(); }
+
+ void PushProcessEvent (const char *p) { m_process_event.assign(p); }
+ const char * GetProcessEvent () { return m_process_event.c_str(); }
+
+ void SetDetachOnError(bool detach) { m_detach_on_error = detach; }
+ bool GetDetachOnError () { return m_detach_on_error; }
+
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from RNBContext can see and modify these
+ //------------------------------------------------------------------
+ nub_process_t m_pid;
+ std::string m_stdin;
+ std::string m_stdout;
+ std::string m_stderr;
+ std::string m_working_dir;
+ nub_size_t m_pid_stop_count;
+ PThreadEvent m_events; // Threaded events that we can wait for
+ pthread_t m_pid_pthread;
+ nub_launch_flavor_t m_launch_flavor; // How to launch our inferior process
+ DNBError m_launch_status; // This holds the status from the last launch attempt.
+ std::vector<std::string> m_arg_vec;
+ std::vector<std::string> m_env_vec; // This will be unparsed - entries FOO=value
+ std::string m_working_directory;
+ std::string m_process_event;
+ bool m_detach_on_error;
+
+ void StartProcessStatusThread();
+ void StopProcessStatusThread();
+ static void* ThreadFunctionProcessStatus(void *arg);
+
+private:
+ //------------------------------------------------------------------
+ // Outlaw copy and assignment operators
+ //------------------------------------------------------------------
+ RNBContext(const RNBContext& rhs);
+ RNBContext& operator=(const RNBContext& rhs);
+};
+
+#endif // #ifndef __RNBContext_h__
diff --git a/tools/debugserver/source/RNBDefs.h b/tools/debugserver/source/RNBDefs.h
new file mode 100644
index 000000000000..984b91152415
--- /dev/null
+++ b/tools/debugserver/source/RNBDefs.h
@@ -0,0 +1,87 @@
+//===-- RNBDefs.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/14/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBDefs_h__
+#define __RNBDefs_h__
+
+#include "DNBDefs.h"
+#include <memory>
+
+#define DEBUGSERVER_PROGRAM_NAME "debugserver"
+
+#ifndef DEBUGSERVER_VERSION_NUM
+extern "C" const unsigned char debugserverVersionString[];
+#define DEBUGSERVER_VERSION_NUM debugserverVersionNumber
+#endif
+
+#ifndef DEBUGSERVER_VERSION_STR
+extern "C" const double debugserverVersionNumber;
+#define DEBUGSERVER_VERSION_STR debugserverVersionString
+#endif
+
+#if defined (__i386__)
+
+#define RNB_ARCH "i386"
+
+#elif defined (__x86_64__)
+
+#define RNB_ARCH "x86_64"
+
+#elif defined (__ppc64__)
+
+#define RNB_ARCH "ppc64"
+
+#elif defined (__powerpc__) || defined (__ppc__)
+
+#define RNB_ARCH "ppc"
+
+#elif defined (__arm64__) || defined (__aarch64__)
+
+#define RNB_ARCH "arm64"
+
+#elif defined (__arm__)
+
+#define RNB_ARCH "armv7"
+
+#else
+
+#error undefined architecture
+
+#endif
+
+class RNBRemote;
+typedef std::shared_ptr<RNBRemote> RNBRemoteSP;
+
+typedef enum
+{
+ rnb_success = 0,
+ rnb_err = 1,
+ rnb_not_connected = 2
+} rnb_err_t;
+
+// Log bits
+// reserve low bits for DNB
+#define LOG_RNB_MINIMAL ((LOG_LO_USER) << 0) // Minimal logging (min verbosity)
+#define LOG_RNB_MEDIUM ((LOG_LO_USER) << 1) // Medium logging (med verbosity)
+#define LOG_RNB_MAX ((LOG_LO_USER) << 2) // Max logging (max verbosity)
+#define LOG_RNB_COMM ((LOG_LO_USER) << 3) // Log communications (RNBSocket)
+#define LOG_RNB_REMOTE ((LOG_LO_USER) << 4) // Log remote (RNBRemote)
+#define LOG_RNB_EVENTS ((LOG_LO_USER) << 5) // Log events (PThreadEvents)
+#define LOG_RNB_PROC ((LOG_LO_USER) << 6) // Log process state (Process thread)
+#define LOG_RNB_PACKETS ((LOG_LO_USER) << 7) // Log gdb remote packets
+#define LOG_RNB_ALL (~((LOG_LO_USER) - 1))
+#define LOG_RNB_DEFAULT (LOG_RNB_ALL)
+
+extern RNBRemoteSP g_remoteSP;
+
+#endif // #ifndef __RNBDefs_h__
diff --git a/tools/debugserver/source/RNBRemote.cpp b/tools/debugserver/source/RNBRemote.cpp
new file mode 100644
index 000000000000..dadf479ce81b
--- /dev/null
+++ b/tools/debugserver/source/RNBRemote.cpp
@@ -0,0 +1,6083 @@
+//===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBRemote.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <mach/exception_types.h>
+#include <mach-o/loader.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#if defined (__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#include "DNB.h"
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "JSONGenerator.h"
+#include "RNBContext.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "lldb/Utility/StringExtractor.h"
+#include "MacOSX/Genealogy.h"
+#include "JSONGenerator.h"
+
+#if defined (HAVE_LIBCOMPRESSION)
+#include <compression.h>
+#endif
+
+#if defined (HAVE_LIBZ)
+#include <zlib.h>
+#endif
+
+#include <iomanip>
+#include <sstream>
+#include <unordered_set>
+#include <TargetConditionals.h> // for endianness predefines
+
+//----------------------------------------------------------------------
+// std::iostream formatting macros
+//----------------------------------------------------------------------
+#define RAW_HEXBASE std::setfill('0') << std::hex << std::right
+#define HEXBASE '0' << 'x' << RAW_HEXBASE
+#define RAWHEX8(x) RAW_HEXBASE << std::setw(2) << ((uint32_t)((uint8_t)x))
+#define RAWHEX16 RAW_HEXBASE << std::setw(4)
+#define RAWHEX32 RAW_HEXBASE << std::setw(8)
+#define RAWHEX64 RAW_HEXBASE << std::setw(16)
+#define HEX8(x) HEXBASE << std::setw(2) << ((uint32_t)(x))
+#define HEX16 HEXBASE << std::setw(4)
+#define HEX32 HEXBASE << std::setw(8)
+#define HEX64 HEXBASE << std::setw(16)
+#define RAW_HEX(x) RAW_HEXBASE << std::setw(sizeof(x)*2) << (x)
+#define HEX(x) HEXBASE << std::setw(sizeof(x)*2) << (x)
+#define RAWHEX_SIZE(x, sz) RAW_HEXBASE << std::setw((sz)) << (x)
+#define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x)
+#define STRING_WIDTH(w) std::setfill(' ') << std::setw(w)
+#define LEFT_STRING_WIDTH(s, w) std::left << std::setfill(' ') << std::setw(w) << (s) << std::right
+#define DECIMAL std::dec << std::setfill(' ')
+#define DECIMAL_WIDTH(w) DECIMAL << std::setw(w)
+#define FLOAT(n, d) std::setfill(' ') << std::setw((n)+(d)+1) << std::setprecision(d) << std::showpoint << std::fixed
+#define INDENT_WITH_SPACES(iword_idx) std::setfill(' ') << std::setw((iword_idx)) << ""
+#define INDENT_WITH_TABS(iword_idx) std::setfill('\t') << std::setw((iword_idx)) << ""
+// Class to handle communications via gdb remote protocol.
+
+
+//----------------------------------------------------------------------
+// Decode a single hex character and return the hex value as a number or
+// -1 if "ch" is not a hex character.
+//----------------------------------------------------------------------
+static inline int
+xdigit_to_sint (char ch)
+{
+ if (ch >= 'a' && ch <= 'f')
+ return 10 + ch - 'a';
+ if (ch >= 'A' && ch <= 'F')
+ return 10 + ch - 'A';
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ return -1;
+}
+
+//----------------------------------------------------------------------
+// Decode a single hex ASCII byte. Return -1 on failure, a value 0-255
+// on success.
+//----------------------------------------------------------------------
+static inline int
+decoded_hex_ascii_char(const char *p)
+{
+ const int hi_nibble = xdigit_to_sint(p[0]);
+ if (hi_nibble == -1)
+ return -1;
+ const int lo_nibble = xdigit_to_sint(p[1]);
+ if (lo_nibble == -1)
+ return -1;
+ return (uint8_t)((hi_nibble << 4) + lo_nibble);
+}
+
+//----------------------------------------------------------------------
+// Decode a hex ASCII string back into a string
+//----------------------------------------------------------------------
+static std::string
+decode_hex_ascii_string(const char *p, uint32_t max_length = UINT32_MAX)
+{
+ std::string arg;
+ if (p)
+ {
+ for (const char *c = p; ((c - p)/2) < max_length; c += 2)
+ {
+ int ch = decoded_hex_ascii_char(c);
+ if (ch == -1)
+ break;
+ else
+ arg.push_back(ch);
+ }
+ }
+ return arg;
+}
+
+uint64_t
+decode_uint64 (const char *p, int base, char **end = nullptr, uint64_t fail_value = 0)
+{
+ nub_addr_t addr = strtoull (p, end, 16);
+ if (addr == 0 && errno != 0)
+ return fail_value;
+ return addr;
+}
+
+extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args);
+
+#if defined (__APPLE__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000)
+// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
+extern "C" {
+#define CS_OPS_STATUS 0 /* return status */
+#define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
+int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
+
+// from rootless.h
+bool rootless_allows_task_for_pid (pid_t pid);
+
+// from sys/csr.h
+typedef uint32_t csr_config_t;
+#define CSR_ALLOW_TASK_FOR_PID (1 << 2)
+int csr_check(csr_config_t mask);
+}
+#endif
+
+RNBRemote::RNBRemote () :
+ m_ctx (),
+ m_comm (),
+ m_arch (),
+ m_continue_thread(-1),
+ m_thread(-1),
+ m_mutex(),
+ m_dispatch_queue_offsets (),
+ m_dispatch_queue_offsets_addr (INVALID_NUB_ADDRESS),
+ m_qSymbol_index (UINT32_MAX),
+ m_packets_recvd(0),
+ m_packets(),
+ m_rx_packets(),
+ m_rx_partial_data(),
+ m_rx_pthread(0),
+ m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4),
+ m_extended_mode(false),
+ m_noack_mode(false),
+ m_thread_suffix_supported (false),
+ m_list_threads_in_stop_reply (false),
+ m_compression_minsize (384),
+ m_enable_compression_next_send_packet (false),
+ m_compression_mode (compression_types::none)
+{
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
+ CreatePacketTable ();
+}
+
+
+RNBRemote::~RNBRemote()
+{
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
+ StopReadRemoteDataThread();
+}
+
+void
+RNBRemote::CreatePacketTable ()
+{
+ // Step required to add new packets:
+ // 1 - Add new enumeration to RNBRemote::PacketEnum
+ // 2 - Create the RNBRemote::HandlePacket_ function if a new function is needed
+ // 3 - Register the Packet definition with any needed callbacks in this function
+ // - If no response is needed for a command, then use NULL for the normal callback
+ // - If the packet is not supported while the target is running, use NULL for the async callback
+ // 4 - If the packet is a standard packet (starts with a '$' character
+ // followed by the payload and then '#' and checksum, then you are done
+ // else go on to step 5
+ // 5 - if the packet is a fixed length packet:
+ // - modify the switch statement for the first character in the payload
+ // in RNBRemote::CommDataReceived so it doesn't reject the new packet
+ // type as invalid
+ // - modify the switch statement for the first character in the payload
+ // in RNBRemote::GetPacketPayload and make sure the payload of the packet
+ // is returned correctly
+
+ std::vector <Packet> &t = m_packets;
+ t.push_back (Packet (ack, NULL, NULL, "+", "ACK"));
+ t.push_back (Packet (nack, NULL, NULL, "-", "!ACK"));
+ t.push_back (Packet (read_memory, &RNBRemote::HandlePacket_m, NULL, "m", "Read memory"));
+ t.push_back (Packet (read_register, &RNBRemote::HandlePacket_p, NULL, "p", "Read one register"));
+ t.push_back (Packet (read_general_regs, &RNBRemote::HandlePacket_g, NULL, "g", "Read registers"));
+ t.push_back (Packet (write_memory, &RNBRemote::HandlePacket_M, NULL, "M", "Write memory"));
+ t.push_back (Packet (write_register, &RNBRemote::HandlePacket_P, NULL, "P", "Write one register"));
+ t.push_back (Packet (write_general_regs, &RNBRemote::HandlePacket_G, NULL, "G", "Write registers"));
+ t.push_back (Packet (insert_mem_bp, &RNBRemote::HandlePacket_z, NULL, "Z0", "Insert memory breakpoint"));
+ t.push_back (Packet (remove_mem_bp, &RNBRemote::HandlePacket_z, NULL, "z0", "Remove memory breakpoint"));
+ t.push_back (Packet (single_step, &RNBRemote::HandlePacket_s, NULL, "s", "Single step"));
+ t.push_back (Packet (cont, &RNBRemote::HandlePacket_c, NULL, "c", "continue"));
+ t.push_back (Packet (single_step_with_sig, &RNBRemote::HandlePacket_S, NULL, "S", "Single step with signal"));
+ t.push_back (Packet (set_thread, &RNBRemote::HandlePacket_H, NULL, "H", "Set thread"));
+ t.push_back (Packet (halt, &RNBRemote::HandlePacket_last_signal, &RNBRemote::HandlePacket_stop_process, "\x03", "^C"));
+// t.push_back (Packet (use_extended_mode, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "!", "Use extended mode"));
+ t.push_back (Packet (why_halted, &RNBRemote::HandlePacket_last_signal, NULL, "?", "Why did target halt"));
+ t.push_back (Packet (set_argv, &RNBRemote::HandlePacket_A, NULL, "A", "Set argv"));
+// t.push_back (Packet (set_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "B", "Set/clear breakpoint"));
+ t.push_back (Packet (continue_with_sig, &RNBRemote::HandlePacket_C, NULL, "C", "Continue with signal"));
+ t.push_back (Packet (detach, &RNBRemote::HandlePacket_D, NULL, "D", "Detach gdb from remote system"));
+// t.push_back (Packet (step_inferior_one_cycle, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "i", "Step inferior by one clock cycle"));
+// t.push_back (Packet (signal_and_step_inf_one_cycle, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "I", "Signal inferior, then step one clock cycle"));
+ t.push_back (Packet (kill, &RNBRemote::HandlePacket_k, NULL, "k", "Kill"));
+// t.push_back (Packet (restart, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "R", "Restart inferior"));
+// t.push_back (Packet (search_mem_backwards, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "t", "Search memory backwards"));
+ t.push_back (Packet (thread_alive_p, &RNBRemote::HandlePacket_T, NULL, "T", "Is thread alive"));
+ t.push_back (Packet (query_supported_features, &RNBRemote::HandlePacket_qSupported, NULL, "qSupported", "Query about supported features"));
+ t.push_back (Packet (vattach, &RNBRemote::HandlePacket_v, NULL, "vAttach", "Attach to a new process"));
+ t.push_back (Packet (vattachwait, &RNBRemote::HandlePacket_v, NULL, "vAttachWait", "Wait for a process to start up then attach to it"));
+ t.push_back (Packet (vattachorwait, &RNBRemote::HandlePacket_v, NULL, "vAttachOrWait", "Attach to the process or if it doesn't exist, wait for the process to start up then attach to it"));
+ t.push_back (Packet (vattachname, &RNBRemote::HandlePacket_v, NULL, "vAttachName", "Attach to an existing process by name"));
+ t.push_back (Packet (vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, "vCont;", "Verbose resume with thread actions"));
+ t.push_back (Packet (vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, "vCont?", "List valid continue-with-thread-actions actions"));
+ t.push_back (Packet (read_data_from_memory, &RNBRemote::HandlePacket_x, NULL, "x", "Read data from memory"));
+ t.push_back (Packet (write_data_to_memory, &RNBRemote::HandlePacket_X, NULL, "X", "Write data to memory"));
+// t.push_back (Packet (insert_hardware_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z1", "Insert hardware breakpoint"));
+// t.push_back (Packet (remove_hardware_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z1", "Remove hardware breakpoint"));
+ t.push_back (Packet (insert_write_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z2", "Insert write watchpoint"));
+ t.push_back (Packet (remove_write_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z2", "Remove write watchpoint"));
+ t.push_back (Packet (insert_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z3", "Insert read watchpoint"));
+ t.push_back (Packet (remove_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z3", "Remove read watchpoint"));
+ t.push_back (Packet (insert_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z4", "Insert access watchpoint"));
+ t.push_back (Packet (remove_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z4", "Remove access watchpoint"));
+ t.push_back (Packet (query_monitor, &RNBRemote::HandlePacket_qRcmd, NULL, "qRcmd", "Monitor command"));
+ t.push_back (Packet (query_current_thread_id, &RNBRemote::HandlePacket_qC, NULL, "qC", "Query current thread ID"));
+ t.push_back (Packet (query_echo, &RNBRemote::HandlePacket_qEcho, NULL, "qEcho:", "Echo the packet back to allow the debugger to sync up with this server"));
+ t.push_back (Packet (query_get_pid, &RNBRemote::HandlePacket_qGetPid, NULL, "qGetPid", "Query process id"));
+ t.push_back (Packet (query_thread_ids_first, &RNBRemote::HandlePacket_qThreadInfo, NULL, "qfThreadInfo", "Get list of active threads (first req)"));
+ t.push_back (Packet (query_thread_ids_subsequent, &RNBRemote::HandlePacket_qThreadInfo, NULL, "qsThreadInfo", "Get list of active threads (subsequent req)"));
+ // APPLE LOCAL: qThreadStopInfo
+ // syntax: qThreadStopInfoTTTT
+ // TTTT is hex thread ID
+ t.push_back (Packet (query_thread_stop_info, &RNBRemote::HandlePacket_qThreadStopInfo, NULL, "qThreadStopInfo", "Get detailed info on why the specified thread stopped"));
+ t.push_back (Packet (query_thread_extra_info, &RNBRemote::HandlePacket_qThreadExtraInfo,NULL, "qThreadExtraInfo", "Get printable status of a thread"));
+// t.push_back (Packet (query_image_offsets, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qOffsets", "Report offset of loaded program"));
+ t.push_back (Packet (query_launch_success, &RNBRemote::HandlePacket_qLaunchSuccess,NULL, "qLaunchSuccess", "Report the success or failure of the launch attempt"));
+ t.push_back (Packet (query_register_info, &RNBRemote::HandlePacket_qRegisterInfo, NULL, "qRegisterInfo", "Dynamically discover remote register context information."));
+ t.push_back (Packet (query_shlib_notify_info_addr, &RNBRemote::HandlePacket_qShlibInfoAddr,NULL, "qShlibInfoAddr", "Returns the address that contains info needed for getting shared library notifications"));
+ t.push_back (Packet (query_step_packet_supported, &RNBRemote::HandlePacket_qStepPacketSupported,NULL, "qStepPacketSupported", "Replys with OK if the 's' packet is supported."));
+ t.push_back (Packet (query_vattachorwait_supported, &RNBRemote::HandlePacket_qVAttachOrWaitSupported,NULL, "qVAttachOrWaitSupported", "Replys with OK if the 'vAttachOrWait' packet is supported."));
+ t.push_back (Packet (query_sync_thread_state_supported, &RNBRemote::HandlePacket_qSyncThreadStateSupported,NULL, "qSyncThreadStateSupported", "Replys with OK if the 'QSyncThreadState:' packet is supported."));
+ t.push_back (Packet (query_host_info, &RNBRemote::HandlePacket_qHostInfo , NULL, "qHostInfo", "Replies with multiple 'key:value;' tuples appended to each other."));
+ t.push_back (Packet (query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion , NULL, "qGDBServerVersion", "Replies with multiple 'key:value;' tuples appended to each other."));
+ t.push_back (Packet (query_process_info, &RNBRemote::HandlePacket_qProcessInfo , NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other."));
+ t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol , NULL, "qSymbol:", "Notify that host debugger is ready to do symbol lookups"));
+ t.push_back (Packet (json_query_thread_extended_info,&RNBRemote::HandlePacket_jThreadExtendedInfo , NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information."));
+ t.push_back (Packet (json_query_get_loaded_dynamic_libraries_infos, &RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos, NULL, "jGetLoadedDynamicLibrariesInfos", "Replies with JSON data of all the shared libraries loaded in this process."));
+ t.push_back (Packet (json_query_threads_info, &RNBRemote::HandlePacket_jThreadsInfo , NULL, "jThreadsInfo", "Replies with JSON data with information about all threads."));
+ t.push_back (Packet (start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets"));
+ t.push_back (Packet (prefix_reg_packets_with_tid, &RNBRemote::HandlePacket_QThreadSuffixSupported , NULL, "QThreadSuffixSupported", "Check if thread specific packets (register packets 'g', 'G', 'p', and 'P') support having the thread ID appended to the end of the command"));
+ t.push_back (Packet (set_logging_mode, &RNBRemote::HandlePacket_QSetLogging , NULL, "QSetLogging:", "Check if register packets ('g', 'G', 'p', and 'P' support having the thread ID prefix"));
+ t.push_back (Packet (set_max_packet_size, &RNBRemote::HandlePacket_QSetMaxPacketSize , NULL, "QSetMaxPacketSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle"));
+ t.push_back (Packet (set_max_payload_size, &RNBRemote::HandlePacket_QSetMaxPayloadSize , NULL, "QSetMaxPayloadSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized payload gdb can handle"));
+ t.push_back (Packet (set_environment_variable, &RNBRemote::HandlePacket_QEnvironment , NULL, "QEnvironment:", "Add an environment variable to the inferior's environment"));
+ t.push_back (Packet (set_environment_variable_hex, &RNBRemote::HandlePacket_QEnvironmentHexEncoded , NULL, "QEnvironmentHexEncoded:", "Add an environment variable to the inferior's environment"));
+ t.push_back (Packet (set_launch_arch, &RNBRemote::HandlePacket_QLaunchArch , NULL, "QLaunchArch:", "Set the architecture to use when launching a process for hosts that can run multiple architecture slices from universal files."));
+ t.push_back (Packet (set_disable_aslr, &RNBRemote::HandlePacket_QSetDisableASLR , NULL, "QSetDisableASLR:", "Set whether to disable ASLR when launching the process with the set argv ('A') packet"));
+ t.push_back (Packet (set_stdin, &RNBRemote::HandlePacket_QSetSTDIO , NULL, "QSetSTDIN:", "Set the standard input for a process to be launched with the 'A' packet"));
+ t.push_back (Packet (set_stdout, &RNBRemote::HandlePacket_QSetSTDIO , NULL, "QSetSTDOUT:", "Set the standard output for a process to be launched with the 'A' packet"));
+ t.push_back (Packet (set_stderr, &RNBRemote::HandlePacket_QSetSTDIO , NULL, "QSetSTDERR:", "Set the standard error for a process to be launched with the 'A' packet"));
+ t.push_back (Packet (set_working_dir, &RNBRemote::HandlePacket_QSetWorkingDir , NULL, "QSetWorkingDir:", "Set the working directory for a process to be launched with the 'A' packet"));
+ t.push_back (Packet (set_list_threads_in_stop_reply,&RNBRemote::HandlePacket_QListThreadsInStopReply , NULL, "QListThreadsInStopReply", "Set if the 'threads' key should be added to the stop reply packets with a list of all thread IDs."));
+ t.push_back (Packet (sync_thread_state, &RNBRemote::HandlePacket_QSyncThreadState , NULL, "QSyncThreadState:", "Do whatever is necessary to make sure 'thread' is in a safe state to call functions on."));
+// t.push_back (Packet (pass_signals_to_inferior, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "QPassSignals:", "Specify which signals are passed to the inferior"));
+ t.push_back (Packet (allocate_memory, &RNBRemote::HandlePacket_AllocateMemory, NULL, "_M", "Allocate memory in the inferior process."));
+ t.push_back (Packet (deallocate_memory, &RNBRemote::HandlePacket_DeallocateMemory, NULL, "_m", "Deallocate memory in the inferior process."));
+ t.push_back (Packet (save_register_state, &RNBRemote::HandlePacket_SaveRegisterState, NULL, "QSaveRegisterState", "Save the register state for the current thread and return a decimal save ID."));
+ t.push_back (Packet (restore_register_state, &RNBRemote::HandlePacket_RestoreRegisterState, NULL, "QRestoreRegisterState:", "Restore the register state given a save ID previously returned from a call to QSaveRegisterState."));
+ t.push_back (Packet (memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL, "qMemoryRegionInfo", "Return size and attributes of a memory region that contains the given address"));
+ t.push_back (Packet (get_profile_data, &RNBRemote::HandlePacket_GetProfileData, NULL, "qGetProfileData", "Return profiling data of the current target."));
+ t.push_back (Packet (set_enable_profiling, &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL, "QSetEnableAsyncProfiling", "Enable or disable the profiling of current target."));
+ t.push_back (Packet (enable_compression, &RNBRemote::HandlePacket_QEnableCompression, NULL, "QEnableCompression:", "Enable compression for the remainder of the connection"));
+ t.push_back (Packet (watchpoint_support_info, &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL, "qWatchpointSupportInfo", "Return the number of supported hardware watchpoints"));
+ t.push_back (Packet (set_process_event, &RNBRemote::HandlePacket_QSetProcessEvent, NULL, "QSetProcessEvent:", "Set a process event, to be passed to the process, can be set before the process is started, or after."));
+ t.push_back (Packet (set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError, NULL, "QSetDetachOnError:", "Set whether debugserver will detach (1) or kill (0) from the process it is controlling if it loses connection to lldb."));
+ t.push_back (Packet (speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:", "Test the maximum speed at which packet can be sent/received."));
+ t.push_back (Packet (query_transfer, &RNBRemote::HandlePacket_qXfer, NULL, "qXfer:", "Support the qXfer packet."));
+}
+
+
+void
+RNBRemote::FlushSTDIO ()
+{
+ if (m_ctx.HasValidProcessID())
+ {
+ nub_process_t pid = m_ctx.ProcessID();
+ char buf[256];
+ nub_size_t count;
+ do
+ {
+ count = DNBProcessGetAvailableSTDOUT(pid, buf, sizeof(buf));
+ if (count > 0)
+ {
+ SendSTDOUTPacket (buf, count);
+ }
+ } while (count > 0);
+
+ do
+ {
+ count = DNBProcessGetAvailableSTDERR(pid, buf, sizeof(buf));
+ if (count > 0)
+ {
+ SendSTDERRPacket (buf, count);
+ }
+ } while (count > 0);
+ }
+}
+
+void
+RNBRemote::SendAsyncProfileData ()
+{
+ if (m_ctx.HasValidProcessID())
+ {
+ nub_process_t pid = m_ctx.ProcessID();
+ char buf[1024];
+ nub_size_t count;
+ do
+ {
+ count = DNBProcessGetAvailableProfileData(pid, buf, sizeof(buf));
+ if (count > 0)
+ {
+ SendAsyncProfileDataPacket (buf, count);
+ }
+ } while (count > 0);
+ }
+}
+
+rnb_err_t
+RNBRemote::SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer)
+{
+ std::ostringstream packet_sstrm;
+ // Append the header cstr if there was one
+ if (header && header[0])
+ packet_sstrm << header;
+ nub_size_t i;
+ const uint8_t *ubuf8 = (const uint8_t *)buf;
+ for (i=0; i<buf_len; i++)
+ {
+ packet_sstrm << RAWHEX8(ubuf8[i]);
+ }
+ // Append the footer cstr if there was one
+ if (footer && footer[0])
+ packet_sstrm << footer;
+
+ return SendPacket(packet_sstrm.str());
+}
+
+rnb_err_t
+RNBRemote::SendSTDOUTPacket (char *buf, nub_size_t buf_size)
+{
+ if (buf_size == 0)
+ return rnb_success;
+ return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
+}
+
+rnb_err_t
+RNBRemote::SendSTDERRPacket (char *buf, nub_size_t buf_size)
+{
+ if (buf_size == 0)
+ return rnb_success;
+ return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
+}
+
+// This makes use of asynchronous bit 'A' in the gdb remote protocol.
+rnb_err_t
+RNBRemote::SendAsyncProfileDataPacket (char *buf, nub_size_t buf_size)
+{
+ if (buf_size == 0)
+ return rnb_success;
+
+ std::string packet("A");
+ packet.append(buf, buf_size);
+ return SendPacket(packet);
+}
+
+// Given a std::string packet contents to send, possibly encode/compress it.
+// If compression is enabled, the returned std::string will be in one of two
+// forms:
+//
+// N<original packet contents uncompressed>
+// C<size of original decompressed packet>:<packet compressed with the requested compression scheme>
+//
+// If compression is not requested, the original packet contents are returned
+
+std::string
+RNBRemote::CompressString (const std::string &orig)
+{
+ std::string compressed;
+ compression_types compression_type = GetCompressionType();
+ if (compression_type != compression_types::none)
+ {
+ bool compress_this_packet = false;
+
+ if (orig.size() > m_compression_minsize)
+ {
+ compress_this_packet = true;
+ }
+
+ if (compress_this_packet)
+ {
+ const size_t encoded_data_buf_size = orig.size() + 128;
+ std::vector<uint8_t> encoded_data (encoded_data_buf_size);
+ size_t compressed_size = 0;
+
+#if defined (HAVE_LIBCOMPRESSION)
+ if (compression_decode_buffer && compression_type == compression_types::lz4)
+ {
+ compressed_size = compression_encode_buffer (encoded_data.data(),
+ encoded_data_buf_size,
+ (uint8_t*) orig.c_str(),
+ orig.size(),
+ nullptr,
+ COMPRESSION_LZ4_RAW);
+ }
+ if (compression_decode_buffer && compression_type == compression_types::zlib_deflate)
+ {
+ compressed_size = compression_encode_buffer (encoded_data.data(),
+ encoded_data_buf_size,
+ (uint8_t*) orig.c_str(),
+ orig.size(),
+ nullptr,
+ COMPRESSION_ZLIB);
+ }
+ if (compression_decode_buffer && compression_type == compression_types::lzma)
+ {
+ compressed_size = compression_encode_buffer (encoded_data.data(),
+ encoded_data_buf_size,
+ (uint8_t*) orig.c_str(),
+ orig.size(),
+ nullptr,
+ COMPRESSION_LZMA);
+ }
+ if (compression_decode_buffer && compression_type == compression_types::lzfse)
+ {
+ compressed_size = compression_encode_buffer (encoded_data.data(),
+ encoded_data_buf_size,
+ (uint8_t*) orig.c_str(),
+ orig.size(),
+ nullptr,
+ COMPRESSION_LZFSE);
+ }
+#endif
+
+#if defined (HAVE_LIBZ)
+ if (compressed_size == 0 && compression_type == compression_types::zlib_deflate)
+ {
+ z_stream stream;
+ memset (&stream, 0, sizeof (z_stream));
+ stream.next_in = (Bytef *) orig.c_str();
+ stream.avail_in = (uInt) orig.size();
+ stream.next_out = (Bytef *) encoded_data.data();
+ stream.avail_out = (uInt) encoded_data_buf_size;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+ deflateInit2 (&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
+ int compress_status = deflate (&stream, Z_FINISH);
+ deflateEnd (&stream);
+ if (compress_status == Z_STREAM_END && stream.total_out > 0)
+ {
+ compressed_size = stream.total_out;
+ }
+ }
+#endif
+
+ if (compressed_size > 0)
+ {
+ compressed.clear ();
+ compressed.reserve (compressed_size);
+ compressed = "C";
+ char numbuf[16];
+ snprintf (numbuf, sizeof (numbuf), "%zu:", orig.size());
+ numbuf[sizeof (numbuf) - 1] = '\0';
+ compressed.append (numbuf);
+
+ for (size_t i = 0; i < compressed_size; i++)
+ {
+ uint8_t byte = encoded_data[i];
+ if (byte == '#' || byte == '$' || byte == '}' || byte == '*' || byte == '\0')
+ {
+ compressed.push_back (0x7d);
+ compressed.push_back (byte ^ 0x20);
+ }
+ else
+ {
+ compressed.push_back (byte);
+ }
+ }
+ }
+ else
+ {
+ compressed = "N" + orig;
+ }
+ }
+ else
+ {
+ compressed = "N" + orig;
+ }
+ }
+ else
+ {
+ compressed = orig;
+ }
+
+ return compressed;
+}
+
+rnb_err_t
+RNBRemote::SendPacket (const std::string &s)
+{
+ DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, s.c_str());
+
+ std::string s_compressed = CompressString (s);
+
+ std::string sendpacket = "$" + s_compressed + "#";
+ int cksum = 0;
+ char hexbuf[5];
+
+ if (m_noack_mode)
+ {
+ sendpacket += "00";
+ }
+ else
+ {
+ for (size_t i = 0; i != s_compressed.size(); ++i)
+ cksum += s_compressed[i];
+ snprintf (hexbuf, sizeof hexbuf, "%02x", cksum & 0xff);
+ sendpacket += hexbuf;
+ }
+
+ rnb_err_t err = m_comm.Write (sendpacket.c_str(), sendpacket.size());
+ if (err != rnb_success)
+ return err;
+
+ if (m_noack_mode)
+ return rnb_success;
+
+ std::string reply;
+ RNBRemote::Packet packet;
+ err = GetPacket (reply, packet, true);
+
+ if (err != rnb_success)
+ {
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s (%s) got error trying to get reply...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str());
+ return err;
+ }
+
+ DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s (%s) got reply: '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str(), reply.c_str());
+
+ if (packet.type == ack)
+ return rnb_success;
+
+ // Should we try to resend the packet at this layer?
+ // if (packet.command == nack)
+ return rnb_err;
+}
+
+/* Get a packet via gdb remote protocol.
+ Strip off the prefix/suffix, verify the checksum to make sure
+ a valid packet was received, send an ACK if they match. */
+
+rnb_err_t
+RNBRemote::GetPacketPayload (std::string &return_packet)
+{
+ //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+
+ PThreadMutex::Locker locker(m_mutex);
+ if (m_rx_packets.empty())
+ {
+ // Only reset the remote command available event if we have no more packets
+ m_ctx.Events().ResetEvents ( RNBContext::event_read_packet_available );
+ //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s error: no packets available...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ return rnb_err;
+ }
+
+ //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s has %u queued packets", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, m_rx_packets.size());
+ return_packet.swap(m_rx_packets.front());
+ m_rx_packets.pop_front();
+ locker.Reset(); // Release our lock on the mutex
+
+ if (m_rx_packets.empty())
+ {
+ // Reset the remote command available event if we have no more packets
+ m_ctx.Events().ResetEvents ( RNBContext::event_read_packet_available );
+ }
+
+ //DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s: '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+
+ switch (return_packet[0])
+ {
+ case '+':
+ case '-':
+ case '\x03':
+ break;
+
+ case '$':
+ {
+ long packet_checksum = 0;
+ if (!m_noack_mode)
+ {
+ for (size_t i = return_packet.size() - 2; i < return_packet.size(); ++i)
+ {
+ char checksum_char = tolower (return_packet[i]);
+ if (!isxdigit (checksum_char))
+ {
+ m_comm.Write ("-", 1);
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s error: packet with invalid checksum characters: %s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+ return rnb_err;
+ }
+ }
+ packet_checksum = strtol (&return_packet[return_packet.size() - 2], NULL, 16);
+ }
+
+ return_packet.erase(0,1); // Strip the leading '$'
+ return_packet.erase(return_packet.size() - 3);// Strip the #XX checksum
+
+ if (!m_noack_mode)
+ {
+ // Compute the checksum
+ int computed_checksum = 0;
+ for (std::string::iterator it = return_packet.begin ();
+ it != return_packet.end ();
+ ++it)
+ {
+ computed_checksum += *it;
+ }
+
+ if (packet_checksum == (computed_checksum & 0xff))
+ {
+ //DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+ m_comm.Write ("+", 1);
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s' (error: packet checksum mismatch (0x%2.2lx != 0x%2.2x))",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__,
+ return_packet.c_str(),
+ packet_checksum,
+ computed_checksum);
+ m_comm.Write ("-", 1);
+ return rnb_err;
+ }
+ }
+ }
+ break;
+
+ default:
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s tossing unexpected packet???? %s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str());
+ if (!m_noack_mode)
+ m_comm.Write ("-", 1);
+ return rnb_err;
+ }
+
+ return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_UNIMPLEMENTED (const char* p)
+{
+ DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s(\"%s\")", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p ? p : "NULL");
+ return SendPacket ("");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_ILLFORMED (const char *file, int line, const char *p, const char *description)
+{
+ DNBLogThreadedIf (LOG_RNB_PACKETS, "%8u %s:%i ILLFORMED: '%s' (%s)", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), file, line, __FUNCTION__, p);
+ return SendPacket ("E03");
+}
+
+rnb_err_t
+RNBRemote::GetPacket (std::string &packet_payload, RNBRemote::Packet& packet_info, bool wait)
+{
+ std::string payload;
+ rnb_err_t err = GetPacketPayload (payload);
+ if (err != rnb_success)
+ {
+ PThreadEvent& events = m_ctx.Events();
+ nub_event_t set_events = events.GetEventBits();
+ // TODO: add timeout version of GetPacket?? We would then need to pass
+ // that timeout value along to DNBProcessTimedWaitForEvent.
+ if (!wait || ((set_events & RNBContext::event_read_thread_running) == 0))
+ return err;
+
+ const nub_event_t events_to_wait_for = RNBContext::event_read_packet_available | RNBContext::event_read_thread_exiting;
+
+ while ((set_events = events.WaitForSetEvents(events_to_wait_for)) != 0)
+ {
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ // Try the queue again now that we got an event
+ err = GetPacketPayload (payload);
+ if (err == rnb_success)
+ break;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting)
+ err = rnb_not_connected;
+
+ if (err == rnb_not_connected)
+ return err;
+
+ } while (err == rnb_err);
+
+ if (set_events == 0)
+ err = rnb_not_connected;
+ }
+
+ if (err == rnb_success)
+ {
+ Packet::iterator it;
+ for (it = m_packets.begin (); it != m_packets.end (); ++it)
+ {
+ if (payload.compare (0, it->abbrev.size(), it->abbrev) == 0)
+ break;
+ }
+
+ // A packet we don't have an entry for. This can happen when we
+ // get a packet that we don't know about or support. We just reply
+ // accordingly and go on.
+ if (it == m_packets.end ())
+ {
+ DNBLogThreadedIf (LOG_RNB_PACKETS, "unimplemented packet: '%s'", payload.c_str());
+ HandlePacket_UNIMPLEMENTED(payload.c_str());
+ return rnb_err;
+ }
+ else
+ {
+ packet_info = *it;
+ packet_payload = payload;
+ }
+ }
+ return err;
+}
+
+rnb_err_t
+RNBRemote::HandleAsyncPacket(PacketEnum *type)
+{
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ static DNBTimer g_packetTimer(true);
+ rnb_err_t err = rnb_err;
+ std::string packet_data;
+ RNBRemote::Packet packet_info;
+ err = GetPacket (packet_data, packet_info, false);
+
+ if (err == rnb_success)
+ {
+ if (!packet_data.empty() && isprint(packet_data[0]))
+ DNBLogThreadedIf (LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (\"%s\");", packet_data.c_str());
+ else
+ DNBLogThreadedIf (LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (%s);", packet_info.printable_name.c_str());
+
+ HandlePacketCallback packet_callback = packet_info.async;
+ if (packet_callback != NULL)
+ {
+ if (type != NULL)
+ *type = packet_info.type;
+ return (this->*packet_callback)(packet_data.c_str());
+ }
+ }
+
+ return err;
+}
+
+rnb_err_t
+RNBRemote::HandleReceivedPacket(PacketEnum *type)
+{
+ static DNBTimer g_packetTimer(true);
+
+ // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ rnb_err_t err = rnb_err;
+ std::string packet_data;
+ RNBRemote::Packet packet_info;
+ err = GetPacket (packet_data, packet_info, false);
+
+ if (err == rnb_success)
+ {
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "HandleReceivedPacket (\"%s\");", packet_data.c_str());
+ HandlePacketCallback packet_callback = packet_info.normal;
+ if (packet_callback != NULL)
+ {
+ if (type != NULL)
+ *type = packet_info.type;
+ return (this->*packet_callback)(packet_data.c_str());
+ }
+ else
+ {
+ // Do not fall through to end of this function, if we have valid
+ // packet_info and it has a NULL callback, then we need to respect
+ // that it may not want any response or anything to be done.
+ return err;
+ }
+ }
+ return rnb_err;
+}
+
+void
+RNBRemote::CommDataReceived(const std::string& new_data)
+{
+ // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ {
+ // Put the packet data into the buffer in a thread safe fashion
+ PThreadMutex::Locker locker(m_mutex);
+
+ std::string data;
+ // See if we have any left over data from a previous call to this
+ // function?
+ if (!m_rx_partial_data.empty())
+ {
+ // We do, so lets start with that data
+ data.swap(m_rx_partial_data);
+ }
+ // Append the new incoming data
+ data += new_data;
+
+ // Parse up the packets into gdb remote packets
+ size_t idx = 0;
+ const size_t data_size = data.size();
+
+ while (idx < data_size)
+ {
+ // end_idx must be one past the last valid packet byte. Start
+ // it off with an invalid value that is the same as the current
+ // index.
+ size_t end_idx = idx;
+
+ switch (data[idx])
+ {
+ case '+': // Look for ack
+ case '-': // Look for cancel
+ case '\x03': // ^C to halt target
+ end_idx = idx + 1; // The command is one byte long...
+ break;
+
+ case '$':
+ // Look for a standard gdb packet?
+ end_idx = data.find('#', idx + 1);
+ if (end_idx == std::string::npos || end_idx + 3 > data_size)
+ {
+ end_idx = std::string::npos;
+ }
+ else
+ {
+ // Add two for the checksum bytes and 1 to point to the
+ // byte just past the end of this packet
+ end_idx += 3;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (end_idx == std::string::npos)
+ {
+ // Not all data may be here for the packet yet, save it for
+ // next time through this function.
+ m_rx_partial_data += data.substr(idx);
+ //DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s saving data for later[%u, npos): '%s'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, idx, m_rx_partial_data.c_str());
+ idx = end_idx;
+ }
+ else
+ if (idx < end_idx)
+ {
+ m_packets_recvd++;
+ // Hack to get rid of initial '+' ACK???
+ if (m_packets_recvd == 1 && (end_idx == idx + 1) && data[idx] == '+')
+ {
+ //DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s throwing first ACK away....[%u, npos): '+'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, idx);
+ }
+ else
+ {
+ // We have a valid packet...
+ m_rx_packets.push_back(data.substr(idx, end_idx - idx));
+ DNBLogThreadedIf (LOG_RNB_PACKETS, "getpkt: %s", m_rx_packets.back().c_str());
+ }
+ idx = end_idx;
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s tossing junk byte at %c",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, data[idx]);
+ idx = idx + 1;
+ }
+ }
+ }
+
+ if (!m_rx_packets.empty())
+ {
+ // Let the main thread know we have received a packet
+
+ //DNBLogThreadedIf (LOG_RNB_EVENTS, "%8d RNBRemote::%s called events.SetEvent(RNBContext::event_read_packet_available)", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ PThreadEvent& events = m_ctx.Events();
+ events.SetEvents (RNBContext::event_read_packet_available);
+ }
+}
+
+rnb_err_t
+RNBRemote::GetCommData ()
+{
+ // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ std::string comm_data;
+ rnb_err_t err = m_comm.Read (comm_data);
+ if (err == rnb_success)
+ {
+ if (!comm_data.empty())
+ CommDataReceived (comm_data);
+ }
+ return err;
+}
+
+void
+RNBRemote::StartReadRemoteDataThread()
+{
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ PThreadEvent& events = m_ctx.Events();
+ if ((events.GetEventBits() & RNBContext::event_read_thread_running) == 0)
+ {
+ events.ResetEvents (RNBContext::event_read_thread_exiting);
+ int err = ::pthread_create (&m_rx_pthread, NULL, ThreadFunctionReadRemoteData, this);
+ if (err == 0)
+ {
+ // Our thread was successfully kicked off, wait for it to
+ // set the started event so we can safely continue
+ events.WaitForSetEvents (RNBContext::event_read_thread_running);
+ }
+ else
+ {
+ events.ResetEvents (RNBContext::event_read_thread_running);
+ events.SetEvents (RNBContext::event_read_thread_exiting);
+ }
+ }
+}
+
+void
+RNBRemote::StopReadRemoteDataThread()
+{
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ PThreadEvent& events = m_ctx.Events();
+ if ((events.GetEventBits() & RNBContext::event_read_thread_running) == RNBContext::event_read_thread_running)
+ {
+ m_comm.Disconnect(true);
+ struct timespec timeout_abstime;
+ DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
+
+ // Wait for 2 seconds for the remote data thread to exit
+ if (events.WaitForSetEvents(RNBContext::event_read_thread_exiting, &timeout_abstime) == 0)
+ {
+ // Kill the remote data thread???
+ }
+ }
+}
+
+
+void*
+RNBRemote::ThreadFunctionReadRemoteData(void *arg)
+{
+ // Keep a shared pointer reference so this doesn't go away on us before the thread is killed.
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread starting...", __FUNCTION__, arg);
+ RNBRemoteSP remoteSP(g_remoteSP);
+ if (remoteSP.get() != NULL)
+ {
+
+#if defined (__APPLE__)
+ pthread_setname_np ("read gdb-remote packets thread");
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0)
+ {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ RNBRemote* remote = remoteSP.get();
+ PThreadEvent& events = remote->Context().Events();
+ events.SetEvents (RNBContext::event_read_thread_running);
+ // START: main receive remote command thread loop
+ bool done = false;
+ while (!done)
+ {
+ rnb_err_t err = remote->GetCommData();
+
+ switch (err)
+ {
+ case rnb_success:
+ break;
+
+ default:
+ case rnb_err:
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "RNBSocket::GetCommData returned error %u", err);
+ done = true;
+ break;
+
+ case rnb_not_connected:
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "RNBSocket::GetCommData returned not connected...");
+ done = true;
+ break;
+ }
+ }
+ // START: main receive remote command thread loop
+ events.ResetEvents (RNBContext::event_read_thread_running);
+ events.SetEvents (RNBContext::event_read_thread_exiting);
+ }
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread exiting...", __FUNCTION__, arg);
+ return NULL;
+}
+
+
+// If we fail to get back a valid CPU type for the remote process,
+// make a best guess for the CPU type based on the currently running
+// debugserver binary -- the debugger may not handle the case of an
+// un-specified process CPU type correctly.
+
+static cpu_type_t
+best_guess_cpu_type ()
+{
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ if (sizeof (char *) == 8)
+ {
+ return CPU_TYPE_ARM64;
+ }
+ else
+ {
+ return CPU_TYPE_ARM;
+ }
+#elif defined (__i386__) || defined (__x86_64__)
+ if (sizeof (char*) == 8)
+ {
+ return CPU_TYPE_X86_64;
+ }
+ else
+ {
+ return CPU_TYPE_I386;
+ }
+#endif
+ return 0;
+}
+
+
+/* Read the bytes in STR which are GDB Remote Protocol binary encoded bytes
+ (8-bit bytes).
+ This encoding uses 0x7d ('}') as an escape character for
+ 0x7d ('}'), 0x23 ('#'), 0x24 ('$'), 0x2a ('*').
+ LEN is the number of bytes to be processed. If a character is escaped,
+ it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte
+ (end of string). */
+
+std::vector<uint8_t>
+decode_binary_data (const char *str, size_t len)
+{
+ std::vector<uint8_t> bytes;
+ if (len == 0)
+ {
+ return bytes;
+ }
+ if (len == (size_t)-1)
+ len = strlen (str);
+
+ while (len--)
+ {
+ unsigned char c = *str;
+ if (c == 0x7d && len > 0)
+ {
+ len--;
+ str++;
+ c = *str ^ 0x20;
+ }
+ bytes.push_back (c);
+ }
+ return bytes;
+}
+
+// Quote any meta characters in a std::string as per the binary
+// packet convention in the gdb-remote protocol.
+
+std::string
+binary_encode_string (const std::string &s)
+{
+ std::string output;
+ const size_t s_size = s.size();
+ const char *s_chars = s.c_str();
+
+ for (size_t i = 0; i < s_size; i++)
+ {
+ unsigned char ch = *(s_chars + i);
+ if (ch == '#' || ch == '$' || ch == '}' || ch == '*')
+ {
+ output.push_back ('}'); // 0x7d
+ output.push_back (ch ^ 0x20);
+ }
+ else
+ {
+ output.push_back (ch);
+ }
+ }
+ return output;
+}
+
+// If the value side of a key-value pair in JSON is a string,
+// and that string has a " character in it, the " character must
+// be escaped.
+
+std::string
+json_string_quote_metachars (const std::string &s)
+{
+ if (s.find('"') == std::string::npos)
+ return s;
+
+ std::string output;
+ const size_t s_size = s.size();
+ const char *s_chars = s.c_str();
+ for (size_t i = 0; i < s_size; i++)
+ {
+ unsigned char ch = *(s_chars + i);
+ if (ch == '"')
+ {
+ output.push_back ('\\');
+ }
+ output.push_back (ch);
+ }
+ return output;
+}
+
+typedef struct register_map_entry
+{
+ uint32_t debugserver_regnum; // debugserver register number
+ uint32_t offset; // Offset in bytes into the register context data with no padding between register values
+ DNBRegisterInfo nub_info; // debugnub register info
+ std::vector<uint32_t> value_regnums;
+ std::vector<uint32_t> invalidate_regnums;
+} register_map_entry_t;
+
+
+
+// If the notion of registers differs from what is handed out by the
+// architecture, then flavors can be defined here.
+
+static std::vector<register_map_entry_t> g_dynamic_register_map;
+static register_map_entry_t *g_reg_entries = NULL;
+static size_t g_num_reg_entries = 0;
+
+void
+RNBRemote::Initialize()
+{
+ DNBInitialize();
+}
+
+
+bool
+RNBRemote::InitializeRegisters (bool force)
+{
+ pid_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return false;
+
+ DNBLogThreadedIf (LOG_RNB_PROC, "RNBRemote::%s() getting native registers from DNB interface", __FUNCTION__);
+ // Discover the registers by querying the DNB interface and letting it
+ // state the registers that it would like to export. This allows the
+ // registers to be discovered using multiple qRegisterInfo calls to get
+ // all register information after the architecture for the process is
+ // determined.
+ if (force)
+ {
+ g_dynamic_register_map.clear();
+ g_reg_entries = NULL;
+ g_num_reg_entries = 0;
+ }
+
+ if (g_dynamic_register_map.empty())
+ {
+ nub_size_t num_reg_sets = 0;
+ const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo (&num_reg_sets);
+
+ assert (num_reg_sets > 0 && reg_sets != NULL);
+
+ uint32_t regnum = 0;
+ uint32_t reg_data_offset = 0;
+ typedef std::map<std::string, uint32_t> NameToRegNum;
+ NameToRegNum name_to_regnum;
+ for (nub_size_t set = 0; set < num_reg_sets; ++set)
+ {
+ if (reg_sets[set].registers == NULL)
+ continue;
+
+ for (uint32_t reg=0; reg < reg_sets[set].num_registers; ++reg)
+ {
+ register_map_entry_t reg_entry = {
+ regnum++, // register number starts at zero and goes up with no gaps
+ reg_data_offset, // Offset into register context data, no gaps between registers
+ reg_sets[set].registers[reg] // DNBRegisterInfo
+ };
+
+ name_to_regnum[reg_entry.nub_info.name] = reg_entry.debugserver_regnum;
+
+ if (reg_entry.nub_info.value_regs == NULL)
+ {
+ reg_data_offset += reg_entry.nub_info.size;
+ }
+
+ g_dynamic_register_map.push_back (reg_entry);
+ }
+ }
+
+ // Now we must find any registers whose values are in other registers and fix up
+ // the offsets since we removed all gaps...
+ for (auto &reg_entry: g_dynamic_register_map)
+ {
+ if (reg_entry.nub_info.value_regs)
+ {
+ uint32_t new_offset = UINT32_MAX;
+ for (size_t i=0; reg_entry.nub_info.value_regs[i] != NULL; ++i)
+ {
+ const char *name = reg_entry.nub_info.value_regs[i];
+ auto pos = name_to_regnum.find(name);
+ if (pos != name_to_regnum.end())
+ {
+ regnum = pos->second;
+ reg_entry.value_regnums.push_back(regnum);
+ if (regnum < g_dynamic_register_map.size())
+ {
+ // The offset for value_regs registers is the offset within the register with the lowest offset
+ const uint32_t reg_offset = g_dynamic_register_map[regnum].offset + reg_entry.nub_info.offset;
+ if (new_offset > reg_offset)
+ new_offset = reg_offset;
+ }
+ }
+ }
+
+ if (new_offset != UINT32_MAX)
+ {
+ reg_entry.offset = new_offset;
+ }
+ else
+ {
+ DNBLogThreaded("no offset was calculated entry for register %s", reg_entry.nub_info.name);
+ reg_entry.offset = UINT32_MAX;
+ }
+ }
+
+ if (reg_entry.nub_info.update_regs)
+ {
+ for (size_t i=0; reg_entry.nub_info.update_regs[i] != NULL; ++i)
+ {
+ const char *name = reg_entry.nub_info.update_regs[i];
+ auto pos = name_to_regnum.find(name);
+ if (pos != name_to_regnum.end())
+ {
+ regnum = pos->second;
+ reg_entry.invalidate_regnums.push_back(regnum);
+ }
+ }
+ }
+ }
+
+
+// for (auto &reg_entry: g_dynamic_register_map)
+// {
+// DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s",
+// reg_entry.offset,
+// reg_entry.nub_info.size,
+// reg_entry.nub_info.value_regs != NULL,
+// reg_entry.nub_info.name);
+// }
+
+ g_reg_entries = g_dynamic_register_map.data();
+ g_num_reg_entries = g_dynamic_register_map.size();
+ }
+ return true;
+}
+
+/* The inferior has stopped executing; send a packet
+ to gdb to let it know. */
+
+void
+RNBRemote::NotifyThatProcessStopped (void)
+{
+ RNBRemote::HandlePacket_last_signal (NULL);
+ return;
+}
+
+
+/* 'A arglen,argnum,arg,...'
+ Update the inferior context CTX with the program name and arg
+ list.
+ The documentation for this packet is underwhelming but my best reading
+ of this is that it is a series of (len, position #, arg)'s, one for
+ each argument with "arg" hex encoded (two 0-9a-f chars?).
+ Why we need BOTH a "len" and a hex encoded "arg" is beyond me - either
+ is sufficient to get around the "," position separator escape issue.
+
+ e.g. our best guess for a valid 'A' packet for "gdb -q a.out" is
+
+ 6,0,676462,4,1,2d71,10,2,612e6f7574
+
+ Note that "argnum" and "arglen" are numbers in base 10. Again, that's
+ not documented either way but I'm assuming it's so. */
+
+rnb_err_t
+RNBRemote::HandlePacket_A (const char *p)
+{
+ if (p == NULL || *p == '\0')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Null packet for 'A' pkt");
+ }
+ p++;
+ if (*p == '\0' || !isdigit (*p))
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "arglen not specified on 'A' pkt");
+ }
+
+ /* I promise I don't modify it anywhere in this function. strtoul()'s
+ 2nd arg has to be non-const which makes it problematic to step
+ through the string easily. */
+ char *buf = const_cast<char *>(p);
+
+ RNBContext& ctx = Context();
+
+ while (*buf != '\0')
+ {
+ unsigned long arglen, argnum;
+ std::string arg;
+ char *c;
+
+ errno = 0;
+ arglen = strtoul (buf, &c, 10);
+ if (errno != 0 && arglen == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "arglen not a number on 'A' pkt");
+ }
+ if (*c != ',')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "arglen not followed by comma on 'A' pkt");
+ }
+ buf = c + 1;
+
+ errno = 0;
+ argnum = strtoul (buf, &c, 10);
+ if (errno != 0 && argnum == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "argnum not a number on 'A' pkt");
+ }
+ if (*c != ',')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "arglen not followed by comma on 'A' pkt");
+ }
+ buf = c + 1;
+
+ c = buf;
+ buf = buf + arglen;
+ while (c < buf && *c != '\0' && c + 1 < buf && *(c + 1) != '\0')
+ {
+ char smallbuf[3];
+ smallbuf[0] = *c;
+ smallbuf[1] = *(c + 1);
+ smallbuf[2] = '\0';
+
+ errno = 0;
+ int ch = static_cast<int>(strtoul (smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'A' pkt");
+ }
+
+ arg.push_back(ch);
+ c += 2;
+ }
+
+ ctx.PushArgument (arg.c_str());
+ if (*buf == ',')
+ buf++;
+ }
+ SendPacket ("OK");
+
+ return rnb_success;
+}
+
+/* 'H c t'
+ Set the thread for subsequent actions; 'c' for step/continue ops,
+ 'g' for other ops. -1 means all threads, 0 means any thread. */
+
+rnb_err_t
+RNBRemote::HandlePacket_H (const char *p)
+{
+ p++; // skip 'H'
+ if (*p != 'c' && *p != 'g')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Missing 'c' or 'g' type in H packet");
+ }
+
+ if (!m_ctx.HasValidProcessID())
+ {
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ }
+
+ errno = 0;
+ nub_thread_t tid = strtoul (p + 1, NULL, 16);
+ if (errno != 0 && tid == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid thread number in H packet");
+ }
+ if (*p == 'c')
+ SetContinueThread (tid);
+ if (*p == 'g')
+ SetCurrentThread (tid);
+
+ return SendPacket ("OK");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_qLaunchSuccess (const char *p)
+{
+ if (m_ctx.HasValidProcessID() || m_ctx.LaunchStatus().Error() == 0)
+ return SendPacket("OK");
+ std::ostringstream ret_str;
+ std::string status_str;
+ ret_str << "E" << m_ctx.LaunchStatusAsString(status_str);
+
+ return SendPacket (ret_str.str());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qShlibInfoAddr (const char *p)
+{
+ if (m_ctx.HasValidProcessID())
+ {
+ nub_addr_t shlib_info_addr = DNBProcessGetSharedLibraryInfoAddress(m_ctx.ProcessID());
+ if (shlib_info_addr != INVALID_NUB_ADDRESS)
+ {
+ std::ostringstream ostrm;
+ ostrm << RAW_HEXBASE << shlib_info_addr;
+ return SendPacket (ostrm.str ());
+ }
+ }
+ return SendPacket ("E44");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qStepPacketSupported (const char *p)
+{
+ // Normally the "s" packet is mandatory, yet in gdb when using ARM, they
+ // get around the need for this packet by implementing software single
+ // stepping from gdb. Current versions of debugserver do support the "s"
+ // packet, yet some older versions do not. We need a way to tell if this
+ // packet is supported so we can disable software single stepping in gdb
+ // for remote targets (so the "s" packet will get used).
+ return SendPacket("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qSyncThreadStateSupported (const char *p)
+{
+ // We support attachOrWait meaning attach if the process exists, otherwise wait to attach.
+ return SendPacket("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qVAttachOrWaitSupported (const char *p)
+{
+ // We support attachOrWait meaning attach if the process exists, otherwise wait to attach.
+ return SendPacket("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qThreadStopInfo (const char *p)
+{
+ p += strlen ("qThreadStopInfo");
+ nub_thread_t tid = strtoul(p, 0, 16);
+ return SendStopReplyPacketForThread (tid);
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qThreadInfo (const char *p)
+{
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("OK");
+
+ // Only "qfThreadInfo" and "qsThreadInfo" get into this function so
+ // we only need to check the second byte to tell which is which
+ if (p[1] == 'f')
+ {
+ nub_size_t numthreads = DNBProcessGetNumThreads (pid);
+ std::ostringstream ostrm;
+ ostrm << "m";
+ bool first = true;
+ for (nub_size_t i = 0; i < numthreads; ++i)
+ {
+ if (first)
+ first = false;
+ else
+ ostrm << ",";
+ nub_thread_t th = DNBProcessGetThreadAtIndex (pid, i);
+ ostrm << std::hex << th;
+ }
+ return SendPacket (ostrm.str ());
+ }
+ else
+ {
+ return SendPacket ("l");
+ }
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qThreadExtraInfo (const char *p)
+{
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("OK");
+
+ /* This is supposed to return a string like 'Runnable' or
+ 'Blocked on Mutex'.
+ The returned string is formatted like the "A" packet - a
+ sequence of letters encoded in as 2-hex-chars-per-letter. */
+ p += strlen ("qThreadExtraInfo");
+ if (*p++ != ',')
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Illformed qThreadExtraInfo packet");
+ errno = 0;
+ nub_thread_t tid = strtoul (p, NULL, 16);
+ if (errno != 0 && tid == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid thread number in qThreadExtraInfo packet");
+ }
+
+ const char * threadInfo = DNBThreadGetInfo(pid, tid);
+ if (threadInfo != NULL && threadInfo[0])
+ {
+ return SendHexEncodedBytePacket(NULL, threadInfo, strlen(threadInfo), NULL);
+ }
+ else
+ {
+ // "OK" == 4f6b
+ // Return "OK" as a ASCII hex byte stream if things go wrong
+ return SendPacket ("4f6b");
+ }
+
+ return SendPacket ("");
+}
+
+
+const char *k_space_delimiters = " \t";
+static void
+skip_spaces (std::string &line)
+{
+ if (!line.empty())
+ {
+ size_t space_pos = line.find_first_not_of (k_space_delimiters);
+ if (space_pos > 0)
+ line.erase(0, space_pos);
+ }
+}
+
+static std::string
+get_identifier (std::string &line)
+{
+ std::string word;
+ skip_spaces (line);
+ const size_t line_size = line.size();
+ size_t end_pos;
+ for (end_pos = 0; end_pos < line_size; ++end_pos)
+ {
+ if (end_pos == 0)
+ {
+ if (isalpha(line[end_pos]) || line[end_pos] == '_')
+ continue;
+ }
+ else if (isalnum(line[end_pos]) || line[end_pos] == '_')
+ continue;
+ break;
+ }
+ word.assign (line, 0, end_pos);
+ line.erase(0, end_pos);
+ return word;
+}
+
+static std::string
+get_operator (std::string &line)
+{
+ std::string op;
+ skip_spaces (line);
+ if (!line.empty())
+ {
+ if (line[0] == '=')
+ {
+ op = '=';
+ line.erase(0,1);
+ }
+ }
+ return op;
+}
+
+static std::string
+get_value (std::string &line)
+{
+ std::string value;
+ skip_spaces (line);
+ if (!line.empty())
+ {
+ value.swap(line);
+ }
+ return value;
+}
+
+extern void FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args);
+extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args);
+
+rnb_err_t
+RNBRemote::HandlePacket_qRcmd (const char *p)
+{
+ const char *c = p + strlen("qRcmd,");
+ std::string line;
+ while (c[0] && c[1])
+ {
+ char smallbuf[3] = { c[0], c[1], '\0' };
+ errno = 0;
+ int ch = static_cast<int>(strtoul (smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in payload of qRcmd packet");
+ line.push_back(ch);
+ c += 2;
+ }
+ if (*c == '\0')
+ {
+ std::string command = get_identifier(line);
+ if (command.compare("set") == 0)
+ {
+ std::string variable = get_identifier (line);
+ std::string op = get_operator (line);
+ std::string value = get_value (line);
+ if (variable.compare("logfile") == 0)
+ {
+ FILE *log_file = fopen(value.c_str(), "w");
+ if (log_file)
+ {
+ DNBLogSetLogCallback(FileLogCallback, log_file);
+ return SendPacket ("OK");
+ }
+ return SendPacket ("E71");
+ }
+ else if (variable.compare("logmask") == 0)
+ {
+ char *end;
+ errno = 0;
+ uint32_t logmask = static_cast<uint32_t>(strtoul (value.c_str(), &end, 0));
+ if (errno == 0 && end && *end == '\0')
+ {
+ DNBLogSetLogMask (logmask);
+ if (!DNBLogGetLogCallback())
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+ return SendPacket ("OK");
+ }
+ errno = 0;
+ logmask = static_cast<uint32_t>(strtoul (value.c_str(), &end, 16));
+ if (errno == 0 && end && *end == '\0')
+ {
+ DNBLogSetLogMask (logmask);
+ return SendPacket ("OK");
+ }
+ return SendPacket ("E72");
+ }
+ return SendPacket ("E70");
+ }
+ return SendPacket ("E69");
+ }
+ return SendPacket ("E73");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qC (const char *p)
+{
+ nub_thread_t tid;
+ std::ostringstream rep;
+ // If we haven't run the process yet, we tell the debugger the
+ // pid is 0. That way it can know to tell use to run later on.
+ if (!m_ctx.HasValidProcessID())
+ tid = 0;
+ else
+ {
+ // Grab the current thread.
+ tid = DNBProcessGetCurrentThread (m_ctx.ProcessID());
+ // Make sure we set the current thread so g and p packets return
+ // the data the gdb will expect.
+ SetCurrentThread (tid);
+ }
+ rep << "QC" << std::hex << tid;
+ return SendPacket (rep.str());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qEcho (const char *p)
+{
+ // Just send the exact same packet back that we received to
+ // synchronize the response packets after a previous packet
+ // timed out. This allows the debugger to get back on track
+ // with responses after a packet timeout.
+ return SendPacket (p);
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qGetPid (const char *p)
+{
+ nub_process_t pid;
+ std::ostringstream rep;
+ // If we haven't run the process yet, we tell the debugger the
+ // pid is 0. That way it can know to tell use to run later on.
+ if (m_ctx.HasValidProcessID())
+ pid = m_ctx.ProcessID();
+ else
+ pid = 0;
+ rep << std::hex << pid;
+ return SendPacket (rep.str());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qRegisterInfo (const char *p)
+{
+ if (g_num_reg_entries == 0)
+ InitializeRegisters ();
+
+ p += strlen ("qRegisterInfo");
+
+ nub_size_t num_reg_sets = 0;
+ const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo (&num_reg_sets);
+ uint32_t reg_num = static_cast<uint32_t>(strtoul(p, 0, 16));
+
+ if (reg_num < g_num_reg_entries)
+ {
+ const register_map_entry_t *reg_entry = &g_reg_entries[reg_num];
+ std::ostringstream ostrm;
+ if (reg_entry->nub_info.name)
+ ostrm << "name:" << reg_entry->nub_info.name << ';';
+ if (reg_entry->nub_info.alt)
+ ostrm << "alt-name:" << reg_entry->nub_info.alt << ';';
+
+ ostrm << "bitsize:" << std::dec << reg_entry->nub_info.size * 8 << ';';
+ ostrm << "offset:" << std::dec << reg_entry->offset << ';';
+
+ switch (reg_entry->nub_info.type)
+ {
+ case Uint: ostrm << "encoding:uint;"; break;
+ case Sint: ostrm << "encoding:sint;"; break;
+ case IEEE754: ostrm << "encoding:ieee754;"; break;
+ case Vector: ostrm << "encoding:vector;"; break;
+ }
+
+ switch (reg_entry->nub_info.format)
+ {
+ case Binary: ostrm << "format:binary;"; break;
+ case Decimal: ostrm << "format:decimal;"; break;
+ case Hex: ostrm << "format:hex;"; break;
+ case Float: ostrm << "format:float;"; break;
+ case VectorOfSInt8: ostrm << "format:vector-sint8;"; break;
+ case VectorOfUInt8: ostrm << "format:vector-uint8;"; break;
+ case VectorOfSInt16: ostrm << "format:vector-sint16;"; break;
+ case VectorOfUInt16: ostrm << "format:vector-uint16;"; break;
+ case VectorOfSInt32: ostrm << "format:vector-sint32;"; break;
+ case VectorOfUInt32: ostrm << "format:vector-uint32;"; break;
+ case VectorOfFloat32: ostrm << "format:vector-float32;"; break;
+ case VectorOfUInt128: ostrm << "format:vector-uint128;"; break;
+ };
+
+ if (reg_set_info && reg_entry->nub_info.set < num_reg_sets)
+ ostrm << "set:" << reg_set_info[reg_entry->nub_info.set].name << ';';
+
+ if (reg_entry->nub_info.reg_ehframe != INVALID_NUB_REGNUM)
+ ostrm << "ehframe:" << std::dec << reg_entry->nub_info.reg_ehframe << ';';
+
+ if (reg_entry->nub_info.reg_dwarf != INVALID_NUB_REGNUM)
+ ostrm << "dwarf:" << std::dec << reg_entry->nub_info.reg_dwarf << ';';
+
+ switch (reg_entry->nub_info.reg_generic)
+ {
+ case GENERIC_REGNUM_FP: ostrm << "generic:fp;"; break;
+ case GENERIC_REGNUM_PC: ostrm << "generic:pc;"; break;
+ case GENERIC_REGNUM_SP: ostrm << "generic:sp;"; break;
+ case GENERIC_REGNUM_RA: ostrm << "generic:ra;"; break;
+ case GENERIC_REGNUM_FLAGS: ostrm << "generic:flags;"; break;
+ case GENERIC_REGNUM_ARG1: ostrm << "generic:arg1;"; break;
+ case GENERIC_REGNUM_ARG2: ostrm << "generic:arg2;"; break;
+ case GENERIC_REGNUM_ARG3: ostrm << "generic:arg3;"; break;
+ case GENERIC_REGNUM_ARG4: ostrm << "generic:arg4;"; break;
+ case GENERIC_REGNUM_ARG5: ostrm << "generic:arg5;"; break;
+ case GENERIC_REGNUM_ARG6: ostrm << "generic:arg6;"; break;
+ case GENERIC_REGNUM_ARG7: ostrm << "generic:arg7;"; break;
+ case GENERIC_REGNUM_ARG8: ostrm << "generic:arg8;"; break;
+ default: break;
+ }
+
+ if (!reg_entry->value_regnums.empty())
+ {
+ ostrm << "container-regs:";
+ for (size_t i=0, n=reg_entry->value_regnums.size(); i < n; ++i)
+ {
+ if (i > 0)
+ ostrm << ',';
+ ostrm << RAW_HEXBASE << reg_entry->value_regnums[i];
+ }
+ ostrm << ';';
+ }
+
+ if (!reg_entry->invalidate_regnums.empty())
+ {
+ ostrm << "invalidate-regs:";
+ for (size_t i=0, n=reg_entry->invalidate_regnums.size(); i < n; ++i)
+ {
+ if (i > 0)
+ ostrm << ',';
+ ostrm << RAW_HEXBASE << reg_entry->invalidate_regnums[i];
+ }
+ ostrm << ';';
+ }
+
+ return SendPacket (ostrm.str ());
+ }
+ return SendPacket ("E45");
+}
+
+
+/* This expects a packet formatted like
+
+ QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE;
+
+ with the "QSetLogging:" already removed from the start. Maybe in the
+ future this packet will include other keyvalue pairs like
+
+ QSetLogging:bitmask=LOG_ALL;mode=asl;
+ */
+
+rnb_err_t
+set_logging (const char *p)
+{
+ int bitmask = 0;
+ while (p && *p != '\0')
+ {
+ if (strncmp (p, "bitmask=", sizeof ("bitmask=") - 1) == 0)
+ {
+ p += sizeof ("bitmask=") - 1;
+ while (p && *p != '\0' && *p != ';')
+ {
+ if (*p == '|')
+ p++;
+
+// to regenerate the LOG_ entries (not including the LOG_RNB entries)
+// $ for logname in `grep '^#define LOG_' DNBDefs.h | egrep -v 'LOG_HI|LOG_LO' | awk '{print $2}'`
+// do
+// echo " else if (strncmp (p, \"$logname\", sizeof (\"$logname\") - 1) == 0)"
+// echo " {"
+// echo " p += sizeof (\"$logname\") - 1;"
+// echo " bitmask |= $logname;"
+// echo " }"
+// done
+ if (strncmp (p, "LOG_VERBOSE", sizeof ("LOG_VERBOSE") - 1) == 0)
+ {
+ p += sizeof ("LOG_VERBOSE") - 1;
+ bitmask |= LOG_VERBOSE;
+ }
+ else if (strncmp (p, "LOG_PROCESS", sizeof ("LOG_PROCESS") - 1) == 0)
+ {
+ p += sizeof ("LOG_PROCESS") - 1;
+ bitmask |= LOG_PROCESS;
+ }
+ else if (strncmp (p, "LOG_THREAD", sizeof ("LOG_THREAD") - 1) == 0)
+ {
+ p += sizeof ("LOG_THREAD") - 1;
+ bitmask |= LOG_THREAD;
+ }
+ else if (strncmp (p, "LOG_EXCEPTIONS", sizeof ("LOG_EXCEPTIONS") - 1) == 0)
+ {
+ p += sizeof ("LOG_EXCEPTIONS") - 1;
+ bitmask |= LOG_EXCEPTIONS;
+ }
+ else if (strncmp (p, "LOG_SHLIB", sizeof ("LOG_SHLIB") - 1) == 0)
+ {
+ p += sizeof ("LOG_SHLIB") - 1;
+ bitmask |= LOG_SHLIB;
+ }
+ else if (strncmp (p, "LOG_MEMORY", sizeof ("LOG_MEMORY") - 1) == 0)
+ {
+ p += sizeof ("LOG_MEMORY") - 1;
+ bitmask |= LOG_MEMORY;
+ }
+ else if (strncmp (p, "LOG_MEMORY_DATA_SHORT", sizeof ("LOG_MEMORY_DATA_SHORT") - 1) == 0)
+ {
+ p += sizeof ("LOG_MEMORY_DATA_SHORT") - 1;
+ bitmask |= LOG_MEMORY_DATA_SHORT;
+ }
+ else if (strncmp (p, "LOG_MEMORY_DATA_LONG", sizeof ("LOG_MEMORY_DATA_LONG") - 1) == 0)
+ {
+ p += sizeof ("LOG_MEMORY_DATA_LONG") - 1;
+ bitmask |= LOG_MEMORY_DATA_LONG;
+ }
+ else if (strncmp (p, "LOG_MEMORY_PROTECTIONS", sizeof ("LOG_MEMORY_PROTECTIONS") - 1) == 0)
+ {
+ p += sizeof ("LOG_MEMORY_PROTECTIONS") - 1;
+ bitmask |= LOG_MEMORY_PROTECTIONS;
+ }
+ else if (strncmp (p, "LOG_BREAKPOINTS", sizeof ("LOG_BREAKPOINTS") - 1) == 0)
+ {
+ p += sizeof ("LOG_BREAKPOINTS") - 1;
+ bitmask |= LOG_BREAKPOINTS;
+ }
+ else if (strncmp (p, "LOG_EVENTS", sizeof ("LOG_EVENTS") - 1) == 0)
+ {
+ p += sizeof ("LOG_EVENTS") - 1;
+ bitmask |= LOG_EVENTS;
+ }
+ else if (strncmp (p, "LOG_WATCHPOINTS", sizeof ("LOG_WATCHPOINTS") - 1) == 0)
+ {
+ p += sizeof ("LOG_WATCHPOINTS") - 1;
+ bitmask |= LOG_WATCHPOINTS;
+ }
+ else if (strncmp (p, "LOG_STEP", sizeof ("LOG_STEP") - 1) == 0)
+ {
+ p += sizeof ("LOG_STEP") - 1;
+ bitmask |= LOG_STEP;
+ }
+ else if (strncmp (p, "LOG_TASK", sizeof ("LOG_TASK") - 1) == 0)
+ {
+ p += sizeof ("LOG_TASK") - 1;
+ bitmask |= LOG_TASK;
+ }
+ else if (strncmp (p, "LOG_ALL", sizeof ("LOG_ALL") - 1) == 0)
+ {
+ p += sizeof ("LOG_ALL") - 1;
+ bitmask |= LOG_ALL;
+ }
+ else if (strncmp (p, "LOG_DEFAULT", sizeof ("LOG_DEFAULT") - 1) == 0)
+ {
+ p += sizeof ("LOG_DEFAULT") - 1;
+ bitmask |= LOG_DEFAULT;
+ }
+// end of auto-generated entries
+
+ else if (strncmp (p, "LOG_NONE", sizeof ("LOG_NONE") - 1) == 0)
+ {
+ p += sizeof ("LOG_NONE") - 1;
+ bitmask = 0;
+ }
+ else if (strncmp (p, "LOG_RNB_MINIMAL", sizeof ("LOG_RNB_MINIMAL") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_MINIMAL") - 1;
+ bitmask |= LOG_RNB_MINIMAL;
+ }
+ else if (strncmp (p, "LOG_RNB_MEDIUM", sizeof ("LOG_RNB_MEDIUM") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_MEDIUM") - 1;
+ bitmask |= LOG_RNB_MEDIUM;
+ }
+ else if (strncmp (p, "LOG_RNB_MAX", sizeof ("LOG_RNB_MAX") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_MAX") - 1;
+ bitmask |= LOG_RNB_MAX;
+ }
+ else if (strncmp (p, "LOG_RNB_COMM", sizeof ("LOG_RNB_COMM") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_COMM") - 1;
+ bitmask |= LOG_RNB_COMM;
+ }
+ else if (strncmp (p, "LOG_RNB_REMOTE", sizeof ("LOG_RNB_REMOTE") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_REMOTE") - 1;
+ bitmask |= LOG_RNB_REMOTE;
+ }
+ else if (strncmp (p, "LOG_RNB_EVENTS", sizeof ("LOG_RNB_EVENTS") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_EVENTS") - 1;
+ bitmask |= LOG_RNB_EVENTS;
+ }
+ else if (strncmp (p, "LOG_RNB_PROC", sizeof ("LOG_RNB_PROC") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_PROC") - 1;
+ bitmask |= LOG_RNB_PROC;
+ }
+ else if (strncmp (p, "LOG_RNB_PACKETS", sizeof ("LOG_RNB_PACKETS") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_PACKETS") - 1;
+ bitmask |= LOG_RNB_PACKETS;
+ }
+ else if (strncmp (p, "LOG_RNB_ALL", sizeof ("LOG_RNB_ALL") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_ALL") - 1;
+ bitmask |= LOG_RNB_ALL;
+ }
+ else if (strncmp (p, "LOG_RNB_DEFAULT", sizeof ("LOG_RNB_DEFAULT") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_DEFAULT") - 1;
+ bitmask |= LOG_RNB_DEFAULT;
+ }
+ else if (strncmp (p, "LOG_RNB_NONE", sizeof ("LOG_RNB_NONE") - 1) == 0)
+ {
+ p += sizeof ("LOG_RNB_NONE") - 1;
+ bitmask = 0;
+ }
+ else
+ {
+ /* Unrecognized logging bit; ignore it. */
+ const char *c = strchr (p, '|');
+ if (c)
+ {
+ p = c;
+ }
+ else
+ {
+ c = strchr (p, ';');
+ if (c)
+ {
+ p = c;
+ }
+ else
+ {
+ // Improperly terminated word; just go to end of str
+ p = strchr (p, '\0');
+ }
+ }
+ }
+ }
+ // Did we get a properly formatted logging bitmask?
+ if (p && *p == ';')
+ {
+ // Enable DNB logging
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+ DNBLogSetLogMask (bitmask);
+ p++;
+ }
+ }
+ // We're not going to support logging to a file for now. All logging
+ // goes through ASL.
+#if 0
+ else if (strncmp (p, "mode=", sizeof ("mode=") - 1) == 0)
+ {
+ p += sizeof ("mode=") - 1;
+ if (strncmp (p, "asl;", sizeof ("asl;") - 1) == 0)
+ {
+ DNBLogToASL ();
+ p += sizeof ("asl;") - 1;
+ }
+ else if (strncmp (p, "file;", sizeof ("file;") - 1) == 0)
+ {
+ DNBLogToFile ();
+ p += sizeof ("file;") - 1;
+ }
+ else
+ {
+ // Ignore unknown argument
+ const char *c = strchr (p, ';');
+ if (c)
+ p = c + 1;
+ else
+ p = strchr (p, '\0');
+ }
+ }
+ else if (strncmp (p, "filename=", sizeof ("filename=") - 1) == 0)
+ {
+ p += sizeof ("filename=") - 1;
+ const char *c = strchr (p, ';');
+ if (c == NULL)
+ {
+ c = strchr (p, '\0');
+ continue;
+ }
+ char *fn = (char *) alloca (c - p + 1);
+ strncpy (fn, p, c - p);
+ fn[c - p] = '\0';
+
+ // A file name of "asl" is special and is another way to indicate
+ // that logging should be done via ASL, not by file.
+ if (strcmp (fn, "asl") == 0)
+ {
+ DNBLogToASL ();
+ }
+ else
+ {
+ FILE *f = fopen (fn, "w");
+ if (f)
+ {
+ DNBLogSetLogFile (f);
+ DNBEnableLogging (f, DNBLogGetLogMask ());
+ DNBLogToFile ();
+ }
+ }
+ p = c + 1;
+ }
+#endif /* #if 0 to enforce ASL logging only. */
+ else
+ {
+ // Ignore unknown argument
+ const char *c = strchr (p, ';');
+ if (c)
+ p = c + 1;
+ else
+ p = strchr (p, '\0');
+ }
+ }
+
+ return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QThreadSuffixSupported (const char *p)
+{
+ m_thread_suffix_supported = true;
+ return SendPacket ("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QStartNoAckMode (const char *p)
+{
+ // Send the OK packet first so the correct checksum is appended...
+ rnb_err_t result = SendPacket ("OK");
+ m_noack_mode = true;
+ return result;
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetLogging (const char *p)
+{
+ p += sizeof ("QSetLogging:") - 1;
+ rnb_err_t result = set_logging (p);
+ if (result == rnb_success)
+ return SendPacket ("OK");
+ else
+ return SendPacket ("E35");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetDisableASLR (const char *p)
+{
+ extern int g_disable_aslr;
+ p += sizeof ("QSetDisableASLR:") - 1;
+ switch (*p)
+ {
+ case '0': g_disable_aslr = 0; break;
+ case '1': g_disable_aslr = 1; break;
+ default:
+ return SendPacket ("E56");
+ }
+ return SendPacket ("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetSTDIO (const char *p)
+{
+ // Only set stdin/out/err if we don't already have a process
+ if (!m_ctx.HasValidProcessID())
+ {
+ bool success = false;
+ // Check the seventh character since the packet will be one of:
+ // QSetSTDIN
+ // QSetSTDOUT
+ // QSetSTDERR
+ StringExtractor packet(p);
+ packet.SetFilePos (7);
+ char ch = packet.GetChar();
+ while (packet.GetChar() != ':')
+ /* Do nothing. */;
+
+ switch (ch)
+ {
+ case 'I': // STDIN
+ packet.GetHexByteString (m_ctx.GetSTDIN());
+ success = !m_ctx.GetSTDIN().empty();
+ break;
+
+ case 'O': // STDOUT
+ packet.GetHexByteString (m_ctx.GetSTDOUT());
+ success = !m_ctx.GetSTDOUT().empty();
+ break;
+
+ case 'E': // STDERR
+ packet.GetHexByteString (m_ctx.GetSTDERR());
+ success = !m_ctx.GetSTDERR().empty();
+ break;
+
+ default:
+ break;
+ }
+ if (success)
+ return SendPacket ("OK");
+ return SendPacket ("E57");
+ }
+ return SendPacket ("E58");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetWorkingDir (const char *p)
+{
+ // Only set the working directory if we don't already have a process
+ if (!m_ctx.HasValidProcessID())
+ {
+ StringExtractor packet(p += sizeof ("QSetWorkingDir:") - 1);
+ if (packet.GetHexByteString (m_ctx.GetWorkingDir()))
+ {
+ struct stat working_dir_stat;
+ if (::stat(m_ctx.GetWorkingDirPath(), &working_dir_stat) == -1)
+ {
+ m_ctx.GetWorkingDir().clear();
+ return SendPacket ("E61"); // Working directory doesn't exist...
+ }
+ else if ((working_dir_stat.st_mode & S_IFMT) == S_IFDIR)
+ {
+ return SendPacket ("OK");
+ }
+ else
+ {
+ m_ctx.GetWorkingDir().clear();
+ return SendPacket ("E62"); // Working directory isn't a directory...
+ }
+ }
+ return SendPacket ("E59"); // Invalid path
+ }
+ return SendPacket ("E60"); // Already had a process, too late to set working dir
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QSyncThreadState (const char *p)
+{
+ if (!m_ctx.HasValidProcessID())
+ {
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ return SendPacket ("OK");
+ }
+
+ errno = 0;
+ p += strlen("QSyncThreadState:");
+ nub_thread_t tid = strtoul (p, NULL, 16);
+ if (errno != 0 && tid == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid thread number in QSyncThreadState packet");
+ }
+ if (DNBProcessSyncThreadState(m_ctx.ProcessID(), tid))
+ return SendPacket("OK");
+ else
+ return SendPacket ("E61");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetDetachOnError (const char *p)
+{
+ p += sizeof ("QSetDetachOnError:") - 1;
+ bool should_detach = true;
+ switch (*p)
+ {
+ case '0': should_detach = false; break;
+ case '1': should_detach = true; break;
+ default:
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid value for QSetDetachOnError - should be 0 or 1");
+ break;
+ }
+
+ m_ctx.SetDetachOnError(should_detach);
+ return SendPacket ("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QListThreadsInStopReply (const char *p)
+{
+ // If this packet is received, it allows us to send an extra key/value
+ // pair in the stop reply packets where we will list all of the thread IDs
+ // separated by commas:
+ //
+ // "threads:10a,10b,10c;"
+ //
+ // This will get included in the stop reply packet as something like:
+ //
+ // "T11thread:10a;00:00000000;01:00010203:threads:10a,10b,10c;"
+ //
+ // This can save two packets on each stop: qfThreadInfo/qsThreadInfo and
+ // speed things up a bit.
+ //
+ // Send the OK packet first so the correct checksum is appended...
+ rnb_err_t result = SendPacket ("OK");
+ m_list_threads_in_stop_reply = true;
+
+ return result;
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetMaxPayloadSize (const char *p)
+{
+ /* The number of characters in a packet payload that gdb is
+ prepared to accept. The packet-start char, packet-end char,
+ 2 checksum chars and terminating null character are not included
+ in this size. */
+ p += sizeof ("QSetMaxPayloadSize:") - 1;
+ errno = 0;
+ uint32_t size = static_cast<uint32_t>(strtoul (p, NULL, 16));
+ if (errno != 0 && size == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in QSetMaxPayloadSize packet");
+ }
+ m_max_payload_size = size;
+ return SendPacket ("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetMaxPacketSize (const char *p)
+{
+ /* This tells us the largest packet that gdb can handle.
+ i.e. the size of gdb's packet-reading buffer.
+ QSetMaxPayloadSize is preferred because it is less ambiguous. */
+ p += sizeof ("QSetMaxPacketSize:") - 1;
+ errno = 0;
+ uint32_t size = static_cast<uint32_t>(strtoul (p, NULL, 16));
+ if (errno != 0 && size == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in QSetMaxPacketSize packet");
+ }
+ m_max_payload_size = size - 5;
+ return SendPacket ("OK");
+}
+
+
+
+
+rnb_err_t
+RNBRemote::HandlePacket_QEnvironment (const char *p)
+{
+ /* This sets the environment for the target program. The packet is of the form:
+
+ QEnvironment:VARIABLE=VALUE
+
+ */
+
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironment: \"%s\"",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p);
+
+ p += sizeof ("QEnvironment:") - 1;
+ RNBContext& ctx = Context();
+
+ ctx.PushEnvironment (p);
+ return SendPacket ("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QEnvironmentHexEncoded (const char *p)
+{
+ /* This sets the environment for the target program. The packet is of the form:
+
+ QEnvironmentHexEncoded:VARIABLE=VALUE
+
+ The VARIABLE=VALUE part is sent hex-encoded so characters like '#' with special
+ meaning in the remote protocol won't break it.
+ */
+
+ DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironmentHexEncoded: \"%s\"",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p);
+
+ p += sizeof ("QEnvironmentHexEncoded:") - 1;
+
+ std::string arg;
+ const char *c;
+ c = p;
+ while (*c != '\0')
+ {
+ if (*(c + 1) == '\0')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'QEnvironmentHexEncoded' pkt");
+ }
+ char smallbuf[3];
+ smallbuf[0] = *c;
+ smallbuf[1] = *(c + 1);
+ smallbuf[2] = '\0';
+ errno = 0;
+ int ch = static_cast<int>(strtoul (smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'QEnvironmentHexEncoded' pkt");
+ }
+ arg.push_back(ch);
+ c += 2;
+ }
+
+ RNBContext& ctx = Context();
+ if (arg.length() > 0)
+ ctx.PushEnvironment (arg.c_str());
+
+ return SendPacket ("OK");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_QLaunchArch (const char *p)
+{
+ p += sizeof ("QLaunchArch:") - 1;
+ if (DNBSetArchitecture(p))
+ return SendPacket ("OK");
+ return SendPacket ("E63");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_QSetProcessEvent (const char *p)
+{
+ p += sizeof ("QSetProcessEvent:") - 1;
+ // If the process is running, then send the event to the process, otherwise
+ // store it in the context.
+ if (Context().HasValidProcessID())
+ {
+ if (DNBProcessSendEvent (Context().ProcessID(), p))
+ return SendPacket("OK");
+ else
+ return SendPacket ("E80");
+ }
+ else
+ {
+ Context().PushProcessEvent(p);
+ }
+ return SendPacket ("OK");
+}
+
+void
+append_hex_value (std::ostream& ostrm, const void *buf, size_t buf_size, bool swap)
+{
+ int i;
+ const uint8_t *p = (const uint8_t *)buf;
+ if (swap)
+ {
+ for (i = static_cast<int>(buf_size)-1; i >= 0; i--)
+ ostrm << RAWHEX8(p[i]);
+ }
+ else
+ {
+ for (size_t i = 0; i < buf_size; i++)
+ ostrm << RAWHEX8(p[i]);
+ }
+}
+
+void
+append_hexified_string (std::ostream& ostrm, const std::string &string)
+{
+ size_t string_size = string.size();
+ const char *string_buf = string.c_str();
+ for (size_t i = 0; i < string_size; i++)
+ {
+ ostrm << RAWHEX8(*(string_buf + i));
+ }
+}
+
+
+
+void
+register_value_in_hex_fixed_width (std::ostream& ostrm,
+ nub_process_t pid,
+ nub_thread_t tid,
+ const register_map_entry_t* reg,
+ const DNBRegisterValue *reg_value_ptr)
+{
+ if (reg != NULL)
+ {
+ DNBRegisterValue reg_value;
+ if (reg_value_ptr == NULL)
+ {
+ if (DNBThreadGetRegisterValueByID (pid, tid, reg->nub_info.set, reg->nub_info.reg, &reg_value))
+ reg_value_ptr = &reg_value;
+ }
+
+ if (reg_value_ptr)
+ {
+ append_hex_value (ostrm, reg_value_ptr->value.v_uint8, reg->nub_info.size, false);
+ }
+ else
+ {
+ // If we fail to read a register value, check if it has a default
+ // fail value. If it does, return this instead in case some of
+ // the registers are not available on the current system.
+ if (reg->nub_info.size > 0)
+ {
+ std::basic_string<uint8_t> zeros(reg->nub_info.size, '\0');
+ append_hex_value (ostrm, zeros.data(), zeros.size(), false);
+ }
+ }
+ }
+}
+
+
+void
+debugserver_regnum_with_fixed_width_hex_register_value (std::ostream& ostrm,
+ nub_process_t pid,
+ nub_thread_t tid,
+ const register_map_entry_t* reg,
+ const DNBRegisterValue *reg_value_ptr)
+{
+ // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX
+ // gdb register number, and VVVVVVVV is the correct number of hex bytes
+ // as ASCII for the register value.
+ if (reg != NULL)
+ {
+ ostrm << RAWHEX8(reg->debugserver_regnum) << ':';
+ register_value_in_hex_fixed_width (ostrm, pid, tid, reg, reg_value_ptr);
+ ostrm << ';';
+ }
+}
+
+
+void
+RNBRemote::DispatchQueueOffsets::GetThreadQueueInfo (nub_process_t pid,
+ nub_addr_t dispatch_qaddr,
+ std::string &queue_name,
+ uint64_t &queue_width,
+ uint64_t &queue_serialnum) const
+{
+ queue_name.clear();
+ queue_width = 0;
+ queue_serialnum = 0;
+
+ if (IsValid() && dispatch_qaddr != INVALID_NUB_ADDRESS && dispatch_qaddr != 0)
+ {
+ nub_addr_t dispatch_queue_addr = DNBProcessMemoryReadPointer (pid, dispatch_qaddr);
+ if (dispatch_queue_addr)
+ {
+ queue_width = DNBProcessMemoryReadInteger (pid, dispatch_queue_addr + dqo_width, dqo_width_size, 0);
+ queue_serialnum = DNBProcessMemoryReadInteger (pid, dispatch_queue_addr + dqo_serialnum, dqo_serialnum_size, 0);
+
+ if (dqo_version >= 4)
+ {
+ // libdispatch versions 4+, pointer to dispatch name is in the
+ // queue structure.
+ nub_addr_t pointer_to_label_address = dispatch_queue_addr + dqo_label;
+ nub_addr_t label_addr = DNBProcessMemoryReadPointer (pid, pointer_to_label_address);
+ if (label_addr)
+ queue_name = std::move(DNBProcessMemoryReadCString (pid, label_addr));
+ }
+ else
+ {
+ // libdispatch versions 1-3, dispatch name is a fixed width char array
+ // in the queue structure.
+ queue_name = std::move(DNBProcessMemoryReadCStringFixed(pid, dispatch_queue_addr + dqo_label, dqo_label_size));
+ }
+ }
+ }
+}
+
+struct StackMemory
+{
+ uint8_t bytes[2*sizeof(nub_addr_t)];
+ nub_size_t length;
+};
+typedef std::map<nub_addr_t, StackMemory> StackMemoryMap;
+
+
+static void
+ReadStackMemory (nub_process_t pid, nub_thread_t tid, StackMemoryMap &stack_mmap, uint32_t backtrace_limit = 256)
+{
+ DNBRegisterValue reg_value;
+ if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_FP, &reg_value))
+ {
+ uint32_t frame_count = 0;
+ uint64_t fp = 0;
+ if (reg_value.info.size == 4)
+ fp = reg_value.value.uint32;
+ else
+ fp = reg_value.value.uint64;
+ while (fp != 0)
+ {
+ // Make sure we never recurse more than 256 times so we don't recurse too far or
+ // store up too much memory in the expedited cache
+ if (++frame_count > backtrace_limit)
+ break;
+
+ const nub_size_t read_size = reg_value.info.size*2;
+ StackMemory stack_memory;
+ stack_memory.length = read_size;
+ if (DNBProcessMemoryRead(pid, fp, read_size, stack_memory.bytes) != read_size)
+ break;
+ // Make sure we don't try to put the same stack memory in more than once
+ if (stack_mmap.find(fp) != stack_mmap.end())
+ break;
+ // Put the entry into the cache
+ stack_mmap[fp] = stack_memory;
+ // Dereference the frame pointer to get to the previous frame pointer
+ if (reg_value.info.size == 4)
+ fp = ((uint32_t *)stack_memory.bytes)[0];
+ else
+ fp = ((uint64_t *)stack_memory.bytes)[0];
+ }
+ }
+}
+
+rnb_err_t
+RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid)
+{
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("E50");
+
+ struct DNBThreadStopInfo tid_stop_info;
+
+ /* Fill the remaining space in this packet with as many registers
+ as we can stuff in there. */
+
+ if (DNBThreadGetStopReason (pid, tid, &tid_stop_info))
+ {
+ const bool did_exec = tid_stop_info.reason == eStopTypeExec;
+ if (did_exec)
+ {
+ RNBRemote::InitializeRegisters(true);
+
+ // Reset any symbols that need resetting when we exec
+ m_dispatch_queue_offsets_addr = INVALID_NUB_ADDRESS;
+ m_dispatch_queue_offsets.Clear();
+ }
+
+ std::ostringstream ostrm;
+ // Output the T packet with the thread
+ ostrm << 'T';
+ int signum = tid_stop_info.details.signal.signo;
+ DNBLogThreadedIf (LOG_RNB_PROC, "%8d %s got signal signo = %u, exc_type = %u", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, signum, tid_stop_info.details.exception.type);
+
+ // Translate any mach exceptions to gdb versions, unless they are
+ // common exceptions like a breakpoint or a soft signal.
+ switch (tid_stop_info.details.exception.type)
+ {
+ default: signum = 0; break;
+ case EXC_BREAKPOINT: signum = SIGTRAP; break;
+ case EXC_BAD_ACCESS: signum = TARGET_EXC_BAD_ACCESS; break;
+ case EXC_BAD_INSTRUCTION: signum = TARGET_EXC_BAD_INSTRUCTION; break;
+ case EXC_ARITHMETIC: signum = TARGET_EXC_ARITHMETIC; break;
+ case EXC_EMULATION: signum = TARGET_EXC_EMULATION; break;
+ case EXC_SOFTWARE:
+ if (tid_stop_info.details.exception.data_count == 2 &&
+ tid_stop_info.details.exception.data[0] == EXC_SOFT_SIGNAL)
+ signum = static_cast<int>(tid_stop_info.details.exception.data[1]);
+ else
+ signum = TARGET_EXC_SOFTWARE;
+ break;
+ }
+
+ ostrm << RAWHEX8(signum & 0xff);
+
+ ostrm << std::hex << "thread:" << tid << ';';
+
+ const char *thread_name = DNBThreadGetName (pid, tid);
+ if (thread_name && thread_name[0])
+ {
+ size_t thread_name_len = strlen(thread_name);
+
+
+ if (::strcspn (thread_name, "$#+-;:") == thread_name_len)
+ ostrm << std::hex << "name:" << thread_name << ';';
+ else
+ {
+ // the thread name contains special chars, send as hex bytes
+ ostrm << std::hex << "hexname:";
+ uint8_t *u_thread_name = (uint8_t *)thread_name;
+ for (size_t i = 0; i < thread_name_len; i++)
+ ostrm << RAWHEX8(u_thread_name[i]);
+ ostrm << ';';
+ }
+ }
+
+ thread_identifier_info_data_t thread_ident_info;
+ if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info))
+ {
+ if (thread_ident_info.dispatch_qaddr != 0)
+ {
+ ostrm << "qaddr:" << std::hex << thread_ident_info.dispatch_qaddr << ';';
+ const DispatchQueueOffsets *dispatch_queue_offsets = GetDispatchQueueOffsets();
+ if (dispatch_queue_offsets)
+ {
+ std::string queue_name;
+ uint64_t queue_width = 0;
+ uint64_t queue_serialnum = 0;
+ dispatch_queue_offsets->GetThreadQueueInfo(pid, thread_ident_info.dispatch_qaddr, queue_name, queue_width, queue_serialnum);
+ if (!queue_name.empty())
+ {
+ ostrm << "qname:";
+ append_hex_value(ostrm, queue_name.data(), queue_name.size(), false);
+ ostrm << ';';
+ }
+ if (queue_width == 1)
+ ostrm << "qkind:serial;";
+ else if (queue_width > 1)
+ ostrm << "qkind:concurrent;";
+
+ if (queue_serialnum > 0)
+ ostrm << "qserial:" << DECIMAL << queue_serialnum << ';';
+ }
+ }
+ }
+
+ // If a 'QListThreadsInStopReply' was sent to enable this feature, we
+ // will send all thread IDs back in the "threads" key whose value is
+ // a list of hex thread IDs separated by commas:
+ // "threads:10a,10b,10c;"
+ // This will save the debugger from having to send a pair of qfThreadInfo
+ // and qsThreadInfo packets, but it also might take a lot of room in the
+ // stop reply packet, so it must be enabled only on systems where there
+ // are no limits on packet lengths.
+ if (m_list_threads_in_stop_reply)
+ {
+ const nub_size_t numthreads = DNBProcessGetNumThreads (pid);
+ if (numthreads > 0)
+ {
+ std::vector<uint64_t> pc_values;
+ ostrm << std::hex << "threads:";
+ for (nub_size_t i = 0; i < numthreads; ++i)
+ {
+ nub_thread_t th = DNBProcessGetThreadAtIndex (pid, i);
+ if (i > 0)
+ ostrm << ',';
+ ostrm << std::hex << th;
+ DNBRegisterValue pc_regval;
+ if (DNBThreadGetRegisterValueByID (pid, th, REGISTER_SET_GENERIC, GENERIC_REGNUM_PC, &pc_regval))
+ {
+ uint64_t pc = INVALID_NUB_ADDRESS;
+ if (pc_regval.value.uint64 != INVALID_NUB_ADDRESS)
+ {
+ if (pc_regval.info.size == 4)
+ {
+ pc = pc_regval.value.uint32;
+ }
+ else if (pc_regval.info.size == 8)
+ {
+ pc = pc_regval.value.uint64;
+ }
+ if (pc != INVALID_NUB_ADDRESS)
+ {
+ pc_values.push_back (pc);
+ }
+ }
+ }
+ }
+ ostrm << ';';
+
+ // If we failed to get any of the thread pc values, the size of our vector will not
+ // be the same as the # of threads. Don't provide any expedited thread pc values in
+ // that case. This should not happen.
+ if (pc_values.size() == numthreads)
+ {
+ ostrm << std::hex << "thread-pcs:";
+ for (nub_size_t i = 0; i < numthreads; ++i)
+ {
+ if (i > 0)
+ ostrm << ',';
+ ostrm << std::hex << pc_values[i];
+ }
+ ostrm << ';';
+ }
+ }
+
+ // Include JSON info that describes the stop reason for any threads
+ // that actually have stop reasons. We use the new "jstopinfo" key
+ // whose values is hex ascii JSON that contains the thread IDs
+ // thread stop info only for threads that have stop reasons. Only send
+ // this if we have more than one thread otherwise this packet has all
+ // the info it needs.
+ if (numthreads > 1)
+ {
+ const bool threads_with_valid_stop_info_only = true;
+ JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only);
+ if (threads_info_sp)
+ {
+ ostrm << std::hex << "jstopinfo:";
+ std::ostringstream json_strm;
+ threads_info_sp->Dump (json_strm);
+ append_hexified_string (ostrm, json_strm.str());
+ ostrm << ';';
+ }
+ }
+ }
+
+
+ if (g_num_reg_entries == 0)
+ InitializeRegisters ();
+
+ if (g_reg_entries != NULL)
+ {
+ DNBRegisterValue reg_value;
+ for (uint32_t reg = 0; reg < g_num_reg_entries; reg++)
+ {
+ // Expedite all registers in the first register set that aren't
+ // contained in other registers
+ if (g_reg_entries[reg].nub_info.set == 1 &&
+ g_reg_entries[reg].nub_info.value_regs == NULL)
+ {
+ if (!DNBThreadGetRegisterValueByID (pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, &reg_value))
+ continue;
+
+ debugserver_regnum_with_fixed_width_hex_register_value (ostrm, pid, tid, &g_reg_entries[reg], &reg_value);
+ }
+ }
+ }
+
+ if (did_exec)
+ {
+ ostrm << "reason:exec;";
+ }
+ else if (tid_stop_info.details.exception.type)
+ {
+ ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type << ';';
+ ostrm << "mecount:" << std::hex << tid_stop_info.details.exception.data_count << ';';
+ for (nub_size_t i = 0; i < tid_stop_info.details.exception.data_count; ++i)
+ ostrm << "medata:" << std::hex << tid_stop_info.details.exception.data[i] << ';';
+ }
+
+ // Add expedited stack memory so stack backtracing doesn't need to read anything from the
+ // frame pointer chain.
+ StackMemoryMap stack_mmap;
+ ReadStackMemory (pid, tid, stack_mmap, 2);
+ if (!stack_mmap.empty())
+ {
+ for (const auto &stack_memory : stack_mmap)
+ {
+ ostrm << "memory:" << HEXBASE << stack_memory.first << '=';
+ append_hex_value (ostrm, stack_memory.second.bytes, stack_memory.second.length, false);
+ ostrm << ';';
+ }
+ }
+
+ return SendPacket (ostrm.str ());
+ }
+ return SendPacket("E51");
+}
+
+/* '?'
+ The stop reply packet - tell gdb what the status of the inferior is.
+ Often called the questionmark_packet. */
+
+rnb_err_t
+RNBRemote::HandlePacket_last_signal (const char *unused)
+{
+ if (!m_ctx.HasValidProcessID())
+ {
+ // Inferior is not yet specified/running
+ return SendPacket ("E02");
+ }
+
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_state_t pid_state = DNBProcessGetState (pid);
+
+ switch (pid_state)
+ {
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ return rnb_success; // Ignore
+
+ case eStateSuspended:
+ case eStateStopped:
+ case eStateCrashed:
+ {
+ nub_thread_t tid = DNBProcessGetCurrentThread (pid);
+ // Make sure we set the current thread so g and p packets return
+ // the data the gdb will expect.
+ SetCurrentThread (tid);
+
+ SendStopReplyPacketForThread (tid);
+ }
+ break;
+
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateExited:
+ {
+ char pid_exited_packet[16] = "";
+ int pid_status = 0;
+ // Process exited with exit status
+ if (!DNBProcessGetExitStatus(pid, &pid_status))
+ pid_status = 0;
+
+ if (pid_status)
+ {
+ if (WIFEXITED (pid_status))
+ snprintf (pid_exited_packet, sizeof(pid_exited_packet), "W%02x", WEXITSTATUS (pid_status));
+ else if (WIFSIGNALED (pid_status))
+ snprintf (pid_exited_packet, sizeof(pid_exited_packet), "X%02x", WEXITSTATUS (pid_status));
+ else if (WIFSTOPPED (pid_status))
+ snprintf (pid_exited_packet, sizeof(pid_exited_packet), "S%02x", WSTOPSIG (pid_status));
+ }
+
+ // If we have an empty exit packet, lets fill one in to be safe.
+ if (!pid_exited_packet[0])
+ {
+ strncpy (pid_exited_packet, "W00", sizeof(pid_exited_packet)-1);
+ pid_exited_packet[sizeof(pid_exited_packet)-1] = '\0';
+ }
+
+ const char *exit_info = DNBProcessGetExitInfo (pid);
+ if (exit_info != NULL && *exit_info != '\0')
+ {
+ std::ostringstream exit_packet;
+ exit_packet << pid_exited_packet;
+ exit_packet << ';';
+ exit_packet << RAW_HEXBASE << "description";
+ exit_packet << ':';
+ for (size_t i = 0; exit_info[i] != '\0'; i++)
+ exit_packet << RAWHEX8(exit_info[i]);
+ exit_packet << ';';
+ return SendPacket (exit_packet.str());
+ }
+ else
+ return SendPacket (pid_exited_packet);
+ }
+ break;
+ }
+ return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_M (const char *p)
+{
+ if (p == NULL || p[0] == '\0' || strlen (p) < 3)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Too short M packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull (p, &c, 16);
+ if (errno != 0 && addr == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in M packet");
+ }
+ if (*c != ',')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in M packet");
+ }
+
+ /* Advance 'p' to the length part of the packet. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ unsigned long length = strtoul (p, &c, 16);
+ if (errno != 0 && length == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in M packet");
+ }
+ if (length == 0)
+ {
+ return SendPacket ("OK");
+ }
+
+ if (*c != ':')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Missing colon in M packet");
+ }
+ /* Advance 'p' to the data part of the packet. */
+ p += (c - p) + 1;
+
+ size_t datalen = strlen (p);
+ if (datalen & 0x1)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Uneven # of hex chars for data in M packet");
+ }
+ if (datalen == 0)
+ {
+ return SendPacket ("OK");
+ }
+
+ uint8_t *buf = (uint8_t *) alloca (datalen / 2);
+ uint8_t *i = buf;
+
+ while (*p != '\0' && *(p + 1) != '\0')
+ {
+ char hexbuf[3];
+ hexbuf[0] = *p;
+ hexbuf[1] = *(p + 1);
+ hexbuf[2] = '\0';
+ errno = 0;
+ uint8_t byte = strtoul (hexbuf, NULL, 16);
+ if (errno != 0 && byte == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid hex byte in M packet");
+ }
+ *i++ = byte;
+ p += 2;
+ }
+
+ nub_size_t wrote = DNBProcessMemoryWrite (m_ctx.ProcessID(), addr, length, buf);
+ if (wrote != length)
+ return SendPacket ("E09");
+ else
+ return SendPacket ("OK");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_m (const char *p)
+{
+ if (p == NULL || p[0] == '\0' || strlen (p) < 3)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Too short m packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull (p, &c, 16);
+ if (errno != 0 && addr == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in m packet");
+ }
+ if (*c != ',')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in m packet");
+ }
+
+ /* Advance 'p' to the length part of the packet. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ auto length = strtoul (p, NULL, 16);
+ if (errno != 0 && length == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in m packet");
+ }
+ if (length == 0)
+ {
+ return SendPacket ("");
+ }
+
+ std::string buf(length, '\0');
+ if (buf.empty())
+ {
+ return SendPacket ("E78");
+ }
+ nub_size_t bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID(), addr, buf.size(), &buf[0]);
+ if (bytes_read == 0)
+ {
+ return SendPacket ("E08");
+ }
+
+ // "The reply may contain fewer bytes than requested if the server was able
+ // to read only part of the region of memory."
+ length = bytes_read;
+
+ std::ostringstream ostrm;
+ for (unsigned long i = 0; i < length; i++)
+ ostrm << RAWHEX8(buf[i]);
+ return SendPacket (ostrm.str ());
+}
+
+// Read memory, sent it up as binary data.
+// Usage: xADDR,LEN
+// ADDR and LEN are both base 16.
+
+// Responds with 'OK' for zero-length request
+// or
+//
+// DATA
+//
+// where DATA is the binary data payload.
+
+rnb_err_t
+RNBRemote::HandlePacket_x (const char *p)
+{
+ if (p == NULL || p[0] == '\0' || strlen (p) < 3)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Too short X packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull (p, &c, 16);
+ if (errno != 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in X packet");
+ }
+ if (*c != ',')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in X packet");
+ }
+
+ /* Advance 'p' to the number of bytes to be read. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ auto length = strtoul (p, NULL, 16);
+ if (errno != 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in x packet");
+ }
+
+ // zero length read means this is a test of whether that packet is implemented or not.
+ if (length == 0)
+ {
+ return SendPacket ("OK");
+ }
+
+ std::vector<uint8_t> buf (length);
+
+ if (buf.capacity() != length)
+ {
+ return SendPacket ("E79");
+ }
+ nub_size_t bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID(), addr, buf.size(), &buf[0]);
+ if (bytes_read == 0)
+ {
+ return SendPacket ("E80");
+ }
+
+ std::vector<uint8_t> buf_quoted;
+ buf_quoted.reserve (bytes_read + 30);
+ for (nub_size_t i = 0; i < bytes_read; i++)
+ {
+ if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*')
+ {
+ buf_quoted.push_back(0x7d);
+ buf_quoted.push_back(buf[i] ^ 0x20);
+ }
+ else
+ {
+ buf_quoted.push_back(buf[i]);
+ }
+ }
+ length = buf_quoted.size();
+
+ std::ostringstream ostrm;
+ for (unsigned long i = 0; i < length; i++)
+ ostrm << buf_quoted[i];
+
+ return SendPacket (ostrm.str ());
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_X (const char *p)
+{
+ if (p == NULL || p[0] == '\0' || strlen (p) < 3)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Too short X packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull (p, &c, 16);
+ if (errno != 0 && addr == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in X packet");
+ }
+ if (*c != ',')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in X packet");
+ }
+
+ /* Advance 'p' to the length part of the packet. NB this is the length of the packet
+ including any escaped chars. The data payload may be a little bit smaller after
+ decoding. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ auto length = strtoul (p, NULL, 16);
+ if (errno != 0 && length == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in X packet");
+ }
+
+ // I think gdb sends a zero length write request to test whether this
+ // packet is accepted.
+ if (length == 0)
+ {
+ return SendPacket ("OK");
+ }
+
+ std::vector<uint8_t> data = decode_binary_data (c, -1);
+ std::vector<uint8_t>::const_iterator it;
+ uint8_t *buf = (uint8_t *) alloca (data.size ());
+ uint8_t *i = buf;
+ for (it = data.begin (); it != data.end (); ++it)
+ {
+ *i++ = *it;
+ }
+
+ nub_size_t wrote = DNBProcessMemoryWrite (m_ctx.ProcessID(), addr, data.size(), buf);
+ if (wrote != data.size ())
+ return SendPacket ("E08");
+ return SendPacket ("OK");
+}
+
+/* 'g' -- read registers
+ Get the contents of the registers for the current thread,
+ send them to gdb.
+ Should the setting of the Hg packet determine which thread's registers
+ are returned? */
+
+rnb_err_t
+RNBRemote::HandlePacket_g (const char *p)
+{
+ std::ostringstream ostrm;
+ if (!m_ctx.HasValidProcessID())
+ {
+ return SendPacket ("E11");
+ }
+
+ if (g_num_reg_entries == 0)
+ InitializeRegisters ();
+
+ nub_process_t pid = m_ctx.ProcessID ();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p + 1);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet");
+
+ // Get the register context size first by calling with NULL buffer
+ nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
+ if (reg_ctx_size)
+ {
+ // Now allocate enough space for the entire register context
+ std::vector<uint8_t> reg_ctx;
+ reg_ctx.resize(reg_ctx_size);
+ // Now read the register context
+ reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, &reg_ctx[0], reg_ctx.size());
+ if (reg_ctx_size)
+ {
+ append_hex_value (ostrm, reg_ctx.data(), reg_ctx.size(), false);
+ return SendPacket (ostrm.str ());
+ }
+ }
+ return SendPacket ("E74");
+}
+
+/* 'G XXX...' -- write registers
+ How is the thread for these specified, beyond "the current thread"?
+ Does gdb actually use the Hg packet to set this? */
+
+rnb_err_t
+RNBRemote::HandlePacket_G (const char *p)
+{
+ if (!m_ctx.HasValidProcessID())
+ {
+ return SendPacket ("E11");
+ }
+
+ if (g_num_reg_entries == 0)
+ InitializeRegisters ();
+
+ StringExtractor packet(p);
+ packet.SetFilePos(1); // Skip the 'G'
+
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet");
+
+ // Get the register context size first by calling with NULL buffer
+ nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
+ if (reg_ctx_size)
+ {
+ // Now allocate enough space for the entire register context
+ std::vector<uint8_t> reg_ctx;
+ reg_ctx.resize(reg_ctx_size);
+
+ const nub_size_t bytes_extracted = packet.GetHexBytes (&reg_ctx[0], reg_ctx.size(), 0xcc);
+ if (bytes_extracted == reg_ctx.size())
+ {
+ // Now write the register context
+ reg_ctx_size = DNBThreadSetRegisterContext(pid, tid, reg_ctx.data(), reg_ctx.size());
+ if (reg_ctx_size == reg_ctx.size())
+ return SendPacket ("OK");
+ else
+ return SendPacket ("E55");
+ }
+ else
+ {
+ DNBLogError("RNBRemote::HandlePacket_G(%s): extracted %llu of %llu bytes, size mismatch\n", p, (uint64_t)bytes_extracted, (uint64_t)reg_ctx_size);
+ return SendPacket ("E64");
+ }
+ }
+ return SendPacket ("E65");
+}
+
+static bool
+RNBRemoteShouldCancelCallback (void *not_used)
+{
+ RNBRemoteSP remoteSP(g_remoteSP);
+ if (remoteSP.get() != NULL)
+ {
+ RNBRemote* remote = remoteSP.get();
+ if (remote->Comm().IsConnected())
+ return false;
+ else
+ return true;
+ }
+ return true;
+}
+
+
+// FORMAT: _MXXXXXX,PPP
+// XXXXXX: big endian hex chars
+// PPP: permissions can be any combo of r w x chars
+//
+// RESPONSE: XXXXXX
+// XXXXXX: hex address of the newly allocated memory
+// EXX: error code
+//
+// EXAMPLES:
+// _M123000,rw
+// _M123000,rwx
+// _M123000,xw
+
+rnb_err_t
+RNBRemote::HandlePacket_AllocateMemory (const char *p)
+{
+ StringExtractor packet (p);
+ packet.SetFilePos(2); // Skip the "_M"
+
+ nub_addr_t size = packet.GetHexMaxU64 (StringExtractor::BigEndian, 0);
+ if (size != 0)
+ {
+ if (packet.GetChar() == ',')
+ {
+ uint32_t permissions = 0;
+ char ch;
+ bool success = true;
+ while (success && (ch = packet.GetChar()) != '\0')
+ {
+ switch (ch)
+ {
+ case 'r': permissions |= eMemoryPermissionsReadable; break;
+ case 'w': permissions |= eMemoryPermissionsWritable; break;
+ case 'x': permissions |= eMemoryPermissionsExecutable; break;
+ default: success = false; break;
+ }
+ }
+
+ if (success)
+ {
+ nub_addr_t addr = DNBProcessMemoryAllocate (m_ctx.ProcessID(), size, permissions);
+ if (addr != INVALID_NUB_ADDRESS)
+ {
+ std::ostringstream ostrm;
+ ostrm << RAW_HEXBASE << addr;
+ return SendPacket (ostrm.str ());
+ }
+ }
+ }
+ }
+ return SendPacket ("E53");
+}
+
+// FORMAT: _mXXXXXX
+// XXXXXX: address that was previously allocated
+//
+// RESPONSE: XXXXXX
+// OK: address was deallocated
+// EXX: error code
+//
+// EXAMPLES:
+// _m123000
+
+rnb_err_t
+RNBRemote::HandlePacket_DeallocateMemory (const char *p)
+{
+ StringExtractor packet (p);
+ packet.SetFilePos(2); // Skip the "_m"
+ nub_addr_t addr = packet.GetHexMaxU64 (StringExtractor::BigEndian, INVALID_NUB_ADDRESS);
+
+ if (addr != INVALID_NUB_ADDRESS)
+ {
+ if (DNBProcessMemoryDeallocate (m_ctx.ProcessID(), addr))
+ return SendPacket ("OK");
+ }
+ return SendPacket ("E54");
+}
+
+
+// FORMAT: QSaveRegisterState;thread:TTTT; (when thread suffix is supported)
+// FORMAT: QSaveRegisterState (when thread suffix is NOT supported)
+// TTTT: thread ID in hex
+//
+// RESPONSE:
+// SAVEID: Where SAVEID is a decimal number that represents the save ID
+// that can be passed back into a "QRestoreRegisterState" packet
+// EXX: error code
+//
+// EXAMPLES:
+// QSaveRegisterState;thread:1E34; (when thread suffix is supported)
+// QSaveRegisterState (when thread suffix is NOT supported)
+
+rnb_err_t
+RNBRemote::HandlePacket_SaveRegisterState (const char *p)
+{
+ nub_process_t pid = m_ctx.ProcessID ();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p);
+ if (tid == INVALID_NUB_THREAD)
+ {
+ if (m_thread_suffix_supported)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in QSaveRegisterState packet");
+ else
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread was is set with the Hg packet");
+ }
+
+ // Get the register context size first by calling with NULL buffer
+ const uint32_t save_id = DNBThreadSaveRegisterState(pid, tid);
+ if (save_id != 0)
+ {
+ char response[64];
+ snprintf (response, sizeof(response), "%u", save_id);
+ return SendPacket (response);
+ }
+ else
+ {
+ return SendPacket ("E75");
+ }
+}
+// FORMAT: QRestoreRegisterState:SAVEID;thread:TTTT; (when thread suffix is supported)
+// FORMAT: QRestoreRegisterState:SAVEID (when thread suffix is NOT supported)
+// TTTT: thread ID in hex
+// SAVEID: a decimal number that represents the save ID that was
+// returned from a call to "QSaveRegisterState"
+//
+// RESPONSE:
+// OK: successfully restored registers for the specified thread
+// EXX: error code
+//
+// EXAMPLES:
+// QRestoreRegisterState:1;thread:1E34; (when thread suffix is supported)
+// QRestoreRegisterState:1 (when thread suffix is NOT supported)
+
+rnb_err_t
+RNBRemote::HandlePacket_RestoreRegisterState (const char *p)
+{
+ nub_process_t pid = m_ctx.ProcessID ();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p);
+ if (tid == INVALID_NUB_THREAD)
+ {
+ if (m_thread_suffix_supported)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in QSaveRegisterState packet");
+ else
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread was is set with the Hg packet");
+ }
+
+ StringExtractor packet (p);
+ packet.SetFilePos(strlen("QRestoreRegisterState:")); // Skip the "QRestoreRegisterState:"
+ const uint32_t save_id = packet.GetU32(0);
+
+ if (save_id != 0)
+ {
+ // Get the register context size first by calling with NULL buffer
+ if (DNBThreadRestoreRegisterState(pid, tid, save_id))
+ return SendPacket ("OK");
+ else
+ return SendPacket ("E77");
+ }
+ return SendPacket ("E76");
+}
+
+static bool
+GetProcessNameFrom_vAttach (const char *&p, std::string &attach_name)
+{
+ bool return_val = true;
+ while (*p != '\0')
+ {
+ char smallbuf[3];
+ smallbuf[0] = *p;
+ smallbuf[1] = *(p + 1);
+ smallbuf[2] = '\0';
+
+ errno = 0;
+ int ch = static_cast<int>(strtoul (smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0)
+ {
+ return_val = false;
+ break;
+ }
+
+ attach_name.push_back(ch);
+ p += 2;
+ }
+ return return_val;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qSupported (const char *p)
+{
+ uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet size--debugger can always use less
+ char buf[256];
+ snprintf (buf, sizeof(buf), "qXfer:features:read+;PacketSize=%x;qEcho+", max_packet_size);
+
+ // By default, don't enable compression. It's only worth doing when we are working
+ // with a low speed communication channel.
+ bool enable_compression = false;
+ (void)enable_compression;
+
+ // Enable compression when debugserver is running on a watchOS device where communication may be over Bluetooth.
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ enable_compression = true;
+#endif
+
+#if defined (HAVE_LIBCOMPRESSION)
+ // libcompression is weak linked so test if compression_decode_buffer() is available
+ if (enable_compression && compression_decode_buffer != NULL)
+ {
+ strcat (buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;DefaultCompressionMinSize=");
+ char numbuf[16];
+ snprintf (numbuf, sizeof (numbuf), "%zu", m_compression_minsize);
+ numbuf[sizeof (numbuf) - 1] = '\0';
+ strcat (buf, numbuf);
+ }
+#elif defined (HAVE_LIBZ)
+ if (enable_compression)
+ {
+ strcat (buf, ";SupportedCompressions=zlib-deflate;DefaultCompressionMinSize=");
+ char numbuf[16];
+ snprintf (numbuf, sizeof (numbuf), "%zu", m_compression_minsize);
+ numbuf[sizeof (numbuf) - 1] = '\0';
+ strcat (buf, numbuf);
+ }
+#endif
+
+ return SendPacket (buf);
+}
+
+/*
+ vAttach;pid
+
+ Attach to a new process with the specified process ID. pid is a hexadecimal integer
+ identifying the process. If the stub is currently controlling a process, it is
+ killed. The attached process is stopped.This packet is only available in extended
+ mode (see extended mode).
+
+ Reply:
+ "ENN" for an error
+ "Any Stop Reply Packet" for success
+ */
+
+rnb_err_t
+RNBRemote::HandlePacket_v (const char *p)
+{
+ if (strcmp (p, "vCont;c") == 0)
+ {
+ // Simple continue
+ return RNBRemote::HandlePacket_c("c");
+ }
+ else if (strcmp (p, "vCont;s") == 0)
+ {
+ // Simple step
+ return RNBRemote::HandlePacket_s("s");
+ }
+ else if (strstr (p, "vCont") == p)
+ {
+ typedef struct
+ {
+ nub_thread_t tid;
+ char action;
+ int signal;
+ } vcont_action_t;
+
+ DNBThreadResumeActions thread_actions;
+ char *c = (char *)(p += strlen("vCont"));
+ char *c_end = c + strlen(c);
+ if (*c == '?')
+ return SendPacket ("vCont;c;C;s;S");
+
+ while (c < c_end && *c == ';')
+ {
+ ++c; // Skip the semi-colon
+ DNBThreadResumeAction thread_action;
+ thread_action.tid = INVALID_NUB_THREAD;
+ thread_action.state = eStateInvalid;
+ thread_action.signal = 0;
+ thread_action.addr = INVALID_NUB_ADDRESS;
+
+ char action = *c++;
+
+ switch (action)
+ {
+ case 'C':
+ errno = 0;
+ thread_action.signal = static_cast<int>(strtoul (c, &c, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse signal in vCont packet");
+ // Fall through to next case...
+
+ case 'c':
+ // Continue
+ thread_action.state = eStateRunning;
+ break;
+
+ case 'S':
+ errno = 0;
+ thread_action.signal = static_cast<int>(strtoul (c, &c, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse signal in vCont packet");
+ // Fall through to next case...
+
+ case 's':
+ // Step
+ thread_action.state = eStateStepping;
+ break;
+
+ default:
+ HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Unsupported action in vCont packet");
+ break;
+ }
+ if (*c == ':')
+ {
+ errno = 0;
+ thread_action.tid = strtoul (++c, &c, 16);
+ if (errno != 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse thread number in vCont packet");
+ }
+
+ thread_actions.Append (thread_action);
+ }
+
+ // If a default action for all other threads wasn't mentioned
+ // then we should stop the threads
+ thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0);
+ DNBProcessResume(m_ctx.ProcessID(), thread_actions.GetFirst (), thread_actions.GetSize());
+ return rnb_success;
+ }
+ else if (strstr (p, "vAttach") == p)
+ {
+ nub_process_t attach_pid = INVALID_NUB_PROCESS; // attach_pid will be set to 0 if the attach fails
+ nub_process_t pid_attaching_to = INVALID_NUB_PROCESS; // pid_attaching_to is the original pid specified
+ char err_str[1024]={'\0'};
+ std::string attach_name;
+
+ if (strstr (p, "vAttachWait;") == p)
+ {
+ p += strlen("vAttachWait;");
+ if (!GetProcessNameFrom_vAttach(p, attach_name))
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'vAttachWait' pkt");
+ }
+ const bool ignore_existing = true;
+ attach_pid = DNBProcessAttachWait(attach_name.c_str (), m_ctx.LaunchFlavor(), ignore_existing, NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback);
+
+ }
+ else if (strstr (p, "vAttachOrWait;") == p)
+ {
+ p += strlen("vAttachOrWait;");
+ if (!GetProcessNameFrom_vAttach(p, attach_name))
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'vAttachOrWait' pkt");
+ }
+ const bool ignore_existing = false;
+ attach_pid = DNBProcessAttachWait(attach_name.c_str (), m_ctx.LaunchFlavor(), ignore_existing, NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback);
+ }
+ else if (strstr (p, "vAttachName;") == p)
+ {
+ p += strlen("vAttachName;");
+ if (!GetProcessNameFrom_vAttach(p, attach_name))
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'vAttachName' pkt");
+ }
+
+ attach_pid = DNBProcessAttachByName (attach_name.c_str(), NULL, err_str, sizeof(err_str));
+
+ }
+ else if (strstr (p, "vAttach;") == p)
+ {
+ p += strlen("vAttach;");
+ char *end = NULL;
+ pid_attaching_to = static_cast<int>(strtoul (p, &end, 16)); // PID will be in hex, so use base 16 to decode
+ if (p != end && *end == '\0')
+ {
+ // Wait at most 30 second for attach
+ struct timespec attach_timeout_abstime;
+ DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, 30, 0);
+ attach_pid = DNBProcessAttach(pid_attaching_to, &attach_timeout_abstime, err_str, sizeof(err_str));
+ }
+ }
+ else
+ {
+ return HandlePacket_UNIMPLEMENTED(p);
+ }
+
+
+ if (attach_pid != INVALID_NUB_PROCESS)
+ {
+ if (m_ctx.ProcessID() != attach_pid)
+ m_ctx.SetProcessID(attach_pid);
+ // Send a stop reply packet to indicate we successfully attached!
+ NotifyThatProcessStopped ();
+ return rnb_success;
+ }
+ else
+ {
+ m_ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ m_ctx.LaunchStatus().SetErrorString(err_str);
+ else
+ m_ctx.LaunchStatus().SetErrorString("attach failed");
+
+#if defined (__APPLE__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000)
+ if (pid_attaching_to == INVALID_NUB_PROCESS && !attach_name.empty())
+ {
+ pid_attaching_to = DNBProcessGetPIDByName (attach_name.c_str());
+ }
+ if (pid_attaching_to != INVALID_NUB_PROCESS && strcmp (err_str, "No such process") != 0)
+ {
+ // csr_check(CSR_ALLOW_TASK_FOR_PID) will be nonzero if System Integrity Protection is in effect.
+ if (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0)
+ {
+ bool attach_failed_due_to_sip = false;
+
+ if (rootless_allows_task_for_pid (pid_attaching_to) == 0)
+ {
+ attach_failed_due_to_sip = true;
+ }
+
+ if (attach_failed_due_to_sip == false)
+ {
+ int csops_flags = 0;
+ int retval = ::csops (pid_attaching_to, CS_OPS_STATUS, &csops_flags, sizeof (csops_flags));
+ if (retval != -1 && (csops_flags & CS_RESTRICT))
+ {
+ attach_failed_due_to_sip = true;
+ }
+ }
+ if (attach_failed_due_to_sip)
+ {
+ SendPacket ("E87"); // E87 is the magic value which says that we are not allowed to attach
+ DNBLogError ("Attach failed because process does not allow attaching: \"%s\".", err_str);
+ return rnb_err;
+ }
+ }
+ }
+
+#endif
+
+ SendPacket ("E01"); // E01 is our magic error value for attach failed.
+ DNBLogError ("Attach failed: \"%s\".", err_str);
+ return rnb_err;
+ }
+ }
+
+ // All other failures come through here
+ return HandlePacket_UNIMPLEMENTED(p);
+}
+
+/* 'T XX' -- status of thread
+ Check if the specified thread is alive.
+ The thread number is in hex? */
+
+rnb_err_t
+RNBRemote::HandlePacket_T (const char *p)
+{
+ p++;
+ if (p == NULL || *p == '\0')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in T packet");
+ }
+ if (!m_ctx.HasValidProcessID())
+ {
+ return SendPacket ("E15");
+ }
+ errno = 0;
+ nub_thread_t tid = strtoul (p, NULL, 16);
+ if (errno != 0 && tid == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse thread number in T packet");
+ }
+
+ nub_state_t state = DNBThreadGetState (m_ctx.ProcessID(), tid);
+ if (state == eStateInvalid || state == eStateExited || state == eStateCrashed)
+ {
+ return SendPacket ("E16");
+ }
+
+ return SendPacket ("OK");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_z (const char *p)
+{
+ if (p == NULL || *p == '\0')
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in z packet");
+
+ if (!m_ctx.HasValidProcessID())
+ return SendPacket ("E15");
+
+ char packet_cmd = *p++;
+ char break_type = *p++;
+
+ if (*p++ != ',')
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma separator missing in z packet");
+
+ char *c = NULL;
+ nub_process_t pid = m_ctx.ProcessID();
+ errno = 0;
+ nub_addr_t addr = strtoull (p, &c, 16);
+ if (errno != 0 && addr == 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in z packet");
+ p = c;
+ if (*p++ != ',')
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma separator missing in z packet");
+
+ errno = 0;
+ auto byte_size = strtoul (p, &c, 16);
+ if (errno != 0 && byte_size == 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in z packet");
+
+ if (packet_cmd == 'Z')
+ {
+ // set
+ switch (break_type)
+ {
+ case '0': // set software breakpoint
+ case '1': // set hardware breakpoint
+ {
+ // gdb can send multiple Z packets for the same address and
+ // these calls must be ref counted.
+ bool hardware = (break_type == '1');
+
+ if (DNBBreakpointSet (pid, addr, byte_size, hardware))
+ {
+ // We successfully created a breakpoint, now lets full out
+ // a ref count structure with the breakID and add it to our
+ // map.
+ return SendPacket ("OK");
+ }
+ else
+ {
+ // We failed to set the software breakpoint
+ return SendPacket ("E09");
+ }
+ }
+ break;
+
+ case '2': // set write watchpoint
+ case '3': // set read watchpoint
+ case '4': // set access watchpoint
+ {
+ bool hardware = true;
+ uint32_t watch_flags = 0;
+ if (break_type == '2')
+ watch_flags = WATCH_TYPE_WRITE;
+ else if (break_type == '3')
+ watch_flags = WATCH_TYPE_READ;
+ else
+ watch_flags = WATCH_TYPE_READ | WATCH_TYPE_WRITE;
+
+ if (DNBWatchpointSet (pid, addr, byte_size, watch_flags, hardware))
+ {
+ return SendPacket ("OK");
+ }
+ else
+ {
+ // We failed to set the watchpoint
+ return SendPacket ("E09");
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (packet_cmd == 'z')
+ {
+ // remove
+ switch (break_type)
+ {
+ case '0': // remove software breakpoint
+ case '1': // remove hardware breakpoint
+ if (DNBBreakpointClear (pid, addr))
+ {
+ return SendPacket ("OK");
+ }
+ else
+ {
+ return SendPacket ("E08");
+ }
+ break;
+
+ case '2': // remove write watchpoint
+ case '3': // remove read watchpoint
+ case '4': // remove access watchpoint
+ if (DNBWatchpointClear (pid, addr))
+ {
+ return SendPacket ("OK");
+ }
+ else
+ {
+ return SendPacket ("E08");
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return HandlePacket_UNIMPLEMENTED(p);
+}
+
+// Extract the thread number from the thread suffix that might be appended to
+// thread specific packets. This will only be enabled if m_thread_suffix_supported
+// is true.
+nub_thread_t
+RNBRemote::ExtractThreadIDFromThreadSuffix (const char *p)
+{
+ if (m_thread_suffix_supported)
+ {
+ nub_thread_t tid = INVALID_NUB_THREAD;
+ if (p)
+ {
+ const char *tid_cstr = strstr (p, "thread:");
+ if (tid_cstr)
+ {
+ tid_cstr += strlen ("thread:");
+ tid = strtoul(tid_cstr, NULL, 16);
+ }
+ }
+ return tid;
+ }
+ return GetCurrentThread();
+
+}
+
+/* 'p XX'
+ print the contents of register X */
+
+rnb_err_t
+RNBRemote::HandlePacket_p (const char *p)
+{
+ if (g_num_reg_entries == 0)
+ InitializeRegisters ();
+
+ if (p == NULL || *p == '\0')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet");
+ }
+ if (!m_ctx.HasValidProcessID())
+ {
+ return SendPacket ("E15");
+ }
+ nub_process_t pid = m_ctx.ProcessID();
+ errno = 0;
+ char *tid_cstr = NULL;
+ uint32_t reg = static_cast<uint32_t>(strtoul (p + 1, &tid_cstr, 16));
+ if (errno != 0 && reg == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse register number in p packet");
+ }
+
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix (tid_cstr);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet");
+
+ const register_map_entry_t *reg_entry;
+
+ if (reg < g_num_reg_entries)
+ reg_entry = &g_reg_entries[reg];
+ else
+ reg_entry = NULL;
+
+ std::ostringstream ostrm;
+ if (reg_entry == NULL)
+ {
+ DNBLogError("RNBRemote::HandlePacket_p(%s): unknown register number %u requested\n", p, reg);
+ ostrm << "00000000";
+ }
+ else if (reg_entry->nub_info.reg == (uint32_t)-1)
+ {
+ if (reg_entry->nub_info.size > 0)
+ {
+ std::basic_string<uint8_t> zeros(reg_entry->nub_info.size, '\0');
+ append_hex_value(ostrm, zeros.data(), zeros.size(), false);
+ }
+ }
+ else
+ {
+ register_value_in_hex_fixed_width (ostrm, pid, tid, reg_entry, NULL);
+ }
+ return SendPacket (ostrm.str());
+}
+
+/* 'Pnn=rrrrr'
+ Set register number n to value r.
+ n and r are hex strings. */
+
+rnb_err_t
+RNBRemote::HandlePacket_P (const char *p)
+{
+ if (g_num_reg_entries == 0)
+ InitializeRegisters ();
+
+ if (p == NULL || *p == '\0')
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Empty P packet");
+ }
+ if (!m_ctx.HasValidProcessID())
+ {
+ return SendPacket ("E28");
+ }
+
+ nub_process_t pid = m_ctx.ProcessID();
+
+ StringExtractor packet (p);
+
+ const char cmd_char = packet.GetChar();
+ // Register ID is always in big endian
+ const uint32_t reg = packet.GetHexMaxU32 (false, UINT32_MAX);
+ const char equal_char = packet.GetChar();
+
+ if (cmd_char != 'P')
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Improperly formed P packet");
+
+ if (reg == UINT32_MAX)
+ return SendPacket ("E29");
+
+ if (equal_char != '=')
+ return SendPacket ("E30");
+
+ const register_map_entry_t *reg_entry;
+
+ if (reg >= g_num_reg_entries)
+ return SendPacket("E47");
+
+ reg_entry = &g_reg_entries[reg];
+
+ if (reg_entry->nub_info.set == (uint32_t)-1 && reg_entry->nub_info.reg == (uint32_t)-1)
+ {
+ DNBLogError("RNBRemote::HandlePacket_P(%s): unknown register number %u requested\n", p, reg);
+ return SendPacket("E48");
+ }
+
+ DNBRegisterValue reg_value;
+ reg_value.info = reg_entry->nub_info;
+ packet.GetHexBytes (reg_value.value.v_sint8, reg_entry->nub_info.size, 0xcc);
+
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet");
+
+ if (!DNBThreadSetRegisterValueByID (pid, tid, reg_entry->nub_info.set, reg_entry->nub_info.reg, &reg_value))
+ {
+ return SendPacket ("E32");
+ }
+ return SendPacket ("OK");
+}
+
+/* 'c [addr]'
+ Continue, optionally from a specified address. */
+
+rnb_err_t
+RNBRemote::HandlePacket_c (const char *p)
+{
+ const nub_process_t pid = m_ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("E23");
+
+ DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS };
+
+ if (*(p + 1) != '\0')
+ {
+ action.tid = GetContinueThread();
+ errno = 0;
+ action.addr = strtoull (p + 1, NULL, 16);
+ if (errno != 0 && action.addr == 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse address in c packet");
+ }
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.Append(action);
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
+ if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+ return SendPacket ("E25");
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_MemoryRegionInfo (const char *p)
+{
+ /* This packet will find memory attributes (e.g. readable, writable, executable, stack, jitted code)
+ for the memory region containing a given address and return that information.
+
+ Users of this packet must be prepared for three results:
+
+ Region information is returned
+ Region information is unavailable for this address because the address is in unmapped memory
+ Region lookup cannot be performed on this platform or process is not yet launched
+ This packet isn't implemented
+
+ Examples of use:
+ qMemoryRegionInfo:3a55140
+ start:3a50000,size:100000,permissions:rwx
+
+ qMemoryRegionInfo:0
+ error:address in unmapped region
+
+ qMemoryRegionInfo:3a551140 (on a different platform)
+ error:region lookup cannot be performed
+
+ qMemoryRegionInfo
+ OK // this packet is implemented by the remote nub
+ */
+
+ p += sizeof ("qMemoryRegionInfo") - 1;
+ if (*p == '\0')
+ return SendPacket ("OK");
+ if (*p++ != ':')
+ return SendPacket ("E67");
+ if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X'))
+ p += 2;
+
+ errno = 0;
+ uint64_t address = strtoul (p, NULL, 16);
+ if (errno != 0 && address == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in qMemoryRegionInfo packet");
+ }
+
+ DNBRegionInfo region_info = { 0, 0, 0 };
+ DNBProcessMemoryRegionInfo (m_ctx.ProcessID(), address, &region_info);
+ std::ostringstream ostrm;
+
+ // start:3a50000,size:100000,permissions:rwx
+ ostrm << "start:" << std::hex << region_info.addr << ';';
+
+ if (region_info.size > 0)
+ ostrm << "size:" << std::hex << region_info.size << ';';
+
+ if (region_info.permissions)
+ {
+ ostrm << "permissions:";
+
+ if (region_info.permissions & eMemoryPermissionsReadable)
+ ostrm << 'r';
+ if (region_info.permissions & eMemoryPermissionsWritable)
+ ostrm << 'w';
+ if (region_info.permissions & eMemoryPermissionsExecutable)
+ ostrm << 'x';
+ ostrm << ';';
+ }
+ return SendPacket (ostrm.str());
+}
+
+// qGetProfileData;scan_type:0xYYYYYYY
+rnb_err_t
+RNBRemote::HandlePacket_GetProfileData (const char *p)
+{
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("OK");
+
+ StringExtractor packet(p += sizeof ("qGetProfileData"));
+ DNBProfileDataScanType scan_type = eProfileAll;
+ std::string name;
+ std::string value;
+ while (packet.GetNameColonValue(name, value))
+ {
+ if (name.compare ("scan_type") == 0)
+ {
+ std::istringstream iss(value);
+ uint32_t int_value = 0;
+ if (iss >> std::hex >> int_value)
+ {
+ scan_type = (DNBProfileDataScanType)int_value;
+ }
+ }
+ }
+
+ std::string data = DNBProcessGetProfileData(pid, scan_type);
+ if (!data.empty())
+ {
+ return SendPacket (data.c_str());
+ }
+ else
+ {
+ return SendPacket ("OK");
+ }
+}
+
+// QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;scan_type:0xYYYYYYY
+rnb_err_t
+RNBRemote::HandlePacket_SetEnableAsyncProfiling (const char *p)
+{
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("OK");
+
+ StringExtractor packet(p += sizeof ("QSetEnableAsyncProfiling"));
+ bool enable = false;
+ uint64_t interval_usec = 0;
+ DNBProfileDataScanType scan_type = eProfileAll;
+ std::string name;
+ std::string value;
+ while (packet.GetNameColonValue(name, value))
+ {
+ if (name.compare ("enable") == 0)
+ {
+ enable = strtoul(value.c_str(), NULL, 10) > 0;
+ }
+ else if (name.compare ("interval_usec") == 0)
+ {
+ interval_usec = strtoul(value.c_str(), NULL, 10);
+ }
+ else if (name.compare ("scan_type") == 0)
+ {
+ std::istringstream iss(value);
+ uint32_t int_value = 0;
+ if (iss >> std::hex >> int_value)
+ {
+ scan_type = (DNBProfileDataScanType)int_value;
+ }
+ }
+ }
+
+ if (interval_usec == 0)
+ {
+ enable = 0;
+ }
+
+ DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec, scan_type);
+ return SendPacket ("OK");
+}
+
+// QEnableCompression:type:<COMPRESSION-TYPE>;minsize:<MINIMUM PACKET SIZE TO COMPRESS>;
+//
+// type: must be a type previously reported by the qXfer:features: SupportedCompressions list
+//
+// minsize: is optional; by default the qXfer:features: DefaultCompressionMinSize value is used
+// debugserver may have a better idea of what a good minimum packet size to compress is than lldb.
+
+rnb_err_t
+RNBRemote::HandlePacket_QEnableCompression (const char *p)
+{
+ p += sizeof ("QEnableCompression:") - 1;
+
+ size_t new_compression_minsize = m_compression_minsize;
+ const char *new_compression_minsize_str = strstr (p, "minsize:");
+ if (new_compression_minsize_str)
+ {
+ new_compression_minsize_str += strlen ("minsize:");
+ errno = 0;
+ new_compression_minsize = strtoul (new_compression_minsize_str, NULL, 10);
+ if (errno != 0 || new_compression_minsize == ULONG_MAX)
+ {
+ new_compression_minsize = m_compression_minsize;
+ }
+ }
+
+#if defined (HAVE_LIBCOMPRESSION)
+ if (compression_decode_buffer != NULL)
+ {
+ if (strstr (p, "type:zlib-deflate;") != nullptr)
+ {
+ EnableCompressionNextSendPacket (compression_types::zlib_deflate);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket ("OK");
+ }
+ else if (strstr (p, "type:lz4;") != nullptr)
+ {
+ EnableCompressionNextSendPacket (compression_types::lz4);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket ("OK");
+ }
+ else if (strstr (p, "type:lzma;") != nullptr)
+ {
+ EnableCompressionNextSendPacket (compression_types::lzma);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket ("OK");
+ }
+ else if (strstr (p, "type:lzfse;") != nullptr)
+ {
+ EnableCompressionNextSendPacket (compression_types::lzfse);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket ("OK");
+ }
+ }
+#endif
+
+#if defined (HAVE_LIBZ)
+ if (strstr (p, "type:zlib-deflate;") != nullptr)
+ {
+ EnableCompressionNextSendPacket (compression_types::zlib_deflate);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket ("OK");
+ }
+#endif
+
+ return SendPacket ("E88");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qSpeedTest (const char *p)
+{
+ p += strlen ("qSpeedTest:response_size:");
+ char *end = NULL;
+ errno = 0;
+ uint64_t response_size = ::strtoul (p, &end, 16);
+ if (errno != 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Didn't find response_size value at right offset");
+ else if (*end == ';')
+ {
+ static char g_data[4*1024*1024+16] = "data:";
+ memset(g_data + 5, 'a', response_size);
+ g_data[response_size + 5] = '\0';
+ return SendPacket (g_data);
+ }
+ else
+ {
+ return SendPacket ("E79");
+ }
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_WatchpointSupportInfo (const char *p)
+{
+ /* This packet simply returns the number of supported hardware watchpoints.
+
+ Examples of use:
+ qWatchpointSupportInfo:
+ num:4
+
+ qWatchpointSupportInfo
+ OK // this packet is implemented by the remote nub
+ */
+
+ p += sizeof ("qWatchpointSupportInfo") - 1;
+ if (*p == '\0')
+ return SendPacket ("OK");
+ if (*p++ != ':')
+ return SendPacket ("E67");
+
+ errno = 0;
+ uint32_t num = DNBWatchpointGetNumSupportedHWP (m_ctx.ProcessID());
+ std::ostringstream ostrm;
+
+ // size:4
+ ostrm << "num:" << std::dec << num << ';';
+ return SendPacket (ostrm.str());
+}
+
+/* 'C sig [;addr]'
+ Resume with signal sig, optionally at address addr. */
+
+rnb_err_t
+RNBRemote::HandlePacket_C (const char *p)
+{
+ const nub_process_t pid = m_ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("E36");
+
+ DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS };
+ int process_signo = -1;
+ if (*(p + 1) != '\0')
+ {
+ action.tid = GetContinueThread();
+ char *end = NULL;
+ errno = 0;
+ process_signo = static_cast<int>(strtoul (p + 1, &end, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse signal in C packet");
+ else if (*end == ';')
+ {
+ errno = 0;
+ action.addr = strtoull (end + 1, NULL, 16);
+ if (errno != 0 && action.addr == 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse address in C packet");
+ }
+ }
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.Append (action);
+ thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, action.signal);
+ if (!DNBProcessSignal(pid, process_signo))
+ return SendPacket ("E52");
+ if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+ return SendPacket ("E38");
+ /* Don't send an "OK" packet; response is the stopped/exited message. */
+ return rnb_success;
+}
+
+//----------------------------------------------------------------------
+// 'D' packet
+// Detach from gdb.
+//----------------------------------------------------------------------
+rnb_err_t
+RNBRemote::HandlePacket_D (const char *p)
+{
+ if (m_ctx.HasValidProcessID())
+ {
+ if (DNBProcessDetach(m_ctx.ProcessID()))
+ SendPacket ("OK");
+ else
+ SendPacket ("E");
+ }
+ else
+ {
+ SendPacket ("E");
+ }
+ return rnb_success;
+}
+
+/* 'k'
+ Kill the inferior process. */
+
+rnb_err_t
+RNBRemote::HandlePacket_k (const char *p)
+{
+ DNBLog ("Got a 'k' packet, killing the inferior process.");
+ // No response to should be sent to the kill packet
+ if (m_ctx.HasValidProcessID())
+ DNBProcessKill (m_ctx.ProcessID());
+ SendPacket ("X09");
+ return rnb_success;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_stop_process (const char *p)
+{
+//#define TEST_EXIT_ON_INTERRUPT // This should only be uncommented to test exiting on interrupt
+#if defined(TEST_EXIT_ON_INTERRUPT)
+ rnb_err_t err = HandlePacket_k (p);
+ m_comm.Disconnect(true);
+ return err;
+#else
+ if (!DNBProcessInterrupt(m_ctx.ProcessID()))
+ {
+ // If we failed to interrupt the process, then send a stop
+ // reply packet as the process was probably already stopped
+ HandlePacket_last_signal (NULL);
+ }
+ return rnb_success;
+#endif
+}
+
+/* 's'
+ Step the inferior process. */
+
+rnb_err_t
+RNBRemote::HandlePacket_s (const char *p)
+{
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("E32");
+
+ // Hardware supported stepping not supported on arm
+ nub_thread_t tid = GetContinueThread ();
+ if (tid == 0 || tid == (nub_thread_t)-1)
+ tid = GetCurrentThread();
+
+ if (tid == INVALID_NUB_THREAD)
+ return SendPacket ("E33");
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.AppendAction(tid, eStateStepping);
+
+ // Make all other threads stop when we are stepping
+ thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0);
+ if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+ return SendPacket ("E49");
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return rnb_success;
+}
+
+/* 'S sig [;addr]'
+ Step with signal sig, optionally at address addr. */
+
+rnb_err_t
+RNBRemote::HandlePacket_S (const char *p)
+{
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket ("E36");
+
+ DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateStepping, 0, INVALID_NUB_ADDRESS };
+
+ if (*(p + 1) != '\0')
+ {
+ char *end = NULL;
+ errno = 0;
+ action.signal = static_cast<int>(strtoul (p + 1, &end, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse signal in S packet");
+ else if (*end == ';')
+ {
+ errno = 0;
+ action.addr = strtoull (end + 1, NULL, 16);
+ if (errno != 0 && action.addr == 0)
+ {
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse address in S packet");
+ }
+ }
+ }
+
+ action.tid = GetContinueThread ();
+ if (action.tid == 0 || action.tid == (nub_thread_t)-1)
+ return SendPacket ("E40");
+
+ nub_state_t tstate = DNBThreadGetState (pid, action.tid);
+ if (tstate == eStateInvalid || tstate == eStateExited)
+ return SendPacket ("E37");
+
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.Append (action);
+
+ // Make all other threads stop when we are stepping
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+ if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize()))
+ return SendPacket ("E39");
+
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return rnb_success;
+}
+
+static const char *
+GetArchName (const uint32_t cputype, const uint32_t cpusubtype)
+{
+ switch (cputype)
+ {
+ case CPU_TYPE_ARM:
+ switch (cpusubtype)
+ {
+ case 5: return "armv4";
+ case 6: return "armv6";
+ case 7: return "armv5t";
+ case 8: return "xscale";
+ case 9: return "armv7";
+ case 10: return "armv7f";
+ case 11: return "armv7s";
+ case 12: return "armv7k";
+ case 14: return "armv6m";
+ case 15: return "armv7m";
+ case 16: return "armv7em";
+ default: return "arm";
+ }
+ break;
+ case CPU_TYPE_ARM64: return "arm64";
+ case CPU_TYPE_I386: return "i386";
+ case CPU_TYPE_X86_64:
+ switch (cpusubtype)
+ {
+ default: return "x86_64";
+ case 8: return "x86_64h";
+ }
+ break;
+ }
+ return NULL;
+}
+
+static bool
+GetHostCPUType (uint32_t &cputype, uint32_t &cpusubtype, uint32_t &is_64_bit_capable, bool &promoted_to_64)
+{
+ static uint32_t g_host_cputype = 0;
+ static uint32_t g_host_cpusubtype = 0;
+ static uint32_t g_is_64_bit_capable = 0;
+ static bool g_promoted_to_64 = false;
+
+ if (g_host_cputype == 0)
+ {
+ g_promoted_to_64 = false;
+ size_t len = sizeof(uint32_t);
+ if (::sysctlbyname("hw.cputype", &g_host_cputype, &len, NULL, 0) == 0)
+ {
+ len = sizeof (uint32_t);
+ if (::sysctlbyname("hw.cpu64bit_capable", &g_is_64_bit_capable, &len, NULL, 0) == 0)
+ {
+ if (g_is_64_bit_capable && ((g_host_cputype & CPU_ARCH_ABI64) == 0))
+ {
+ g_promoted_to_64 = true;
+ g_host_cputype |= CPU_ARCH_ABI64;
+ }
+ }
+ }
+
+ len = sizeof(uint32_t);
+ if (::sysctlbyname("hw.cpusubtype", &g_host_cpusubtype, &len, NULL, 0) == 0)
+ {
+ if (g_promoted_to_64 &&
+ g_host_cputype == CPU_TYPE_X86_64 && g_host_cpusubtype == CPU_SUBTYPE_486)
+ g_host_cpusubtype = CPU_SUBTYPE_X86_64_ALL;
+ }
+ }
+
+ cputype = g_host_cputype;
+ cpusubtype = g_host_cpusubtype;
+ is_64_bit_capable = g_is_64_bit_capable;
+ promoted_to_64 = g_promoted_to_64;
+ return g_host_cputype != 0;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qHostInfo (const char *p)
+{
+ std::ostringstream strm;
+
+ uint32_t cputype = 0;
+ uint32_t cpusubtype = 0;
+ uint32_t is_64_bit_capable = 0;
+ bool promoted_to_64 = false;
+ if (GetHostCPUType (cputype, cpusubtype, is_64_bit_capable, promoted_to_64))
+ {
+ strm << "cputype:" << std::dec << cputype << ';';
+ strm << "cpusubtype:" << std::dec << cpusubtype << ';';
+ }
+
+ // The OS in the triple should be "ios" or "macosx" which doesn't match our
+ // "Darwin" which gets returned from "kern.ostype", so we need to hardcode
+ // this for now.
+ if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64)
+ {
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+ strm << "ostype:tvos;";
+#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ strm << "ostype:watchos;";
+#else
+ strm << "ostype:ios;";
+#endif
+
+ // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.
+ strm << "watchpoint_exceptions_received:before;";
+ }
+ else
+ {
+ strm << "ostype:macosx;";
+ strm << "watchpoint_exceptions_received:after;";
+ }
+// char ostype[64];
+// len = sizeof(ostype);
+// if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0)
+// {
+// len = strlen(ostype);
+// std::transform (ostype, ostype + len, ostype, tolower);
+// strm << "ostype:" << std::dec << ostype << ';';
+// }
+
+ strm << "vendor:apple;";
+
+ uint64_t major, minor, patch;
+ if (DNBGetOSVersionNumbers (&major, &minor, &patch))
+ {
+ strm << "osmajor:" << major << ";";
+ strm << "osminor:" << minor << ";";
+ strm << "ospatch:" << patch << ";";
+
+ strm << "version:" << major << "." << minor;
+ if (patch != 0)
+ {
+ strm << "." << patch;
+ }
+ strm << ";";
+ }
+
+#if defined (__LITTLE_ENDIAN__)
+ strm << "endian:little;";
+#elif defined (__BIG_ENDIAN__)
+ strm << "endian:big;";
+#elif defined (__PDP_ENDIAN__)
+ strm << "endian:pdp;";
+#endif
+
+ if (promoted_to_64)
+ strm << "ptrsize:8;";
+ else
+ strm << "ptrsize:" << std::dec << sizeof(void *) << ';';
+
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ strm << "default_packet_timeout:10;";
+#endif
+
+ return SendPacket (strm.str());
+}
+
+void
+XMLElementStart (std::ostringstream &s, uint32_t indent, const char *name, bool has_attributes)
+{
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+ s << '<' << name;
+ if (!has_attributes)
+ s << '>' << std::endl;
+}
+
+void
+XMLElementStartEndAttributes (std::ostringstream &s, bool empty)
+{
+ if (empty)
+ s << '/';
+ s << '>' << std::endl;
+}
+
+void
+XMLElementEnd (std::ostringstream &s, uint32_t indent, const char *name)
+{
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+ s << '<' << '/' << name << '>' << std::endl;
+}
+
+void
+XMLElementWithStringValue (std::ostringstream &s, uint32_t indent, const char *name, const char *value, bool close = true)
+{
+ if (value)
+ {
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+ s << '<' << name << '>' << value;
+ if (close)
+ XMLElementEnd(s, 0, name);
+ }
+}
+
+void
+XMLElementWithUnsignedValue (std::ostringstream &s, uint32_t indent, const char *name, uint64_t value, bool close = true)
+{
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+
+ s << '<' << name << '>' << DECIMAL << value;
+ if (close)
+ XMLElementEnd(s, 0, name);
+}
+
+void
+XMLAttributeString (std::ostringstream &s, const char *name, const char *value, const char *default_value = NULL)
+{
+ if (value)
+ {
+ if (default_value && strcmp(value, default_value) == 0)
+ return; // No need to emit the attribute because it matches the default value
+ s <<' ' << name << "=\"" << value << "\"";
+ }
+}
+
+void
+XMLAttributeUnsignedDecimal (std::ostringstream &s, const char *name, uint64_t value)
+{
+ s <<' ' << name << "=\"" << DECIMAL << value << "\"";
+}
+
+void
+GenerateTargetXMLRegister (std::ostringstream &s,
+ const uint32_t reg_num,
+ nub_size_t num_reg_sets,
+ const DNBRegisterSetInfo *reg_set_info,
+ const register_map_entry_t &reg)
+{
+ const char *default_lldb_encoding = "uint";
+ const char *lldb_encoding = default_lldb_encoding;
+ const char *gdb_group = "general";
+ const char *default_gdb_type = "int";
+ const char *gdb_type = default_gdb_type;
+ const char *default_lldb_format = "hex";
+ const char *lldb_format = default_lldb_format;
+ const char *lldb_set = NULL;
+
+ switch (reg.nub_info.type)
+ {
+ case Uint: lldb_encoding = "uint"; break;
+ case Sint: lldb_encoding = "sint"; break;
+ case IEEE754: lldb_encoding = "ieee754"; if (reg.nub_info.set > 0) gdb_group = "float"; break;
+ case Vector: lldb_encoding = "vector"; if (reg.nub_info.set > 0) gdb_group = "vector"; break;
+ }
+
+ switch (reg.nub_info.format)
+ {
+ case Binary: lldb_format = "binary"; break;
+ case Decimal: lldb_format = "decimal"; break;
+ case Hex: lldb_format = "hex"; break;
+ case Float: gdb_type = "float"; lldb_format = "float"; break;
+ case VectorOfSInt8: gdb_type = "float"; lldb_format = "vector-sint8"; break;
+ case VectorOfUInt8: gdb_type = "float"; lldb_format = "vector-uint8"; break;
+ case VectorOfSInt16: gdb_type = "float"; lldb_format = "vector-sint16"; break;
+ case VectorOfUInt16: gdb_type = "float"; lldb_format = "vector-uint16"; break;
+ case VectorOfSInt32: gdb_type = "float"; lldb_format = "vector-sint32"; break;
+ case VectorOfUInt32: gdb_type = "float"; lldb_format = "vector-uint32"; break;
+ case VectorOfFloat32: gdb_type = "float"; lldb_format = "vector-float32"; break;
+ case VectorOfUInt128: gdb_type = "float"; lldb_format = "vector-uint128"; break;
+ };
+ if (reg_set_info && reg.nub_info.set < num_reg_sets)
+ lldb_set = reg_set_info[reg.nub_info.set].name;
+
+ uint32_t indent = 2;
+
+ XMLElementStart(s, indent, "reg", true);
+ XMLAttributeString(s, "name", reg.nub_info.name);
+ XMLAttributeUnsignedDecimal(s, "regnum", reg_num);
+ XMLAttributeUnsignedDecimal(s, "offset", reg.offset);
+ XMLAttributeUnsignedDecimal(s, "bitsize", reg.nub_info.size * 8);
+ XMLAttributeString(s, "group", gdb_group);
+ XMLAttributeString(s, "type", gdb_type, default_gdb_type);
+ XMLAttributeString (s, "altname", reg.nub_info.alt);
+ XMLAttributeString(s, "encoding", lldb_encoding, default_lldb_encoding);
+ XMLAttributeString(s, "format", lldb_format, default_lldb_format);
+ XMLAttributeUnsignedDecimal(s, "group_id", reg.nub_info.set);
+ if (reg.nub_info.reg_ehframe != INVALID_NUB_REGNUM)
+ XMLAttributeUnsignedDecimal(s, "ehframe_regnum", reg.nub_info.reg_ehframe);
+ if (reg.nub_info.reg_dwarf != INVALID_NUB_REGNUM)
+ XMLAttributeUnsignedDecimal(s, "dwarf_regnum", reg.nub_info.reg_dwarf);
+
+ const char *lldb_generic = NULL;
+ switch (reg.nub_info.reg_generic)
+ {
+ case GENERIC_REGNUM_FP: lldb_generic = "fp"; break;
+ case GENERIC_REGNUM_PC: lldb_generic = "pc"; break;
+ case GENERIC_REGNUM_SP: lldb_generic = "sp"; break;
+ case GENERIC_REGNUM_RA: lldb_generic = "ra"; break;
+ case GENERIC_REGNUM_FLAGS: lldb_generic = "flags"; break;
+ case GENERIC_REGNUM_ARG1: lldb_generic = "arg1"; break;
+ case GENERIC_REGNUM_ARG2: lldb_generic = "arg2"; break;
+ case GENERIC_REGNUM_ARG3: lldb_generic = "arg3"; break;
+ case GENERIC_REGNUM_ARG4: lldb_generic = "arg4"; break;
+ case GENERIC_REGNUM_ARG5: lldb_generic = "arg5"; break;
+ case GENERIC_REGNUM_ARG6: lldb_generic = "arg6"; break;
+ case GENERIC_REGNUM_ARG7: lldb_generic = "arg7"; break;
+ case GENERIC_REGNUM_ARG8: lldb_generic = "arg8"; break;
+ default: break;
+ }
+ XMLAttributeString(s, "generic", lldb_generic);
+
+
+ bool empty = reg.value_regnums.empty() && reg.invalidate_regnums.empty();
+ if (!empty)
+ {
+ if (!reg.value_regnums.empty())
+ {
+ std::ostringstream regnums;
+ bool first = true;
+ regnums << DECIMAL;
+ for (auto regnum : reg.value_regnums)
+ {
+ if (!first)
+ regnums << ',';
+ regnums << regnum;
+ first = false;
+ }
+ XMLAttributeString(s, "value_regnums", regnums.str().c_str());
+ }
+
+ if (!reg.invalidate_regnums.empty())
+ {
+ std::ostringstream regnums;
+ bool first = true;
+ regnums << DECIMAL;
+ for (auto regnum : reg.invalidate_regnums)
+ {
+ if (!first)
+ regnums << ',';
+ regnums << regnum;
+ first = false;
+ }
+ XMLAttributeString(s, "invalidate_regnums", regnums.str().c_str());
+ }
+ }
+ XMLElementStartEndAttributes(s, true);
+}
+
+void
+GenerateTargetXMLRegisters (std::ostringstream &s)
+{
+ nub_size_t num_reg_sets = 0;
+ const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo (&num_reg_sets);
+
+
+ uint32_t cputype = DNBGetRegisterCPUType();
+ if (cputype)
+ {
+ XMLElementStart(s, 0, "feature", true);
+ std::ostringstream name_strm;
+ name_strm << "com.apple.debugserver." << GetArchName (cputype, 0);
+ XMLAttributeString(s, "name", name_strm.str().c_str());
+ XMLElementStartEndAttributes(s, false);
+ for (uint32_t reg_num = 0; reg_num < g_num_reg_entries; ++reg_num)
+// for (const auto &reg: g_dynamic_register_map)
+ {
+ GenerateTargetXMLRegister(s, reg_num, num_reg_sets, reg_sets, g_reg_entries[reg_num]);
+ }
+ XMLElementEnd(s, 0, "feature");
+
+ if (num_reg_sets > 0)
+ {
+ XMLElementStart(s, 0, "groups", false);
+ for (uint32_t set=1; set<num_reg_sets; ++set)
+ {
+ XMLElementStart(s, 2, "group", true);
+ XMLAttributeUnsignedDecimal(s, "id", set);
+ XMLAttributeString(s, "name", reg_sets[set].name);
+ XMLElementStartEndAttributes(s, true);
+ }
+ XMLElementEnd(s, 0, "groups");
+ }
+ }
+}
+
+static const char *g_target_xml_header = R"(<?xml version="1.0"?>
+<target version="1.0">)";
+
+static const char *g_target_xml_footer = "</target>";
+
+static std::string g_target_xml;
+
+void
+UpdateTargetXML ()
+{
+ std::ostringstream s;
+ s << g_target_xml_header << std::endl;
+
+ // Set the architecture
+ //s << "<architecture>" << arch "</architecture>" << std::endl;
+
+ // Set the OSABI
+ //s << "<osabi>abi-name</osabi>"
+
+ GenerateTargetXMLRegisters(s);
+
+ s << g_target_xml_footer << std::endl;
+
+ // Save the XML output in case it gets retrieved in chunks
+ g_target_xml = s.str();
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qXfer (const char *command)
+{
+ const char *p = command;
+ p += strlen ("qXfer:");
+ const char *sep = strchr(p, ':');
+ if (sep)
+ {
+ std::string object(p, sep - p); // "auxv", "backtrace", "features", etc
+ p = sep + 1;
+ sep = strchr(p, ':');
+ if (sep)
+ {
+ std::string rw(p, sep - p); // "read" or "write"
+ p = sep + 1;
+ sep = strchr(p, ':');
+ if (sep)
+ {
+ std::string annex(p, sep - p); // "read" or "write"
+
+ p = sep + 1;
+ sep = strchr(p, ',');
+ if (sep)
+ {
+ std::string offset_str(p, sep - p); // read the length as a string
+ p = sep + 1;
+ std::string length_str(p); // read the offset as a string
+ char *end = nullptr;
+ const uint64_t offset = strtoul(offset_str.c_str(), &end, 16); // convert offset_str to a offset
+ if (*end == '\0')
+ {
+ const uint64_t length = strtoul(length_str.c_str(), &end, 16); // convert length_str to a length
+ if (*end == '\0')
+ {
+ if (object == "features" &&
+ rw == "read" &&
+ annex == "target.xml")
+ {
+ std::ostringstream xml_out;
+
+ if (offset == 0)
+ {
+ InitializeRegisters (true);
+
+ UpdateTargetXML();
+ if (g_target_xml.empty())
+ return SendPacket("E83");
+
+ if (length > g_target_xml.size())
+ {
+ xml_out << 'l'; // No more data
+ xml_out << binary_encode_string(g_target_xml);
+ }
+ else
+ {
+ xml_out << 'm'; // More data needs to be read with a subsequent call
+ xml_out << binary_encode_string(std::string(g_target_xml, offset, length));
+ }
+ }
+ else
+ {
+ // Retrieving target XML in chunks
+ if (offset < g_target_xml.size())
+ {
+ std::string chunk(g_target_xml, offset, length);
+ if (chunk.size() < length)
+ xml_out << 'l'; // No more data
+ else
+ xml_out << 'm'; // More data needs to be read with a subsequent call
+ xml_out << binary_encode_string(chunk.data());
+ }
+ }
+ return SendPacket(xml_out.str());
+ }
+ // Well formed, put not supported
+ return HandlePacket_UNIMPLEMENTED (command);
+ }
+ }
+ }
+ }
+ else
+ {
+ SendPacket ("E85");
+ }
+ }
+ else
+ {
+ SendPacket ("E86");
+ }
+ }
+ return SendPacket ("E82");
+}
+
+
+rnb_err_t
+RNBRemote::HandlePacket_qGDBServerVersion (const char *p)
+{
+ std::ostringstream strm;
+
+#if defined(DEBUGSERVER_PROGRAM_NAME)
+ strm << "name:" DEBUGSERVER_PROGRAM_NAME ";";
+#else
+ strm << "name:debugserver;";
+#endif
+ strm << "version:" << DEBUGSERVER_VERSION_NUM << ";";
+
+ return SendPacket (strm.str());
+}
+
+// A helper function that retrieves a single integer value from
+// a one-level-deep JSON dictionary of key-value pairs. e.g.
+// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}]
+//
+uint64_t
+get_integer_value_for_key_name_from_json (const char *key, const char *json_string)
+{
+ uint64_t retval = INVALID_NUB_ADDRESS;
+ std::string key_with_quotes = "\"";
+ key_with_quotes += key;
+ key_with_quotes += "\"";
+ const char *c = strstr (json_string, key_with_quotes.c_str());
+ if (c)
+ {
+ c += key_with_quotes.size();
+
+ while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ if (*c == ':')
+ {
+ c++;
+
+ while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ errno = 0;
+ retval = strtoul (c, NULL, 10);
+ if (errno != 0)
+ {
+ retval = INVALID_NUB_ADDRESS;
+ }
+ }
+ }
+ return retval;
+
+}
+
+JSONGenerator::ObjectSP
+RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only)
+{
+ JSONGenerator::ArraySP threads_array_sp;
+ if (m_ctx.HasValidProcessID())
+ {
+ threads_array_sp.reset(new JSONGenerator::Array());
+
+ nub_process_t pid = m_ctx.ProcessID();
+
+ nub_size_t numthreads = DNBProcessGetNumThreads (pid);
+ for (nub_size_t i = 0; i < numthreads; ++i)
+ {
+ nub_thread_t tid = DNBProcessGetThreadAtIndex (pid, i);
+
+ struct DNBThreadStopInfo tid_stop_info;
+
+ const bool stop_info_valid = DNBThreadGetStopReason (pid, tid, &tid_stop_info);
+
+ // If we are doing stop info only, then we only show threads that have a
+ // valid stop reason
+ if (threads_with_valid_stop_info_only)
+ {
+ if (!stop_info_valid || tid_stop_info.reason == eStopTypeInvalid)
+ continue;
+ }
+
+ JSONGenerator::DictionarySP thread_dict_sp(new JSONGenerator::Dictionary());
+ thread_dict_sp->AddIntegerItem("tid", tid);
+
+ std::string reason_value("none");
+
+ if (stop_info_valid)
+ {
+ switch (tid_stop_info.reason)
+ {
+ case eStopTypeInvalid:
+ break;
+
+ case eStopTypeSignal:
+ if (tid_stop_info.details.signal.signo != 0)
+ {
+ thread_dict_sp->AddIntegerItem("signal", tid_stop_info.details.signal.signo);
+ reason_value = "signal";
+ }
+ break;
+
+ case eStopTypeException:
+ if (tid_stop_info.details.exception.type != 0)
+ {
+ reason_value = "exception";
+ thread_dict_sp->AddIntegerItem("metype", tid_stop_info.details.exception.type);
+ JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array());
+ for (nub_size_t i=0; i<tid_stop_info.details.exception.data_count; ++i)
+ {
+ medata_array_sp->AddItem(JSONGenerator::IntegerSP(new JSONGenerator::Integer(tid_stop_info.details.exception.data[i])));
+ }
+ thread_dict_sp->AddItem("medata", medata_array_sp);
+ }
+ break;
+
+ case eStopTypeExec:
+ reason_value = "exec";
+ break;
+ }
+ }
+
+ thread_dict_sp->AddStringItem("reason", reason_value);
+
+ if (threads_with_valid_stop_info_only == false)
+ {
+ const char *thread_name = DNBThreadGetName (pid, tid);
+ if (thread_name && thread_name[0])
+ thread_dict_sp->AddStringItem("name", thread_name);
+
+ thread_identifier_info_data_t thread_ident_info;
+ if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info))
+ {
+ if (thread_ident_info.dispatch_qaddr != 0)
+ {
+ thread_dict_sp->AddIntegerItem("qaddr", thread_ident_info.dispatch_qaddr);
+
+ const DispatchQueueOffsets *dispatch_queue_offsets = GetDispatchQueueOffsets();
+ if (dispatch_queue_offsets)
+ {
+ std::string queue_name;
+ uint64_t queue_width = 0;
+ uint64_t queue_serialnum = 0;
+ dispatch_queue_offsets->GetThreadQueueInfo(pid, thread_ident_info.dispatch_qaddr, queue_name, queue_width, queue_serialnum);
+ if (!queue_name.empty())
+ thread_dict_sp->AddStringItem("qname", queue_name);
+ if (queue_width == 1)
+ thread_dict_sp->AddStringItem("qkind", "serial");
+ else if (queue_width > 1)
+ thread_dict_sp->AddStringItem("qkind", "concurrent");
+ if (queue_serialnum > 0)
+ thread_dict_sp->AddIntegerItem("qserial", queue_serialnum);
+ }
+ }
+ }
+
+ DNBRegisterValue reg_value;
+
+ if (g_reg_entries != NULL)
+ {
+ JSONGenerator::DictionarySP registers_dict_sp(new JSONGenerator::Dictionary());
+
+ for (uint32_t reg = 0; reg < g_num_reg_entries; reg++)
+ {
+ // Expedite all registers in the first register set that aren't
+ // contained in other registers
+ if (g_reg_entries[reg].nub_info.set == 1 &&
+ g_reg_entries[reg].nub_info.value_regs == NULL)
+ {
+ if (!DNBThreadGetRegisterValueByID (pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, &reg_value))
+ continue;
+
+ std::ostringstream reg_num;
+ reg_num << std::dec << g_reg_entries[reg].debugserver_regnum;
+ // Encode native byte ordered bytes as hex ascii
+ registers_dict_sp->AddBytesAsHexASCIIString(reg_num.str(), reg_value.value.v_uint8, g_reg_entries[reg].nub_info.size);
+ }
+ }
+ thread_dict_sp->AddItem("registers", registers_dict_sp);
+ }
+
+ // Add expedited stack memory so stack backtracing doesn't need to read anything from the
+ // frame pointer chain.
+ StackMemoryMap stack_mmap;
+ ReadStackMemory (pid, tid, stack_mmap);
+ if (!stack_mmap.empty())
+ {
+ JSONGenerator::ArraySP memory_array_sp(new JSONGenerator::Array());
+
+ for (const auto &stack_memory : stack_mmap)
+ {
+ JSONGenerator::DictionarySP stack_memory_sp(new JSONGenerator::Dictionary());
+ stack_memory_sp->AddIntegerItem("address", stack_memory.first);
+ stack_memory_sp->AddBytesAsHexASCIIString("bytes", stack_memory.second.bytes, stack_memory.second.length);
+ memory_array_sp->AddItem(stack_memory_sp);
+ }
+ thread_dict_sp->AddItem("memory", memory_array_sp);
+ }
+ }
+
+ threads_array_sp->AddItem(thread_dict_sp);
+ }
+ }
+ return threads_array_sp;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_jThreadsInfo (const char *p)
+{
+ JSONGenerator::ObjectSP threads_info_sp;
+ std::ostringstream json;
+ std::ostringstream reply_strm;
+ // If we haven't run the process yet, return an error.
+ if (m_ctx.HasValidProcessID())
+ {
+ const bool threads_with_valid_stop_info_only = false;
+ JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only);
+
+ if (threads_info_sp)
+ {
+ std::ostringstream strm;
+ threads_info_sp->Dump (strm);
+ std::string binary_packet = binary_encode_string (strm.str());
+ if (!binary_packet.empty())
+ return SendPacket (binary_packet.c_str());
+ }
+ }
+ return SendPacket ("E85");
+
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p)
+{
+ nub_process_t pid;
+ std::ostringstream json;
+ // If we haven't run the process yet, return an error.
+ if (!m_ctx.HasValidProcessID())
+ {
+ return SendPacket ("E81");
+ }
+
+ pid = m_ctx.ProcessID();
+
+ const char thread_extended_info_str[] = { "jThreadExtendedInfo:{" };
+ if (strncmp (p, thread_extended_info_str, sizeof (thread_extended_info_str) - 1) == 0)
+ {
+ p += strlen (thread_extended_info_str);
+
+ uint64_t tid = get_integer_value_for_key_name_from_json ("thread", p);
+ uint64_t plo_pthread_tsd_base_address_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_address_offset", p);
+ uint64_t plo_pthread_tsd_base_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_offset", p);
+ uint64_t plo_pthread_tsd_entry_size = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_entry_size", p);
+ uint64_t dti_qos_class_index = get_integer_value_for_key_name_from_json ("dti_qos_class_index", p);
+ // Commented out the two variables below as they are not being used
+// uint64_t dti_queue_index = get_integer_value_for_key_name_from_json ("dti_queue_index", p);
+// uint64_t dti_voucher_index = get_integer_value_for_key_name_from_json ("dti_voucher_index", p);
+
+ if (tid != INVALID_NUB_ADDRESS)
+ {
+ nub_addr_t pthread_t_value = DNBGetPThreadT (pid, tid);
+
+ uint64_t tsd_address = INVALID_NUB_ADDRESS;
+ if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS
+ && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS
+ && plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS)
+ {
+ tsd_address = DNBGetTSDAddressForThread (pid, tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
+ }
+
+ bool timed_out = false;
+ Genealogy::ThreadActivitySP thread_activity_sp;
+
+ // If the pthread_t value is invalid, or if we were able to fetch the thread's TSD base
+ // and got an invalid value back, then we have a thread in early startup or shutdown and
+ // it's possible that gathering the genealogy information for this thread go badly.
+ // Ideally fetching this info for a thread in these odd states shouldn't matter - but
+ // we've seen some problems with these new SPI and threads in edge-casey states.
+
+ double genealogy_fetch_time = 0;
+ if (pthread_t_value != INVALID_NUB_ADDRESS && tsd_address != INVALID_NUB_ADDRESS)
+ {
+ DNBTimer timer(false);
+ thread_activity_sp = DNBGetGenealogyInfoForThread (pid, tid, timed_out);
+ genealogy_fetch_time = timer.ElapsedMicroSeconds(false) / 1000000.0;
+ }
+
+ std::unordered_set<uint32_t> process_info_indexes; // an array of the process info #'s seen
+
+ json << "{";
+
+ bool need_to_print_comma = false;
+
+ if (thread_activity_sp && timed_out == false)
+ {
+ const Genealogy::Activity *activity = &thread_activity_sp->current_activity;
+ bool need_vouchers_comma_sep = false;
+ json << "\"activity_query_timed_out\":false,";
+ if (genealogy_fetch_time != 0)
+ {
+ // If we append the floating point value with << we'll get it in scientific
+ // notation.
+ char floating_point_ascii_buffer[64];
+ floating_point_ascii_buffer[0] = '\0';
+ snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time);
+ if (strlen (floating_point_ascii_buffer) > 0)
+ {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"activity_query_duration\":" << floating_point_ascii_buffer;
+ }
+ }
+ if (activity->activity_id != 0)
+ {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ need_vouchers_comma_sep = true;
+ json << "\"activity\":{";
+ json << "\"start\":" << activity->activity_start << ",";
+ json << "\"id\":" << activity->activity_id << ",";
+ json << "\"parent_id\":" << activity->parent_id << ",";
+ json << "\"name\":\"" << json_string_quote_metachars (activity->activity_name) << "\",";
+ json << "\"reason\":\"" << json_string_quote_metachars (activity->reason) << "\"";
+ json << "}";
+ }
+ if (thread_activity_sp->messages.size() > 0)
+ {
+ need_to_print_comma = true;
+ if (need_vouchers_comma_sep)
+ json << ",";
+ need_vouchers_comma_sep = true;
+ json << "\"trace_messages\":[";
+ bool printed_one_message = false;
+ for (auto iter = thread_activity_sp->messages.begin() ; iter != thread_activity_sp->messages.end(); ++iter)
+ {
+ if (printed_one_message)
+ json << ",";
+ else
+ printed_one_message = true;
+ json << "{";
+ json << "\"timestamp\":" << iter->timestamp << ",";
+ json << "\"activity_id\":" << iter->activity_id << ",";
+ json << "\"trace_id\":" << iter->trace_id << ",";
+ json << "\"thread\":" << iter->thread << ",";
+ json << "\"type\":" << (int) iter->type << ",";
+ json << "\"process_info_index\":" << iter->process_info_index << ",";
+ process_info_indexes.insert (iter->process_info_index);
+ json << "\"message\":\"" << json_string_quote_metachars (iter->message) << "\"";
+ json << "}";
+ }
+ json << "]";
+ }
+ if (thread_activity_sp->breadcrumbs.size() == 1)
+ {
+ need_to_print_comma = true;
+ if (need_vouchers_comma_sep)
+ json << ",";
+ need_vouchers_comma_sep = true;
+ json << "\"breadcrumb\":{";
+ for (auto iter = thread_activity_sp->breadcrumbs.begin() ; iter != thread_activity_sp->breadcrumbs.end(); ++iter)
+ {
+ json << "\"breadcrumb_id\":" << iter->breadcrumb_id << ",";
+ json << "\"activity_id\":" << iter->activity_id << ",";
+ json << "\"timestamp\":" << iter->timestamp << ",";
+ json << "\"name\":\"" << json_string_quote_metachars (iter->name) << "\"";
+ }
+ json << "}";
+ }
+ if (process_info_indexes.size() > 0)
+ {
+ need_to_print_comma = true;
+ if (need_vouchers_comma_sep)
+ json << ",";
+ need_vouchers_comma_sep = true;
+ json << "\"process_infos\":[";
+ bool printed_one_process_info = false;
+ for (auto iter = process_info_indexes.begin(); iter != process_info_indexes.end(); ++iter)
+ {
+ if (printed_one_process_info)
+ json << ",";
+ else
+ printed_one_process_info = true;
+ Genealogy::ProcessExecutableInfoSP image_info_sp;
+ uint32_t idx = *iter;
+ image_info_sp = DNBGetGenealogyImageInfo (pid, idx);
+ json << "{";
+ char uuid_buf[37];
+ uuid_unparse_upper (image_info_sp->image_uuid, uuid_buf);
+ json << "\"process_info_index\":" << idx << ",";
+ json << "\"image_path\":\"" << json_string_quote_metachars (image_info_sp->image_path) << "\",";
+ json << "\"image_uuid\":\"" << uuid_buf <<"\"";
+ json << "}";
+ }
+ json << "]";
+ }
+ }
+ else
+ {
+ if (timed_out)
+ {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"activity_query_timed_out\":true";
+ if (genealogy_fetch_time != 0)
+ {
+ // If we append the floating point value with << we'll get it in scientific
+ // notation.
+ char floating_point_ascii_buffer[64];
+ floating_point_ascii_buffer[0] = '\0';
+ snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time);
+ if (strlen (floating_point_ascii_buffer) > 0)
+ {
+ json << ",";
+ json << "\"activity_query_duration\":" << floating_point_ascii_buffer;
+ }
+ }
+ }
+ }
+
+ if (tsd_address != INVALID_NUB_ADDRESS)
+ {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"tsd_address\":" << tsd_address;
+
+ if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX)
+ {
+ ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread (pid, tid, tsd_address, dti_qos_class_index);
+ if (requested_qos.IsValid())
+ {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"requested_qos\":{";
+ json << "\"enum_value\":" << requested_qos.enum_value << ",";
+ json << "\"constant_name\":\"" << json_string_quote_metachars (requested_qos.constant_name) << "\",";
+ json << "\"printable_name\":\"" << json_string_quote_metachars (requested_qos.printable_name) << "\"";
+ json << "}";
+ }
+ }
+ }
+
+ if (pthread_t_value != INVALID_NUB_ADDRESS)
+ {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"pthread_t\":" << pthread_t_value;
+ }
+
+ nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT (pid, tid);
+ if (dispatch_queue_t_value != INVALID_NUB_ADDRESS)
+ {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"dispatch_queue_t\":" << dispatch_queue_t_value;
+ }
+
+ json << "}";
+ std::string json_quoted = binary_encode_string (json.str());
+ return SendPacket (json_quoted);
+ }
+ }
+ return SendPacket ("OK");
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos (const char *p)
+{
+ nub_process_t pid;
+ // If we haven't run the process yet, return an error.
+ if (!m_ctx.HasValidProcessID())
+ {
+ return SendPacket ("E83");
+ }
+
+ pid = m_ctx.ProcessID();
+
+ const char get_loaded_dynamic_libraries_infos_str[] = { "jGetLoadedDynamicLibrariesInfos:{" };
+ if (strncmp (p, get_loaded_dynamic_libraries_infos_str, sizeof (get_loaded_dynamic_libraries_infos_str) - 1) == 0)
+ {
+ p += strlen (get_loaded_dynamic_libraries_infos_str);
+
+ nub_addr_t image_list_address = get_integer_value_for_key_name_from_json ("image_list_address", p);
+ nub_addr_t image_count = get_integer_value_for_key_name_from_json ("image_count", p);
+
+ if (image_list_address != INVALID_NUB_ADDRESS && image_count != INVALID_NUB_ADDRESS)
+ {
+ JSONGenerator::ObjectSP json_sp;
+
+ json_sp = DNBGetLoadedDynamicLibrariesInfos (pid, image_list_address, image_count);
+
+ if (json_sp.get())
+ {
+ std::ostringstream json_str;
+ json_sp->Dump (json_str);
+ if (json_str.str().size() > 0)
+ {
+ std::string json_str_quoted = binary_encode_string (json_str.str());
+ return SendPacket (json_str_quoted.c_str());
+ }
+ else
+ {
+ SendPacket ("E84");
+ }
+ }
+ }
+ }
+ return SendPacket ("OK");
+}
+
+static bool
+MachHeaderIsMainExecutable (nub_process_t pid, uint32_t addr_size, nub_addr_t mach_header_addr, mach_header &mh)
+{
+ DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx)", pid, addr_size, mach_header_addr);
+ const nub_size_t bytes_read = DNBProcessMemoryRead(pid, mach_header_addr, sizeof(mh), &mh);
+ if (bytes_read == sizeof(mh))
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx): mh = {\n magic = 0x%8.8x\n cpu = 0x%8.8x\n sub = 0x%8.8x\n filetype = %u\n ncmds = %u\n sizeofcmds = 0x%8.8x\n flags = 0x%8.8x }", pid, addr_size, mach_header_addr, mh.magic, mh.cputype, mh.cpusubtype, mh.filetype, mh.ncmds, mh.sizeofcmds, mh.flags);
+ if ((addr_size == 4 && mh.magic == MH_MAGIC) ||
+ (addr_size == 8 && mh.magic == MH_MAGIC_64))
+ {
+ if (mh.filetype == MH_EXECUTE)
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx) -> this is the executable!!!", pid, addr_size, mach_header_addr);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static nub_addr_t
+GetMachHeaderForMainExecutable (const nub_process_t pid, const uint32_t addr_size, mach_header &mh)
+{
+ struct AllImageInfos
+ {
+ uint32_t version;
+ uint32_t dylib_info_count;
+ uint64_t dylib_info_addr;
+ };
+
+ uint64_t mach_header_addr = 0;
+
+ const nub_addr_t shlib_addr = DNBProcessGetSharedLibraryInfoAddress (pid);
+ uint8_t bytes[256];
+ nub_size_t bytes_read = 0;
+ DNBDataRef data (bytes, sizeof(bytes), false);
+ DNBDataRef::offset_t offset = 0;
+ data.SetPointerSize(addr_size);
+
+ //----------------------------------------------------------------------
+ // When we are sitting at __dyld_start, the kernel has placed the
+ // address of the mach header of the main executable on the stack. If we
+ // read the SP and dereference a pointer, we might find the mach header
+ // for the executable. We also just make sure there is only 1 thread
+ // since if we are at __dyld_start we shouldn't have multiple threads.
+ //----------------------------------------------------------------------
+ if (DNBProcessGetNumThreads(pid) == 1)
+ {
+ nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, 0);
+ if (tid != INVALID_NUB_THREAD)
+ {
+ DNBRegisterValue sp_value;
+ if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_SP, &sp_value))
+ {
+ uint64_t sp = addr_size == 8 ? sp_value.value.uint64 : sp_value.value.uint32;
+ bytes_read = DNBProcessMemoryRead(pid, sp, addr_size, bytes);
+ if (bytes_read == addr_size)
+ {
+ offset = 0;
+ mach_header_addr = data.GetPointer(&offset);
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Check the dyld_all_image_info structure for a list of mach header
+ // since it is a very easy thing to check
+ //----------------------------------------------------------------------
+ if (shlib_addr != INVALID_NUB_ADDRESS)
+ {
+ bytes_read = DNBProcessMemoryRead(pid, shlib_addr, sizeof(AllImageInfos), bytes);
+ if (bytes_read > 0)
+ {
+ AllImageInfos aii;
+ offset = 0;
+ aii.version = data.Get32(&offset);
+ aii.dylib_info_count = data.Get32(&offset);
+ if (aii.dylib_info_count > 0)
+ {
+ aii.dylib_info_addr = data.GetPointer(&offset);
+ if (aii.dylib_info_addr != 0)
+ {
+ const size_t image_info_byte_size = 3 * addr_size;
+ for (uint32_t i=0; i<aii.dylib_info_count; ++i)
+ {
+ bytes_read = DNBProcessMemoryRead(pid, aii.dylib_info_addr + i * image_info_byte_size, image_info_byte_size, bytes);
+ if (bytes_read != image_info_byte_size)
+ break;
+ offset = 0;
+ mach_header_addr = data.GetPointer(&offset);
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ }
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // We failed to find the executable's mach header from the all image
+ // infos and by dereferencing the stack pointer. Now we fall back to
+ // enumerating the memory regions and looking for regions that are
+ // executable.
+ //----------------------------------------------------------------------
+ DNBRegionInfo region_info;
+ mach_header_addr = 0;
+ while (DNBProcessMemoryRegionInfo(pid, mach_header_addr, &region_info))
+ {
+ if (region_info.size == 0)
+ break;
+
+ if (region_info.permissions & eMemoryPermissionsExecutable)
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx) permissions = %c%c%c: checking region for executable mach header", region_info.addr, region_info.addr + region_info.size, (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx): permissions = %c%c%c: skipping region", region_info.addr, region_info.addr + region_info.size, (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
+ }
+ // Set the address to the next mapped region
+ mach_header_addr = region_info.addr + region_info.size;
+ }
+ bzero (&mh, sizeof(mh));
+ return INVALID_NUB_ADDRESS;
+}
+
+rnb_err_t
+RNBRemote::HandlePacket_qSymbol (const char *command)
+{
+ const char *p = command;
+ p += strlen ("qSymbol:");
+ const char *sep = strchr(p, ':');
+
+ std::string symbol_name;
+ std::string symbol_value_str;
+ // Extract the symbol value if there is one
+ if (sep > p)
+ symbol_value_str.assign(p, sep - p);
+ p = sep + 1;
+
+ if (*p)
+ {
+ // We have a symbol name
+ symbol_name = std::move(decode_hex_ascii_string(p));
+ if (!symbol_value_str.empty())
+ {
+ nub_addr_t symbol_value = decode_uint64(symbol_value_str.c_str(), 16);
+ if (symbol_name == "dispatch_queue_offsets")
+ m_dispatch_queue_offsets_addr = symbol_value;
+ }
+ ++m_qSymbol_index;
+ }
+ else
+ {
+ // No symbol name, set our symbol index to zero so we can
+ // read any symbols that we need
+ m_qSymbol_index = 0;
+ }
+
+ symbol_name.clear();
+
+ if (m_qSymbol_index == 0)
+ {
+ if (m_dispatch_queue_offsets_addr == INVALID_NUB_ADDRESS)
+ symbol_name = "dispatch_queue_offsets";
+ else
+ ++m_qSymbol_index;
+ }
+
+// // Lookup next symbol when we have one...
+// if (m_qSymbol_index == 1)
+// {
+// }
+
+
+ if (symbol_name.empty())
+ {
+ // Done with symbol lookups
+ return SendPacket ("OK");
+ }
+ else
+ {
+ std::ostringstream reply;
+ reply << "qSymbol:";
+ for (size_t i = 0; i < symbol_name.size(); ++i)
+ reply << RAWHEX8(symbol_name[i]);
+ return SendPacket (reply.str().c_str());
+ }
+}
+
+// Note that all numeric values returned by qProcessInfo are hex encoded,
+// including the pid and the cpu type.
+
+rnb_err_t
+RNBRemote::HandlePacket_qProcessInfo (const char *p)
+{
+ nub_process_t pid;
+ std::ostringstream rep;
+
+ // If we haven't run the process yet, return an error.
+ if (!m_ctx.HasValidProcessID())
+ return SendPacket ("E68");
+
+ pid = m_ctx.ProcessID();
+
+ rep << "pid:" << std::hex << pid << ';';
+
+ int procpid_mib[4];
+ procpid_mib[0] = CTL_KERN;
+ procpid_mib[1] = KERN_PROC;
+ procpid_mib[2] = KERN_PROC_PID;
+ procpid_mib[3] = pid;
+ struct kinfo_proc proc_kinfo;
+ size_t proc_kinfo_size = sizeof(struct kinfo_proc);
+
+ if (::sysctl (procpid_mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0)
+ {
+ if (proc_kinfo_size > 0)
+ {
+ rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ';';
+ rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid << ';';
+ rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid << ';';
+ rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid << ';';
+ if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
+ rep << "effective-gid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ';';
+ }
+ }
+
+ cpu_type_t cputype = DNBProcessGetCPUType (pid);
+ if (cputype == 0)
+ {
+ DNBLog ("Unable to get the process cpu_type, making a best guess.");
+ cputype = best_guess_cpu_type();
+ }
+
+ uint32_t addr_size = 0;
+ if (cputype != 0)
+ {
+ rep << "cputype:" << std::hex << cputype << ";";
+ if (cputype & CPU_ARCH_ABI64)
+ addr_size = 8;
+ else
+ addr_size = 4;
+ }
+
+ bool host_cpu_is_64bit = false;
+ 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 = is64bit_capable != 0;
+
+ uint32_t cpusubtype;
+ size_t cpusubtype_len = sizeof(cpusubtype);
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &cpusubtype_len, NULL, 0) == 0)
+ {
+ // If a process is CPU_TYPE_X86, then ignore the cpusubtype that we detected
+ // from the host and use CPU_SUBTYPE_I386_ALL because we don't want the
+ // CPU_SUBTYPE_X86_ARCH1 or CPU_SUBTYPE_X86_64_H to be used as the cpu subtype
+ // for i386...
+ if (host_cpu_is_64bit)
+ {
+ if (cputype == CPU_TYPE_X86)
+ {
+ cpusubtype = 3; // CPU_SUBTYPE_I386_ALL
+ }
+ else if (cputype == CPU_TYPE_ARM)
+ {
+ // We can query a process' cputype but we cannot query a process' cpusubtype.
+ // If the process has cputype CPU_TYPE_ARM, then it is an armv7 (32-bit process) and we
+ // need to override the host cpusubtype (which is in the CPU_SUBTYPE_ARM64 subtype namespace)
+ // with a reasonable CPU_SUBTYPE_ARMV7 subtype.
+ cpusubtype = 11; // CPU_SUBTYPE_ARM_V7S
+ }
+ }
+ rep << "cpusubtype:" << std::hex << cpusubtype << ';';
+ }
+
+ bool os_handled = false;
+ if (addr_size > 0)
+ {
+ rep << "ptrsize:" << std::dec << addr_size << ';';
+
+#if (defined (__x86_64__) || defined (__i386__))
+ // Try and get the OS type by looking at the load commands in the main
+ // executable and looking for a LC_VERSION_MIN load command. This is the
+ // most reliable way to determine the "ostype" value when on desktop.
+
+ mach_header mh;
+ nub_addr_t exe_mach_header_addr = GetMachHeaderForMainExecutable (pid, addr_size, mh);
+ if (exe_mach_header_addr != INVALID_NUB_ADDRESS)
+ {
+ uint64_t load_command_addr = exe_mach_header_addr + ((addr_size == 8) ? sizeof(mach_header_64) : sizeof(mach_header));
+ load_command lc;
+ for (uint32_t i=0; i<mh.ncmds && !os_handled; ++i)
+ {
+ const nub_size_t bytes_read = DNBProcessMemoryRead (pid, load_command_addr, sizeof(lc), &lc);
+ uint32_t raw_cmd = lc.cmd & ~LC_REQ_DYLD;
+ if (bytes_read != sizeof(lc))
+ break;
+ switch (raw_cmd)
+ {
+ case LC_VERSION_MIN_IPHONEOS:
+ os_handled = true;
+ rep << "ostype:ios;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_IPHONEOS -> 'ostype:ios;'");
+ break;
+
+ case LC_VERSION_MIN_MACOSX:
+ os_handled = true;
+ rep << "ostype:macosx;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_MACOSX -> 'ostype:macosx;'");
+ break;
+
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+ case LC_VERSION_MIN_TVOS:
+ os_handled = true;
+ rep << "ostype:tvos;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_TVOS -> 'ostype:tvos;'");
+ break;
+#endif
+
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ case LC_VERSION_MIN_WATCHOS:
+ os_handled = true;
+ rep << "ostype:watchos;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_WATCHOS -> 'ostype:watchos;'");
+ break;
+#endif
+
+ default:
+ break;
+ }
+ load_command_addr = load_command_addr + lc.cmdsize;
+ }
+ }
+#endif
+ }
+
+ // If we weren't able to find the OS in a LC_VERSION_MIN load command, try
+ // to set it correctly by using the cpu type and other tricks
+ if (!os_handled)
+ {
+ // The OS in the triple should be "ios" or "macosx" which doesn't match our
+ // "Darwin" which gets returned from "kern.ostype", so we need to hardcode
+ // this for now.
+ if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64)
+ {
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+ rep << "ostype:tvos;";
+#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ rep << "ostype:watchos;";
+#else
+ rep << "ostype:ios;";
+#endif
+ }
+ else
+ {
+ bool is_ios_simulator = false;
+ if (cputype == CPU_TYPE_X86 || cputype == CPU_TYPE_X86_64)
+ {
+ // Check for iOS simulator binaries by getting the process argument
+ // and environment and checking for SIMULATOR_UDID in the environment
+ int proc_args_mib[3] = { CTL_KERN, KERN_PROCARGS2, (int)pid };
+
+ uint8_t arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl (proc_args_mib, 3, arg_data, &arg_data_size , NULL, 0) == 0)
+ {
+ DNBDataRef data (arg_data, arg_data_size, false);
+ DNBDataRef::offset_t offset = 0;
+ uint32_t argc = data.Get32 (&offset);
+ const char *cstr;
+
+ cstr = data.GetCStr (&offset);
+ if (cstr)
+ {
+ // Skip NULLs
+ while (1)
+ {
+ const char *p = data.PeekCStr(offset);
+ if ((p == NULL) || (*p != '\0'))
+ break;
+ ++offset;
+ }
+ // Now skip all arguments
+ for (uint32_t i = 0; i < argc; ++i)
+ {
+ data.GetCStr(&offset);
+ }
+
+ // Now iterate across all environment variables
+ while ((cstr = data.GetCStr(&offset)))
+ {
+ if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0)
+ {
+ is_ios_simulator = true;
+ break;
+ }
+ if (cstr[0] == '\0')
+ break;
+
+ }
+ }
+ }
+ }
+ if (is_ios_simulator)
+ {
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+ rep << "ostype:tvos;";
+#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ rep << "ostype:watchos;";
+#else
+ rep << "ostype:ios;";
+#endif
+ }
+ else
+ {
+ rep << "ostype:macosx;";
+ }
+ }
+ }
+
+ rep << "vendor:apple;";
+
+#if defined (__LITTLE_ENDIAN__)
+ rep << "endian:little;";
+#elif defined (__BIG_ENDIAN__)
+ rep << "endian:big;";
+#elif defined (__PDP_ENDIAN__)
+ rep << "endian:pdp;";
+#endif
+
+ if (addr_size == 0)
+ {
+#if (defined (__x86_64__) || defined (__i386__)) && defined (x86_THREAD_STATE)
+ nub_thread_t thread = DNBProcessGetCurrentThreadMachPort (pid);
+ kern_return_t kr;
+ x86_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT;
+ kr = thread_get_state (static_cast<thread_act_t>(thread),
+ x86_THREAD_STATE,
+ (thread_state_t) &gp_regs,
+ &gp_count);
+ if (kr == KERN_SUCCESS)
+ {
+ if (gp_regs.tsh.flavor == x86_THREAD_STATE64)
+ rep << "ptrsize:8;";
+ else
+ rep << "ptrsize:4;";
+ }
+#elif defined (__arm__)
+ rep << "ptrsize:4;";
+#elif (defined (__arm64__) || defined (__aarch64__)) && defined (ARM_UNIFIED_THREAD_STATE)
+ nub_thread_t thread = DNBProcessGetCurrentThreadMachPort (pid);
+ kern_return_t kr;
+ arm_unified_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = ARM_UNIFIED_THREAD_STATE_COUNT;
+ kr = thread_get_state (thread, ARM_UNIFIED_THREAD_STATE,
+ (thread_state_t) &gp_regs, &gp_count);
+ if (kr == KERN_SUCCESS)
+ {
+ if (gp_regs.ash.flavor == ARM_THREAD_STATE64)
+ rep << "ptrsize:8;";
+ else
+ rep << "ptrsize:4;";
+ }
+#endif
+ }
+
+ return SendPacket (rep.str());
+}
+
+const RNBRemote::DispatchQueueOffsets *
+RNBRemote::GetDispatchQueueOffsets()
+{
+ if (!m_dispatch_queue_offsets.IsValid() && m_dispatch_queue_offsets_addr != INVALID_NUB_ADDRESS && m_ctx.HasValidProcessID())
+ {
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_size_t bytes_read = DNBProcessMemoryRead(pid, m_dispatch_queue_offsets_addr, sizeof(m_dispatch_queue_offsets), &m_dispatch_queue_offsets);
+ if (bytes_read != sizeof(m_dispatch_queue_offsets))
+ m_dispatch_queue_offsets.Clear();
+ }
+
+ if (m_dispatch_queue_offsets.IsValid())
+ return &m_dispatch_queue_offsets;
+ else
+ return nullptr;
+}
+
+void
+RNBRemote::EnableCompressionNextSendPacket (compression_types type)
+{
+ m_compression_mode = type;
+ m_enable_compression_next_send_packet = true;
+}
+
+compression_types
+RNBRemote::GetCompressionType ()
+{
+ // The first packet we send back to the debugger after a QEnableCompression request
+ // should be uncompressed -- so we can indicate whether the compression was enabled
+ // or not via OK / Enn returns. After that, all packets sent will be using the
+ // compression protocol.
+
+ if (m_enable_compression_next_send_packet)
+ {
+ // One time, we send back "None" as our compression type
+ m_enable_compression_next_send_packet = false;
+ return compression_types::none;
+ }
+ return m_compression_mode;
+}
diff --git a/tools/debugserver/source/RNBRemote.h b/tools/debugserver/source/RNBRemote.h
new file mode 100644
index 000000000000..9d30106d5b82
--- /dev/null
+++ b/tools/debugserver/source/RNBRemote.h
@@ -0,0 +1,452 @@
+
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBRemote_h__
+#define __RNBRemote_h__
+
+#include "RNBDefs.h"
+#include "DNB.h"
+#include "RNBContext.h"
+#include "RNBSocket.h"
+#include "PThreadMutex.h"
+#include <string>
+#include <vector>
+#include <deque>
+#include <map>
+
+class RNBSocket;
+class RNBContext;
+class PThreadEvents;
+
+enum event_loop_mode { debug_nub, gdb_remote_protocol, done };
+
+enum class compression_types { zlib_deflate, lz4, lzma, lzfse, none };
+
+class RNBRemote
+{
+public:
+
+ typedef enum {
+ invalid_packet = 0,
+ ack, // '+'
+ nack, // '-'
+ halt, // ^C (async halt)
+ use_extended_mode, // '!'
+ why_halted, // '?'
+ set_argv, // 'A'
+ set_bp, // 'B'
+ cont, // 'c'
+ continue_with_sig, // 'C'
+ detach, // 'D'
+ read_general_regs, // 'g'
+ write_general_regs, // 'G'
+ set_thread, // 'H'
+ step_inferior_one_cycle, // 'i'
+ signal_and_step_inf_one_cycle, // 'I'
+ kill, // 'k'
+ read_memory, // 'm'
+ write_memory, // 'M'
+ read_register, // 'p'
+ write_register, // 'P'
+ restart, // 'R'
+ single_step, // 's'
+ single_step_with_sig, // 'S'
+ search_mem_backwards, // 't'
+ thread_alive_p, // 'T'
+ vattach, // 'vAttach;pid'
+ vattachwait, // 'vAttachWait:XX...' where XX is one or more hex encoded process name ASCII bytes
+ vattachorwait, // 'vAttachOrWait:XX...' where XX is one or more hex encoded process name ASCII bytes
+ vattachname, // 'vAttachName:XX...' where XX is one or more hex encoded process name ASCII bytes
+ vcont, // 'vCont'
+ vcont_list_actions, // 'vCont?'
+ read_data_from_memory, // 'x'
+ write_data_to_memory, // 'X'
+ insert_mem_bp, // 'Z0'
+ remove_mem_bp, // 'z0'
+ insert_hardware_bp, // 'Z1'
+ remove_hardware_bp, // 'z1'
+ insert_write_watch_bp, // 'Z2'
+ remove_write_watch_bp, // 'z2'
+ insert_read_watch_bp, // 'Z3'
+ remove_read_watch_bp, // 'z3'
+ insert_access_watch_bp, // 'Z4'
+ remove_access_watch_bp, // 'z4'
+
+ query_monitor, // 'qRcmd'
+ query_current_thread_id, // 'qC'
+ query_get_pid, // 'qGetPid'
+ query_echo, // 'qEcho'
+ query_thread_ids_first, // 'qfThreadInfo'
+ query_thread_ids_subsequent, // 'qsThreadInfo'
+ query_thread_extra_info, // 'qThreadExtraInfo'
+ query_thread_stop_info, // 'qThreadStopInfo'
+ query_image_offsets, // 'qOffsets'
+ query_symbol_lookup, // 'qSymbol'
+ query_launch_success, // 'qLaunchSuccess'
+ query_register_info, // 'qRegisterInfo'
+ query_shlib_notify_info_addr, // 'qShlibInfoAddr'
+ query_step_packet_supported, // 'qStepPacketSupported'
+ query_supported_features, // 'qSupported'
+ query_vattachorwait_supported, // 'qVAttachOrWaitSupported'
+ query_sync_thread_state_supported,// 'QSyncThreadState'
+ query_host_info, // 'qHostInfo'
+ query_gdb_server_version, // 'qGDBServerVersion'
+ query_process_info, // 'qProcessInfo'
+ json_query_thread_extended_info,// 'jThreadExtendedInfo'
+ json_query_get_loaded_dynamic_libraries_infos, // 'jGetLoadedDynamicLibrariesInfos'
+ json_query_threads_info, // 'jThreadsInfo'
+ pass_signals_to_inferior, // 'QPassSignals'
+ start_noack_mode, // 'QStartNoAckMode'
+ prefix_reg_packets_with_tid, // 'QPrefixRegisterPacketsWithThreadID
+ set_logging_mode, // 'QSetLogging:'
+ set_max_packet_size, // 'QSetMaxPacketSize:'
+ set_max_payload_size, // 'QSetMaxPayloadSize:'
+ set_environment_variable, // 'QEnvironment:'
+ set_environment_variable_hex, // 'QEnvironmentHexEncoded:'
+ set_launch_arch, // 'QLaunchArch:'
+ set_disable_aslr, // 'QSetDisableASLR:'
+ set_stdin, // 'QSetSTDIN:'
+ set_stdout, // 'QSetSTDOUT:'
+ set_stderr, // 'QSetSTDERR:'
+ set_working_dir, // 'QSetWorkingDir:'
+ set_list_threads_in_stop_reply, // 'QListThreadsInStopReply:'
+ sync_thread_state, // 'QSyncThreadState:'
+ memory_region_info, // 'qMemoryRegionInfo:'
+ get_profile_data, // 'qGetProfileData'
+ set_enable_profiling, // 'QSetEnableAsyncProfiling'
+ enable_compression, // 'QEnableCompression:'
+ watchpoint_support_info, // 'qWatchpointSupportInfo:'
+ allocate_memory, // '_M'
+ deallocate_memory, // '_m'
+ set_process_event, // 'QSetProcessEvent:'
+ save_register_state, // '_g'
+ restore_register_state, // '_G'
+ speed_test, // 'qSpeedTest:'
+ set_detach_on_error, // 'QSetDetachOnError:'
+ query_transfer, // 'qXfer:'
+ unknown_type
+ } PacketEnum;
+
+ typedef rnb_err_t (RNBRemote::*HandlePacketCallback)(const char *p);
+
+ RNBRemote ();
+ ~RNBRemote ();
+
+ void Initialize();
+
+ bool InitializeRegisters (bool force = false);
+
+ rnb_err_t HandleAsyncPacket(PacketEnum *type = NULL);
+ rnb_err_t HandleReceivedPacket(PacketEnum *type = NULL);
+
+ nub_thread_t GetContinueThread () const
+ {
+ return m_continue_thread;
+ }
+
+ void SetContinueThread (nub_thread_t tid)
+ {
+ m_continue_thread = tid;
+ }
+
+ nub_thread_t GetCurrentThread () const
+ {
+ if (m_thread == 0 || m_thread == (nub_thread_t)-1)
+ return DNBProcessGetCurrentThread (m_ctx.ProcessID());
+ return m_thread;
+ }
+
+ void SetCurrentThread (nub_thread_t tid)
+ {
+ DNBProcessSetCurrentThread (m_ctx.ProcessID(), tid);
+ m_thread = tid;
+ }
+
+ static void* ThreadFunctionReadRemoteData(void *arg);
+ void StartReadRemoteDataThread ();
+ void StopReadRemoteDataThread ();
+
+ void NotifyThatProcessStopped (void);
+
+ rnb_err_t HandlePacket_A (const char *p);
+ rnb_err_t HandlePacket_H (const char *p);
+ rnb_err_t HandlePacket_qC (const char *p);
+ rnb_err_t HandlePacket_qRcmd (const char *p);
+ rnb_err_t HandlePacket_qGetPid (const char *p);
+ rnb_err_t HandlePacket_qEcho (const char *p);
+ rnb_err_t HandlePacket_qLaunchSuccess (const char *p);
+ rnb_err_t HandlePacket_qRegisterInfo (const char *p);
+ rnb_err_t HandlePacket_qShlibInfoAddr (const char *p);
+ rnb_err_t HandlePacket_qStepPacketSupported (const char *p);
+ rnb_err_t HandlePacket_qVAttachOrWaitSupported (const char *p);
+ rnb_err_t HandlePacket_qSyncThreadStateSupported (const char *p);
+ rnb_err_t HandlePacket_qThreadInfo (const char *p);
+ rnb_err_t HandlePacket_jThreadExtendedInfo (const char *p);
+ rnb_err_t HandlePacket_jGetLoadedDynamicLibrariesInfos (const char *p);
+ rnb_err_t HandlePacket_jThreadsInfo (const char *p);
+ rnb_err_t HandlePacket_qThreadExtraInfo (const char *p);
+ rnb_err_t HandlePacket_qThreadStopInfo (const char *p);
+ rnb_err_t HandlePacket_qHostInfo (const char *p);
+ rnb_err_t HandlePacket_qGDBServerVersion (const char *p);
+ rnb_err_t HandlePacket_qProcessInfo (const char *p);
+ rnb_err_t HandlePacket_qSymbol (const char *p);
+ rnb_err_t HandlePacket_QStartNoAckMode (const char *p);
+ rnb_err_t HandlePacket_QThreadSuffixSupported (const char *p);
+ rnb_err_t HandlePacket_QSetLogging (const char *p);
+ rnb_err_t HandlePacket_QSetDisableASLR (const char *p);
+ rnb_err_t HandlePacket_QSetSTDIO (const char *p);
+ rnb_err_t HandlePacket_QSetWorkingDir (const char *p);
+ rnb_err_t HandlePacket_QSetMaxPayloadSize (const char *p);
+ rnb_err_t HandlePacket_QSetMaxPacketSize (const char *p);
+ rnb_err_t HandlePacket_QEnvironment (const char *p);
+ rnb_err_t HandlePacket_QEnvironmentHexEncoded (const char *p);
+ rnb_err_t HandlePacket_QLaunchArch (const char *p);
+ rnb_err_t HandlePacket_QListThreadsInStopReply (const char *p);
+ rnb_err_t HandlePacket_QSyncThreadState (const char *p);
+ rnb_err_t HandlePacket_QPrefixRegisterPacketsWithThreadID (const char *p);
+ rnb_err_t HandlePacket_QSetProcessEvent (const char *p);
+ rnb_err_t HandlePacket_last_signal (const char *p);
+ rnb_err_t HandlePacket_m (const char *p);
+ rnb_err_t HandlePacket_M (const char *p);
+ rnb_err_t HandlePacket_x (const char *p);
+ rnb_err_t HandlePacket_X (const char *p);
+ rnb_err_t HandlePacket_g (const char *p);
+ rnb_err_t HandlePacket_G (const char *p);
+ rnb_err_t HandlePacket_z (const char *p);
+ rnb_err_t HandlePacket_T (const char *p);
+ rnb_err_t HandlePacket_p (const char *p);
+ rnb_err_t HandlePacket_P (const char *p);
+ rnb_err_t HandlePacket_c (const char *p);
+ rnb_err_t HandlePacket_C (const char *p);
+ rnb_err_t HandlePacket_D (const char *p);
+ rnb_err_t HandlePacket_k (const char *p);
+ rnb_err_t HandlePacket_s (const char *p);
+ rnb_err_t HandlePacket_S (const char *p);
+ rnb_err_t HandlePacket_qSupported (const char *p);
+ rnb_err_t HandlePacket_v (const char *p);
+ rnb_err_t HandlePacket_UNIMPLEMENTED (const char *p);
+ rnb_err_t HandlePacket_ILLFORMED (const char *file, int line, const char *p, const char *description);
+ rnb_err_t HandlePacket_AllocateMemory (const char *p);
+ rnb_err_t HandlePacket_DeallocateMemory (const char *p);
+ rnb_err_t HandlePacket_SaveRegisterState (const char *p);
+ rnb_err_t HandlePacket_RestoreRegisterState (const char *p);
+ rnb_err_t HandlePacket_MemoryRegionInfo (const char *p);
+ rnb_err_t HandlePacket_GetProfileData(const char *p);
+ rnb_err_t HandlePacket_SetEnableAsyncProfiling(const char *p);
+ rnb_err_t HandlePacket_QEnableCompression(const char *p);
+ rnb_err_t HandlePacket_WatchpointSupportInfo (const char *p);
+ rnb_err_t HandlePacket_qSpeedTest (const char *p);
+ rnb_err_t HandlePacket_qXfer (const char *p);
+ rnb_err_t HandlePacket_stop_process (const char *p);
+ rnb_err_t HandlePacket_QSetDetachOnError (const char *p);
+
+ rnb_err_t SendStopReplyPacketForThread (nub_thread_t tid);
+ rnb_err_t SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer);
+ rnb_err_t SendSTDOUTPacket (char *buf, nub_size_t buf_size);
+ rnb_err_t SendSTDERRPacket (char *buf, nub_size_t buf_size);
+ void FlushSTDIO ();
+ void SendAsyncProfileData ();
+ rnb_err_t SendAsyncProfileDataPacket (char *buf, nub_size_t buf_size);
+
+ RNBContext& Context() { return m_ctx; }
+ RNBSocket& Comm() { return m_comm; }
+
+private:
+ // Outlaw some constructors
+ RNBRemote (const RNBRemote &);
+
+protected:
+
+ rnb_err_t GetCommData ();
+ void CommDataReceived(const std::string& data);
+ struct Packet
+ {
+ typedef std::vector<Packet> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ PacketEnum type;
+ HandlePacketCallback normal; // Function to call when inferior is halted
+ HandlePacketCallback async; // Function to call when inferior is running
+ std::string abbrev;
+ std::string printable_name;
+
+ bool
+ IsPlatformPacket () const
+ {
+ switch (type)
+ {
+ case set_logging_mode:
+ case query_host_info:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+ Packet() :
+ type(invalid_packet),
+ normal (NULL),
+ async (NULL),
+ abbrev (),
+ printable_name ()
+ {
+ }
+
+ Packet( PacketEnum in_type,
+ HandlePacketCallback in_normal,
+ HandlePacketCallback in_async,
+ const char *in_abbrev,
+ const char *in_printable_name) :
+ type (in_type),
+ normal (in_normal),
+ async (in_async),
+ abbrev (in_abbrev),
+ printable_name (in_printable_name)
+ {
+ }
+ };
+
+
+ struct DispatchQueueOffsets
+ {
+ uint16_t dqo_version;
+ uint16_t dqo_label;
+ uint16_t dqo_label_size;
+ uint16_t dqo_flags;
+ uint16_t dqo_flags_size;
+ uint16_t dqo_serialnum;
+ uint16_t dqo_serialnum_size;
+ uint16_t dqo_width;
+ uint16_t dqo_width_size;
+ uint16_t dqo_running;
+ uint16_t dqo_running_size;
+ uint16_t dqo_suspend_cnt; // version 5 and later, starting with Mac OS X 10.10/iOS 8
+ uint16_t dqo_suspend_cnt_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8
+ uint16_t dqo_target_queue; // version 5 and later, starting with Mac OS X 10.10/iOS 8
+ uint16_t dqo_target_queue_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8
+ uint16_t dqo_priority; // version 5 and later, starting with Mac OS X 10.10/iOS 8
+ uint16_t dqo_priority_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8
+
+ DispatchQueueOffsets ()
+ {
+ Clear();
+ }
+
+ void
+ Clear()
+ {
+ dqo_version = UINT16_MAX;
+ dqo_label = UINT16_MAX;
+ dqo_label_size = UINT16_MAX;
+ dqo_flags = UINT16_MAX;
+ dqo_flags_size = UINT16_MAX;
+ dqo_serialnum = UINT16_MAX;
+ dqo_serialnum_size = UINT16_MAX;
+ dqo_width = UINT16_MAX;
+ dqo_width_size = UINT16_MAX;
+ dqo_running = UINT16_MAX;
+ dqo_running_size = UINT16_MAX;
+ dqo_suspend_cnt = UINT16_MAX;
+ dqo_suspend_cnt_size = UINT16_MAX;
+ dqo_target_queue = UINT16_MAX;
+ dqo_target_queue_size = UINT16_MAX;
+ dqo_priority = UINT16_MAX;
+ dqo_priority_size = UINT16_MAX;
+ }
+
+ bool
+ IsValid () const
+ {
+ return dqo_version != UINT16_MAX;
+ }
+
+ void
+ GetThreadQueueInfo (nub_process_t pid,
+ nub_addr_t dispatch_qaddr,
+ std::string &queue_name,
+ uint64_t &queue_width,
+ uint64_t &queue_serialnum) const;
+ };
+
+ rnb_err_t GetPacket (std::string &packet_data, RNBRemote::Packet& packet_info, bool wait);
+ rnb_err_t SendPacket (const std::string &);
+ std::string CompressString (const std::string &);
+
+ void CreatePacketTable ();
+ rnb_err_t GetPacketPayload (std::string &);
+
+ nub_thread_t
+ ExtractThreadIDFromThreadSuffix (const char *p);
+
+ void
+ EnableCompressionNextSendPacket (compression_types);
+
+ compression_types
+ GetCompressionType ();
+
+ const DispatchQueueOffsets *
+ GetDispatchQueueOffsets();
+
+ JSONGenerator::ObjectSP
+ GetJSONThreadsInfo (bool threads_with_valid_stop_info_only);
+
+ RNBContext m_ctx; // process context
+ RNBSocket m_comm; // communication port
+ std::string m_arch;
+ nub_thread_t m_continue_thread; // thread to continue; 0 for any, -1 for all
+ nub_thread_t m_thread; // thread for other ops; 0 for any, -1 for all
+ PThreadMutex m_mutex; // Mutex that protects
+ DispatchQueueOffsets m_dispatch_queue_offsets;
+ nub_addr_t m_dispatch_queue_offsets_addr;
+ uint32_t m_qSymbol_index;
+ uint32_t m_packets_recvd;
+ Packet::collection m_packets;
+ std::deque<std::string> m_rx_packets;
+ std::string m_rx_partial_data; // For packets that may come in more than one batch, anything left over can be left here
+ pthread_t m_rx_pthread;
+ uint32_t m_max_payload_size; // the maximum sized payload we should send to gdb
+ bool m_extended_mode; // are we in extended mode?
+ bool m_noack_mode; // are we in no-ack mode?
+ bool m_thread_suffix_supported; // Set to true if the 'p', 'P', 'g', and 'G' packets should be prefixed with the thread ID and colon:
+ // "$pRR;thread:TTTT;" instead of "$pRR"
+ // "$PRR=VVVVVVVV;thread:TTTT;" instead of "$PRR=VVVVVVVV"
+ // "$g;thread:TTTT" instead of "$g"
+ // "$GVVVVVVVVVVVVVV;thread:TTTT;#00 instead of "$GVVVVVVVVVVVVVV"
+ bool m_list_threads_in_stop_reply;
+
+ size_t m_compression_minsize; // only packets larger than this size will be compressed
+ bool m_enable_compression_next_send_packet;
+
+ compression_types m_compression_mode;
+};
+
+/* We translate the /usr/include/mach/exception_types.h exception types
+ (e.g. EXC_BAD_ACCESS) to the fake BSD signal numbers that gdb uses
+ in include/gdb/signals.h (e.g. TARGET_EXC_BAD_ACCESS). These hard
+ coded values for TARGET_EXC_BAD_ACCESS et al must match the gdb
+ values in its include/gdb/signals.h. */
+
+#define TARGET_EXC_BAD_ACCESS 0x91
+#define TARGET_EXC_BAD_INSTRUCTION 0x92
+#define TARGET_EXC_ARITHMETIC 0x93
+#define TARGET_EXC_EMULATION 0x94
+#define TARGET_EXC_SOFTWARE 0x95
+#define TARGET_EXC_BREAKPOINT 0x96
+
+/* Generally speaking, you can't assume gdb can receive more than 399 bytes
+ at a time with a random gdb. This bufsize constant is only specifying
+ how many bytes gdb can *receive* from debugserver -- it tells us nothing
+ about how many bytes gdb might try to send in a single packet. */
+#define DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE 399
+
+#endif // #ifndef __RNBRemote_h__
diff --git a/tools/debugserver/source/RNBServices.cpp b/tools/debugserver/source/RNBServices.cpp
new file mode 100644
index 000000000000..ebd390267f48
--- /dev/null
+++ b/tools/debugserver/source/RNBServices.cpp
@@ -0,0 +1,226 @@
+//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Christopher Friesen on 3/21/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBServices.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <libproc.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include "CFString.h"
+#include <vector>
+#include "DNBLog.h"
+#include "MacOSX/CFUtils.h"
+
+// For now only SpringBoard has a notion of "Applications" that it can list for us.
+// So we have to use the SpringBoard API's here.
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
+#include <SpringBoardServices/SpringBoardServices.h>
+#endif
+
+// From DNB.cpp
+size_t GetAllInfos (std::vector<struct kinfo_proc>& proc_infos);
+
+int
+GetProcesses (CFMutableArrayRef plistMutableArray, bool all_users)
+{
+ if (plistMutableArray == NULL)
+ return -1;
+
+ // Running as root, get all processes
+ std::vector<struct kinfo_proc> proc_infos;
+ const size_t num_proc_infos = GetAllInfos(proc_infos);
+ if (num_proc_infos > 0)
+ {
+ const pid_t our_pid = getpid();
+ const uid_t our_uid = getuid();
+ uint32_t i;
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+
+ for (i=0; i<num_proc_infos; i++)
+ {
+ struct kinfo_proc &proc_info = proc_infos[i];
+
+ bool kinfo_user_matches;
+ // Special case, if lldb is being run as root we can attach to anything.
+ if (all_users)
+ kinfo_user_matches = true;
+ else
+ kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;
+
+
+ const pid_t pid = proc_info.kp_proc.p_pid;
+ // Skip zombie processes and processes with unset status
+ if (kinfo_user_matches == false || // User is acceptable
+ pid == our_pid || // Skip this process
+ pid == 0 || // Skip kernel (kernel pid is zero)
+ proc_info.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains...
+ proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?
+ proc_info.kp_proc.p_flag & P_WEXIT || // Working on exiting?
+ proc_info.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta)
+ continue;
+
+ // Create a new mutable dictionary for each application
+ CFReleaser<CFMutableDictionaryRef> appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+
+ // Get the process id for the app (if there is one)
+ const int32_t pid_int32 = pid;
+ CFReleaser<CFNumberRef> pidCFNumber (::CFNumberCreate (alloc, kCFNumberSInt32Type, &pid_int32));
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get());
+
+ // Set the a boolean to indicate if this is the front most
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse);
+
+ const char *pid_basename = proc_info.kp_proc.p_comm;
+ char proc_path_buf[PATH_MAX];
+
+ int return_val = proc_pidpath (pid, proc_path_buf, PATH_MAX);
+ if (return_val > 0)
+ {
+ // Okay, now search backwards from that to see if there is a
+ // slash in the name. Note, even though we got all the args we don't care
+ // because the list data is just a bunch of concatenated null terminated strings
+ // so strrchr will start from the end of argv0.
+
+ pid_basename = strrchr(proc_path_buf, '/');
+ if (pid_basename)
+ {
+ // Skip the '/'
+ ++pid_basename;
+ }
+ else
+ {
+ // We didn't find a directory delimiter in the process argv[0], just use what was in there
+ pid_basename = proc_path_buf;
+ }
+ CFString cf_pid_path (proc_path_buf);
+ if (cf_pid_path.get())
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PATH_KEY, cf_pid_path.get());
+ }
+
+ if (pid_basename && pid_basename[0])
+ {
+ CFString pid_name (pid_basename);
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
+ }
+
+ // Append the application info to the plist array
+ ::CFArrayAppendValue (plistMutableArray, appInfoDict.get());
+ }
+ }
+ return 0;
+}
+int
+ListApplications(std::string& plist, bool opt_runningApps, bool opt_debuggable)
+{
+ int result = -1;
+
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+
+ // Create a mutable array that we can populate. Specify zero so it can be of any size.
+ CFReleaser<CFMutableArrayRef> plistMutableArray (::CFArrayCreateMutable (alloc, 0, &kCFTypeArrayCallBacks));
+
+ const uid_t our_uid = getuid();
+
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
+
+ if (our_uid == 0)
+ {
+ bool all_users = true;
+ result = GetProcesses (plistMutableArray.get(), all_users);
+ }
+ else
+ {
+ CFReleaser<CFStringRef> sbsFrontAppID (::SBSCopyFrontmostApplicationDisplayIdentifier ());
+ CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable));
+
+ // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
+ CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount (sbsAppIDs.get()) : 0;
+ CFIndex i = 0;
+ for (i = 0; i < count; i++)
+ {
+ CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i);
+
+ // Create a new mutable dictionary for each application
+ CFReleaser<CFMutableDictionaryRef> appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+
+ // Get the process id for the app (if there is one)
+ pid_t pid = INVALID_NUB_PROCESS;
+ if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &pid) == true)
+ {
+ CFReleaser<CFNumberRef> pidCFNumber (::CFNumberCreate (alloc, kCFNumberSInt32Type, &pid));
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get());
+ }
+
+ // Set the a boolean to indicate if this is the front most
+ if (sbsFrontAppID.get() && displayIdentifier && (::CFStringCompare (sbsFrontAppID.get(), displayIdentifier, 0) == kCFCompareEqualTo))
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanTrue);
+ else
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse);
+
+
+ CFReleaser<CFStringRef> executablePath (::SBSCopyExecutablePathForDisplayIdentifier (displayIdentifier));
+ if (executablePath.get() != NULL)
+ {
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PATH_KEY, executablePath.get());
+ }
+
+ CFReleaser<CFStringRef> iconImagePath (::SBSCopyIconImagePathForDisplayIdentifier (displayIdentifier)) ;
+ if (iconImagePath.get() != NULL)
+ {
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, iconImagePath.get());
+ }
+
+ CFReleaser<CFStringRef> localizedDisplayName (::SBSCopyLocalizedApplicationNameForDisplayIdentifier (displayIdentifier));
+ if (localizedDisplayName.get() != NULL)
+ {
+ ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, localizedDisplayName.get());
+ }
+
+ // Append the application info to the plist array
+ ::CFArrayAppendValue (plistMutableArray.get(), appInfoDict.get());
+ }
+ }
+#else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
+ // When root, show all processes
+ bool all_users = (our_uid == 0);
+ GetProcesses (plistMutableArray.get(), all_users);
+#endif
+
+ CFReleaser<CFDataRef> plistData (::CFPropertyListCreateXMLData (alloc, plistMutableArray.get()));
+
+ // write plist to service port
+ if (plistData.get() != NULL)
+ {
+ CFIndex size = ::CFDataGetLength (plistData.get());
+ const UInt8 *bytes = ::CFDataGetBytePtr (plistData.get());
+ if (bytes != NULL && size > 0)
+ {
+ plist.assign((char *)bytes, size);
+ return 0; // Success
+ }
+ else
+ {
+ DNBLogError("empty application property list.");
+ result = -2;
+ }
+ }
+ else
+ {
+ DNBLogError("serializing task list.");
+ result = -3;
+ }
+
+ return result;
+
+}
diff --git a/tools/debugserver/source/RNBServices.h b/tools/debugserver/source/RNBServices.h
new file mode 100644
index 000000000000..b0b9c2193573
--- /dev/null
+++ b/tools/debugserver/source/RNBServices.h
@@ -0,0 +1,28 @@
+//===-- RNBServices.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Christopher Friesen on 3/21/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBServices_h__
+#define __RNBServices_h__
+
+#include <string>
+#include "RNBDefs.h"
+
+#define DTSERVICES_APP_FRONTMOST_KEY CFSTR("isFrontApp")
+#define DTSERVICES_APP_PATH_KEY CFSTR("executablePath")
+#define DTSERVICES_APP_ICON_PATH_KEY CFSTR("iconPath")
+#define DTSERVICES_APP_DISPLAY_NAME_KEY CFSTR("displayName")
+#define DTSERVICES_APP_PID_KEY CFSTR("pid")
+
+int ListApplications (std::string &plist, bool opt_runningApps, bool opt_debuggable);
+
+#endif // __RNBServices_h__
diff --git a/tools/debugserver/source/RNBSocket.cpp b/tools/debugserver/source/RNBSocket.cpp
new file mode 100644
index 000000000000..ce4886ab9df3
--- /dev/null
+++ b/tools/debugserver/source/RNBSocket.cpp
@@ -0,0 +1,421 @@
+//===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBSocket.h"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <termios.h>
+#include "DNBLog.h"
+#include "DNBError.h"
+
+#ifdef WITH_LOCKDOWN
+#include "lockdown.h"
+#endif
+
+/* Once we have a RNBSocket object with a port # specified,
+ this function is called to wait for an incoming connection.
+ This function blocks while waiting for that connection. */
+
+bool
+ResolveIPV4HostName (const char *hostname, in_addr_t &addr)
+{
+ if (hostname == NULL ||
+ hostname[0] == '\0' ||
+ strcmp(hostname, "localhost") == 0 ||
+ strcmp(hostname, "127.0.0.1") == 0)
+ {
+ addr = htonl (INADDR_LOOPBACK);
+ return true;
+ }
+ else if (strcmp(hostname, "*") == 0)
+ {
+ addr = htonl (INADDR_ANY);
+ return true;
+ }
+ else
+ {
+ // See if an IP address was specified as numbers
+ int inet_pton_result = ::inet_pton (AF_INET, hostname, &addr);
+
+ if (inet_pton_result == 1)
+ return true;
+
+ struct hostent *host_entry = gethostbyname (hostname);
+ if (host_entry)
+ {
+ std::string ip_str (::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list));
+ inet_pton_result = ::inet_pton (AF_INET, ip_str.c_str(), &addr);
+ if (inet_pton_result == 1)
+ return true;
+ }
+ }
+ return false;
+}
+
+rnb_err_t
+RNBSocket::Listen (const char *listen_host, uint16_t port, PortBoundCallback callback, const void *callback_baton)
+{
+ //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
+ // Disconnect without saving errno
+ Disconnect (false);
+
+ // Now figure out the hostname that will be attaching and palce it into
+ struct sockaddr_in listen_addr;
+ ::memset (&listen_addr, 0, sizeof listen_addr);
+ listen_addr.sin_len = sizeof listen_addr;
+ listen_addr.sin_family = AF_INET;
+ listen_addr.sin_port = htons (port);
+ listen_addr.sin_addr.s_addr = INADDR_ANY;
+
+ if (!ResolveIPV4HostName(listen_host, listen_addr.sin_addr.s_addr))
+ {
+ DNBLogThreaded("error: failed to resolve connecting host '%s'", listen_host);
+ return rnb_err;
+ }
+
+ DNBError err;
+ int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (listen_fd == -1)
+ err.SetError(errno, DNBError::POSIX);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_fd);
+
+ if (err.Fail())
+ return rnb_err;
+
+ // enable local address reuse
+ SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ struct sockaddr_in sa;
+ ::memset (&sa, 0, sizeof sa);
+ sa.sin_len = sizeof sa;
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (port);
+ sa.sin_addr.s_addr = INADDR_ANY; // Let incoming connections bind to any host network interface (this is NOT who can connect to us)
+ int error = ::bind (listen_fd, (struct sockaddr *) &sa, sizeof(sa));
+ if (error == -1)
+ err.SetError(errno, DNBError::POSIX);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_fd);
+
+ if (err.Fail())
+ {
+ ClosePort (listen_fd, false);
+ return rnb_err;
+ }
+
+ error = ::listen (listen_fd, 5);
+ if (error == -1)
+ err.SetError(errno, DNBError::POSIX);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd);
+
+ if (err.Fail())
+ {
+ ClosePort (listen_fd, false);
+ return rnb_err;
+ }
+
+ if (callback)
+ {
+ // We were asked to listen on port zero which means we
+ // must now read the actual port that was given to us
+ // as port zero is a special code for "find an open port
+ // for me".
+ if (port == 0)
+ {
+ socklen_t sa_len = sizeof (sa);
+ if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0)
+ {
+ port = ntohs (sa.sin_port);
+ callback (callback_baton, port);
+ }
+ }
+ else
+ {
+ callback (callback_baton, port);
+ }
+ }
+
+ struct sockaddr_in accept_addr;
+ ::memset (&accept_addr, 0, sizeof accept_addr);
+ accept_addr.sin_len = sizeof accept_addr;
+
+ bool accept_connection = false;
+
+ // Loop until we are happy with our connection
+ while (!accept_connection)
+ {
+ socklen_t accept_addr_len = sizeof accept_addr;
+ m_fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len);
+
+ if (m_fd == -1)
+ err.SetError(errno, DNBError::POSIX);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::accept ( socket = %i, address = %p, address_len = %u )", listen_fd, &accept_addr, accept_addr_len);
+
+ if (err.Fail())
+ break;
+
+ if (listen_addr.sin_addr.s_addr == INADDR_ANY)
+ accept_connection = true;
+ else
+ {
+ if (accept_addr_len == listen_addr.sin_len &&
+ accept_addr.sin_addr.s_addr == listen_addr.sin_addr.s_addr)
+ {
+ accept_connection = true;
+ }
+ else
+ {
+ ::close (m_fd);
+ m_fd = -1;
+ const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr;
+ const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sin_addr.s_addr;
+ ::fprintf (stderr,
+ "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n",
+ accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
+ listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
+ DNBLogThreaded ("error: rejecting connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)",
+ accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
+ listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
+ }
+ }
+ }
+
+ ClosePort (listen_fd, false);
+
+ if (err.Fail())
+ {
+ return rnb_err;
+ }
+ else
+ {
+ // Keep our TCP packets coming without any delays.
+ SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
+ }
+
+ return rnb_success;
+}
+
+rnb_err_t
+RNBSocket::Connect (const char *host, uint16_t port)
+{
+ Disconnect (false);
+
+ // Create the socket
+ m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (m_fd == -1)
+ return rnb_err;
+
+ // Enable local address reuse
+ SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ struct sockaddr_in sa;
+ ::memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (port);
+
+ if (!ResolveIPV4HostName(host, sa.sin_addr.s_addr))
+ {
+ DNBLogThreaded("error: failed to resolve host '%s'", host);
+ Disconnect (false);
+ return rnb_err;
+ }
+
+ if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa)))
+ {
+ Disconnect (false);
+ return rnb_err;
+ }
+
+ // Keep our TCP packets coming without any delays.
+ SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
+ return rnb_success;
+}
+
+rnb_err_t
+RNBSocket::useFD(int fd)
+{
+ if (fd < 0) {
+ DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in.");
+ return rnb_err;
+ }
+
+ m_fd = fd;
+ return rnb_success;
+}
+
+#ifdef WITH_LOCKDOWN
+rnb_err_t
+RNBSocket::ConnectToService()
+{
+ DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME);
+ // Disconnect from any previous connections
+ Disconnect(false);
+ if (::secure_lockdown_checkin (&m_ld_conn, NULL, NULL) != kLDESuccess)
+ {
+ DNBLogThreadedIf(LOG_RNB_COMM, "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed");
+ m_fd = -1;
+ return rnb_not_connected;
+ }
+ m_fd = ::lockdown_get_socket (m_ld_conn);
+ if (m_fd == -1)
+ {
+ DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed");
+ return rnb_not_connected;
+ }
+ m_fd_from_lockdown = true;
+ return rnb_success;
+}
+#endif
+
+rnb_err_t
+RNBSocket::OpenFile (const char *path)
+{
+ DNBError err;
+ m_fd = open (path, O_RDWR);
+ if (m_fd == -1)
+ {
+ err.SetError(errno, DNBError::POSIX);
+ err.LogThreaded ("can't open file '%s'", path);
+ return rnb_not_connected;
+ }
+ else
+ {
+ struct termios stdin_termios;
+
+ if (::tcgetattr (m_fd, &stdin_termios) == 0)
+ {
+ stdin_termios.c_lflag &= ~ECHO; // Turn off echoing
+ stdin_termios.c_lflag &= ~ICANON; // Get one char at a time
+ ::tcsetattr (m_fd, TCSANOW, &stdin_termios);
+ }
+ }
+ return rnb_success;
+}
+
+int
+RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value)
+{
+ return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value));
+}
+
+rnb_err_t
+RNBSocket::Disconnect (bool save_errno)
+{
+#ifdef WITH_LOCKDOWN
+ if (m_fd_from_lockdown)
+ {
+ m_fd_from_lockdown = false;
+ m_fd = -1;
+ lockdown_disconnect (m_ld_conn);
+ return rnb_success;
+ }
+#endif
+ return ClosePort (m_fd, save_errno);
+}
+
+
+rnb_err_t
+RNBSocket::Read (std::string &p)
+{
+ char buf[1024];
+ p.clear();
+
+ // Note that BUF is on the stack so we must be careful to keep any
+ // writes to BUF from overflowing or we'll have security issues.
+
+ if (m_fd == -1)
+ return rnb_err;
+
+ //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
+ DNBError err;
+ ssize_t bytesread = read (m_fd, buf, sizeof (buf));
+ if (bytesread <= 0)
+ err.SetError(errno, DNBError::POSIX);
+ else
+ p.append(buf, bytesread);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof (buf), (uint64_t)bytesread);
+
+ // Our port went away - we have to mark this so IsConnected will return the truth.
+ if (bytesread == 0)
+ {
+ m_fd = -1;
+ return rnb_not_connected;
+ }
+ else if (bytesread == -1)
+ {
+ m_fd = -1;
+ return rnb_err;
+ }
+ // Strip spaces from the end of the buffer
+ while (!p.empty() && isspace (p[p.size() - 1]))
+ p.erase (p.size () - 1);
+
+ // Most data in the debugserver packets valid printable characters...
+ DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str());
+ return rnb_success;
+}
+
+rnb_err_t
+RNBSocket::Write (const void *buffer, size_t length)
+{
+ if (m_fd == -1)
+ return rnb_err;
+
+ DNBError err;
+ ssize_t bytessent = write (m_fd, buffer, length);
+ if (bytessent < 0)
+ err.SetError(errno, DNBError::POSIX);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", m_fd, buffer, length, (uint64_t)bytessent);
+
+ if (bytessent < 0)
+ return rnb_err;
+
+ if ((size_t)bytessent != length)
+ return rnb_err;
+
+ DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *)buffer); // All data is string based in debugserver, so this is safe
+ DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer);
+
+ return rnb_success;
+}
+
+
+rnb_err_t
+RNBSocket::ClosePort (int& fd, bool save_errno)
+{
+ int close_err = 0;
+ if (fd > 0)
+ {
+ errno = 0;
+ close_err = close (fd);
+ fd = -1;
+ }
+ return close_err != 0 ? rnb_err : rnb_success;
+}
+
+
diff --git a/tools/debugserver/source/RNBSocket.h b/tools/debugserver/source/RNBSocket.h
new file mode 100644
index 000000000000..32f4ebeed2a4
--- /dev/null
+++ b/tools/debugserver/source/RNBSocket.h
@@ -0,0 +1,85 @@
+//===-- RNBSocket.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBSocket_h__
+#define __RNBSocket_h__
+
+#include "RNBDefs.h"
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <string>
+#include "DNBTimer.h"
+
+#ifdef WITH_LOCKDOWN
+#include "lockdown.h"
+#endif
+
+class RNBSocket
+{
+public:
+ typedef void (*PortBoundCallback) (const void *baton, uint16_t port);
+
+ RNBSocket () :
+ m_fd (-1),
+#ifdef WITH_LOCKDOWN
+ m_fd_from_lockdown (false),
+ m_ld_conn (),
+#endif
+ m_timer (true) // Make a thread safe timer
+ {
+ }
+ ~RNBSocket (void)
+ {
+ Disconnect (false);
+ }
+
+ rnb_err_t Listen (const char *listen_host,
+ uint16_t port,
+ PortBoundCallback callback,
+ const void *callback_baton);
+ rnb_err_t Connect (const char *host, uint16_t port);
+
+ rnb_err_t useFD(int fd);
+
+#ifdef WITH_LOCKDOWN
+ rnb_err_t ConnectToService();
+#endif
+ rnb_err_t OpenFile (const char *path);
+ rnb_err_t Disconnect (bool save_errno);
+ rnb_err_t Read (std::string &p);
+ rnb_err_t Write (const void *buffer, size_t length);
+
+ bool IsConnected () const { return m_fd != -1; }
+ void SaveErrno (int curr_errno);
+ DNBTimer& Timer() { return m_timer; }
+
+ static int SetSocketOption(int fd, int level, int option_name, int option_value);
+private:
+ // Outlaw some constructors
+ RNBSocket (const RNBSocket &);
+
+protected:
+ rnb_err_t ClosePort (int& fd, bool save_errno);
+
+ int m_fd; // Socket we use to communicate once conn established
+
+#ifdef WITH_LOCKDOWN
+ bool m_fd_from_lockdown;
+ lockdown_connection m_ld_conn;
+#endif
+
+ DNBTimer m_timer;
+};
+
+
+#endif // #ifndef __RNBSocket_h__
diff --git a/tools/debugserver/source/SysSignal.cpp b/tools/debugserver/source/SysSignal.cpp
new file mode 100644
index 000000000000..69f34ed605c5
--- /dev/null
+++ b/tools/debugserver/source/SysSignal.cpp
@@ -0,0 +1,66 @@
+//===-- SysSignal.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SysSignal.h"
+#include <signal.h>
+#include <stddef.h>
+
+const char *
+SysSignal::Name(int signal)
+{
+ switch (signal)
+ {
+ case SIGHUP: return "SIGHUP"; // 1 hangup
+ case SIGINT: return "SIGINT"; // 2 interrupt
+ case SIGQUIT: return "SIGQUIT"; // 3 quit
+ case SIGILL: return "SIGILL"; // 4 illegal instruction (not reset when caught)
+ case SIGTRAP: return "SIGTRAP"; // 5 trace trap (not reset when caught)
+ case SIGABRT: return "SIGABRT"; // 6 abort()
+#if defined(_POSIX_C_SOURCE)
+ case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported)
+#else // !_POSIX_C_SOURCE
+ case SIGEMT: return "SIGEMT"; // 7 EMT instruction
+#endif // !_POSIX_C_SOURCE
+ case SIGFPE: return "SIGFPE"; // 8 floating point exception
+ case SIGKILL: return "SIGKILL"; // 9 kill (cannot be caught or ignored)
+ case SIGBUS: return "SIGBUS"; // 10 bus error
+ case SIGSEGV: return "SIGSEGV"; // 11 segmentation violation
+ case SIGSYS: return "SIGSYS"; // 12 bad argument to system call
+ case SIGPIPE: return "SIGPIPE"; // 13 write on a pipe with no one to read it
+ case SIGALRM: return "SIGALRM"; // 14 alarm clock
+ case SIGTERM: return "SIGTERM"; // 15 software termination signal from kill
+ case SIGURG: return "SIGURG"; // 16 urgent condition on IO channel
+ case SIGSTOP: return "SIGSTOP"; // 17 sendable stop signal not from tty
+ case SIGTSTP: return "SIGTSTP"; // 18 stop signal from tty
+ case SIGCONT: return "SIGCONT"; // 19 continue a stopped process
+ case SIGCHLD: return "SIGCHLD"; // 20 to parent on child stop or exit
+ case SIGTTIN: return "SIGTTIN"; // 21 to readers pgrp upon background tty read
+ case SIGTTOU: return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local&LTOSTOP)
+#if !defined(_POSIX_C_SOURCE)
+ case SIGIO: return "SIGIO"; // 23 input/output possible signal
+#endif
+ case SIGXCPU: return "SIGXCPU"; // 24 exceeded CPU time limit
+ case SIGXFSZ: return "SIGXFSZ"; // 25 exceeded file size limit
+ case SIGVTALRM: return "SIGVTALRM"; // 26 virtual time alarm
+ case SIGPROF: return "SIGPROF"; // 27 profiling time alarm
+#if !defined(_POSIX_C_SOURCE)
+ case SIGWINCH: return "SIGWINCH"; // 28 window size changes
+ case SIGINFO: return "SIGINFO"; // 29 information request
+#endif
+ case SIGUSR1: return "SIGUSR1"; // 30 user defined signal 1
+ case SIGUSR2: return "SIGUSR2"; // 31 user defined signal 2
+ default:
+ break;
+ }
+ return NULL;
+}
diff --git a/tools/debugserver/source/SysSignal.h b/tools/debugserver/source/SysSignal.h
new file mode 100644
index 000000000000..438d137f3104
--- /dev/null
+++ b/tools/debugserver/source/SysSignal.h
@@ -0,0 +1,23 @@
+//===-- SysSignal.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __SysSignal_h__
+#define __SysSignal_h__
+
+class SysSignal
+{
+public:
+ static const char *Name(int signal);
+};
+
+#endif
diff --git a/tools/debugserver/source/TTYState.cpp b/tools/debugserver/source/TTYState.cpp
new file mode 100644
index 000000000000..28bc956dc28b
--- /dev/null
+++ b/tools/debugserver/source/TTYState.cpp
@@ -0,0 +1,122 @@
+//===-- TTYState.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TTYState.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/signal.h>
+
+TTYState::TTYState() :
+ m_fd(-1),
+ m_tflags(-1),
+ m_ttystateErr(-1),
+ m_processGroup(-1)
+{
+}
+
+TTYState::~TTYState()
+{
+}
+
+bool
+TTYState::GetTTYState (int fd, bool saveProcessGroup)
+{
+ if (fd >= 0 && ::isatty (fd))
+ {
+ m_fd = fd;
+ m_tflags = fcntl (fd, F_GETFL, 0);
+ m_ttystateErr = tcgetattr (fd, &m_ttystate);
+ if (saveProcessGroup)
+ m_processGroup = tcgetpgrp (0);
+ else
+ m_processGroup = -1;
+ }
+ else
+ {
+ m_fd = -1;
+ m_tflags = -1;
+ m_ttystateErr = -1;
+ m_processGroup = -1;
+ }
+ return m_ttystateErr == 0;
+}
+
+bool
+TTYState::SetTTYState () const
+{
+ int result = 0;
+ if (IsValid())
+ {
+ if (TFlagsValid())
+ result = fcntl (m_fd, F_SETFL, m_tflags);
+
+ if (TTYStateValid())
+ result = tcsetattr (m_fd, TCSANOW, &m_ttystate);
+
+ if (ProcessGroupValid())
+ {
+ // Save the original signal handler.
+ void (*saved_sigttou_callback) (int) = NULL;
+ saved_sigttou_callback = (void (*)(int)) signal (SIGTTOU, SIG_IGN);
+ // Set the process group
+ result = tcsetpgrp (m_fd, m_processGroup);
+ // Restore the original signal handler.
+ signal (SIGTTOU, saved_sigttou_callback);
+ }
+ return true;
+ }
+ return false;
+}
+
+
+
+TTYStateSwitcher::TTYStateSwitcher() :
+ m_currentState(~0)
+{
+}
+
+TTYStateSwitcher::~TTYStateSwitcher()
+{
+}
+
+bool
+TTYStateSwitcher::GetState(uint32_t idx, int fd, bool saveProcessGroup)
+{
+ if (ValidStateIndex(idx))
+ return m_ttystates[idx].GetTTYState(fd, saveProcessGroup);
+ return false;
+}
+
+bool
+TTYStateSwitcher::SetState(uint32_t idx) const
+{
+ if (!ValidStateIndex(idx))
+ return false;
+
+ // See if we already are in this state?
+ if (ValidStateIndex(m_currentState) && (idx == m_currentState) && m_ttystates[idx].IsValid())
+ return true;
+
+ // Set the state to match the index passed in and only update the
+ // current state if there are no errors.
+ if (m_ttystates[idx].SetTTYState())
+ {
+ m_currentState = idx;
+ return true;
+ }
+
+ // We failed to set the state. The tty state was invalid or not
+ // initialized.
+ return false;
+}
+
diff --git a/tools/debugserver/source/TTYState.h b/tools/debugserver/source/TTYState.h
new file mode 100644
index 000000000000..c01d51255439
--- /dev/null
+++ b/tools/debugserver/source/TTYState.h
@@ -0,0 +1,61 @@
+//===-- TTYState.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __TTYState_h__
+#define __TTYState_h__
+
+#include <termios.h>
+#include <stdint.h>
+
+class TTYState
+{
+public:
+ TTYState();
+ ~TTYState();
+
+ bool GetTTYState (int fd, bool saveProcessGroup);
+ bool SetTTYState () const;
+
+ bool IsValid() const { return FileDescriptorValid() && TFlagsValid() && TTYStateValid(); }
+ bool FileDescriptorValid() const { return m_fd >= 0; }
+ bool TFlagsValid() const { return m_tflags != -1; }
+ bool TTYStateValid() const { return m_ttystateErr == 0; }
+ bool ProcessGroupValid() const { return m_processGroup != -1; }
+
+protected:
+ int m_fd; // File descriptor
+ int m_tflags;
+ int m_ttystateErr;
+ struct termios m_ttystate;
+ pid_t m_processGroup;
+
+};
+
+
+class TTYStateSwitcher
+{
+public:
+ TTYStateSwitcher();
+ ~TTYStateSwitcher();
+
+ bool GetState(uint32_t idx, int fd, bool saveProcessGroup);
+ bool SetState(uint32_t idx) const;
+ uint32_t NumStates() const { return sizeof(m_ttystates)/sizeof(TTYState); }
+ bool ValidStateIndex(uint32_t idx) const { return idx < NumStates(); }
+
+protected:
+ mutable uint32_t m_currentState;
+ TTYState m_ttystates[2];
+};
+
+#endif \ No newline at end of file
diff --git a/tools/debugserver/source/com.apple.debugserver.applist.internal.plist b/tools/debugserver/source/com.apple.debugserver.applist.internal.plist
new file mode 100644
index 000000000000..e9a74bd0bf79
--- /dev/null
+++ b/tools/debugserver/source/com.apple.debugserver.applist.internal.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.applist.internal</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--applist</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/tools/debugserver/source/com.apple.debugserver.applist.plist b/tools/debugserver/source/com.apple.debugserver.applist.plist
new file mode 100644
index 000000000000..002e90d98d13
--- /dev/null
+++ b/tools/debugserver/source/com.apple.debugserver.applist.plist
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.applist</string>
+ <key>UserName</key>
+ <string>mobile</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--applist</string>
+ <string>--launch=frontboard</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/tools/debugserver/source/com.apple.debugserver.internal.plist b/tools/debugserver/source/com.apple.debugserver.internal.plist
new file mode 100644
index 000000000000..b9f57f731232
--- /dev/null
+++ b/tools/debugserver/source/com.apple.debugserver.internal.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.internal</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/tools/debugserver/source/com.apple.debugserver.plist b/tools/debugserver/source/com.apple.debugserver.plist
new file mode 100644
index 000000000000..c07466e27cd2
--- /dev/null
+++ b/tools/debugserver/source/com.apple.debugserver.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver</string>
+ <key>UserName</key>
+ <string>mobile</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--launch=frontboard</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/tools/debugserver/source/com.apple.debugserver.posix.plist b/tools/debugserver/source/com.apple.debugserver.posix.plist
new file mode 100644
index 000000000000..4083f8a75c67
--- /dev/null
+++ b/tools/debugserver/source/com.apple.debugserver.posix.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.posix</string>
+ <key>UserName</key>
+ <string>mobile</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--launch=posix</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/tools/debugserver/source/debugserver-entitlements.plist b/tools/debugserver/source/debugserver-entitlements.plist
new file mode 100644
index 000000000000..4134ee958613
--- /dev/null
+++ b/tools/debugserver/source/debugserver-entitlements.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.springboard.debugapplications</key>
+ <true/>
+ <key>com.apple.backboardd.launchapplications</key>
+ <true/>
+ <key>com.apple.backboardd.debugapplications</key>
+ <true/>
+ <key>com.apple.frontboard.launchapplications</key>
+ <true/>
+ <key>com.apple.frontboard.debugapplications</key>
+ <true/>
+ <key>run-unsigned-code</key>
+ <true/>
+ <key>seatbelt-profiles</key>
+ <array>
+ <string>debugserver</string>
+ </array>
+ <key>com.apple.diagnosticd.diagnostic</key>
+ <true/>
+ <key>com.apple.security.network.server</key>
+ <true/>
+ <key>com.apple.security.network.client</key>
+ <true/>
+</dict>
+</plist>
diff --git a/tools/debugserver/source/debugserver-macosx-entitlements.plist b/tools/debugserver/source/debugserver-macosx-entitlements.plist
new file mode 100644
index 000000000000..eddbaa0063ef
--- /dev/null
+++ b/tools/debugserver/source/debugserver-macosx-entitlements.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.diagnosticd.diagnostic</key>
+ <true/>
+</dict>
+</plist>
diff --git a/tools/debugserver/source/debugserver.cpp b/tools/debugserver/source/debugserver.cpp
new file mode 100644
index 000000000000..78990a671d82
--- /dev/null
+++ b/tools/debugserver/source/debugserver.cpp
@@ -0,0 +1,1672 @@
+//===-- debugserver.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+#include <string>
+#include <vector>
+#include <asl.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <crt_externs.h> // for _NSGetEnviron()
+
+#if defined (__APPLE__)
+#include <sched.h>
+extern "C" int proc_set_wakemon_params(pid_t, int, int); // <libproc_internal.h> SPI
+#endif
+
+#include "CFString.h"
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DNBTimer.h"
+#include "PseudoTerminal.h"
+#include "RNBContext.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "RNBRemote.h"
+#include "SysSignal.h"
+
+// Global PID in case we get a signal and need to stop the process...
+nub_process_t g_pid = INVALID_NUB_PROCESS;
+
+//----------------------------------------------------------------------
+// Run loop modes which determine which run loop function will be called
+//----------------------------------------------------------------------
+typedef enum
+{
+ eRNBRunLoopModeInvalid = 0,
+ eRNBRunLoopModeGetStartModeFromRemoteProtocol,
+ eRNBRunLoopModeInferiorAttaching,
+ eRNBRunLoopModeInferiorLaunching,
+ eRNBRunLoopModeInferiorExecuting,
+ eRNBRunLoopModePlatformMode,
+ eRNBRunLoopModeExit
+} RNBRunLoopMode;
+
+
+//----------------------------------------------------------------------
+// Global Variables
+//----------------------------------------------------------------------
+RNBRemoteSP g_remoteSP;
+static int g_lockdown_opt = 0;
+static int g_applist_opt = 0;
+static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault;
+int g_disable_aslr = 0;
+
+int g_isatty = 0;
+bool g_detach_on_error = true;
+
+#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
+#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
+
+//----------------------------------------------------------------------
+// Get our program path and arguments from the remote connection.
+// We will need to start up the remote connection without a PID, get the
+// arguments, wait for the new process to finish launching and hit its
+// entry point, and then return the run loop mode that should come next.
+//----------------------------------------------------------------------
+RNBRunLoopMode
+RNBRunLoopGetStartModeFromRemote (RNBRemote* remote)
+{
+ std::string packet;
+
+ if (remote)
+ {
+ RNBContext& ctx = remote->Context();
+ uint32_t event_mask = RNBContext::event_read_packet_available |
+ RNBContext::event_read_thread_exiting;
+
+ // Spin waiting to get the A packet.
+ while (1)
+ {
+ DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
+
+ if (set_events & RNBContext::event_read_thread_exiting)
+ {
+ RNBLogSTDERR ("error: packet read thread exited.\n");
+ return eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ rnb_err_t err = rnb_err;
+ RNBRemote::PacketEnum type;
+
+ err = remote->HandleReceivedPacket (&type);
+
+ // check if we tried to attach to a process
+ if (type == RNBRemote::vattach || type == RNBRemote::vattachwait || type == RNBRemote::vattachorwait)
+ {
+ if (err == rnb_success)
+ {
+ RNBLogSTDOUT ("Attach succeeded, ready to debug.\n");
+ return eRNBRunLoopModeInferiorExecuting;
+ }
+ else
+ {
+ RNBLogSTDERR ("error: attach failed.\n");
+ return eRNBRunLoopModeExit;
+ }
+ }
+
+ if (err == rnb_success)
+ {
+ // If we got our arguments we are ready to launch using the arguments
+ // and any environment variables we received.
+ if (type == RNBRemote::set_argv)
+ {
+ return eRNBRunLoopModeInferiorLaunching;
+ }
+ }
+ else if (err == rnb_not_connected)
+ {
+ RNBLogSTDERR ("error: connection lost.\n");
+ return eRNBRunLoopModeExit;
+ }
+ else
+ {
+ // a catch all for any other gdb remote packets that failed
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
+ continue;
+ }
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ }
+ }
+ return eRNBRunLoopModeExit;
+}
+
+
+//----------------------------------------------------------------------
+// This run loop mode will wait for the process to launch and hit its
+// entry point. It will currently ignore all events except for the
+// process state changed event, where it watches for the process stopped
+// or crash process state.
+//----------------------------------------------------------------------
+RNBRunLoopMode
+RNBRunLoopLaunchInferior (RNBRemote *remote, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio)
+{
+ RNBContext& ctx = remote->Context();
+
+ // The Process stuff takes a c array, the RNBContext has a vector...
+ // So make up a c array.
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__, ctx.ArgumentAtIndex(0));
+
+ size_t inferior_argc = ctx.ArgumentCount();
+ // Initialize inferior_argv with inferior_argc + 1 NULLs
+ std::vector<const char *> inferior_argv(inferior_argc + 1, NULL);
+
+ size_t i;
+ for (i = 0; i < inferior_argc; i++)
+ inferior_argv[i] = ctx.ArgumentAtIndex(i);
+
+ // Pass the environment array the same way:
+
+ size_t inferior_envc = ctx.EnvironmentCount();
+ // Initialize inferior_argv with inferior_argc + 1 NULLs
+ std::vector<const char *> inferior_envp(inferior_envc + 1, NULL);
+
+ for (i = 0; i < inferior_envc; i++)
+ inferior_envp[i] = ctx.EnvironmentAtIndex(i);
+
+ // Our launch type hasn't been set to anything concrete, so we need to
+ // figure our how we are going to launch automatically.
+
+ nub_launch_flavor_t launch_flavor = g_launch_flavor;
+ if (launch_flavor == eLaunchFlavorDefault)
+ {
+ // Our default launch method is posix spawn
+ launch_flavor = eLaunchFlavorPosixSpawn;
+
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(inferior_argv[0], ".app"))
+ {
+ launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(inferior_argv[0], ".app"))
+ {
+ launch_flavor = eLaunchFlavorBKS;
+ }
+#elif defined WITH_SPRINGBOARD
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (strstr(inferior_argv[0], ".app"))
+ {
+ launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ ctx.SetLaunchFlavor(launch_flavor);
+ char resolved_path[PATH_MAX];
+
+ // If we fail to resolve the path to our executable, then just use what we
+ // were given and hope for the best
+ if ( !DNBResolveExecutablePath (inferior_argv[0], resolved_path, sizeof(resolved_path)) )
+ ::strncpy(resolved_path, inferior_argv[0], sizeof(resolved_path));
+
+ char launch_err_str[PATH_MAX];
+ launch_err_str[0] = '\0';
+ const char * cwd = (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath()
+ : ctx.GetWorkingDirectory());
+ const char *process_event = ctx.GetProcessEvent();
+ nub_process_t pid = DNBProcessLaunch (resolved_path,
+ &inferior_argv[0],
+ &inferior_envp[0],
+ cwd,
+ stdin_path,
+ stdout_path,
+ stderr_path,
+ no_stdio,
+ launch_flavor,
+ g_disable_aslr,
+ process_event,
+ launch_err_str,
+ sizeof(launch_err_str));
+
+ g_pid = pid;
+
+ if (pid == INVALID_NUB_PROCESS && strlen (launch_err_str) > 0)
+ {
+ DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__, launch_err_str);
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ ctx.LaunchStatus().SetErrorString(launch_err_str);
+ }
+ else if (pid == INVALID_NUB_PROCESS)
+ {
+ DNBLogThreaded ("%s DNBProcessLaunch() failed to launch process, unknown failure", __FUNCTION__);
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ ctx.LaunchStatus().SetErrorString("<unknown failure>");
+ }
+ else
+ {
+ ctx.LaunchStatus().Clear();
+ }
+
+ if (remote->Comm().IsConnected())
+ {
+ // It we are connected already, the next thing gdb will do is ask
+ // whether the launch succeeded, and if not, whether there is an
+ // error code. So we need to fetch one packet from gdb before we wait
+ // on the stop from the target.
+
+ uint32_t event_mask = RNBContext::event_read_packet_available;
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ rnb_err_t err = rnb_err;
+ RNBRemote::PacketEnum type;
+
+ err = remote->HandleReceivedPacket (&type);
+
+ if (err != rnb_success)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ if (type != RNBRemote::query_launch_success)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__);
+ }
+ }
+ }
+
+ while (pid != INVALID_NUB_PROCESS)
+ {
+ // Wait for process to start up and hit entry point
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid);
+ nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL);
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events);
+
+ if (set_events == 0)
+ {
+ pid = INVALID_NUB_PROCESS;
+ g_pid = pid;
+ }
+ else
+ {
+ if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
+ {
+ nub_state_t pid_state = DNBProcessGetState (pid);
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state));
+
+ switch (pid_state)
+ {
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateSuspended:
+ break; // Ignore
+
+ case eStateRunning:
+ case eStateStepping:
+ // Still waiting to stop at entry point...
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ ctx.SetProcessID(pid);
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateDetached:
+ case eStateExited:
+ pid = INVALID_NUB_PROCESS;
+ g_pid = pid;
+ return eRNBRunLoopModeExit;
+ }
+ }
+
+ DNBProcessResetEvents(pid, set_events);
+ }
+ }
+
+ return eRNBRunLoopModeExit;
+}
+
+
+//----------------------------------------------------------------------
+// This run loop mode will wait for the process to launch and hit its
+// entry point. It will currently ignore all events except for the
+// process state changed event, where it watches for the process stopped
+// or crash process state.
+//----------------------------------------------------------------------
+RNBRunLoopMode
+RNBRunLoopLaunchAttaching (RNBRemote *remote, nub_process_t attach_pid, nub_process_t& pid)
+{
+ RNBContext& ctx = remote->Context();
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid);
+ char err_str[1024];
+ pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str));
+ g_pid = pid;
+
+ if (pid == INVALID_NUB_PROCESS)
+ {
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ ctx.LaunchStatus().SetErrorString(err_str);
+ return eRNBRunLoopModeExit;
+ }
+ else
+ {
+ ctx.SetProcessID(pid);
+ return eRNBRunLoopModeInferiorExecuting;
+ }
+}
+
+//----------------------------------------------------------------------
+// Watch for signals:
+// SIGINT: so we can halt our inferior. (disabled for now)
+// SIGPIPE: in case our child process dies
+//----------------------------------------------------------------------
+int g_sigint_received = 0;
+int g_sigpipe_received = 0;
+void
+signal_handler(int signo)
+{
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
+
+ switch (signo)
+ {
+ case SIGINT:
+ g_sigint_received++;
+ if (g_pid != INVALID_NUB_PROCESS)
+ {
+ // Only send a SIGINT once...
+ if (g_sigint_received == 1)
+ {
+ switch (DNBProcessGetState (g_pid))
+ {
+ case eStateRunning:
+ case eStateStepping:
+ DNBProcessSignal (g_pid, SIGSTOP);
+ return;
+ default:
+ break;
+ }
+ }
+ }
+ exit (SIGINT);
+ break;
+
+ case SIGPIPE:
+ g_sigpipe_received = 1;
+ break;
+ }
+}
+
+// Return the new run loop mode based off of the current process state
+RNBRunLoopMode
+HandleProcessStateChange (RNBRemote *remote, bool initialize)
+{
+ RNBContext& ctx = remote->Context();
+ nub_process_t pid = ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ nub_state_t pid_state = DNBProcessGetState (pid);
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
+
+ switch (pid_state)
+ {
+ case eStateInvalid:
+ case eStateUnloaded:
+ // Something bad happened
+ return eRNBRunLoopModeExit;
+ break;
+
+ case eStateAttaching:
+ case eStateLaunching:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateSuspended:
+ case eStateCrashed:
+ case eStateStopped:
+ // If we stop due to a signal, so clear the fact that we got a SIGINT
+ // so we can stop ourselves again (but only while our inferior
+ // process is running..)
+ g_sigint_received = 0;
+ if (initialize == false)
+ {
+ // Compare the last stop count to our current notion of a stop count
+ // to make sure we don't notify more than once for a given stop.
+ nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
+ bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
+ if (pid_stop_count_changed)
+ {
+ remote->FlushSTDIO();
+
+ if (ctx.GetProcessStopCount() == 1)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);
+ }
+ else
+ {
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);
+ remote->NotifyThatProcessStopped ();
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);
+ }
+ }
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateStepping:
+ case eStateRunning:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateExited:
+ remote->HandlePacket_last_signal(NULL);
+ case eStateDetached:
+ return eRNBRunLoopModeExit;
+
+ }
+
+ // Catch all...
+ return eRNBRunLoopModeExit;
+}
+// This function handles the case where our inferior program is stopped and
+// we are waiting for gdb remote protocol packets. When a packet occurs that
+// makes the inferior run, we need to leave this function with a new state
+// as the return code.
+RNBRunLoopMode
+RNBRunLoopInferiorExecuting (RNBRemote *remote)
+{
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ RNBContext& ctx = remote->Context();
+
+ // Init our mode and set 'is_running' based on the current process state
+ RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
+
+ while (ctx.ProcessID() != INVALID_NUB_PROCESS)
+ {
+
+ std::string set_events_str;
+ uint32_t event_mask = ctx.NormalEventBits();
+
+ if (!ctx.ProcessStateRunning())
+ {
+ // Clear some bits if we are not running so we don't send any async packets
+ event_mask &= ~RNBContext::event_proc_stdio_available;
+ event_mask &= ~RNBContext::event_proc_profile_data;
+ }
+
+ // We want to make sure we consume all process state changes and have
+ // whomever is notifying us to wait for us to reset the event bit before
+ // continuing.
+ //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
+
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str));
+
+ if (set_events)
+ {
+ if ((set_events & RNBContext::event_proc_thread_exiting) ||
+ (set_events & RNBContext::event_proc_stdio_available))
+ {
+ remote->FlushSTDIO();
+ }
+
+ if (set_events & RNBContext::event_proc_profile_data)
+ {
+ remote->SendAsyncProfileData();
+ }
+
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ // handleReceivedPacket will take care of resetting the
+ // event_read_packet_available events when there are no more...
+ set_events ^= RNBContext::event_read_packet_available;
+
+ if (ctx.ProcessStateRunning())
+ {
+ if (remote->HandleAsyncPacket() == rnb_not_connected)
+ {
+ // TODO: connect again? Exit?
+ }
+ }
+ else
+ {
+ if (remote->HandleReceivedPacket() == rnb_not_connected)
+ {
+ // TODO: connect again? Exit?
+ }
+ }
+ }
+
+ if (set_events & RNBContext::event_proc_state_changed)
+ {
+ mode = HandleProcessStateChange (remote, false);
+ ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
+ set_events ^= RNBContext::event_proc_state_changed;
+ }
+
+ if (set_events & RNBContext::event_proc_thread_exiting)
+ {
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting)
+ {
+ // Out remote packet receiving thread exited, exit for now.
+ if (ctx.HasValidProcessID())
+ {
+ // TODO: We should add code that will leave the current process
+ // in its current state and listen for another connection...
+ if (ctx.ProcessStateRunning())
+ {
+ if (ctx.GetDetachOnError())
+ {
+ DNBLog ("debugserver's event read thread is exiting, detaching from the inferior process.");
+ DNBProcessDetach (ctx.ProcessID());
+ }
+ else
+ {
+ DNBLog ("debugserver's event read thread is exiting, killing the inferior process.");
+ DNBProcessKill (ctx.ProcessID());
+ }
+ }
+ else
+ {
+ if (ctx.GetDetachOnError())
+ {
+ DNBLog ("debugserver's event read thread is exiting, detaching from the inferior process.");
+ DNBProcessDetach (ctx.ProcessID());
+ }
+ }
+ }
+ mode = eRNBRunLoopModeExit;
+ }
+ }
+
+ // Reset all event bits that weren't reset for now...
+ if (set_events != 0)
+ ctx.Events().ResetEvents(set_events);
+
+ if (mode != eRNBRunLoopModeInferiorExecuting)
+ break;
+ }
+
+ return mode;
+}
+
+
+RNBRunLoopMode
+RNBRunLoopPlatform (RNBRemote *remote)
+{
+ RNBRunLoopMode mode = eRNBRunLoopModePlatformMode;
+ RNBContext& ctx = remote->Context();
+
+ while (mode == eRNBRunLoopModePlatformMode)
+ {
+ std::string set_events_str;
+ const uint32_t event_mask = RNBContext::event_read_packet_available |
+ RNBContext::event_read_thread_exiting;
+
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str));
+
+ if (set_events)
+ {
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ if (remote->HandleReceivedPacket() == rnb_not_connected)
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting)
+ {
+ mode = eRNBRunLoopModeExit;
+ }
+ ctx.Events().ResetEvents(set_events);
+ }
+ }
+ return eRNBRunLoopModeExit;
+}
+
+//----------------------------------------------------------------------
+// Convenience function to set up the remote listening port
+// Returns 1 for success 0 for failure.
+//----------------------------------------------------------------------
+
+static void
+PortWasBoundCallbackUnixSocket (const void *baton, in_port_t port)
+{
+ //::printf ("PortWasBoundCallbackUnixSocket (baton = %p, port = %u)\n", baton, port);
+
+ const char *unix_socket_name = (const char *)baton;
+
+ if (unix_socket_name && unix_socket_name[0])
+ {
+ // We were given a unix socket name to use to communicate the port
+ // that we ended up binding to back to our parent process
+ struct sockaddr_un saddr_un;
+ int s = ::socket (AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ {
+ perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");
+ exit(1);
+ }
+
+ saddr_un.sun_family = AF_UNIX;
+ ::strncpy(saddr_un.sun_path, unix_socket_name, sizeof(saddr_un.sun_path) - 1);
+ saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
+ saddr_un.sun_len = SUN_LEN (&saddr_un);
+
+ if (::connect (s, (struct sockaddr *)&saddr_un, static_cast<socklen_t>(SUN_LEN (&saddr_un))) < 0)
+ {
+ perror("error: connect (socket, &saddr_un, saddr_un_len)");
+ exit(1);
+ }
+
+ //::printf ("connect () sucess!!\n");
+
+
+ // We were able to connect to the socket, now write our PID so whomever
+ // launched us will know this process's ID
+ RNBLogSTDOUT ("Listening to port %i...\n", port);
+
+ char pid_str[64];
+ const int pid_str_len = ::snprintf (pid_str, sizeof(pid_str), "%u", port);
+ const ssize_t bytes_sent = ::send (s, pid_str, pid_str_len, 0);
+
+ if (pid_str_len != bytes_sent)
+ {
+ perror("error: send (s, pid_str, pid_str_len, 0)");
+ exit (1);
+ }
+
+ //::printf ("send () sucess!!\n");
+
+ // We are done with the socket
+ close (s);
+ }
+}
+
+static void
+PortWasBoundCallbackNamedPipe (const void *baton, uint16_t port)
+{
+ const char *named_pipe = (const char *)baton;
+ if (named_pipe && named_pipe[0])
+ {
+ int fd = ::open(named_pipe, O_WRONLY);
+ if (fd > -1)
+ {
+ char port_str[64];
+ const ssize_t port_str_len = ::snprintf (port_str, sizeof(port_str), "%u", port);
+ // Write the port number as a C string with the NULL terminator
+ ::write (fd, port_str, port_str_len + 1);
+ close (fd);
+ }
+ }
+}
+
+static int
+ConnectRemote (RNBRemote *remote,
+ const char *host,
+ int port,
+ bool reverse_connect,
+ const char *named_pipe_path,
+ const char *unix_socket_name)
+{
+ if (!remote->Comm().IsConnected())
+ {
+ if (reverse_connect)
+ {
+ if (port == 0)
+ {
+ DNBLogThreaded("error: invalid port supplied for reverse connection: %i.\n", port);
+ return 0;
+ }
+ if (remote->Comm().Connect(host, port) != rnb_success)
+ {
+ DNBLogThreaded("Failed to reverse connect to %s:%i.\n", host, port);
+ return 0;
+ }
+ }
+ else
+ {
+ if (port != 0)
+ RNBLogSTDOUT ("Listening to port %i for a connection from %s...\n", port, host ? host : "127.0.0.1");
+ if (unix_socket_name && unix_socket_name[0])
+ {
+ if (remote->Comm().Listen(host, port, PortWasBoundCallbackUnixSocket, unix_socket_name) != rnb_success)
+ {
+ RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
+ return 0;
+ }
+ }
+ else
+ {
+ if (remote->Comm().Listen(host, port, PortWasBoundCallbackNamedPipe, named_pipe_path) != rnb_success)
+ {
+ RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
+ return 0;
+ }
+ }
+ }
+ remote->StartReadRemoteDataThread();
+ }
+ return 1;
+}
+
+//----------------------------------------------------------------------
+// ASL Logging callback that can be registered with DNBLogSetLogCallback
+//----------------------------------------------------------------------
+void
+ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
+{
+ if (format == NULL)
+ return;
+ 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.%s-%s", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_STR);
+ ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
+ }
+
+ int asl_level;
+ if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT;
+ else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR;
+ else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING;
+ else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO;
+ else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG;
+
+ ::asl_vlog (NULL, g_aslmsg, asl_level, format, args);
+}
+
+//----------------------------------------------------------------------
+// FILE based Logging callback that can be registered with
+// DNBLogSetLogCallback
+//----------------------------------------------------------------------
+void
+FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
+{
+ if (baton == NULL || format == NULL)
+ return;
+
+ ::vfprintf ((FILE *)baton, format, args);
+ ::fprintf ((FILE *)baton, "\n");
+}
+
+
+void
+show_usage_and_exit (int exit_code)
+{
+ RNBLogSTDERR ("Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR (" %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR (" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR (" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR (" %s host:port --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR (" %s /path/file --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
+ exit (exit_code);
+}
+
+
+//----------------------------------------------------------------------
+// option descriptors for getopt_long_only()
+//----------------------------------------------------------------------
+static struct option g_long_options[] =
+{
+ { "attach", required_argument, NULL, 'a' },
+ { "arch", required_argument, NULL, 'A' },
+ { "debug", no_argument, NULL, 'g' },
+ { "kill-on-error", no_argument, NULL, 'K' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k"
+ { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t"
+ { "log-file", required_argument, NULL, 'l' },
+ { "log-flags", required_argument, NULL, 'f' },
+ { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only)
+ { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose name starts with ARG
+ { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name
+ { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name
+ { "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture.
+ { "stdio-path", required_argument, NULL, 's' }, // Set the STDIO path to be used when launching applications (STDIN, STDOUT and STDERR) (only if debugserver launches the process)
+ { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process)
+ { "stdout-path", required_argument, NULL, 'O' }, // Set the STDOUT path to be used when launching applications (only if debugserver launches the process)
+ { "stderr-path", required_argument, NULL, 'E' }, // Set the STDERR path to be used when launching applications (only if debugserver launches the process)
+ { "no-stdio", no_argument, NULL, 'n' }, // Do not set up any stdio (perhaps the program is a GUI program) (only if debugserver launches the process)
+ { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session
+ { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization
+ { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process)
+ { "platform", required_argument, NULL, 'p' }, // Put this executable into a remote platform mode
+ { "unix-socket", required_argument, NULL, 'u' }, // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use
+ { "named-pipe", required_argument, NULL, 'P' },
+ { "reverse-connect", no_argument, NULL, 'R' },
+ { "env", required_argument, NULL, 'e' }, // When debugserver launches the process, set a single environment entry as specified by the option value ("./debugserver -e FOO=1 -e BAR=2 localhost:1234 -- /bin/ls")
+ { "forward-env", no_argument, NULL, 'F' }, // When debugserver launches the process, forward debugserver's current environment variables to the child process ("./debugserver -F localhost:1234 -- /bin/ls"
+ { NULL, 0, NULL, 0 }
+};
+
+
+//----------------------------------------------------------------------
+// main
+//----------------------------------------------------------------------
+int
+main (int argc, char *argv[])
+{
+ const char *argv_sub_zero = argv[0]; // save a copy of argv[0] for error reporting post-launch
+
+#if defined (__APPLE__)
+ pthread_setname_np ("main thread");
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0)
+ {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+
+ ::proc_set_wakemon_params (getpid(), 500, 0); // Allow up to 500 wakeups/sec to avoid EXC_RESOURCE for normal use.
+#endif
+#endif
+
+ g_isatty = ::isatty (STDIN_FILENO);
+
+ // ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
+ // getuid(),
+ // geteuid(),
+ // getgid(),
+ // getegid());
+
+
+ // signal (SIGINT, signal_handler);
+ signal (SIGPIPE, signal_handler);
+ signal (SIGHUP, signal_handler);
+
+ // We're always sitting in waitpid or kevent waiting on our target process' death,
+ // we don't need no stinking SIGCHLD's...
+
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+ g_remoteSP.reset (new RNBRemote ());
+
+
+ RNBRemote *remote = g_remoteSP.get();
+ if (remote == NULL)
+ {
+ RNBLogSTDERR ("error: failed to create a remote connection class\n");
+ return -1;
+ }
+
+ RNBContext& ctx = remote->Context();
+
+ int i;
+ int attach_pid = INVALID_NUB_PROCESS;
+
+ FILE* log_file = NULL;
+ uint32_t log_flags = 0;
+ // Parse our options
+ int ch;
+ int long_option_index = 0;
+ int debug = 0;
+ std::string compile_options;
+ std::string waitfor_pid_name; // Wait for a process that starts with this name
+ std::string attach_pid_name;
+ std::string arch_name;
+ std::string working_dir; // The new working directory to use for the inferior
+ std::string unix_socket_name; // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use
+ std::string named_pipe_path; // If we need to handshake with our parent process, an option will be passed down that specifies a named pipe to use
+ useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
+ useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever.
+ bool no_stdio = false;
+ bool reverse_connect = false; // Set to true by an option to indicate we should reverse connect to the host:port supplied as the first debugserver argument
+
+#if !defined (DNBLOG_ENABLED)
+ compile_options += "(no-logging) ";
+#endif
+
+ RNBRunLoopMode start_mode = eRNBRunLoopModeExit;
+
+ char short_options[512];
+ uint32_t short_options_idx = 0;
+
+ // Handle the two case that don't have short options in g_long_options
+ short_options[short_options_idx++] = 'k';
+ short_options[short_options_idx++] = 't';
+
+ for (i=0; g_long_options[i].name != NULL; ++i)
+ {
+ if (isalpha(g_long_options[i].val))
+ {
+ short_options[short_options_idx++] = g_long_options[i].val;
+ switch (g_long_options[i].has_arg)
+ {
+ default:
+ case no_argument:
+ break;
+
+ case optional_argument:
+ short_options[short_options_idx++] = ':';
+ // Fall through to required_argument case below...
+ case required_argument:
+ short_options[short_options_idx++] = ':';
+ break;
+ }
+ }
+ }
+ // NULL terminate the short option string.
+ short_options[short_options_idx++] = '\0';
+
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+
+ while ((ch = getopt_long_only(argc, argv, short_options, g_long_options, &long_option_index)) != -1)
+ {
+ DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
+ ch, (uint8_t)ch,
+ g_long_options[long_option_index].name,
+ g_long_options[long_option_index].has_arg ? '=' : ' ',
+ optarg ? optarg : "");
+ switch (ch)
+ {
+ case 0: // Any optional that auto set themselves will return 0
+ break;
+
+ case 'A':
+ if (optarg && optarg[0])
+ arch_name.assign(optarg);
+ break;
+
+ case 'a':
+ if (optarg && optarg[0])
+ {
+ if (isdigit(optarg[0]))
+ {
+ char *end = NULL;
+ attach_pid = static_cast<int>(strtoul(optarg, &end, 0));
+ if (end == NULL || *end != '\0')
+ {
+ RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg);
+ exit (4);
+ }
+ }
+ else
+ {
+ attach_pid_name = optarg;
+ }
+ start_mode = eRNBRunLoopModeInferiorAttaching;
+ }
+ break;
+
+ // --waitfor=NAME
+ case 'w':
+ if (optarg && optarg[0])
+ {
+ waitfor_pid_name = optarg;
+ start_mode = eRNBRunLoopModeInferiorAttaching;
+ }
+ break;
+
+ // --waitfor-interval=USEC
+ case 'i':
+ if (optarg && optarg[0])
+ {
+ char *end = NULL;
+ waitfor_interval = static_cast<useconds_t>(strtoul(optarg, &end, 0));
+ if (end == NULL || *end != '\0')
+ {
+ RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg);
+ exit (6);
+ }
+ }
+ break;
+
+ // --waitfor-duration=SEC
+ case 'd':
+ if (optarg && optarg[0])
+ {
+ char *end = NULL;
+ waitfor_duration = static_cast<useconds_t>(strtoul(optarg, &end, 0));
+ if (end == NULL || *end != '\0')
+ {
+ RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg);
+ exit (7);
+ }
+ }
+ break;
+
+ case 'K':
+ g_detach_on_error = false;
+
+ case 'W':
+ if (optarg && optarg[0])
+ working_dir.assign(optarg);
+ break;
+
+ case 'x':
+ if (optarg && optarg[0])
+ {
+ if (strcasecmp(optarg, "auto") == 0)
+ g_launch_flavor = eLaunchFlavorDefault;
+ else if (strcasestr(optarg, "posix") == optarg)
+ g_launch_flavor = eLaunchFlavorPosixSpawn;
+ else if (strcasestr(optarg, "fork") == optarg)
+ g_launch_flavor = eLaunchFlavorForkExec;
+#ifdef WITH_SPRINGBOARD
+ else if (strcasestr(optarg, "spring") == optarg)
+ g_launch_flavor = eLaunchFlavorSpringBoard;
+#endif
+#ifdef WITH_BKS
+ else if (strcasestr(optarg, "backboard") == optarg)
+ g_launch_flavor = eLaunchFlavorBKS;
+#endif
+#ifdef WITH_FBS
+ else if (strcasestr(optarg, "frontboard") == optarg)
+ g_launch_flavor = eLaunchFlavorFBS;
+#endif
+
+ else
+ {
+ RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg);
+ RNBLogSTDERR ("Valid values TYPE are:\n");
+ RNBLogSTDERR (" auto Auto-detect the best launch method to use.\n");
+ RNBLogSTDERR (" posix Launch the executable using posix_spawn.\n");
+ RNBLogSTDERR (" fork Launch the executable using fork and exec.\n");
+#ifdef WITH_SPRINGBOARD
+ RNBLogSTDERR (" spring Launch the executable through Springboard.\n");
+#endif
+#ifdef WITH_BKS
+ RNBLogSTDERR (" backboard Launch the executable through BackBoard Services.\n");
+#endif
+#ifdef WITH_FBS
+ RNBLogSTDERR (" frontboard Launch the executable through FrontBoard Services.\n");
+#endif
+ exit (5);
+ }
+ }
+ break;
+
+ case 'l': // Set Log File
+ if (optarg && optarg[0])
+ {
+ if (strcasecmp(optarg, "stdout") == 0)
+ log_file = stdout;
+ else if (strcasecmp(optarg, "stderr") == 0)
+ log_file = stderr;
+ else
+ {
+ log_file = fopen(optarg, "w");
+ if (log_file != NULL)
+ setlinebuf(log_file);
+ }
+
+ if (log_file == NULL)
+ {
+ const char *errno_str = strerror(errno);
+ RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
+ }
+ }
+ break;
+
+ case 'f': // Log Flags
+ if (optarg && optarg[0])
+ log_flags = static_cast<uint32_t>(strtoul(optarg, NULL, 0));
+ break;
+
+ case 'g':
+ debug = 1;
+ DNBLogSetDebug(debug);
+ break;
+
+ case 't':
+ g_applist_opt = 1;
+ break;
+
+ case 'k':
+ g_lockdown_opt = 1;
+ break;
+
+ case 'r':
+ // Do nothing, native regs is the default these days
+ break;
+
+ case 'R':
+ reverse_connect = true;
+ break;
+ case 'v':
+ DNBLogSetVerbose(1);
+ break;
+
+ case 's':
+ ctx.GetSTDIN().assign(optarg);
+ ctx.GetSTDOUT().assign(optarg);
+ ctx.GetSTDERR().assign(optarg);
+ break;
+
+ case 'I':
+ ctx.GetSTDIN().assign(optarg);
+ break;
+
+ case 'O':
+ ctx.GetSTDOUT().assign(optarg);
+ break;
+
+ case 'E':
+ ctx.GetSTDERR().assign(optarg);
+ break;
+
+ case 'n':
+ no_stdio = true;
+ break;
+
+ case 'S':
+ // Put debugserver into a new session. Terminals group processes
+ // into sessions and when a special terminal key sequences
+ // (like control+c) are typed they can cause signals to go out to
+ // all processes in a session. Using this --setsid (-S) option
+ // will cause debugserver to run in its own sessions and be free
+ // from such issues.
+ //
+ // This is useful when debugserver is spawned from a command
+ // line application that uses debugserver to do the debugging,
+ // yet that application doesn't want debugserver receiving the
+ // signals sent to the session (i.e. dying when anyone hits ^C).
+ setsid();
+ break;
+ case 'D':
+ g_disable_aslr = 1;
+ break;
+
+ case 'p':
+ start_mode = eRNBRunLoopModePlatformMode;
+ break;
+
+ case 'u':
+ unix_socket_name.assign (optarg);
+ break;
+
+ case 'P':
+ named_pipe_path.assign (optarg);
+ break;
+
+ case 'e':
+ // Pass a single specified environment variable down to the process that gets launched
+ remote->Context().PushEnvironment(optarg);
+ break;
+
+ case 'F':
+ // Pass the current environment down to the process that gets launched
+ {
+ char **host_env = *_NSGetEnviron();
+ char *env_entry;
+ size_t i;
+ for (i=0; (env_entry = host_env[i]) != NULL; ++i)
+ remote->Context().PushEnvironment(env_entry);
+ }
+ break;
+ }
+ }
+
+ if (arch_name.empty())
+ {
+#if defined (__arm__)
+ arch_name.assign ("arm");
+#endif
+ }
+ else
+ {
+ DNBSetArchitecture (arch_name.c_str());
+ }
+
+// if (arch_name.empty())
+// {
+// fprintf(stderr, "error: no architecture was specified\n");
+// exit (8);
+// }
+ // Skip any options we consumed with getopt_long_only
+ argc -= optind;
+ argv += optind;
+
+
+ if (!working_dir.empty())
+ {
+ if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false)
+ {
+ RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str());
+ exit (8);
+ }
+ }
+
+ remote->Context().SetDetachOnError(g_detach_on_error);
+
+ remote->Initialize();
+
+ // It is ok for us to set NULL as the logfile (this will disable any logging)
+
+ if (log_file != NULL)
+ {
+ DNBLogSetLogCallback(FileLogCallback, log_file);
+ // If our log file was set, yet we have no log flags, log everything!
+ if (log_flags == 0)
+ log_flags = LOG_ALL | LOG_RNB_ALL;
+
+ DNBLogSetLogMask (log_flags);
+ }
+ else
+ {
+ // Enable DNB logging
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+ DNBLogSetLogMask (log_flags);
+
+ }
+
+ if (DNBLogEnabled())
+ {
+ for (i=0; i<argc; i++)
+ DNBLogDebug("argv[%i] = %s", i, argv[i]);
+ }
+
+ // as long as we're dropping remotenub in as a replacement for gdbserver,
+ // explicitly note that this is not gdbserver.
+
+ RNBLogSTDOUT ("%s-%s %sfor %s.\n",
+ DEBUGSERVER_PROGRAM_NAME,
+ DEBUGSERVER_VERSION_STR,
+ compile_options.c_str(),
+ RNB_ARCH);
+
+ std::string host;
+ int port = INT32_MAX;
+ char str[PATH_MAX];
+ str[0] = '\0';
+
+ if (g_lockdown_opt == 0 && g_applist_opt == 0)
+ {
+ // Make sure we at least have port
+ if (argc < 1)
+ {
+ show_usage_and_exit (1);
+ }
+ // accept 'localhost:' prefix on port number
+
+ int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &port);
+ if (items_scanned == 2)
+ {
+ host = str;
+ DNBLogDebug("host = '%s' port = %i", host.c_str(), port);
+ }
+ else
+ {
+ // No hostname means "localhost"
+ int items_scanned = ::sscanf (argv[0], "%i", &port);
+ if (items_scanned == 1)
+ {
+ host = "127.0.0.1";
+ DNBLogDebug("host = '%s' port = %i", host.c_str(), port);
+ }
+ else if (argv[0][0] == '/')
+ {
+ port = INT32_MAX;
+ strncpy(str, argv[0], sizeof(str));
+ }
+ else
+ {
+ show_usage_and_exit (2);
+ }
+ }
+
+ // We just used the 'host:port' or the '/path/file' arg...
+ argc--;
+ argv++;
+
+ }
+
+ // If we know we're waiting to attach, we don't need any of this other info.
+ if (start_mode != eRNBRunLoopModeInferiorAttaching &&
+ start_mode != eRNBRunLoopModePlatformMode)
+ {
+ if (argc == 0 || g_lockdown_opt)
+ {
+ if (g_lockdown_opt != 0)
+ {
+ // Work around for SIGPIPE crashes due to posix_spawn issue.
+ // We have to close STDOUT and STDERR, else the first time we
+ // try and do any, we get SIGPIPE and die as posix_spawn is
+ // doing bad things with our file descriptors at the moment.
+ int null = open("/dev/null", O_RDWR);
+ dup2(null, STDOUT_FILENO);
+ dup2(null, STDERR_FILENO);
+ }
+ else if (g_applist_opt != 0)
+ {
+ // List all applications we are able to see
+ std::string applist_plist;
+ int err = ListApplications(applist_plist, false, false);
+ if (err == 0)
+ {
+ fputs (applist_plist.c_str(), stdout);
+ }
+ else
+ {
+ RNBLogSTDERR ("error: ListApplications returned error %i\n", err);
+ }
+ // Exit with appropriate error if we were asked to list the applications
+ // with no other args were given (and we weren't trying to do this over
+ // lockdown)
+ return err;
+ }
+
+ DNBLogDebug("Get args from remote protocol...");
+ start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
+ }
+ else
+ {
+ start_mode = eRNBRunLoopModeInferiorLaunching;
+ // Fill in the argv array in the context from the rest of our args.
+ // Skip the name of this executable and the port number
+ for (int i = 0; i < argc; i++)
+ {
+ DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);
+ ctx.PushArgument (argv[i]);
+ }
+ }
+ }
+
+ if (start_mode == eRNBRunLoopModeExit)
+ return -1;
+
+ RNBRunLoopMode mode = start_mode;
+ char err_str[1024] = {'\0'};
+
+ while (mode != eRNBRunLoopModeExit)
+ {
+ switch (mode)
+ {
+ case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
+#ifdef WITH_LOCKDOWN
+ if (g_lockdown_opt)
+ {
+ if (!remote->Comm().IsConnected())
+ {
+ if (remote->Comm().ConnectToService () != rnb_success)
+ {
+ RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
+ mode = eRNBRunLoopModeExit;
+ }
+ else if (g_applist_opt != 0)
+ {
+ // List all applications we are able to see
+ std::string applist_plist;
+ if (ListApplications(applist_plist, false, false) == 0)
+ {
+ DNBLogDebug("Task list: %s", applist_plist.c_str());
+
+ remote->Comm().Write(applist_plist.c_str(), applist_plist.size());
+ // Issue a read that will never yield any data until the other side
+ // closes the socket so this process doesn't just exit and cause the
+ // socket to close prematurely on the other end and cause data loss.
+ std::string buf;
+ remote->Comm().Read(buf);
+ }
+ remote->Comm().Disconnect(false);
+ mode = eRNBRunLoopModeExit;
+ break;
+ }
+ else
+ {
+ // Start watching for remote packets
+ remote->StartReadRemoteDataThread();
+ }
+ }
+ }
+ else
+#endif
+ if (port != INT32_MAX)
+ {
+ if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ }
+ else if (str[0] == '/')
+ {
+ if (remote->Comm().OpenFile (str))
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (mode != eRNBRunLoopModeExit)
+ {
+ RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n");
+
+ mode = RNBRunLoopGetStartModeFromRemote (remote);
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorAttaching:
+ if (!waitfor_pid_name.empty())
+ {
+ // Set our end wait time if we are using a waitfor-duration
+ // option that may have been specified
+ struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
+ if (waitfor_duration != 0)
+ {
+ DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
+ timeout_ptr = &attach_timeout_abstime;
+ }
+ nub_launch_flavor_t launch_flavor = g_launch_flavor;
+ if (launch_flavor == eLaunchFlavorDefault)
+ {
+ // Our default launch method is posix spawn
+ launch_flavor = eLaunchFlavorPosixSpawn;
+
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find (".app") != std::string::npos)
+ {
+ launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find (".app") != std::string::npos)
+ {
+ launch_flavor = eLaunchFlavorBKS;
+ }
+#elif defined WITH_SPRINGBOARD
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find (".app") != std::string::npos)
+ {
+ launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ ctx.SetLaunchFlavor(launch_flavor);
+ bool ignore_existing = false;
+ RNBLogSTDOUT ("Waiting to attach to process %s...\n", waitfor_pid_name.c_str());
+ nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, ignore_existing, timeout_ptr, waitfor_interval, err_str, sizeof(err_str));
+ g_pid = pid;
+
+ if (pid == INVALID_NUB_PROCESS)
+ {
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ ctx.LaunchStatus().SetErrorString(err_str);
+ RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s\n", waitfor_pid_name.c_str(), err_str);
+ mode = eRNBRunLoopModeExit;
+ }
+ else
+ {
+ ctx.SetProcessID(pid);
+ mode = eRNBRunLoopModeInferiorExecuting;
+ }
+ }
+ else if (attach_pid != INVALID_NUB_PROCESS)
+ {
+
+ RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid);
+ nub_process_t attached_pid;
+ mode = RNBRunLoopLaunchAttaching (remote, attach_pid, attached_pid);
+ if (mode != eRNBRunLoopModeInferiorExecuting)
+ {
+ const char *error_str = remote->Context().LaunchStatus().AsString();
+ RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error.");
+ mode = eRNBRunLoopModeExit;
+ }
+ }
+ else if (!attach_pid_name.empty ())
+ {
+ struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
+ if (waitfor_duration != 0)
+ {
+ DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
+ timeout_ptr = &attach_timeout_abstime;
+ }
+
+ RNBLogSTDOUT ("Attaching to process %s...\n", attach_pid_name.c_str());
+ nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str));
+ g_pid = pid;
+ if (pid == INVALID_NUB_PROCESS)
+ {
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ ctx.LaunchStatus().SetErrorString(err_str);
+ RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s\n", waitfor_pid_name.c_str(), err_str);
+ mode = eRNBRunLoopModeExit;
+ }
+ else
+ {
+ ctx.SetProcessID(pid);
+ mode = eRNBRunLoopModeInferiorExecuting;
+ }
+
+ }
+ else
+ {
+ RNBLogSTDERR ("error: asked to attach with empty name and invalid PID.\n");
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (mode != eRNBRunLoopModeExit)
+ {
+ if (port != INT32_MAX)
+ {
+ if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ }
+ else if (str[0] == '/')
+ {
+ if (remote->Comm().OpenFile (str))
+ mode = eRNBRunLoopModeExit;
+ }
+ if (mode != eRNBRunLoopModeExit)
+ RNBLogSTDOUT ("Waiting for debugger instructions for process %d.\n", attach_pid);
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorLaunching:
+ {
+ mode = RNBRunLoopLaunchInferior (remote,
+ ctx.GetSTDINPath(),
+ ctx.GetSTDOUTPath(),
+ ctx.GetSTDERRPath(),
+ no_stdio);
+
+ if (mode == eRNBRunLoopModeInferiorExecuting)
+ {
+ if (port != INT32_MAX)
+ {
+ if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ }
+ else if (str[0] == '/')
+ {
+ if (remote->Comm().OpenFile (str))
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (mode != eRNBRunLoopModeExit)
+ {
+ const char *proc_name = "<unknown>";
+ if (ctx.ArgumentCount() > 0)
+ proc_name = ctx.ArgumentAtIndex(0);
+ RNBLogSTDOUT ("Got a connection, launched process %s (pid = %d).\n", proc_name, ctx.ProcessID());
+ }
+ }
+ else
+ {
+ const char *error_str = remote->Context().LaunchStatus().AsString();
+ RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv_sub_zero, error_str ? error_str : "unknown error.");
+ }
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorExecuting:
+ mode = RNBRunLoopInferiorExecuting(remote);
+ break;
+
+ case eRNBRunLoopModePlatformMode:
+ if (port != INT32_MAX)
+ {
+ if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ }
+ else if (str[0] == '/')
+ {
+ if (remote->Comm().OpenFile (str))
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (mode != eRNBRunLoopModeExit)
+ mode = RNBRunLoopPlatform (remote);
+ break;
+
+ default:
+ mode = eRNBRunLoopModeExit;
+ case eRNBRunLoopModeExit:
+ break;
+ }
+ }
+
+ remote->StopReadRemoteDataThread ();
+ remote->Context().SetProcessID(INVALID_NUB_PROCESS);
+ RNBLogSTDOUT ("Exiting.\n");
+
+ return 0;
+}
diff --git a/tools/debugserver/source/libdebugserver.cpp b/tools/debugserver/source/libdebugserver.cpp
new file mode 100644
index 000000000000..63d76eb26aef
--- /dev/null
+++ b/tools/debugserver/source/libdebugserver.cpp
@@ -0,0 +1,397 @@
+//===-- libdebugserver.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DNBTimer.h"
+#include "PseudoTerminal.h"
+#include "RNBContext.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "RNBRemote.h"
+#include "SysSignal.h"
+
+//----------------------------------------------------------------------
+// Run loop modes which determine which run loop function will be called
+//----------------------------------------------------------------------
+typedef enum
+{
+ eRNBRunLoopModeInvalid = 0,
+ eRNBRunLoopModeGetStartModeFromRemoteProtocol,
+ eRNBRunLoopModeInferiorExecuting,
+ eRNBRunLoopModeExit
+} RNBRunLoopMode;
+
+
+//----------------------------------------------------------------------
+// Global Variables
+//----------------------------------------------------------------------
+RNBRemoteSP g_remoteSP;
+int g_disable_aslr = 0;
+int g_isatty = 0;
+
+#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
+#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
+
+
+//----------------------------------------------------------------------
+// Get our program path and arguments from the remote connection.
+// We will need to start up the remote connection without a PID, get the
+// arguments, wait for the new process to finish launching and hit its
+// entry point, and then return the run loop mode that should come next.
+//----------------------------------------------------------------------
+RNBRunLoopMode
+RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remoteSP)
+{
+ std::string packet;
+
+ if (remoteSP.get() != NULL)
+ {
+ RNBRemote* remote = remoteSP.get();
+ RNBContext& ctx = remote->Context();
+ uint32_t event_mask = RNBContext::event_read_packet_available;
+
+ // Spin waiting to get the A packet.
+ while (1)
+ {
+ DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
+
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ rnb_err_t err = rnb_err;
+ RNBRemote::PacketEnum type;
+
+ err = remote->HandleReceivedPacket (&type);
+
+ // check if we tried to attach to a process
+ if (type == RNBRemote::vattach || type == RNBRemote::vattachwait)
+ {
+ if (err == rnb_success)
+ return eRNBRunLoopModeInferiorExecuting;
+ else
+ {
+ RNBLogSTDERR ("error: attach failed.");
+ return eRNBRunLoopModeExit;
+ }
+ }
+
+
+ if (err == rnb_success)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Got success...",__FUNCTION__);
+ continue;
+ }
+ else if (err == rnb_not_connected)
+ {
+ RNBLogSTDERR ("error: connection lost.");
+ return eRNBRunLoopModeExit;
+ }
+ else
+ {
+ // a catch all for any other gdb remote packets that failed
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
+ continue;
+ }
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ }
+ }
+ return eRNBRunLoopModeExit;
+}
+
+
+//----------------------------------------------------------------------
+// Watch for signals:
+// SIGINT: so we can halt our inferior. (disabled for now)
+// SIGPIPE: in case our child process dies
+//----------------------------------------------------------------------
+nub_process_t g_pid;
+int g_sigpipe_received = 0;
+void
+signal_handler(int signo)
+{
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
+
+ switch (signo)
+ {
+ // case SIGINT:
+ // DNBProcessKill (g_pid, signo);
+ // break;
+
+ case SIGPIPE:
+ g_sigpipe_received = 1;
+ break;
+ }
+}
+
+// Return the new run loop mode based off of the current process state
+RNBRunLoopMode
+HandleProcessStateChange (RNBRemoteSP &remote, bool initialize)
+{
+ RNBContext& ctx = remote->Context();
+ nub_process_t pid = ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ nub_state_t pid_state = DNBProcessGetState (pid);
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
+
+ switch (pid_state)
+ {
+ case eStateInvalid:
+ case eStateUnloaded:
+ // Something bad happened
+ return eRNBRunLoopModeExit;
+ break;
+
+ case eStateAttaching:
+ case eStateLaunching:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateSuspended:
+ case eStateCrashed:
+ case eStateStopped:
+ if (initialize == false)
+ {
+ // Compare the last stop count to our current notion of a stop count
+ // to make sure we don't notify more than once for a given stop.
+ nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
+ bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
+ if (pid_stop_count_changed)
+ {
+ remote->FlushSTDIO();
+
+ if (ctx.GetProcessStopCount() == 1)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %zu (old %zu)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
+ }
+ else
+ {
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %zu (old %zu)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
+ remote->NotifyThatProcessStopped ();
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %zu (old %zu)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
+ }
+ }
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateStepping:
+ case eStateRunning:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateExited:
+ remote->HandlePacket_last_signal(NULL);
+ return eRNBRunLoopModeExit;
+ case eStateDetached:
+ return eRNBRunLoopModeExit;
+
+ }
+
+ // Catch all...
+ return eRNBRunLoopModeExit;
+}
+// This function handles the case where our inferior program is stopped and
+// we are waiting for gdb remote protocol packets. When a packet occurs that
+// makes the inferior run, we need to leave this function with a new state
+// as the return code.
+RNBRunLoopMode
+RNBRunLoopInferiorExecuting (RNBRemoteSP &remote)
+{
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ RNBContext& ctx = remote->Context();
+
+ // Init our mode and set 'is_running' based on the current process state
+ RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
+
+ while (ctx.ProcessID() != INVALID_NUB_PROCESS)
+ {
+
+ std::string set_events_str;
+ uint32_t event_mask = ctx.NormalEventBits();
+
+ if (!ctx.ProcessStateRunning())
+ {
+ // Clear the stdio bits if we are not running so we don't send any async packets
+ event_mask &= ~RNBContext::event_proc_stdio_available;
+ }
+
+ // We want to make sure we consume all process state changes and have
+ // whomever is notifying us to wait for us to reset the event bit before
+ // continuing.
+ //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
+
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str));
+
+ if (set_events)
+ {
+ if ((set_events & RNBContext::event_proc_thread_exiting) ||
+ (set_events & RNBContext::event_proc_stdio_available))
+ {
+ remote->FlushSTDIO();
+ }
+
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ // handleReceivedPacket will take care of resetting the
+ // event_read_packet_available events when there are no more...
+ set_events ^= RNBContext::event_read_packet_available;
+
+ if (ctx.ProcessStateRunning())
+ {
+ if (remote->HandleAsyncPacket() == rnb_not_connected)
+ {
+ // TODO: connect again? Exit?
+ }
+ }
+ else
+ {
+ if (remote->HandleReceivedPacket() == rnb_not_connected)
+ {
+ // TODO: connect again? Exit?
+ }
+ }
+ }
+
+ if (set_events & RNBContext::event_proc_state_changed)
+ {
+ mode = HandleProcessStateChange (remote, false);
+ ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
+ set_events ^= RNBContext::event_proc_state_changed;
+ }
+
+ if (set_events & RNBContext::event_proc_thread_exiting)
+ {
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting)
+ {
+ // Out remote packet receiving thread exited, exit for now.
+ if (ctx.HasValidProcessID())
+ {
+ // TODO: We should add code that will leave the current process
+ // in its current state and listen for another connection...
+ if (ctx.ProcessStateRunning())
+ {
+ DNBProcessKill (ctx.ProcessID());
+ }
+ }
+ mode = eRNBRunLoopModeExit;
+ }
+ }
+
+ // Reset all event bits that weren't reset for now...
+ if (set_events != 0)
+ ctx.Events().ResetEvents(set_events);
+
+ if (mode != eRNBRunLoopModeInferiorExecuting)
+ break;
+ }
+
+ return mode;
+}
+
+void
+ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
+{
+#if 0
+ vprintf(format, args);
+#endif
+}
+
+extern "C" int
+debug_server_main(int fd)
+{
+#if 1
+ g_isatty = 0;
+#else
+ g_isatty = ::isatty (STDIN_FILENO);
+
+ DNBLogSetDebug(1);
+ DNBLogSetVerbose(1);
+ DNBLogSetLogMask(-1);
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+#endif
+
+ signal (SIGPIPE, signal_handler);
+
+ g_remoteSP.reset (new RNBRemote);
+
+ RNBRemote *remote = g_remoteSP.get();
+ if (remote == NULL)
+ {
+ RNBLogSTDERR ("error: failed to create a remote connection class\n");
+ return -1;
+ }
+
+
+ RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
+
+ while (mode != eRNBRunLoopModeExit)
+ {
+ switch (mode)
+ {
+ case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
+ if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
+ RNBLogSTDOUT("Starting remote data thread.\n");
+ g_remoteSP->StartReadRemoteDataThread();
+
+ RNBLogSTDOUT("Waiting for start mode from remote.\n");
+ mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
+ }
+ else
+ {
+ mode = eRNBRunLoopModeExit;
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorExecuting:
+ mode = RNBRunLoopInferiorExecuting(g_remoteSP);
+ break;
+
+ default:
+ mode = eRNBRunLoopModeExit;
+ break;
+
+ case eRNBRunLoopModeExit:
+ break;
+ }
+ }
+
+ g_remoteSP->StopReadRemoteDataThread ();
+ g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
+
+ return 0;
+}
diff --git a/tools/debugserver/source/libdebugserver.h b/tools/debugserver/source/libdebugserver.h
new file mode 100644
index 000000000000..2576e269ee60
--- /dev/null
+++ b/tools/debugserver/source/libdebugserver.h
@@ -0,0 +1,15 @@
+//===-- libdebugserver.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef debugserver_libdebugserver_h
+#define debugserver_libdebugserver_h
+
+int debug_server_main(int fd);
+
+#endif
diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt
new file mode 100644
index 000000000000..2d4f905323e3
--- /dev/null
+++ b/tools/driver/CMakeLists.txt
@@ -0,0 +1,28 @@
+add_lldb_executable(lldb
+ Driver.cpp
+ Platform.cpp
+ )
+
+if ( CMAKE_SYSTEM_NAME MATCHES "Windows" )
+ add_definitions( -DIMPORT_LIBLLDB )
+endif()
+
+# Add lldb dependency on lldb-server if we can use it.
+if ( LLDB_CAN_USE_LLDB_SERVER )
+ add_dependencies(lldb lldb-server)
+endif()
+
+# Add lldb dependency on debugserver if we can use it.
+if ( LLDB_CAN_USE_DEBUGSERVER )
+ add_dependencies(lldb debugserver)
+endif()
+
+target_link_libraries(lldb liblldb)
+# TODO: why isn't this done by add_lldb_executable?
+#target_link_libraries(lldb ${LLDB_USED_LIBS})
+#llvm_config(lldb ${LLVM_LINK_COMPONENTS})
+
+set_target_properties(lldb PROPERTIES VERSION ${LLDB_VERSION})
+
+install(TARGETS lldb
+ RUNTIME DESTINATION bin)
diff --git a/tools/driver/Makefile b/tools/driver/Makefile
new file mode 100644
index 000000000000..05a245721bde
--- /dev/null
+++ b/tools/driver/Makefile
@@ -0,0 +1,36 @@
+##===- tools/driver/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 := ../..
+
+TOOLNAME = lldb
+
+NO_PEDANTIC = 1
+
+include $(LLDB_LEVEL)/Makefile
+
+ifneq ($(HOST_OS),MingW)
+LLVMLibsOptions += -ledit -llldb -llldbUtility
+else
+LLVMLibsOptions += -llldb -llldbUtility
+CPP.Flags += -DIMPORT_LIBLLDB
+endif
+
+ifeq ($(HOST_OS),Darwin)
+ LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/
+ LLVMLibsOptions += -Wl,-sectcreate -Wl,__TEXT -Wl,__info_plist -Wl,"$(PROJ_SRC_DIR)/lldb-Info.plist"
+endif
+
+ifneq (,$(filter $(HOST_OS), Linux GNU/kFreeBSD NetBSD))
+ LLVMLibsOptions += -Wl,-rpath,$(LibDir)
+endif
+
+ifeq ($(HOST_OS),FreeBSD)
+ CPP.Flags += -I/usr/include/edit #-v
+ LLVMLibsOptions += -Wl,-rpath,$(LibDir)
+endif
diff --git a/tools/driver/lldb-Info.plist b/tools/driver/lldb-Info.plist
new file mode 100644
index 000000000000..7c1bfc734a7f
--- /dev/null
+++ b/tools/driver/lldb-Info.plist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.lldb</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>lldb</string>
+ <key>CFBundleVersion</key>
+ <string>2</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/tools/install-headers/Makefile b/tools/install-headers/Makefile
new file mode 100644
index 000000000000..384a54e6d6f6
--- /dev/null
+++ b/tools/install-headers/Makefile
@@ -0,0 +1,23 @@
+installsrc:
+ echo "installsrc (doing nothing)"
+
+install:
+ echo "install (doing nothing)"
+
+clean:
+ echo "clean (doing nothing)"
+
+LLDB_VERSION=`echo ${CURRENT_PROJECT_VERSION} | /usr/bin/sed -E 's/^([0-9]+).([0-9]+).([0-9]+)(.[0-9]+)?$$/\1/g'`
+LLDB_REVISION=`echo ${CURRENT_PROJECT_VERSION} | /usr/bin/sed -E 's/^([0-9]+).([0-9]+).([0-9]+)(.[0-9]+)?$$/\3/g'`
+LLDB_VERSION_STRING=`echo ${CURRENT_PROJECT_VERSION}`
+
+installhdrs:
+ cd "${TARGET_BUILD_DIR}/${LLDB_FRAMEWORK_INSTALL_DIR}/LLDB.framework/Headers" ;\
+ for file in *.h ;\
+ do \
+ /usr/bin/sed -i '' 's/\(#include\)[ ]*"lldb\/\(API\/\)\{0,1\}\(.*\)"/\1 <LLDB\/\3>/1' "$$file" ;\
+ /usr/bin/sed -i '' 's|<LLDB/Utility|<LLDB|' "$$file" ;\
+ /usr/bin/sed -i '' "s|//#define LLDB_VERSION$$|#define LLDB_VERSION $(LLDB_VERSION) |" "$$file" ;\
+ /usr/bin/sed -i '' "s|//#define LLDB_REVISION|#define LLDB_REVISION $(LLDB_REVISION) |" "$$file" ;\
+ /usr/bin/sed -i '' "s|//#define LLDB_VERSION_STRING|#define LLDB_VERSION_STRING \"$(LLDB_VERSION_STRING)\" |" "$$file" ;\
+ done
diff --git a/tools/lldb-mi/CMakeLists.txt b/tools/lldb-mi/CMakeLists.txt
new file mode 100644
index 000000000000..7fd6ed199e9d
--- /dev/null
+++ b/tools/lldb-mi/CMakeLists.txt
@@ -0,0 +1,102 @@
+set(LLDB_MI_SOURCES
+ MICmdArgContext.cpp
+ MICmdArgSet.cpp
+ MICmdArgValBase.cpp
+ MICmdArgValConsume.cpp
+ MICmdArgValFile.cpp
+ MICmdArgValListBase.cpp
+ MICmdArgValListOfN.cpp
+ MICmdArgValNumber.cpp
+ MICmdArgValOptionLong.cpp
+ MICmdArgValOptionShort.cpp
+ MICmdArgValPrintValues.cpp
+ MICmdArgValString.cpp
+ MICmdArgValThreadGrp.cpp
+ MICmdBase.cpp
+ MICmdCommands.cpp
+ MICmdCmd.cpp
+ MICmdCmdBreak.cpp
+ MICmdCmdData.cpp
+ MICmdCmdEnviro.cpp
+ MICmdCmdExec.cpp
+ MICmdCmdFile.cpp
+ MICmdCmdGdbInfo.cpp
+ MICmdCmdGdbSet.cpp
+ MICmdCmdGdbShow.cpp
+ MICmdCmdGdbThread.cpp
+ MICmdCmdMiscellanous.cpp
+ MICmdCmdStack.cpp
+ MICmdCmdSupportInfo.cpp
+ MICmdCmdSupportList.cpp
+ MICmdCmdSymbol.cpp
+ MICmdCmdTarget.cpp
+ MICmdCmdThread.cpp
+ MICmdCmdTrace.cpp
+ MICmdCmdVar.cpp
+ MICmdData.cpp
+ MICmdFactory.cpp
+ MICmdInterpreter.cpp
+ MICmdInvoker.cpp
+ MICmdMgr.cpp
+ MICmdMgrSetCmdDeleteCallback.cpp
+ MICmnBase.cpp
+ MICmnLLDBBroadcaster.cpp
+ MICmnLLDBDebugger.cpp
+ MICmnLLDBDebuggerHandleEvents.cpp
+ MICmnLLDBDebugSessionInfo.cpp
+ MICmnLLDBDebugSessionInfoVarObj.cpp
+ MICmnLLDBProxySBValue.cpp
+ MICmnLLDBUtilSBValue.cpp
+ MICmnLog.cpp
+ MICmnLogMediumFile.cpp
+ MICmnMIOutOfBandRecord.cpp
+ MICmnMIResultRecord.cpp
+ MICmnMIValue.cpp
+ MICmnMIValueConst.cpp
+ MICmnMIValueList.cpp
+ MICmnMIValueResult.cpp
+ MICmnMIValueTuple.cpp
+ MICmnResources.cpp
+ MICmnStreamStderr.cpp
+ MICmnStreamStdin.cpp
+ MICmnStreamStdout.cpp
+ MICmnThreadMgrStd.cpp
+ MIDriver.cpp
+ MIDriverBase.cpp
+ MIDriverMain.cpp
+ MIDriverMgr.cpp
+ MIUtilParse.cpp
+ MIUtilDateTimeStd.cpp
+ MIUtilDebug.cpp
+ MIUtilFileStd.cpp
+ MIUtilMapIdToVariant.cpp
+ MIUtilString.cpp
+ MIUtilThreadBaseStd.cpp
+ MIUtilVariant.cpp
+ Platform.cpp
+ )
+
+if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" )
+ add_definitions( -DIMPORT_LIBLLDB )
+ list(APPEND LLDB_MI_SOURCES
+ ${LLDB_SOURCE_ROOT}/Host/common/GetOptInc.cpp
+ )
+endif ()
+
+include(../../cmake/LLDBDependencies.cmake)
+
+add_lldb_executable(lldb-mi ${LLDB_MI_SOURCES})
+
+target_link_libraries(lldb-mi liblldb)
+if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" )
+ target_link_libraries(lldb-mi pthread)
+endif ()
+
+# TODO: why isn't this done by add_lldb_executable?
+#target_link_libraries(lldb-mi ${LLDB_USED_LIBS})
+llvm_config(lldb-mi ${LLVM_LINK_COMPONENTS})
+
+set_target_properties(lldb-mi PROPERTIES VERSION ${LLDB_VERSION})
+
+install(TARGETS lldb-mi
+ RUNTIME DESTINATION bin)
diff --git a/tools/lldb-mi/MICmdCmdSymbol.cpp b/tools/lldb-mi/MICmdCmdSymbol.cpp
index d99ceef22498..abaa3924ddce 100644
--- a/tools/lldb-mi/MICmdCmdSymbol.cpp
+++ b/tools/lldb-mi/MICmdCmdSymbol.cpp
@@ -82,11 +82,7 @@ CMICmdCmdSymbolListLines::Execute()
CMICMDBASE_GETOPTION(pArgFile, File, m_constStrArgNameFile);
const CMIUtilString &strFilePath(pArgFile->GetValue());
- // FIXME: this won't work for header files! To try and use existing
- // commands to get this to work for header files would be too slow.
- // Instead, this code should be rewritten to use APIs and/or support
- // should be added to lldb which would work for header files.
- const CMIUtilString strCmd(CMIUtilString::Format("target modules dump line-table \"%s\"", strFilePath.AddSlashes().c_str()));
+ const CMIUtilString strCmd(CMIUtilString::Format("source info --file \"%s\"", strFilePath.AddSlashes().c_str()));
CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance());
const lldb::ReturnStatus rtn = rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand(strCmd.c_str(), m_lldbResult);
@@ -110,10 +106,10 @@ ParseLLDBLineAddressHeader(const char *input, CMIUtilString &file)
{
// Match LineEntry using regex.
static MIUtilParse::CRegexParser g_lineentry_header_regex(
- "^ *Line table for (.+) in `(.+)$");
- // ^1=file ^2=module
+ "^ *Lines found for file (.+) in compilation unit (.+) in `(.+)$");
+ // ^1=file ^2=cu ^3=module
- MIUtilParse::CRegexParser::Match match(3);
+ MIUtilParse::CRegexParser::Match match(4);
const bool ok = g_lineentry_header_regex.Execute(input, match);
if (ok)
@@ -146,12 +142,12 @@ ParseLLDBLineAddressEntry(const char *input, CMIUtilString &addr,
// Match LineEntry using regex.
static MIUtilParse::CRegexParser g_lineentry_nocol_regex(
- "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+)$");
+ "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+)$");
static MIUtilParse::CRegexParser g_lineentry_col_regex(
- "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+):[0-9]+$");
- // ^1=addr ^2=f ^3=line ^4=:col(opt)
+ "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+):[0-9]+$");
+ // ^1=start ^2=end ^3=f ^4=line ^5=:col(opt)
- MIUtilParse::CRegexParser::Match match(5);
+ MIUtilParse::CRegexParser::Match match(6);
// First try matching the LineEntry with the column,
// then try without the column.
@@ -160,8 +156,8 @@ ParseLLDBLineAddressEntry(const char *input, CMIUtilString &addr,
if (ok)
{
addr = match.GetMatchAtIndex(1);
- file = match.GetMatchAtIndex(2);
- line = match.GetMatchAtIndex(3);
+ file = match.GetMatchAtIndex(3);
+ line = match.GetMatchAtIndex(4);
}
return ok;
}
@@ -222,10 +218,6 @@ CMICmdCmdSymbolListLines::Acknowledge()
if (!ParseLLDBLineAddressEntry(rLine.c_str(), strAddr, strFile, strLine))
continue;
- // Skip entries which don't match the desired source.
- if (strWantFile != strFile)
- continue;
-
const CMICmnMIValueConst miValueConst(strAddr);
const CMICmnMIValueResult miValueResult("pc", miValueConst);
CMICmnMIValueTuple miValueTuple(miValueResult);
diff --git a/tools/lldb-mi/Makefile b/tools/lldb-mi/Makefile
new file mode 100644
index 000000000000..cdd766633b16
--- /dev/null
+++ b/tools/lldb-mi/Makefile
@@ -0,0 +1,32 @@
+##===- tools/lldb-mi/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 := ../..
+
+TOOLNAME = lldb-mi
+
+NO_PEDANTIC = 1
+
+LLVMLibsOptions += -ledit -llldb -llldbUtility
+LINK_COMPONENTS := support
+
+include $(LLDB_LEVEL)/Makefile
+
+ifeq ($(HOST_OS),Darwin)
+ LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/
+ LLVMLibsOptions += -Wl,-sectcreate -Wl,__TEXT -Wl,__info_plist -Wl,"$(PROJ_SRC_DIR)/lldb-Info.plist"
+endif
+
+ifneq (,$(filter $(HOST_OS), Linux GNU/kFreeBSD NetBSD))
+ LLVMLibsOptions += -Wl,-rpath,$(LibDir) -lpthread
+endif
+
+ifeq ($(HOST_OS),FreeBSD)
+ CPP.Flags += -I/usr/include/edit #-v
+ LLVMLibsOptions += -Wl,-rpath,$(LibDir) -lpthread
+endif
diff --git a/tools/lldb-mi/lldb-Info.plist b/tools/lldb-mi/lldb-Info.plist
new file mode 100644
index 000000000000..795512691ef0
--- /dev/null
+++ b/tools/lldb-mi/lldb-Info.plist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.lldb</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>lldb-mi</string>
+ <key>CFBundleVersion</key>
+ <string>2</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/tools/lldb-perf/README b/tools/lldb-perf/README
new file mode 100644
index 000000000000..7cec4faac2c8
--- /dev/null
+++ b/tools/lldb-perf/README
@@ -0,0 +1,295 @@
+ The lldb-perf infrastructure for LLDB performance testing
+===========================================================
+
+lldb-perf is an infrastructure meant to simplify the creation of performance
+tests for the LLDB debugger. It is contained in liblldbperf.a which is part of
+the standard opensource checkout of LLDB
+
+Its main concepts are:
+- Gauges: a gauge is a thing that takes a sample. Samples include elapsed time,
+ memory used, and energy consumed.
+- Metrics: a metric is a collection of samples that knows how to do statistics
+ like sum() and average(). Metrics can be extended as needed.
+- Measurements: a measurement is the thing that stores an action, a gauge and
+ a metric. You define measurements as in “take the time to run this function”,
+ “take the memory to run this block of code”, and then after you invoke it,
+ your stats will automagically be there.
+- Tests: a test is a sequence of steps and measurements.
+
+Tests cases should be added as targets to the lldbperf.xcodeproj project. It
+is probably easiest to duplicate one of the existing targets. In order to
+write a test based on lldb-perf, you need to subclass lldb_perf::TestCase:
+
+using namespace lldb_perf;
+
+class FormattersTest : public TestCase
+{
+
+Usually, you will define measurements as variables of your test case class:
+
+private:
+ // C++ formatters
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_std_vector_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_std_list_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_std_map_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_std_string_measurement;
+
+ // Cocoa formatters
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsstring_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsarray_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdictionary_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsset_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsbundle_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdate_measurement;
+
+A TimeMeasurement is, obviously, a class that measures “how much time to run
+this block of code”. The block of code is passed as an std::function which you
+can construct with a lambda! You need to give the prototype of your block of
+code. In this example, we run blocks of code that take an SBValue and return
+nothing.
+
+These blocks look like:
+
+ m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-vector", "time to dump an std::vector");
+
+Here we are saying: make me a measurement named “std-vector”, whose
+description is “time to dump an std::vector” and that takes the time required
+to call lldb_perf::Xcode::FetchVariable(value,1,false).
+
+The Xcode class is a collection of utility functions that replicate common
+Xcode patterns (FetchVariable unsurprisingly calls API functions that Xcode
+could use when populating a variables view entry - the 1 means “expand 1 level
+of depth” and the false means “do not dump the data to stdout”)
+
+A full constructor for a TestCase looks like:
+
+FormattersTest () : TestCase()
+{
+ m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-vector", "time to dump an std::vector");
+ m_dump_std_list_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-list", "time to dump an std::list");
+ m_dump_std_map_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-map", "time to dump an std::map");
+ m_dump_std_string_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-string", "time to dump an std::string");
+
+ m_dump_nsstring_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,0,false);
+ }, "ns-string", "time to dump an NSString");
+
+ m_dump_nsarray_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-array", "time to dump an NSArray");
+
+ m_dump_nsdictionary_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-dictionary", "time to dump an NSDictionary");
+
+ m_dump_nsset_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-set", "time to dump an NSSet");
+
+ m_dump_nsbundle_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-bundle", "time to dump an NSBundle");
+
+ m_dump_nsdate_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,0,false);
+ }, "ns-date", "time to dump an NSDate");
+}
+
+Once your test case is constructed, Setup() is called on it:
+
+ virtual bool
+ Setup (int argc, const char** argv)
+ {
+ m_app_path.assign(argv[1]);
+ m_out_path.assign(argv[2]);
+ m_target = m_debugger.CreateTarget(m_app_path.c_str());
+ m_target.BreakpointCreateByName("main");
+ SBLaunchInfo launch_info (argv);
+ return Launch (launch_info);
+ }
+
+Setup() returns a boolean value that indicates if setup was successful.
+In Setup() you fill out a SBLaunchInfo with any needed settings for launching
+your process like arguments, environment variables, working directory, and
+much more.
+
+The last thing you want to do in setup is call Launch():
+
+ bool
+ Launch (coSBLaunchInfo &launch_info);
+
+This ensures your target is now alive. Make sure to have a breakpoint created.
+
+Once you launched, the event loop is entered. The event loop waits for stops,
+and when it gets one, it calls your test case’s TestStep() function:
+
+ virtual void
+ TestStep (int counter, ActionWanted &next_action)
+
+the counter is the step id (a monotonically increasing counter). In TestStep()
+you will essentially run your measurements and then return what you want the
+driver to do by filling in the ActionWanted object named "next_action".
+
+Possible options are:
+- continue process next_action.Continue();
+- kill process next_action.Kill();
+- Step-out on a thread next_action.StepOut(SBThread)
+- step-over on a thread. next_action.StepOver(SBThread)
+
+If you use ActionWanted::Next() or ActionWanted::Finish() you need to specify
+a thread to use. By default the TestCase class will select the first thread
+that had a stop reason other than eStopReasonNone and place it into the
+m_thread member variable of TestCase. This means if your test case hits a
+breakpoint or steps, the thread that hit the breakpoint or finished the step
+will automatically be selected in the process (m_process) and m_thread will
+be set to this thread. If you have one or more threads that will stop with a
+reason simultaneously, you will need to find those threads manually by
+iterating through the process list and determine what to do next.
+
+For your convenience TestCase has m_debugger, m_target and m_process as member
+variables. As state above m_thread will be filled in with the first thread
+that has a stop reason.
+
+An example:
+
+ virtual void
+ TestStep (int counter, ActionWanted &next_action)
+ {
+ case 0:
+ m_target.BreakpointCreateByLocation("fmts_tester.mm", 68);
+ next_action.Continue();
+ break;
+ case 1:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 2:
+ DoTest ();
+ next_action.StepOver(m_thread);
+ break;
+
+DoTest() is a function I define in my own class that calls the measurements:
+ void
+ DoTest ()
+ {
+ SBThread thread_main(m_thread);
+ SBFrame frame_zero(thread_main.GetFrameAtIndex(0));
+
+ m_dump_nsarray_measurement(frame_zero.FindVariable("nsarray", lldb::eDynamicCanRunTarget));
+ m_dump_nsarray_measurement(frame_zero.FindVariable("nsmutablearray", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsdictionary", lldb::eDynamicCanRunTarget));
+ m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsmutabledictionary", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str0", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str1", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str2", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str3", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str4", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsdate_measurement(frame_zero.FindVariable("me", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("cutie", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("mom", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("dad", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("today", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsbundle_measurement(frame_zero.FindVariable("bundles", lldb::eDynamicCanRunTarget));
+ m_dump_nsbundle_measurement(frame_zero.FindVariable("frameworks", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsset_measurement(frame_zero.FindVariable("nsset", lldb::eDynamicCanRunTarget));
+ m_dump_nsset_measurement(frame_zero.FindVariable("nsmutableset", lldb::eDynamicCanRunTarget));
+
+ m_dump_std_vector_measurement(frame_zero.FindVariable("vector", lldb::eDynamicCanRunTarget));
+ m_dump_std_list_measurement(frame_zero.FindVariable("list", lldb::eDynamicCanRunTarget));
+ m_dump_std_map_measurement(frame_zero.FindVariable("map", lldb::eDynamicCanRunTarget));
+
+ m_dump_std_string_measurement(frame_zero.FindVariable("sstr0", lldb::eDynamicCanRunTarget));
+ m_dump_std_string_measurement(frame_zero.FindVariable("sstr1", lldb::eDynamicCanRunTarget));
+ m_dump_std_string_measurement(frame_zero.FindVariable("sstr2", lldb::eDynamicCanRunTarget));
+ m_dump_std_string_measurement(frame_zero.FindVariable("sstr3", lldb::eDynamicCanRunTarget));
+ m_dump_std_string_measurement(frame_zero.FindVariable("sstr4", lldb::eDynamicCanRunTarget));
+ }
+
+Essentially, you call your measurements as if they were functions, passing
+them arguments and all, and they will do the right thing with gathering stats.
+
+The last step is usually to KILL the inferior and bail out:
+
+ virtual ActionWanted
+ TestStep (int counter)
+ {
+...
+ case 9:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 10:
+ DoTest ();
+ next_action.Continue();
+ break;
+ default:
+ next_action.Kill();
+ break;
+ }
+
+
+At the end, you define a Results() function:
+
+ void
+ Results ()
+ {
+ CFCMutableArray array;
+ m_dump_std_vector_measurement.Write(array);
+ m_dump_std_list_measurement.Write(array);
+ m_dump_std_map_measurement.Write(array);
+ m_dump_std_string_measurement.Write(array);
+
+ m_dump_nsstring_measurement.Write(array);
+ m_dump_nsarray_measurement.Write(array);
+ m_dump_nsdictionary_measurement.Write(array);
+ m_dump_nsset_measurement.Write(array);
+ m_dump_nsbundle_measurement.Write(array);
+ m_dump_nsdate_measurement.Write(array);
+
+ CFDataRef xmlData = CFPropertyListCreateData (kCFAllocatorDefault,
+ array.get(),
+ kCFPropertyListXMLFormat_v1_0,
+ 0,
+ NULL);
+
+ CFURLRef file = CFURLCreateFromFileSystemRepresentation (NULL,
+ (const UInt8*)m_out_path.c_str(),
+ m_out_path.size(),
+ FALSE);
+
+ CFURLWriteDataAndPropertiesToResource(file,xmlData,NULL,NULL);
+ }
+
+For now, pretty much copy this and just call Write() on all your measurements.
+I plan to move this higher in the hierarchy (e.g. make a
+TestCase::Write(filename) fairly soon).
+
+Your main() will look like:
+
+int main(int argc, const char * argv[])
+{
+ MyTest test;
+ TestCase::Run (test, argc, argv);
+ return 0;
+}
+
+If you are debugging your test, before Run() call
+
+ test.SetVerbose(true);
+
+Feel free to send any questions and ideas for improvements.
diff --git a/tools/lldb-perf/common/clang/build-clang.sh b/tools/lldb-perf/common/clang/build-clang.sh
new file mode 100755
index 000000000000..3d9add79c4ab
--- /dev/null
+++ b/tools/lldb-perf/common/clang/build-clang.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+if [ -d "llvm-build" ]; then
+ echo "Using existing 'llvm-build' directory..."
+else
+ mkdir llvm-build
+fi
+
+cd llvm-build
+
+if [ -d "llvm" ]; then
+ echo "Using existing 'llvm' directory..."
+else
+ svn co --revision 176809 http://llvm.org/svn/llvm-project/llvm/trunk llvm
+ ( cd llvm/tools ; svn co --revision 176809 http://llvm.org/svn/llvm-project/cfe/trunk clang )
+fi
+
+if [ ! -d "build" ]; then
+ mkdir build
+ cd build
+ ../llvm/configure --enable-targets=x86_64,arm --build=x86_64-apple-darwin10 --disable-optimized --disable-assertions --enable-libcpp
+ make -j8 clang-only DEBUG_SYMBOLS=1
+ rm -rf lib projects runtime unittests utils config.*
+ ( cd ./Debug/bin ; rm -rf ll* clang-check clang-tblgen count diagtool fpcmp macho-dump not opt yaml2obj FileCheck FileUpdate arcmt-test c-arcmt-test c-index-test bugpoint )
+ ( cd ./tools ; rm -rf ll* clang-check clang-tblgen count diagtool fpcmp lto macho-dump not opt yaml2obj FileCheck FileUpdate arcmt-test c-arcmt-test c-index-test bugpoint )
+ ( cd ./tools/clang ; rm -rf lib unittests utils )
+ ( cd ./tools/clang/tools ; rm -rf arcmt-test c-arcmt-test c-index-test clang-check diagtool libclang )
+ ( cd ../llvm ; rm -rf cmake configure docs examples projects *.txt *.TXT autoconf bindings test unittests utils ; find . -type d -name .svn -print0 | xargs -0 rm -rf )
+ ( cd ../llvm/tools ; rm -rf *.txt bugpoint bugpoint-passes ll* lto macho-dump opt gold )
+fi
+
+
+
diff --git a/tools/lldb-perf/common/clang/lldb_perf_clang.cpp b/tools/lldb-perf/common/clang/lldb_perf_clang.cpp
new file mode 100644
index 000000000000..ac9481c366ec
--- /dev/null
+++ b/tools/lldb-perf/common/clang/lldb_perf_clang.cpp
@@ -0,0 +1,484 @@
+//===-- lldb_perf_clang.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-perf/lib/Timer.h"
+#include "lldb-perf/lib/Metric.h"
+#include "lldb-perf/lib/Measurement.h"
+#include "lldb-perf/lib/Results.h"
+#include "lldb-perf/lib/TestCase.h"
+#include "lldb-perf/lib/Xcode.h"
+#include "llvm/ADT/STLExtras.h"
+#include <iostream>
+#include <unistd.h>
+#include <fstream>
+#include <getopt.h>
+
+using namespace lldb_perf;
+
+#define NUM_EXPR_ITERATIONS 3
+class ClangTest : public TestCase
+{
+public:
+ ClangTest () :
+ TestCase(),
+ m_time_create_target ([this] () -> void
+ {
+ m_memory_change_create_target.Start();
+ m_target = m_debugger.CreateTarget(m_exe_path.c_str());
+ m_memory_change_create_target.Stop();
+ }, "time-create-target", "The time it takes to create a target."),
+ m_time_set_bp_main([this] () -> void
+ {
+ m_memory_change_break_main.Start();
+ m_target.BreakpointCreateByName("main");
+ m_memory_change_break_main.Stop();
+ }, "time-set-break-main", "Elapsed time it takes to set a breakpoint at 'main' by name."),
+ m_memory_change_create_target (),
+ m_memory_change_break_main (),
+ m_memory_total (),
+ m_time_launch_stop_main(),
+ m_time_total (),
+ m_expr_first_evaluate([this] (SBFrame frame) -> void
+ {
+ frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError();
+ }, "time-expr", "Elapsed time it takes to evaluate an expression for the first time."),
+ m_expr_frame_zero ([this] (SBFrame frame) -> void
+ {
+ frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError();
+ }, "time-expr-frame-zero", "Elapsed time it takes to evaluate an expression 3 times at frame zero."),
+ m_expr_frame_non_zero ([this] (SBFrame frame) -> void
+ {
+ frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError();
+ }, "time-expr-frame-non-zero", "Elapsed time it takes to evaluate an expression 3 times at a non-zero frame."),
+ m_exe_path(),
+ m_out_path(),
+ m_launch_info (NULL),
+ m_use_dsym (false)
+ {
+ }
+
+ virtual
+ ~ClangTest ()
+ {
+ }
+
+ virtual bool
+ Setup (int& argc, const char**& argv)
+ {
+ if (m_exe_path.empty())
+ return false;
+ m_launch_info.SetArguments(argv, false);
+ return true;
+ }
+
+ void
+ DoTest ()
+ {
+ }
+
+ virtual void
+ TestStep (int counter, ActionWanted &next_action)
+ {
+ char temp_source_path[PATH_MAX] = "/tmp/main.XXXXXX.cpp";
+
+ switch (counter)
+ {
+ case 0:
+ {
+ //Xcode::RunCommand(m_debugger,"log enable -f /tmp/packets.txt gdb-remote packets",true);
+
+ m_memory_total.Start();
+ m_time_total.Start();
+
+ // Time creating the target
+ m_time_create_target();
+
+ m_time_set_bp_main();
+
+ int fd = mkstemps(temp_source_path, 4);
+
+ if (fd >= 0)
+ {
+ const char *source_content = R"(
+#include <stdio.h>
+#include <stdint.h>
+#include <vector>
+
+namespace {
+ struct Foo
+ {
+ int i; int j;
+ };
+ void doit (const Foo &foo)
+ {
+ printf ("doit(%i)\n", foo.i);
+ }
+}
+
+int main (int argc, char const *argv[], char const *envp[])
+{
+ std::vector<int> ints;
+ for (int i=0;i<10;++i)
+ ints.push_back(i);
+ printf ("hello world\n");
+ Foo foo = { 12, 13 };
+ doit (foo);
+ return 0;
+}
+)";
+ write (fd, source_content, strlen(source_content));
+ close(fd);
+ }
+ else
+ {
+ const char *error_cstr = strerror(errno);
+ fprintf (stderr, "error: failed to created temporary source file: '%s' (%s)", temp_source_path, error_cstr);
+ exit(2);
+ }
+
+ m_time_launch_stop_main.Start();
+ const char *clang_argv[] = {
+ "-cc1",
+ "-triple", "x86_64-apple-macosx10.8.0",
+ "-emit-obj",
+ "-mrelax-all",
+ "-disable-free",
+ "-disable-llvm-verifier",
+ "-main-file-name", "main.cpp",
+ "-mrelocation-model", "pic",
+ "-pic-level", "2",
+ "-mdisable-fp-elim",
+ "-masm-verbose",
+ "-munwind-tables",
+ "-target-cpu", "core2",
+ "-target-linker-version", "132.10.1",
+ "-v",
+ "-g",
+ "-O0",
+ "-fdeprecated-macro",
+ "-ferror-limit", "19",
+ "-fmessage-length", "298",
+ "-stack-protector", "1",
+ "-mstackrealign",
+ "-fblocks",
+ "-fobjc-runtime=macosx-10.8.0",
+ "-fobjc-dispatch-method=mixed",
+ "-fencode-extended-block-signature",
+ "-fcxx-exceptions",
+ "-fexceptions",
+ "-fdiagnostics-show-option",
+ "-fcolor-diagnostics",
+ "-backend-option",
+ "-vectorize-loops",
+ "-o", "/tmp/main.o",
+ "-x", "c++",
+ NULL,
+ NULL };
+ clang_argv[llvm::array_lengthof(clang_argv)-2] = temp_source_path;
+ SBLaunchInfo launch_info(clang_argv);
+ Launch (launch_info);
+ next_action.None(); // Don't continue or do anything, just wait for next event...
+ }
+ break;
+ case 1:
+ {
+ m_time_launch_stop_main.Stop();
+ m_time_total.Stop();
+ SBFrame frame (m_thread.GetFrameAtIndex(0));
+
+ // Time the first expression evaluation
+ m_expr_first_evaluate(frame);
+
+ SBValue result;
+ for (size_t i=0; i<NUM_EXPR_ITERATIONS; ++i)
+ {
+ m_expr_frame_zero(frame);
+ }
+ m_target.BreakpointCreateByName("DeclContext::lookup");
+ next_action.Continue();
+ }
+ break;
+ case 2:
+ {
+ SBFrame frame (m_thread.GetFrameAtIndex(21));
+ SBValue result;
+ for (size_t i=0; i<NUM_EXPR_ITERATIONS; ++i)
+ {
+ m_expr_frame_non_zero(frame);
+ }
+ next_action.Continue();
+ }
+ break;
+ default:
+ m_memory_total.Stop();
+ next_action.Kill();
+ break;
+ }
+ }
+
+ void
+ WriteResults (Results &results)
+ {
+ Results::Dictionary& results_dict = results.GetDictionary();
+
+ m_time_set_bp_main.WriteAverageAndStandardDeviation(results);
+ results_dict.Add ("memory-change-create-target",
+ "Memory increase that occurs due to creating the target.",
+ m_memory_change_create_target.GetDeltaValue().GetResult(NULL, NULL));
+
+ results_dict.Add ("memory-change-break-main",
+ "Memory increase that occurs due to setting a breakpoint at main by name.",
+ m_memory_change_break_main.GetDeltaValue().GetResult(NULL, NULL));
+
+ m_time_create_target.WriteAverageAndStandardDeviation(results);
+ m_expr_first_evaluate.WriteAverageAndStandardDeviation(results);
+ m_expr_frame_zero.WriteAverageAndStandardDeviation(results);
+ m_expr_frame_non_zero.WriteAverageAndStandardDeviation(results);
+ results_dict.Add ("memory-total-break-main",
+ "The total memory that the current process is using after setting the first breakpoint.",
+ m_memory_total.GetStopValue().GetResult(NULL, NULL));
+
+ results_dict.AddDouble("time-launch-stop-main",
+ "The time it takes to launch the process and stop at main.",
+ m_time_launch_stop_main.GetDeltaValue());
+
+ results_dict.AddDouble("time-total",
+ "The time it takes to create the target, set breakpoint at main, launch clang and hit the breakpoint at main.",
+ m_time_total.GetDeltaValue());
+ results.Write(GetResultFilePath());
+ }
+
+
+
+ const char *
+ GetExecutablePath () const
+ {
+ if (m_exe_path.empty())
+ return NULL;
+ return m_exe_path.c_str();
+ }
+
+ const char *
+ GetResultFilePath () const
+ {
+ if (m_out_path.empty())
+ return NULL;
+ return m_out_path.c_str();
+ }
+
+ void
+ SetExecutablePath (const char *path)
+ {
+ if (path && path[0])
+ m_exe_path = path;
+ else
+ m_exe_path.clear();
+ }
+
+ void
+ SetResultFilePath (const char *path)
+ {
+ if (path && path[0])
+ m_out_path = path;
+ else
+ m_out_path.clear();
+ }
+
+ void
+ SetUseDSYM (bool b)
+ {
+ m_use_dsym = b;
+ }
+
+
+
+private:
+ // C++ formatters
+ TimeMeasurement<std::function<void()>> m_time_create_target;
+ TimeMeasurement<std::function<void()>> m_time_set_bp_main;
+ MemoryGauge m_memory_change_create_target;
+ MemoryGauge m_memory_change_break_main;
+ MemoryGauge m_memory_total;
+ TimeGauge m_time_launch_stop_main;
+ TimeGauge m_time_total;
+ TimeMeasurement<std::function<void(SBFrame)>> m_expr_first_evaluate;
+ TimeMeasurement<std::function<void(SBFrame)>> m_expr_frame_zero;
+ TimeMeasurement<std::function<void(SBFrame)>> m_expr_frame_non_zero;
+ std::string m_exe_path;
+ std::string m_out_path;
+ SBLaunchInfo m_launch_info;
+ bool m_use_dsym;
+
+};
+
+
+struct Options
+{
+ std::string clang_path;
+ std::string out_file;
+ bool verbose;
+ bool use_dsym;
+ bool error;
+ bool print_help;
+
+ Options() :
+ verbose (false),
+ error (false),
+ print_help (false)
+ {
+ }
+};
+
+static struct option g_long_options[] = {
+ { "verbose", no_argument, NULL, 'v' },
+ { "clang", required_argument, NULL, 'c' },
+ { "out-file", required_argument, NULL, 'o' },
+ { "dsym", no_argument, NULL, 'd' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+std::string
+GetShortOptionString (struct option *long_options)
+{
+ std::string option_string;
+ for (int i = 0; long_options[i].name != NULL; ++i)
+ {
+ if (long_options[i].flag == NULL)
+ {
+ option_string.push_back ((char) long_options[i].val);
+ switch (long_options[i].has_arg)
+ {
+ default:
+ case no_argument:
+ break;
+ case required_argument:
+ option_string.push_back (':');
+ break;
+ case optional_argument:
+ option_string.append (2, ':');
+ break;
+ }
+ }
+ }
+ return option_string;
+}
+
+int main(int argc, const char * argv[])
+{
+
+ // Prepare for & make calls to getopt_long_only.
+
+ std::string short_option_string (GetShortOptionString(g_long_options));
+
+ ClangTest test;
+
+ Options option_data;
+ bool done = false;
+
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+ while (!done)
+ {
+ int long_options_index = -1;
+ const int short_option = ::getopt_long_only (argc,
+ const_cast<char **>(argv),
+ short_option_string.c_str(),
+ g_long_options,
+ &long_options_index);
+
+ switch (short_option)
+ {
+ case 0:
+ // Already handled
+ break;
+
+ case -1:
+ done = true;
+ break;
+
+ case '?':
+ option_data.print_help = true;
+ break;
+
+ case 'h':
+ option_data.print_help = true;
+ break;
+
+ case 'v':
+ option_data.verbose = true;
+ break;
+
+ case 'c':
+ {
+ SBFileSpec file(optarg);
+ if (file.Exists())
+ test.SetExecutablePath(optarg);
+ else
+ fprintf(stderr, "error: file specified in --clang (-c) option doesn't exist: '%s'\n", optarg);
+ }
+ break;
+
+ case 'o':
+ test.SetResultFilePath(optarg);
+ break;
+
+ case 'd':
+ test.SetUseDSYM(true);
+ break;
+
+ default:
+ option_data.error = true;
+ option_data.print_help = true;
+ fprintf (stderr, "error: unrecognized option %c\n", short_option);
+ break;
+ }
+ }
+
+
+ if (test.GetExecutablePath() == NULL)
+ {
+ // --clang is mandatory
+ option_data.print_help = true;
+ option_data.error = true;
+ fprintf (stderr, "error: the '--clang=PATH' option is mandatory\n");
+ }
+
+ if (option_data.print_help)
+ {
+ puts(R"(
+NAME
+ lldb_perf_clang -- a tool that measures LLDB peformance while debugging clang.
+
+SYNOPSIS
+ lldb_perf_clang --clang=PATH [--out-file=PATH --verbose --dsym] -- [clang options]
+
+DESCRIPTION
+ Runs a set of static timing and memory tasks against clang and outputs results
+ to a plist file.
+)");
+ }
+ if (option_data.error)
+ {
+ exit(1);
+ }
+
+ // Update argc and argv after parsing options
+ argc -= optind;
+ argv += optind;
+
+ test.SetVerbose(true);
+ TestCase::Run(test, argc, argv);
+ return 0;
+}
+
diff --git a/tools/lldb-perf/common/clang/main.cpp b/tools/lldb-perf/common/clang/main.cpp
new file mode 100644
index 000000000000..709c3946fb2f
--- /dev/null
+++ b/tools/lldb-perf/common/clang/main.cpp
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <vector>
+
+namespace {
+ struct Foo
+ {
+ int i; int j;
+ };
+ void doit (const Foo &foo)
+ {
+ printf ("doit(%i)\n", foo.i);
+ }
+}
+int main (int argc, char const *argv[], char const *envp[])
+{
+ std::vector<int> ints;
+ for (int i=0;i<10;++i)
+ ints.push_back(i);
+ printf ("hello world\n");
+ Foo foo = { 12, 13 };
+ doit (foo);
+ return 0;
+}
diff --git a/tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp b/tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp
new file mode 100644
index 000000000000..bb607ef06fa7
--- /dev/null
+++ b/tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp
@@ -0,0 +1,335 @@
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "lldb-perf/lib/Timer.h"
+#include "lldb-perf/lib/Metric.h"
+#include "lldb-perf/lib/Measurement.h"
+#include "lldb-perf/lib/TestCase.h"
+#include "lldb-perf/lib/Xcode.h"
+
+#include <unistd.h>
+#include <string>
+#include <getopt.h>
+
+using namespace lldb_perf;
+
+class StepTest : public TestCase
+{
+ typedef void (*no_function) (void);
+
+public:
+ StepTest(bool use_single_stepping = false) :
+ m_main_source("stepping-testcase.cpp"),
+ m_use_single_stepping(use_single_stepping),
+ m_time_measurements(nullptr)
+ {
+ }
+
+ virtual
+ ~StepTest() {}
+
+ virtual bool
+ Setup (int& argc, const char**& argv)
+ {
+ TestCase::Setup (argc, argv);
+
+ // Toggle the fast stepping command on or off as required.
+ const char *single_step_cmd = "settings set target.use-fast-stepping false";
+ const char *fast_step_cmd = "settings set target.use-fast-stepping true";
+ const char *cmd_to_use;
+
+ if (m_use_single_stepping)
+ cmd_to_use = single_step_cmd;
+ else
+ cmd_to_use = fast_step_cmd;
+
+ SBCommandReturnObject return_object;
+ m_debugger.GetCommandInterpreter().HandleCommand(cmd_to_use,
+ return_object);
+ if (!return_object.Succeeded())
+ {
+ if (return_object.GetError() != NULL)
+ printf ("Got an error running settings set: %s.\n", return_object.GetError());
+ else
+ printf ("Failed running settings set, no error.\n");
+ }
+
+ m_target = m_debugger.CreateTarget(m_app_path.c_str());
+ m_first_bp = m_target.BreakpointCreateBySourceRegex("Here is some code to stop at originally.", m_main_source);
+
+ const char* file_arg = m_app_path.c_str();
+ const char* empty = nullptr;
+ const char* args[] = {file_arg, empty};
+ SBLaunchInfo launch_info (args);
+
+ return Launch (launch_info);
+ }
+
+ void
+ WriteResults (Results &results)
+ {
+ // Gotta turn off the last timer now.
+ m_individual_step_times.push_back(m_time_measurements.Stop());
+
+ size_t num_time_measurements = m_individual_step_times.size();
+
+ Results::Dictionary& results_dict = results.GetDictionary();
+ const char *short_format_string = "step-time-%0.2d";
+ const size_t short_size = strlen(short_format_string) + 5;
+ char short_buffer[short_size];
+ const char *long_format_string = "The time it takes for step %d in the step sequence.";
+ const size_t long_size = strlen(long_format_string) + 5;
+ char long_buffer[long_size];
+
+ for (size_t i = 0; i < num_time_measurements; i++)
+ {
+ snprintf (short_buffer, short_size, short_format_string, i);
+ snprintf (long_buffer, long_size, long_format_string, i);
+
+ results_dict.AddDouble(short_buffer,
+ long_buffer,
+ m_individual_step_times[i]);
+
+ }
+ results_dict.AddDouble ("total-time", "Total time spent stepping.", m_time_measurements.GetMetric().GetSum());
+ results_dict.AddDouble ("stddev-time", "StdDev of time spent stepping.", m_time_measurements.GetMetric().GetStandardDeviation());
+
+ results.Write(m_out_path.c_str());
+ }
+
+
+ const char *
+ GetExecutablePath () const
+ {
+ if (m_app_path.empty())
+ return NULL;
+ return m_app_path.c_str();
+ }
+
+ const char *
+ GetResultFilePath () const
+ {
+ if (m_out_path.empty())
+ return NULL;
+ return m_out_path.c_str();
+ }
+
+ void
+ SetExecutablePath (const char *path)
+ {
+ if (path && path[0])
+ m_app_path = path;
+ else
+ m_app_path.clear();
+ }
+
+ void
+ SetResultFilePath (const char *path)
+ {
+ if (path && path[0])
+ m_out_path = path;
+ else
+ m_out_path.clear();
+ }
+
+ void
+ SetUseSingleStep (bool use_it)
+ {
+ m_use_single_stepping = use_it;
+ }
+private:
+ virtual void
+ TestStep (int counter, ActionWanted &next_action)
+ {
+ if (counter > 0)
+ {
+ m_individual_step_times.push_back(m_time_measurements.Stop());
+
+ }
+
+ // Disable the breakpoint, just in case it gets multiple locations we don't want that confusing the stepping.
+ if (counter == 0)
+ m_first_bp.SetEnabled(false);
+
+ next_action.StepOver(m_process.GetThreadAtIndex(0));
+ m_time_measurements.Start();
+
+
+ }
+
+ SBBreakpoint m_first_bp;
+ SBFileSpec m_main_source;
+ TimeMeasurement<no_function> m_time_measurements;
+ std::vector<double> m_individual_step_times;
+ bool m_use_single_stepping;
+ std::string m_app_path;
+ std::string m_out_path;
+
+
+};
+
+struct Options
+{
+ std::string test_file_path;
+ std::string out_file;
+ bool verbose;
+ bool fast_step;
+ bool error;
+ bool print_help;
+
+ Options() :
+ verbose (false),
+ fast_step (true),
+ error (false),
+ print_help (false)
+ {
+ }
+};
+
+static struct option g_long_options[] = {
+ { "verbose", no_argument, NULL, 'v' },
+ { "single-step", no_argument, NULL, 's' },
+ { "test-file", required_argument, NULL, 't' },
+ { "out-file", required_argument, NULL, 'o' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+std::string
+GetShortOptionString (struct option *long_options)
+{
+ std::string option_string;
+ for (int i = 0; long_options[i].name != NULL; ++i)
+ {
+ if (long_options[i].flag == NULL)
+ {
+ option_string.push_back ((char) long_options[i].val);
+ switch (long_options[i].has_arg)
+ {
+ default:
+ case no_argument:
+ break;
+ case required_argument:
+ option_string.push_back (':');
+ break;
+ case optional_argument:
+ option_string.append (2, ':');
+ break;
+ }
+ }
+ }
+ return option_string;
+}
+
+int main(int argc, const char * argv[])
+{
+
+ // Prepare for & make calls to getopt_long_only.
+
+ std::string short_option_string (GetShortOptionString(g_long_options));
+
+ StepTest test;
+
+ Options option_data;
+ bool done = false;
+
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+ while (!done)
+ {
+ int long_options_index = -1;
+ const int short_option = ::getopt_long_only (argc,
+ const_cast<char **>(argv),
+ short_option_string.c_str(),
+ g_long_options,
+ &long_options_index);
+
+ switch (short_option)
+ {
+ case 0:
+ // Already handled
+ break;
+
+ case -1:
+ done = true;
+ break;
+
+ case '?':
+ option_data.print_help = true;
+ break;
+
+ case 'h':
+ option_data.print_help = true;
+ break;
+
+ case 'v':
+ option_data.verbose = true;
+ break;
+
+ case 's':
+ option_data.fast_step = false;
+ test.SetUseSingleStep(true);
+ break;
+
+ case 't':
+ {
+ SBFileSpec file(optarg);
+ if (file.Exists())
+ test.SetExecutablePath(optarg);
+ else
+ fprintf(stderr, "error: file specified in --test-file (-t) option doesn't exist: '%s'\n", optarg);
+ }
+ break;
+
+ case 'o':
+ test.SetResultFilePath(optarg);
+ break;
+
+ default:
+ option_data.error = true;
+ option_data.print_help = true;
+ fprintf (stderr, "error: unrecognized option %c\n", short_option);
+ break;
+ }
+ }
+
+
+ if (option_data.print_help)
+ {
+ puts(R"(
+NAME
+ lldb-perf-stepping -- a tool that measures LLDB peformance of simple stepping operations.
+
+SYNOPSIS
+ lldb-perf-stepping --test-file=FILE [--out-file=PATH --verbose --fast-step]
+
+DESCRIPTION
+ Runs a set of stepping operations, timing each step and outputs results
+ to a plist file.
+)");
+ exit(0);
+ }
+ if (option_data.error)
+ {
+ exit(1);
+ }
+
+ if (test.GetExecutablePath() == NULL)
+ {
+ // --clang is mandatory
+ option_data.print_help = true;
+ option_data.error = true;
+ fprintf (stderr, "error: the '--test-file=PATH' option is mandatory\n");
+ }
+
+ // Update argc and argv after parsing options
+ argc -= optind;
+ argv += optind;
+
+ test.SetVerbose(true);
+ TestCase::Run(test, argc, argv);
+ return 0;
+}
diff --git a/tools/lldb-perf/common/stepping/stepping-testcase.cpp b/tools/lldb-perf/common/stepping/stepping-testcase.cpp
new file mode 100644
index 000000000000..f842c2379c1a
--- /dev/null
+++ b/tools/lldb-perf/common/stepping/stepping-testcase.cpp
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <vector>
+#include <string>
+
+struct struct_for_copying
+{
+ struct_for_copying (int in_int, double in_double, const char *in_string) :
+ int_value(in_int),
+ double_value(in_double),
+ string_value (in_string)
+ {
+
+ }
+ struct_for_copying()
+ {
+ struct_for_copying (0, 0, "");
+ }
+
+ int int_value;
+ double double_value;
+ std::string string_value;
+};
+
+int main (int argc, char **argv)
+{
+ struct_for_copying input_struct (150 * argc, 10.0 * argc, argv[0]);
+ struct_for_copying output_struct;
+ int some_int = 44;
+ double some_double = 34.5;
+ double other_double;
+ size_t vector_size;
+ std::vector<struct_for_copying> my_vector;
+
+ printf ("Here is some code to stop at originally. Got: %d, %p.\n", argc, argv);
+ output_struct = input_struct;
+ other_double = (some_double * some_int)/((double) argc);
+ other_double = other_double > 0 ? some_double/other_double : some_double > 0 ? other_double/some_double : 10.0;
+ my_vector.push_back (input_struct);
+ vector_size = my_vector.size();
+
+ return vector_size == 0 ? 0 : 1;
+}
diff --git a/tools/lldb-perf/darwin/formatters/fmts_tester.mm b/tools/lldb-perf/darwin/formatters/fmts_tester.mm
new file mode 100644
index 000000000000..57ce008297d9
--- /dev/null
+++ b/tools/lldb-perf/darwin/formatters/fmts_tester.mm
@@ -0,0 +1,79 @@
+//===-- fmts_tester.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#import <Cocoa/Cocoa.h>
+#include <vector>
+#include <list>
+#include <map>
+#include <string>
+
+int main()
+{
+ NSArray* nsarray = @[@1,@2,@"hello world",@3,@4,@"foobar"];
+ NSMutableArray* nsmutablearray = [[NSMutableArray alloc] initWithCapacity:5];
+ [nsmutablearray addObject:@1];
+ [nsmutablearray addObject:@2];
+ [nsmutablearray addObject:@"hello world"];
+ [nsmutablearray addObject:@3];
+ [nsmutablearray addObject:@4];
+ [nsmutablearray addObject:@"foobar"];
+ NSDictionary* nsdictionary = @{@1 : @1, @2 : @2, @"hello" : @"world", @3 : @3};
+ NSMutableDictionary* nsmutabledictionary = [[NSMutableDictionary alloc] initWithCapacity:5];
+ [nsmutabledictionary setObject:@1 forKey:@1];
+ [nsmutabledictionary setObject:@2 forKey:@2];
+ [nsmutabledictionary setObject:@"hello" forKey:@"world"];
+ [nsmutabledictionary setObject:@3 forKey:@3];
+ NSString* str0 = @"Hello world";
+ NSString* str1 = @"Hello ℥";
+ NSString* str2 = @"Hello world";
+ NSString* str3 = @"Hello ℥";
+ NSString* str4 = @"Hello world";
+ NSDate* me = [NSDate dateWithNaturalLanguageString:@"April 10, 1985"];
+ NSDate* cutie = [NSDate dateWithNaturalLanguageString:@"January 29, 1983"];
+ NSDate* mom = [NSDate dateWithNaturalLanguageString:@"May 24, 1959"];
+ NSDate* dad = [NSDate dateWithNaturalLanguageString:@"October 29, 1954"];
+ NSDate* today = [NSDate dateWithNaturalLanguageString:@"March 14, 2013"];
+ NSArray* bundles = [NSBundle allBundles];
+ NSArray* frameworks = [NSBundle allFrameworks];
+ NSSet* nsset = [NSSet setWithArray:nsarray];
+ NSMutableSet* nsmutableset = [NSMutableSet setWithCapacity:5];
+ [nsmutableset addObject:@1];
+ [nsmutableset addObject:@2];
+ [nsmutableset addObject:@"hello world"];
+ [nsmutableset addObject:@3];
+ [nsmutableset addObject:@4];
+ [nsmutableset addObject:@"foobar"];
+ std::vector<int> vector;
+ vector.push_back(1);
+ vector.push_back(2);
+ vector.push_back(3);
+ vector.push_back(4);
+ vector.push_back(5);
+ std::list<int> list;
+ list.push_back(1);
+ list.push_back(2);
+ list.push_back(3);
+ list.push_back(4);
+ list.push_back(5);
+ std::map<int,int> map;
+ map[1] = 1;
+ map[2] = 2;
+ map[3] = 3;
+ map[4] = 4;
+ map[5] = 5;
+ std::string sstr0("Hello world");
+ std::string sstr1("Hello world");
+ std::string sstr2("Hello world");
+ std::string sstr3("Hello world");
+ std::string sstr4("Hello world");
+ int x = 0;
+ for (;;)
+ x++;
+} \ No newline at end of file
diff --git a/tools/lldb-perf/darwin/formatters/formatters.cpp b/tools/lldb-perf/darwin/formatters/formatters.cpp
new file mode 100644
index 000000000000..ee3875618427
--- /dev/null
+++ b/tools/lldb-perf/darwin/formatters/formatters.cpp
@@ -0,0 +1,246 @@
+//===-- formatters.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "lldb-perf/lib/Timer.h"
+#include "lldb-perf/lib/Metric.h"
+#include "lldb-perf/lib/Measurement.h"
+#include "lldb-perf/lib/TestCase.h"
+#include "lldb-perf/lib/Xcode.h"
+
+#include <iostream>
+#include <unistd.h>
+#include <fstream>
+
+using namespace lldb_perf;
+
+class FormattersTest : public TestCase
+{
+public:
+ FormattersTest () : TestCase()
+ {
+ m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-vector", "time to dump an std::vector");
+ m_dump_std_list_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-list", "time to dump an std::list");
+ m_dump_std_map_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "std-map", "time to dump an std::map");
+
+ // use this in manual mode
+ m_dump_std_string_measurement = CreateTimeMeasurement([] () -> void {
+ }, "std-string", "time to dump an std::string");
+
+ m_dump_nsstring_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,0,false);
+ }, "ns-string", "time to dump an NSString");
+
+ m_dump_nsarray_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-array", "time to dump an NSArray");
+
+ m_dump_nsdictionary_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-dictionary", "time to dump an NSDictionary");
+
+ m_dump_nsset_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-set", "time to dump an NSSet");
+
+ m_dump_nsbundle_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,1,false);
+ }, "ns-bundle", "time to dump an NSBundle");
+
+ m_dump_nsdate_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
+ lldb_perf::Xcode::FetchVariable (value,0,false);
+ }, "ns-date", "time to dump an NSDate");
+ }
+
+ virtual
+ ~FormattersTest ()
+ {
+ }
+
+ virtual bool
+ Setup (int& argc, const char**& argv)
+ {
+ m_app_path.assign(argv[1]);
+ m_out_path.assign(argv[2]);
+ m_target = m_debugger.CreateTarget(m_app_path.c_str());
+ m_target.BreakpointCreateByName("main");
+ SBLaunchInfo launch_info(argv);
+ return Launch (launch_info);
+ }
+
+ void
+ DoTest ()
+ {
+ SBFrame frame_zero(m_thread.GetFrameAtIndex(0));
+
+ m_dump_nsarray_measurement(frame_zero.FindVariable("nsarray", lldb::eDynamicCanRunTarget));
+ m_dump_nsarray_measurement(frame_zero.FindVariable("nsmutablearray", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsdictionary", lldb::eDynamicCanRunTarget));
+ m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsmutabledictionary", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str0", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str1", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str2", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str3", lldb::eDynamicCanRunTarget));
+ m_dump_nsstring_measurement(frame_zero.FindVariable("str4", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsdate_measurement(frame_zero.FindVariable("me", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("cutie", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("mom", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("dad", lldb::eDynamicCanRunTarget));
+ m_dump_nsdate_measurement(frame_zero.FindVariable("today", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsbundle_measurement(frame_zero.FindVariable("bundles", lldb::eDynamicCanRunTarget));
+ m_dump_nsbundle_measurement(frame_zero.FindVariable("frameworks", lldb::eDynamicCanRunTarget));
+
+ m_dump_nsset_measurement(frame_zero.FindVariable("nsset", lldb::eDynamicCanRunTarget));
+ m_dump_nsset_measurement(frame_zero.FindVariable("nsmutableset", lldb::eDynamicCanRunTarget));
+
+ m_dump_std_vector_measurement(frame_zero.FindVariable("vector", lldb::eDynamicCanRunTarget));
+ m_dump_std_list_measurement(frame_zero.FindVariable("list", lldb::eDynamicCanRunTarget));
+ m_dump_std_map_measurement(frame_zero.FindVariable("map", lldb::eDynamicCanRunTarget));
+
+ auto sstr0 = frame_zero.FindVariable("sstr0", lldb::eDynamicCanRunTarget);
+ auto sstr1 = frame_zero.FindVariable("sstr1", lldb::eDynamicCanRunTarget);
+ auto sstr2 = frame_zero.FindVariable("sstr2", lldb::eDynamicCanRunTarget);
+ auto sstr3 = frame_zero.FindVariable("sstr3", lldb::eDynamicCanRunTarget);
+ auto sstr4 = frame_zero.FindVariable("sstr4", lldb::eDynamicCanRunTarget);
+
+ m_dump_std_string_measurement.Start();
+ Xcode::FetchVariable(sstr0,0,false);
+ m_dump_std_string_measurement.Stop();
+
+ m_dump_std_string_measurement.Start();
+ Xcode::FetchVariable(sstr1,0,false);
+ m_dump_std_string_measurement.Stop();
+
+ m_dump_std_string_measurement.Start();
+ Xcode::FetchVariable(sstr2,0,false);
+ m_dump_std_string_measurement.Stop();
+
+ m_dump_std_string_measurement.Start();
+ Xcode::FetchVariable(sstr3,0,false);
+ m_dump_std_string_measurement.Stop();
+
+ m_dump_std_string_measurement.Start();
+ Xcode::FetchVariable(sstr4,0,false);
+ m_dump_std_string_measurement.Stop();
+
+ }
+
+ virtual void
+ TestStep (int counter, ActionWanted &next_action)
+ {
+ switch (counter)
+ {
+ case 0:
+ m_target.BreakpointCreateByLocation("fmts_tester.mm", 78);
+ next_action.Continue();
+ break;
+ case 1:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 2:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 3:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 4:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 5:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 6:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 7:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 8:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 9:
+ DoTest ();
+ next_action.Continue();
+ break;
+ case 10:
+ DoTest ();
+ next_action.Continue();
+ break;
+ default:
+ next_action.Kill();
+ break;
+ }
+ }
+
+ virtual void
+ WriteResults (Results &results)
+ {
+ m_dump_std_vector_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_std_list_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_std_map_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_std_string_measurement.WriteAverageAndStandardDeviation(results);
+
+ m_dump_nsstring_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_nsarray_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_nsdictionary_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_nsset_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_nsbundle_measurement.WriteAverageAndStandardDeviation(results);
+ m_dump_nsdate_measurement.WriteAverageAndStandardDeviation(results);
+ results.Write(m_out_path.c_str());
+ }
+
+private:
+ // C++ formatters
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_std_vector_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_std_list_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_std_map_measurement;
+ TimeMeasurement<std::function<void()>> m_dump_std_string_measurement;
+
+ // Cocoa formatters
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsstring_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsarray_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdictionary_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsset_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsbundle_measurement;
+ TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdate_measurement;
+
+ // useful files
+ std::string m_app_path;
+ std::string m_out_path;
+};
+
+// argv[1] == path to app
+// argv[2] == path to result
+int main(int argc, const char * argv[])
+{
+ FormattersTest frmtest;
+ frmtest.SetVerbose(true);
+ TestCase::Run(frmtest,argc,argv);
+ return 0;
+}
+
diff --git a/tools/lldb-perf/darwin/sketch/foobar.sketch2 b/tools/lldb-perf/darwin/sketch/foobar.sketch2
new file mode 100644
index 000000000000..553c698b180c
--- /dev/null
+++ b/tools/lldb-perf/darwin/sketch/foobar.sketch2
Binary files differ
diff --git a/tools/lldb-perf/darwin/sketch/sketch.cpp b/tools/lldb-perf/darwin/sketch/sketch.cpp
new file mode 100644
index 000000000000..93e39165c133
--- /dev/null
+++ b/tools/lldb-perf/darwin/sketch/sketch.cpp
@@ -0,0 +1,380 @@
+//===-- sketch.cpp ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "lldb-perf/lib/Timer.h"
+#include "lldb-perf/lib/Metric.h"
+#include "lldb-perf/lib/Measurement.h"
+#include "lldb-perf/lib/TestCase.h"
+#include "lldb-perf/lib/Xcode.h"
+
+#include <iostream>
+#include <unistd.h>
+#include <fstream>
+#include <getopt.h>
+
+using namespace lldb_perf;
+
+static struct option g_long_options[] = {
+ { "verbose", no_argument, NULL, 'v' },
+ { "sketch", required_argument, NULL, 'c' },
+ { "foobar", required_argument, NULL, 'f' },
+ { "out-file", required_argument, NULL, 'o' },
+ { NULL, 0, NULL, 0 }
+};
+
+class SketchTest : public TestCase
+{
+public:
+ SketchTest () :
+ m_fetch_frames_measurement ([this] () -> void
+ {
+ Xcode::FetchFrames (GetProcess(),false,false);
+ }, "fetch-frames", "time to dump backtrace for every frame in every thread"),
+ m_file_line_bp_measurement([this] (const char* file, uint32_t line) -> void
+ {
+ Xcode::CreateFileLineBreakpoint(GetTarget(), file, line);
+ }, "file-line-bkpt", "time to set a breakpoint given a file and line"),
+ m_fetch_modules_measurement ([this] () -> void
+ {
+ Xcode::FetchModules(GetTarget());
+ }, "fetch-modules", "time to get info for all modules in the process"),
+ m_fetch_vars_measurement([this] (int depth) -> void
+ {
+ SBProcess process (GetProcess());
+ auto threads_count = process.GetNumThreads();
+ for (size_t thread_num = 0; thread_num < threads_count; thread_num++)
+ {
+ SBThread thread(process.GetThreadAtIndex(thread_num));
+ SBFrame frame(thread.GetFrameAtIndex(0));
+ Xcode::FetchVariables(frame,depth,GetVerbose());
+ }
+ }, "fetch-vars", "time to dump variables for the topmost frame in every thread"),
+ m_run_expr_measurement([this] (SBFrame frame, const char* expr) -> void
+ {
+ SBValue value(frame.EvaluateExpression(expr, lldb::eDynamicCanRunTarget));
+ Xcode::FetchVariable (value, 0, GetVerbose());
+ }, "run-expr", "time to evaluate an expression and display the result")
+ {
+ m_app_path.clear();
+ m_out_path.clear();
+ m_doc_path.clear();
+ m_print_help = false;
+ }
+
+ virtual
+ ~SketchTest ()
+ {
+ }
+
+ virtual bool
+ ParseOption (int short_option, const char* optarg)
+ {
+ switch (short_option)
+ {
+ case 0:
+ return false;
+
+ case -1:
+ return false;
+
+ case '?':
+ case 'h':
+ m_print_help = true;
+ break;
+
+ case 'v':
+ SetVerbose(true);
+ break;
+
+ case 'c':
+ {
+ SBFileSpec file(optarg);
+ if (file.Exists())
+ SetExecutablePath(optarg);
+ else
+ fprintf(stderr, "error: file specified in --sketch (-c) option doesn't exist: '%s'\n", optarg);
+ }
+ break;
+
+ case 'f':
+ {
+ SBFileSpec file(optarg);
+ if (file.Exists())
+ SetDocumentPath(optarg);
+ else
+ fprintf(stderr, "error: file specified in --foobar (-f) option doesn't exist: '%s'\n", optarg);
+ }
+ break;
+
+ case 'o':
+ SetResultFilePath(optarg);
+ break;
+
+ default:
+ m_print_help = true;
+ fprintf (stderr, "error: unrecognized option %c\n", short_option);
+ break;
+ }
+ return true;
+ }
+
+ virtual struct option*
+ GetLongOptions ()
+ {
+ return g_long_options;
+ }
+
+ virtual bool
+ Setup (int& argc, const char**& argv)
+ {
+ TestCase::Setup(argc,argv);
+ bool error = false;
+
+ if (GetExecutablePath() == NULL)
+ {
+ // --sketch is mandatory
+ error = true;
+ fprintf (stderr, "error: the '--sketch=PATH' option is mandatory\n");
+ }
+
+ if (GetDocumentPath() == NULL)
+ {
+ // --foobar is mandatory
+ error = true;
+ fprintf (stderr, "error: the '--foobar=PATH' option is mandatory\n");
+ }
+
+ if (error || GetPrintHelp())
+ {
+ puts(R"(
+ NAME
+ lldb_perf_sketch -- a tool that measures LLDB peformance while debugging sketch.
+
+ SYNOPSIS
+ lldb_perf_sketch --sketch=PATH --foobar=PATH [--out-file=PATH --verbose]
+
+ DESCRIPTION
+ Runs a set of static timing and memory tasks against sketch and outputs results
+ to a plist file.
+ )");
+ }
+
+ if (error)
+ {
+ exit(1);
+ }
+ lldb::SBLaunchInfo launch_info = GetLaunchInfo();
+ m_target = m_debugger.CreateTarget(m_app_path.c_str());
+ m_file_line_bp_measurement("SKTDocument.m",245);
+ m_file_line_bp_measurement("SKTDocument.m",283);
+ m_file_line_bp_measurement("SKTText.m",326);
+ return Launch (launch_info);
+ }
+
+ lldb::SBLaunchInfo
+ GetLaunchInfo ()
+ {
+ const char* file_arg = m_doc_path.c_str();
+ const char* persist_arg = "-ApplePersistenceIgnoreState";
+ const char* persist_skip = "YES";
+ const char* empty = nullptr;
+ const char* args[] = {file_arg,persist_arg,persist_skip,empty};
+ return SBLaunchInfo(args);
+ }
+
+ void
+ DoTest ()
+ {
+ m_fetch_frames_measurement();
+ m_fetch_modules_measurement();
+ m_fetch_vars_measurement(1);
+ }
+
+ virtual void
+ TestStep (int counter, ActionWanted &next_action)
+ {
+ static int launch = 1;
+ switch (counter % 10)
+ {
+ case 0:
+ {
+ DoTest ();
+ if (counter == 0)
+ m_file_line_bp_measurement("SKTDocument.m",254);
+ next_action.Continue();
+ }
+ break;
+
+ case 1:
+ {
+ DoTest ();
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"properties");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[properties description]");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"typeName");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"data");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[data description]");
+ next_action.Continue();
+ }
+ break;
+
+ case 2:
+ {
+ DoTest ();
+ next_action.Continue();
+ }
+ break;
+
+ case 3:
+ {
+ DoTest ();
+ next_action.StepOver(m_thread);
+ }
+ break;
+
+ case 4:
+ {
+ DoTest ();
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"layoutManager");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"contents");
+ next_action.StepOver(m_thread);
+ }
+ break;
+
+ case 5:
+ {
+ DoTest ();
+ next_action.StepOver(m_thread);
+ }
+ break;
+
+ case 6:
+ {
+ DoTest ();
+ next_action.StepOver(m_thread);
+ }
+ break;
+
+ case 7:
+ {
+ DoTest ();
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@\"an NSString\"");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[(id)@\"an NSString\" description]");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@[@1,@2,@3]");
+ next_action.StepOut(m_thread);
+ }
+ break;
+
+ case 8:
+ {
+ DoTest ();
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[graphics description]");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[selectionIndexes description]");
+ m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"(BOOL)NSIntersectsRect(rect, graphicDrawingBounds)");
+ }
+ next_action.CallNext();
+ break;
+ case 9:
+ if (++launch < 10)
+ next_action.Relaunch(GetLaunchInfo());
+ else
+ next_action.Kill();
+ break;
+
+
+ default:
+ {
+ next_action.Kill();
+ }
+ break;
+ }
+ }
+
+ virtual void
+ WriteResults (Results &results)
+ {
+ m_fetch_frames_measurement.WriteAverageAndStandardDeviation(results);
+ m_file_line_bp_measurement.WriteAverageAndStandardDeviation(results);
+ m_fetch_modules_measurement.WriteAverageAndStandardDeviation(results);
+ m_fetch_vars_measurement.WriteAverageAndStandardDeviation(results);
+ m_run_expr_measurement.WriteAverageAndStandardDeviation(results);
+ results.Write(GetResultFilePath());
+ }
+
+ void
+ SetExecutablePath (const char* str)
+ {
+ if (str)
+ m_app_path.assign(str);
+ }
+
+ const char*
+ GetExecutablePath ()
+ {
+ if (m_app_path.empty())
+ return NULL;
+ return m_app_path.c_str();
+ }
+
+ void
+ SetDocumentPath (const char* str)
+ {
+ if (str)
+ m_doc_path.assign(str);
+ }
+
+ const char*
+ GetDocumentPath ()
+ {
+ if (m_doc_path.empty())
+ return NULL;
+ return m_doc_path.c_str();
+ }
+
+
+ void
+ SetResultFilePath (const char* str)
+ {
+ if (str)
+ m_out_path.assign(str);
+ }
+
+ const char*
+ GetResultFilePath ()
+ {
+ if (m_out_path.empty())
+ return "/dev/stdout";
+ return m_out_path.c_str();
+ }
+
+ bool
+ GetPrintHelp ()
+ {
+ return m_print_help;
+ }
+
+private:
+ Measurement<lldb_perf::TimeGauge, std::function<void()>> m_fetch_frames_measurement;
+ Measurement<lldb_perf::TimeGauge, std::function<void(const char*, uint32_t)>> m_file_line_bp_measurement;
+ Measurement<lldb_perf::TimeGauge, std::function<void()>> m_fetch_modules_measurement;
+ Measurement<lldb_perf::TimeGauge, std::function<void(int)>> m_fetch_vars_measurement;
+ Measurement<lldb_perf::TimeGauge, std::function<void(SBFrame, const char*)>> m_run_expr_measurement;
+
+ std::string m_app_path;
+ std::string m_doc_path;
+ std::string m_out_path;
+ bool m_print_help;
+};
+
+int main(int argc, const char * argv[])
+{
+ SketchTest test;
+ return TestCase::Run(test, argc, argv);
+}
diff --git a/tools/lldb-perf/lib/Gauge.cpp b/tools/lldb-perf/lib/Gauge.cpp
new file mode 100644
index 000000000000..4c4593b3b292
--- /dev/null
+++ b/tools/lldb-perf/lib/Gauge.cpp
@@ -0,0 +1,53 @@
+//===-- Gauge.cpp -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Gauge.h"
+#include "lldb/lldb-forward.h"
+
+template <>
+lldb_perf::Results::ResultSP
+lldb_perf::GetResult (const char *description, double value)
+{
+ if (description && description[0])
+ {
+ std::unique_ptr<Results::Dictionary> value_dict_ap (new Results::Dictionary ());
+ value_dict_ap->AddString("description", NULL, description);
+ value_dict_ap->AddDouble("value", NULL, value);
+ return Results::ResultSP (value_dict_ap.release());
+ }
+ return Results::ResultSP (new Results::Double (NULL, NULL, value));
+}
+
+template <>
+lldb_perf::Results::ResultSP
+lldb_perf::GetResult (const char *description, uint64_t value)
+{
+ if (description && description[0])
+ {
+ std::unique_ptr<Results::Dictionary> value_dict_ap (new Results::Dictionary ());
+ value_dict_ap->AddString("description", NULL, description);
+ value_dict_ap->AddUnsigned("value", NULL, value);
+ return Results::ResultSP (value_dict_ap.release());
+ }
+ return Results::ResultSP (new Results::Unsigned (NULL, NULL, value));
+}
+
+template <>
+lldb_perf::Results::ResultSP
+lldb_perf::GetResult (const char *description, std::string value)
+{
+ if (description && description[0])
+ {
+ std::unique_ptr<Results::Dictionary> value_dict_ap (new Results::Dictionary ());
+ value_dict_ap->AddString("description", NULL, description);
+ value_dict_ap->AddString("value", NULL, value.c_str());
+ return Results::ResultSP (value_dict_ap.release());
+ }
+ return Results::ResultSP (new Results::String (NULL, NULL, value.c_str()));
+}
diff --git a/tools/lldb-perf/lib/Gauge.h b/tools/lldb-perf/lib/Gauge.h
new file mode 100644
index 000000000000..fc5c444a38b8
--- /dev/null
+++ b/tools/lldb-perf/lib/Gauge.h
@@ -0,0 +1,64 @@
+//===-- Gauge.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef PerfTestDriver_Gauge_h
+#define PerfTestDriver_Gauge_h
+
+#include <functional>
+#include <string>
+
+#include "Results.h"
+
+namespace lldb_perf {
+
+template <class T>
+class Gauge
+{
+public:
+ typedef T ValueType;
+
+ Gauge ()
+ {}
+
+ virtual
+ ~Gauge ()
+ {}
+
+ virtual void
+ Start () = 0;
+
+ virtual ValueType
+ Stop () = 0;
+
+ virtual ValueType
+ GetStartValue () const = 0;
+
+ virtual ValueType
+ GetStopValue () const = 0;
+
+ virtual ValueType
+ GetDeltaValue () const = 0;
+
+};
+
+template <class T>
+Results::ResultSP GetResult (const char *description, T value);
+
+template <>
+Results::ResultSP GetResult (const char *description, double value);
+
+template <>
+Results::ResultSP GetResult (const char *description, uint64_t value);
+
+template <>
+Results::ResultSP GetResult (const char *description, std::string value);
+
+}
+
+#endif
diff --git a/tools/lldb-perf/lib/Measurement.h b/tools/lldb-perf/lib/Measurement.h
new file mode 100644
index 000000000000..877e618d5469
--- /dev/null
+++ b/tools/lldb-perf/lib/Measurement.h
@@ -0,0 +1,217 @@
+//===-- Measurement.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PerfTestDriver__Measurement__
+#define __PerfTestDriver__Measurement__
+
+#include "Gauge.h"
+#include "Timer.h"
+#include "Metric.h"
+#include "MemoryGauge.h"
+
+namespace lldb_perf
+{
+template <typename GaugeType, typename Callable>
+class Measurement
+{
+public:
+ Measurement () :
+ m_gauge (),
+ m_callable (),
+ m_metric ()
+ {
+ }
+
+ Measurement (Callable callable, const char* name, const char* desc) :
+ m_gauge (),
+ m_callable (callable),
+ m_metric (Metric<typename GaugeType::ValueType>(name, desc))
+ {
+ }
+
+ Measurement (const char* name, const char* desc) :
+ m_gauge (),
+ m_callable (),
+ m_metric (Metric<typename GaugeType::ValueType>(name, desc))
+ {
+ }
+
+ template <typename GaugeType_Rhs, typename Callable_Rhs>
+ Measurement (const Measurement<GaugeType_Rhs, Callable_Rhs>& rhs) :
+ m_gauge(rhs.GetGauge()),
+ m_callable(rhs.GetCallable()),
+ m_metric(rhs.GetMetric())
+ {
+ }
+
+ template <typename... Args>
+ void
+ operator () (Args... args)
+ {
+ m_gauge.Start();
+ m_callable(args...);
+ m_metric.Append (m_gauge.Stop());
+ }
+
+ virtual const Callable&
+ GetCallable () const
+ {
+ return m_callable;
+ }
+
+ virtual const GaugeType&
+ GetGauge () const
+ {
+ return m_gauge;
+ }
+
+ virtual const Metric<typename GaugeType::ValueType>&
+ GetMetric () const
+ {
+ return m_metric;
+ }
+
+ void
+ Start ()
+ {
+ m_gauge.Start();
+ }
+
+ typename GaugeType::ValueType
+ Stop ()
+ {
+ auto value = m_gauge.Stop();
+ m_metric.Append(value);
+ return value;
+ }
+
+ void
+ WriteStartValue (Results &results)
+ {
+ auto metric = GetMetric ();
+ results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult<typename GaugeType::ValueType> (NULL, metric.GetStartValue()));
+ }
+
+ void
+ WriteStopValue (Results &results)
+ {
+ auto metric = GetMetric ();
+ results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult<typename GaugeType::ValueType> (NULL, metric.GetStopValue()));
+ }
+
+ void
+ WriteAverageValue (Results &results)
+ {
+ auto metric = GetMetric ();
+ results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult<typename GaugeType::ValueType> (NULL, metric.GetAverage()));
+ }
+
+ void
+ WriteAverageAndStandardDeviation (Results &results)
+ {
+ auto metric = GetMetric ();
+ auto dictionary = (Results::Dictionary*)results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult<typename GaugeType::ValueType> (NULL, metric.GetAverage())).get();
+ if (dictionary)
+ {
+ dictionary->Add("stddev", NULL, lldb_perf::GetResult<typename GaugeType::ValueType> (NULL, metric.GetStandardDeviation()));
+ }
+ }
+
+ void
+ WriteStandardDeviation (Results &results)
+ {
+ auto metric = GetMetric ();
+ results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult<typename GaugeType::ValueType> (NULL, metric.GetStandardDeviation()));
+ }
+
+protected:
+ GaugeType m_gauge;
+ Callable m_callable;
+ Metric<typename GaugeType::ValueType> m_metric;
+};
+
+template <typename Callable>
+class TimeMeasurement : public Measurement<TimeGauge,Callable>
+{
+public:
+ TimeMeasurement () :
+ Measurement<TimeGauge,Callable> ()
+ {
+ }
+
+ TimeMeasurement (Callable callable,
+ const char* name = NULL,
+ const char* descr = NULL) :
+ Measurement<TimeGauge,Callable> (callable, name, descr)
+ {
+ }
+
+ template <typename Callable_Rhs>
+ TimeMeasurement (const TimeMeasurement<Callable_Rhs>& rhs) :
+ Measurement<TimeGauge,Callable>(rhs)
+ {
+ }
+
+ template <typename GaugeType_Rhs, typename Callable_Rhs>
+ TimeMeasurement (const Measurement<GaugeType_Rhs, Callable_Rhs>& rhs) :
+ Measurement<GaugeType_Rhs,Callable_Rhs>(rhs)
+ {
+ }
+
+ template <typename... Args>
+ void
+ operator () (Args... args)
+ {
+ Measurement<TimeGauge,Callable>::operator()(args...);
+ }
+};
+
+template <typename Callable>
+class MemoryMeasurement : public Measurement<MemoryGauge,Callable>
+{
+public:
+ MemoryMeasurement () : Measurement<MemoryGauge,Callable> ()
+ {
+ }
+
+ MemoryMeasurement (Callable callable,
+ const char* name,
+ const char* descr) :
+ Measurement<MemoryGauge,Callable> (callable, name, descr)
+ {
+ }
+
+ MemoryMeasurement (const char* name, const char* descr) :
+ Measurement<MemoryGauge,Callable> (name, descr)
+ {
+ }
+
+ template <typename Callable_Rhs>
+ MemoryMeasurement (const MemoryMeasurement<Callable_Rhs>& rhs) :
+ Measurement<MemoryGauge,Callable>(rhs)
+ {
+ }
+
+ template <typename GaugeType_Rhs, typename Callable_Rhs>
+ MemoryMeasurement (const Measurement<GaugeType_Rhs, Callable_Rhs>& rhs) :
+ Measurement<GaugeType_Rhs,Callable_Rhs>(rhs)
+ {
+ }
+
+ template <typename... Args>
+ void
+ operator () (Args... args)
+ {
+ Measurement<MemoryGauge,Callable>::operator()(args...);
+ }
+};
+
+}
+
+#endif /* defined(__PerfTestDriver__Measurement__) */
diff --git a/tools/lldb-perf/lib/MemoryGauge.cpp b/tools/lldb-perf/lib/MemoryGauge.cpp
new file mode 100644
index 000000000000..2a46453f540b
--- /dev/null
+++ b/tools/lldb-perf/lib/MemoryGauge.cpp
@@ -0,0 +1,165 @@
+//===-- MemoryGauge.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MemoryGauge.h"
+#include "lldb/lldb-forward.h"
+#include <assert.h>
+#include <cmath>
+#include <mach/mach.h>
+#include <mach/task.h>
+#include <mach/mach_traps.h>
+
+using namespace lldb_perf;
+
+MemoryStats::MemoryStats (mach_vm_size_t virtual_size,
+ mach_vm_size_t resident_size,
+ mach_vm_size_t max_resident_size) :
+ m_virtual_size (virtual_size),
+ m_resident_size (resident_size),
+ m_max_resident_size (max_resident_size)
+{
+}
+
+MemoryStats::MemoryStats (const MemoryStats& rhs) :
+ m_virtual_size (rhs.m_virtual_size),
+ m_resident_size (rhs.m_resident_size),
+ m_max_resident_size (rhs.m_max_resident_size)
+{
+}
+
+
+MemoryStats&
+MemoryStats::operator = (const MemoryStats& rhs)
+{
+ if (this != &rhs)
+ {
+ m_virtual_size = rhs.m_virtual_size;
+ m_resident_size = rhs.m_resident_size;
+ m_max_resident_size = rhs.m_max_resident_size;
+ }
+ return *this;
+}
+
+MemoryStats&
+MemoryStats::operator += (const MemoryStats& rhs)
+{
+ m_virtual_size += rhs.m_virtual_size;
+ m_resident_size += rhs.m_resident_size;
+ m_max_resident_size += rhs.m_max_resident_size;
+ return *this;
+}
+
+MemoryStats
+MemoryStats::operator - (const MemoryStats& rhs)
+{
+ return MemoryStats(m_virtual_size - rhs.m_virtual_size,
+ m_resident_size - rhs.m_resident_size,
+ m_max_resident_size - rhs.m_max_resident_size);
+}
+
+MemoryStats
+MemoryStats::operator + (const MemoryStats& rhs)
+{
+ return MemoryStats(m_virtual_size + rhs.m_virtual_size,
+ m_resident_size + rhs.m_resident_size,
+ m_max_resident_size + rhs.m_max_resident_size);
+}
+
+MemoryStats
+MemoryStats::operator / (size_t n)
+{
+ MemoryStats result(*this);
+ result.m_virtual_size /= n;
+ result.m_resident_size /= n;
+ result.m_max_resident_size /= n;
+ return result;
+}
+
+MemoryStats
+MemoryStats::operator * (const MemoryStats& rhs)
+{
+ return MemoryStats(m_virtual_size * rhs.m_virtual_size,
+ m_resident_size * rhs.m_resident_size,
+ m_max_resident_size * rhs.m_max_resident_size);
+}
+
+Results::ResultSP
+MemoryStats::GetResult (const char *name, const char *description) const
+{
+ std::unique_ptr<Results::Dictionary> dict_ap (new Results::Dictionary (name, NULL));
+ dict_ap->AddUnsigned("resident", NULL, GetResidentSize());
+ dict_ap->AddUnsigned("max_resident", NULL, GetMaxResidentSize());
+ return Results::ResultSP(dict_ap.release());
+}
+
+MemoryGauge::ValueType
+MemoryGauge::Now ()
+{
+ task_t task = mach_task_self();
+ mach_task_basic_info_data_t taskBasicInfo;
+ mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+ auto task_info_ret = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t) & taskBasicInfo, &count);
+ if (task_info_ret == KERN_SUCCESS) {
+ return MemoryStats(taskBasicInfo.virtual_size, taskBasicInfo.resident_size, taskBasicInfo.resident_size_max);
+ }
+ return 0;
+}
+
+MemoryGauge::MemoryGauge () :
+ m_state(MemoryGauge::State::eNeverUsed),
+ m_start(),
+ m_delta()
+{
+}
+
+void
+MemoryGauge::Start ()
+{
+ m_state = MemoryGauge::State::eCounting;
+ m_start = Now();
+}
+
+MemoryGauge::ValueType
+MemoryGauge::Stop ()
+{
+ m_stop = Now();
+ assert(m_state == MemoryGauge::State::eCounting && "cannot stop a non-started gauge");
+ m_state = MemoryGauge::State::eStopped;
+ m_delta = m_stop - m_start;
+ return m_delta;
+}
+
+
+MemoryGauge::ValueType
+MemoryGauge::GetDeltaValue () const
+{
+ assert(m_state == MemoryGauge::State::eStopped && "gauge must be used before you can evaluate it");
+ return m_delta;
+}
+
+template <>
+Results::ResultSP
+lldb_perf::GetResult (const char *description, MemoryStats value)
+{
+ return value.GetResult (NULL, description);
+}
+
+MemoryStats
+sqrt (const MemoryStats& arg)
+{
+ long double virt_size = arg.GetVirtualSize();
+ long double resident_size = arg.GetResidentSize();
+ long double max_resident_size = arg.GetMaxResidentSize();
+
+ virt_size = sqrtl(virt_size);
+ resident_size = sqrtl(resident_size);
+ max_resident_size = sqrtl(max_resident_size);
+
+ return MemoryStats(virt_size,resident_size,max_resident_size);
+}
diff --git a/tools/lldb-perf/lib/MemoryGauge.h b/tools/lldb-perf/lib/MemoryGauge.h
new file mode 100644
index 000000000000..a1221b6b66cc
--- /dev/null
+++ b/tools/lldb-perf/lib/MemoryGauge.h
@@ -0,0 +1,147 @@
+//===-- MemoryGauge.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PerfTestDriver__MemoryGauge__
+#define __PerfTestDriver__MemoryGauge__
+
+#include "Gauge.h"
+#include "Results.h"
+
+#include <mach/task_info.h>
+
+namespace lldb_perf {
+
+class MemoryStats
+{
+public:
+ MemoryStats (mach_vm_size_t virtual_size = 0,
+ mach_vm_size_t resident_size = 0,
+ mach_vm_size_t max_resident_size = 0);
+ MemoryStats (const MemoryStats& rhs);
+
+ MemoryStats&
+ operator = (const MemoryStats& rhs);
+
+ MemoryStats&
+ operator += (const MemoryStats& rhs);
+
+ MemoryStats
+ operator - (const MemoryStats& rhs);
+
+ MemoryStats
+ operator + (const MemoryStats& rhs);
+
+ MemoryStats
+ operator / (size_t rhs);
+
+ MemoryStats
+ operator * (const MemoryStats& rhs);
+
+ mach_vm_size_t
+ GetVirtualSize () const
+ {
+ return m_virtual_size;
+ }
+
+ mach_vm_size_t
+ GetResidentSize () const
+ {
+ return m_resident_size;
+ }
+
+ mach_vm_size_t
+ GetMaxResidentSize () const
+ {
+ return m_max_resident_size;
+ }
+
+ void
+ SetVirtualSize (mach_vm_size_t vs)
+ {
+ m_virtual_size = vs;
+ }
+
+ void
+ SetResidentSize (mach_vm_size_t rs)
+ {
+ m_resident_size = rs;
+ }
+
+ void
+ SetMaxResidentSize (mach_vm_size_t mrs)
+ {
+ m_max_resident_size = mrs;
+ }
+
+ Results::ResultSP
+ GetResult (const char *name, const char *description) const;
+private:
+ mach_vm_size_t m_virtual_size;
+ mach_vm_size_t m_resident_size;
+ mach_vm_size_t m_max_resident_size;
+};
+
+class MemoryGauge : public Gauge<MemoryStats>
+{
+public:
+ MemoryGauge ();
+
+ virtual
+ ~MemoryGauge ()
+ {
+ }
+
+ void
+ Start ();
+
+ ValueType
+ Stop ();
+
+ virtual ValueType
+ GetStartValue() const
+ {
+ return m_start;
+ }
+
+ virtual ValueType
+ GetStopValue() const
+ {
+ return m_stop;
+ }
+
+ virtual ValueType
+ GetDeltaValue() const;
+
+private:
+ enum class State
+ {
+ eNeverUsed,
+ eCounting,
+ eStopped
+ };
+
+ ValueType
+ Now ();
+
+ State m_state;
+ ValueType m_start;
+ ValueType m_stop;
+ ValueType m_delta;
+};
+
+template <>
+Results::ResultSP
+GetResult (const char *description, MemoryStats value);
+
+} // namespace lldb_perf
+
+lldb_perf::MemoryStats
+sqrt (const lldb_perf::MemoryStats& arg);
+
+#endif // #ifndef __PerfTestDriver__MemoryGauge__
diff --git a/tools/lldb-perf/lib/Metric.cpp b/tools/lldb-perf/lib/Metric.cpp
new file mode 100644
index 000000000000..1951cdb0250a
--- /dev/null
+++ b/tools/lldb-perf/lib/Metric.cpp
@@ -0,0 +1,85 @@
+//===-- Metric.cpp ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Metric.h"
+#include "MemoryGauge.h"
+#include <cmath>
+
+using namespace lldb_perf;
+
+template <class T>
+Metric<T>::Metric () : Metric ("")
+{
+}
+
+template <class T>
+Metric<T>::Metric (const char* n, const char* d) :
+ m_name(n ? n : ""),
+ m_description(d ? d : ""),
+ m_dataset ()
+{
+}
+
+template <class T>
+void
+Metric<T>::Append (T v)
+{
+ m_dataset.push_back(v);
+}
+
+template <class T>
+size_t
+Metric<T>::GetCount () const
+{
+ return m_dataset.size();
+}
+
+template <class T>
+T
+Metric<T>::GetSum () const
+{
+ T sum = 0;
+ for (auto v : m_dataset)
+ sum += v;
+ return sum;
+}
+
+template <class T>
+T
+Metric<T>::GetAverage () const
+{
+ return GetSum()/GetCount();
+}
+
+
+// Knuth's algorithm for stddev - massive cancellation resistant
+template <class T>
+T
+Metric<T>::GetStandardDeviation (StandardDeviationMode mode) const
+{
+ size_t n = 0;
+ T mean = 0;
+ T M2 = 0;
+ for (auto x : m_dataset)
+ {
+ n = n + 1;
+ T delta = x - mean;
+ mean = mean + delta/n;
+ M2 = M2+delta*(x-mean);
+ }
+ T variance;
+ if (mode == StandardDeviationMode::ePopulation || n == 1)
+ variance = M2 / n;
+ else
+ variance = M2 / (n - 1);
+ return sqrt(variance);
+}
+
+template class lldb_perf::Metric<double>;
+template class lldb_perf::Metric<MemoryStats>;
diff --git a/tools/lldb-perf/lib/Metric.h b/tools/lldb-perf/lib/Metric.h
new file mode 100644
index 000000000000..45342d25b41a
--- /dev/null
+++ b/tools/lldb-perf/lib/Metric.h
@@ -0,0 +1,72 @@
+//===-- Metric.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PerfTestDriver__Metric__
+#define __PerfTestDriver__Metric__
+
+#include <vector>
+#include <string>
+#include <mach/task_info.h>
+
+namespace lldb_perf {
+
+class MemoryStats;
+
+template <class ValueType>
+class Metric
+{
+public:
+ enum class StandardDeviationMode
+ {
+ eSample,
+ ePopulation
+ };
+
+ Metric ();
+ Metric (const char*, const char* = NULL);
+
+ void
+ Append (ValueType v);
+
+ ValueType
+ GetAverage () const;
+
+ size_t
+ GetCount () const;
+
+ ValueType
+ GetSum () const;
+
+ ValueType
+ GetStandardDeviation (StandardDeviationMode mode = StandardDeviationMode::ePopulation) const;
+
+ const char*
+ GetName () const
+ {
+ if (m_name.empty())
+ return NULL;
+ return m_name.c_str();
+ }
+
+ const char*
+ GetDescription () const
+ {
+ if (m_description.empty())
+ return NULL;
+ return m_description.c_str();
+ }
+
+private:
+ std::string m_name;
+ std::string m_description;
+ std::vector<ValueType> m_dataset;
+};
+}
+
+#endif /* defined(__PerfTestDriver__Metric__) */
diff --git a/tools/lldb-perf/lib/Results.cpp b/tools/lldb-perf/lib/Results.cpp
new file mode 100644
index 000000000000..6abf67e53b67
--- /dev/null
+++ b/tools/lldb-perf/lib/Results.cpp
@@ -0,0 +1,275 @@
+//===-- Results.cpp ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Results.h"
+#include <assert.h>
+
+#ifdef __APPLE__
+#include "CFCMutableArray.h"
+#include "CFCMutableDictionary.h"
+#include "CFCReleaser.h"
+#include "CFCString.h"
+#endif
+
+using namespace lldb_perf;
+
+static void
+AddResultToArray (CFCMutableArray &array, Results::Result *result);
+
+static void
+AddResultToDictionary (CFCMutableDictionary &parent_dict, const char *key, Results::Result *result);
+
+static void
+AddResultToArray (CFCMutableArray &parent_array, Results::Result *result)
+{
+ switch (result->GetType())
+ {
+ case Results::Result::Type::Invalid:
+ break;
+
+ case Results::Result::Type::Array:
+ {
+ Results::Array *value = result->GetAsArray();
+ CFCMutableArray array;
+ value->ForEach([&array](const Results::ResultSP &value_sp) -> bool
+ {
+ AddResultToArray (array, value_sp.get());
+ return true;
+ });
+ parent_array.AppendValue(array.get(), true);
+ }
+ break;
+
+ case Results::Result::Type::Dictionary:
+ {
+ Results::Dictionary *value = result->GetAsDictionary();
+ CFCMutableDictionary dict;
+ value->ForEach([&dict](const std::string &key, const Results::ResultSP &value_sp) -> bool
+ {
+ AddResultToDictionary (dict, key.c_str(), value_sp.get());
+ return true;
+ });
+ if (result->GetDescription())
+ {
+ dict.AddValueCString(CFSTR("description"), result->GetDescription());
+ }
+ parent_array.AppendValue(dict.get(), true);
+ }
+ break;
+
+ case Results::Result::Type::Double:
+ {
+ double d = result->GetAsDouble()->GetValue();
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &d));
+ if (cf_number.get())
+ parent_array.AppendValue(cf_number.get(), true);
+ }
+ break;
+ case Results::Result::Type::String:
+ {
+ CFCString cfstr (result->GetAsString()->GetValue());
+ if (cfstr.get())
+ parent_array.AppendValue(cfstr.get(), true);
+ }
+ break;
+
+ case Results::Result::Type::Unsigned:
+ {
+ uint64_t uval64 = result->GetAsUnsigned()->GetValue();
+ CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &uval64));
+ if (cf_number.get())
+ parent_array.AppendValue(cf_number.get(), true);
+ }
+ break;
+
+ default:
+ assert (!"unhandled result");
+ break;
+ }
+}
+
+
+static void
+AddResultToDictionary (CFCMutableDictionary &parent_dict, const char *key, Results::Result *result)
+{
+ assert (key && key[0]);
+ CFCString cf_key(key);
+ switch (result->GetType())
+ {
+ case Results::Result::Type::Invalid:
+ break;
+
+ case Results::Result::Type::Array:
+ {
+ Results::Array *value = result->GetAsArray();
+ CFCMutableArray array;
+ value->ForEach([&array](const Results::ResultSP &value_sp) -> bool
+ {
+ AddResultToArray (array, value_sp.get());
+ return true;
+ });
+ parent_dict.AddValue(cf_key.get(), array.get(), true);
+ }
+ break;
+ case Results::Result::Type::Dictionary:
+ {
+ Results::Dictionary *value = result->GetAsDictionary();
+ CFCMutableDictionary dict;
+ value->ForEach([&dict](const std::string &key, const Results::ResultSP &value_sp) -> bool
+ {
+ AddResultToDictionary (dict, key.c_str(), value_sp.get());
+ return true;
+ });
+ if (result->GetDescription())
+ {
+ dict.AddValueCString(CFSTR("description"), result->GetDescription());
+ }
+ parent_dict.AddValue(cf_key.get(), dict.get(), true);
+ }
+ break;
+ case Results::Result::Type::Double:
+ {
+ parent_dict.SetValueDouble(cf_key.get(), result->GetAsDouble()->GetValue(), true);
+ }
+ break;
+ case Results::Result::Type::String:
+ {
+ parent_dict.SetValueCString(cf_key.get(), result->GetAsString()->GetValue(), true);
+ }
+ break;
+
+ case Results::Result::Type::Unsigned:
+ {
+ parent_dict.SetValueUInt64 (cf_key.get(), result->GetAsUnsigned()->GetValue(), true);
+ }
+ break;
+ default:
+ assert (!"unhandled result");
+ break;
+ }
+}
+void
+Results::Write (const char *out_path)
+{
+#ifdef __APPLE__
+ CFCMutableDictionary dict;
+
+ m_results.ForEach([&dict](const std::string &key, const ResultSP &value_sp) -> bool
+ {
+ AddResultToDictionary (dict, key.c_str(), value_sp.get());
+ return true;
+ });
+ CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, dict.get(), kCFPropertyListXMLFormat_v1_0, 0, NULL);
+
+ if (out_path == NULL)
+ out_path = "/dev/stdout";
+
+ CFURLRef file = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*)out_path, strlen(out_path), FALSE);
+
+ CFURLWriteDataAndPropertiesToResource(file, xmlData, NULL, NULL);
+#endif
+}
+
+Results::ResultSP
+Results::Dictionary::AddUnsigned (const char *name, const char *description, uint64_t value)
+{
+ assert (name && name[0]);
+ if (description && description[0])
+ {
+ std::unique_ptr<Results::Dictionary> value_dict_ap (new Results::Dictionary ());
+ value_dict_ap->AddString("description", NULL, description);
+ value_dict_ap->AddUnsigned("value", NULL, value);
+ m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release());
+ }
+ else
+ m_dictionary[std::string(name)] = ResultSP (new Unsigned (name, description, value));
+ return m_dictionary[std::string(name)];
+}
+
+Results::ResultSP
+Results::Dictionary::AddDouble (const char *name, const char *description, double value)
+{
+ assert (name && name[0]);
+
+ if (description && description[0])
+ {
+ std::unique_ptr<Results::Dictionary> value_dict_ap (new Results::Dictionary ());
+ value_dict_ap->AddString("description", NULL, description);
+ value_dict_ap->AddDouble("value", NULL, value);
+ m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release());
+ }
+ else
+ m_dictionary[std::string(name)] = ResultSP (new Double (name, description, value));
+ return m_dictionary[std::string(name)];
+}
+Results::ResultSP
+Results::Dictionary::AddString (const char *name, const char *description, const char *value)
+{
+ assert (name && name[0]);
+ if (description && description[0])
+ {
+ std::unique_ptr<Results::Dictionary> value_dict_ap (new Results::Dictionary ());
+ value_dict_ap->AddString("description", NULL, description);
+ value_dict_ap->AddString("value", NULL, value);
+ m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release());
+ }
+ else
+ m_dictionary[std::string(name)] = ResultSP (new String (name, description, value));
+ return m_dictionary[std::string(name)];
+}
+
+Results::ResultSP
+Results::Dictionary::Add (const char *name, const char *description, const ResultSP &result_sp)
+{
+ assert (name && name[0]);
+ if (description && description[0])
+ {
+ std::unique_ptr<Results::Dictionary> value_dict_ap (new Results::Dictionary ());
+ value_dict_ap->AddString("description", NULL, description);
+ value_dict_ap->Add("value", NULL, result_sp);
+ m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release());
+ }
+ else
+ m_dictionary[std::string(name)] = result_sp;
+ return m_dictionary[std::string(name)];
+}
+
+void
+Results::Dictionary::ForEach (const std::function <bool (const std::string &, const ResultSP &)> &callback)
+{
+ collection::const_iterator pos, end = m_dictionary.end();
+ for (pos = m_dictionary.begin(); pos != end; ++pos)
+ {
+ if (callback (pos->first.c_str(), pos->second) == false)
+ return;
+ }
+}
+
+
+
+Results::ResultSP
+Results::Array::Append (const ResultSP &result_sp)
+{
+ m_array.push_back (result_sp);
+ return result_sp;
+}
+
+void
+Results::Array::ForEach (const std::function <bool (const ResultSP &)> &callback)
+{
+ collection::const_iterator pos, end = m_array.end();
+ for (pos = m_array.begin(); pos != end; ++pos)
+ {
+ if (callback (*pos) == false)
+ return;
+ }
+}
+
+
+
diff --git a/tools/lldb-perf/lib/Results.h b/tools/lldb-perf/lib/Results.h
new file mode 100644
index 000000000000..388077e7f581
--- /dev/null
+++ b/tools/lldb-perf/lib/Results.h
@@ -0,0 +1,312 @@
+//===-- Results.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PerfTestDriver_Results_h__
+#define __PerfTestDriver_Results_h__
+
+#include "lldb/lldb-forward.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace lldb_perf {
+
+class Results
+{
+public:
+ class Array;
+ class Dictionary;
+ class Double;
+ class String;
+ class Unsigned;
+
+ class Result
+ {
+ public:
+ enum class Type
+ {
+ Invalid,
+ Array,
+ Dictionary,
+ Double,
+ String,
+ Unsigned
+ };
+
+ Result (Type type, const char *name, const char *description) :
+ m_name (),
+ m_description(),
+ m_type (type)
+ {
+ if (name && name[0])
+ m_name = name;
+ if (description && description[0])
+ m_description = description;
+ }
+
+ virtual
+ ~Result()
+ {
+ }
+
+ virtual void
+ Write (Results &results) = 0;
+
+ Array *
+ GetAsArray ()
+ {
+ if (m_type == Type::Array)
+ return (Array *)this;
+ return NULL;
+ }
+ Dictionary *
+ GetAsDictionary ()
+ {
+ if (m_type == Type::Dictionary)
+ return (Dictionary *)this;
+ return NULL;
+ }
+ Double *
+ GetAsDouble ()
+ {
+ if (m_type == Type::Double)
+ return (Double *)this;
+ return NULL;
+ }
+
+ String *
+ GetAsString ()
+ {
+ if (m_type == Type::String)
+ return (String *)this;
+ return NULL;
+ }
+ Unsigned *
+ GetAsUnsigned ()
+ {
+ if (m_type == Type::Unsigned)
+ return (Unsigned *)this;
+ return NULL;
+ }
+
+ const char *
+ GetName() const
+ {
+ if (m_name.empty())
+ return NULL;
+ return m_name.c_str();
+ }
+
+ const char *
+ GetDescription() const
+ {
+ if (m_description.empty())
+ return NULL;
+ return m_description.c_str();
+ }
+
+ Type
+ GetType() const
+ {
+ return m_type;
+ }
+
+ protected:
+ std::string m_name;
+ std::string m_description;
+ Type m_type;
+ };
+
+ typedef std::shared_ptr<Result> ResultSP;
+
+ class Array : public Result
+ {
+ public:
+ Array (const char *name, const char *description) :
+ Result (Type::Array, name, description)
+ {
+ }
+
+ virtual
+ ~Array()
+ {
+ }
+
+ ResultSP
+ Append (const ResultSP &result_sp);
+
+ void
+ ForEach (const std::function <bool (const ResultSP &)> &callback);
+
+ virtual void
+ Write (Results &results)
+ {
+ }
+ protected:
+ typedef std::vector<ResultSP> collection;
+ collection m_array;
+ };
+
+ class Dictionary : public Result
+ {
+ public:
+ Dictionary () :
+ Result (Type::Dictionary, NULL, NULL)
+ {
+ }
+
+ Dictionary (const char *name, const char *description) :
+ Result (Type::Dictionary, name, description)
+ {
+ }
+
+ virtual
+ ~Dictionary()
+ {
+ }
+
+ virtual void
+ Write (Results &results)
+ {
+ }
+
+ void
+ ForEach (const std::function <bool (const std::string &, const ResultSP &)> &callback);
+
+ ResultSP
+ Add (const char *name, const char *description, const ResultSP &result_sp);
+
+ ResultSP
+ AddDouble (const char *name, const char *descriptiorn, double value);
+
+ ResultSP
+ AddUnsigned (const char *name, const char *description, uint64_t value);
+
+ ResultSP
+ AddString (const char *name, const char *description, const char *value);
+
+ protected:
+
+ typedef std::map<std::string, ResultSP> collection;
+ collection m_dictionary;
+ };
+
+ class String : public Result
+ {
+ public:
+ String (const char *name, const char *description, const char *value) :
+ Result (Type::String, name, description),
+ m_string ()
+ {
+ if (value && value[0])
+ m_string = value;
+ }
+
+ virtual
+ ~String()
+ {
+ }
+
+ virtual void
+ Write (Results &results)
+ {
+ }
+
+ const char *
+ GetValue () const
+ {
+ return m_string.empty() ? NULL : m_string.c_str();
+ }
+
+ protected:
+ std::string m_string;
+ };
+
+ class Double : public Result
+ {
+ public:
+ Double (const char *name, const char *description, double value) :
+ Result (Type::Double, name, description),
+ m_double (value)
+ {
+ }
+
+ virtual
+ ~Double()
+ {
+ }
+
+ virtual void
+ Write (Results &results)
+ {
+ }
+
+ double
+ GetValue () const
+ {
+ return m_double;
+ }
+
+ protected:
+ double m_double;
+ };
+
+ class Unsigned : public Result
+ {
+ public:
+ Unsigned (const char *name, const char *description, uint64_t value) :
+ Result (Type::Unsigned, name, description),
+ m_unsigned (value)
+ {
+ }
+
+ virtual
+ ~Unsigned()
+ {
+ }
+
+ virtual void
+ Write (Results &results)
+ {
+ }
+
+ uint64_t
+ GetValue () const
+ {
+ return m_unsigned;
+ }
+
+ protected:
+ uint64_t m_unsigned;
+ };
+
+ Results () :
+ m_results ()
+ {
+ }
+
+ ~Results()
+ {
+ }
+
+ Dictionary &
+ GetDictionary ()
+ {
+ return m_results;
+ }
+
+ void
+ Write (const char *path);
+
+protected:
+ Dictionary m_results;
+};
+
+} // namespace lldb_perf
+#endif // #ifndef __PerfTestDriver_Results_h__
diff --git a/tools/lldb-perf/lib/TestCase.cpp b/tools/lldb-perf/lib/TestCase.cpp
new file mode 100644
index 000000000000..c23a5e519773
--- /dev/null
+++ b/tools/lldb-perf/lib/TestCase.cpp
@@ -0,0 +1,358 @@
+//===-- TestCase.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestCase.h"
+#include "Results.h"
+#include "Xcode.h"
+
+using namespace lldb_perf;
+
+TestCase::TestCase () :
+ m_debugger(),
+ m_target(),
+ m_process(),
+ m_thread(),
+ m_listener(),
+ m_verbose(false),
+ m_step(0)
+{
+ SBDebugger::Initialize();
+ SBHostOS::ThreadCreated ("<lldb-tester.app.main>");
+ m_debugger = SBDebugger::Create(false);
+ m_listener = m_debugger.GetListener();
+ m_listener.StartListeningForEventClass (m_debugger, SBProcess::GetBroadcasterClass(), SBProcess::eBroadcastBitStateChanged | SBProcess::eBroadcastBitInterrupt);
+}
+
+static std::string
+GetShortOptionString (struct option *long_options)
+{
+ std::string option_string;
+ for (int i = 0; long_options[i].name != NULL; ++i)
+ {
+ if (long_options[i].flag == NULL)
+ {
+ option_string.push_back ((char) long_options[i].val);
+ switch (long_options[i].has_arg)
+ {
+ default:
+ case no_argument:
+ break;
+ case required_argument:
+ option_string.push_back (':');
+ break;
+ case optional_argument:
+ option_string.append (2, ':');
+ break;
+ }
+ }
+ }
+ return option_string;
+}
+
+bool
+TestCase::Setup (int& argc, const char**& argv)
+{
+ bool done = false;
+
+ struct option* long_options = GetLongOptions();
+
+ if (long_options)
+ {
+ std::string short_option_string (GetShortOptionString(long_options));
+
+ #if __GLIBC__
+ optind = 0;
+ #else
+ optreset = 1;
+ optind = 1;
+ #endif
+ while (!done)
+ {
+ int long_options_index = -1;
+ const int short_option = ::getopt_long_only (argc,
+ const_cast<char **>(argv),
+ short_option_string.c_str(),
+ long_options,
+ &long_options_index);
+
+ switch (short_option)
+ {
+ case 0:
+ // Already handled
+ break;
+
+ case -1:
+ done = true;
+ break;
+
+ default:
+ done = !ParseOption(short_option, optarg);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ }
+
+ return false;
+}
+
+bool
+TestCase::Launch (lldb::SBLaunchInfo &launch_info)
+{
+ lldb::SBError error;
+ m_process = m_target.Launch (launch_info, error);
+ if (!error.Success())
+ fprintf (stderr, "error: %s\n", error.GetCString());
+ if (m_process.IsValid())
+ return true;
+ return false;
+}
+
+bool
+TestCase::Launch (std::initializer_list<const char*> args)
+{
+ std::vector<const char*> args_vect(args);
+ args_vect.push_back(NULL);
+ lldb::SBLaunchInfo launch_info((const char**)&args_vect[0]);
+ return Launch(launch_info);
+}
+
+void
+TestCase::SetVerbose (bool b)
+{
+ m_verbose = b;
+}
+
+bool
+TestCase::GetVerbose ()
+{
+ return m_verbose;
+}
+
+void
+TestCase::Loop ()
+{
+ while (true)
+ {
+ bool call_test_step = false;
+ if (m_process.IsValid())
+ {
+ SBEvent evt;
+ m_listener.WaitForEvent (UINT32_MAX, evt);
+ StateType state = SBProcess::GetStateFromEvent (evt);
+ if (m_verbose)
+ printf("event = %s\n",SBDebugger::StateAsCString(state));
+ if (SBProcess::GetRestartedFromEvent(evt))
+ {
+ if (m_verbose)
+ {
+ const uint32_t num_threads = m_process.GetNumThreads();
+ for (auto thread_index = 0; thread_index < num_threads; thread_index++)
+ {
+ SBThread thread(m_process.GetThreadAtIndex(thread_index));
+ SBFrame frame(thread.GetFrameAtIndex(0));
+ SBStream strm;
+ strm.RedirectToFileHandle(stdout, false);
+ frame.GetDescription(strm);
+ }
+ puts("restarted");
+ }
+ call_test_step = false;
+ }
+ else
+ {
+ switch (state)
+ {
+ case eStateInvalid:
+ case eStateDetached:
+ case eStateCrashed:
+ case eStateUnloaded:
+ break;
+ case eStateExited:
+ return;
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ call_test_step = false;
+ break;
+
+ case eStateStopped:
+ case eStateSuspended:
+ {
+ call_test_step = true;
+ bool fatal = false;
+ bool selected_thread = false;
+ const uint32_t num_threads = m_process.GetNumThreads();
+ for (auto thread_index = 0; thread_index < num_threads; thread_index++)
+ {
+ SBThread thread(m_process.GetThreadAtIndex(thread_index));
+ SBFrame frame(thread.GetFrameAtIndex(0));
+ SBStream strm;
+ strm.RedirectToFileHandle(stdout, false);
+ frame.GetDescription(strm);
+ bool select_thread = false;
+ StopReason stop_reason = thread.GetStopReason();
+ if (m_verbose) printf("tid = 0x%llx pc = 0x%llx ",thread.GetThreadID(),frame.GetPC());
+ switch (stop_reason)
+ {
+ case eStopReasonNone:
+ if (m_verbose)
+ printf("none\n");
+ break;
+
+ case eStopReasonTrace:
+ select_thread = true;
+ if (m_verbose)
+ printf("trace\n");
+ break;
+
+ case eStopReasonPlanComplete:
+ select_thread = true;
+ if (m_verbose)
+ printf("plan complete\n");
+ break;
+ case eStopReasonThreadExiting:
+ if (m_verbose)
+ printf("thread exiting\n");
+ break;
+ case eStopReasonExec:
+ if (m_verbose)
+ printf("exec\n");
+ break;
+ case eStopReasonInvalid:
+ if (m_verbose)
+ printf("invalid\n");
+ break;
+ case eStopReasonException:
+ select_thread = true;
+ if (m_verbose)
+ printf("exception\n");
+ fatal = true;
+ break;
+ case eStopReasonBreakpoint:
+ select_thread = true;
+ if (m_verbose)
+ printf("breakpoint id = %lld.%lld\n",thread.GetStopReasonDataAtIndex(0),thread.GetStopReasonDataAtIndex(1));
+ break;
+ case eStopReasonWatchpoint:
+ select_thread = true;
+ if (m_verbose)
+ printf("watchpoint id = %lld\n",thread.GetStopReasonDataAtIndex(0));
+ break;
+ case eStopReasonSignal:
+ select_thread = true;
+ if (m_verbose)
+ printf("signal %d\n",(int)thread.GetStopReasonDataAtIndex(0));
+ break;
+ }
+ if (select_thread && !selected_thread)
+ {
+ m_thread = thread;
+ selected_thread = m_process.SetSelectedThread(thread);
+ }
+ }
+ if (fatal)
+ {
+ if (m_verbose) Xcode::RunCommand(m_debugger,"bt all",true);
+ exit(1);
+ }
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ call_test_step = true;
+ }
+
+ if (call_test_step)
+ {
+ do_the_call:
+ if (m_verbose)
+ printf("RUNNING STEP %d\n",m_step);
+ ActionWanted action;
+ TestStep(m_step, action);
+ m_step++;
+ SBError err;
+ switch (action.type)
+ {
+ case ActionWanted::Type::eNone:
+ // Just exit and wait for the next event
+ break;
+ case ActionWanted::Type::eContinue:
+ err = m_process.Continue();
+ break;
+ case ActionWanted::Type::eStepOut:
+ if (action.thread.IsValid() == false)
+ {
+ if (m_verbose)
+ {
+ Xcode::RunCommand(m_debugger,"bt all",true);
+ printf("error: invalid thread for step out on step %d\n", m_step);
+ }
+ exit(501);
+ }
+ m_process.SetSelectedThread(action.thread);
+ action.thread.StepOut();
+ break;
+ case ActionWanted::Type::eStepOver:
+ if (action.thread.IsValid() == false)
+ {
+ if (m_verbose)
+ {
+ Xcode::RunCommand(m_debugger,"bt all",true);
+ printf("error: invalid thread for step over %d\n",m_step);
+ }
+ exit(500);
+ }
+ m_process.SetSelectedThread(action.thread);
+ action.thread.StepOver();
+ break;
+ case ActionWanted::Type::eRelaunch:
+ if (m_process.IsValid())
+ {
+ m_process.Kill();
+ m_process.Clear();
+ }
+ Launch(action.launch_info);
+ break;
+ case ActionWanted::Type::eKill:
+ if (m_verbose)
+ printf("kill\n");
+ m_process.Kill();
+ return;
+ case ActionWanted::Type::eCallNext:
+ goto do_the_call;
+ break;
+ }
+ }
+
+ }
+
+ if (GetVerbose()) printf("I am gonna die at step %d\n",m_step);
+}
+
+int
+TestCase::Run (TestCase& test, int argc, const char** argv)
+{
+ if (test.Setup(argc, argv))
+ {
+ test.Loop();
+ Results results;
+ test.WriteResults(results);
+ return RUN_SUCCESS;
+ }
+ else
+ return RUN_SETUP_ERROR;
+}
+
diff --git a/tools/lldb-perf/lib/TestCase.h b/tools/lldb-perf/lib/TestCase.h
new file mode 100644
index 000000000000..811d0432b58a
--- /dev/null
+++ b/tools/lldb-perf/lib/TestCase.h
@@ -0,0 +1,205 @@
+//===-- TestCase.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PerfTestDriver__TestCase__
+#define __PerfTestDriver__TestCase__
+
+#include "lldb/API/LLDB.h"
+#include "Measurement.h"
+#include <getopt.h>
+
+namespace lldb_perf {
+
+class Results;
+
+class TestCase
+{
+public:
+ TestCase();
+
+ struct ActionWanted
+ {
+ enum class Type
+ {
+ eStepOver,
+ eContinue,
+ eStepOut,
+ eRelaunch,
+ eCallNext,
+ eNone,
+ eKill
+ } type;
+ lldb::SBThread thread;
+ lldb::SBLaunchInfo launch_info;
+
+ ActionWanted () :
+ type (Type::eContinue),
+ thread (),
+ launch_info (NULL)
+ {
+ }
+
+ void
+ None ()
+ {
+ type = Type::eNone;
+ thread = lldb::SBThread();
+ }
+
+ void
+ Continue()
+ {
+ type = Type::eContinue;
+ thread = lldb::SBThread();
+ }
+
+ void
+ StepOver (lldb::SBThread t)
+ {
+ type = Type::eStepOver;
+ thread = t;
+ }
+
+ void
+ StepOut (lldb::SBThread t)
+ {
+ type = Type::eStepOut;
+ thread = t;
+ }
+
+ void
+ Relaunch (lldb::SBLaunchInfo l)
+ {
+ type = Type::eRelaunch;
+ thread = lldb::SBThread();
+ launch_info = l;
+ }
+
+ void
+ Kill ()
+ {
+ type = Type::eKill;
+ thread = lldb::SBThread();
+ }
+
+ void
+ CallNext ()
+ {
+ type = Type::eCallNext;
+ thread = lldb::SBThread();
+ }
+ };
+
+ virtual
+ ~TestCase ()
+ {
+ }
+
+ virtual bool
+ Setup (int& argc, const char**& argv);
+
+ virtual void
+ TestStep (int counter, ActionWanted &next_action) = 0;
+
+ bool
+ Launch (lldb::SBLaunchInfo &launch_info);
+
+ bool
+ Launch (std::initializer_list<const char*> args = {});
+
+ void
+ Loop();
+
+ void
+ SetVerbose (bool);
+
+ bool
+ GetVerbose ();
+
+ virtual void
+ WriteResults (Results &results) = 0;
+
+ template <typename G,typename A>
+ Measurement<G,A> CreateMeasurement (A a, const char* name = NULL, const char* description = NULL)
+ {
+ return Measurement<G,A> (a,name, description);
+ }
+
+ template <typename A>
+ TimeMeasurement<A> CreateTimeMeasurement (A a, const char* name = NULL, const char* description = NULL)
+ {
+ return TimeMeasurement<A> (a,name, description);
+ }
+
+ template <typename A>
+ MemoryMeasurement<A> CreateMemoryMeasurement (A a, const char* name = NULL, const char* description = NULL)
+ {
+ return MemoryMeasurement<A> (a,name, description);
+ }
+
+ static int
+ Run (TestCase& test, int argc, const char** argv);
+
+ virtual bool
+ ParseOption (int short_option, const char* optarg)
+ {
+ return false;
+ }
+
+ virtual struct option*
+ GetLongOptions ()
+ {
+ return NULL;
+ }
+
+ lldb::SBDebugger &
+ GetDebugger()
+ {
+ return m_debugger;
+ }
+
+ lldb::SBTarget &
+ GetTarget()
+ {
+ return m_target;
+ }
+
+ lldb::SBProcess &
+ GetProcess ()
+ {
+ return m_process;
+ }
+
+ lldb::SBThread &
+ GetThread ()
+ {
+ return m_thread;
+ }
+
+ int
+ GetStep ()
+ {
+ return m_step;
+ }
+
+ static const int RUN_SUCCESS = 0;
+ static const int RUN_SETUP_ERROR = 100;
+
+protected:
+ lldb::SBDebugger m_debugger;
+ lldb::SBTarget m_target;
+ lldb::SBProcess m_process;
+ lldb::SBThread m_thread;
+ lldb::SBListener m_listener;
+ bool m_verbose;
+ int m_step;
+};
+}
+
+#endif /* defined(__PerfTestDriver__TestCase__) */
diff --git a/tools/lldb-perf/lib/Timer.cpp b/tools/lldb-perf/lib/Timer.cpp
new file mode 100644
index 000000000000..4bbab904c63e
--- /dev/null
+++ b/tools/lldb-perf/lib/Timer.cpp
@@ -0,0 +1,61 @@
+//===-- Timer.cpp -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Timer.h"
+#include <assert.h>
+
+using namespace lldb_perf;
+
+TimeGauge::TimeType
+TimeGauge::Now ()
+{
+ return high_resolution_clock::now();
+}
+
+TimeGauge::TimeGauge () :
+ m_start(),
+ m_state(TimeGauge::State::eNeverUsed)
+{
+}
+
+void
+TimeGauge::Start ()
+{
+ m_state = TimeGauge::State::eCounting;
+ m_start = Now();
+}
+
+double
+TimeGauge::Stop ()
+{
+ m_stop = Now();
+ assert(m_state == TimeGauge::State::eCounting && "cannot stop a non-started clock");
+ m_state = TimeGauge::State::eStopped;
+ m_delta = duration_cast<duration<double>>(m_stop-m_start).count();
+ return m_delta;
+}
+
+double
+TimeGauge::GetStartValue () const
+{
+ return (double)m_start.time_since_epoch().count() * (double)system_clock::period::num / (double)system_clock::period::den;
+}
+
+double
+TimeGauge::GetStopValue () const
+{
+ return (double)m_stop.time_since_epoch().count() * (double)system_clock::period::num / (double)system_clock::period::den;
+}
+
+double
+TimeGauge::GetDeltaValue () const
+{
+ assert(m_state == TimeGauge::State::eStopped && "clock must be used before you can evaluate it");
+ return m_delta;
+}
diff --git a/tools/lldb-perf/lib/Timer.h b/tools/lldb-perf/lib/Timer.h
new file mode 100644
index 000000000000..ff179355cd43
--- /dev/null
+++ b/tools/lldb-perf/lib/Timer.h
@@ -0,0 +1,66 @@
+//===-- Timer.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PerfTestDriver__Timer__
+#define __PerfTestDriver__Timer__
+
+#include "Gauge.h"
+
+#include <chrono>
+
+using namespace std::chrono;
+
+namespace lldb_perf
+{
+class TimeGauge : public Gauge<double>
+{
+public:
+ TimeGauge ();
+
+ virtual
+ ~TimeGauge ()
+ {
+ }
+
+ void
+ Start ();
+
+ double
+ Stop ();
+
+ virtual double
+ GetStartValue () const;
+
+ virtual double
+ GetStopValue () const;
+
+ virtual double
+ GetDeltaValue () const;
+
+private:
+ enum class State
+ {
+ eNeverUsed,
+ eCounting,
+ eStopped
+ };
+
+ typedef high_resolution_clock::time_point TimeType;
+ TimeType m_start;
+ TimeType m_stop;
+ double m_delta;
+ State m_state;
+
+ TimeType
+ Now ();
+
+};
+}
+
+#endif /* defined(__PerfTestDriver__Timer__) */
diff --git a/tools/lldb-perf/lib/Xcode.cpp b/tools/lldb-perf/lib/Xcode.cpp
new file mode 100644
index 000000000000..7b35e1c8cb54
--- /dev/null
+++ b/tools/lldb-perf/lib/Xcode.cpp
@@ -0,0 +1,165 @@
+//===-- Xcode.cpp -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Xcode.h"
+#include <string>
+
+using namespace std;
+using namespace lldb_perf;
+
+void
+Xcode::FetchVariable (SBValue value, uint32_t expand, bool verbose)
+{
+ auto name = value.GetName();
+ auto num_value = value.GetValueAsUnsigned(0);
+ auto summary = value.GetSummary();
+ auto in_scope = value.IsInScope();
+ auto has_children = value.MightHaveChildren();
+ auto type_1 = value.GetType();
+ auto type_2 = value.GetType();
+ auto type_name_1 = value.GetTypeName();
+ auto type_3 = value.GetType();
+ auto type_name_2 = value.GetTypeName();
+ if (verbose)
+ printf("%s %s = 0x%llx (%llu) %s\n",value.GetTypeName(),value.GetName(),num_value, num_value,summary);
+ if (expand > 0)
+ {
+ auto count = value.GetNumChildren();
+ for (int i = 0; i < count; i++)
+ {
+ SBValue child(value.GetChildAtIndex(i, lldb::eDynamicCanRunTarget, true));
+ FetchVariable (child,expand-1,verbose);
+ }
+ }
+}
+
+void
+Xcode::FetchModules (SBTarget target, bool verbose)
+{
+ auto count = target.GetNumModules();
+ for (int i = 0; i < count; i++)
+ {
+ SBModule module(target.GetModuleAtIndex(i));
+ auto fspec = module.GetFileSpec();
+ std::string path(1024,0);
+ fspec.GetPath(&path[0],1024);
+ auto uuid = module.GetUUIDBytes();
+ if (verbose)
+ {
+ printf("%s %s\n",path.c_str(),module.GetUUIDString());
+ }
+ }
+}
+
+void
+Xcode::FetchVariables (SBFrame frame, uint32_t expand, bool verbose)
+{
+ auto values = frame.GetVariables (true,true,true,false, eDynamicCanRunTarget);
+ auto count = values.GetSize();
+ for (int i = 0; i < count; i++)
+ {
+ SBValue value(values.GetValueAtIndex(i));
+ FetchVariable (value,expand,verbose);
+ }
+}
+
+void
+Xcode::FetchFrames(SBProcess process, bool variables, bool verbose)
+{
+ auto pCount = process.GetNumThreads();
+ for (int p = 0; p < pCount; p++)
+ {
+ SBThread thread(process.GetThreadAtIndex(p));
+ auto tCount = thread.GetNumFrames ();
+ if (verbose)
+ printf("%s %d %d {%d}\n",thread.GetQueueName(),tCount,thread.GetStopReason(),eStopReasonBreakpoint);
+ for (int t = 0; t < tCount; t++)
+ {
+ SBFrame frame(thread.GetFrameAtIndex(t));
+ auto fp = frame.GetFP();
+ SBThread thread_dup = frame.GetThread();
+ SBFileSpec filespec(process.GetTarget().GetExecutable());
+ std::string path(1024,0);
+ filespec.GetPath(&path[0],1024);
+ auto state = process.GetState();
+ auto pCount_dup = process.GetNumThreads();
+ auto byte_size = process.GetAddressByteSize();
+ auto pc = frame.GetPC();
+ SBSymbolContext context(frame.GetSymbolContext(0x0000006e));
+ SBModule module(context.GetModule());
+ SBLineEntry entry(context.GetLineEntry());
+ SBFileSpec entry_filespec(process.GetTarget().GetExecutable());
+ std::string entry_path(1024,0);
+ entry_filespec.GetPath(&entry_path[0],1024);
+ auto line_1 = entry.GetLine();
+ auto line_2 = entry.GetLine();
+ auto fname = frame.GetFunctionName();
+ if (verbose)
+ printf("%llu %s %d %d %llu %s %d %s\n",fp,path.c_str(),state,byte_size,pc,entry_path.c_str(),line_1,fname);
+ if (variables)
+ FetchVariables (frame, 0, verbose);
+ }
+ }
+}
+
+void
+Xcode::RunExpression (SBFrame frame, const char* expression, bool po, bool verbose)
+{
+ SBValue value (frame.EvaluateExpression (expression, eDynamicCanRunTarget));
+ FetchVariable (value,0,verbose);
+ if (po)
+ {
+ auto descr = value.GetObjectDescription();
+ if (descr)
+ printf("po = %s\n",descr);
+ }
+}
+
+void
+Xcode::Next (SBThread thread)
+{
+ thread.StepOver();
+}
+
+void
+Xcode::Continue (SBProcess process)
+{
+ process.Continue();
+}
+
+void
+Xcode::RunCommand (SBDebugger debugger, const char* cmd, bool verbose)
+{
+ SBCommandReturnObject sb_ret;
+ auto interpreter = debugger.GetCommandInterpreter();
+ interpreter.HandleCommand(cmd,sb_ret);
+ if (verbose)
+ printf("%s\n%s\n",sb_ret.GetOutput(false),sb_ret.GetError(false));
+}
+
+SBThread
+Xcode::GetThreadWithStopReason (SBProcess process, StopReason reason)
+{
+ auto threads_count = process.GetNumThreads();
+ for (auto thread_num = 0; thread_num < threads_count; thread_num++)
+ {
+ SBThread thread(process.GetThreadAtIndex(thread_num));
+ if (thread.GetStopReason() == reason)
+ {
+ return thread;
+ }
+ }
+ return SBThread();
+}
+
+SBBreakpoint
+Xcode::CreateFileLineBreakpoint (SBTarget target, const char* file, uint32_t line)
+{
+ return target.BreakpointCreateByLocation(file, line);
+}
diff --git a/tools/lldb-perf/lib/Xcode.h b/tools/lldb-perf/lib/Xcode.h
new file mode 100644
index 000000000000..77e025369372
--- /dev/null
+++ b/tools/lldb-perf/lib/Xcode.h
@@ -0,0 +1,64 @@
+//===-- Xcode.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PerfTestDriver__Xcode__
+#define __PerfTestDriver__Xcode__
+
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBModule.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBLineEntry.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBBreakpoint.h"
+
+using namespace lldb;
+
+namespace lldb_perf
+{
+class Xcode
+{
+public:
+ static void
+ FetchVariable (SBValue value, uint32_t expand = 0, bool verbose = false);
+
+ static void
+ FetchModules (SBTarget target, bool verbose = false);
+
+ static void
+ FetchVariables (SBFrame frame, uint32_t expand = 0, bool verbose = false);
+
+ static void
+ FetchFrames (SBProcess process, bool variables = false, bool verbose = false);
+
+ static void
+ RunExpression (SBFrame frame, const char* expression, bool po = false, bool verbose = false);
+
+ static void
+ Next (SBThread thread);
+
+ static void
+ Continue (SBProcess process);
+
+ static void
+ RunCommand (SBDebugger debugger, const char* cmd, bool verbose = false);
+
+ static SBThread
+ GetThreadWithStopReason (SBProcess process, StopReason reason);
+
+ static SBBreakpoint
+ CreateFileLineBreakpoint (SBTarget target, const char* file, uint32_t line);
+};
+}
+
+#endif /* defined(__PerfTestDriver__Xcode__) */
diff --git a/tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj b/tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj
new file mode 100644
index 000000000000..0750f700b11d
--- /dev/null
+++ b/tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj
@@ -0,0 +1,1224 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 4C1E37E316F7A0A500FF10BB /* All Perf Tests */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 4C1E37E416F7A0A600FF10BB /* Build configuration list for PBXAggregateTarget "All Perf Tests" */;
+ buildPhases = (
+ );
+ dependencies = (
+ 4CE3706916FB6FCA00BFD501 /* PBXTargetDependency */,
+ 4CE3706B16FB6FCC00BFD501 /* PBXTargetDependency */,
+ 4CE3706D16FB6FCF00BFD501 /* PBXTargetDependency */,
+ 4CE3706F16FB6FD200BFD501 /* PBXTargetDependency */,
+ );
+ name = "All Perf Tests";
+ productName = All;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 26B902E51700FA8D00EFDCE2 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; };
+ 26B902E61700FAE800EFDCE2 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; };
+ 26DBAD6216FA63F0008243D2 /* lldb_perf_clang.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DBAD4916FA637D008243D2 /* lldb_perf_clang.cpp */; };
+ 26DBAD6316FA66DC008243D2 /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; };
+ 26DBAD6516FA66EA008243D2 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; };
+ 26DF762916FBCE7100B4CC2E /* Results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DF762716FBCE7100B4CC2E /* Results.cpp */; };
+ 26DF762A16FBCE7100B4CC2E /* Results.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DF762816FBCE7100B4CC2E /* Results.h */; };
+ 26DF764316FBF30E00B4CC2E /* Gauge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DF764216FBF30E00B4CC2E /* Gauge.cpp */; };
+ 4C1E374E16F407C800FF10BB /* Gauge.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374216F407C800FF10BB /* Gauge.h */; };
+ 4C1E374F16F407C800FF10BB /* Measurement.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374316F407C800FF10BB /* Measurement.h */; };
+ 4C1E375016F407C800FF10BB /* MemoryGauge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374416F407C800FF10BB /* MemoryGauge.cpp */; };
+ 4C1E375116F407C800FF10BB /* MemoryGauge.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374516F407C800FF10BB /* MemoryGauge.h */; };
+ 4C1E375216F407C800FF10BB /* Metric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374616F407C800FF10BB /* Metric.cpp */; };
+ 4C1E375316F407C800FF10BB /* Metric.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374716F407C800FF10BB /* Metric.h */; };
+ 4C1E375416F407C800FF10BB /* TestCase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374816F407C800FF10BB /* TestCase.cpp */; };
+ 4C1E375516F407C800FF10BB /* TestCase.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374916F407C800FF10BB /* TestCase.h */; };
+ 4C1E375616F407C800FF10BB /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374A16F407C800FF10BB /* Timer.cpp */; };
+ 4C1E375716F407C800FF10BB /* Timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374B16F407C800FF10BB /* Timer.h */; };
+ 4C1E375816F407C800FF10BB /* Xcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374C16F407C800FF10BB /* Xcode.cpp */; };
+ 4C1E375916F407C800FF10BB /* Xcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374D16F407C800FF10BB /* Xcode.h */; };
+ 4C1E377916F4089E00FF10BB /* sketch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E377816F4089E00FF10BB /* sketch.cpp */; };
+ 4C1E377A16F4091700FF10BB /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; };
+ 4C1E378216F40B8900FF10BB /* CFCBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E375B16F4081300FF10BB /* CFCBundle.cpp */; };
+ 4C1E378316F40B8C00FF10BB /* CFCData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E375D16F4081300FF10BB /* CFCData.cpp */; };
+ 4C1E378416F40B8F00FF10BB /* CFCMutableArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E375F16F4081300FF10BB /* CFCMutableArray.cpp */; };
+ 4C1E378516F40B9200FF10BB /* CFCMutableDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E376116F4081300FF10BB /* CFCMutableDictionary.cpp */; };
+ 4C1E378616F40B9600FF10BB /* CFCMutableSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E376316F4081300FF10BB /* CFCMutableSet.cpp */; };
+ 4C1E378716F40B9C00FF10BB /* CFCReleaser.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376516F4081300FF10BB /* CFCReleaser.h */; };
+ 4C1E378816F40B9F00FF10BB /* CFCString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E376616F4081300FF10BB /* CFCString.cpp */; };
+ 4C1E378916F40BA200FF10BB /* CFCString.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376716F4081300FF10BB /* CFCString.h */; };
+ 4C1E378A16F40BA600FF10BB /* CoreFoundationCPP.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376816F4081300FF10BB /* CoreFoundationCPP.h */; };
+ 4C1E378B16F40BAB00FF10BB /* CFCBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E375C16F4081300FF10BB /* CFCBundle.h */; };
+ 4C1E378C16F40BAF00FF10BB /* CFCData.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E375E16F4081300FF10BB /* CFCData.h */; };
+ 4C1E378D16F40BB300FF10BB /* CFCMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376016F4081300FF10BB /* CFCMutableArray.h */; };
+ 4C1E378E16F40BB700FF10BB /* CFCMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376216F4081300FF10BB /* CFCMutableDictionary.h */; };
+ 4C1E378F16F40BBB00FF10BB /* CFCMutableSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376416F4081300FF10BB /* CFCMutableSet.h */; };
+ 4C1E37C716F79EC300FF10BB /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; };
+ 4C1E37CB16F79EFA00FF10BB /* formatters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E37B416F79E4600FF10BB /* formatters.cpp */; };
+ 4C1E37E016F7A05D00FF10BB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; };
+ 4C1E37E216F7A07300FF10BB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; };
+ 4C86C5CB16F7C1D300844407 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; };
+ 4C86C5CC16F7C1E000844407 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; };
+ 4C86C5DA16F7CED300844407 /* fmts_tester.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E37B316F79E4600FF10BB /* fmts_tester.mm */; };
+ 4CDDF51017011EBB00D95015 /* stepping-testcase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CE3708716FB70E100BFD501 /* stepping-testcase.cpp */; };
+ 4CE3707316FB701000BFD501 /* lldb-perf-stepping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CE3707216FB701000BFD501 /* lldb-perf-stepping.cpp */; };
+ 4CE3707516FB703B00BFD501 /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; };
+ 4CE3707616FB704300BFD501 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 264B3DE816F7E47600D1E7AB /* LLDB.framework */; };
+ 4CE3707716FB704B00BFD501 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 264B3DE516F7E47600D1E7AB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 26F5C26A10F3D9A4009D5894;
+ remoteInfo = "lldb-tool";
+ };
+ 264B3DE716F7E47600D1E7AB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 26680207115FD0ED008E1FE4;
+ remoteInfo = LLDB;
+ };
+ 264B3DE916F7E47600D1E7AB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 26579F68126A25920007C5CB;
+ remoteInfo = "darwin-debug";
+ };
+ 264B3DEB16F7E47600D1E7AB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 2689FFCA13353D7A00698AC0;
+ remoteInfo = "lldb-core";
+ };
+ 264B3DED16F7E47600D1E7AB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 26DC6A101337FE6900FF7998;
+ remoteInfo = "lldb-platform";
+ };
+ 264B3DEF16F7E47600D1E7AB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = EDC6D49914E5C19B001B75F8;
+ remoteInfo = launcherXPCService;
+ };
+ 264B3DF116F7E47600D1E7AB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = EDE274EC14EDCE1F005B0F75;
+ remoteInfo = launcherRootXPCService;
+ };
+ 26B902D61700F9BD00EFDCE2 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ proxyType = 1;
+ remoteGlobalIDString = 26680206115FD0ED008E1FE4;
+ remoteInfo = LLDB;
+ };
+ 26B902E21700F9CC00EFDCE2 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4C1E373816F4035D00FF10BB;
+ remoteInfo = lldbperf;
+ };
+ 4C1E379016F40BCD00FF10BB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4C1E373816F4035D00FF10BB;
+ remoteInfo = lldbperf;
+ };
+ 4C1E37C316F79EB000FF10BB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4C1E373816F4035D00FF10BB;
+ remoteInfo = lldbperf;
+ };
+ 4C86C5DB16F7CF1200844407 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4C86C5D016F7CC8900844407;
+ remoteInfo = "format-tester";
+ };
+ 4CE3706816FB6FCA00BFD501 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4C1E376C16F4087A00FF10BB;
+ remoteInfo = "lldb-perf-sketch";
+ };
+ 4CE3706A16FB6FCC00BFD501 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4C1E37B916F79E9D00FF10BB;
+ remoteInfo = "lldb-perf-formatters";
+ };
+ 4CE3706C16FB6FCF00BFD501 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 26DBAD5816FA63B1008243D2;
+ remoteInfo = "lldb-perf-clang";
+ };
+ 4CE3706E16FB6FD200BFD501 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4CE3705316FB6FA100BFD501;
+ remoteInfo = "lldb-perf-step";
+ };
+ 4CE3708A16FB711200BFD501 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4CE3707B16FB70AD00BFD501;
+ remoteInfo = "stepping-testcase";
+ };
+ 4CE3708C16FB712300BFD501 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C1E373116F4035D00FF10BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4C1E373816F4035D00FF10BB;
+ remoteInfo = lldbperf;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 26DBAD5716FA63B1008243D2 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 26DBAD4816FA637D008243D2 /* build-clang.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-clang.sh"; sourceTree = "<group>"; };
+ 26DBAD4916FA637D008243D2 /* lldb_perf_clang.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lldb_perf_clang.cpp; sourceTree = "<group>"; };
+ 26DBAD5916FA63B1008243D2 /* lldb-perf-clang */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-clang"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 26DF762716FBCE7100B4CC2E /* Results.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Results.cpp; sourceTree = "<group>"; };
+ 26DF762816FBCE7100B4CC2E /* Results.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Results.h; sourceTree = "<group>"; };
+ 26DF764216FBF30E00B4CC2E /* Gauge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Gauge.cpp; sourceTree = "<group>"; };
+ 4C1E373916F4035D00FF10BB /* liblldbperf.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblldbperf.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4C1E374216F407C800FF10BB /* Gauge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gauge.h; sourceTree = "<group>"; };
+ 4C1E374316F407C800FF10BB /* Measurement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Measurement.h; sourceTree = "<group>"; };
+ 4C1E374416F407C800FF10BB /* MemoryGauge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryGauge.cpp; sourceTree = "<group>"; };
+ 4C1E374516F407C800FF10BB /* MemoryGauge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryGauge.h; sourceTree = "<group>"; };
+ 4C1E374616F407C800FF10BB /* Metric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Metric.cpp; sourceTree = "<group>"; };
+ 4C1E374716F407C800FF10BB /* Metric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Metric.h; sourceTree = "<group>"; };
+ 4C1E374816F407C800FF10BB /* TestCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestCase.cpp; sourceTree = "<group>"; };
+ 4C1E374916F407C800FF10BB /* TestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestCase.h; sourceTree = "<group>"; };
+ 4C1E374A16F407C800FF10BB /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = "<group>"; };
+ 4C1E374B16F407C800FF10BB /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = "<group>"; };
+ 4C1E374C16F407C800FF10BB /* Xcode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Xcode.cpp; sourceTree = "<group>"; };
+ 4C1E374D16F407C800FF10BB /* Xcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Xcode.h; sourceTree = "<group>"; };
+ 4C1E375B16F4081300FF10BB /* CFCBundle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCBundle.cpp; sourceTree = "<group>"; };
+ 4C1E375C16F4081300FF10BB /* CFCBundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCBundle.h; sourceTree = "<group>"; };
+ 4C1E375D16F4081300FF10BB /* CFCData.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCData.cpp; sourceTree = "<group>"; };
+ 4C1E375E16F4081300FF10BB /* CFCData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCData.h; sourceTree = "<group>"; };
+ 4C1E375F16F4081300FF10BB /* CFCMutableArray.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCMutableArray.cpp; sourceTree = "<group>"; };
+ 4C1E376016F4081300FF10BB /* CFCMutableArray.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCMutableArray.h; sourceTree = "<group>"; };
+ 4C1E376116F4081300FF10BB /* CFCMutableDictionary.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCMutableDictionary.cpp; sourceTree = "<group>"; };
+ 4C1E376216F4081300FF10BB /* CFCMutableDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCMutableDictionary.h; sourceTree = "<group>"; };
+ 4C1E376316F4081300FF10BB /* CFCMutableSet.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCMutableSet.cpp; sourceTree = "<group>"; };
+ 4C1E376416F4081300FF10BB /* CFCMutableSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCMutableSet.h; sourceTree = "<group>"; };
+ 4C1E376516F4081300FF10BB /* CFCReleaser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCReleaser.h; sourceTree = "<group>"; };
+ 4C1E376616F4081300FF10BB /* CFCString.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCString.cpp; sourceTree = "<group>"; };
+ 4C1E376716F4081300FF10BB /* CFCString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCString.h; sourceTree = "<group>"; };
+ 4C1E376816F4081300FF10BB /* CoreFoundationCPP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreFoundationCPP.h; sourceTree = "<group>"; };
+ 4C1E376D16F4087A00FF10BB /* lldb-perf-sketch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-sketch"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4C1E377716F4089E00FF10BB /* foobar.sketch2 */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = foobar.sketch2; sourceTree = "<group>"; };
+ 4C1E377816F4089E00FF10BB /* sketch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sketch.cpp; sourceTree = "<group>"; };
+ 4C1E37B316F79E4600FF10BB /* fmts_tester.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = fmts_tester.mm; sourceTree = "<group>"; };
+ 4C1E37B416F79E4600FF10BB /* formatters.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = formatters.cpp; sourceTree = "<group>"; };
+ 4C1E37BA16F79E9D00FF10BB /* lldb-perf-formatters */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-formatters"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
+ 4C86C5C316F7A35000844407 /* lldb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = lldb.xcodeproj; path = ../../lldb.xcodeproj; sourceTree = "<group>"; };
+ 4C86C5C616F7A37800844407 /* LLDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LLDB.framework; path = build/Debug/LLDB.framework; sourceTree = "<group>"; };
+ 4C86C5D116F7CC8900844407 /* format-tester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "format-tester"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4CE3705416FB6FA100BFD501 /* lldb-perf-step */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-step"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4CE3707216FB701000BFD501 /* lldb-perf-stepping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lldb-perf-stepping.cpp"; path = "stepping/lldb-perf-stepping.cpp"; sourceTree = "<group>"; };
+ 4CE3707C16FB70AD00BFD501 /* stepping-testcase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "stepping-testcase"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4CE3708716FB70E100BFD501 /* stepping-testcase.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "stepping-testcase.cpp"; path = "stepping/stepping-testcase.cpp"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 26DBAD5616FA63B1008243D2 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 26B902E61700FAE800EFDCE2 /* LLDB.framework in Frameworks */,
+ 26DBAD6516FA66EA008243D2 /* CoreFoundation.framework in Frameworks */,
+ 26DBAD6316FA66DC008243D2 /* liblldbperf.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C1E373616F4035D00FF10BB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C1E376A16F4087A00FF10BB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4C86C5CC16F7C1E000844407 /* LLDB.framework in Frameworks */,
+ 4C1E377A16F4091700FF10BB /* liblldbperf.a in Frameworks */,
+ 4C1E37E016F7A05D00FF10BB /* CoreFoundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C1E37B716F79E9D00FF10BB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4C86C5CB16F7C1D300844407 /* LLDB.framework in Frameworks */,
+ 4C1E37E216F7A07300FF10BB /* CoreFoundation.framework in Frameworks */,
+ 4C1E37C716F79EC300FF10BB /* liblldbperf.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C86C5CE16F7CC8900844407 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4CE3705116FB6FA100BFD501 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 26B902E51700FA8D00EFDCE2 /* LLDB.framework in Frameworks */,
+ 4CE3707716FB704B00BFD501 /* CoreFoundation.framework in Frameworks */,
+ 4CE3707616FB704300BFD501 /* LLDB.framework in Frameworks */,
+ 4CE3707516FB703B00BFD501 /* liblldbperf.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4CE3707916FB70AD00BFD501 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 26DBAD4616FA637D008243D2 /* common */ = {
+ isa = PBXGroup;
+ children = (
+ 4CE3707416FB701E00BFD501 /* stepping */,
+ 26DBAD4716FA637D008243D2 /* clang */,
+ );
+ path = common;
+ sourceTree = "<group>";
+ };
+ 26DBAD4716FA637D008243D2 /* clang */ = {
+ isa = PBXGroup;
+ children = (
+ 26DBAD4816FA637D008243D2 /* build-clang.sh */,
+ 26DBAD4916FA637D008243D2 /* lldb_perf_clang.cpp */,
+ );
+ path = clang;
+ sourceTree = "<group>";
+ };
+ 4C1E373016F4035D00FF10BB = {
+ isa = PBXGroup;
+ children = (
+ 4C86C5C316F7A35000844407 /* lldb.xcodeproj */,
+ 4C1E37B516F79E6600FF10BB /* Darwin */,
+ 26DBAD4616FA637D008243D2 /* common */,
+ 4C1E375A16F4081300FF10BB /* cfcpp */,
+ 4C1E374116F407C800FF10BB /* lib */,
+ 4C1E373A16F4035D00FF10BB /* Products */,
+ 4C1E37DD16F7A03900FF10BB /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 4C1E373A16F4035D00FF10BB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 4C1E373916F4035D00FF10BB /* liblldbperf.a */,
+ 4C1E376D16F4087A00FF10BB /* lldb-perf-sketch */,
+ 4C1E37BA16F79E9D00FF10BB /* lldb-perf-formatters */,
+ 4C86C5D116F7CC8900844407 /* format-tester */,
+ 26DBAD5916FA63B1008243D2 /* lldb-perf-clang */,
+ 4CE3705416FB6FA100BFD501 /* lldb-perf-step */,
+ 4CE3707C16FB70AD00BFD501 /* stepping-testcase */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 4C1E374116F407C800FF10BB /* lib */ = {
+ isa = PBXGroup;
+ children = (
+ 4C1E374216F407C800FF10BB /* Gauge.h */,
+ 26DF764216FBF30E00B4CC2E /* Gauge.cpp */,
+ 4C1E374316F407C800FF10BB /* Measurement.h */,
+ 4C1E374416F407C800FF10BB /* MemoryGauge.cpp */,
+ 4C1E374516F407C800FF10BB /* MemoryGauge.h */,
+ 4C1E374616F407C800FF10BB /* Metric.cpp */,
+ 4C1E374716F407C800FF10BB /* Metric.h */,
+ 26DF762716FBCE7100B4CC2E /* Results.cpp */,
+ 26DF762816FBCE7100B4CC2E /* Results.h */,
+ 4C1E374816F407C800FF10BB /* TestCase.cpp */,
+ 4C1E374916F407C800FF10BB /* TestCase.h */,
+ 4C1E374A16F407C800FF10BB /* Timer.cpp */,
+ 4C1E374B16F407C800FF10BB /* Timer.h */,
+ 4C1E374C16F407C800FF10BB /* Xcode.cpp */,
+ 4C1E374D16F407C800FF10BB /* Xcode.h */,
+ );
+ path = lib;
+ sourceTree = "<group>";
+ };
+ 4C1E375A16F4081300FF10BB /* cfcpp */ = {
+ isa = PBXGroup;
+ children = (
+ 4C1E375B16F4081300FF10BB /* CFCBundle.cpp */,
+ 4C1E375C16F4081300FF10BB /* CFCBundle.h */,
+ 4C1E375D16F4081300FF10BB /* CFCData.cpp */,
+ 4C1E375E16F4081300FF10BB /* CFCData.h */,
+ 4C1E375F16F4081300FF10BB /* CFCMutableArray.cpp */,
+ 4C1E376016F4081300FF10BB /* CFCMutableArray.h */,
+ 4C1E376116F4081300FF10BB /* CFCMutableDictionary.cpp */,
+ 4C1E376216F4081300FF10BB /* CFCMutableDictionary.h */,
+ 4C1E376316F4081300FF10BB /* CFCMutableSet.cpp */,
+ 4C1E376416F4081300FF10BB /* CFCMutableSet.h */,
+ 4C1E376516F4081300FF10BB /* CFCReleaser.h */,
+ 4C1E376616F4081300FF10BB /* CFCString.cpp */,
+ 4C1E376716F4081300FF10BB /* CFCString.h */,
+ 4C1E376816F4081300FF10BB /* CoreFoundationCPP.h */,
+ );
+ name = cfcpp;
+ path = ../../source/Host/macosx/cfcpp;
+ sourceTree = "<group>";
+ };
+ 4C1E377616F4089E00FF10BB /* sketch */ = {
+ isa = PBXGroup;
+ children = (
+ 4C1E377716F4089E00FF10BB /* foobar.sketch2 */,
+ 4C1E377816F4089E00FF10BB /* sketch.cpp */,
+ );
+ name = sketch;
+ path = darwin/sketch;
+ sourceTree = "<group>";
+ };
+ 4C1E37B216F79E4600FF10BB /* formatters */ = {
+ isa = PBXGroup;
+ children = (
+ 4C1E37B316F79E4600FF10BB /* fmts_tester.mm */,
+ 4C1E37B416F79E4600FF10BB /* formatters.cpp */,
+ );
+ name = formatters;
+ path = darwin/formatters;
+ sourceTree = "<group>";
+ };
+ 4C1E37B516F79E6600FF10BB /* Darwin */ = {
+ isa = PBXGroup;
+ children = (
+ 4C1E377616F4089E00FF10BB /* sketch */,
+ 4C1E37B216F79E4600FF10BB /* formatters */,
+ );
+ name = Darwin;
+ sourceTree = "<group>";
+ };
+ 4C1E37DD16F7A03900FF10BB /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 4C86C5C616F7A37800844407 /* LLDB.framework */,
+ 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */,
+ );
+ name = Frameworks;
+ path = ../..;
+ sourceTree = "<group>";
+ };
+ 4C86C5DE16F7D18F00844407 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 264B3DE616F7E47600D1E7AB /* lldb */,
+ 264B3DE816F7E47600D1E7AB /* LLDB.framework */,
+ 264B3DEA16F7E47600D1E7AB /* darwin-debug */,
+ 264B3DEC16F7E47600D1E7AB /* liblldb-core.a */,
+ 264B3DEE16F7E47600D1E7AB /* lldb-platform */,
+ 264B3DF016F7E47600D1E7AB /* com.apple.lldb.launcherXPCService.xpc */,
+ 264B3DF216F7E47600D1E7AB /* com.apple.lldb.launcherRootXPCService.xpc */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 4CE3707416FB701E00BFD501 /* stepping */ = {
+ isa = PBXGroup;
+ children = (
+ 4CE3708716FB70E100BFD501 /* stepping-testcase.cpp */,
+ 4CE3707216FB701000BFD501 /* lldb-perf-stepping.cpp */,
+ );
+ name = stepping;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 4C1E373716F4035D00FF10BB /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4C1E375716F407C800FF10BB /* Timer.h in Headers */,
+ 26DF762A16FBCE7100B4CC2E /* Results.h in Headers */,
+ 4C1E378D16F40BB300FF10BB /* CFCMutableArray.h in Headers */,
+ 4C1E374E16F407C800FF10BB /* Gauge.h in Headers */,
+ 4C1E378716F40B9C00FF10BB /* CFCReleaser.h in Headers */,
+ 4C1E378916F40BA200FF10BB /* CFCString.h in Headers */,
+ 4C1E375116F407C800FF10BB /* MemoryGauge.h in Headers */,
+ 4C1E378C16F40BAF00FF10BB /* CFCData.h in Headers */,
+ 4C1E375516F407C800FF10BB /* TestCase.h in Headers */,
+ 4C1E375916F407C800FF10BB /* Xcode.h in Headers */,
+ 4C1E375316F407C800FF10BB /* Metric.h in Headers */,
+ 4C1E378F16F40BBB00FF10BB /* CFCMutableSet.h in Headers */,
+ 4C1E378E16F40BB700FF10BB /* CFCMutableDictionary.h in Headers */,
+ 4C1E378B16F40BAB00FF10BB /* CFCBundle.h in Headers */,
+ 4C1E378A16F40BA600FF10BB /* CoreFoundationCPP.h in Headers */,
+ 4C1E374F16F407C800FF10BB /* Measurement.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 26DBAD5816FA63B1008243D2 /* lldb-perf-clang */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 26DBAD5F16FA63B1008243D2 /* Build configuration list for PBXNativeTarget "lldb-perf-clang" */;
+ buildPhases = (
+ 26DBAD5516FA63B1008243D2 /* Sources */,
+ 26DBAD5616FA63B1008243D2 /* Frameworks */,
+ 26DBAD5716FA63B1008243D2 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 26B902E31700F9CC00EFDCE2 /* PBXTargetDependency */,
+ );
+ name = "lldb-perf-clang";
+ productName = lldb_perf_clang;
+ productReference = 26DBAD5916FA63B1008243D2 /* lldb-perf-clang */;
+ productType = "com.apple.product-type.tool";
+ };
+ 4C1E373816F4035D00FF10BB /* lldbperf */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4C1E373D16F4035D00FF10BB /* Build configuration list for PBXNativeTarget "lldbperf" */;
+ buildPhases = (
+ 4C1E373516F4035D00FF10BB /* Sources */,
+ 4C1E373616F4035D00FF10BB /* Frameworks */,
+ 4C1E373716F4035D00FF10BB /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 26B902D71700F9BD00EFDCE2 /* PBXTargetDependency */,
+ );
+ name = lldbperf;
+ productName = lldbperf;
+ productReference = 4C1E373916F4035D00FF10BB /* liblldbperf.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 4C1E376C16F4087A00FF10BB /* lldb-perf-sketch */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4C1E377316F4087A00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-sketch" */;
+ buildPhases = (
+ 4C1E376916F4087A00FF10BB /* Sources */,
+ 4C1E376A16F4087A00FF10BB /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 4C1E379116F40BCD00FF10BB /* PBXTargetDependency */,
+ );
+ name = "lldb-perf-sketch";
+ productName = "lldb-perf-sketch";
+ productReference = 4C1E376D16F4087A00FF10BB /* lldb-perf-sketch */;
+ productType = "com.apple.product-type.tool";
+ };
+ 4C1E37B916F79E9D00FF10BB /* lldb-perf-formatters */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4C1E37C016F79E9D00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-formatters" */;
+ buildPhases = (
+ 4C1E37B616F79E9D00FF10BB /* Sources */,
+ 4C1E37B716F79E9D00FF10BB /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 4C1E37C416F79EB000FF10BB /* PBXTargetDependency */,
+ 4C86C5DC16F7CF1200844407 /* PBXTargetDependency */,
+ );
+ name = "lldb-perf-formatters";
+ productName = formatters;
+ productReference = 4C1E37BA16F79E9D00FF10BB /* lldb-perf-formatters */;
+ productType = "com.apple.product-type.tool";
+ };
+ 4C86C5D016F7CC8900844407 /* format-tester */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4C86C5D716F7CC8900844407 /* Build configuration list for PBXNativeTarget "format-tester" */;
+ buildPhases = (
+ 4C86C5CD16F7CC8900844407 /* Sources */,
+ 4C86C5CE16F7CC8900844407 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "format-tester";
+ productName = "format-tester";
+ productReference = 4C86C5D116F7CC8900844407 /* format-tester */;
+ productType = "com.apple.product-type.tool";
+ };
+ 4CE3705316FB6FA100BFD501 /* lldb-perf-step */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4CE3706716FB6FA100BFD501 /* Build configuration list for PBXNativeTarget "lldb-perf-step" */;
+ buildPhases = (
+ 4CE3705016FB6FA100BFD501 /* Sources */,
+ 4CE3705116FB6FA100BFD501 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 4CE3708D16FB712300BFD501 /* PBXTargetDependency */,
+ 4CE3708B16FB711200BFD501 /* PBXTargetDependency */,
+ );
+ name = "lldb-perf-step";
+ productName = "lldb-step-test";
+ productReference = 4CE3705416FB6FA100BFD501 /* lldb-perf-step */;
+ productType = "com.apple.product-type.tool";
+ };
+ 4CE3707B16FB70AD00BFD501 /* stepping-testcase */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4CE3708216FB70AD00BFD501 /* Build configuration list for PBXNativeTarget "stepping-testcase" */;
+ buildPhases = (
+ 4CE3707816FB70AD00BFD501 /* Sources */,
+ 4CE3707916FB70AD00BFD501 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "stepping-testcase";
+ productName = "stepping-testcase";
+ productReference = 4CE3707C16FB70AD00BFD501 /* stepping-testcase */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 4C1E373116F4035D00FF10BB /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0500;
+ ORGANIZATIONNAME = lldb.llvm.org;
+ };
+ buildConfigurationList = 4C1E373416F4035D00FF10BB /* Build configuration list for PBXProject "lldbperf" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 4C1E373016F4035D00FF10BB;
+ productRefGroup = 4C1E373A16F4035D00FF10BB /* Products */;
+ projectDirPath = "";
+ projectReferences = (
+ {
+ ProductGroup = 4C86C5DE16F7D18F00844407 /* Products */;
+ ProjectRef = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */;
+ },
+ );
+ projectRoot = "";
+ targets = (
+ 4C1E373816F4035D00FF10BB /* lldbperf */,
+ 4C1E376C16F4087A00FF10BB /* lldb-perf-sketch */,
+ 4C1E37B916F79E9D00FF10BB /* lldb-perf-formatters */,
+ 26DBAD5816FA63B1008243D2 /* lldb-perf-clang */,
+ 4C86C5D016F7CC8900844407 /* format-tester */,
+ 4CE3705316FB6FA100BFD501 /* lldb-perf-step */,
+ 4CE3707B16FB70AD00BFD501 /* stepping-testcase */,
+ 4C1E37E316F7A0A500FF10BB /* All Perf Tests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+ 264B3DE616F7E47600D1E7AB /* lldb */ = {
+ isa = PBXReferenceProxy;
+ fileType = "compiled.mach-o.executable";
+ path = lldb;
+ remoteRef = 264B3DE516F7E47600D1E7AB /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 264B3DE816F7E47600D1E7AB /* LLDB.framework */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.framework;
+ path = LLDB.framework;
+ remoteRef = 264B3DE716F7E47600D1E7AB /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 264B3DEA16F7E47600D1E7AB /* darwin-debug */ = {
+ isa = PBXReferenceProxy;
+ fileType = "compiled.mach-o.executable";
+ path = "darwin-debug";
+ remoteRef = 264B3DE916F7E47600D1E7AB /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 264B3DEC16F7E47600D1E7AB /* liblldb-core.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = "compiled.mach-o.dylib";
+ path = "liblldb-core.a";
+ remoteRef = 264B3DEB16F7E47600D1E7AB /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 264B3DEE16F7E47600D1E7AB /* lldb-platform */ = {
+ isa = PBXReferenceProxy;
+ fileType = "compiled.mach-o.executable";
+ path = "lldb-platform";
+ remoteRef = 264B3DED16F7E47600D1E7AB /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 264B3DF016F7E47600D1E7AB /* com.apple.lldb.launcherXPCService.xpc */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.cfbundle;
+ path = com.apple.lldb.launcherXPCService.xpc;
+ remoteRef = 264B3DEF16F7E47600D1E7AB /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 264B3DF216F7E47600D1E7AB /* com.apple.lldb.launcherRootXPCService.xpc */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.cfbundle;
+ path = com.apple.lldb.launcherRootXPCService.xpc;
+ remoteRef = 264B3DF116F7E47600D1E7AB /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+/* End PBXReferenceProxy section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 26DBAD5516FA63B1008243D2 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 26DBAD6216FA63F0008243D2 /* lldb_perf_clang.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C1E373516F4035D00FF10BB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4C1E378316F40B8C00FF10BB /* CFCData.cpp in Sources */,
+ 4C1E378416F40B8F00FF10BB /* CFCMutableArray.cpp in Sources */,
+ 4C1E378216F40B8900FF10BB /* CFCBundle.cpp in Sources */,
+ 4C1E375016F407C800FF10BB /* MemoryGauge.cpp in Sources */,
+ 4C1E375416F407C800FF10BB /* TestCase.cpp in Sources */,
+ 4C1E375816F407C800FF10BB /* Xcode.cpp in Sources */,
+ 26DF762916FBCE7100B4CC2E /* Results.cpp in Sources */,
+ 4C1E375216F407C800FF10BB /* Metric.cpp in Sources */,
+ 4C1E375616F407C800FF10BB /* Timer.cpp in Sources */,
+ 4C1E378616F40B9600FF10BB /* CFCMutableSet.cpp in Sources */,
+ 4C1E378516F40B9200FF10BB /* CFCMutableDictionary.cpp in Sources */,
+ 4C1E378816F40B9F00FF10BB /* CFCString.cpp in Sources */,
+ 26DF764316FBF30E00B4CC2E /* Gauge.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C1E376916F4087A00FF10BB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4C1E377916F4089E00FF10BB /* sketch.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C1E37B616F79E9D00FF10BB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4C1E37CB16F79EFA00FF10BB /* formatters.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4C86C5CD16F7CC8900844407 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4C86C5DA16F7CED300844407 /* fmts_tester.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4CE3705016FB6FA100BFD501 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4CE3707316FB701000BFD501 /* lldb-perf-stepping.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4CE3707816FB70AD00BFD501 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4CDDF51017011EBB00D95015 /* stepping-testcase.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 26B902D71700F9BD00EFDCE2 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ name = LLDB;
+ targetProxy = 26B902D61700F9BD00EFDCE2 /* PBXContainerItemProxy */;
+ };
+ 26B902E31700F9CC00EFDCE2 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4C1E373816F4035D00FF10BB /* lldbperf */;
+ targetProxy = 26B902E21700F9CC00EFDCE2 /* PBXContainerItemProxy */;
+ };
+ 4C1E379116F40BCD00FF10BB /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4C1E373816F4035D00FF10BB /* lldbperf */;
+ targetProxy = 4C1E379016F40BCD00FF10BB /* PBXContainerItemProxy */;
+ };
+ 4C1E37C416F79EB000FF10BB /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4C1E373816F4035D00FF10BB /* lldbperf */;
+ targetProxy = 4C1E37C316F79EB000FF10BB /* PBXContainerItemProxy */;
+ };
+ 4C86C5DC16F7CF1200844407 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4C86C5D016F7CC8900844407 /* format-tester */;
+ targetProxy = 4C86C5DB16F7CF1200844407 /* PBXContainerItemProxy */;
+ };
+ 4CE3706916FB6FCA00BFD501 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4C1E376C16F4087A00FF10BB /* lldb-perf-sketch */;
+ targetProxy = 4CE3706816FB6FCA00BFD501 /* PBXContainerItemProxy */;
+ };
+ 4CE3706B16FB6FCC00BFD501 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4C1E37B916F79E9D00FF10BB /* lldb-perf-formatters */;
+ targetProxy = 4CE3706A16FB6FCC00BFD501 /* PBXContainerItemProxy */;
+ };
+ 4CE3706D16FB6FCF00BFD501 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 26DBAD5816FA63B1008243D2 /* lldb-perf-clang */;
+ targetProxy = 4CE3706C16FB6FCF00BFD501 /* PBXContainerItemProxy */;
+ };
+ 4CE3706F16FB6FD200BFD501 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4CE3705316FB6FA100BFD501 /* lldb-perf-step */;
+ targetProxy = 4CE3706E16FB6FD200BFD501 /* PBXContainerItemProxy */;
+ };
+ 4CE3708B16FB711200BFD501 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4CE3707B16FB70AD00BFD501 /* stepping-testcase */;
+ targetProxy = 4CE3708A16FB711200BFD501 /* PBXContainerItemProxy */;
+ };
+ 4CE3708D16FB712300BFD501 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4C1E373816F4035D00FF10BB /* lldbperf */;
+ targetProxy = 4CE3708C16FB712300BFD501 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 26DBAD6016FA63B1008243D2 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/";
+ };
+ name = Debug;
+ };
+ 26DBAD6116FA63B1008243D2 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Release";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/";
+ };
+ name = Release;
+ };
+ 4C1E373B16F4035D00FF10BB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ ONLY_ACTIVE_ARCH = YES;
+ };
+ name = Debug;
+ };
+ 4C1E373C16F4035D00FF10BB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ };
+ name = Release;
+ };
+ 4C1E373E16F4035D00FF10BB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ EXECUTABLE_PREFIX = lib;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include";
+ };
+ name = Debug;
+ };
+ 4C1E373F16F4035D00FF10BB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ EXECUTABLE_PREFIX = lib;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include";
+ };
+ name = Release;
+ };
+ 4C1E377416F4087A00FF10BB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include $(SRCROOT)/../../source/Host/macosx/cfcpp $(SRCROOT)/../";
+ };
+ name = Debug;
+ };
+ 4C1E377516F4087A00FF10BB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include $(SRCROOT)/../../source/Host/macosx/cfcpp $(SRCROOT)/../";
+ };
+ name = Release;
+ };
+ 4C1E37C116F79E9D00FF10BB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/";
+ };
+ name = Debug;
+ };
+ 4C1E37C216F79E9D00FF10BB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/";
+ };
+ name = Release;
+ };
+ 4C1E37E516F7A0A600FF10BB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 4C1E37E616F7A0A600FF10BB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ 4C86C5D816F7CC8900844407 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 4C86C5D916F7CC8900844407 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ 4CE3705A16FB6FA100BFD501 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/";
+ };
+ name = Debug;
+ };
+ 4CE3705B16FB6FA100BFD501 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../build/Debug",
+ );
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/";
+ };
+ name = Release;
+ };
+ 4CE3708316FB70AD00BFD501 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 4CE3708416FB70AD00BFD501 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_OPTIMIZATION_LEVEL = 0;
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 26DBAD5F16FA63B1008243D2 /* Build configuration list for PBXNativeTarget "lldb-perf-clang" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 26DBAD6016FA63B1008243D2 /* Debug */,
+ 26DBAD6116FA63B1008243D2 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4C1E373416F4035D00FF10BB /* Build configuration list for PBXProject "lldbperf" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4C1E373B16F4035D00FF10BB /* Debug */,
+ 4C1E373C16F4035D00FF10BB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4C1E373D16F4035D00FF10BB /* Build configuration list for PBXNativeTarget "lldbperf" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4C1E373E16F4035D00FF10BB /* Debug */,
+ 4C1E373F16F4035D00FF10BB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4C1E377316F4087A00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-sketch" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4C1E377416F4087A00FF10BB /* Debug */,
+ 4C1E377516F4087A00FF10BB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4C1E37C016F79E9D00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-formatters" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4C1E37C116F79E9D00FF10BB /* Debug */,
+ 4C1E37C216F79E9D00FF10BB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4C1E37E416F7A0A600FF10BB /* Build configuration list for PBXAggregateTarget "All Perf Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4C1E37E516F7A0A600FF10BB /* Debug */,
+ 4C1E37E616F7A0A600FF10BB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4C86C5D716F7CC8900844407 /* Build configuration list for PBXNativeTarget "format-tester" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4C86C5D816F7CC8900844407 /* Debug */,
+ 4C86C5D916F7CC8900844407 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4CE3706716FB6FA100BFD501 /* Build configuration list for PBXNativeTarget "lldb-perf-step" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4CE3705A16FB6FA100BFD501 /* Debug */,
+ 4CE3705B16FB6FA100BFD501 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4CE3708216FB70AD00BFD501 /* Build configuration list for PBXNativeTarget "stepping-testcase" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4CE3708316FB70AD00BFD501 /* Debug */,
+ 4CE3708416FB70AD00BFD501 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 4C1E373116F4035D00FF10BB /* Project object */;
+}
diff --git a/tools/lldb-server/CMakeLists.txt b/tools/lldb-server/CMakeLists.txt
new file mode 100644
index 000000000000..c82be8a2beb3
--- /dev/null
+++ b/tools/lldb-server/CMakeLists.txt
@@ -0,0 +1,58 @@
+if ( CMAKE_SYSTEM_NAME MATCHES "Linux" )
+include_directories(
+ ../../../../llvm/include
+ ../../source/Plugins/Process/Linux
+ ../../source/Plugins/Process/POSIX
+ )
+endif ()
+
+if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" )
+include_directories(
+ ../../../../llvm/include
+ ../../source/Plugins/Process/FreeBSD
+ ../../source/Plugins/Process/POSIX
+ )
+endif ()
+
+if ( CMAKE_SYSTEM_NAME MATCHES "NetBSD" )
+include_directories(
+ ../../../../llvm/include
+ ../../source/Plugins/Process/POSIX
+ )
+endif ()
+
+include_directories(../../source)
+
+include(../../cmake/LLDBDependencies.cmake)
+
+add_lldb_executable(lldb-server
+ Acceptor.cpp
+ lldb-gdbserver.cpp
+ lldb-platform.cpp
+ lldb-server.cpp
+ LLDBServerUtilities.cpp
+)
+
+if (BUILD_SHARED_LIBS )
+ target_link_libraries(lldb-server liblldb)
+ target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS})
+else()
+ # The Darwin linker doesn't understand --start-group/--end-group.
+ if (LLDB_LINKER_SUPPORTS_GROUPS)
+ target_link_libraries(lldb-server
+ -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group)
+ target_link_libraries(lldb-server
+ -Wl,--start-group ${CLANG_USED_LIBS} -Wl,--end-group)
+ else()
+ target_link_libraries(lldb-server ${LLDB_USED_LIBS})
+ target_link_libraries(lldb-server ${CLANG_USED_LIBS})
+ endif()
+ llvm_config(lldb-server ${LLVM_LINK_COMPONENTS})
+
+ target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS})
+endif()
+
+set_target_properties(lldb-server PROPERTIES VERSION ${LLDB_VERSION})
+
+install(TARGETS lldb-server
+ RUNTIME DESTINATION bin)
diff --git a/tools/lldb-server/Makefile b/tools/lldb-server/Makefile
new file mode 100644
index 000000000000..849b313dae8a
--- /dev/null
+++ b/tools/lldb-server/Makefile
@@ -0,0 +1,25 @@
+##===- tools/lldb-server/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 := ../..
+
+TOOLNAME = lldb-server
+
+LLVMLibsOptions += -llldb -llldbUtility
+
+LINK_COMPONENTS := support
+
+include $(LLDB_LEVEL)/Makefile
+
+ifeq ($(HOST_OS),Darwin)
+ LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/
+endif
+
+ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux FreeBSD GNU/kFreeBSD NetBSD))
+ LLVMLibsOptions += -Wl,-rpath,$(LibDir) -lpthread
+endif