diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 | 
| commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
| tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /tools | |
| parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) | |
Notes
Diffstat (limited to 'tools')
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(®_event, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT|NOTE_EXITSTATUS|NOTE_EXIT_DETAIL, 0, NULL); +    // Register the event: +    int result = kevent (kq_id, ®_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, ¤t_used_time); +        TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv); +        timeradd(¤t_used_time, &tv, ¤t_used_time); +        task_used_usec = current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec; +         +        struct timeval current_elapsed_time; +        int res = gettimeofday(¤t_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.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 ®) +{ +    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 ®); + +    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 ®_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 ®_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, ®_value)) +                reg_value_ptr = ®_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, ®_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, ®_value)) +                        continue; + +                    debugserver_regnum_with_fixed_width_hex_register_value (ostrm, pid, tid, &g_reg_entries[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, ®_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 (®_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, ®_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, ®ion_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 ®) +{ +    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 ®: 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, ®_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, ®ion_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<OSTOP) +#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 Binary files differnew file mode 100644 index 000000000000..553c698b180c --- /dev/null +++ b/tools/lldb-perf/darwin/sketch/foobar.sketch2 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  | 
