diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/tools | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/tools')
214 files changed, 61562 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/tools/CMakeLists.txt b/gnu/llvm/lldb/tools/CMakeLists.txt new file mode 100644 index 00000000000..1585fd4dc4b --- /dev/null +++ b/gnu/llvm/lldb/tools/CMakeLists.txt @@ -0,0 +1,22 @@ +add_subdirectory(argdumper) +add_subdirectory(driver) +add_subdirectory(intel-features) + +# We want lldb-test to be built only when it's needed, +# i.e. if a target requires it as dependency. The typical +# example is `check-lldb`. So, we pass EXCLUDE_FROM_ALL here. +add_subdirectory(lldb-test EXCLUDE_FROM_ALL) + +add_lldb_tool_subdirectory(lldb-instr) +add_lldb_tool_subdirectory(lldb-vscode) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_lldb_tool_subdirectory(darwin-debug) + if(NOT LLDB_USE_SYSTEM_DEBUGSERVER) + add_lldb_tool_subdirectory(debugserver) + endif() +endif() + +if (LLDB_CAN_USE_LLDB_SERVER) + add_lldb_tool_subdirectory(lldb-server) +endif() diff --git a/gnu/llvm/lldb/tools/argdumper/CMakeLists.txt b/gnu/llvm/lldb/tools/argdumper/CMakeLists.txt new file mode 100644 index 00000000000..92494632519 --- /dev/null +++ b/gnu/llvm/lldb/tools/argdumper/CMakeLists.txt @@ -0,0 +1,6 @@ +add_lldb_tool(lldb-argdumper ADD_TO_FRAMEWORK + argdumper.cpp + + LINK_COMPONENTS + Support +) diff --git a/gnu/llvm/lldb/tools/argdumper/argdumper.cpp b/gnu/llvm/lldb/tools/argdumper/argdumper.cpp new file mode 100644 index 00000000000..1cf0d6dae7a --- /dev/null +++ b/gnu/llvm/lldb/tools/argdumper/argdumper.cpp @@ -0,0 +1,20 @@ +//===-- argdumper.cpp --------------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/JSON.h" + +using namespace llvm; + +int main(int argc, char *argv[]) { + json::Array Arguments; + for (int i = 1; i < argc; i++) { + Arguments.push_back(argv[i]); + } + llvm::outs() << json::Object({{"arguments", std::move(Arguments)}}); + return 0; +} diff --git a/gnu/llvm/lldb/tools/argdumper/argdumper.exports b/gnu/llvm/lldb/tools/argdumper/argdumper.exports new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/gnu/llvm/lldb/tools/argdumper/argdumper.exports diff --git a/gnu/llvm/lldb/tools/compact-unwind/compact-unwind-dumper.c b/gnu/llvm/lldb/tools/compact-unwind/compact-unwind-dumper.c new file mode 100644 index 00000000000..d4706eaf538 --- /dev/null +++ b/gnu/llvm/lldb/tools/compact-unwind/compact-unwind-dumper.c @@ -0,0 +1,1515 @@ +#include <fcntl.h> +#include <inttypes.h> +#include <mach-o/compact_unwind_encoding.h> +#include <mach-o/loader.h> +#include <mach-o/nlist.h> +#include <mach/machine.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/errno.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +enum { + UNWIND_ARM64_MODE_MASK = 0x0F000000, + UNWIND_ARM64_MODE_FRAMELESS = 0x02000000, + UNWIND_ARM64_MODE_DWARF = 0x03000000, + UNWIND_ARM64_MODE_FRAME = 0x04000000, + + UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001, + UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002, + UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004, + UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008, + UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010, + UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100, + UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200, + UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400, + UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800, + + UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000, + UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_ARM_MODE_MASK = 0x0F000000, + UNWIND_ARM_MODE_FRAME = 0x01000000, + UNWIND_ARM_MODE_FRAME_D = 0x02000000, + UNWIND_ARM_MODE_DWARF = 0x04000000, + + UNWIND_ARM_FRAME_STACK_ADJUST_MASK = 0x00C00000, + + UNWIND_ARM_FRAME_FIRST_PUSH_R4 = 0x00000001, + UNWIND_ARM_FRAME_FIRST_PUSH_R5 = 0x00000002, + UNWIND_ARM_FRAME_FIRST_PUSH_R6 = 0x00000004, + + UNWIND_ARM_FRAME_SECOND_PUSH_R8 = 0x00000008, + UNWIND_ARM_FRAME_SECOND_PUSH_R9 = 0x00000010, + UNWIND_ARM_FRAME_SECOND_PUSH_R10 = 0x00000020, + UNWIND_ARM_FRAME_SECOND_PUSH_R11 = 0x00000040, + UNWIND_ARM_FRAME_SECOND_PUSH_R12 = 0x00000080, + + UNWIND_ARM_FRAME_D_REG_COUNT_MASK = 0x00000700, + + UNWIND_ARM_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +#define EXTRACT_BITS(value, mask) \ + ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) + +// A quick sketch of a program which can parse the compact unwind info +// used on Darwin systems for exception handling. The output of +// unwinddump will be more authoritative/reliable but this program +// can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries +// correctly. + +struct symbol { + uint64_t file_address; + const char *name; +}; + +int symbol_compare(const void *a, const void *b) { + return (int)((struct symbol *)a)->file_address - + ((struct symbol *)b)->file_address; +} + +struct baton { + cpu_type_t cputype; + + uint8_t *mach_header_start; // pointer into this program's address space + uint8_t *compact_unwind_start; // pointer into this program's address space + + int addr_size; // 4 or 8 bytes, the size of addresses in this file + + uint64_t text_segment_vmaddr; // __TEXT segment vmaddr + uint64_t text_segment_file_offset; + + uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr + uint64_t text_section_file_offset; + + uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame + // section + + uint8_t + *lsda_array_start; // for the currently-being-processed first-level index + uint8_t + *lsda_array_end; // the lsda_array_start for the NEXT first-level index + + struct symbol *symbols; + int symbols_count; + + uint64_t *function_start_addresses; + int function_start_addresses_count; + + int current_index_table_number; + + struct unwind_info_section_header unwind_header; + struct unwind_info_section_header_index_entry first_level_index_entry; + struct unwind_info_compressed_second_level_page_header + compressed_second_level_page_header; + struct unwind_info_regular_second_level_page_header + regular_second_level_page_header; +}; + +uint64_t read_leb128(uint8_t **offset) { + uint64_t result = 0; + int shift = 0; + while (1) { + uint8_t byte = **offset; + *offset = *offset + 1; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + + return result; +} + +// step through the load commands in a thin mach-o binary, +// find the cputype and the start of the __TEXT,__unwind_info +// section, return a pointer to that section or NULL if not found. + +static void scan_macho_load_commands(struct baton *baton) { + struct symtab_command symtab_cmd; + uint64_t linkedit_segment_vmaddr; + uint64_t linkedit_segment_file_offset; + + baton->compact_unwind_start = 0; + + uint32_t *magic = (uint32_t *)baton->mach_header_start; + + if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) { + printf("Unexpected magic number 0x%x in header, exiting.", *magic); + exit(1); + } + + bool is_64bit = false; + if (*magic == MH_MAGIC_64) + is_64bit = true; + + uint8_t *offset = baton->mach_header_start; + + struct mach_header mh; + memcpy(&mh, offset, sizeof(struct mach_header)); + if (is_64bit) + offset += sizeof(struct mach_header_64); + else + offset += sizeof(struct mach_header); + + if (is_64bit) + baton->addr_size = 8; + else + baton->addr_size = 4; + + baton->cputype = mh.cputype; + + uint8_t *start_of_load_commands = offset; + + uint32_t cur_cmd = 0; + while (cur_cmd < mh.ncmds && + (offset - start_of_load_commands) < mh.sizeofcmds) { + struct load_command lc; + uint32_t *lc_cmd = (uint32_t *)offset; + uint32_t *lc_cmdsize = (uint32_t *)offset + 1; + uint8_t *start_of_this_load_cmd = offset; + + if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) { + char segment_name[17]; + segment_name[0] = '\0'; + uint32_t nsects = 0; + uint64_t segment_offset = 0; + uint64_t segment_vmaddr = 0; + + if (*lc_cmd == LC_SEGMENT_64) { + struct segment_command_64 seg; + memcpy(&seg, offset, sizeof(struct segment_command_64)); + memcpy(&segment_name, &seg.segname, 16); + segment_name[16] = '\0'; + nsects = seg.nsects; + segment_offset = seg.fileoff; + segment_vmaddr = seg.vmaddr; + offset += sizeof(struct segment_command_64); + if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) { + printf("Segment '%s' is encrypted.\n", segment_name); + } + } + + if (*lc_cmd == LC_SEGMENT) { + struct segment_command seg; + memcpy(&seg, offset, sizeof(struct segment_command)); + memcpy(&segment_name, &seg.segname, 16); + segment_name[16] = '\0'; + nsects = seg.nsects; + segment_offset = seg.fileoff; + segment_vmaddr = seg.vmaddr; + offset += sizeof(struct segment_command); + if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) { + printf("Segment '%s' is encrypted.\n", segment_name); + } + } + + if (nsects != 0 && strcmp(segment_name, "__TEXT") == 0) { + baton->text_segment_vmaddr = segment_vmaddr; + baton->text_segment_file_offset = segment_offset; + + uint32_t current_sect = 0; + while (current_sect < nsects && + (offset - start_of_this_load_cmd) < *lc_cmdsize) { + char sect_name[17]; + memcpy(§_name, offset, 16); + sect_name[16] = '\0'; + if (strcmp(sect_name, "__unwind_info") == 0) { + if (is_64bit) { + struct section_64 sect; + memset(§, 0, sizeof(struct section_64)); + memcpy(§, offset, sizeof(struct section_64)); + baton->compact_unwind_start = + baton->mach_header_start + sect.offset; + } else { + struct section sect; + memset(§, 0, sizeof(struct section)); + memcpy(§, offset, sizeof(struct section)); + baton->compact_unwind_start = + baton->mach_header_start + sect.offset; + } + } + if (strcmp(sect_name, "__eh_frame") == 0) { + if (is_64bit) { + struct section_64 sect; + memset(§, 0, sizeof(struct section_64)); + memcpy(§, offset, sizeof(struct section_64)); + baton->eh_section_file_address = sect.addr; + } else { + struct section sect; + memset(§, 0, sizeof(struct section)); + memcpy(§, offset, sizeof(struct section)); + baton->eh_section_file_address = sect.addr; + } + } + if (strcmp(sect_name, "__text") == 0) { + if (is_64bit) { + struct section_64 sect; + memset(§, 0, sizeof(struct section_64)); + memcpy(§, offset, sizeof(struct section_64)); + baton->text_section_vmaddr = sect.addr; + baton->text_section_file_offset = sect.offset; + } else { + struct section sect; + memset(§, 0, sizeof(struct section)); + memcpy(§, offset, sizeof(struct section)); + baton->text_section_vmaddr = sect.addr; + } + } + if (is_64bit) { + offset += sizeof(struct section_64); + } else { + offset += sizeof(struct section); + } + } + } + + if (strcmp(segment_name, "__LINKEDIT") == 0) { + linkedit_segment_vmaddr = segment_vmaddr; + linkedit_segment_file_offset = segment_offset; + } + } + + if (*lc_cmd == LC_SYMTAB) { + memcpy(&symtab_cmd, offset, sizeof(struct symtab_command)); + } + + if (*lc_cmd == LC_DYSYMTAB) { + struct dysymtab_command dysymtab_cmd; + memcpy(&dysymtab_cmd, offset, sizeof(struct dysymtab_command)); + + int nlist_size = 12; + if (is_64bit) + nlist_size = 16; + + char *string_table = + (char *)(baton->mach_header_start + symtab_cmd.stroff); + uint8_t *local_syms = baton->mach_header_start + symtab_cmd.symoff + + (dysymtab_cmd.ilocalsym * nlist_size); + int local_syms_count = dysymtab_cmd.nlocalsym; + uint8_t *exported_syms = baton->mach_header_start + symtab_cmd.symoff + + (dysymtab_cmd.iextdefsym * nlist_size); + int exported_syms_count = dysymtab_cmd.nextdefsym; + + // We're only going to create records for a small number of these symbols + // but to + // simplify the memory management I'll allocate enough space to store all + // of them. + baton->symbols = (struct symbol *)malloc( + sizeof(struct symbol) * (local_syms_count + exported_syms_count)); + baton->symbols_count = 0; + + for (int i = 0; i < local_syms_count; i++) { + struct nlist_64 nlist; + memset(&nlist, 0, sizeof(struct nlist_64)); + if (is_64bit) { + memcpy(&nlist, local_syms + (i * nlist_size), + sizeof(struct nlist_64)); + } else { + struct nlist nlist_32; + memset(&nlist_32, 0, sizeof(struct nlist)); + memcpy(&nlist_32, local_syms + (i * nlist_size), + sizeof(struct nlist)); + nlist.n_un.n_strx = nlist_32.n_un.n_strx; + nlist.n_type = nlist_32.n_type; + nlist.n_sect = nlist_32.n_sect; + nlist.n_desc = nlist_32.n_desc; + nlist.n_value = nlist_32.n_value; + } + if ((nlist.n_type & N_STAB) == 0 && + ((nlist.n_type & N_EXT) == 1 || + ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) && + nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) { + baton->symbols[baton->symbols_count].file_address = nlist.n_value; + if (baton->cputype == CPU_TYPE_ARM) + baton->symbols[baton->symbols_count].file_address = + baton->symbols[baton->symbols_count].file_address & ~1; + baton->symbols[baton->symbols_count].name = + string_table + nlist.n_un.n_strx; + baton->symbols_count++; + } + } + + for (int i = 0; i < exported_syms_count; i++) { + struct nlist_64 nlist; + memset(&nlist, 0, sizeof(struct nlist_64)); + if (is_64bit) { + memcpy(&nlist, exported_syms + (i * nlist_size), + sizeof(struct nlist_64)); + } else { + struct nlist nlist_32; + memcpy(&nlist_32, exported_syms + (i * nlist_size), + sizeof(struct nlist)); + nlist.n_un.n_strx = nlist_32.n_un.n_strx; + nlist.n_type = nlist_32.n_type; + nlist.n_sect = nlist_32.n_sect; + nlist.n_desc = nlist_32.n_desc; + nlist.n_value = nlist_32.n_value; + } + if ((nlist.n_type & N_STAB) == 0 && + ((nlist.n_type & N_EXT) == 1 || + ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) && + nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) { + baton->symbols[baton->symbols_count].file_address = nlist.n_value; + if (baton->cputype == CPU_TYPE_ARM) + baton->symbols[baton->symbols_count].file_address = + baton->symbols[baton->symbols_count].file_address & ~1; + baton->symbols[baton->symbols_count].name = + string_table + nlist.n_un.n_strx; + baton->symbols_count++; + } + } + + qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol), + symbol_compare); + } + + if (*lc_cmd == LC_FUNCTION_STARTS) { + struct linkedit_data_command function_starts_cmd; + memcpy(&function_starts_cmd, offset, + sizeof(struct linkedit_data_command)); + + uint8_t *funcstarts_offset = + baton->mach_header_start + function_starts_cmd.dataoff; + uint8_t *function_end = funcstarts_offset + function_starts_cmd.datasize; + int count = 0; + + while (funcstarts_offset < function_end) { + if (read_leb128(&funcstarts_offset) != 0) { + count++; + } + } + + baton->function_start_addresses = + (uint64_t *)malloc(sizeof(uint64_t) * count); + baton->function_start_addresses_count = count; + + funcstarts_offset = + baton->mach_header_start + function_starts_cmd.dataoff; + uint64_t current_pc = baton->text_segment_vmaddr; + int i = 0; + while (funcstarts_offset < function_end) { + uint64_t func_start = read_leb128(&funcstarts_offset); + if (func_start != 0) { + current_pc += func_start; + baton->function_start_addresses[i++] = current_pc; + } + } + } + + offset = start_of_this_load_cmd + *lc_cmdsize; + cur_cmd++; + } + + // Augment the symbol table with the function starts table -- adding symbol + // entries + // for functions that were stripped. + + int unnamed_functions_to_add = 0; + for (int i = 0; i < baton->function_start_addresses_count; i++) { + struct symbol search_key; + search_key.file_address = baton->function_start_addresses[i]; + if (baton->cputype == CPU_TYPE_ARM) + search_key.file_address = search_key.file_address & ~1; + struct symbol *sym = + bsearch(&search_key, baton->symbols, baton->symbols_count, + sizeof(struct symbol), symbol_compare); + if (sym == NULL) + unnamed_functions_to_add++; + } + + baton->symbols = (struct symbol *)realloc( + baton->symbols, sizeof(struct symbol) * + (baton->symbols_count + unnamed_functions_to_add)); + + int current_unnamed_symbol = 1; + int number_symbols_added = 0; + for (int i = 0; i < baton->function_start_addresses_count; i++) { + struct symbol search_key; + search_key.file_address = baton->function_start_addresses[i]; + if (baton->cputype == CPU_TYPE_ARM) + search_key.file_address = search_key.file_address & ~1; + struct symbol *sym = + bsearch(&search_key, baton->symbols, baton->symbols_count, + sizeof(struct symbol), symbol_compare); + if (sym == NULL) { + char *name; + asprintf(&name, "unnamed function #%d", current_unnamed_symbol++); + baton->symbols[baton->symbols_count + number_symbols_added].file_address = + baton->function_start_addresses[i]; + baton->symbols[baton->symbols_count + number_symbols_added].name = name; + number_symbols_added++; + } + } + baton->symbols_count += number_symbols_added; + qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol), + symbol_compare); + + // printf ("function start addresses\n"); + // for (int i = 0; i < baton->function_start_addresses_count; i++) + // { + // printf ("0x%012llx\n", baton->function_start_addresses[i]); + // } + + // printf ("symbol table names & addresses\n"); + // for (int i = 0; i < baton->symbols_count; i++) + // { + // printf ("0x%012llx %s\n", baton->symbols[i].file_address, + // baton->symbols[i].name); + // } +} + +void print_encoding_x86_64(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + int mode = encoding & UNWIND_X86_64_MODE_MASK; + switch (mode) { + case UNWIND_X86_64_MODE_RBP_FRAME: { + printf("frame func: CFA is rbp+%d ", 16); + printf(" rip=[CFA-8] rbp=[CFA-16]"); + uint32_t saved_registers_offset = + EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + + uint32_t saved_registers_locations = + EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + saved_registers_offset += 2; + + for (int i = 0; i < 5; i++) { + switch (saved_registers_locations & 0x7) { + case UNWIND_X86_64_REG_NONE: + break; + case UNWIND_X86_64_REG_RBX: + printf(" rbx=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R12: + printf(" r12=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R13: + printf(" r13=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R14: + printf(" r14=[CFA-%d]", saved_registers_offset * 8); + break; + case UNWIND_X86_64_REG_R15: + printf(" r15=[CFA-%d]", saved_registers_offset * 8); + break; + } + saved_registers_offset--; + saved_registers_locations >>= 3; + } + } break; + + case UNWIND_X86_64_MODE_STACK_IND: + case UNWIND_X86_64_MODE_STACK_IMMD: { + uint32_t stack_size = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t register_count = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + + if (mode == UNWIND_X86_64_MODE_STACK_IND && function_start) { + uint32_t stack_adjust = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + + // offset into the function instructions; 0 == beginning of first + // instruction + uint32_t offset_to_subl_insn = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + + stack_size = *((uint32_t *)(function_start + offset_to_subl_insn)); + + stack_size += stack_adjust * 8; + + printf("large stack "); + } + + if (mode == UNWIND_X86_64_MODE_STACK_IND) { + printf("frameless function: stack size %d, register count %d ", + stack_size * 8, register_count); + } else { + printf("frameless function: stack size %d, register count %d ", + stack_size, register_count); + } + + if (register_count == 0) { + printf(" no registers saved"); + } else { + + // We need to include (up to) 6 registers in 10 bits. + // That would be 18 bits if we just used 3 bits per reg to indicate + // the order they're saved on the stack. + // + // This is done with Lehmer code permutation, e.g. see + // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms + int permunreg[6]; + + // This decodes the variable-base number in the 10 bits + // and gives us the Lehmer code sequence which can then + // be decoded. + + switch (register_count) { + case 6: + permunreg[0] = permutation / 120; // 120 == 5! + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; // 24 == 4! + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; // 6 == 3! + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; // 2 == 2! + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; // 1 == 1! + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + + // Decode the Lehmer code for this permutation of + // the registers v. http://en.wikipedia.org/wiki/Lehmer_code + + int registers[6]; + bool used[7] = {false, false, false, false, false, false, false}; + for (int i = 0; i < register_count; i++) { + int renum = 0; + for (int j = 1; j < 7; j++) { + if (used[j] == false) { + if (renum == permunreg[i]) { + registers[i] = j; + used[j] = true; + break; + } + renum++; + } + } + } + + if (mode == UNWIND_X86_64_MODE_STACK_IND) { + printf(" CFA is rsp+%d ", stack_size); + } else { + printf(" CFA is rsp+%d ", stack_size * 8); + } + + uint32_t saved_registers_offset = 1; + printf(" rip=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + + for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { + switch (registers[i]) { + case UNWIND_X86_64_REG_NONE: + break; + case UNWIND_X86_64_REG_RBX: + printf(" rbx=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R12: + printf(" r12=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R13: + printf(" r13=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R14: + printf(" r14=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_R15: + printf(" r15=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + case UNWIND_X86_64_REG_RBP: + printf(" rbp=[CFA-%d]", saved_registers_offset * 8); + saved_registers_offset++; + break; + } + } + } + + } break; + + case UNWIND_X86_64_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; + + case 0: { + printf(" no unwind information"); + } break; + } +} + +void print_encoding_i386(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + int mode = encoding & UNWIND_X86_MODE_MASK; + switch (mode) { + case UNWIND_X86_MODE_EBP_FRAME: { + printf("frame func: CFA is ebp+%d ", 8); + printf(" eip=[CFA-4] ebp=[CFA-8]"); + uint32_t saved_registers_offset = + EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET); + + uint32_t saved_registers_locations = + EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + saved_registers_offset += 2; + + for (int i = 0; i < 5; i++) { + switch (saved_registers_locations & 0x7) { + case UNWIND_X86_REG_NONE: + break; + case UNWIND_X86_REG_EBX: + printf(" ebx=[CFA-%d]", saved_registers_offset * 4); + break; + case UNWIND_X86_REG_ECX: + printf(" ecx=[CFA-%d]", saved_registers_offset * 4); + break; + case UNWIND_X86_REG_EDX: + printf(" edx=[CFA-%d]", saved_registers_offset * 4); + break; + case UNWIND_X86_REG_EDI: + printf(" edi=[CFA-%d]", saved_registers_offset * 4); + break; + case UNWIND_X86_REG_ESI: + printf(" esi=[CFA-%d]", saved_registers_offset * 4); + break; + } + saved_registers_offset--; + saved_registers_locations >>= 3; + } + } break; + + case UNWIND_X86_MODE_STACK_IND: + case UNWIND_X86_MODE_STACK_IMMD: { + uint32_t stack_size = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t register_count = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + + if (mode == UNWIND_X86_MODE_STACK_IND && function_start) { + uint32_t stack_adjust = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + + // offset into the function instructions; 0 == beginning of first + // instruction + uint32_t offset_to_subl_insn = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + + stack_size = *((uint32_t *)(function_start + offset_to_subl_insn)); + + stack_size += stack_adjust * 4; + + printf("large stack "); + } + + if (mode == UNWIND_X86_MODE_STACK_IND) { + printf("frameless function: stack size %d, register count %d ", + stack_size, register_count); + } else { + printf("frameless function: stack size %d, register count %d ", + stack_size * 4, register_count); + } + + if (register_count == 0) { + printf(" no registers saved"); + } else { + + // We need to include (up to) 6 registers in 10 bits. + // That would be 18 bits if we just used 3 bits per reg to indicate + // the order they're saved on the stack. + // + // This is done with Lehmer code permutation, e.g. see + // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms + int permunreg[6]; + + // This decodes the variable-base number in the 10 bits + // and gives us the Lehmer code sequence which can then + // be decoded. + + switch (register_count) { + case 6: + permunreg[0] = permutation / 120; // 120 == 5! + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; // 24 == 4! + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; // 6 == 3! + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; // 2 == 2! + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; // 1 == 1! + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + + // Decode the Lehmer code for this permutation of + // the registers v. http://en.wikipedia.org/wiki/Lehmer_code + + int registers[6]; + bool used[7] = {false, false, false, false, false, false, false}; + for (int i = 0; i < register_count; i++) { + int renum = 0; + for (int j = 1; j < 7; j++) { + if (used[j] == false) { + if (renum == permunreg[i]) { + registers[i] = j; + used[j] = true; + break; + } + renum++; + } + } + } + + if (mode == UNWIND_X86_MODE_STACK_IND) { + printf(" CFA is esp+%d ", stack_size); + } else { + printf(" CFA is esp+%d ", stack_size * 4); + } + + uint32_t saved_registers_offset = 1; + printf(" eip=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + + for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { + switch (registers[i]) { + case UNWIND_X86_REG_NONE: + break; + case UNWIND_X86_REG_EBX: + printf(" ebx=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_ECX: + printf(" ecx=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_EDX: + printf(" edx=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_EDI: + printf(" edi=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_ESI: + printf(" esi=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + case UNWIND_X86_REG_EBP: + printf(" ebp=[CFA-%d]", saved_registers_offset * 4); + saved_registers_offset++; + break; + } + } + } + + } break; + + case UNWIND_X86_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; + + case 0: { + printf(" no unwind information"); + } break; + } +} + +void print_encoding_arm64(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + const int wordsize = 8; + int mode = encoding & UNWIND_ARM64_MODE_MASK; + switch (mode) { + case UNWIND_ARM64_MODE_FRAME: { + printf("frame func: CFA is fp+%d ", 16); + printf(" pc=[CFA-8] fp=[CFA-16]"); + int reg_pairs_saved_count = 1; + uint32_t saved_register_bits = encoding & 0xfff; + if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x19=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x20=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x21=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x22=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x23=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x24=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x25=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x26=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" x27=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" x28=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d8=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d9=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d11=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d12=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d13=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + int cfa_offset = reg_pairs_saved_count * -2 * wordsize; + cfa_offset -= wordsize; + printf(" d14=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" d15=[CFA%d]", cfa_offset); + reg_pairs_saved_count++; + } + + } break; + + case UNWIND_ARM64_MODE_FRAMELESS: { + uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK; + printf("frameless function: stack size %d ", stack_size * 16); + + } break; + + case UNWIND_ARM64_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; + + case 0: { + printf(" no unwind information"); + } break; + } +} + +void print_encoding_armv7(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + const int wordsize = 4; + int mode = encoding & UNWIND_ARM_MODE_MASK; + switch (mode) { + case UNWIND_ARM_MODE_FRAME_D: + case UNWIND_ARM_MODE_FRAME: { + int stack_adjust = + EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize; + + printf("frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust); + int cfa_offset = -stack_adjust; + + cfa_offset -= wordsize; + printf(" pc=[CFA%d]", cfa_offset); + cfa_offset -= wordsize; + printf(" fp=[CFA%d]", cfa_offset); + + uint32_t saved_register_bits = encoding & 0xff; + if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) { + cfa_offset -= wordsize; + printf(" r6=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) { + cfa_offset -= wordsize; + printf(" r5=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) { + cfa_offset -= wordsize; + printf(" r4=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) { + cfa_offset -= wordsize; + printf(" r12=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) { + cfa_offset -= wordsize; + printf(" r11=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) { + cfa_offset -= wordsize; + printf(" r10=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) { + cfa_offset -= wordsize; + printf(" r9=[CFA%d]", cfa_offset); + } + if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) { + cfa_offset -= wordsize; + printf(" r8=[CFA%d]", cfa_offset); + } + + if (mode == UNWIND_ARM_MODE_FRAME_D) { + uint32_t d_reg_bits = + EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK); + switch (d_reg_bits) { + case 0: + // vpush {d8} + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 1: + // vpush {d10} + // vpush {d8} + cfa_offset -= 8; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 2: + // vpush {d12} + // vpush {d10} + // vpush {d8} + cfa_offset -= 8; + printf(" d12=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 3: + // vpush {d14} + // vpush {d12} + // vpush {d10} + // vpush {d8} + cfa_offset -= 8; + printf(" d14=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d12=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d10=[CFA%d]", cfa_offset); + cfa_offset -= 8; + printf(" d8=[CFA%d]", cfa_offset); + break; + case 4: + // vpush {d14} + // vpush {d12} + // sp = (sp - 24) & (-16); + // vst {d8, d9, d10} + printf(" d14, d12, d10, d9, d8"); + break; + case 5: + // vpush {d14} + // sp = (sp - 40) & (-16); + // vst {d8, d9, d10, d11} + // vst {d12} + printf(" d14, d11, d10, d9, d8, d12"); + break; + case 6: + // sp = (sp - 56) & (-16); + // vst {d8, d9, d10, d11} + // vst {d12, d13, d14} + printf(" d11, d10, d9, d8, d14, d13, d12"); + break; + case 7: + // sp = (sp - 64) & (-16); + // vst {d8, d9, d10, d11} + // vst {d12, d13, d14, d15} + printf(" d11, d10, d9, d8, d15, d14, d13, d12"); + break; + } + } + } break; + + case UNWIND_ARM_MODE_DWARF: { + uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET; + printf( + "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 + ")", + dwarf_offset, dwarf_offset + baton.eh_section_file_address); + } break; + + case 0: { + printf(" no unwind information"); + } break; + } +} + +void print_encoding(struct baton baton, uint8_t *function_start, + uint32_t encoding) { + + if (baton.cputype == CPU_TYPE_X86_64) { + print_encoding_x86_64(baton, function_start, encoding); + } else if (baton.cputype == CPU_TYPE_I386) { + print_encoding_i386(baton, function_start, encoding); + } else if (baton.cputype == CPU_TYPE_ARM64 || baton.cputype == CPU_TYPE_ARM64_32) { + print_encoding_arm64(baton, function_start, encoding); + } else if (baton.cputype == CPU_TYPE_ARM) { + print_encoding_armv7(baton, function_start, encoding); + } else { + printf(" -- unsupported encoding arch -- "); + } +} + +void print_function_encoding(struct baton baton, uint32_t idx, + uint32_t encoding, uint32_t entry_encoding_index, + uint32_t entry_func_offset) { + + char *entry_encoding_index_str = ""; + if (entry_encoding_index != (uint32_t)-1) { + asprintf(&entry_encoding_index_str, ", encoding #%d", entry_encoding_index); + } else { + asprintf(&entry_encoding_index_str, ""); + } + + uint64_t file_address = baton.first_level_index_entry.functionOffset + + entry_func_offset + baton.text_segment_vmaddr; + + if (baton.cputype == CPU_TYPE_ARM) + file_address = file_address & ~1; + + printf( + " func [%d] offset %d (file addr 0x%" PRIx64 ")%s, encoding is 0x%x", + idx, entry_func_offset, file_address, entry_encoding_index_str, encoding); + + struct symbol *symbol = NULL; + for (int i = 0; i < baton.symbols_count; i++) { + if (i == baton.symbols_count - 1 && + baton.symbols[i].file_address <= file_address) { + symbol = &(baton.symbols[i]); + break; + } else { + if (baton.symbols[i].file_address <= file_address && + baton.symbols[i + 1].file_address > file_address) { + symbol = &(baton.symbols[i]); + break; + } + } + } + + printf("\n "); + if (symbol) { + int offset = file_address - symbol->file_address; + + // FIXME this is a poor heuristic - if we're greater than 16 bytes past the + // start of the function, this is the unwind info for a stripped function. + // In reality the compact unwind entry may not line up exactly with the + // function bounds. + if (offset >= 0) { + printf("name: %s", symbol->name); + if (offset > 0) { + printf(" + %d", offset); + } + } + printf("\n "); + } + + print_encoding(baton, baton.mach_header_start + + baton.first_level_index_entry.functionOffset + + baton.text_section_file_offset + entry_func_offset, + encoding); + + bool has_lsda = encoding & UNWIND_HAS_LSDA; + + if (has_lsda) { + uint32_t func_offset = + entry_func_offset + baton.first_level_index_entry.functionOffset; + + int lsda_entry_number = -1; + + uint32_t low = 0; + uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) / + sizeof(struct unwind_info_section_header_lsda_index_entry); + + while (low < high) { + uint32_t mid = (low + high) / 2; + + uint8_t *mid_lsda_entry_addr = + (baton.lsda_array_start + + (mid * sizeof(struct unwind_info_section_header_lsda_index_entry))); + struct unwind_info_section_header_lsda_index_entry mid_lsda_entry; + memcpy(&mid_lsda_entry, mid_lsda_entry_addr, + sizeof(struct unwind_info_section_header_lsda_index_entry)); + if (mid_lsda_entry.functionOffset == func_offset) { + lsda_entry_number = + (mid_lsda_entry_addr - baton.lsda_array_start) / + sizeof(struct unwind_info_section_header_lsda_index_entry); + break; + } else if (mid_lsda_entry.functionOffset < func_offset) { + low = mid + 1; + } else { + high = mid; + } + } + + if (lsda_entry_number != -1) { + printf(", LSDA entry #%d", lsda_entry_number); + } else { + printf(", LSDA entry not found"); + } + } + + uint32_t pers_idx = EXTRACT_BITS(encoding, UNWIND_PERSONALITY_MASK); + if (pers_idx != 0) { + pers_idx--; // Change 1-based to 0-based index + printf(", personality entry #%d", pers_idx); + } + + printf("\n"); +} + +void print_second_level_index_regular(struct baton baton) { + uint8_t *page_entries = + baton.compact_unwind_start + + baton.first_level_index_entry.secondLevelPagesSectionOffset + + baton.regular_second_level_page_header.entryPageOffset; + uint32_t entries_count = baton.regular_second_level_page_header.entryCount; + + uint8_t *offset = page_entries; + + uint32_t idx = 0; + while (idx < entries_count) { + uint32_t func_offset = *((uint32_t *)(offset)); + uint32_t encoding = *((uint32_t *)(offset + 4)); + + // UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the + // functionOffset from the containing index table already. + // UNWIND_SECOND_LEVEL_COMPRESSED + // entries only have the offset from the containing index table + // functionOffset. + // So strip off the containing index table functionOffset value here so they + // can + // be treated the same at the lower layers. + + print_function_encoding(baton, idx, encoding, (uint32_t)-1, + func_offset - + baton.first_level_index_entry.functionOffset); + idx++; + offset += 8; + } +} + +void print_second_level_index_compressed(struct baton baton) { + uint8_t *this_index = + baton.compact_unwind_start + + baton.first_level_index_entry.secondLevelPagesSectionOffset; + uint8_t *start_of_entries = + this_index + baton.compressed_second_level_page_header.entryPageOffset; + uint8_t *offset = start_of_entries; + for (uint16_t idx = 0; + idx < baton.compressed_second_level_page_header.entryCount; idx++) { + uint32_t entry = *((uint32_t *)offset); + offset += 4; + uint32_t encoding; + + uint32_t entry_encoding_index = + UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry); + uint32_t entry_func_offset = + UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry); + + if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) { + // encoding is in common table in section header + encoding = + *((uint32_t *)(baton.compact_unwind_start + + baton.unwind_header.commonEncodingsArraySectionOffset + + (entry_encoding_index * sizeof(uint32_t)))); + } else { + // encoding is in page specific table + uint32_t page_encoding_index = + entry_encoding_index - baton.unwind_header.commonEncodingsArrayCount; + encoding = *((uint32_t *)(this_index + + baton.compressed_second_level_page_header + .encodingsPageOffset + + (page_encoding_index * sizeof(uint32_t)))); + } + + print_function_encoding(baton, idx, encoding, entry_encoding_index, + entry_func_offset); + } +} + +void print_second_level_index(struct baton baton) { + uint8_t *index_start = + baton.compact_unwind_start + + baton.first_level_index_entry.secondLevelPagesSectionOffset; + + if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_REGULAR) { + struct unwind_info_regular_second_level_page_header header; + memcpy(&header, index_start, + sizeof(struct unwind_info_regular_second_level_page_header)); + printf( + " UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n", + baton.current_index_table_number, header.entryPageOffset, + header.entryCount); + baton.regular_second_level_page_header = header; + print_second_level_index_regular(baton); + } + + if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_COMPRESSED) { + struct unwind_info_compressed_second_level_page_header header; + memcpy(&header, index_start, + sizeof(struct unwind_info_compressed_second_level_page_header)); + printf(" UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, " + "entryCount %d, encodingsPageOffset %d, encodingsCount %d\n", + baton.current_index_table_number, header.entryPageOffset, + header.entryCount, header.encodingsPageOffset, + header.encodingsCount); + baton.compressed_second_level_page_header = header; + print_second_level_index_compressed(baton); + } +} + +void print_index_sections(struct baton baton) { + uint8_t *index_section_offset = + baton.compact_unwind_start + baton.unwind_header.indexSectionOffset; + uint32_t index_count = baton.unwind_header.indexCount; + + uint32_t cur_idx = 0; + + uint8_t *offset = index_section_offset; + while (cur_idx < index_count) { + baton.current_index_table_number = cur_idx; + struct unwind_info_section_header_index_entry index_entry; + memcpy(&index_entry, offset, + sizeof(struct unwind_info_section_header_index_entry)); + printf("index section #%d: functionOffset %d, " + "secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n", + cur_idx, index_entry.functionOffset, + index_entry.secondLevelPagesSectionOffset, + index_entry.lsdaIndexArraySectionOffset); + + // secondLevelPagesSectionOffset == 0 means this is a sentinel entry + if (index_entry.secondLevelPagesSectionOffset != 0) { + struct unwind_info_section_header_index_entry next_index_entry; + memcpy(&next_index_entry, + offset + sizeof(struct unwind_info_section_header_index_entry), + sizeof(struct unwind_info_section_header_index_entry)); + + baton.lsda_array_start = + baton.compact_unwind_start + index_entry.lsdaIndexArraySectionOffset; + baton.lsda_array_end = baton.compact_unwind_start + + next_index_entry.lsdaIndexArraySectionOffset; + + uint8_t *lsda_entry_offset = baton.lsda_array_start; + uint32_t lsda_count = 0; + while (lsda_entry_offset < baton.lsda_array_end) { + struct unwind_info_section_header_lsda_index_entry lsda_entry; + memcpy(&lsda_entry, lsda_entry_offset, + sizeof(struct unwind_info_section_header_lsda_index_entry)); + uint64_t function_file_address = + baton.first_level_index_entry.functionOffset + + lsda_entry.functionOffset + baton.text_segment_vmaddr; + uint64_t lsda_file_address = + lsda_entry.lsdaOffset + baton.text_segment_vmaddr; + printf(" LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx64 + "), lsdaOffset %d (file address 0x%" PRIx64 ")\n", + lsda_count, lsda_entry.functionOffset, + lsda_entry.functionOffset - index_entry.functionOffset, + function_file_address, lsda_entry.lsdaOffset, lsda_file_address); + lsda_count++; + lsda_entry_offset += + sizeof(struct unwind_info_section_header_lsda_index_entry); + } + + printf("\n"); + + baton.first_level_index_entry = index_entry; + print_second_level_index(baton); + } + + printf("\n"); + + cur_idx++; + offset += sizeof(struct unwind_info_section_header_index_entry); + } +} + +int main(int argc, char **argv) { + struct stat st; + char *file = argv[0]; + if (argc > 1) + file = argv[1]; + int fd = open(file, O_RDONLY); + if (fd == -1) { + printf("Failed to open '%s'\n", file); + exit(1); + } + fstat(fd, &st); + uint8_t *file_mem = + (uint8_t *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0); + if (file_mem == MAP_FAILED) { + printf("Failed to mmap() '%s'\n", file); + } + + FILE *f = fopen("a.out", "r"); + + struct baton baton; + baton.mach_header_start = file_mem; + baton.symbols = NULL; + baton.symbols_count = 0; + baton.function_start_addresses = NULL; + baton.function_start_addresses_count = 0; + + scan_macho_load_commands(&baton); + + if (baton.compact_unwind_start == NULL) { + printf("could not find __TEXT,__unwind_info section\n"); + exit(1); + } + + struct unwind_info_section_header header; + memcpy(&header, baton.compact_unwind_start, + sizeof(struct unwind_info_section_header)); + printf("Header:\n"); + printf(" version %u\n", header.version); + printf(" commonEncodingsArraySectionOffset is %d\n", + header.commonEncodingsArraySectionOffset); + printf(" commonEncodingsArrayCount is %d\n", + header.commonEncodingsArrayCount); + printf(" personalityArraySectionOffset is %d\n", + header.personalityArraySectionOffset); + printf(" personalityArrayCount is %d\n", header.personalityArrayCount); + printf(" indexSectionOffset is %d\n", header.indexSectionOffset); + printf(" indexCount is %d\n", header.indexCount); + + uint8_t *common_encodings = + baton.compact_unwind_start + header.commonEncodingsArraySectionOffset; + uint32_t encoding_idx = 0; + while (encoding_idx < header.commonEncodingsArrayCount) { + uint32_t encoding = *((uint32_t *)common_encodings); + printf(" Common Encoding [%d]: 0x%x ", encoding_idx, encoding); + print_encoding(baton, NULL, encoding); + printf("\n"); + common_encodings += sizeof(uint32_t); + encoding_idx++; + } + + uint8_t *pers_arr = + baton.compact_unwind_start + header.personalityArraySectionOffset; + uint32_t pers_idx = 0; + while (pers_idx < header.personalityArrayCount) { + int32_t pers_delta = *((int32_t *)(baton.compact_unwind_start + + header.personalityArraySectionOffset + + (pers_idx * sizeof(uint32_t)))); + printf(" Personality [%d]: personality function ptr @ offset %d (file " + "address 0x%" PRIx64 ")\n", + pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta); + pers_idx++; + pers_arr += sizeof(uint32_t); + } + + printf("\n"); + + baton.unwind_header = header; + + print_index_sections(baton); + + return 0; +} diff --git a/gnu/llvm/lldb/tools/darwin-debug/CMakeLists.txt b/gnu/llvm/lldb/tools/darwin-debug/CMakeLists.txt new file mode 100644 index 00000000000..b902788f05a --- /dev/null +++ b/gnu/llvm/lldb/tools/darwin-debug/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_tool(darwin-debug ADD_TO_FRAMEWORK + darwin-debug.cpp +) diff --git a/gnu/llvm/lldb/tools/darwin-debug/darwin-debug.cpp b/gnu/llvm/lldb/tools/darwin-debug/darwin-debug.cpp new file mode 100644 index 00000000000..b184cb5a652 --- /dev/null +++ b/gnu/llvm/lldb/tools/darwin-debug/darwin-debug.cpp @@ -0,0 +1,334 @@ +//===-- darwin-debug.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// 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> +#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/gnu/llvm/lldb/tools/darwin-threads/examine-threads.c b/gnu/llvm/lldb/tools/darwin-threads/examine-threads.c new file mode 100644 index 00000000000..51f35b6b0cc --- /dev/null +++ b/gnu/llvm/lldb/tools/darwin-threads/examine-threads.c @@ -0,0 +1,507 @@ +#include <ctype.h> +#include <dispatch/dispatch.h> +#include <errno.h> +#include <libproc.h> +#include <mach/mach.h> +#include <mach/task_info.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/sysctl.h> +#include <time.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 0x%llx, 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/gnu/llvm/lldb/tools/debugserver/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/CMakeLists.txt new file mode 100644 index 00000000000..1dc32434ba4 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.4.3) + +project(Debugserver LANGUAGES C CXX ASM-ATT) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_SOURCE_DIR}/../../cmake" + "${CMAKE_SOURCE_DIR}/../../cmake/modules" + ) + + include(LLDBStandalone) + include(debugserverConfig) + include(AddLLDB) + + set(LLDB_SOURCE_DIR "${CMAKE_SOURCE_DIR}/../../") + include_directories(${LLDB_SOURCE_DIR}/include) +endif() + +add_subdirectory(source) diff --git a/gnu/llvm/lldb/tools/debugserver/debugnub-exports b/gnu/llvm/lldb/tools/debugserver/debugnub-exports new file mode 100644 index 00000000000..662bf9308a6 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/debugnub-exports @@ -0,0 +1,2 @@ +_DNB* +__DNB* diff --git a/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..f4267b7633a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj @@ -0,0 +1,1901 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 23562ED61D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */; }; + 23562ED71D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */; }; + 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; }; + 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; }; + 26CE05C3115C36580022F371 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; }; + 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; }; + 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; }; + 456F67551AD46CE9002850C2 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; }; + 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; }; + 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; }; + 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; }; + 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; }; + 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; }; + 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; }; + 266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; }; + 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; }; + 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; }; + 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; }; + 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; }; + 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; }; + 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; }; + 456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; }; + 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; }; + 456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; }; + 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; }; + 456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; }; + 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; }; + 456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; }; + 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; }; + 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; }; + 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; }; + 456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; }; + 23AE72E41D25DECF00945BCE /* DarwinLogCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */; }; + 23AE72E51D25DEE100945BCE /* DarwinLogCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */; }; + 49D404621E39260F00570CDC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49D404611E39260F00570CDC /* Foundation.framework */; }; + AFA3FCA11E39984900218D5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49D404611E39260F00570CDC /* Foundation.framework */; }; + 456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; }; + AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; }; + 23043C9D1D35DBEC00FC25CA /* JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA51D2DB54300E98261 /* JSON.cpp */; }; + 233B4EA71D2DB54300E98261 /* JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA51D2DB54300E98261 /* JSON.cpp */; }; + 23AC04C61D2F41A00072351D /* LogFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C41D2F41A00072351D /* LogFilter.cpp */; }; + 23AC04C71D2F41A00072351D /* LogFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C41D2F41A00072351D /* LogFilter.cpp */; }; + 23AC04CA1D2F42250072351D /* LogFilterChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C81D2F42250072351D /* LogFilterChain.cpp */; }; + 23AC04CB1D2F42250072351D /* LogFilterChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C81D2F42250072351D /* LogFilterChain.cpp */; }; + 2307CCCB1D4A5D630016ABC0 /* LogFilterExactMatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */; }; + 237821B01D4917D20028B7A1 /* LogFilterExactMatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */; }; + 23AC04CF1D2F58AF0072351D /* LogFilterRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */; }; + 23AC04D01D2F58AF0072351D /* LogFilterRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */; }; + 23562ED91D342B0000AB2BD4 /* LogMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */; }; + 23562EDA1D342B0000AB2BD4 /* LogMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */; }; + 23562ED21D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */; }; + 23562ED31D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */; }; + 26CE05B0115C36340022F371 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; }; + 456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; }; + 26CE05B1115C36350022F371 /* MachProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; }; + 456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; }; + 26CE05B6115C36390022F371 /* MachTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; }; + 456F67541AD46CE9002850C2 /* MachTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; }; + 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; }; + 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; }; + 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; }; + 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; }; + 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; }; + 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; }; + 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; }; + 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; }; + 23D1B0291D497E8B00FF831B /* OsLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D1B0271D497E8B00FF831B /* OsLogger.cpp */; }; + 23D1B02A1D497E8B00FF831B /* OsLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D1B0271D497E8B00FF831B /* OsLogger.cpp */; }; + 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; }; + 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; }; + 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; }; + 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; }; + 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; }; + 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; }; + 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; }; + 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; }; + 26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; }; + 456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; }; + 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; }; + 456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; }; + 26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; }; + 456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; }; + AF588449206077BD00A0CB5A /* SocketAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */; }; + D6631CA91E848FE9006A7B11 /* SocketAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */; }; + AF48558C1D75126800D19C07 /* StdStringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */; }; + AF48558D1D75127500D19C07 /* StdStringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */; }; + 23043C9E1D35DBFA00FC25CA /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */; }; + 233B4EA91D2DB96A00E98261 /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */; }; + 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; }; + 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; }; + 26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; }; + 456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActivityStore.cpp; sourceTree = "<group>"; }; + 23562ED41D3426DD00AB2BD4 /* ActivityStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityStore.h; sourceTree = "<group>"; }; + 23AE72E61D25DEFB00945BCE /* ActivityStreamSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityStreamSPI.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>"; }; + 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFString.cpp; sourceTree = "<group>"; }; + 2695DD9A0D3EC160007E4CA2 /* CFString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFString.h; sourceTree = "<group>"; }; + 26C637E70C71334A0024798E /* CFUtils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CFUtils.h; sourceTree = "<group>"; }; + 2307CCCC1D4A5DAE0016ABC0 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; }; + 237821AD1D4917D20028B7A1 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; }; + 26593A060D4931CC001C9FE3 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; }; + 26ACA3340D3E956300A2120B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 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>"; }; + 264D5D571293835600ED4C01 /* DNBArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArch.cpp; sourceTree = "<group>"; }; + 26C637D80C71334A0024798E /* DNBArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArch.h; sourceTree = "<group>"; }; + 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = DNBArchImpl.cpp; path = arm/DNBArchImpl.cpp; sourceTree = "<group>"; }; + 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImpl.cpp; sourceTree = "<group>"; }; + 2675D4230CCEB705000F49AF /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = DNBArchImpl.h; path = arm/DNBArchImpl.h; sourceTree = "<group>"; }; + 26C637FC0C71334A0024798E /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImpl.h; sourceTree = "<group>"; }; + 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>"; }; + 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>"; }; + 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>"; }; + 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>"; }; + 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>"; }; + 26A8FE1E0D11A77B00203048 /* DNBTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBTimer.h; sourceTree = "<group>"; }; + 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DarwinLogCollector.cpp; sourceTree = "<group>"; }; + 23AE72E31D25DECF00945BCE /* DarwinLogCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarwinLogCollector.h; sourceTree = "<group>"; }; + 23CF6F5E1D28A3760088ADC9 /* DarwinLogEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogEvent.h; sourceTree = "<group>"; }; + 23AC04CC1D2F42F10072351D /* DarwinLogInterfaces.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogInterfaces.h; sourceTree = "<group>"; }; + 23562ECF1D34110D00AB2BD4 /* DarwinLogTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogTypes.h; sourceTree = "<group>"; }; + 49D404611E39260F00570CDC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Genealogy.cpp; 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>"; }; + 233B4EA51D2DB54300E98261 /* JSON.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSON.cpp; sourceTree = "<group>"; }; + 233B4EA61D2DB54300E98261 /* JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSON.h; sourceTree = "<group>"; }; + 264F679A1B2F9EB200140093 /* JSONGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSONGenerator.h; sourceTree = "<group>"; }; + 23AC04C41D2F41A00072351D /* LogFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilter.cpp; sourceTree = "<group>"; }; + 23AC04C51D2F41A00072351D /* LogFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilter.h; sourceTree = "<group>"; }; + 23AC04C81D2F42250072351D /* LogFilterChain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterChain.cpp; sourceTree = "<group>"; }; + 23AC04C91D2F42250072351D /* LogFilterChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterChain.h; sourceTree = "<group>"; }; + 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterExactMatch.cpp; sourceTree = "<group>"; }; + 237821AF1D4917D20028B7A1 /* LogFilterExactMatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterExactMatch.h; sourceTree = "<group>"; }; + 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterRegex.cpp; sourceTree = "<group>"; }; + 23AC04CE1D2F58AF0072351D /* LogFilterRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterRegex.h; sourceTree = "<group>"; }; + 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogMessage.cpp; sourceTree = "<group>"; }; + 23AC04D11D2F60130072351D /* LogMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogMessage.h; sourceTree = "<group>"; }; + 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogMessageOsLog.cpp; sourceTree = "<group>"; }; + 23562ED11D3424DF00AB2BD4 /* LogMessageOsLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogMessageOsLog.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>"; }; + 26C637F10C71334A0024798E /* MachProcess.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachProcess.h; sourceTree = "<group>"; }; + 26C637F00C71334A0024798E /* MachProcess.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = MachProcess.mm; 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>"; }; + 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>"; }; + 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>"; }; + 23D1B0271D497E8B00FF831B /* OsLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OsLogger.cpp; sourceTree = "<group>"; }; + 23D1B0281D497E8B00FF831B /* OsLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OsLogger.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>"; }; + 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PThreadMutex.cpp; sourceTree = "<group>"; }; + 26C638000C71334A0024798E /* PThreadMutex.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadMutex.h; 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>"; }; + 26A68F7E0D104EC800665A9E /* RNBContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBContext.cpp; sourceTree = "<group>"; }; + 26A68F7D0D104EC800665A9E /* RNBContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBContext.h; sourceTree = "<group>"; }; + 26E6B9DA0D1329010037ECDD /* RNBDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBDefs.h; sourceTree = "<group>"; }; + 26A68FD60D10574500665A9E /* RNBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBRemote.cpp; sourceTree = "<group>"; }; + 26A68FD50D10574500665A9E /* RNBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBRemote.h; sourceTree = "<group>"; }; + EF8878A00D9C797C001831DA /* RNBServices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBServices.cpp; sourceTree = "<group>"; }; + EF88789F0D9C797C001831DA /* RNBServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBServices.h; sourceTree = "<group>"; }; + 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBSocket.cpp; sourceTree = "<group>"; }; + 26A68FAF0D1054DA00665A9E /* RNBSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBSocket.h; sourceTree = "<group>"; }; + D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SocketAddress.cpp; path = ../../source/Host/common/SocketAddress.cpp; sourceTree = "<group>"; }; + AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StdStringExtractor.cpp; sourceTree = "<group>"; }; + 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringConvert.cpp; path = ../../../source/Host/common/StringConvert.cpp; 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>"; }; + 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.internal.plist; sourceTree = "<group>"; }; + EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.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>"; }; + 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.plist; sourceTree = "<group>"; }; + 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.posix.plist; sourceTree = "<group>"; }; + AF949ED620605DC2002A91F9 /* com.apple.internal.xpc.remote.debugserver.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.apple.internal.xpc.remote.debugserver.plist; sourceTree = "<group>"; }; + 26C637E80C71334A0024798E /* dbgnub-mig.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 30; path = "dbgnub-mig.defs"; sourceTree = "<group>"; }; + 260FC7320E5B290400043FC9 /* debugnub-exports */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "debugnub-exports"; sourceTree = SOURCE_ROOT; }; + 26CE0594115C31C20022F371 /* debugserver */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = debugserver; sourceTree = BUILT_PRODUCTS_DIR; }; + 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "debugserver-entitlements.plist"; sourceTree = "<group>"; }; + AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "debugserver-macosx-entitlements.plist"; sourceTree = "<group>"; }; + 456F67721AD46CE9002850C2 /* debugserver-nonui */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "debugserver-nonui"; sourceTree = BUILT_PRODUCTS_DIR; }; + 26A02918114AB9240029C479 /* debugserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugserver.cpp; sourceTree = "<group>"; }; + 9457ECF61419864100DFE7D8 /* stack_logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stack_logging.h; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 26CE0592115C31C20022F371 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 49D404621E39260F00570CDC /* Foundation.framework in Frameworks */, + 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 456F676A1AD46CE9002850C2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */, + AFA3FCA11E39984900218D5E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* dbgnub */ = { + isa = PBXGroup; + children = ( + D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */, + 26ACA3330D3E94F200A2120B /* Framework */, + 26C637D50C71334A0024798E /* source */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + 49D404601E39260F00570CDC /* Frameworks */, + ); + name = dbgnub; + sourceTree = "<group>"; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 26CE0594115C31C20022F371 /* debugserver */, + 456F67721AD46CE9002850C2 /* debugserver-nonui */, + ); + name = Products; + sourceTree = "<group>"; + }; + 23AC04C31D2F3E9A0072351D /* DarwinLog */ = { + isa = PBXGroup; + children = ( + 237821AD1D4917D20028B7A1 /* CMakeLists.txt */, + 23562ED41D3426DD00AB2BD4 /* ActivityStore.h */, + 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */, + 23AE72E61D25DEFB00945BCE /* ActivityStreamSPI.h */, + 23AE72E31D25DECF00945BCE /* DarwinLogCollector.h */, + 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */, + 23CF6F5E1D28A3760088ADC9 /* DarwinLogEvent.h */, + 23AC04CC1D2F42F10072351D /* DarwinLogInterfaces.h */, + 23562ECF1D34110D00AB2BD4 /* DarwinLogTypes.h */, + 23AC04C51D2F41A00072351D /* LogFilter.h */, + 23AC04C41D2F41A00072351D /* LogFilter.cpp */, + 23AC04C91D2F42250072351D /* LogFilterChain.h */, + 23AC04C81D2F42250072351D /* LogFilterChain.cpp */, + 237821AF1D4917D20028B7A1 /* LogFilterExactMatch.h */, + 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */, + 23AC04CE1D2F58AF0072351D /* LogFilterRegex.h */, + 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */, + 23AC04D11D2F60130072351D /* LogMessage.h */, + 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */, + 23562ED11D3424DF00AB2BD4 /* LogMessageOsLog.h */, + 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */, + ); + path = DarwinLog; + 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 = ( + 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */, + EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */, + 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */, + 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */, + 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */, + AF949ED620605DC2002A91F9 /* com.apple.internal.xpc.remote.debugserver.plist */, + 260FC7320E5B290400043FC9 /* debugnub-exports */, + 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */, + AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.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 */, + 233B4EA61D2DB54300E98261 /* JSON.h */, + 233B4EA51D2DB54300E98261 /* JSON.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 */, + 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */, + 26C638020C71334A0024798E /* SysSignal.h */, + 26C638010C71334A0024798E /* SysSignal.cpp */, + 26C638060C71334A0024798E /* TTYState.h */, + 26C638050C71334A0024798E /* TTYState.cpp */, + ); + name = libdebugnub; + sourceTree = "<group>"; + }; + 26ACA3330D3E94F200A2120B /* Framework */ = { + isa = PBXGroup; + children = ( + 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 = ( + 23AC04C31D2F3E9A0072351D /* DarwinLog */, + 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */, + 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */, + 2695DD9A0D3EC160007E4CA2 /* CFString.h */, + 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */, + 26C637E70C71334A0024798E /* CFUtils.h */, + 2307CCCC1D4A5DAE0016ABC0 /* CMakeLists.txt */, + 2675D41C0CCEB6CF000F49AF /* arm */, + 266B5ECE1460A68200E43F0A /* arm64 */, + 26C637E90C71334A0024798E /* i386 */, + 26C637FA0C71334A0024798E /* ppc */, + 26CF99A11142EB7400011AAB /* x86_64 */, + 26C637E80C71334A0024798E /* dbgnub-mig.defs */, + AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */, + AF0934BA18E12B92005A11FD /* Genealogy.h */, + AF0934BB18E12B92005A11FD /* GenealogySPI.h */, + 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 */, + 23D1B0281D497E8B00FF831B /* OsLogger.h */, + 23D1B0271D497E8B00FF831B /* OsLogger.cpp */, + 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 */, + AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */, + ); + name = debugserver; + sourceTree = "<group>"; + usesTabs = 0; + }; + 49D404601E39260F00570CDC /* Frameworks */ = { + isa = PBXGroup; + children = ( + 49D404611E39260F00570CDC /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; +/* 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-nonui */; + 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 --entitlements ${SRCROOT}/../../resources/debugserver-macosx-entitlements.plist \"${TARGET_BUILD_DIR}/${TARGET_NAME}\"\n fi\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 26CE0591115C31C20022F371 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D6631CA91E848FE9006A7B11 /* SocketAddress.cpp in Sources */, + 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */, + 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */, + 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */, + AF48558C1D75126800D19C07 /* StdStringExtractor.cpp in Sources */, + 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */, + 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */, + 23D1B0291D497E8B00FF831B /* OsLogger.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 */, + 233B4EA71D2DB54300E98261 /* JSON.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 */, + 23AC04CF1D2F58AF0072351D /* LogFilterRegex.cpp in Sources */, + 233B4EA91D2DB96A00E98261 /* StringConvert.cpp in Sources */, + 23562ED21D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */, + 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */, + 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */, + 23AC04CA1D2F42250072351D /* LogFilterChain.cpp in Sources */, + 23562ED61D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */, + 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */, + 23AC04C61D2F41A00072351D /* LogFilter.cpp in Sources */, + 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */, + 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */, + 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */, + 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */, + 23AE72E41D25DECF00945BCE /* DarwinLogCollector.cpp in Sources */, + 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */, + 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */, + 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */, + 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */, + 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */, + 26CE05C3115C36580022F371 /* CFString.cpp in Sources */, + 23562ED91D342B0000AB2BD4 /* LogMessage.cpp in Sources */, + 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */, + 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */, + 237821B01D4917D20028B7A1 /* LogFilterExactMatch.cpp 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 */, + 23043C9D1D35DBEC00FC25CA /* JSON.cpp in Sources */, + 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */, + 23D1B02A1D497E8B00FF831B /* OsLogger.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 */, + 2307CCCB1D4A5D630016ABC0 /* LogFilterExactMatch.cpp in Sources */, + 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */, + 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */, + 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */, + 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */, + 23562ED71D342A5A00AB2BD4 /* ActivityStore.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 */, + 23562ED31D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */, + 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */, + 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */, + 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */, + 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */, + 23AE72E51D25DEE100945BCE /* DarwinLogCollector.cpp in Sources */, + 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */, + 23562EDA1D342B0000AB2BD4 /* LogMessage.cpp in Sources */, + 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */, + 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */, + 23AC04C71D2F41A00072351D /* LogFilter.cpp in Sources */, + 23043C9E1D35DBFA00FC25CA /* StringConvert.cpp in Sources */, + 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */, + AF588449206077BD00A0CB5A /* SocketAddress.cpp in Sources */, + 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */, + 23AC04CB1D2F42250072351D /* LogFilterChain.cpp in Sources */, + AF48558D1D75127500D19C07 /* StdStringExtractor.cpp in Sources */, + 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */, + 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */, + 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */, + 23AC04D01D2F58AF0072351D /* LogFilterRegex.cpp in Sources */, + 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB914F08733D8E0010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + "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; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 360.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_LDFLAGS = "-lcompression"; + LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; + LLDB_USE_OS_LOG = 0; + 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 = { + ALWAYS_SEARCH_USER_PATHS = NO; + "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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; + LLDB_USE_OS_LOG = 0; + 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 = { + ALWAYS_SEARCH_USER_PATHS = NO; + "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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; + LLDB_USE_OS_LOG = 1; + 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=*]" = "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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + "-isystem", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + SDKROOT = 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-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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_BKS", + "-DWITH_FBS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + "-isystem", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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-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 = 360.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; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + "-isystem", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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-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 = 360.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/local/bin; + LLDB_COMPRESSION_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ( + "-Wparentheses", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = "debugserver-nonui"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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-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 = 360.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/local/bin; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "$(LLDB_ENERGY_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = "debugserver-nonui"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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-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 = 360.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/local/bin; + LLDB_COMPRESSION_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "$(LLDB_ENERGY_CFLAGS)", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = "debugserver-nonui"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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=*]" = "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 = 360.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/local/bin; + "INSTALL_PATH[sdk=iphoneos*]" = /usr/local/bin; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_OS_LOG_CFLAGS)", + ); + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; + "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_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = "debugserver-nonui"; + SDKROOT = 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; + }; + 4968B7A916657FAE00741ABB /* DebugClang */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + "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; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 360.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_LDFLAGS = "-lcompression"; + LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; + LLDB_USE_OS_LOG = 0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = 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-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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + "-isystem", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = DebugClang; + }; + 940AD5251B1FE3B10051E88F /* DebugPresubmission */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + "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; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 360.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_LDFLAGS = "-lcompression"; + LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; + LLDB_USE_OS_LOG = 0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = 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-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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + "-isystem", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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-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 = 360.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/local/bin; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "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_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = "debugserver-nonui"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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-nonui"; + }; + name = "CustomSwift-Debug"; + }; + 94BA9B371B1A7C5700035A23 /* CustomSwift-Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "lldb-debugserver-nonui"; + }; + name = "CustomSwift-Release"; + }; + 94D72C871ADF10AA00A3F718 /* CustomSwift-Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + "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; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 360.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_LDFLAGS = "-lcompression"; + LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; + LLDB_USE_OS_LOG = 0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = 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-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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_BKS", + "-DWITH_FBS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + "-isystem", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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 = { + ALWAYS_SEARCH_USER_PATHS = NO; + "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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; + LLDB_USE_OS_LOG = 0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = 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-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 = 360.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_LDFLAGS = "-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; + OTHER_CFLAGS = ( + "-Wparentheses", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_OS_LOG_CFLAGS)", + "-isystem", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = 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/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..c24931480d4 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme new file mode 100644 index 00000000000..2f27b5e4eff --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist b/gnu/llvm/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist new file mode 100644 index 00000000000..343325c2765 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/scripts/diagnose-termination.d b/gnu/llvm/lldb/tools/debugserver/scripts/diagnose-termination.d new file mode 100644 index 00000000000..d216c975003 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/source/ARM_DWARF_Registers.h b/gnu/llvm/lldb/tools/debugserver/source/ARM_DWARF_Registers.h new file mode 100644 index 00000000000..e892927f6cf --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/ARM_DWARF_Registers.h @@ -0,0 +1,205 @@ +//===-- ARM_DWARF_Registers.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/gnu/llvm/lldb/tools/debugserver/source/ARM_ehframe_Registers.h b/gnu/llvm/lldb/tools/debugserver/source/ARM_ehframe_Registers.h new file mode 100644 index 00000000000..b1a085a506e --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/ARM_ehframe_Registers.h @@ -0,0 +1,33 @@ +//===-- ARM_ehframe_Registers.h -------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/gnu/llvm/lldb/tools/debugserver/source/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/source/CMakeLists.txt new file mode 100644 index 00000000000..a7d789dd9e1 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/CMakeLists.txt @@ -0,0 +1,272 @@ +include(CheckCXXCompilerFlag) +include(CheckLibraryExists) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) +include_directories(${LLDB_SOURCE_DIR}/source) +include_directories(MacOSX/DarwinLog) + +include_directories(MacOSX) + +function(check_certificate identity result_valid) + execute_process( + COMMAND security find-certificate -Z -p -c ${identity} /Library/Keychains/System.keychain + RESULT_VARIABLE exit_code OUTPUT_QUIET ERROR_QUIET) + if(exit_code) + set(${result_valid} FALSE PARENT_SCOPE) + else() + set(${result_valid} TRUE PARENT_SCOPE) + endif() +endfunction() + +function(get_debugserver_codesign_identity result) + string(CONCAT not_found_help + "This will cause failures in the test suite." + "Pass '-DLLDB_USE_SYSTEM_DEBUGSERVER=ON' to use the system one instead." + "See 'Code Signing on macOS' in the documentation." + ) + + # Explicit override: warn if unavailable + if(LLDB_CODESIGN_IDENTITY) + set(${result} ${LLDB_CODESIGN_IDENTITY} PARENT_SCOPE) + check_certificate(${LLDB_CODESIGN_IDENTITY} available) + if(NOT available) + message(WARNING "LLDB_CODESIGN_IDENTITY not found: '${LLDB_CODESIGN_IDENTITY}' ${not_found_help}") + endif() + return() + endif() + + # Development signing identity: use if available + check_certificate(lldb_codesign available) + if(available) + set(${result} lldb_codesign PARENT_SCOPE) + return() + endif() + + message(WARNING "Development code sign identiy not found: 'lldb_codesign' ${not_found_help}") + + # LLVM pendant: fallback if available + if(LLVM_CODESIGNING_IDENTITY) + check_certificate(${LLVM_CODESIGNING_IDENTITY} available) + if(available) + set(${result} ${LLVM_CODESIGNING_IDENTITY} PARENT_SCOPE) + return() + endif() + endif() + + # Ad-hoc signing: last resort + set(${result} "-" PARENT_SCOPE) +endfunction() + +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") + +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 () + +check_library_exists(compression compression_encode_buffer "" HAVE_LIBCOMPRESSION) + +add_subdirectory(MacOSX) + +set(LLDB_CODESIGN_IDENTITY "" CACHE STRING + "Identity override for debugserver; see 'Code Signing on macOS' in the documentation (Darwin only)") + +get_debugserver_codesign_identity(debugserver_codesign_identity) + +# Override locally, so the identity is used for targets created in this scope. +set(LLVM_CODESIGNING_IDENTITY ${debugserver_codesign_identity}) + +# Use the same identity later in the test suite. +set_property(GLOBAL PROPERTY + LLDB_DEBUGSERVER_CODESIGN_IDENTITY ${debugserver_codesign_identity}) + +if(APPLE) + if(IOS) + find_library(BACKBOARD_LIBRARY BackBoardServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(FRONTBOARD_LIBRARY FrontBoardServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(SPRINGBOARD_LIBRARY SpringBoardServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(MOBILESERVICES_LIBRARY MobileCoreServices + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) + find_library(LOCKDOWN_LIBRARY lockdown) + + if(NOT BACKBOARD_LIBRARY) + set(SKIP_TEST_DEBUGSERVER ON CACHE BOOL "" FORCE) + endif() + endif() +endif() + +if(HAVE_LIBCOMPRESSION) + set(LIBCOMPRESSION compression) +endif() + +if(LLDB_USE_ENTITLEMENTS) + if(IOS) + set(entitlements ${CMAKE_CURRENT_SOURCE_DIR}/debugserver-entitlements.plist) + else() + # Same entitlements file as used for lldb-server + set(entitlements ${LLDB_SOURCE_DIR}/resources/debugserver-macosx-entitlements.plist) + endif() +endif() + +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}/MacOSX/dbgnub-mig.defs + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/MacOSX/dbgnub-mig.defs + ) + +set(DEBUGSERVER_VERS_GENERATED_FILE ${CMAKE_CURRENT_BINARY_DIR}/debugserver_vers.c) +configure_file(debugserver_vers.c.in + ${DEBUGSERVER_VERS_GENERATED_FILE} @ONLY) + +set(lldbDebugserverCommonSources + DNBArch.cpp + DNBBreakpoint.cpp + DNB.cpp + DNBDataRef.cpp + DNBError.cpp + DNBLog.cpp + DNBRegisterInfo.cpp + DNBThreadResumeActions.cpp + JSON.cpp + StdStringExtractor.cpp + # JSON reader depends on the following LLDB-common files + ${LLDB_SOURCE_DIR}/source/Host/common/StringConvert.cpp + ${LLDB_SOURCE_DIR}/source/Host/common/SocketAddress.cpp + # end JSON reader dependencies + libdebugserver.cpp + PseudoTerminal.cpp + PThreadEvent.cpp + PThreadMutex.cpp + RNBContext.cpp + RNBRemote.cpp + RNBServices.cpp + RNBSocket.cpp + SysSignal.cpp + TTYState.cpp + + MacOSX/CFBundle.cpp + MacOSX/CFString.cpp + MacOSX/Genealogy.cpp + MacOSX/MachException.cpp + MacOSX/MachProcess.mm + MacOSX/MachTask.mm + MacOSX/MachThread.cpp + MacOSX/MachThreadList.cpp + MacOSX/MachVMMemory.cpp + MacOSX/MachVMRegion.cpp + MacOSX/OsLogger.cpp + ${generated_mach_interfaces} + ${DEBUGSERVER_VERS_GENERATED_FILE}) + +add_library(lldbDebugserverCommon ${lldbDebugserverCommonSources}) +set_target_properties(lldbDebugserverCommon PROPERTIES FOLDER "lldb libraries/debugserver") + +target_link_libraries(lldbDebugserverCommon + INTERFACE ${COCOA_LIBRARY} + ${CORE_FOUNDATION_LIBRARY} + ${FOUNDATION_LIBRARY} + ${BACKBOARD_LIBRARY} + ${FRONTBOARD_LIBRARY} + ${SPRINGBOARD_LIBRARY} + ${MOBILESERVICES_LIBRARY} + ${LOCKDOWN_LIBRARY} + lldbDebugserverArchSupport + lldbDebugserverDarwin_DarwinLog + ${LIBCOMPRESSION}) +if(HAVE_LIBCOMPRESSION) + set_property(TARGET lldbDebugserverCommon APPEND PROPERTY + COMPILE_DEFINITIONS HAVE_LIBCOMPRESSION) +endif() +set(LLVM_OPTIONAL_SOURCES ${lldbDebugserverCommonSources}) +add_lldb_tool(debugserver ADD_TO_FRAMEWORK + debugserver.cpp + LINK_LIBS lldbDebugserverCommon + ENTITLEMENTS ${entitlements} +) + +# Workaround for Xcode-specific code-signing behavior: +# The XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY option causes debugserver to be copied +# into the framework first and code-signed afterwards. Sign the copy manually. +if (debugserver_codesign_identity AND LLDB_BUILD_FRAMEWORK AND + CMAKE_GENERATOR STREQUAL "Xcode") + if(NOT CMAKE_CODESIGN_ALLOCATE) + execute_process( + COMMAND xcrun -f codesign_allocate + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CMAKE_CODESIGN_ALLOCATE + ) + endif() + if(entitlements) + set(pass_entitlements --entitlements ${entitlements}) + endif() + + set(copy_location ${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Versions/${LLDB_FRAMEWORK_VERSION}/Resources/debugserver) + + add_custom_command(TARGET debugserver POST_BUILD + COMMAND ${CMAKE_COMMAND} -E + env CODESIGN_ALLOCATE=${CMAKE_CODESIGN_ALLOCATE} + xcrun codesign -f -s ${debugserver_codesign_identity} + ${pass_entitlements} ${copy_location} + COMMENT "Code-sign debugserver copy in the build-tree framework: ${copy_location}" + ) +endif() + +set_target_properties(debugserver PROPERTIES FOLDER "lldb libraries/debugserver") + +if(IOS) + set_property(TARGET lldbDebugserverCommon APPEND PROPERTY COMPILE_DEFINITIONS + WITH_LOCKDOWN + WITH_FBS + WITH_BKS + ) + set_property(TARGET debugserver APPEND PROPERTY COMPILE_DEFINITIONS + WITH_LOCKDOWN + WITH_FBS + WITH_BKS + ) + set_property(TARGET lldbDebugserverCommon APPEND PROPERTY COMPILE_FLAGS + -F${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks + ) + + add_library(lldbDebugserverCommon_NonUI ${lldbDebugserverCommonSources}) + target_link_libraries(lldbDebugserverCommon_NonUI + INTERFACE ${COCOA_LIBRARY} + ${CORE_FOUNDATION_LIBRARY} + ${FOUNDATION_LIBRARY} + lldbDebugserverArchSupport + lldbDebugserverDarwin_DarwinLog + ${LIBCOMPRESSION}) + if(HAVE_LIBCOMPRESSION) + set_property(TARGET lldbDebugserverCommon_NonUI APPEND PROPERTY + COMPILE_DEFINITIONS HAVE_LIBCOMPRESSION) + endif() + + add_lldb_tool(debugserver-nonui + debugserver.cpp + + LINK_LIBS + lldbDebugserverCommon_NonUI + + ENTITLEMENTS + ${entitlements} + ) +endif() diff --git a/gnu/llvm/lldb/tools/debugserver/source/ChangeLog b/gnu/llvm/lldb/tools/debugserver/source/ChangeLog new file mode 100644 index 00000000000..898f2fba7b0 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/source/DNB.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNB.cpp new file mode 100644 index 00000000000..8d9c691f9d3 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNB.cpp @@ -0,0 +1,1734 @@ +//===-- DNB.cpp -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/23/07. +// +//===----------------------------------------------------------------------===// + +#include "DNB.h" +#include <inttypes.h> +#include <libproc.h> +#include <map> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <vector> + +#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 "CFBundle.h" +#include "CFString.h" +#include "DNBDataRef.h" +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "DNBTimer.h" +#include "MacOSX/DarwinLog/DarwinLogCollector.h" +#include "MacOSX/Genealogy.h" +#include "MacOSX/MachProcess.h" +#include "MacOSX/MachTask.h" +#include "MacOSX/ThreadInfo.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 (true) { + 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 (true) { + 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, static_cast<void *>(argv), + static_cast<void *>(envp), working_directory, stdin_path, + stdout_path, stderr_path, no_stdio, launch_flavor, + disable_aslr, static_cast<void *>(err_str), + static_cast<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) { + strlcpy(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) { + 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(); +} + +JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid) { + MachProcessSP procSP; + if (GetProcessSP(pid, procSP)) { + return procSP->GetAllLoadedLibrariesInfos(pid); + } + return JSONGenerator::ObjectSP(); +} + +JSONGenerator::ObjectSP +DNBGetLibrariesInfoForAddresses(nub_process_t pid, + std::vector<uint64_t> &macho_addresses) { + MachProcessSP procSP; + if (GetProcessSP(pid, procSP)) { + return procSP->GetLibrariesInfoForAddresses(pid, macho_addresses); + } + return JSONGenerator::ObjectSP(); +} + +JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid) { + MachProcessSP procSP; + if (GetProcessSP(pid, procSP)) { + return procSP->GetSharedCacheInfo(pid); + } + 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; +} + +const char *DNBGetDeploymentInfo(nub_process_t pid, + const struct load_command& lc, + uint64_t load_command_address, + uint32_t& major_version, + uint32_t& minor_version, + uint32_t& patch_version) { + MachProcessSP procSP; + if (GetProcessSP(pid, procSP)) + return procSP->GetDeploymentInfo(lc, load_command_address, + major_version, minor_version, + patch_version); + return nullptr; +} + + +// 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; +} + +DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid) { + return DarwinLogCollector::GetEventsForProcess(pid); +} + +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... + ::strlcpy(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) { + ::strlcpy(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); +} + +std::string DNBGetMacCatalystVersionString() { + return MachProcess::GetMacCatalystVersionString(); +} + +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_32") == arch || + strstr(arch, "aarch64_32") == arch) + return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32); + else if (strstr(arch, "arm64e") == arch) + return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64); + 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/gnu/llvm/lldb/tools/debugserver/source/DNB.h b/gnu/llvm/lldb/tools/debugserver/source/DNB.h new file mode 100644 index 00000000000..e29fa0fa636 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNB.h @@ -0,0 +1,237 @@ +//===-- DNB.h ---------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/23/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNB_h__ +#define __DNB_h__ + +#include "DNBDefs.h" +#include "JSONGenerator.h" +#include "MacOSX/DarwinLog/DarwinLogEvent.h" +#include "MacOSX/Genealogy.h" +#include "MacOSX/ThreadInfo.h" +#include <mach/thread_info.h> +#include <string> +#include <Availability.h> +#include <mach/machine.h> + +#define DNB_EXPORT __attribute__((visibility("default"))) + +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 ((cpu_type_t)12 | 0x01000000) +#endif + +#ifndef CPU_TYPE_ARM64_32 +#define CPU_TYPE_ARM64_32 ((cpu_type_t)12 | 0x02000000) +#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; +DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid); + +// 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; +const char *DNBGetDeploymentInfo(nub_process_t pid, + const struct load_command& lc, + uint64_t load_command_address, + uint32_t& major_version, + uint32_t& minor_version, + uint32_t& patch_version); +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); +JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid); +JSONGenerator::ObjectSP +DNBGetLibrariesInfoForAddresses(nub_process_t pid, + std::vector<uint64_t> &macho_addresses); +JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid); + +// +// 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); +/// \return the iOSSupportVersion of the host OS. +std::string DNBGetMacCatalystVersionString(); +#endif diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBArch.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.cpp new file mode 100644 index 00000000000..931d623647f --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.cpp @@ -0,0 +1,79 @@ +//===-- DNBArch.cpp ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/DNBArch.h b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.h new file mode 100644 index 00000000000..b5e2e25ef47 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.h @@ -0,0 +1,126 @@ +//===-- DNBArch.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/ppc/DNBArchImpl.h" +#include "MacOSX/x86_64/DNBArchImplX86_64.h" + +#endif diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp new file mode 100644 index 00000000000..890bde024bf --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp @@ -0,0 +1,177 @@ +//===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/29/07. +// +//===----------------------------------------------------------------------===// + +#include "DNBBreakpoint.h" +#include "DNBLog.h" +#include "MachProcess.h" +#include <algorithm> +#include <assert.h> +#include <inttypes.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/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.h b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.h new file mode 100644 index 00000000000..0b4e077ae8a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.h @@ -0,0 +1,148 @@ +//===-- DNBBreakpoint.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.cpp new file mode 100644 index 00000000000..f19c913f65d --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.cpp @@ -0,0 +1,320 @@ +//===-- DNBDataRef.cpp ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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; + memcpy(&val, p, sizeof(uint16_t)); + + 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; + memcpy(&val, p, sizeof(uint32_t)); + 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; + memcpy(&val, p, sizeof(uint64_t)); + 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(false && "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(false && "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 = (const 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 |= (uint64_t)(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 |= (int64_t)(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) { + 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/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.h b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.h new file mode 100644 index 00000000000..d521700d151 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.h @@ -0,0 +1,124 @@ +//===-- DNBDataRef.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +class DNBDataRef { +public: + // For use with Dump + enum Type { + TypeUInt8 = 0, + TypeChar, + TypeUInt16, + TypeUInt32, + TypeUInt64, + TypePointer, + TypeULEB128, + TypeSLEB128 + }; + 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/gnu/llvm/lldb/tools/debugserver/source/DNBDefs.h b/gnu/llvm/lldb/tools/debugserver/source/DNBDefs.h new file mode 100644 index 00000000000..22cfce1757f --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBDefs.h @@ -0,0 +1,363 @@ +//===-- DNBDefs.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBDefs_h__ +#define __DNBDefs_h__ + +#include <signal.h> +#include <stdint.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) + +enum nub_state_t { + eStateInvalid = 0, + eStateUnloaded, + eStateAttaching, + eStateLaunching, + eStateStopped, + eStateRunning, + eStateStepping, + eStateCrashed, + eStateDetached, + eStateExited, + eStateSuspended +}; + +enum nub_launch_flavor_t { + eLaunchFlavorDefault = 0, + eLaunchFlavorPosixSpawn = 1, + eLaunchFlavorForkExec = 2, +#ifdef WITH_SPRINGBOARD + eLaunchFlavorSpringBoard = 3, +#endif +#ifdef WITH_BKS + eLaunchFlavorBKS = 4, +#endif +#ifdef WITH_FBS + eLaunchFlavorFBS = 5 +#endif +}; + +#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_DARWIN_LOG (1u << 14) +#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[64]; + int16_t v_sint16[32]; + int32_t v_sint32[16]; + int64_t v_sint64[8]; + uint8_t v_uint8[64]; + uint16_t v_uint16[32]; + uint32_t v_uint32[16]; + uint64_t v_uint64[8]; + float v_float32[16]; + double v_float64[8]; + 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), + eProfileMemoryAnonymous = + (1 << 8), // Assume eProfileMemory, get Anonymous memory as well. + + eProfileEnergy = (1 << 9), + eProfileEnergyCPUCap = (1 << 10), + + eProfileMemoryCap = (1 << 15), + + 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/gnu/llvm/lldb/tools/debugserver/source/DNBError.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBError.cpp new file mode 100644 index 00000000000..00933bce1a7 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBError.cpp @@ -0,0 +1,115 @@ +//===-- DNBError.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/DNBError.h b/gnu/llvm/lldb/tools/debugserver/source/DNBError.h new file mode 100644 index 00000000000..7e67cf2c553 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBError.h @@ -0,0 +1,97 @@ +//===-- DNBError.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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; + enum FlavorType { + Generic = 0, + MachKernel = 1, + POSIX = 2 +#ifdef WITH_SPRINGBOARD + , + SpringBoard = 3 +#endif +#ifdef WITH_BKS + , + BackBoard = 4 +#endif +#ifdef WITH_FBS + , + FrontBoard = 5 +#endif + }; + + 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 Status() 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/gnu/llvm/lldb/tools/debugserver/source/DNBLog.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.cpp new file mode 100644 index 00000000000..11b2d0a04b7 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.cpp @@ -0,0 +1,268 @@ +//===-- DNBLog.cpp ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 "PThreadMutex.h" +#include <mach/mach.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <unistd.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/gnu/llvm/lldb/tools/debugserver/source/DNBLog.h b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.h new file mode 100644 index 00000000000..47d8aeb066e --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.h @@ -0,0 +1,152 @@ +//===-- DNBLog.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBLog_h__ +#define __DNBLog_h__ + +#include "DNBDefs.h" +#include <stdint.h> +#include <stdio.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/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.cpp new file mode 100644 index 00000000000..c224fc7056d --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.cpp @@ -0,0 +1,251 @@ +//===-- DNBRegisterInfo.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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) { \ + strlcpy(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: + strlcpy(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); + [[clang::fallthrough]]; + 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/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.h b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.h new file mode 100644 index 00000000000..3aa0e03eaa1 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.h @@ -0,0 +1,29 @@ +//===-- DNBRegisterInfo.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 8/3/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBRegisterInfo_h__ +#define __DNBRegisterInfo_h__ + +#include "DNBDefs.h" +#include <stdint.h> +#include <stdio.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/gnu/llvm/lldb/tools/debugserver/source/DNBRuntimeAction.h b/gnu/llvm/lldb/tools/debugserver/source/DNBRuntimeAction.h new file mode 100644 index 00000000000..3f0d6021aea --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBRuntimeAction.h @@ -0,0 +1,23 @@ +//===-- DNBRuntimeAction.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp new file mode 100644 index 00000000000..cb747eeeaa1 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp @@ -0,0 +1,88 @@ +//===-- DNBThreadResumeActions.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.h b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.h new file mode 100644 index 00000000000..e2a25abca2a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.h @@ -0,0 +1,65 @@ +//===-- DNBThreadResumeActions.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/DNBTimer.h b/gnu/llvm/lldb/tools/debugserver/source/DNBTimer.h new file mode 100644 index 00000000000..21ee2351d69 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/DNBTimer.h @@ -0,0 +1,134 @@ +//===-- DNBTimer.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/13/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBTimer_h__ +#define __DNBTimer_h__ + +#include "DNBDefs.h" +#include "PThreadMutex.h" +#include <memory> +#include <stdint.h> +#include <sys/time.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/gnu/llvm/lldb/tools/debugserver/source/JSON.cpp b/gnu/llvm/lldb/tools/debugserver/source/JSON.cpp new file mode 100644 index 00000000000..12d96d4ed4d --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/JSON.cpp @@ -0,0 +1,592 @@ +//===--------------------- JSON.cpp -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "JSON.h" + +// C includes +#include <assert.h> +#include <limits.h> + +// C++ includes +#include "lldb/Host/StringConvert.h" +#include <iomanip> +#include <sstream> + +using namespace lldb_private; + +std::string JSONString::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; +} + +JSONString::JSONString() : JSONValue(JSONValue::Kind::String), m_data() {} + +JSONString::JSONString(const char *s) + : JSONValue(JSONValue::Kind::String), m_data(s ? s : "") {} + +JSONString::JSONString(const std::string &s) + : JSONValue(JSONValue::Kind::String), m_data(s) {} + +void JSONString::Write(std::ostream &s) { + s << "\"" << json_string_quote_metachars(m_data).c_str() << "\""; +} + +uint64_t JSONNumber::GetAsUnsigned() const { + switch (m_data_type) { + case DataType::Unsigned: + return m_data.m_unsigned; + case DataType::Signed: + return (uint64_t)m_data.m_signed; + case DataType::Double: + return (uint64_t)m_data.m_double; + } +} + +int64_t JSONNumber::GetAsSigned() const { + switch (m_data_type) { + case DataType::Unsigned: + return (int64_t)m_data.m_unsigned; + case DataType::Signed: + return m_data.m_signed; + case DataType::Double: + return (int64_t)m_data.m_double; + } +} + +double JSONNumber::GetAsDouble() const { + switch (m_data_type) { + case DataType::Unsigned: + return (double)m_data.m_unsigned; + case DataType::Signed: + return (double)m_data.m_signed; + case DataType::Double: + return m_data.m_double; + } +} + +void JSONNumber::Write(std::ostream &s) { + switch (m_data_type) { + case DataType::Unsigned: + s << m_data.m_unsigned; + break; + case DataType::Signed: + s << m_data.m_signed; + break; + case DataType::Double: + // Set max precision to emulate %g. + s << std::setprecision(std::numeric_limits<double>::digits10 + 1); + s << m_data.m_double; + break; + } +} + +JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {} + +void JSONTrue::Write(std::ostream &s) { s << "true"; } + +JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {} + +void JSONFalse::Write(std::ostream &s) { s << "false"; } + +JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {} + +void JSONNull::Write(std::ostream &s) { s << "null"; } + +JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {} + +void JSONObject::Write(std::ostream &s) { + bool first = true; + s << '{'; + auto iter = m_elements.begin(), end = m_elements.end(); + for (; iter != end; iter++) { + if (first) + first = false; + else + s << ','; + JSONString key(iter->first); + JSONValue::SP value(iter->second); + key.Write(s); + s << ':'; + value->Write(s); + } + s << '}'; +} + +bool JSONObject::SetObject(const std::string &key, JSONValue::SP value) { + if (key.empty() || nullptr == value.get()) + return false; + m_elements[key] = value; + return true; +} + +JSONValue::SP JSONObject::GetObject(const std::string &key) const { + auto iter = m_elements.find(key), end = m_elements.end(); + if (iter == end) + return JSONValue::SP(); + return iter->second; +} + +bool JSONObject::GetObjectAsBool(const std::string &key, bool &value) const { + auto value_sp = GetObject(key); + if (!value_sp) { + // The given key doesn't exist, so we have no value. + return false; + } + + if (JSONTrue::classof(value_sp.get())) { + // We have the value, and it is true. + value = true; + return true; + } else if (JSONFalse::classof(value_sp.get())) { + // We have the value, and it is false. + value = false; + return true; + } else { + // We don't have a valid bool value for the given key. + return false; + } +} + +bool JSONObject::GetObjectAsString(const std::string &key, + std::string &value) const { + auto value_sp = GetObject(key); + if (!value_sp) { + // The given key doesn't exist, so we have no value. + return false; + } + + if (!JSONString::classof(value_sp.get())) + return false; + + value = static_cast<JSONString *>(value_sp.get())->GetData(); + return true; +} + +JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {} + +void JSONArray::Write(std::ostream &s) { + bool first = true; + s << '['; + auto iter = m_elements.begin(), end = m_elements.end(); + for (; iter != end; iter++) { + if (first) + first = false; + else + s << ','; + (*iter)->Write(s); + } + s << ']'; +} + +bool JSONArray::SetObject(Index i, JSONValue::SP value) { + if (value.get() == nullptr) + return false; + if (i < m_elements.size()) { + m_elements[i] = value; + return true; + } + if (i == m_elements.size()) { + m_elements.push_back(value); + return true; + } + return false; +} + +bool JSONArray::AppendObject(JSONValue::SP value) { + if (value.get() == nullptr) + return false; + m_elements.push_back(value); + return true; +} + +JSONValue::SP JSONArray::GetObject(Index i) { + if (i < m_elements.size()) + return m_elements[i]; + return JSONValue::SP(); +} + +JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); } + +JSONParser::JSONParser(const char *cstr) : StdStringExtractor(cstr) {} + +JSONParser::Token JSONParser::GetToken(std::string &value) { + std::ostringstream error; + + value.clear(); + SkipSpaces(); + const uint64_t start_index = m_index; + const char ch = GetChar(); + switch (ch) { + case '{': + return Token::ObjectStart; + case '}': + return Token::ObjectEnd; + case '[': + return Token::ArrayStart; + case ']': + return Token::ArrayEnd; + case ',': + return Token::Comma; + case ':': + return Token::Colon; + case '\0': + return Token::EndOfFile; + case 't': + if (GetChar() == 'r') + if (GetChar() == 'u') + if (GetChar() == 'e') + return Token::True; + break; + + case 'f': + if (GetChar() == 'a') + if (GetChar() == 'l') + if (GetChar() == 's') + if (GetChar() == 'e') + return Token::False; + break; + + case 'n': + if (GetChar() == 'u') + if (GetChar() == 'l') + if (GetChar() == 'l') + return Token::Null; + break; + + case '"': { + while (true) { + bool was_escaped = false; + int escaped_ch = GetEscapedChar(was_escaped); + if (escaped_ch == -1) { + error << "error: an error occurred getting a character from offset " + << start_index; + value = error.str(); + return Token::Status; + + } else { + const bool is_end_quote = escaped_ch == '"'; + const bool is_null = escaped_ch == 0; + if (was_escaped || (!is_end_quote && !is_null)) { + if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX) { + value.append(1, (char)escaped_ch); + } else { + error << "error: wide character support is needed for unicode " + "character 0x" + << std::setprecision(4) << std::hex << escaped_ch; + error << " at offset " << start_index; + value = error.str(); + return Token::Status; + } + } else if (is_end_quote) { + return Token::String; + } else if (is_null) { + value = "error: missing end quote for string"; + return Token::Status; + } + } + } + } break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + bool done = false; + bool got_decimal_point = false; + uint64_t exp_index = 0; + bool got_int_digits = (ch >= '0') && (ch <= '9'); + bool got_frac_digits = false; + bool got_exp_digits = false; + while (!done) { + const char next_ch = PeekChar(); + switch (next_ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (exp_index != 0) { + got_exp_digits = true; + } else if (got_decimal_point) { + got_frac_digits = true; + } else { + got_int_digits = true; + } + ++m_index; // Skip this character + break; + + case '.': + if (got_decimal_point) { + error << "error: extra decimal point found at offset " << start_index; + value = error.str(); + return Token::Status; + } else { + got_decimal_point = true; + ++m_index; // Skip this character + } + break; + + case 'e': + case 'E': + if (exp_index != 0) { + error << "error: extra exponent character found at offset " + << start_index; + value = error.str(); + return Token::Status; + } else { + exp_index = m_index; + ++m_index; // Skip this character + } + break; + + case '+': + case '-': + // The '+' and '-' can only come after an exponent character... + if (exp_index == m_index - 1) { + ++m_index; // Skip the exponent sign character + } else { + error << "error: unexpected " << next_ch << " character at offset " + << start_index; + value = error.str(); + return Token::Status; + } + break; + + default: + done = true; + break; + } + } + + if (m_index > start_index) { + value = m_packet.substr(start_index, m_index - start_index); + if (got_decimal_point) { + if (exp_index != 0) { + // We have an exponent, make sure we got exponent digits + if (got_exp_digits) { + return Token::Float; + } else { + error << "error: got exponent character but no exponent digits at " + "offset in float value \"" + << value.c_str() << "\""; + value = error.str(); + return Token::Status; + } + } else { + // No exponent, but we need at least one decimal after the decimal + // point + if (got_frac_digits) { + return Token::Float; + } else { + error << "error: no digits after decimal point \"" << value.c_str() + << "\""; + value = error.str(); + return Token::Status; + } + } + } else { + // No decimal point + if (got_int_digits) { + // We need at least some integer digits to make an integer + return Token::Integer; + } else { + error << "error: no digits negate sign \"" << value.c_str() << "\""; + value = error.str(); + return Token::Status; + } + } + } else { + error << "error: invalid number found at offset " << start_index; + value = error.str(); + return Token::Status; + } + } break; + default: + break; + } + error << "error: failed to parse token at offset " << start_index + << " (around character '" << ch << "')"; + value = error.str(); + return Token::Status; +} + +int JSONParser::GetEscapedChar(bool &was_escaped) { + was_escaped = false; + const char ch = GetChar(); + if (ch == '\\') { + was_escaped = true; + const char ch2 = GetChar(); + switch (ch2) { + case '"': + case '\\': + case '/': + default: + break; + + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'u': { + const int hi_byte = DecodeHexU8(); + const int lo_byte = DecodeHexU8(); + if (hi_byte >= 0 && lo_byte >= 0) + return hi_byte << 8 | lo_byte; + return -1; + } break; + } + return ch2; + } + return ch; +} + +JSONValue::SP JSONParser::ParseJSONObject() { + // The "JSONParser::Token::ObjectStart" token should have already been + // consumed + // by the time this function is called + std::unique_ptr<JSONObject> dict_up(new JSONObject()); + + std::string value; + std::string key; + while (true) { + JSONParser::Token token = GetToken(value); + + if (token == JSONParser::Token::String) { + key.swap(value); + token = GetToken(value); + if (token == JSONParser::Token::Colon) { + JSONValue::SP value_sp = ParseJSONValue(); + if (value_sp) + dict_up->SetObject(key, value_sp); + else + break; + } + } else if (token == JSONParser::Token::ObjectEnd) { + return JSONValue::SP(dict_up.release()); + } else if (token == JSONParser::Token::Comma) { + continue; + } else { + break; + } + } + return JSONValue::SP(); +} + +JSONValue::SP JSONParser::ParseJSONArray() { + // The "JSONParser::Token::ObjectStart" token should have already been + // consumed + // by the time this function is called + std::unique_ptr<JSONArray> array_up(new JSONArray()); + + std::string value; + std::string key; + while (true) { + JSONParser::Token token = GetToken(value); + if (token == JSONParser::Token::ArrayEnd) + return JSONValue::SP(array_up.release()); + JSONValue::SP value_sp = ParseJSONValue(value, token); + if (value_sp) + array_up->AppendObject(value_sp); + else + break; + + token = GetToken(value); + if (token == JSONParser::Token::Comma) { + continue; + } else if (token == JSONParser::Token::ArrayEnd) { + return JSONValue::SP(array_up.release()); + } else { + break; + } + } + return JSONValue::SP(); +} + +JSONValue::SP JSONParser::ParseJSONValue() { + std::string value; + const JSONParser::Token token = GetToken(value); + return ParseJSONValue(value, token); +} + +JSONValue::SP JSONParser::ParseJSONValue(const std::string &value, + const Token &token) { + switch (token) { + case JSONParser::Token::ObjectStart: + return ParseJSONObject(); + + case JSONParser::Token::ArrayStart: + return ParseJSONArray(); + + case JSONParser::Token::Integer: { + if (value.front() == '-') { + bool success = false; + int64_t sval = StringConvert::ToSInt64(value.c_str(), 0, 0, &success); + if (success) + return JSONValue::SP(new JSONNumber(sval)); + } else { + bool success = false; + uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success); + if (success) + return JSONValue::SP(new JSONNumber(uval)); + } + } break; + + case JSONParser::Token::Float: { + bool success = false; + double val = StringConvert::ToDouble(value.c_str(), 0.0, &success); + if (success) + return JSONValue::SP(new JSONNumber(val)); + } break; + + case JSONParser::Token::String: + return JSONValue::SP(new JSONString(value)); + + case JSONParser::Token::True: + return JSONValue::SP(new JSONTrue()); + + case JSONParser::Token::False: + return JSONValue::SP(new JSONFalse()); + + case JSONParser::Token::Null: + return JSONValue::SP(new JSONNull()); + + default: + break; + } + return JSONValue::SP(); +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/JSON.h b/gnu/llvm/lldb/tools/debugserver/source/JSON.h new file mode 100644 index 00000000000..70bfdd7259a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/JSON.h @@ -0,0 +1,302 @@ +//===---------------------JSON.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef utility_JSON_h_ +#define utility_JSON_h_ + +#include "StdStringExtractor.h" + +// C includes +#include <inttypes.h> +#include <stdint.h> + +// C++ includes +#include <map> +#include <memory> +#include <ostream> +#include <string> +#include <vector> + +class JSONValue { +public: + virtual void Write(std::ostream &s) = 0; + + typedef std::shared_ptr<JSONValue> SP; + + enum class Kind { String, Number, True, False, Null, Object, Array }; + + JSONValue(Kind k) : m_kind(k) {} + + Kind GetKind() const { return m_kind; } + + virtual ~JSONValue() = default; + +private: + const Kind m_kind; +}; + +class JSONString : public JSONValue { +public: + JSONString(); + JSONString(const char *s); + JSONString(const std::string &s); + + JSONString(const JSONString &s) = delete; + JSONString &operator=(const JSONString &s) = delete; + + void Write(std::ostream &s) override; + + typedef std::shared_ptr<JSONString> SP; + + std::string GetData() { return m_data; } + + static bool classof(const JSONValue *V) { + return V->GetKind() == JSONValue::Kind::String; + } + + ~JSONString() override = default; + +private: + static std::string json_string_quote_metachars(const std::string &); + + std::string m_data; +}; + +class JSONNumber : public JSONValue { +public: + typedef std::shared_ptr<JSONNumber> SP; + + // We cretae a constructor for all integer and floating point type with using + // templates and + // SFINAE to avoid having ambiguous overloads because of the implicit type + // promotion. If we + // would have constructors only with int64_t, uint64_t and double types then + // constructing a + // JSONNumber from an int32_t (or any other similar type) would fail to + // compile. + + template <typename T, typename std::enable_if< + std::is_integral<T>::value && + std::is_unsigned<T>::value>::type * = nullptr> + explicit JSONNumber(T u) + : JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Unsigned) { + m_data.m_unsigned = u; + } + + template <typename T, + typename std::enable_if<std::is_integral<T>::value && + std::is_signed<T>::value>::type * = nullptr> + explicit JSONNumber(T s) + : JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Signed) { + m_data.m_signed = s; + } + + template <typename T, typename std::enable_if< + std::is_floating_point<T>::value>::type * = nullptr> + explicit JSONNumber(T d) + : JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Double) { + m_data.m_double = d; + } + + ~JSONNumber() override = default; + + JSONNumber(const JSONNumber &s) = delete; + JSONNumber &operator=(const JSONNumber &s) = delete; + + void Write(std::ostream &s) override; + + uint64_t GetAsUnsigned() const; + + int64_t GetAsSigned() const; + + double GetAsDouble() const; + + static bool classof(const JSONValue *V) { + return V->GetKind() == JSONValue::Kind::Number; + } + +private: + enum class DataType : uint8_t { Unsigned, Signed, Double } m_data_type; + + union { + uint64_t m_unsigned; + int64_t m_signed; + double m_double; + } m_data; +}; + +class JSONTrue : public JSONValue { +public: + JSONTrue(); + + JSONTrue(const JSONTrue &s) = delete; + JSONTrue &operator=(const JSONTrue &s) = delete; + + void Write(std::ostream &s) override; + + typedef std::shared_ptr<JSONTrue> SP; + + static bool classof(const JSONValue *V) { + return V->GetKind() == JSONValue::Kind::True; + } + + ~JSONTrue() override = default; +}; + +class JSONFalse : public JSONValue { +public: + JSONFalse(); + + JSONFalse(const JSONFalse &s) = delete; + JSONFalse &operator=(const JSONFalse &s) = delete; + + void Write(std::ostream &s) override; + + typedef std::shared_ptr<JSONFalse> SP; + + static bool classof(const JSONValue *V) { + return V->GetKind() == JSONValue::Kind::False; + } + + ~JSONFalse() override = default; +}; + +class JSONNull : public JSONValue { +public: + JSONNull(); + + JSONNull(const JSONNull &s) = delete; + JSONNull &operator=(const JSONNull &s) = delete; + + void Write(std::ostream &s) override; + + typedef std::shared_ptr<JSONNull> SP; + + static bool classof(const JSONValue *V) { + return V->GetKind() == JSONValue::Kind::Null; + } + + ~JSONNull() override = default; +}; + +class JSONObject : public JSONValue { +public: + JSONObject(); + + JSONObject(const JSONObject &s) = delete; + JSONObject &operator=(const JSONObject &s) = delete; + + void Write(std::ostream &s) override; + + typedef std::shared_ptr<JSONObject> SP; + + static bool classof(const JSONValue *V) { + return V->GetKind() == JSONValue::Kind::Object; + } + + bool SetObject(const std::string &key, JSONValue::SP value); + + JSONValue::SP GetObject(const std::string &key) const; + + /// Return keyed value as bool + /// + /// \param[in] key + /// The value of the key to lookup + /// + /// \param[out] value + /// The value of the key as a bool. Undefined if the key doesn't + /// exist or if the key is not either true or false. + /// + /// \return + /// true if the key existed as was a bool value; false otherwise. + /// Note the return value is *not* the value of the bool, use + /// \b value for that. + bool GetObjectAsBool(const std::string &key, bool &value) const; + + bool GetObjectAsString(const std::string &key, std::string &value) const; + + ~JSONObject() override = default; + +private: + typedef std::map<std::string, JSONValue::SP> Map; + typedef Map::iterator Iterator; + Map m_elements; +}; + +class JSONArray : public JSONValue { +public: + JSONArray(); + + JSONArray(const JSONArray &s) = delete; + JSONArray &operator=(const JSONArray &s) = delete; + + void Write(std::ostream &s) override; + + typedef std::shared_ptr<JSONArray> SP; + + static bool classof(const JSONValue *V) { + return V->GetKind() == JSONValue::Kind::Array; + } + +private: + typedef std::vector<JSONValue::SP> Vector; + typedef Vector::iterator Iterator; + typedef Vector::size_type Index; + typedef Vector::size_type Size; + +public: + bool SetObject(Index i, JSONValue::SP value); + + bool AppendObject(JSONValue::SP value); + + JSONValue::SP GetObject(Index i); + + Size GetNumElements(); + + ~JSONArray() override = default; + + Vector m_elements; +}; + +class JSONParser : public StdStringExtractor { +public: + enum Token { + Invalid, + Status, + ObjectStart, + ObjectEnd, + ArrayStart, + ArrayEnd, + Comma, + Colon, + String, + Integer, + Float, + True, + False, + Null, + EndOfFile + }; + + JSONParser(const char *cstr); + + int GetEscapedChar(bool &was_escaped); + + Token GetToken(std::string &value); + + JSONValue::SP ParseJSONValue(); + +protected: + JSONValue::SP ParseJSONValue(const std::string &value, const Token &token); + + JSONValue::SP ParseJSONObject(); + + JSONValue::SP ParseJSONArray(); +}; + +#endif // utility_JSON_h_ diff --git a/gnu/llvm/lldb/tools/debugserver/source/JSONGenerator.h b/gnu/llvm/lldb/tools/debugserver/source/JSONGenerator.h new file mode 100644 index 00000000000..70708f9a5ac --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/JSONGenerator.h @@ -0,0 +1,314 @@ +//===-- JSONGenerator.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __JSONGenerator_h_ +#define __JSONGenerator_h_ + + +#include <iomanip> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +/// \class JSONGenerator JSONGenerator.h +/// 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) + 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) { + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp new file mode 100644 index 00000000000..6971c1f4c32 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp @@ -0,0 +1,69 @@ +//===-- CFBundle.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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) { + if (this != &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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.h new file mode 100644 index 00000000000..f49dc30f1f8 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.h @@ -0,0 +1,35 @@ +//===-- CFBundle.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.cpp new file mode 100644 index 00000000000..637ba65ae4d --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.cpp @@ -0,0 +1,154 @@ +//===-- CFString.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/16/08. +// +//===----------------------------------------------------------------------===// + +#include "CFString.h" +#include <glob.h> +#include <string> + +// 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.h new file mode 100644 index 00000000000..d1bd5682689 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.h @@ -0,0 +1,40 @@ +//===-- CFString.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFUtils.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFUtils.h new file mode 100644 index 00000000000..b567524ce63 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFUtils.h @@ -0,0 +1,75 @@ +//===-- CFUtils.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 ©) : _ptr(copy.get()) { + if (get()) + ::CFRetain(get()); + } + virtual ~CFReleaser() { reset(); } + + // Assignments + CFReleaser &operator=(const CFReleaser<T> ©) { + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt new file mode 100644 index 00000000000..73ba6492a0e --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt @@ -0,0 +1,43 @@ +# The debugserver build needs to conditionally include files depending on the +# target architecture. +# +# Switch on the architecture specified by TARGET_TRIPLE, as +# the llvm and swift build systems use this variable to identify the +# target (through LLVM_HOST_TRIPLE). +# +# It would be possible to switch on CMAKE_OSX_ARCHITECTURES, but the swift +# build does not provide it, preferring instead to pass arch-specific +# CFLAGS etc explicitly. Switching on LLVM_HOST_TRIPLE is also an option, +# but it breaks down when cross-compiling. + +if(TARGET_TRIPLE) + string(REGEX MATCH "^[^-]*" LLDB_DEBUGSERVER_ARCH ${TARGET_TRIPLE}) +else() + set(LLDB_DEBUGSERVER_ARCH ${CMAKE_OSX_ARCHITECTURES}) +endif() + +if("${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*arm.*") + list(APPEND SOURCES arm/DNBArchImpl.cpp arm64/DNBArchImplARM64.cpp) + include_directories(${CURRENT_SOURCE_DIR}/arm ${CURRENT_SOURCE_DIR}/arm64) +endif() + +if(NOT LLDB_DEBUGSERVER_ARCH OR "${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*86.*") + list(APPEND SOURCES i386/DNBArchImplI386.cpp x86_64/DNBArchImplX86_64.cpp) + include_directories(${CURRENT_SOURCE_DIR}/i386 ${CURRENT_SOURCE_DIR}/x86_64) +endif() + +if("${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*ppc.*") + list(APPEND SOURCES ppc/DNBArchImpl.cpp) + include_directories(${CURRENT_SOURCE_DIR}/ppc) +endif() + +add_subdirectory(DarwinLog) + +include_directories(..) + +include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source) +add_library(lldbDebugserverArchSupport + ${SOURCES} + ) + +set_target_properties(lldbDebugserverArchSupport PROPERTIES FOLDER "lldb libraries/debugserver") diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp new file mode 100644 index 00000000000..2d831252bfe --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp @@ -0,0 +1,13 @@ +//===-- ActivityStore.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ActivityStore.h" + +ActivityStore::ActivityStore() {} + +ActivityStore::~ActivityStore() {} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h new file mode 100644 index 00000000000..b66a789592c --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h @@ -0,0 +1,29 @@ +//===-- ActivityStore.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef ActivityStore_h +#define ActivityStore_h + +#include <string> + +#include "ActivityStreamSPI.h" + +class ActivityStore { +public: + virtual ~ActivityStore(); + + virtual const char *GetActivityForID(os_activity_id_t activity_id) const = 0; + + virtual std::string + GetActivityChainForID(os_activity_id_t activity_id) const = 0; + +protected: + ActivityStore(); +}; + +#endif /* ActivityStore_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h new file mode 100644 index 00000000000..99721f69a2d --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h @@ -0,0 +1,190 @@ +//===-- ActivityStreamSPI.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef ActivityStreamSPI_h +#define ActivityStreamSPI_h + +#include <sys/time.h> +#include <xpc/xpc.h> + +#define OS_ACTIVITY_MAX_CALLSTACK 32 + +// Enums + +enum { + OS_ACTIVITY_STREAM_PROCESS_ONLY = 0x00000001, + OS_ACTIVITY_STREAM_SKIP_DECODE = 0x00000002, + OS_ACTIVITY_STREAM_PAYLOAD = 0x00000004, + OS_ACTIVITY_STREAM_HISTORICAL = 0x00000008, + OS_ACTIVITY_STREAM_CALLSTACK = 0x00000010, + OS_ACTIVITY_STREAM_DEBUG = 0x00000020, + OS_ACTIVITY_STREAM_BUFFERED = 0x00000040, + OS_ACTIVITY_STREAM_NO_SENSITIVE = 0x00000080, + OS_ACTIVITY_STREAM_INFO = 0x00000100, + OS_ACTIVITY_STREAM_PROMISCUOUS = 0x00000200, + OS_ACTIVITY_STREAM_PRECISE_TIMESTAMPS = 0x00000200 +}; +typedef uint32_t os_activity_stream_flag_t; + +enum { + OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE = 0x0201, + OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION = 0x0202, + OS_ACTIVITY_STREAM_TYPE_ACTIVITY_USERACTION = 0x0203, + + OS_ACTIVITY_STREAM_TYPE_TRACE_MESSAGE = 0x0300, + + OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE = 0x0400, + OS_ACTIVITY_STREAM_TYPE_LEGACY_LOG_MESSAGE = 0x0480, + + OS_ACTIVITY_STREAM_TYPE_SIGNPOST_BEGIN = 0x0601, + OS_ACTIVITY_STREAM_TYPE_SIGNPOST_END = 0x0602, + OS_ACTIVITY_STREAM_TYPE_SIGNPOST_EVENT = 0x0603, + + OS_ACTIVITY_STREAM_TYPE_STATEDUMP_EVENT = 0x0A00, +}; +typedef uint32_t os_activity_stream_type_t; + +enum { + OS_ACTIVITY_STREAM_EVENT_STARTED = 1, + OS_ACTIVITY_STREAM_EVENT_STOPPED = 2, + OS_ACTIVITY_STREAM_EVENT_FAILED = 3, + OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED = 4, + OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED = 5, +}; +typedef uint32_t os_activity_stream_event_t; + +// Types + +typedef uint64_t os_activity_id_t; +typedef struct os_activity_stream_s *os_activity_stream_t; +typedef struct os_activity_stream_entry_s *os_activity_stream_entry_t; + +#define OS_ACTIVITY_STREAM_COMMON() \ + uint64_t trace_id; \ + uint64_t timestamp; \ + uint64_t thread; \ + const uint8_t *image_uuid; \ + const char *image_path; \ + struct timeval tv_gmt; \ + struct timezone tz; \ + uint32_t offset + +typedef struct os_activity_stream_common_s { + OS_ACTIVITY_STREAM_COMMON(); +} * os_activity_stream_common_t; + +struct os_activity_create_s { + OS_ACTIVITY_STREAM_COMMON(); + const char *name; + os_activity_id_t creator_aid; + uint64_t unique_pid; +}; + +struct os_activity_transition_s { + OS_ACTIVITY_STREAM_COMMON(); + os_activity_id_t transition_id; +}; + +typedef struct os_log_message_s { + OS_ACTIVITY_STREAM_COMMON(); + const char *format; + const uint8_t *buffer; + size_t buffer_sz; + const uint8_t *privdata; + size_t privdata_sz; + const char *subsystem; + const char *category; + uint32_t oversize_id; + uint8_t ttl; + bool persisted; +} * os_log_message_t; + +typedef struct os_trace_message_v2_s { + OS_ACTIVITY_STREAM_COMMON(); + const char *format; + const void *buffer; + size_t bufferLen; + xpc_object_t __unsafe_unretained payload; +} * os_trace_message_v2_t; + +typedef struct os_activity_useraction_s { + OS_ACTIVITY_STREAM_COMMON(); + const char *action; + bool persisted; +} * os_activity_useraction_t; + +typedef struct os_signpost_s { + OS_ACTIVITY_STREAM_COMMON(); + const char *format; + const uint8_t *buffer; + size_t buffer_sz; + const uint8_t *privdata; + size_t privdata_sz; + const char *subsystem; + const char *category; + uint64_t duration_nsec; + uint32_t callstack_depth; + uint64_t callstack[OS_ACTIVITY_MAX_CALLSTACK]; +} * os_signpost_t; + +typedef struct os_activity_statedump_s { + OS_ACTIVITY_STREAM_COMMON(); + char *message; + size_t message_size; + char image_path_buffer[PATH_MAX]; +} * os_activity_statedump_t; + +struct os_activity_stream_entry_s { + os_activity_stream_type_t type; + + // information about the process streaming the data + pid_t pid; + uint64_t proc_id; + const uint8_t *proc_imageuuid; + const char *proc_imagepath; + + // the activity associated with this streamed event + os_activity_id_t activity_id; + os_activity_id_t parent_id; + + union { + struct os_activity_stream_common_s common; + struct os_activity_create_s activity_create; + struct os_activity_transition_s activity_transition; + struct os_log_message_s log_message; + struct os_trace_message_v2_s trace_message; + struct os_activity_useraction_s useraction; + struct os_signpost_s signpost; + struct os_activity_statedump_s statedump; + }; +}; + +// Blocks + +typedef bool (^os_activity_stream_block_t)(os_activity_stream_entry_t entry, + int error); + +typedef void (^os_activity_stream_event_block_t)( + os_activity_stream_t stream, os_activity_stream_event_t event); + +// SPI entry point prototypes + +typedef os_activity_stream_t (*os_activity_stream_for_pid_t)( + pid_t pid, os_activity_stream_flag_t flags, + os_activity_stream_block_t stream_block); + +typedef void (*os_activity_stream_resume_t)(os_activity_stream_t stream); + +typedef void (*os_activity_stream_cancel_t)(os_activity_stream_t stream); + +typedef char *(*os_log_copy_formatted_message_t)(os_log_message_t log_message); + +typedef void (*os_activity_stream_set_event_handler_t)( + os_activity_stream_t stream, os_activity_stream_event_block_t block); + +#endif /* ActivityStreamSPI_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt new file mode 100644 index 00000000000..71abb36358a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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(lldbDebugserverDarwin_DarwinLog + ActivityStore.cpp + DarwinLogCollector.cpp + LogFilter.cpp + LogFilterChain.cpp + LogFilterExactMatch.cpp + LogFilterRegex.cpp + LogMessage.cpp + LogMessageOsLog.cpp + ) + +set_target_properties(lldbDebugserverDarwin_DarwinLog PROPERTIES FOLDER "lldb libraries/debugserver") diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp new file mode 100644 index 00000000000..a9f8956b8d4 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp @@ -0,0 +1,699 @@ +//===-- DarwinLogCollector.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DarwinLogCollector.h" +#include "ActivityStreamSPI.h" + +#include <dlfcn.h> + +#include <cinttypes> +#include <memory> +#include <mutex> +#include <vector> + +#include "DNB.h" +#include "DNBLog.h" +#include "DarwinLogTypes.h" +#include "LogFilterChain.h" +#include "LogFilterExactMatch.h" +#include "LogFilterRegex.h" +#include "LogMessageOsLog.h" +#include "MachProcess.h" +#include "RNBContext.h" +#include "RNBDefs.h" +#include "RNBRemote.h" + +// Use an anonymous namespace for variables and methods that have no +// reason to leak out through the interface. +namespace { +/// Specify max depth that the activity parent-child chain will search +/// back to get the full activity chain name. If we do more than this, +/// we assume either we hit a loop or it's just too long. +static const size_t MAX_ACTIVITY_CHAIN_DEPTH = 10; + +// Used to tap into and retrieve logs from target process. +// (Consumer of os_log). +static os_activity_stream_for_pid_t s_os_activity_stream_for_pid; +static os_activity_stream_resume_t s_os_activity_stream_resume; +static os_activity_stream_cancel_t s_os_activity_stream_cancel; +static os_log_copy_formatted_message_t s_os_log_copy_formatted_message; +static os_activity_stream_set_event_handler_t + s_os_activity_stream_set_event_handler; + +bool LookupSPICalls() { + static std::once_flag s_once_flag; + static bool s_has_spi; + + std::call_once(s_once_flag, [] { + dlopen ("/System/Library/PrivateFrameworks/LoggingSupport.framework/LoggingSupport", RTLD_NOW); + s_os_activity_stream_for_pid = (os_activity_stream_for_pid_t)dlsym( + RTLD_DEFAULT, "os_activity_stream_for_pid"); + s_os_activity_stream_resume = (os_activity_stream_resume_t)dlsym( + RTLD_DEFAULT, "os_activity_stream_resume"); + s_os_activity_stream_cancel = (os_activity_stream_cancel_t)dlsym( + RTLD_DEFAULT, "os_activity_stream_cancel"); + s_os_log_copy_formatted_message = (os_log_copy_formatted_message_t)dlsym( + RTLD_DEFAULT, "os_log_copy_formatted_message"); + s_os_activity_stream_set_event_handler = + (os_activity_stream_set_event_handler_t)dlsym( + RTLD_DEFAULT, "os_activity_stream_set_event_handler"); + + // We'll indicate we're all set if every function entry point + // was found. + s_has_spi = (s_os_activity_stream_for_pid != nullptr) && + (s_os_activity_stream_resume != nullptr) && + (s_os_activity_stream_cancel != nullptr) && + (s_os_log_copy_formatted_message != nullptr) && + (s_os_activity_stream_set_event_handler != nullptr); + if (s_has_spi) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Found os_log SPI calls."); + // Tell LogMessageOsLog how to format messages when search + // criteria requires it. + LogMessageOsLog::SetFormatterFunction(s_os_log_copy_formatted_message); + } else { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Failed to find os_log SPI " + "calls."); + } + }); + + return s_has_spi; +} + +using Mutex = std::mutex; +static Mutex s_collector_mutex; +static std::vector<DarwinLogCollectorSP> s_collectors; + +static void TrackCollector(const DarwinLogCollectorSP &collector_sp) { + std::lock_guard<Mutex> locker(s_collector_mutex); + if (std::find(s_collectors.begin(), s_collectors.end(), collector_sp) != + s_collectors.end()) { + DNBLogThreadedIf(LOG_DARWIN_LOG, + "attempted to add same collector multiple times"); + return; + } + s_collectors.push_back(collector_sp); +} + +static void StopTrackingCollector(const DarwinLogCollectorSP &collector_sp) { + std::lock_guard<Mutex> locker(s_collector_mutex); + s_collectors.erase( + std::remove(s_collectors.begin(), s_collectors.end(), collector_sp), + s_collectors.end()); +} + +static DarwinLogCollectorSP FindCollectorForProcess(pid_t pid) { + std::lock_guard<Mutex> locker(s_collector_mutex); + for (const auto &collector_sp : s_collectors) { + if (collector_sp && (collector_sp->GetProcessID() == pid)) + return collector_sp; + } + return DarwinLogCollectorSP(); +} + +static FilterTarget TargetStringToEnum(const std::string &filter_target_name) { + if (filter_target_name == "activity") + return eFilterTargetActivity; + else if (filter_target_name == "activity-chain") + return eFilterTargetActivityChain; + else if (filter_target_name == "category") + return eFilterTargetCategory; + else if (filter_target_name == "message") + return eFilterTargetMessage; + else if (filter_target_name == "subsystem") + return eFilterTargetSubsystem; + else + return eFilterTargetInvalid; +} + +class Configuration { +public: + Configuration(const JSONObject &config) + : m_is_valid(false), + m_activity_stream_flags(OS_ACTIVITY_STREAM_PROCESS_ONLY), + m_filter_chain_sp(nullptr) { + // Parse out activity stream flags + if (!ParseSourceFlags(config)) { + m_is_valid = false; + return; + } + + // Parse filter rules + if (!ParseFilterRules(config)) { + m_is_valid = false; + return; + } + + // Everything worked. + m_is_valid = true; + } + + bool ParseSourceFlags(const JSONObject &config) { + // Get the source-flags dictionary. + auto source_flags_sp = config.GetObject("source-flags"); + if (!source_flags_sp) + return false; + if (!JSONObject::classof(source_flags_sp.get())) + return false; + + const JSONObject &source_flags = + *static_cast<JSONObject *>(source_flags_sp.get()); + + // Parse out the flags. + bool include_any_process = false; + bool include_callstacks = false; + bool include_info_level = false; + bool include_debug_level = false; + bool live_stream = false; + + if (!source_flags.GetObjectAsBool("any-process", include_any_process)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'any-process' missing from " + "configuration."); + return false; + } + if (!source_flags.GetObjectAsBool("callstacks", include_callstacks)) { + // We currently suppress the availability of this on the lldb + // side. We include here for devices when we enable in the + // future. + // DNBLogThreadedIf(LOG_DARWIN_LOG, + // "Source-flag 'callstacks' missing from " + // "configuration."); + + // OK. We just skip callstacks. + // return false; + } + if (!source_flags.GetObjectAsBool("info-level", include_info_level)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'info-level' missing from " + "configuration."); + return false; + } + if (!source_flags.GetObjectAsBool("debug-level", include_debug_level)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'debug-level' missing from " + "configuration."); + return false; + } + if (!source_flags.GetObjectAsBool("live-stream", live_stream)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'live-stream' missing from " + "configuration."); + return false; + } + + // Setup the SPI flags based on this. + m_activity_stream_flags = 0; + if (!include_any_process) + m_activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY; + if (include_callstacks) + m_activity_stream_flags |= OS_ACTIVITY_STREAM_CALLSTACK; + if (include_info_level) + m_activity_stream_flags |= OS_ACTIVITY_STREAM_INFO; + if (include_debug_level) + m_activity_stream_flags |= OS_ACTIVITY_STREAM_DEBUG; + if (!live_stream) + m_activity_stream_flags |= OS_ACTIVITY_STREAM_BUFFERED; + + DNBLogThreadedIf(LOG_DARWIN_LOG, "m_activity_stream_flags = 0x%03x", + m_activity_stream_flags); + + return true; + } + + bool ParseFilterRules(const JSONObject &config) { + // Retrieve the default rule. + bool filter_default_accept = true; + if (!config.GetObjectAsBool("filter-fall-through-accepts", + filter_default_accept)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Setting 'filter-fall-through-accepts' " + "missing from configuration."); + return false; + } + m_filter_chain_sp = std::make_shared<LogFilterChain>(filter_default_accept); + DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLog no-match rule: %s.", + filter_default_accept ? "accept" : "reject"); + + // If we don't have the filter-rules array, we're done. + auto filter_rules_sp = config.GetObject("filter-rules"); + if (!filter_rules_sp) { + DNBLogThreadedIf(LOG_DARWIN_LOG, + "No 'filter-rules' config element, all log " + "entries will use the no-match action (%s).", + filter_default_accept ? "accept" : "reject"); + return true; + } + if (!JSONArray::classof(filter_rules_sp.get())) + return false; + const JSONArray &rules_config = + *static_cast<JSONArray *>(filter_rules_sp.get()); + + // Create the filters. + for (auto &rule_sp : rules_config.m_elements) { + if (!JSONObject::classof(rule_sp.get())) + return false; + const JSONObject &rule_config = *static_cast<JSONObject *>(rule_sp.get()); + + // Get whether this filter accepts or rejects. + bool filter_accepts = true; + if (!rule_config.GetObjectAsBool("accept", filter_accepts)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'accept' element missing."); + return false; + } + + // Grab the target log field attribute for the match. + std::string target_attribute; + if (!rule_config.GetObjectAsString("attribute", target_attribute)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'attribute' element missing."); + return false; + } + auto target_enum = TargetStringToEnum(target_attribute); + if (target_enum == eFilterTargetInvalid) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter attribute '%s' unsupported.", + target_attribute.c_str()); + return false; + } + + // Handle operation-specific fields and filter creation. + std::string filter_type; + if (!rule_config.GetObjectAsString("type", filter_type)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'type' element missing."); + return false; + } + DNBLogThreadedIf(LOG_DARWIN_LOG, "Reading filter of type '%s'", + filter_type.c_str()); + + LogFilterSP filter_sp; + if (filter_type == "regex") { + // Grab the regex for the match. + std::string regex; + if (!rule_config.GetObjectAsString("regex", regex)) { + DNBLogError("Regex filter missing 'regex' element."); + return false; + } + DNBLogThreadedIf(LOG_DARWIN_LOG, "regex for filter: \"%s\"", + regex.c_str()); + + // Create the regex filter. + auto regex_filter = + new LogFilterRegex(filter_accepts, target_enum, regex); + filter_sp.reset(regex_filter); + + // Validate that the filter is okay. + if (!regex_filter->IsValid()) { + DNBLogError("Invalid regex in filter: " + "regex=\"%s\", error=%s", + regex.c_str(), regex_filter->GetErrorAsCString()); + return false; + } + } else if (filter_type == "match") { + // Grab the regex for the match. + std::string exact_text; + if (!rule_config.GetObjectAsString("exact_text", exact_text)) { + DNBLogError("Exact match filter missing " + "'exact_text' element."); + return false; + } + + // Create the filter. + filter_sp = std::make_shared<LogFilterExactMatch>( + filter_accepts, target_enum, exact_text); + } + + // Add the filter to the chain. + m_filter_chain_sp->AppendFilter(filter_sp); + } + return true; + } + + bool IsValid() const { return m_is_valid; } + + os_activity_stream_flag_t GetActivityStreamFlags() const { + return m_activity_stream_flags; + } + + const LogFilterChainSP &GetLogFilterChain() const { + return m_filter_chain_sp; + } + +private: + bool m_is_valid; + os_activity_stream_flag_t m_activity_stream_flags; + LogFilterChainSP m_filter_chain_sp; +}; +} + +bool DarwinLogCollector::IsSupported() { + // We're supported if we have successfully looked up the SPI entry points. + return LookupSPICalls(); +} + +bool DarwinLogCollector::StartCollectingForProcess(nub_process_t pid, + const JSONObject &config) { + // If we're currently collecting for this process, kill the existing + // collector. + if (CancelStreamForProcess(pid)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, + "%s() killed existing DarwinLog collector for pid %d.", + __FUNCTION__, pid); + } + + // If the process isn't alive, we're done. + if (!DNBProcessIsAlive(pid)) { + DNBLogThreadedIf(LOG_DARWIN_LOG, + "%s() cannot collect for pid %d: process not alive.", + __FUNCTION__, pid); + return false; + } + + // Validate the configuration. + auto spi_config = Configuration(config); + if (!spi_config.IsValid()) { + DNBLogThreadedIf(LOG_DARWIN_LOG, + "%s() invalid configuration, will not enable log " + "collection", + __FUNCTION__); + return false; + } + + // Create the stream collector that will manage collected data + // for this pid. + DarwinLogCollectorSP collector_sp( + new DarwinLogCollector(pid, spi_config.GetLogFilterChain())); + std::weak_ptr<DarwinLogCollector> collector_wp(collector_sp); + + // Setup the stream handling block. + os_activity_stream_block_t block = + ^bool(os_activity_stream_entry_t entry, int error) { + // Check if our collector is still alive. + DarwinLogCollectorSP inner_collector_sp = collector_wp.lock(); + if (!inner_collector_sp) + return false; + return inner_collector_sp->HandleStreamEntry(entry, error); + }; + + os_activity_stream_event_block_t stream_event_block = ^void( + os_activity_stream_t stream, os_activity_stream_event_t event) { + switch (event) { + case OS_ACTIVITY_STREAM_EVENT_STARTED: + DNBLogThreadedIf(LOG_DARWIN_LOG, + "received stream event: " + "OS_ACTIVITY_STREAM_EVENT_STARTED, stream %p.", + (void *)stream); + break; + case OS_ACTIVITY_STREAM_EVENT_STOPPED: + DNBLogThreadedIf(LOG_DARWIN_LOG, + "received stream event: " + "OS_ACTIVITY_STREAM_EVENT_STOPPED, stream %p.", + (void *)stream); + break; + case OS_ACTIVITY_STREAM_EVENT_FAILED: + DNBLogThreadedIf(LOG_DARWIN_LOG, + "received stream event: " + "OS_ACTIVITY_STREAM_EVENT_FAILED, stream %p.", + (void *)stream); + break; + case OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED: + DNBLogThreadedIf(LOG_DARWIN_LOG, + "received stream event: " + "OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED, stream %p.", + (void *)stream); + break; + case OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED: + DNBLogThreadedIf(LOG_DARWIN_LOG, + "received stream event: " + "OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED, stream %p.", + (void *)stream); + break; + } + }; + + // Create the stream. + os_activity_stream_t activity_stream = (*s_os_activity_stream_for_pid)( + pid, spi_config.GetActivityStreamFlags(), block); + collector_sp->SetActivityStream(activity_stream); + + // Specify the stream-related event handler. + (*s_os_activity_stream_set_event_handler)(activity_stream, + stream_event_block); + + // Start the stream. + (*s_os_activity_stream_resume)(activity_stream); + + TrackCollector(collector_sp); + return true; +} + +DarwinLogEventVector +DarwinLogCollector::GetEventsForProcess(nub_process_t pid) { + auto collector_sp = FindCollectorForProcess(pid); + if (!collector_sp) { + // We're not tracking a stream for this process. + return DarwinLogEventVector(); + } + + return collector_sp->RemoveEvents(); +} + +bool DarwinLogCollector::CancelStreamForProcess(nub_process_t pid) { + auto collector_sp = FindCollectorForProcess(pid); + if (!collector_sp) { + // We're not tracking a stream for this process. + return false; + } + + collector_sp->CancelActivityStream(); + StopTrackingCollector(collector_sp); + + return true; +} + +const char * +DarwinLogCollector::GetActivityForID(os_activity_id_t activity_id) const { + auto find_it = m_activity_map.find(activity_id); + return (find_it != m_activity_map.end()) ? find_it->second.m_name.c_str() + : nullptr; +} + +/// Retrieve the full parent-child chain for activity names. These +/// can be arbitrarily deep. This method assumes the caller has already +/// locked the activity mutex. +void DarwinLogCollector::GetActivityChainForID_internal( + os_activity_id_t activity_id, std::string &result, size_t depth) const { + if (depth > MAX_ACTIVITY_CHAIN_DEPTH) { + // Terminating condition - too deeply nested. + return; + } else if (activity_id == 0) { + // Terminating condition - no activity. + return; + } + + auto find_it = m_activity_map.find(activity_id); + if (find_it == m_activity_map.end()) { + // Terminating condition - no data for activity_id. + return; + } + + // Activity name becomes parent activity name chain + ':' + our activity + // name. + GetActivityChainForID_internal(find_it->second.m_parent_id, result, + depth + 1); + if (!result.empty()) + result += ':'; + result += find_it->second.m_name; +} + +std::string +DarwinLogCollector::GetActivityChainForID(os_activity_id_t activity_id) const { + std::string result; + { + std::lock_guard<std::mutex> locker(m_activity_info_mutex); + GetActivityChainForID_internal(activity_id, result, 1); + } + return result; +} + +DarwinLogCollector::DarwinLogCollector(nub_process_t pid, + const LogFilterChainSP &filter_chain_sp) + : ActivityStore(), m_pid(pid), m_activity_stream(0), m_events(), + m_events_mutex(), m_filter_chain_sp(filter_chain_sp), + m_activity_info_mutex(), m_activity_map() {} + +DarwinLogCollector::~DarwinLogCollector() { + // Cancel the stream. + if (m_activity_stream) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "tearing down activity stream " + "collector for %d", + m_pid); + (*s_os_activity_stream_cancel)(m_activity_stream); + m_activity_stream = 0; + } else { + DNBLogThreadedIf(LOG_DARWIN_LOG, "no stream to tear down for %d", m_pid); + } +} + +void DarwinLogCollector::SignalDataAvailable() { + RNBRemoteSP remoteSP(g_remoteSP); + if (!remoteSP) { + // We're done. This is unexpected. + StopTrackingCollector(shared_from_this()); + return; + } + + RNBContext &ctx = remoteSP->Context(); + ctx.Events().SetEvents(RNBContext::event_darwin_log_data_available); + // Wait for the main thread to consume this notification if it requested + // we wait for it. + ctx.Events().WaitForResetAck(RNBContext::event_darwin_log_data_available); +} + +void DarwinLogCollector::SetActivityStream( + os_activity_stream_t activity_stream) { + m_activity_stream = activity_stream; +} + +bool DarwinLogCollector::HandleStreamEntry(os_activity_stream_entry_t entry, + int error) { + if ((error == 0) && (entry != nullptr)) { + if (entry->pid != m_pid) { + // For now, skip messages not originating from our process. + // Later we might want to keep all messages related to an event + // that we're tracking, even when it came from another process, + // possibly doing work on our behalf. + return true; + } + + switch (entry->type) { + case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE: + DNBLogThreadedIf( + LOG_DARWIN_LOG, "received activity create: " + "%s, creator aid %" PRIu64 ", unique_pid %" PRIu64 + "(activity id=%" PRIu64 ", parent id=%" PRIu64 ")", + entry->activity_create.name, entry->activity_create.creator_aid, + entry->activity_create.unique_pid, entry->activity_id, + entry->parent_id); + { + std::lock_guard<std::mutex> locker(m_activity_info_mutex); + m_activity_map.insert( + std::make_pair(entry->activity_id, + ActivityInfo(entry->activity_create.name, + entry->activity_id, entry->parent_id))); + } + break; + + case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION: + DNBLogThreadedIf( + LOG_DARWIN_LOG, "received activity transition:" + "new aid: %" PRIu64 "(activity id=%" PRIu64 + ", parent id=%" PRIu64 ", tid %" PRIu64 ")", + entry->activity_transition.transition_id, entry->activity_id, + entry->parent_id, entry->activity_transition.thread); + break; + + case OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE: { + DNBLogThreadedIf( + LOG_DARWIN_LOG, "received log message: " + "(activity id=%" PRIu64 ", parent id=%" PRIu64 ", " + "tid %" PRIu64 "): format %s", + entry->activity_id, entry->parent_id, entry->log_message.thread, + entry->log_message.format ? entry->log_message.format + : "<invalid-format>"); + + // Do the real work here. + { + // Ensure our process is still alive. If not, we can + // cancel the collection. + if (!DNBProcessIsAlive(m_pid)) { + // We're outta here. This is the manner in which we + // stop collecting for a process. + StopTrackingCollector(shared_from_this()); + return false; + } + + LogMessageOsLog os_log_message(*this, *entry); + if (!m_filter_chain_sp || + !m_filter_chain_sp->GetAcceptMessage(os_log_message)) { + // This log message was rejected by the filter, + // so stop processing it now. + return true; + } + + // Copy over the relevant bits from the message. + const struct os_log_message_s &log_message = entry->log_message; + + DarwinLogEventSP message_sp(new DarwinLogEvent()); + // Indicate this event is a log message event. + message_sp->AddStringItem("type", "log"); + + // Add the message contents (fully expanded). + // Consider expanding on the remote side. + // Then we don't pay for expansion until when it is + // used. + const char *message_text = os_log_message.GetMessage(); + if (message_text) + message_sp->AddStringItem("message", message_text); + + // Add some useful data fields. + message_sp->AddIntegerItem("timestamp", log_message.timestamp); + + // Do we want to do all activity name resolution on this + // side? Maybe. For now, send IDs and ID->name mappings + // and fix this up on that side. Later, when we add + // debugserver-side filtering, we'll want to get the + // activity names over here, so we should probably + // just send them as resolved strings. + message_sp->AddIntegerItem("activity_id", entry->activity_id); + message_sp->AddIntegerItem("parent_id", entry->parent_id); + message_sp->AddIntegerItem("thread_id", log_message.thread); + if (log_message.subsystem && strlen(log_message.subsystem) > 0) + message_sp->AddStringItem("subsystem", log_message.subsystem); + if (log_message.category && strlen(log_message.category) > 0) + message_sp->AddStringItem("category", log_message.category); + if (entry->activity_id != 0) { + std::string activity_chain = + GetActivityChainForID(entry->activity_id); + if (!activity_chain.empty()) + message_sp->AddStringItem("activity-chain", activity_chain); + } + + // Add it to the list for later collection. + { + std::lock_guard<std::mutex> locker(m_events_mutex); + m_events.push_back(message_sp); + } + SignalDataAvailable(); + } + break; + } + } + } else { + DNBLogThreadedIf(LOG_DARWIN_LOG, "HandleStreamEntry: final call, " + "error %d", + error); + } + return true; +} + +DarwinLogEventVector DarwinLogCollector::RemoveEvents() { + DarwinLogEventVector returned_events; + { + std::lock_guard<std::mutex> locker(m_events_mutex); + returned_events.swap(m_events); + } + DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLogCollector::%s(): removing %lu " + "queued log entries", + __FUNCTION__, returned_events.size()); + return returned_events; +} + +void DarwinLogCollector::CancelActivityStream() { + if (!m_activity_stream) + return; + + DNBLogThreadedIf(LOG_DARWIN_LOG, + "DarwinLogCollector::%s(): canceling " + "activity stream %p", + __FUNCTION__, static_cast<void *>(m_activity_stream)); + (*s_os_activity_stream_cancel)(m_activity_stream); + m_activity_stream = nullptr; +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h new file mode 100644 index 00000000000..24ab4230e38 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h @@ -0,0 +1,107 @@ +//===-- DarwinLogCollector.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef DarwinLogCollector_h +#define DarwinLogCollector_h + +#include <sys/types.h> + +#include <memory> +#include <mutex> +#include <unordered_map> + +#include "ActivityStore.h" +#include "ActivityStreamSPI.h" +#include "DNBDefs.h" +#include "DarwinLogEvent.h" +#include "DarwinLogInterfaces.h" +#include "JSON.h" + +class DarwinLogCollector; +typedef std::shared_ptr<DarwinLogCollector> DarwinLogCollectorSP; + +class DarwinLogCollector + : public std::enable_shared_from_this<DarwinLogCollector>, + public ActivityStore { +public: + /// Return whether the os_log and activity tracing SPI is available. + /// + /// \return \b true if the activity stream support is available, + /// \b false otherwise. + static bool IsSupported(); + + /// Return a log function suitable for DNBLog to use as the internal + /// logging function. + /// + /// \return a DNBLog-style logging function if IsSupported() returns + /// true; otherwise, returns nullptr. + static DNBCallbackLog GetLogFunction(); + + static bool StartCollectingForProcess(nub_process_t pid, + const JSONObject &config); + + static bool CancelStreamForProcess(nub_process_t pid); + + static DarwinLogEventVector GetEventsForProcess(nub_process_t pid); + + ~DarwinLogCollector(); + + pid_t GetProcessID() const { return m_pid; } + + // ActivityStore API + const char *GetActivityForID(os_activity_id_t activity_id) const override; + + std::string + GetActivityChainForID(os_activity_id_t activity_id) const override; + +private: + DarwinLogCollector() = delete; + DarwinLogCollector(const DarwinLogCollector &) = delete; + DarwinLogCollector &operator=(const DarwinLogCollector &) = delete; + + explicit DarwinLogCollector(nub_process_t pid, + const LogFilterChainSP &filter_chain_sp); + + void SignalDataAvailable(); + + void SetActivityStream(os_activity_stream_t activity_stream); + + bool HandleStreamEntry(os_activity_stream_entry_t entry, int error); + + DarwinLogEventVector RemoveEvents(); + + void CancelActivityStream(); + + void GetActivityChainForID_internal(os_activity_id_t activity_id, + std::string &result, size_t depth) const; + + struct ActivityInfo { + ActivityInfo(const char *name, os_activity_id_t activity_id, + os_activity_id_t parent_activity_id) + : m_name(name), m_id(activity_id), m_parent_id(parent_activity_id) {} + + const std::string m_name; + const os_activity_id_t m_id; + const os_activity_id_t m_parent_id; + }; + + using ActivityMap = std::unordered_map<os_activity_id_t, ActivityInfo>; + + const nub_process_t m_pid; + os_activity_stream_t m_activity_stream; + DarwinLogEventVector m_events; + std::mutex m_events_mutex; + LogFilterChainSP m_filter_chain_sp; + + /// Mutex to protect activity info (activity name and parent structures) + mutable std::mutex m_activity_info_mutex; + /// Map of activity id to ActivityInfo + ActivityMap m_activity_map; +}; + +#endif /* LogStreamCollector_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h new file mode 100644 index 00000000000..fb142146bf8 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h @@ -0,0 +1,26 @@ +//===-- DarwinLogEvent.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef DarwinLogEvent_h +#define DarwinLogEvent_h + +#include <memory> +#include <vector> + +#include "JSONGenerator.h" + +// ============================================================================= +/// Each discrete unit of information is described as an event, such as +/// the emission of a single log message. +// ============================================================================= + +using DarwinLogEvent = JSONGenerator::Dictionary; +using DarwinLogEventSP = std::shared_ptr<DarwinLogEvent>; +using DarwinLogEventVector = std::vector<DarwinLogEventSP>; + +#endif diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h new file mode 100644 index 00000000000..2bfcb50068b --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h @@ -0,0 +1,24 @@ +//===-- DarwinLogInterfaces.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef DarwinLogInterfaces_h +#define DarwinLogInterfaces_h + +#include <memory> + +class ActivityStore; + +class LogFilter; +using LogFilterSP = std::shared_ptr<LogFilter>; + +class LogFilterChain; +using LogFilterChainSP = std::shared_ptr<LogFilterChain>; + +class LogMessage; + +#endif /* DarwinLogInterfaces_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h new file mode 100644 index 00000000000..e9d84991a4a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h @@ -0,0 +1,21 @@ +//===-- DarwinLogTypes.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef DarwinLogTypes_h +#define DarwinLogTypes_h + +enum FilterTarget { + eFilterTargetInvalid, + eFilterTargetActivity, + eFilterTargetActivityChain, + eFilterTargetCategory, + eFilterTargetMessage, + eFilterTargetSubsystem +}; + +#endif /* DarwinLogTypes_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp new file mode 100644 index 00000000000..c533c59fa9e --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp @@ -0,0 +1,11 @@ +//===-- LogFilter.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LogFilter.h" + +LogFilter::~LogFilter() {} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h new file mode 100644 index 00000000000..76ab60f1d9f --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h @@ -0,0 +1,29 @@ +//===-- LogFilter.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LogFilter_h +#define LogFilter_h + +#include "DarwinLogInterfaces.h" + +class LogFilter { +public: + virtual ~LogFilter(); + + virtual bool DoesMatch(const LogMessage &message) const = 0; + + bool MatchesAreAccepted() const { return m_matches_accept; } + +protected: + LogFilter(bool matches_accept) : m_matches_accept(matches_accept) {} + +private: + bool m_matches_accept; +}; + +#endif /* LogFilter_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp new file mode 100644 index 00000000000..2d8c655fcf0 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp @@ -0,0 +1,41 @@ +//===-- LogFilterChain.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LogFilterChain.h" + +#include "LogFilter.h" + +LogFilterChain::LogFilterChain(bool default_accept) + : m_filters(), m_default_accept(default_accept) {} + +void LogFilterChain::AppendFilter(const LogFilterSP &filter_sp) { + if (filter_sp) + m_filters.push_back(filter_sp); +} + +void LogFilterChain::ClearFilterChain() { m_filters.clear(); } + +bool LogFilterChain::GetDefaultAccepts() const { return m_default_accept; } + +void LogFilterChain::SetDefaultAccepts(bool default_accept) { + m_default_accept = default_accept; +} + +bool LogFilterChain::GetAcceptMessage(const LogMessage &message) const { + for (auto filter_sp : m_filters) { + if (filter_sp->DoesMatch(message)) { + // This message matches this filter. If the filter accepts matches, + // this message matches; otherwise, it rejects matches. + return filter_sp->MatchesAreAccepted(); + } + } + + // None of the filters matched. Therefore, we do whatever the + // default fall-through rule says. + return m_default_accept; +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h new file mode 100644 index 00000000000..f231c308232 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h @@ -0,0 +1,37 @@ +//===-- LogFilterChain.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LogFilterChain_h +#define LogFilterChain_h + +#include <vector> + +#include "DarwinLogInterfaces.h" + +class LogFilterChain { +public: + LogFilterChain(bool default_accept); + + void AppendFilter(const LogFilterSP &filter_sp); + + void ClearFilterChain(); + + bool GetDefaultAccepts() const; + + void SetDefaultAccepts(bool default_accepts); + + bool GetAcceptMessage(const LogMessage &message) const; + +private: + using FilterVector = std::vector<LogFilterSP>; + + FilterVector m_filters; + bool m_default_accept; +}; + +#endif /* LogFilterChain_hpp */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp new file mode 100644 index 00000000000..bbf911f28cd --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp @@ -0,0 +1,48 @@ +//===-- LogFilterExactMatch.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LogFilterExactMatch.h" +#include "LogMessage.h" + +LogFilterExactMatch::LogFilterExactMatch(bool match_accepts, + FilterTarget filter_target, + const std::string &match_text) + : LogFilter(match_accepts), m_filter_target(filter_target), + m_match_text(match_text) {} + +bool LogFilterExactMatch::DoesMatch(const LogMessage &message) const { + switch (m_filter_target) { + case eFilterTargetActivity: + // Empty fields never match a condition. + if (!message.HasActivity()) + return false; + return m_match_text == message.GetActivity(); + case eFilterTargetActivityChain: + // Empty fields never match a condition. + if (!message.HasActivity()) + return false; + return m_match_text == message.GetActivityChain(); + case eFilterTargetCategory: + // Empty fields never match a condition. + if (!message.HasCategory()) + return false; + return m_match_text == message.GetCategory(); + case eFilterTargetMessage: { + const char *message_text = message.GetMessage(); + return (message_text != nullptr) && (m_match_text == message_text); + } + case eFilterTargetSubsystem: + // Empty fields never match a condition. + if (!message.HasSubsystem()) + return false; + return m_match_text == message.GetSubsystem(); + default: + // We don't know this type. + return false; + } +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h new file mode 100644 index 00000000000..5c0b9c22356 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h @@ -0,0 +1,30 @@ +//===-- LogFilterExactMatch.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LogFilterExactMatch_h +#define LogFilterExactMatch_h + +#include <string> + +#include "DarwinLogInterfaces.h" +#include "DarwinLogTypes.h" +#include "LogFilter.h" + +class LogFilterExactMatch : public LogFilter { +public: + LogFilterExactMatch(bool match_accepts, FilterTarget filter_target, + const std::string &match_text); + + bool DoesMatch(const LogMessage &message) const override; + +private: + const FilterTarget m_filter_target; + const std::string m_match_text; +}; + +#endif diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp new file mode 100644 index 00000000000..489c056fa81 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp @@ -0,0 +1,94 @@ +//===-- LogFilterRegex.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LogFilterRegex.h" + +#include "DNBLog.h" +#include "LogMessage.h" + +// Enable enhanced mode if it is available. This allows for things like +// \d for digit, \s for space, and many more, but it isn't available +// everywhere. +#if defined(REG_ENHANCED) +#define DEFAULT_COMPILE_FLAGS (REG_ENHANCED | REG_EXTENDED) +#else +#define DEFAULT_COMPILE_FLAGS (REG_EXTENDED) +#endif + +LogFilterRegex::LogFilterRegex(bool match_accepts, FilterTarget filter_target, + const std::string ®ex) + : LogFilter(match_accepts), m_filter_target(filter_target), + m_regex_text(regex), m_regex(), m_is_valid(false), m_error_text() { + // Clear it. + memset(&m_regex, 0, sizeof(m_regex)); + + // Compile it. + if (!regex.empty()) { + auto comp_err = ::regcomp(&m_regex, regex.c_str(), DEFAULT_COMPILE_FLAGS); + m_is_valid = (comp_err == 0); + if (!m_is_valid) { + char buffer[256]; + buffer[0] = '\0'; + ::regerror(comp_err, &m_regex, buffer, sizeof(buffer)); + m_error_text = buffer; + } + } +} + +LogFilterRegex::~LogFilterRegex() { + if (m_is_valid) { + // Free the regex internals. + regfree(&m_regex); + } +} + +bool LogFilterRegex::DoesMatch(const LogMessage &message) const { + switch (m_filter_target) { + case eFilterTargetActivity: + // Empty fields never match a condition. + if (!message.HasActivity()) + return false; + return ::regexec(&m_regex, message.GetActivity(), 0, nullptr, 0) == 0; + case eFilterTargetActivityChain: + // Empty fields never match a condition. + if (!message.HasActivity()) + return false; + return ::regexec(&m_regex, message.GetActivityChain().c_str(), 0, nullptr, + 0) == 0; + case eFilterTargetCategory: + // Empty fields never match a condition. + if (!message.HasCategory()) + return false; + return ::regexec(&m_regex, message.GetCategory(), 0, nullptr, 0) == 0; + case eFilterTargetMessage: { + const char *message_text = message.GetMessage(); + if (!message_text) { + DNBLogThreadedIf(LOG_DARWIN_LOG, + "LogFilterRegex: regex " + "\"%s\" no match due to nullptr message.", + m_regex_text.c_str()); + return false; + } + + bool match = ::regexec(&m_regex, message_text, 0, nullptr, 0) == 0; + DNBLogThreadedIf(LOG_DARWIN_LOG, "LogFilterRegex: regex " + "\"%s\" %s message \"%s\".", + m_regex_text.c_str(), match ? "matches" : "does not match", + message_text); + return match; + } + case eFilterTargetSubsystem: + // Empty fields never match a condition. + if (!message.HasSubsystem()) + return false; + return ::regexec(&m_regex, message.GetSubsystem(), 0, nullptr, 0) == 0; + default: + // We don't know this type. + return false; + } +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h new file mode 100644 index 00000000000..5bcc366f077 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h @@ -0,0 +1,43 @@ +//===-- LogFilterRegex.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LogFilterRegex_h +#define LogFilterRegex_h + +// C includes +#include <regex.h> + +// C++ includes +#include <string> + +#include "DarwinLogInterfaces.h" +#include "DarwinLogTypes.h" +#include "LogFilter.h" + +class LogFilterRegex : public LogFilter { +public: + LogFilterRegex(bool match_accepts, FilterTarget filter_target, + const std::string ®ex); + + virtual ~LogFilterRegex(); + + bool IsValid() const { return m_is_valid; } + + const char *GetErrorAsCString() const { return m_error_text.c_str(); } + + bool DoesMatch(const LogMessage &message) const override; + +private: + const FilterTarget m_filter_target; + const std::string m_regex_text; + regex_t m_regex; + bool m_is_valid; + std::string m_error_text; +}; + +#endif /* LogFilterSubsystemRegex_hpp */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp new file mode 100644 index 00000000000..6e5e26ddf89 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp @@ -0,0 +1,13 @@ +//===-- LogMessage.cpp ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LogMessage.h" + +LogMessage::LogMessage() {} + +LogMessage::~LogMessage() {} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h new file mode 100644 index 00000000000..1f5808c2cd1 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h @@ -0,0 +1,39 @@ +//===-- LogMessage.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LogMessage_h +#define LogMessage_h + +#include <string> + +class LogMessage { +public: + virtual ~LogMessage(); + + virtual bool HasActivity() const = 0; + + virtual const char *GetActivity() const = 0; + + virtual std::string GetActivityChain() const = 0; + + virtual bool HasCategory() const = 0; + + virtual const char *GetCategory() const = 0; + + virtual bool HasSubsystem() const = 0; + + virtual const char *GetSubsystem() const = 0; + + // This can be expensive, so once we ask for it, we'll cache the result. + virtual const char *GetMessage() const = 0; + +protected: + LogMessage(); +}; + +#endif /* LogMessage_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp new file mode 100644 index 00000000000..b5855a9ff5e --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp @@ -0,0 +1,67 @@ +//===-- LogMessageOsLog.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LogMessageOsLog.h" + +#include "ActivityStore.h" +#include "ActivityStreamSPI.h" + +namespace { +static os_log_copy_formatted_message_t s_log_copy_formatted_message; +} + +void LogMessageOsLog::SetFormatterFunction( + os_log_copy_formatted_message_t format_func) { + s_log_copy_formatted_message = format_func; +} + +LogMessageOsLog::LogMessageOsLog(const ActivityStore &activity_store, + ActivityStreamEntry &entry) + : LogMessage(), m_activity_store(activity_store), m_entry(entry), + m_message() {} + +bool LogMessageOsLog::HasActivity() const { return m_entry.activity_id != 0; } + +const char *LogMessageOsLog::GetActivity() const { + return m_activity_store.GetActivityForID(m_entry.activity_id); +} + +std::string LogMessageOsLog::GetActivityChain() const { + return m_activity_store.GetActivityChainForID(m_entry.activity_id); +} + +bool LogMessageOsLog::HasCategory() const { + return m_entry.log_message.category && (m_entry.log_message.category[0] != 0); +} + +const char *LogMessageOsLog::GetCategory() const { + return m_entry.log_message.category; +} + +bool LogMessageOsLog::HasSubsystem() const { + return m_entry.log_message.subsystem && + (m_entry.log_message.subsystem[0] != 0); +} + +const char *LogMessageOsLog::GetSubsystem() const { + return m_entry.log_message.subsystem; +} + +const char *LogMessageOsLog::GetMessage() const { + if (m_message.empty()) { + std::unique_ptr<char[]> formatted_message( + s_log_copy_formatted_message(&m_entry.log_message)); + if (formatted_message) + m_message = formatted_message.get(); + // else + // TODO log + } + + // This is safe to return as we're not modifying it once we've formatted it. + return m_message.c_str(); +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h new file mode 100644 index 00000000000..39b6adccb1b --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h @@ -0,0 +1,56 @@ +//===-- LogMessageOsLog.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LogMessageOsLog_h +#define LogMessageOsLog_h + +#include "DarwinLogInterfaces.h" + +#include "ActivityStreamSPI.h" +#include "LogMessage.h" + +using ActivityStreamEntry = struct os_activity_stream_entry_s; + +/// Provides a unified wrapper around os_log()-style log messages. +/// +/// The lifetime of this class is intended to be very short. The caller +/// must ensure that the passed in ActivityStore and ActivityStreamEntry +/// outlive this LogMessageOsLog entry. + +class LogMessageOsLog : public LogMessage { +public: + static void SetFormatterFunction(os_log_copy_formatted_message_t format_func); + + LogMessageOsLog(const ActivityStore &activity_store, + ActivityStreamEntry &entry); + + // API methods + + bool HasActivity() const override; + + const char *GetActivity() const override; + + std::string GetActivityChain() const override; + + bool HasCategory() const override; + + const char *GetCategory() const override; + + bool HasSubsystem() const override; + + const char *GetSubsystem() const override; + + const char *GetMessage() const override; + +private: + const ActivityStore &m_activity_store; + ActivityStreamEntry &m_entry; + mutable std::string m_message; +}; + +#endif /* LogMessageOsLog_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp new file mode 100644 index 00000000000..72d923309a6 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp @@ -0,0 +1,315 @@ +//===-- Genealogy.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <Availability.h> +#include <dlfcn.h> +#include <string> +#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) { + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.h new file mode 100644 index 00000000000..969405a18af --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.h @@ -0,0 +1,119 @@ +//===-- Genealogy.h ---------------------------------------------*- C++ -*-===// +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __Genealogy_h__ +#define __Genealogy_h__ + +#include <mach/task.h> +#include <map> +#include <pthread.h> +#include <string> +#include <vector> + +#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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h new file mode 100644 index 00000000000..0aea512b369 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h @@ -0,0 +1,94 @@ +//===-- GenealogySPI.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// + +#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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp new file mode 100644 index 00000000000..8690960e4cd --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp @@ -0,0 +1,514 @@ +//===-- MachException.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + +#include "MachException.h" +#include "DNB.h" +#include "DNBError.h" +#include "DNBLog.h" +#include "MachProcess.h" +#include "PThreadMutex.h" +#include "SysSignal.h" +#include <errno.h> +#include <sys/ptrace.h> +#include <sys/types.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); + +// Note: g_message points to the storage allocated to catch the data from +// catching the current exception raise. It's populated when we catch a raised +// exception which can't immediately be replied to. +// +// If it becomes possible to catch exceptions from multiple threads +// simultaneously, accesses to g_message would need to be mutually exclusive. +static MachException::Data *g_message = NULL; + +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)); + } + + 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)); + } + g_message->exc_type = 0; + g_message->exc_data.clear(); + + 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->AppendExceptionData(exc_data, exc_data_count); + return KERN_SUCCESS; + } else if (!MachTask::IsValid(g_message->task_port)) { + // Our original exception port isn't valid anymore check for a SIGTRAP + if (exc_type == EXC_SOFTWARE && exc_data_count == 2 && + exc_data[0] == EXC_SOFT_SIGNAL && exc_data[1] == SIGTRAP) { + // We got a SIGTRAP which indicates we might have exec'ed and possibly + // lost our old task port during the exec, so we just need to switch over + // to using this new task port + g_message->task_port = task_port; + g_message->thread_port = thread_port; + g_message->exc_type = exc_type; + g_message->AppendExceptionData(exc_data, exc_data_count); + 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.Status(); +} + +bool MachException::Message::CatchExceptionRaise(task_t task) { + bool success = false; + 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.Status() == 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.Status()); + } else { + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + err.LogThreaded("::mach_msg() - failed (child of task)"); + } + } + } + + return err.Status(); +} + +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]); + } + } +} + +// The EXC_MASK_ALL value hard-coded here so that lldb can be built +// on a new OS with an older deployment target . The new OS may have +// an addition to its EXC_MASK_ALL that the old OS will not recognize - +// <mach/exception_types.h> doesn't vary the value based on the deployment +// target. So we need a known set of masks that can be assumed to be +// valid when running on an older OS. We'll fall back to trying +// PREV_EXC_MASK_ALL if the EXC_MASK_ALL value lldb was compiled with is +// not recognized. + +#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_RESOURCE | \ + EXC_MASK_GUARD | \ + EXC_MASK_MACHINE) + +#define LLDB_EXC_MASK EXC_MASK_ALL + +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.Status() == 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.Status(); +} + +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.Status(); +} + +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 + case EXC_RESOURCE: + return "EXC_RESOURCE"; +#ifdef EXC_GUARD + case EXC_GUARD: + return "EXC_GUARD"; +#endif +#ifdef EXC_CORPSE_NOTIFY + case EXC_CORPSE_NOTIFY: + return "EXC_CORPSE_NOTIFY"; +#endif +#ifdef EXC_CORPSE_VARIANT_BIT + case EXC_CORPSE_VARIANT_BIT: + return "EXC_CORPSE_VARIANT_BIT"; +#endif + default: + break; + } + return NULL; +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.h new file mode 100644 index 00000000000..0a1d34170d6 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.h @@ -0,0 +1,132 @@ +//===-- MachException.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 AppendExceptionData(mach_exception_data_t Data, + mach_msg_type_number_t Count) { + mach_exception_data_type_t Buf; + for (mach_msg_type_number_t i = 0; i < Count; ++i) { + // Perform an unaligned copy. + memcpy(&Buf, Data + i, sizeof(mach_exception_data_type_t)); + exc_data.push_back(Buf); + } + } + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.h new file mode 100644 index 00000000000..e31486cc39b --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -0,0 +1,433 @@ +//===-- MachProcess.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/15/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachProcess_h__ +#define __MachProcess_h__ + +#include <CoreFoundation/CoreFoundation.h> +#include <mach-o/loader.h> +#include <mach/mach.h> +#include <pthread.h> +#include <sys/signal.h> +#include <uuid/uuid.h> +#include <vector> + +#include "DNBBreakpoint.h" +#include "DNBDefs.h" +#include "DNBError.h" +#include "DNBThreadResumeActions.h" +#include "Genealogy.h" +#include "JSONGenerator.h" +#include "MachException.h" +#include "MachTask.h" +#include "MachThreadList.h" +#include "MachVMMemory.h" +#include "PThreadCondition.h" +#include "PThreadEvent.h" +#include "PThreadMutex.h" +#include "ThreadInfo.h" + +class DNBThreadResumeActions; + +class MachProcess { +public: + // Constructors and Destructors + MachProcess(); + ~MachProcess(); + + // A structure that can hold everything debugserver needs to know from + // a binary's Mach-O header / load commands. + + struct mach_o_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 mach_o_information { + struct mach_header_64 mach_header; + std::vector<struct mach_o_segment> segments; + uuid_t uuid; + std::string min_version_os_name; + std::string min_version_os_version; + }; + + struct binary_image_information { + std::string filename; + uint64_t load_address; + uint64_t mod_date; // may not be available - 0 if so + struct mach_o_information macho_info; + + binary_image_information() + : filename(), load_address(INVALID_NUB_ADDRESS), mod_date(0) {} + }; + + // 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); + static std::string GetMacCatalystVersionString(); +#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); + const char * + GetDeploymentInfo(const struct load_command&, uint64_t load_command_address, + uint32_t& major_version, uint32_t& minor_version, + uint32_t& patch_version); + bool GetMachOInformationFromMemory(uint32_t platform, + nub_addr_t mach_o_header_addr, + int wordsize, + struct mach_o_information &inf); + JSONGenerator::ObjectSP FormatDynamicLibrariesIntoJSON( + const std::vector<struct binary_image_information> &image_infos); + uint32_t GetAllLoadedBinariesViaDYLDSPI( + std::vector<struct binary_image_information> &image_infos); + JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos( + nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count); + JSONGenerator::ObjectSP + GetLibrariesInfoForAddresses(nub_process_t pid, + std::vector<uint64_t> &macho_addresses); + JSONGenerator::ObjectSP GetAllLoadedLibrariesInfos(nub_process_t pid); + JSONGenerator::ObjectSP GetSharedCacheInfo(nub_process_t pid); + + 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; + } + } + + void CalculateBoardStatus(); + + bool ProcessUsingBackBoard(); + + bool ProcessUsingFrontBoard(); + + 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), + eMachProcessFlagsUsingBKS = (1 << 2), // only read via ProcessUsingBackBoard() + eMachProcessFlagsUsingFBS = (1 << 3), // only read via ProcessUsingFrontBoard() + eMachProcessFlagsBoardCalculated = (1 << 4) + }; + 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; + + void *(*m_dyld_process_info_create)(task_t task, uint64_t timestamp, + kern_return_t *kernelError); + void (*m_dyld_process_info_for_each_image)( + void *info, void (^callback)(uint64_t machHeaderAddress, + const uuid_t uuid, const char *path)); + void (*m_dyld_process_info_release)(void *info); + void (*m_dyld_process_info_get_cache)(void *info, void *cacheInfo); + uint32_t (*m_dyld_process_info_get_platform)(void *info); +}; + +#endif // __MachProcess_h__ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.mm new file mode 100644 index 00000000000..40facdfb5cf --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -0,0 +1,4029 @@ +//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/15/07. +// +//===----------------------------------------------------------------------===// + +#include "DNB.h" +#include "MacOSX/CFUtils.h" +#include "SysSignal.h" +#include <dlfcn.h> +#include <inttypes.h> +#include <mach-o/loader.h> +#include <mach/mach.h> +#include <mach/task.h> +#include <pthread.h> +#include <signal.h> +#include <spawn.h> +#include <sys/fcntl.h> +#include <sys/ptrace.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <unistd.h> +#include <uuid/uuid.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 "CFString.h" + +#ifndef PLATFORM_BRIDGEOS +#define PLATFORM_BRIDGEOS 5 +#endif + +#ifndef PLATFORM_MACCATALYST +#define PLATFORM_MACCATALYST 6 +#endif + +#ifndef PLATFORM_IOSSIMULATOR +#define PLATFORM_IOSSIMULATOR 7 +#endif + +#ifndef PLATFORM_TVOSSIMULATOR +#define PLATFORM_TVOSSIMULATOR 8 +#endif + +#ifndef PLATFORM_WATCHOSSIMULATOR +#define PLATFORM_WATCHOSSIMULATOR 9 +#endif + +#ifndef PLATFORM_DRIVERKIT +#define PLATFORM_DRIVERKIT 10 +#endif + +#ifdef WITH_SPRINGBOARD + +#include <CoreFoundation/CoreFoundation.h> +#include <SpringBoardServices/SBSWatchdogAssertion.h> +#include <SpringBoardServices/SpringBoardServer.h> + +#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, std::string, 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; + __block std::string open_app_error_string; + bool wants_pid = (return_pid != NULL); + __block pid_t pid_in_block; + + const char *cstr = [bundleIDNSStr UTF8String]; + if (!cstr) + cstr = "<Unknown Bundle ID>"; + + NSString *description = [options description]; + DNBLog("About to launch process for bundle ID: %s - options:\n%s", cstr, + [description UTF8String]); + [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]; + if (error_str) { + open_app_error_string = error_str; + } + 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 = 30; + + 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, open_app_error_string, 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 + +#if defined(WITH_BKS) || defined(WITH_FBS) +static void SplitEventData(const char *data, std::vector<std::string> &elements) +{ + elements.clear(); + if (!data) + return; + + const char *start = data; + + while (*start != '\0') { + const char *token = strchr(start, ':'); + if (!token) { + elements.push_back(std::string(start)); + return; + } + if (token != start) + elements.push_back(std::string(start, token - start)); + start = ++token; + } +} +#endif + +#ifdef WITH_BKS +#import <Foundation/Foundation.h> +extern "C" { +#import <BackBoardServices/BKSOpenApplicationConstants_Private.h> +#import <BackBoardServices/BKSSystemService_LaunchServices.h> +#import <BackBoardServices/BackBoardServices.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, + std::string error_description, + DNBError &error) { + error.SetError(error_code, DNBError::BackBoard); + NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString( + (BKSOpenApplicationErrorCode)error_code); + std::string err_str = "unknown BKS error"; + if (error_description.empty() == false) { + err_str = error_description; + } else if (err_nsstr != nullptr) { + err_str = [err_nsstr UTF8String]; + } + error.SetErrorString(err_str.c_str()); +} + +static bool BKSAddEventDataToOptions(NSMutableDictionary *options, + const char *event_data, + DNBError &option_error) { + std::vector<std::string> values; + SplitEventData(event_data, values); + bool found_one = false; + for (std::string value : values) + { + if (value.compare("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]; + found_one = true; + } else if (value.compare("ActivateSuspended") == 0) { + DNBLog("Setting ActivateSuspended key in options dictionary."); + [options setObject:@YES forKey: BKSOpenApplicationOptionKeyActivateSuspended]; + found_one = true; + } else { + DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str()); + option_error.SetErrorString("Unrecognized event data"); + } + } + return found_one; +} + +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/FBSOpenApplicationConstants_Private.h> +#import <FrontBoardServices/FBSSystemService_LaunchServices.h> +#import <FrontBoardServices/FrontBoardServices.h> +#import <MobileCoreServices/LSResourceProxy.h> +#import <MobileCoreServices/MobileCoreServices.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 false; +} +#endif + +static void SetFBSError(NSInteger error_code, + std::string error_description, + DNBError &error) { + error.SetError((DNBError::ValueType)error_code, DNBError::FrontBoard); + NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString( + (FBSOpenApplicationErrorCode)error_code); + std::string err_str = "unknown FBS error"; + if (error_description.empty() == false) { + err_str = error_description; + } else if (err_nsstr != nullptr) { + err_str = [err_nsstr UTF8String]; + } + error.SetErrorString(err_str.c_str()); +} + +static bool FBSAddEventDataToOptions(NSMutableDictionary *options, + const char *event_data, + DNBError &option_error) { + std::vector<std::string> values; + SplitEventData(event_data, values); + bool found_one = false; + for (std::string value : values) + { + if (value.compare("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]; + found_one = true; + } else if (value.compare("ActivateSuspended") == 0) { + DNBLog("Setting ActivateSuspended key in options dictionary."); + [options setObject:@YES forKey: FBSOpenApplicationOptionKeyActivateSuspended]; + found_one = true; + } else { + DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str()); + option_error.SetErrorString("Unrecognized event data."); + } + } + return found_one; +} + +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), + m_dyld_process_info_create(nullptr), + m_dyld_process_info_for_each_image(nullptr), + m_dyld_process_info_release(nullptr), + m_dyld_process_info_get_cache(nullptr) { + m_dyld_process_info_create = + (void *(*)(task_t task, uint64_t timestamp, kern_return_t * kernelError)) + dlsym(RTLD_DEFAULT, "_dyld_process_info_create"); + m_dyld_process_info_for_each_image = + (void (*)(void *info, void (^)(uint64_t machHeaderAddress, + const uuid_t uuid, const char *path))) + dlsym(RTLD_DEFAULT, "_dyld_process_info_for_each_image"); + m_dyld_process_info_release = + (void (*)(void *info))dlsym(RTLD_DEFAULT, "_dyld_process_info_release"); + m_dyld_process_info_get_cache = (void (*)(void *info, void *cacheInfo))dlsym( + RTLD_DEFAULT, "_dyld_process_info_get_cache"); + m_dyld_process_info_get_platform = (uint32_t (*)(void *info))dlsym( + RTLD_DEFAULT, "_dyld_process_info_get_platform"); + + 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); +} + + +const char *MachProcess::GetDeploymentInfo(const struct load_command& lc, + uint64_t load_command_address, + uint32_t& major_version, + uint32_t& minor_version, + uint32_t& patch_version) { + uint32_t cmd = lc.cmd & ~LC_REQ_DYLD; + bool lc_cmd_known = + cmd == LC_VERSION_MIN_IPHONEOS || cmd == LC_VERSION_MIN_MACOSX || + cmd == LC_VERSION_MIN_TVOS || cmd == LC_VERSION_MIN_WATCHOS; + + if (lc_cmd_known) { + struct version_min_command vers_cmd; + if (ReadMemory(load_command_address, sizeof(struct version_min_command), + &vers_cmd) != sizeof(struct version_min_command)) { + return nullptr; + } + major_version = vers_cmd.sdk >> 16; + minor_version = (vers_cmd.sdk >> 8) & 0xffu; + patch_version = vers_cmd.sdk & 0xffu; + + switch (cmd) { + case LC_VERSION_MIN_IPHONEOS: + return "ios"; + case LC_VERSION_MIN_MACOSX: + return "macosx"; + case LC_VERSION_MIN_TVOS: + return "tvos"; + case LC_VERSION_MIN_WATCHOS: + return "watchos"; + default: + return nullptr; + } + } +#if defined (LC_BUILD_VERSION) + if (cmd == LC_BUILD_VERSION) { + struct build_version_command build_vers; + if (ReadMemory(load_command_address, sizeof(struct build_version_command), + &build_vers) != sizeof(struct build_version_command)) { + return nullptr; + } + major_version = build_vers.sdk >> 16;; + minor_version = (build_vers.sdk >> 8) & 0xffu; + patch_version = build_vers.sdk & 0xffu; + + switch (build_vers.platform) { + case PLATFORM_MACOS: + return "macosx"; + case PLATFORM_MACCATALYST: + return "maccatalyst"; + case PLATFORM_IOS: + case PLATFORM_IOSSIMULATOR: + return "ios"; + case PLATFORM_TVOS: + case PLATFORM_TVOSSIMULATOR: + return "tvos"; + case PLATFORM_WATCHOS: + case PLATFORM_WATCHOSSIMULATOR: + return "watchos"; + case PLATFORM_BRIDGEOS: + return "bridgeos"; + case PLATFORM_DRIVERKIT: + return "driverkit"; + } + } +#endif + return nullptr; +} + +// Given an address, read the mach-o header and load commands out of memory to +// fill in +// the mach_o_information "inf" object. +// +// Returns false if there was an error in reading this mach-o file header/load +// commands. + +bool MachProcess::GetMachOInformationFromMemory( + uint32_t dyld_platform, nub_addr_t mach_o_header_addr, int wordsize, + struct mach_o_information &inf) { + uint64_t load_cmds_p; + if (wordsize == 4) { + struct mach_header header; + if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header), &header) != + sizeof(struct mach_header)) { + return false; + } + load_cmds_p = mach_o_header_addr + sizeof(struct mach_header); + inf.mach_header.magic = header.magic; + inf.mach_header.cputype = header.cputype; + // high byte of cpusubtype is used for "capability bits", v. + // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h + inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff; + inf.mach_header.filetype = header.filetype; + inf.mach_header.ncmds = header.ncmds; + inf.mach_header.sizeofcmds = header.sizeofcmds; + inf.mach_header.flags = header.flags; + } else { + struct mach_header_64 header; + if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header_64), + &header) != sizeof(struct mach_header_64)) { + return false; + } + load_cmds_p = mach_o_header_addr + sizeof(struct mach_header_64); + inf.mach_header.magic = header.magic; + inf.mach_header.cputype = header.cputype; + // high byte of cpusubtype is used for "capability bits", v. + // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h + inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff; + inf.mach_header.filetype = header.filetype; + inf.mach_header.ncmds = header.ncmds; + inf.mach_header.sizeofcmds = header.sizeofcmds; + inf.mach_header.flags = header.flags; + } + for (uint32_t j = 0; j < inf.mach_header.ncmds; j++) { + struct load_command lc; + if (ReadMemory(load_cmds_p, sizeof(struct load_command), &lc) != + sizeof(struct load_command)) { + return false; + } + if (lc.cmd == LC_SEGMENT) { + struct segment_command seg; + if (ReadMemory(load_cmds_p, sizeof(struct segment_command), &seg) != + sizeof(struct segment_command)) { + return false; + } + struct mach_o_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; + inf.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 false; + } + struct mach_o_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; + inf.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(inf.uuid, uuidcmd.uuid); + } + + uint32_t major_version, minor_version, patch_version; + if (const char *lc_platform = GetDeploymentInfo( + lc, load_cmds_p, major_version, minor_version, patch_version)) { + // macCatalyst support. + // + // This handles two special cases: + // + // 1. Frameworks that have both a PLATFORM_MACOS and a + // PLATFORM_MACCATALYST load command. Make sure to select + // the requested one. + // + // 2. The xctest binary is a pure macOS binary but is launched + // with DYLD_FORCE_PLATFORM=6. + if (dyld_platform == PLATFORM_MACCATALYST && + inf.mach_header.filetype == MH_EXECUTE && + inf.min_version_os_name.empty() && + (strcmp("macosx", lc_platform) == 0)) { + // DYLD says this *is* a macCatalyst process. If we haven't + // parsed any load commands, transform a macOS load command + // into a generic macCatalyst load command. It will be + // overwritten by a more specific one if there is one. This + // is only done for the main executable. It is perfectly fine + // for a macCatalyst binary to link against a macOS-only framework. + inf.min_version_os_name = "maccatalyst"; + inf.min_version_os_version = GetMacCatalystVersionString(); + } else if (dyld_platform != PLATFORM_MACCATALYST && + inf.min_version_os_name == "macosx") { + // This is a binary with both PLATFORM_MACOS and + // PLATFORM_MACCATALYST load commands and the process is not + // running as PLATFORM_MACCATALYST. Stick with the + // "macosx" load command that we've already processed, + // ignore this one, which is presumed to be a + // PLATFORM_MACCATALYST one. + } else { + inf.min_version_os_name = lc_platform; + inf.min_version_os_version = ""; + inf.min_version_os_version += std::to_string(major_version); + inf.min_version_os_version += "."; + inf.min_version_os_version += std::to_string(minor_version); + if (patch_version != 0) { + inf.min_version_os_version += "."; + inf.min_version_os_version += std::to_string(patch_version); + } + } + } + + load_cmds_p += lc.cmdsize; + } + return true; +} + +// Given completely filled in array of binary_image_information structures, +// create a JSONGenerator object +// with all the details we want to send to lldb. +JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON( + const std::vector<struct binary_image_information> &image_infos) { + + JSONGenerator::ArraySP image_infos_array_sp(new JSONGenerator::Array()); + + const size_t image_count = image_infos.size(); + + 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].filename); + + uuid_string_t uuidstr; + uuid_unparse_upper(image_infos[i].macho_info.uuid, uuidstr); + image_info_dict_sp->AddStringItem("uuid", uuidstr); + + if (!image_infos[i].macho_info.min_version_os_name.empty() && + !image_infos[i].macho_info.min_version_os_version.empty()) { + image_info_dict_sp->AddStringItem( + "min_version_os_name", image_infos[i].macho_info.min_version_os_name); + image_info_dict_sp->AddStringItem( + "min_version_os_sdk", + image_infos[i].macho_info.min_version_os_version); + } + + JSONGenerator::DictionarySP mach_header_dict_sp( + new JSONGenerator::Dictionary()); + mach_header_dict_sp->AddIntegerItem( + "magic", image_infos[i].macho_info.mach_header.magic); + mach_header_dict_sp->AddIntegerItem( + "cputype", (uint32_t)image_infos[i].macho_info.mach_header.cputype); + mach_header_dict_sp->AddIntegerItem( + "cpusubtype", + (uint32_t)image_infos[i].macho_info.mach_header.cpusubtype); + mach_header_dict_sp->AddIntegerItem( + "filetype", image_infos[i].macho_info.mach_header.filetype); + mach_header_dict_sp->AddIntegerItem ("flags", + image_infos[i].macho_info.mach_header.flags); + + // DynamicLoaderMacOSX doesn't currently need these fields, so + // don't send them. + // mach_header_dict_sp->AddIntegerItem ("ncmds", + // image_infos[i].macho_info.mach_header.ncmds); + // mach_header_dict_sp->AddIntegerItem ("sizeofcmds", + // image_infos[i].macho_info.mach_header.sizeofcmds); + 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].macho_info.segments.size(); j++) { + JSONGenerator::DictionarySP segment_sp(new JSONGenerator::Dictionary()); + segment_sp->AddStringItem("name", + image_infos[i].macho_info.segments[j].name); + segment_sp->AddIntegerItem("vmaddr", + image_infos[i].macho_info.segments[j].vmaddr); + segment_sp->AddIntegerItem("vmsize", + image_infos[i].macho_info.segments[j].vmsize); + segment_sp->AddIntegerItem("fileoff", + image_infos[i].macho_info.segments[j].fileoff); + segment_sp->AddIntegerItem( + "filesize", image_infos[i].macho_info.segments[j].filesize); + segment_sp->AddIntegerItem("maxprot", + image_infos[i].macho_info.segments[j].maxprot); + + // DynamicLoaderMacOSX doesn't currently need these fields, + // so don't send them. + // segment_sp->AddIntegerItem ("initprot", + // image_infos[i].macho_info.segments[j].initprot); + // segment_sp->AddIntegerItem ("nsects", + // image_infos[i].macho_info.segments[j].nsects); + // segment_sp->AddIntegerItem ("flags", + // image_infos[i].macho_info.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); + } + + JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary()); + ; + reply_sp->AddItem("images", image_infos_array_sp); + + return reply_sp; +} + +// Get the shared library information using the old (pre-macOS 10.12, pre-iOS +// 10, pre-tvOS 10, pre-watchOS 3) +// code path. We'll be given the address of an array of structures in the form +// {void* load_addr, void* mod_date, void* pathname} +// +// In macOS 10.12 etc and newer, we'll use SPI calls into dyld to gather this +// information. +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; + + std::vector<struct binary_image_information> 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 binary_image_information 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.filename = ""; + 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.filename += 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.macho_info.uuid); + image_infos.push_back(info); + } + if (image_infos.size() == 0) { + return reply_sp; + } + + free(image_info_buf); + + //// Second, read the mach header / load commands for all the dylibs + + for (size_t i = 0; i < image_count; i++) { + // The SPI to provide platform is not available on older systems. + uint32_t platform = 0; + if (!GetMachOInformationFromMemory(platform, + image_infos[i].load_address, + pointer_size, + image_infos[i].macho_info)) { + return reply_sp; + } + } + + //// Third, format all of the above in the JSONGenerator object. + + return FormatDynamicLibrariesIntoJSON(image_infos); + } + + return reply_sp; +} + +// From dyld SPI header dyld_process_info.h +typedef void *dyld_process_info; +struct dyld_process_cache_info { + uuid_t cacheUUID; // UUID of cache used by process + uint64_t cacheBaseAddress; // load address of dyld shared cache + bool noCache; // process is running without a dyld cache + bool privateCache; // process is using a private copy of its dyld cache +}; + +// Use the dyld SPI present in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer +// to get +// the load address, uuid, and filenames of all the libraries. +// This only fills in those three fields in the 'struct +// binary_image_information' - call +// GetMachOInformationFromMemory to fill in the mach-o header/load command +// details. +uint32_t MachProcess::GetAllLoadedBinariesViaDYLDSPI( + std::vector<struct binary_image_information> &image_infos) { + uint32_t platform = 0; + kern_return_t kern_ret; + if (m_dyld_process_info_create) { + dyld_process_info info = + m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret); + if (info) { + m_dyld_process_info_for_each_image( + info, + ^(uint64_t mach_header_addr, const uuid_t uuid, const char *path) { + struct binary_image_information image; + image.filename = path; + uuid_copy(image.macho_info.uuid, uuid); + image.load_address = mach_header_addr; + image_infos.push_back(image); + }); + if (m_dyld_process_info_get_platform) + platform = m_dyld_process_info_get_platform(info); + m_dyld_process_info_release(info); + } + } + return platform; +} + +// Fetch information about all shared libraries using the dyld SPIs that exist +// in +// macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer. +JSONGenerator::ObjectSP +MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid) { + 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; + + std::vector<struct binary_image_information> image_infos; + uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(image_infos); + const size_t image_count = image_infos.size(); + for (size_t i = 0; i < image_count; i++) { + GetMachOInformationFromMemory(platform, + image_infos[i].load_address, pointer_size, + image_infos[i].macho_info); + } + return FormatDynamicLibrariesIntoJSON(image_infos); + } + return reply_sp; +} + +// Fetch information about the shared libraries at the given load addresses +// using the +// dyld SPIs that exist in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer. +JSONGenerator::ObjectSP MachProcess::GetLibrariesInfoForAddresses( + nub_process_t pid, std::vector<uint64_t> &macho_addresses) { + 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; + + std::vector<struct binary_image_information> all_image_infos; + uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(all_image_infos); + + std::vector<struct binary_image_information> image_infos; + const size_t macho_addresses_count = macho_addresses.size(); + const size_t all_image_infos_count = all_image_infos.size(); + for (size_t i = 0; i < macho_addresses_count; i++) { + for (size_t j = 0; j < all_image_infos_count; j++) { + if (all_image_infos[j].load_address == macho_addresses[i]) { + image_infos.push_back(all_image_infos[j]); + } + } + } + + const size_t image_infos_count = image_infos.size(); + for (size_t i = 0; i < image_infos_count; i++) { + GetMachOInformationFromMemory(platform, + image_infos[i].load_address, pointer_size, + image_infos[i].macho_info); + } + return FormatDynamicLibrariesIntoJSON(image_infos); + } + return reply_sp; +} + +// From dyld's internal podyld_process_info.h: + +JSONGenerator::ObjectSP MachProcess::GetSharedCacheInfo(nub_process_t pid) { + JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary()); + ; + kern_return_t kern_ret; + if (m_dyld_process_info_create && m_dyld_process_info_get_cache) { + dyld_process_info info = + m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret); + if (info) { + struct dyld_process_cache_info shared_cache_info; + m_dyld_process_info_get_cache(info, &shared_cache_info); + + reply_sp->AddIntegerItem("shared_cache_base_address", + shared_cache_info.cacheBaseAddress); + + uuid_string_t uuidstr; + uuid_unparse_upper(shared_cache_info.cacheUUID, uuidstr); + reply_sp->AddStringItem("shared_cache_uuid", uuidstr); + + reply_sp->AddBooleanItem("no_shared_cache", shared_cache_info.noCache); + reply_sp->AddBooleanItem("shared_cache_private_cache", + shared_cache_info.privateCache); + + m_dyld_process_info_release(info); + } + } + 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(); + if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail()) { + err.LogThreaded("MachProcess::Kill() DoSIGSTOP() ::ptrace " + "(PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", + m_pid, err.Status(), 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); + return true; + } 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, + static_cast<const void *>(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, static_cast<const void *>(timeout_abstime)); + m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged, + timeout_abstime); + state = GetState(); + DNBLogThreadedIf( + LOG_PROCESS, + "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, + static_cast<const void *>(timeout_abstime), DNBStateAsString(state)); + return !IsRunning(state); + } + DNBLogThreadedIf( + LOG_PROCESS, + "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", + signal, static_cast<const void *>(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 restored 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()) { + 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, static_cast<void *>(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, static_cast<void *>(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) { + 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 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) { + 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) { + // 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__, + static_cast<void *>(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__, + static_cast<void *>(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"); + DNBLogError ("MachProcess::AttachForDebug pid %d does not exist", pid); + return INVALID_NUB_PROCESS; + } + + SetState(eStateAttaching); + m_pid = pid; + 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); + DNBLogError ("MachProcess::AttachForDebug failed to start exception thread: %s", err_str); + m_pid = INVALID_NUB_PROCESS; + return INVALID_NUB_PROCESS; + } + + errno = 0; + if (::ptrace(PT_ATTACHEXC, pid, 0, 0)) { + err.SetError(errno); + DNBLogError ("MachProcess::AttachForDebug failed to ptrace(PT_ATTACHEXC): %s", err.AsString()); + } 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()); + DNBLogError ("MachProcess::AttachForDebug error: failed to attach to pid %d", pid); + + struct kinfo_proc kinfo; + 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 && len > 0) { + if (kinfo.kp_proc.p_flag & P_TRACED) { + ::snprintf(err_str, err_len, "%s - process %d is already being debugged", err.AsString(), pid); + DNBLogError ("MachProcess::AttachForDebug pid %d is already being debugged", 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) { +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101000) + return false; +#else + 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; + + [pool drain]; + + return true; +#endif +} + +std::string MachProcess::GetMacCatalystVersionString() { + @autoreleasepool { + NSDictionary *version_info = + [NSDictionary dictionaryWithContentsOfFile: + @"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *version_value = [version_info objectForKey: @"iOSSupportVersion"]; + if (const char *version_str = [version_value UTF8String]) + return version_str; + } + return {}; +} + +// 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) { + std::string empty_str; + SetFBSError(attach_error_code, empty_str, 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) { + std::string empty_str; + SetBKSError(attach_error_code, empty_str, 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, static_cast<const void *>(argv), + static_cast<const void *>(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 | eMachProcessFlagsBoardCalculated); + 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 | eMachProcessFlagsBoardCalculated); + 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.Status(), + 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, static_cast<const void *>(argv), + static_cast<const void *>(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, + const_cast<char *const *>(argv), + const_cast<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, + const_cast<char *const *>(argv), + const_cast<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::Status 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) { + //-------------------------------------------------------------- + // Status 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, const_cast<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_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::Status 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::Status 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 (ProcessUsingBackBoard()) { + 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 (ProcessUsingFrontBoard()) { + 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 (ProcessUsingBackBoard()) { + return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL); + } +#endif +#ifdef WITH_FBS + if (ProcessUsingFrontBoard()) { + 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 (ProcessUsingBackBoard()) { + 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 (ProcessUsingFrontBoard()) { + 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 + + +void MachProcess::CalculateBoardStatus() +{ + if (m_flags & eMachProcessFlagsBoardCalculated) + return; + if (m_pid == 0) + return; + +#if defined (WITH_FBS) || defined (WITH_BKS) + bool found_app_flavor = false; +#endif + +#if defined(WITH_FBS) + if (!found_app_flavor && IsFBSProcess(m_pid)) { + found_app_flavor = true; + m_flags |= eMachProcessFlagsUsingFBS; + } +#endif +#if defined(WITH_BKS) + if (!found_app_flavor && IsBKSProcess(m_pid)) { + found_app_flavor = true; + m_flags |= eMachProcessFlagsUsingBKS; + } +#endif + + m_flags |= eMachProcessFlagsBoardCalculated; +} + +bool MachProcess::ProcessUsingBackBoard() { + CalculateBoardStatus(); + return (m_flags & eMachProcessFlagsUsingBKS) != 0; +} + +bool MachProcess::ProcessUsingFrontBoard() { + CalculateBoardStatus(); + return (m_flags & eMachProcessFlagsUsingFBS) != 0; +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.h new file mode 100644 index 00000000000..c975e15a555 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.h @@ -0,0 +1,109 @@ +//===-- MachTask.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// MachTask.h +// debugserver +// +// Created by Greg Clayton on 12/5/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachTask_h__ +#define __MachTask_h__ + +#include <mach/mach.h> +#include <sys/socket.h> +#include <map> +#include <string> +#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); + void TaskPortChanged(task_t task); + 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(); + +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 &) = delete; + MachTask &operator=(const MachTask &rhs) = delete; +}; + +#endif // __MachTask_h__ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.mm new file mode 100644 index 00000000000..0d5a63a28f2 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.mm @@ -0,0 +1,963 @@ +//===-- MachTask.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// 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 "DNBDataRef.h" +#include "DNBError.h" +#include "DNBLog.h" +#include "MachProcess.h" + +#ifdef WITH_SPRINGBOARD + +#include <CoreFoundation/CoreFoundation.h> +#include <SpringBoardServices/SBSWatchdogAssertion.h> +#include <SpringBoardServices/SpringBoardServer.h> + +#endif + +#ifdef WITH_BKS +extern "C" { +#import <BackBoardServices/BKSWatchdogAssertion.h> +#import <BackBoardServices/BackBoardServices.h> +#import <Foundation/Foundation.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.Status(); +} + +//---------------------------------------------------------------------- +// 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.Status(); +} + +//---------------------------------------------------------------------- +// 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((const 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); + } + + vm_statistics64_data_t vminfo; + uint64_t physical_memory = 0; + uint64_t anonymous = 0; + uint64_t phys_footprint = 0; + uint64_t memory_cap = 0; + if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, + m_process->GetCPUType(), pid, vminfo, + physical_memory, anonymous, + phys_footprint, memory_cap)) { + 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) { + static vm_size_t pagesize = vm_kernel_page_size; + + // 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; + profile_data_stream << "used:" << total_used_count * pagesize << ';'; + + if (scanType & eProfileMemoryAnonymous) { + profile_data_stream << "anonymous:" << anonymous << ';'; + } + + profile_data_stream << "phys_footprint:" << phys_footprint << ';'; + } + + if (scanType & eProfileMemoryCap) { + profile_data_stream << "mem_cap:" << memory_cap << ';'; + } + +#ifdef LLDB_ENERGY + if (scanType & eProfileEnergy) { + 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.Status(), + err.AsString() ? err.AsString() : "success"); + if (err.Fail()) { + err.SetErrorString(str); + DNBLogError ("MachTask::TaskPortForProcessID task_for_pid failed: %s", 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.Status(); +} + +//---------------------------------------------------------------------- +// 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.Status(); +} + +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, 1); + } 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.Status() == 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.Status() == 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.Status() != 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)) { + if (exception_message.state.task_port != task) { + if (exception_message.state.IsValid()) { + // We exec'ed and our task port changed on us. + DNBLogThreadedIf(LOG_EXCEPTIONS, + "task port changed from 0x%4.4x to 0x%4.4x", + task, exception_message.state.task_port); + task = exception_message.state.task_port; + mach_task->TaskPortChanged(exception_message.state.task_port); + } + } + ++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.Status() == 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.Status() == 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; +} + +void MachTask::TaskPortChanged(task_t task) +{ + m_task = task; +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.cpp new file mode 100644 index 00000000000..80d6042caa5 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.cpp @@ -0,0 +1,782 @@ +//===-- MachThread.cpp ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/19/07. +// +//===----------------------------------------------------------------------===// + +#include "MachThread.h" +#include "DNB.h" +#include "DNBLog.h" +#include "MachProcess.h" +#include "ThreadInfo.h" +#include <dlfcn.h> +#include <inttypes.h> +#include <mach/thread_policy.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_up(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_up->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 )", + static_cast<void *>(&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)) + 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)) + 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: strlcpy(run_state_str, "running", + // run_state_str_size); break; + // case TH_STATE_STOPPED: strlcpy(run_state_str, "stopped", + // run_state_str_size); break; + // case TH_STATE_WAITING: strlcpy(run_state_str, "waiting", + // run_state_str_size); break; + // case TH_STATE_UNINTERRUPTIBLE: strlcpy(run_state_str, + // "uninterruptible", run_state_str_size); break; + // case TH_STATE_HALTED: strlcpy(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 GetPC(0) != 0; +} + +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_up->GetRegisterState(flavor, force) == KERN_SUCCESS; +} + +bool MachThread::SetRegisterState(int flavor) { + return m_arch_up->SetRegisterState(flavor) == KERN_SUCCESS; +} + +uint64_t MachThread::GetPC(uint64_t failValue) { + // Get program counter + return m_arch_up->GetPC(failValue); +} + +bool MachThread::SetPC(uint64_t value) { + // Set program counter + return m_arch_up->SetPC(value); +} + +uint64_t MachThread::GetSP(uint64_t failValue) { + // Get stack pointer + return m_arch_up->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_up->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_up->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_up->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_up->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_up->RegisterSetStateIsValid((int)regSet)) { + const size_t numRegisters = GetNumRegistersInSet(regSet); + uint32_t regIndex = 0; + DNBRegisterValueClass reg; + for (regIndex = 0; regIndex < numRegisters; ++regIndex) { + if (m_arch_up->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_up->GetRegisterValue(set, reg, value); +} + +bool MachThread::SetRegisterValue(uint32_t set, uint32_t reg, + const DNBRegisterValue *value) { + return m_arch_up->SetRegisterValue(set, reg, value); +} + +nub_size_t MachThread::GetRegisterContext(void *buf, nub_size_t buf_len) { + return m_arch_up->GetRegisterContext(buf, buf_len); +} + +nub_size_t MachThread::SetRegisterContext(const void *buf, nub_size_t buf_len) { + return m_arch_up->SetRegisterContext(buf, buf_len); +} + +uint32_t MachThread::SaveRegisterState() { + return m_arch_up->SaveRegisterState(); +} +bool MachThread::RestoreRegisterState(uint32_t save_id) { + return m_arch_up->RestoreRegisterState(save_id); +} + +uint32_t MachThread::EnableHardwareBreakpoint(const DNBBreakpoint *bp) { + if (bp != NULL && bp->IsBreakpoint()) + return m_arch_up->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_up->EnableHardwareWatchpoint( + wp->Address(), wp->ByteSize(), wp->WatchpointRead(), + wp->WatchpointWrite(), also_set_on_task); + return INVALID_NUB_HW_INDEX; +} + +bool MachThread::RollbackTransForHWP() { + return m_arch_up->RollbackTransForHWP(); +} + +bool MachThread::FinishTransForHWP() { return m_arch_up->FinishTransForHWP(); } + +bool MachThread::DisableHardwareBreakpoint(const DNBBreakpoint *bp) { + if (bp != NULL && bp->IsHardware()) + return m_arch_up->DisableHardwareBreakpoint(bp->GetHardwareIndex()); + return false; +} + +bool MachThread::DisableHardwareWatchpoint(const DNBBreakpoint *wp, + bool also_set_on_task) { + if (wp != NULL && wp->IsHardware()) + return m_arch_up->DisableHardwareWatchpoint(wp->GetHardwareIndex(), + also_set_on_task); + return false; +} + +uint32_t MachThread::NumSupportedHardwareWatchpoints() const { + return m_arch_up->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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.h new file mode 100644 index 00000000000..1634522fde4 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.h @@ -0,0 +1,169 @@ +//===-- MachThread.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 "DNBArch.h" +#include "DNBRegisterInfo.h" +#include "MachException.h" +#include "PThreadCondition.h" +#include "PThreadMutex.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_up.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_up; // 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp new file mode 100644 index 00000000000..d2aae9da0c4 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp @@ -0,0 +1,586 @@ +//===-- MachThreadList.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/19/07. +// +//===----------------------------------------------------------------------===// + +#include "MachThreadList.h" + +#include "DNB.h" +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "MachProcess.h" + +#include <inttypes.h> +#include <sys/sysctl.h> + +#include <memory> + +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 false; +} + +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 { + if (process->GetCPUType() == CPU_TYPE_ARM64_32) + DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32); + 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.Status() == 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 = std::make_shared<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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.h new file mode 100644 index 00000000000..b9b7aa8c4c9 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.h @@ -0,0 +1,96 @@ +//===-- MachThreadList.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp new file mode 100644 index 00000000000..2b039c7b16c --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp @@ -0,0 +1,296 @@ +//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#include "MachVMMemory.h" +#include "DNBLog.h" +#include "MachVMRegion.h" +#include <dlfcn.h> +#include <mach/mach_vm.h> +#include <mach/shared_region.h> +#include <sys/sysctl.h> + +#if defined(WITH_FBS) || defined(WITH_BKS) +extern "C" { +#import <System/sys/kern_memorystatus.h> +} +#endif + +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; +} + +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; +} + +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, uint64_t &anonymous, + uint64_t &phys_footprint, uint64_t &memory_cap) +{ + if (scanType & eProfileHostMemory) + physical_memory = GetPhysicalMemory(); + + if (scanType & eProfileMemory) { + static mach_port_t localHost = mach_host_self(); + mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; + host_statistics64(localHost, HOST_VM_INFO64, (host_info64_t)&vminfo, + &count); + + 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) { + if (scanType & eProfileMemoryAnonymous) { + anonymous = vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap; + } + + phys_footprint = vm_info.phys_footprint; + } + } + +#if defined(WITH_FBS) || defined(WITH_BKS) + if (scanType & eProfileMemoryCap) { + memorystatus_memlimit_properties_t memlimit_properties; + memset(&memlimit_properties, 0, sizeof(memlimit_properties)); + if (memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &memlimit_properties, sizeof(memlimit_properties)) == 0) { + memory_cap = memlimit_properties.memlimit_active; + } + } +#endif + + 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) { + // Status 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h new file mode 100644 index 00000000000..538e11232a8 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h @@ -0,0 +1,47 @@ +//===-- MachVMMemory.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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); + 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, uint64_t &anonymous, + uint64_t &phys_footprint, uint64_t &memory_cap); + +protected: + nub_size_t MaxBytesLeftInPage(task_t task, nub_addr_t addr, nub_size_t count); + + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp new file mode 100644 index 00000000000..6cb34292252 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp @@ -0,0 +1,184 @@ +//===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#include "MachVMRegion.h" +#include "DNBLog.h" +#include <assert.h> +#include <mach/mach_vm.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; + static_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. + return !((addr < m_start) || (addr >= (m_start + m_size))); +} + +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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h new file mode 100644 index 00000000000..2e6303c4b39 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h @@ -0,0 +1,72 @@ +//===-- MachVMRegion.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp new file mode 100644 index 00000000000..a83afb62b36 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp @@ -0,0 +1,63 @@ +//===-- OsLogger.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "OsLogger.h" +#include <Availability.h> + +#if (LLDB_USE_OS_LOG) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) + +#include <os/log.h> + +#include "DNBDefs.h" +#include "DNBLog.h" + +#define LLDB_OS_LOG_MAX_BUFFER_LENGTH 256 + +namespace { +// Darwin os_log logging callback that can be registered with +// DNBLogSetLogCallback +void DarwinLogCallback(void *baton, uint32_t flags, const char *format, + va_list args) { + if (format == nullptr) + return; + + static os_log_t g_logger; + if (!g_logger) { + g_logger = os_log_create("com.apple.dt.lldb", "debugserver"); + if (!g_logger) + return; + } + + os_log_type_t log_type; + if (flags & DNBLOG_FLAG_FATAL) + log_type = OS_LOG_TYPE_FAULT; + else if (flags & DNBLOG_FLAG_ERROR) + log_type = OS_LOG_TYPE_ERROR; + else if (flags & DNBLOG_FLAG_WARNING) + log_type = OS_LOG_TYPE_DEFAULT; + else if (flags & DNBLOG_FLAG_VERBOSE) + log_type = OS_LOG_TYPE_DEBUG; + else + log_type = OS_LOG_TYPE_DEFAULT; + + // This code is unfortunate. os_log* only takes static strings, but + // our current log API isn't set up to make use of that style. + char buffer[LLDB_OS_LOG_MAX_BUFFER_LENGTH]; + vsnprintf(buffer, sizeof(buffer), format, args); + os_log_with_type(g_logger, log_type, "%{public}s", buffer); +} +} + +DNBCallbackLog OsLogger::GetLogFunction() { return DarwinLogCallback; } + +#else + +DNBCallbackLog OsLogger::GetLogFunction() { return nullptr; } + +#endif + diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.h new file mode 100644 index 00000000000..5a173c42b83 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.h @@ -0,0 +1,19 @@ +//===-- OsLogger.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef OsLogger_h +#define OsLogger_h + +#include "DNBDefs.h" + +class OsLogger { +public: + static DNBCallbackLog GetLogFunction(); +}; + +#endif /* OsLogger_h */ diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h new file mode 100644 index 00000000000..00c368c6ab0 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h @@ -0,0 +1,25 @@ +//===-- ThreadInfo.h -----------------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp new file mode 100644 index 00000000000..45d05d6e0bd --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp @@ -0,0 +1,2189 @@ +//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + +#include "MacOSX/arm/DNBArchImpl.h" +#include "ARM_DWARF_Registers.h" +#include "ARM_ehframe_Registers.h" +#include "DNB.h" +#include "DNBBreakpoint.h" +#include "DNBLog.h" +#include "DNBRegisterInfo.h" +#include "MacOSX/MachProcess.h" +#include "MacOSX/MachThread.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.Status(); + } + + err = GetDBGState(false); + + if (err.Fail()) { + err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__); + return err.Status(); + } + +// 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h new file mode 100644 index 00000000000..4bb899b4503 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h @@ -0,0 +1,274 @@ +//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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); + + enum RegisterSet { + 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 + }; + + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp new file mode 100644 index 00000000000..f99dbc48b12 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp @@ -0,0 +1,2201 @@ +//===-- DNBArchImplARM64.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 "DNB.h" +#include "DNBBreakpoint.h" +#include "DNBLog.h" +#include "DNBRegisterInfo.h" +#include "MacOSX/MachProcess.h" +#include "MacOSX/MachThread.h" + +#include <inttypes.h> +#include <sys/sysctl.h> + +#if __has_feature(ptrauth_calls) +#include <ptrauth.h> +#endif + +// 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 + +// 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); + + DNBArchPluginInfo arch_plugin_info_32 = { + CPU_TYPE_ARM64_32, DNBArchMachARM64::Create, + DNBArchMachARM64::GetRegisterSetInfo, + DNBArchMachARM64::SoftwareBreakpointOpcode}; + + // Register this arch plug-in with the main protocol class + DNBArchProtocol::RegisterArchPlugin(arch_plugin_info_32); +} + +DNBArchProtocol *DNBArchMachARM64::Create(MachThread *thread) { + DNBArchMachARM64 *obj = new DNBArchMachARM64(thread); + + return obj; +} + +const uint8_t * +DNBArchMachARM64::SoftwareBreakpointOpcode(nub_size_t byte_size) { + return g_arm64_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) +#if defined(__LP64__) + return arm_thread_state64_get_pc(m_state.context.gpr); +#else + return m_state.context.gpr.__pc; +#endif + return failValue; +} + +kern_return_t DNBArchMachARM64::SetPC(uint64_t value) { + // Get program counter + kern_return_t err = GetGPRState(false); + if (err == KERN_SUCCESS) { +#if defined(__LP64__) +#if __has_feature(ptrauth_calls) + // The incoming value could be garbage. Strip it to avoid + // trapping when it gets resigned in the thread state. + value = (uint64_t) ptrauth_strip((void*) value, ptrauth_key_function_pointer); + value = (uint64_t) ptrauth_sign_unauthenticated((void*) value, ptrauth_key_function_pointer, 0); +#endif + arm_thread_state64_set_pc_fptr (m_state.context.gpr, (void*) value); +#else + m_state.context.gpr.__pc = value; +#endif + err = SetGPRState(); + } + return err == KERN_SUCCESS; +} + +uint64_t DNBArchMachARM64::GetSP(uint64_t failValue) { + // Get stack pointer + if (GetGPRState(false) == KERN_SUCCESS) +#if defined(__LP64__) + return arm_thread_state64_get_sp(m_state.context.gpr); +#else + return m_state.context.gpr.__sp; +#endif + 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], +#if defined(__LP64__) + (uint64_t) arm_thread_state64_get_fp (m_state.context.gpr), + (uint64_t) arm_thread_state64_get_lr (m_state.context.gpr), + (uint64_t) arm_thread_state64_get_sp (m_state.context.gpr), + (uint64_t) arm_thread_state64_get_pc (m_state.context.gpr), +#else + m_state.context.gpr.__fp, m_state.context.gpr.__lr, + m_state.context.gpr.__sp, m_state.context.gpr.__pc, +#endif + 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.Status(); + } + + err = GetDBGState(false); + + if (err.Fail()) { + err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__); + return err.Status(); + } + + if (enable) { + DNBLogThreadedIf(LOG_STEP, + "%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx", +#if defined(__LP64__) + __FUNCTION__, (uint64_t)arm_thread_state64_get_pc (m_state.context.gpr)); +#else + __FUNCTION__, (uint64_t)m_state.context.gpr.__pc); +#endif + m_state.dbg.__mdscr_el1 |= SS_ENABLE; + } else { + DNBLogThreadedIf(LOG_STEP, + "%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx", +#if defined(__LP64__) + __FUNCTION__, (uint64_t)arm_thread_state64_get_pc (m_state.context.gpr)); +#else + __FUNCTION__, (uint64_t)m_state.context.gpr.__pc); +#endif + 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; + + // Aarch64 watchpoints are in one of two forms: (1) 1-8 bytes, aligned to + // an 8 byte address, or (2) a power-of-two size region of memory; minimum + // 8 bytes, maximum 2GB; the starting address must be aligned to that power + // of two. + // + // For (1), 1-8 byte watchpoints, using the Byte Address Selector field in + // DBGWCR<n>.BAS. Any of the bytes may be watched, but if multiple bytes + // are watched, the bytes selected must be contiguous. The start address + // watched must be doubleword (8-byte) aligned; if the start address is + // word (4-byte) aligned, only 4 bytes can be watched. + // + // For (2), the MASK field in DBGWCR<n>.MASK is used. + // + // See the ARM ARM, section "Watchpoint exceptions", and more specifically, + // "Watchpoint data address comparisons". + // + // debugserver today only supports (1) - the Byte Address Selector 1-8 byte + // watchpoints that are 8-byte aligned. To support larger watchpoints, + // debugserver would need to interpret the mach exception when the watched + // region was hit, see if the address accessed lies within the subset + // of the power-of-two region that lldb asked us to watch (v. ARM ARM, + // "Determining the memory location that caused a Watchpoint exception"), + // and silently resume the inferior (disable watchpoint, stepi, re-enable + // watchpoint) if the address lies outside the region that lldb asked us + // to watch. + // + // Alternatively, lldb would need to be prepared for a larger region + // being watched than it requested, and silently resume the inferior if + // the accessed address is outside the region lldb wants to watch. + + 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), + // For the G/g packet we want to show where the offset into the regctx + // is for fp/lr/sp/pc, but we cannot directly access them on arm64e + // devices (and therefore can't offsetof() them)) - add the offset based + // on the last accessible register by hand for advertising the location + // in the regctx to lldb. We'll go through the accessor functions when + // we read/write them here. + { + e_regSetGPR, gpr_fp, "fp", "x29", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 8, + dwarf_fp, dwarf_fp, GENERIC_REGNUM_FP, debugserver_gpr_fp, NULL, NULL + }, + { + e_regSetGPR, gpr_lr, "lr", "x30", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 16, + dwarf_lr, dwarf_lr, GENERIC_REGNUM_RA, debugserver_gpr_lr, NULL, NULL + }, + { + e_regSetGPR, gpr_sp, "sp", "xsp", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 24, + dwarf_sp, dwarf_sp, GENERIC_REGNUM_SP, debugserver_gpr_sp, NULL, NULL + }, + { + e_regSetGPR, gpr_pc, "pc", NULL, Uint, Hex, 8, GPR_OFFSET_IDX(28) + 32, + dwarf_pc, dwarf_pc, GENERIC_REGNUM_PC, debugserver_gpr_pc, NULL, NULL + }, + + // 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) { +#if defined(__LP64__) + if (reg == gpr_pc) + value->value.uint64 = arm_thread_state64_get_pc (m_state.context.gpr); + else if (reg == gpr_lr) + value->value.uint64 = arm_thread_state64_get_lr (m_state.context.gpr); + else if (reg == gpr_sp) + value->value.uint64 = arm_thread_state64_get_sp (m_state.context.gpr); + else if (reg == gpr_fp) + value->value.uint64 = arm_thread_state64_get_fp (m_state.context.gpr); + else + value->value.uint64 = m_state.context.gpr.__x[reg]; +#else + value->value.uint64 = m_state.context.gpr.__x[reg]; +#endif + 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) { +#if defined(__LP64__) + uint64_t signed_value = value->value.uint64; +#if __has_feature(ptrauth_calls) + // The incoming value could be garbage. Strip it to avoid + // trapping when it gets resigned in the thread state. + signed_value = (uint64_t) ptrauth_strip((void*) signed_value, ptrauth_key_function_pointer); + signed_value = (uint64_t) ptrauth_sign_unauthenticated((void*) signed_value, ptrauth_key_function_pointer, 0); +#endif + if (reg == gpr_pc) + arm_thread_state64_set_pc_fptr (m_state.context.gpr, (void*) signed_value); + else if (reg == gpr_lr) + arm_thread_state64_set_lr_fptr (m_state.context.gpr, (void*) signed_value); + else if (reg == gpr_sp) + arm_thread_state64_set_sp (m_state.context.gpr, value->value.uint64); + else if (reg == gpr_fp) + arm_thread_state64_set_fp (m_state.context.gpr, value->value.uint64); + else + m_state.context.gpr.__x[reg] = value->value.uint64; +#else + m_state.context.gpr.__x[reg] = value->value.uint64; +#endif + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h new file mode 100644 index 00000000000..2c59a0ceb5d --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h @@ -0,0 +1,248 @@ +//===-- DNBArchImplARM64.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBArchImplARM64_h__ +#define __DNBArchImplARM64_h__ + +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + +#include <mach/thread_status.h> +#include <map> + +#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 ®); + + enum RegisterSet { + 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 + }; + + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs new file mode 100644 index 00000000000..cd5be170070 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs @@ -0,0 +1,5 @@ +/* + * nub.defs + */ + +#import <mach/mach_exc.defs> diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp new file mode 100644 index 00000000000..7d2d0c2ef1b --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp @@ -0,0 +1,2382 @@ +//===-- DNBArchImplI386.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#include <sys/cdefs.h> + +#include "DNBLog.h" +#include "MacOSX/i386/DNBArchImplI386.h" +#include "MachProcess.h" +#include "MachThread.h" + +extern "C" bool CPUHasAVX(); // Defined over in DNBArchImplX86_64.cpp +extern "C" bool CPUHasAVX512f(); // 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, + fpu_k0, + fpu_k1, + fpu_k2, + fpu_k3, + fpu_k4, + fpu_k5, + fpu_k6, + fpu_k7, + fpu_zmm0, + fpu_zmm1, + fpu_zmm2, + fpu_zmm3, + fpu_zmm4, + fpu_zmm5, + fpu_zmm6, + fpu_zmm7, + 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, + dwarf_zmm0 = dwarf_xmm0, + dwarf_zmm1 = dwarf_xmm1, + dwarf_zmm2 = dwarf_xmm2, + dwarf_zmm3 = dwarf_xmm3, + dwarf_zmm4 = dwarf_xmm4, + dwarf_zmm5 = dwarf_xmm5, + dwarf_zmm6 = dwarf_xmm6, + dwarf_zmm7 = dwarf_xmm7, + dwarf_k0 = 118, + dwarf_k1, + dwarf_k2, + dwarf_k3, + dwarf_k4, + dwarf_k5, + dwarf_k6, + dwarf_k7, +}; + +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, + debugserver_zmm0 = debugserver_xmm0, + debugserver_zmm1 = debugserver_xmm1, + debugserver_zmm2 = debugserver_xmm2, + debugserver_zmm3 = debugserver_xmm3, + debugserver_zmm4 = debugserver_xmm4, + debugserver_zmm5 = debugserver_xmm5, + debugserver_zmm6 = debugserver_xmm6, + debugserver_zmm7 = debugserver_xmm7, + debugserver_k0 = 118, + debugserver_k1 = 119, + debugserver_k2 = 120, + debugserver_k3 = 121, + debugserver_k4 = 122, + debugserver_k5 = 123, + debugserver_k6 = 124, + debugserver_k7 = 125, +}; + +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) { + + 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; + for (int 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 (int 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; + + if (CPUHasAVX() || FORCE_AVX_REGS) { + for (int i = 0; i < sizeof(m_state.context.fpu.avx.__avx_reserved1); + ++i) + m_state.context.fpu.avx.__avx_reserved1[i] = INT8_MIN; + + for (int 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'; + } + } + if (CPUHasAVX512f() || FORCE_AVX_REGS) { + for (int i = 0; i < 8; ++i) { + m_state.context.fpu.avx512f.__fpu_k0.__opmask_reg[i] = '0'; + m_state.context.fpu.avx512f.__fpu_k1.__opmask_reg[i] = '1'; + m_state.context.fpu.avx512f.__fpu_k2.__opmask_reg[i] = '2'; + m_state.context.fpu.avx512f.__fpu_k3.__opmask_reg[i] = '3'; + m_state.context.fpu.avx512f.__fpu_k4.__opmask_reg[i] = '4'; + m_state.context.fpu.avx512f.__fpu_k5.__opmask_reg[i] = '5'; + m_state.context.fpu.avx512f.__fpu_k6.__opmask_reg[i] = '6'; + m_state.context.fpu.avx512f.__fpu_k7.__opmask_reg[i] = '7'; + } + + for (int i = 0; i < 32; ++i) { + m_state.context.fpu.avx512f.__fpu_zmmh0.__ymm_reg[i] = '0'; + m_state.context.fpu.avx512f.__fpu_zmmh1.__ymm_reg[i] = '1'; + m_state.context.fpu.avx512f.__fpu_zmmh2.__ymm_reg[i] = '2'; + m_state.context.fpu.avx512f.__fpu_zmmh3.__ymm_reg[i] = '3'; + m_state.context.fpu.avx512f.__fpu_zmmh4.__ymm_reg[i] = '4'; + m_state.context.fpu.avx512f.__fpu_zmmh5.__ymm_reg[i] = '5'; + m_state.context.fpu.avx512f.__fpu_zmmh6.__ymm_reg[i] = '6'; + m_state.context.fpu.avx512f.__fpu_zmmh7.__ymm_reg[i] = '7'; + } + } + m_state.SetError(e_regSetFPU, Read, 0); + } else { + mach_msg_type_number_t count = e_regSetWordSizeFPU; + int flavor = __i386_FLOAT_STATE; + + // On a machine with the AVX512 register set, a process only gets a + // full AVX512 register context after it uses the AVX512 registers; + // if the process has not yet triggered this change, trying to fetch + // the AVX512 registers will fail. Fall through to fetching the AVX + // registers. + if (CPUHasAVX512f() || FORCE_AVX_REGS) { + count = e_regSetWordSizeAVX512f; + flavor = __i386_AVX512F_STATE; + m_state.SetError(e_regSetFPU, Read, + ::thread_get_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.fpu, + &count)); + DNBLogThreadedIf(LOG_THREAD, + "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x", + m_thread->MachPortNumber(), flavor, (uint32_t)count, + m_state.GetError(e_regSetFPU, Read)); + if (m_state.GetError(e_regSetFPU, Read) == KERN_SUCCESS) + return m_state.GetError(e_regSetFPU, Read); + } + if (CPUHasAVX()) { + count = e_regSetWordSizeAVX; + flavor = __i386_AVX_STATE; + } + m_state.SetError(e_regSetFPU, Read, + ::thread_get_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.fpu, + &count)); + DNBLogThreadedIf(LOG_THREAD, + "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x", + m_thread->MachPortNumber(), flavor, (uint32_t)count, + 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 { + int flavor = __i386_FLOAT_STATE; + mach_msg_type_number_t count = e_regSetWordSizeFPU; + if (CPUHasAVX512f() || FORCE_AVX_REGS) { + flavor = __i386_AVX512F_STATE; + count = e_regSetWordSizeAVX512f; + } else + if (CPUHasAVX()) { + flavor = __i386_AVX_STATE; + count = e_regSetWordSizeAVX; + } + + m_state.SetError(e_regSetFPU, Write, + ::thread_set_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.fpu, + count)); + 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); + + return kret == KERN_SUCCESS; +} +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 && !write) + 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 AVX512F_OFFSET(reg) \ + (offsetof(DNBArchImplI386::AVX512F, __fpu_##reg) + \ + offsetof(DNBArchImplI386::Context, fpu.avx512f)) +#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 FPU_SIZE_ZMM(reg) (64) +#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)) + +// TODO: Test this and come back. +#define AVX512F_OFFSET_ZMM(n) (AVX_OFFSET_YMM(7) + FPU_SIZE_XMM(xmm7) + (64 * 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, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, + 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, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, + 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}, + +}; + + +#define STR(s) #s + +#define ZMM_REG_DEF(reg) \ + { \ + e_regSetFPU, fpu_zmm##reg, STR(zmm##reg), NULL, Vector, VectorOfUInt8, \ + FPU_SIZE_ZMM(zmm##reg), AVX512F_OFFSET_ZMM(reg), INVALID_NUB_REGNUM, \ + dwarf_zmm##reg, INVALID_NUB_REGNUM, debugserver_zmm##reg, NULL, NULL \ + } + +#define YMM_REG_ALIAS(reg) \ + { \ + e_regSetFPU, fpu_ymm##reg, STR(ymm##reg), NULL, Vector, VectorOfUInt8, \ + FPU_SIZE_YMM(ymm##reg), 0, INVALID_NUB_REGNUM, dwarf_ymm##reg, \ + INVALID_NUB_REGNUM, debugserver_ymm##reg, g_contained_zmm##reg, NULL \ + } + +#define XMM_REG_ALIAS(reg) \ + { \ + e_regSetFPU, fpu_xmm##reg, STR(xmm##reg), NULL, Vector, VectorOfUInt8, \ + FPU_SIZE_XMM(xmm##reg), 0, INVALID_NUB_REGNUM, dwarf_xmm##reg, \ + INVALID_NUB_REGNUM, debugserver_xmm##reg, g_contained_zmm##reg, NULL \ + } + +#define AVX512_K_REG_DEF(reg) \ + { \ + e_regSetFPU, fpu_k##reg, STR(k##reg), NULL, Vector, VectorOfUInt8, 8, \ + AVX512F_OFFSET(k##reg), dwarf_k##reg, dwarf_k##reg, -1U, \ + debugserver_k##reg, NULL, NULL \ + } + +static const char *g_contained_zmm0[] = {"zmm0", NULL}; +static const char *g_contained_zmm1[] = {"zmm1", NULL}; +static const char *g_contained_zmm2[] = {"zmm2", NULL}; +static const char *g_contained_zmm3[] = {"zmm3", NULL}; +static const char *g_contained_zmm4[] = {"zmm4", NULL}; +static const char *g_contained_zmm5[] = {"zmm5", NULL}; +static const char *g_contained_zmm6[] = {"zmm6", NULL}; +static const char *g_contained_zmm7[] = {"zmm7", NULL}; + +const DNBRegisterInfo DNBArchImplI386::g_fpu_registers_avx512f[] = { + {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, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, + 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), + 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}, + + AVX512_K_REG_DEF(0), + AVX512_K_REG_DEF(1), + AVX512_K_REG_DEF(2), + AVX512_K_REG_DEF(3), + AVX512_K_REG_DEF(4), + AVX512_K_REG_DEF(5), + AVX512_K_REG_DEF(6), + AVX512_K_REG_DEF(7), + + ZMM_REG_DEF(0), + ZMM_REG_DEF(1), + ZMM_REG_DEF(2), + ZMM_REG_DEF(3), + ZMM_REG_DEF(4), + ZMM_REG_DEF(5), + ZMM_REG_DEF(6), + ZMM_REG_DEF(7), + + YMM_REG_ALIAS(0), + YMM_REG_ALIAS(1), + YMM_REG_ALIAS(2), + YMM_REG_ALIAS(3), + YMM_REG_ALIAS(4), + YMM_REG_ALIAS(5), + YMM_REG_ALIAS(6), + YMM_REG_ALIAS(7), + + XMM_REG_ALIAS(0), + XMM_REG_ALIAS(1), + XMM_REG_ALIAS(2), + XMM_REG_ALIAS(3), + XMM_REG_ALIAS(4), + XMM_REG_ALIAS(5), + XMM_REG_ALIAS(6), + XMM_REG_ALIAS(7) + +}; + +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_fpu_registers_avx512f = + sizeof(g_fpu_registers_avx512f) / 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; +const size_t DNBArchImplI386::k_num_all_registers_avx512f = + k_num_gpr_registers + k_num_fpu_registers_avx512f + 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}}; + +const DNBRegisterSetInfo DNBArchImplI386::g_reg_sets_avx512f[] = { + {"i386 Registers", NULL, k_num_all_registers_avx512f}, + {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers}, + {"Floating Point Registers", g_fpu_registers_avx512f, + k_num_fpu_registers_avx512f}, + {"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_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 (CPUHasAVX512f() || FORCE_AVX_REGS) + return g_reg_sets_avx512f; + if (CPUHasAVX()) + 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 (reg > fpu_xmm7 && !(CPUHasAVX() || FORCE_AVX_REGS)) + return false; + if (reg > fpu_ymm7 && !(CPUHasAVX512f() || FORCE_AVX_REGS)) + return false; + 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: + memcpy (&value->value.uint16, &m_state.context.fpu.no_avx.__fpu_ftw, 2); + 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; + +#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 + + case fpu_k0: + case fpu_k1: + case fpu_k2: + case fpu_k3: + case fpu_k4: + case fpu_k5: + case fpu_k6: + case fpu_k7: + memcpy((&value->value.uint8), + &m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0), 8); + return true; + case fpu_zmm0: + case fpu_zmm1: + case fpu_zmm2: + case fpu_zmm3: + case fpu_zmm4: + case fpu_zmm5: + case fpu_zmm6: + case fpu_zmm7: + memcpy(&value->value.uint8, + &m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0), 16); + memcpy(&value->value.uint8 + 16, + &m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0), 16); + memcpy(&value->value.uint8 + 32, + &m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0), 32); + 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 (reg > fpu_xmm7 && !(CPUHasAVX() || FORCE_AVX_REGS)) + return false; + if (reg > fpu_ymm7 && !(CPUHasAVX512f() || FORCE_AVX_REGS)) + return false; + 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: + memcpy (&m_state.context.fpu.no_avx.__fpu_ftw, &value->value.uint16, 2); + 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; + +#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 + + case fpu_k0: + case fpu_k1: + case fpu_k2: + case fpu_k3: + case fpu_k4: + case fpu_k5: + case fpu_k6: + case fpu_k7: + memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0), + &value->value.uint8, 8); + return true; + case fpu_zmm0: + case fpu_zmm1: + case fpu_zmm2: + case fpu_zmm3: + case fpu_zmm4: + case fpu_zmm5: + case fpu_zmm6: + case fpu_zmm7: + memcpy(&m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0), + &value->value.uint8, 16); + memcpy(&m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0), + &value->value.uint8 + 16, 16); + memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0), + &value->value.uint8 + 32, 32); + return true; + } + 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(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < k_num_fpu_registers_avx512f; ++i) { + if (g_fpu_registers_avx512f[i].value_regs == NULL) + g_cached_size += g_fpu_registers_avx512f[i].size; + } + } else + if (CPUHasAVX()) { + 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); + + // 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; + } + + if (CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 8; ++i) { + memcpy(p, &m_state.context.fpu.avx512f.__fpu_k0 + i, 8); + p += 8; + } + } + + if (CPUHasAVX() || FORCE_AVX_REGS) { + // 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; + } + if(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 8; ++i) { + memcpy(p, &m_state.context.fpu.avx512f.__fpu_zmmh0 + i, 32); + p += 32; + } + } + } else { + // 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; + + const uint8_t *p = (const uint8_t *)buf; + // Copy the GPR registers + memcpy(&m_state.context.gpr, p, sizeof(GPR)); + p += sizeof(GPR); + + // 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; + } + + if(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 8; ++i) { + memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + i, p, 8); + p += 8; + } + } + + if (CPUHasAVX() || FORCE_AVX_REGS) { + // 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; + } + + if(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 8; ++i) { + memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + i, p, 32); + p += 32; + } + } + } else { + // 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 - (const 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h new file mode 100644 index 00000000000..12b515a2957 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h @@ -0,0 +1,238 @@ +//===-- DNBArchImplI386.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBArchImplI386_h__ +#define __DNBArchImplI386_h__ + +#if defined(__i386__) || defined(__x86_64__) + +#include "DNBArch.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 __i386_avx512f_state_t AVX512F; + static const DNBRegisterInfo g_fpu_registers_avx512f[]; + static const DNBRegisterSetInfo g_reg_sets_avx512f[]; + static const size_t k_num_fpu_registers_avx512f; + static const size_t k_num_all_registers_avx512f; + + enum RegisterSet { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPU, + e_regSetEXC, + e_regSetDBG, + kNumRegisterSets + }; + + enum RegisterSetWordSize { + 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_regSetWordSizeAVX512f = sizeof(AVX512F) / sizeof(int), + e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int) + }; + + enum { Read = 0, Write = 1, kNumErrors = 2 }; + + struct Context { + GPR gpr; + union { + FPU no_avx; + AVX avx; + AVX512F avx512f; + } 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h new file mode 100644 index 00000000000..6eab0ad7a5a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h @@ -0,0 +1,241 @@ +//===-- MachRegisterStatesI386.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 +#define __i386_AVX512F_STATE 19 + +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 { uint8_t __ymm_reg[32]; } __i386_ymm_reg; +typedef struct { uint8_t __opmask_reg[8]; } __i386_opmask_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; + 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_opmask_reg __fpu_k0; + __i386_opmask_reg __fpu_k1; + __i386_opmask_reg __fpu_k2; + __i386_opmask_reg __fpu_k3; + __i386_opmask_reg __fpu_k4; + __i386_opmask_reg __fpu_k5; + __i386_opmask_reg __fpu_k6; + __i386_opmask_reg __fpu_k7; + __i386_ymm_reg __fpu_zmmh0; + __i386_ymm_reg __fpu_zmmh1; + __i386_ymm_reg __fpu_zmmh2; + __i386_ymm_reg __fpu_zmmh3; + __i386_ymm_reg __fpu_zmmh4; + __i386_ymm_reg __fpu_zmmh5; + __i386_ymm_reg __fpu_zmmh6; + __i386_ymm_reg __fpu_zmmh7; +} __i386_avx512f_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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp new file mode 100644 index 00000000000..c6671b5066a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp @@ -0,0 +1,487 @@ +//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 "DNBBreakpoint.h" +#include "DNBLog.h" +#include "DNBRegisterInfo.h" +#include "MacOSX/MachThread.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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h new file mode 100644 index 00000000000..7d20f18d027 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h @@ -0,0 +1,159 @@ +//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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); + + enum RegisterSet { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPR, + e_regSetEXC, + e_regSetVEC, + kNumRegisterSets + }; + + 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/stack_logging.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/stack_logging.h new file mode 100644 index 00000000000..5209e38a08e --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/stack_logging.h @@ -0,0 +1,158 @@ +/* + * 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp new file mode 100644 index 00000000000..319215e8a7f --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp @@ -0,0 +1,2885 @@ +//===-- DNBArchImplX86_64.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#include <sys/cdefs.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +#include "DNBLog.h" +#include "MacOSX/x86_64/DNBArchImplX86_64.h" +#include "MachProcess.h" +#include "MachThread.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 + +bool DetectHardwareFeature(const char *feature) { + int answer = 0; + size_t answer_size = sizeof(answer); + int error = ::sysctlbyname(feature, &answer, &answer_size, NULL, 0); + return error == 0 && answer != 0; +} + +enum AVXPresence { eAVXUnknown = -1, eAVXNotPresent = 0, eAVXPresent = 1 }; + +bool LogAVXAndReturn(AVXPresence has_avx, int err, const char * os_ver) { + DNBLogThreadedIf(LOG_THREAD, + "CPUHasAVX(): g_has_avx = %i (err = %i, os_ver = %s)", + has_avx, err, os_ver); + return (has_avx == eAVXPresent); +} + +extern "C" bool CPUHasAVX() { + static AVXPresence g_has_avx = eAVXUnknown; + if (g_has_avx != eAVXUnknown) + return LogAVXAndReturn(g_has_avx, 0, ""); + + g_has_avx = eAVXNotPresent; + + // OS X 10.7.3 and earlier have a bug in thread_get_state that truncated the + // size of the return. To work around this we have to disable AVX debugging + // on hosts prior to 10.7.3 (<rdar://problem/10122874>). + int mib[2]; + char buffer[1024]; + size_t length = sizeof(buffer); + mib[0] = CTL_KERN; + mib[1] = KERN_OSVERSION; + + // KERN_OSVERSION returns the build number which is a number signifying the + // major version, a capitol letter signifying the minor version, and numbers + // signifying the build (ex: on 10.12.3, the returned value is 16D32). + int err = ::sysctl(mib, 2, &buffer, &length, NULL, 0); + if (err != 0) + return LogAVXAndReturn(g_has_avx, err, ""); + + size_t first_letter = 0; + for (; first_letter < length; ++first_letter) { + // This is looking for the first uppercase letter + if (isupper(buffer[first_letter])) + break; + } + char letter = buffer[first_letter]; + buffer[first_letter] = '\0'; + auto major_ver = strtoull(buffer, NULL, 0); + buffer[first_letter] = letter; + + // In this check we're looking to see that our major and minor version numer + // was >= 11E, which is the 10.7.4 release. + if (major_ver < 11 || (major_ver == 11 && letter < 'E')) + return LogAVXAndReturn(g_has_avx, err, buffer); + if (DetectHardwareFeature("hw.optional.avx1_0")) + g_has_avx = eAVXPresent; + + return LogAVXAndReturn(g_has_avx, err, buffer); +} + +extern "C" bool CPUHasAVX512f() { + static AVXPresence g_has_avx512f = eAVXUnknown; + if (g_has_avx512f != eAVXUnknown) + return g_has_avx512f == eAVXPresent; + + g_has_avx512f = DetectHardwareFeature("hw.optional.avx512f") ? eAVXPresent + : eAVXNotPresent; + + return (g_has_avx512f == 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) { + 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; + for (int 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 (int 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; + + if (CPUHasAVX() || FORCE_AVX_REGS) { + for (int i = 0; i < 16; ++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 (int i = 0; i < sizeof(m_state.context.fpu.avx.__avx_reserved1); ++i) + m_state.context.fpu.avx.__avx_reserved1[i] = INT8_MIN; + } + if (CPUHasAVX512f() || FORCE_AVX_REGS) { + for (int i = 0; i < 8; ++i) { + m_state.context.fpu.avx512f.__fpu_k0.__opmask_reg[i] = '0'; + m_state.context.fpu.avx512f.__fpu_k1.__opmask_reg[i] = '1'; + m_state.context.fpu.avx512f.__fpu_k2.__opmask_reg[i] = '2'; + m_state.context.fpu.avx512f.__fpu_k3.__opmask_reg[i] = '3'; + m_state.context.fpu.avx512f.__fpu_k4.__opmask_reg[i] = '4'; + m_state.context.fpu.avx512f.__fpu_k5.__opmask_reg[i] = '5'; + m_state.context.fpu.avx512f.__fpu_k6.__opmask_reg[i] = '6'; + m_state.context.fpu.avx512f.__fpu_k7.__opmask_reg[i] = '7'; + } + + for (int i = 0; i < 32; ++i) { + m_state.context.fpu.avx512f.__fpu_zmmh0.__ymm_reg[i] = '0'; + m_state.context.fpu.avx512f.__fpu_zmmh1.__ymm_reg[i] = '1'; + m_state.context.fpu.avx512f.__fpu_zmmh2.__ymm_reg[i] = '2'; + m_state.context.fpu.avx512f.__fpu_zmmh3.__ymm_reg[i] = '3'; + m_state.context.fpu.avx512f.__fpu_zmmh4.__ymm_reg[i] = '4'; + m_state.context.fpu.avx512f.__fpu_zmmh5.__ymm_reg[i] = '5'; + m_state.context.fpu.avx512f.__fpu_zmmh6.__ymm_reg[i] = '6'; + m_state.context.fpu.avx512f.__fpu_zmmh7.__ymm_reg[i] = '7'; + m_state.context.fpu.avx512f.__fpu_zmmh8.__ymm_reg[i] = '8'; + m_state.context.fpu.avx512f.__fpu_zmmh9.__ymm_reg[i] = '9'; + m_state.context.fpu.avx512f.__fpu_zmmh10.__ymm_reg[i] = 'A'; + m_state.context.fpu.avx512f.__fpu_zmmh11.__ymm_reg[i] = 'B'; + m_state.context.fpu.avx512f.__fpu_zmmh12.__ymm_reg[i] = 'C'; + m_state.context.fpu.avx512f.__fpu_zmmh13.__ymm_reg[i] = 'D'; + m_state.context.fpu.avx512f.__fpu_zmmh14.__ymm_reg[i] = 'E'; + m_state.context.fpu.avx512f.__fpu_zmmh15.__ymm_reg[i] = 'F'; + } + for (int i = 0; i < 64; ++i) { + m_state.context.fpu.avx512f.__fpu_zmm16.__zmm_reg[i] = 'G'; + m_state.context.fpu.avx512f.__fpu_zmm17.__zmm_reg[i] = 'H'; + m_state.context.fpu.avx512f.__fpu_zmm18.__zmm_reg[i] = 'I'; + m_state.context.fpu.avx512f.__fpu_zmm19.__zmm_reg[i] = 'J'; + m_state.context.fpu.avx512f.__fpu_zmm20.__zmm_reg[i] = 'K'; + m_state.context.fpu.avx512f.__fpu_zmm21.__zmm_reg[i] = 'L'; + m_state.context.fpu.avx512f.__fpu_zmm22.__zmm_reg[i] = 'M'; + m_state.context.fpu.avx512f.__fpu_zmm23.__zmm_reg[i] = 'N'; + m_state.context.fpu.avx512f.__fpu_zmm24.__zmm_reg[i] = 'O'; + m_state.context.fpu.avx512f.__fpu_zmm25.__zmm_reg[i] = 'P'; + m_state.context.fpu.avx512f.__fpu_zmm26.__zmm_reg[i] = 'Q'; + m_state.context.fpu.avx512f.__fpu_zmm27.__zmm_reg[i] = 'R'; + m_state.context.fpu.avx512f.__fpu_zmm28.__zmm_reg[i] = 'S'; + m_state.context.fpu.avx512f.__fpu_zmm29.__zmm_reg[i] = 'T'; + m_state.context.fpu.avx512f.__fpu_zmm30.__zmm_reg[i] = 'U'; + m_state.context.fpu.avx512f.__fpu_zmm31.__zmm_reg[i] = 'V'; + } + } + m_state.SetError(e_regSetFPU, Read, 0); + } else { + mach_msg_type_number_t count = e_regSetWordSizeFPU; + int flavor = __x86_64_FLOAT_STATE; + // On a machine with the AVX512 register set, a process only gets a + // full AVX512 register context after it uses the AVX512 registers; + // if the process has not yet triggered this change, trying to fetch + // the AVX512 registers will fail. Fall through to fetching the AVX + // registers. + if (CPUHasAVX512f() || FORCE_AVX_REGS) { + count = e_regSetWordSizeAVX512f; + flavor = __x86_64_AVX512F_STATE; + m_state.SetError(e_regSetFPU, Read, + ::thread_get_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.fpu, + &count)); + DNBLogThreadedIf(LOG_THREAD, + "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x", + m_thread->MachPortNumber(), flavor, (uint32_t)count, + m_state.GetError(e_regSetFPU, Read)); + + if (m_state.GetError(e_regSetFPU, Read) == KERN_SUCCESS) + return m_state.GetError(e_regSetFPU, Read); + else + DNBLogThreadedIf(LOG_THREAD, + "::thread_get_state attempted fetch of avx512 fpu regctx failed, will try fetching avx"); + } + if (CPUHasAVX() || FORCE_AVX_REGS) { + count = e_regSetWordSizeAVX; + flavor = __x86_64_AVX_STATE; + } + m_state.SetError(e_regSetFPU, Read, + ::thread_get_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.fpu, + &count)); + DNBLogThreadedIf(LOG_THREAD, + "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x", + m_thread->MachPortNumber(), flavor, (uint32_t)count, + 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 { + int flavor = __x86_64_FLOAT_STATE; + mach_msg_type_number_t count = e_regSetWordSizeFPU; + if (CPUHasAVX512f() || FORCE_AVX_REGS) { + count = e_regSetWordSizeAVX512f; + flavor = __x86_64_AVX512F_STATE; + m_state.SetError( + e_regSetFPU, Write, + ::thread_set_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.fpu, count)); + if (m_state.GetError(e_regSetFPU, Write) == KERN_SUCCESS) + return m_state.GetError(e_regSetFPU, Write); + else + DNBLogThreadedIf(LOG_THREAD, + "::thread_get_state attempted save of avx512 fpu regctx failed, will try saving avx regctx"); + } + + if (CPUHasAVX() || FORCE_AVX_REGS) { + flavor = __x86_64_AVX_STATE; + count = e_regSetWordSizeAVX; + } + m_state.SetError( + e_regSetFPU, Write, + ::thread_set_state(m_thread->MachPortNumber(), flavor, + (thread_state_t)&m_state.context.fpu, count)); + 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); + + return kret == KERN_SUCCESS; +} +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 && !write) + 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, + fpu_k0, + fpu_k1, + fpu_k2, + fpu_k3, + fpu_k4, + fpu_k5, + fpu_k6, + fpu_k7, + fpu_zmm0, + fpu_zmm1, + fpu_zmm2, + fpu_zmm3, + fpu_zmm4, + fpu_zmm5, + fpu_zmm6, + fpu_zmm7, + fpu_zmm8, + fpu_zmm9, + fpu_zmm10, + fpu_zmm11, + fpu_zmm12, + fpu_zmm13, + fpu_zmm14, + fpu_zmm15, + fpu_zmm16, + fpu_zmm17, + fpu_zmm18, + fpu_zmm19, + fpu_zmm20, + fpu_zmm21, + fpu_zmm22, + fpu_zmm23, + fpu_zmm24, + fpu_zmm25, + fpu_zmm26, + fpu_zmm27, + fpu_zmm28, + fpu_zmm29, + fpu_zmm30, + fpu_zmm31, + 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, + ehframe_dwarf_zmm0 = ehframe_dwarf_xmm0, + ehframe_dwarf_zmm1 = ehframe_dwarf_xmm1, + ehframe_dwarf_zmm2 = ehframe_dwarf_xmm2, + ehframe_dwarf_zmm3 = ehframe_dwarf_xmm3, + ehframe_dwarf_zmm4 = ehframe_dwarf_xmm4, + ehframe_dwarf_zmm5 = ehframe_dwarf_xmm5, + ehframe_dwarf_zmm6 = ehframe_dwarf_xmm6, + ehframe_dwarf_zmm7 = ehframe_dwarf_xmm7, + ehframe_dwarf_zmm8 = ehframe_dwarf_xmm8, + ehframe_dwarf_zmm9 = ehframe_dwarf_xmm9, + ehframe_dwarf_zmm10 = ehframe_dwarf_xmm10, + ehframe_dwarf_zmm11 = ehframe_dwarf_xmm11, + ehframe_dwarf_zmm12 = ehframe_dwarf_xmm12, + ehframe_dwarf_zmm13 = ehframe_dwarf_xmm13, + ehframe_dwarf_zmm14 = ehframe_dwarf_xmm14, + ehframe_dwarf_zmm15 = ehframe_dwarf_xmm15, + ehframe_dwarf_zmm16 = 67, + ehframe_dwarf_zmm17, + ehframe_dwarf_zmm18, + ehframe_dwarf_zmm19, + ehframe_dwarf_zmm20, + ehframe_dwarf_zmm21, + ehframe_dwarf_zmm22, + ehframe_dwarf_zmm23, + ehframe_dwarf_zmm24, + ehframe_dwarf_zmm25, + ehframe_dwarf_zmm26, + ehframe_dwarf_zmm27, + ehframe_dwarf_zmm28, + ehframe_dwarf_zmm29, + ehframe_dwarf_zmm30, + ehframe_dwarf_zmm31, + ehframe_dwarf_k0 = 118, + ehframe_dwarf_k1, + ehframe_dwarf_k2, + ehframe_dwarf_k3, + ehframe_dwarf_k4, + ehframe_dwarf_k5, + ehframe_dwarf_k6, + ehframe_dwarf_k7, +}; + +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, + debugserver_zmm0 = debugserver_xmm0, + debugserver_zmm1 = debugserver_xmm1, + debugserver_zmm2 = debugserver_xmm2, + debugserver_zmm3 = debugserver_xmm3, + debugserver_zmm4 = debugserver_xmm4, + debugserver_zmm5 = debugserver_xmm5, + debugserver_zmm6 = debugserver_xmm6, + debugserver_zmm7 = debugserver_xmm7, + debugserver_zmm8 = debugserver_xmm8, + debugserver_zmm9 = debugserver_xmm9, + debugserver_zmm10 = debugserver_xmm10, + debugserver_zmm11 = debugserver_xmm11, + debugserver_zmm12 = debugserver_xmm12, + debugserver_zmm13 = debugserver_xmm13, + debugserver_zmm14 = debugserver_xmm14, + debugserver_zmm15 = debugserver_xmm15, + debugserver_zmm16 = 67, + debugserver_zmm17 = 68, + debugserver_zmm18 = 69, + debugserver_zmm19 = 70, + debugserver_zmm20 = 71, + debugserver_zmm21 = 72, + debugserver_zmm22 = 73, + debugserver_zmm23 = 74, + debugserver_zmm24 = 75, + debugserver_zmm25 = 76, + debugserver_zmm26 = 77, + debugserver_zmm27 = 78, + debugserver_zmm28 = 79, + debugserver_zmm29 = 80, + debugserver_zmm30 = 81, + debugserver_zmm31 = 82, + debugserver_k0 = 118, + debugserver_k1 = 119, + debugserver_k2 = 120, + debugserver_k3 = 121, + debugserver_k4 = 122, + debugserver_k5 = 123, + debugserver_k6 = 124, + debugserver_k7 = 125, +}; + +#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 AVX512F_OFFSET(reg) \ + (offsetof(DNBArchImplX86_64::AVX512F, __fpu_##reg) + \ + offsetof(DNBArchImplX86_64::Context, fpu.avx512f)) +#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 AVX512F_OFFSET_ZMM(n) (AVX512F_OFFSET(zmmh0) + (64 * 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 FPU_SIZE_ZMM(reg) (64) +#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, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, + 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, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, + 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} + +}; + +static const char *g_contained_zmm0[] = {"zmm0", NULL}; +static const char *g_contained_zmm1[] = {"zmm1", NULL}; +static const char *g_contained_zmm2[] = {"zmm2", NULL}; +static const char *g_contained_zmm3[] = {"zmm3", NULL}; +static const char *g_contained_zmm4[] = {"zmm4", NULL}; +static const char *g_contained_zmm5[] = {"zmm5", NULL}; +static const char *g_contained_zmm6[] = {"zmm6", NULL}; +static const char *g_contained_zmm7[] = {"zmm7", NULL}; +static const char *g_contained_zmm8[] = {"zmm8", NULL}; +static const char *g_contained_zmm9[] = {"zmm9", NULL}; +static const char *g_contained_zmm10[] = {"zmm10", NULL}; +static const char *g_contained_zmm11[] = {"zmm11", NULL}; +static const char *g_contained_zmm12[] = {"zmm12", NULL}; +static const char *g_contained_zmm13[] = {"zmm13", NULL}; +static const char *g_contained_zmm14[] = {"zmm14", NULL}; +static const char *g_contained_zmm15[] = {"zmm15", NULL}; + +#define STR(s) #s + +#define ZMM_REG_DEF(reg) \ + { \ + e_regSetFPU, fpu_zmm##reg, STR(zmm##reg), NULL, Vector, VectorOfUInt8, \ + FPU_SIZE_ZMM(zmm##reg), AVX512F_OFFSET_ZMM(reg), \ + ehframe_dwarf_zmm##reg, ehframe_dwarf_zmm##reg, -1U, \ + debugserver_zmm##reg, NULL, NULL \ + } + +#define YMM_REG_ALIAS(reg) \ + { \ + e_regSetFPU, fpu_ymm##reg, STR(ymm##reg), NULL, Vector, VectorOfUInt8, \ + FPU_SIZE_YMM(ymm##reg), 0, ehframe_dwarf_ymm##reg, \ + ehframe_dwarf_ymm##reg, -1U, debugserver_ymm##reg, \ + g_contained_zmm##reg, NULL \ + } + +#define XMM_REG_ALIAS(reg) \ + { \ + e_regSetFPU, fpu_xmm##reg, STR(xmm##reg), NULL, Vector, VectorOfUInt8, \ + FPU_SIZE_XMM(xmm##reg), 0, ehframe_dwarf_xmm##reg, \ + ehframe_dwarf_xmm##reg, -1U, debugserver_xmm##reg, \ + g_contained_zmm##reg, NULL \ + } + +#define AVX512_K_REG_DEF(reg) \ + { \ + e_regSetFPU, fpu_k##reg, STR(k##reg), NULL, Vector, VectorOfUInt8, 8, \ + AVX512F_OFFSET(k##reg), ehframe_dwarf_k##reg, ehframe_dwarf_k##reg, \ + -1U, debugserver_k##reg, NULL, NULL \ + } + +const DNBRegisterInfo DNBArchImplX86_64::g_fpu_registers_avx512f[] = { + {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, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */, + 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}, + + AVX512_K_REG_DEF(0), + AVX512_K_REG_DEF(1), + AVX512_K_REG_DEF(2), + AVX512_K_REG_DEF(3), + AVX512_K_REG_DEF(4), + AVX512_K_REG_DEF(5), + AVX512_K_REG_DEF(6), + AVX512_K_REG_DEF(7), + + ZMM_REG_DEF(0), + ZMM_REG_DEF(1), + ZMM_REG_DEF(2), + ZMM_REG_DEF(3), + ZMM_REG_DEF(4), + ZMM_REG_DEF(5), + ZMM_REG_DEF(6), + ZMM_REG_DEF(7), + ZMM_REG_DEF(8), + ZMM_REG_DEF(9), + ZMM_REG_DEF(10), + ZMM_REG_DEF(11), + ZMM_REG_DEF(12), + ZMM_REG_DEF(13), + ZMM_REG_DEF(14), + ZMM_REG_DEF(15), + ZMM_REG_DEF(16), + ZMM_REG_DEF(17), + ZMM_REG_DEF(18), + ZMM_REG_DEF(19), + ZMM_REG_DEF(20), + ZMM_REG_DEF(21), + ZMM_REG_DEF(22), + ZMM_REG_DEF(23), + ZMM_REG_DEF(24), + ZMM_REG_DEF(25), + ZMM_REG_DEF(26), + ZMM_REG_DEF(27), + ZMM_REG_DEF(28), + ZMM_REG_DEF(29), + ZMM_REG_DEF(30), + ZMM_REG_DEF(31), + + YMM_REG_ALIAS(0), + YMM_REG_ALIAS(1), + YMM_REG_ALIAS(2), + YMM_REG_ALIAS(3), + YMM_REG_ALIAS(4), + YMM_REG_ALIAS(5), + YMM_REG_ALIAS(6), + YMM_REG_ALIAS(7), + YMM_REG_ALIAS(8), + YMM_REG_ALIAS(9), + YMM_REG_ALIAS(10), + YMM_REG_ALIAS(11), + YMM_REG_ALIAS(12), + YMM_REG_ALIAS(13), + YMM_REG_ALIAS(14), + YMM_REG_ALIAS(15), + + XMM_REG_ALIAS(0), + XMM_REG_ALIAS(1), + XMM_REG_ALIAS(2), + XMM_REG_ALIAS(3), + XMM_REG_ALIAS(4), + XMM_REG_ALIAS(5), + XMM_REG_ALIAS(6), + XMM_REG_ALIAS(7), + XMM_REG_ALIAS(8), + XMM_REG_ALIAS(9), + XMM_REG_ALIAS(10), + XMM_REG_ALIAS(11), + XMM_REG_ALIAS(12), + XMM_REG_ALIAS(13), + XMM_REG_ALIAS(14), + XMM_REG_ALIAS(15), + +}; + + +// 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; +const size_t DNBArchImplX86_64::k_num_fpu_registers_avx512f = + sizeof(g_fpu_registers_avx512f) / sizeof(DNBRegisterInfo); +const size_t DNBArchImplX86_64::k_num_all_registers_avx512f = + k_num_gpr_registers + k_num_fpu_registers_avx512f + 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}}; + +const DNBRegisterSetInfo DNBArchImplX86_64::g_reg_sets_avx512f[] = { + {"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_avx512f, + k_num_fpu_registers_avx512f}, + {"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 (CPUHasAVX512f() || FORCE_AVX_REGS) + return g_reg_sets_avx512f; + 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 (reg > fpu_xmm15 && !(CPUHasAVX() || FORCE_AVX_REGS)) + return false; + if (reg > fpu_ymm15 && !(CPUHasAVX512f() || FORCE_AVX_REGS)) + return false; + 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: + memcpy (&value->value.uint16, &m_state.context.fpu.no_avx.__fpu_ftw, 2); + 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; + + 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; + case fpu_k0: + case fpu_k1: + case fpu_k2: + case fpu_k3: + case fpu_k4: + case fpu_k5: + case fpu_k6: + case fpu_k7: + memcpy((&value->value.uint8), + &m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0), 8); + return true; + case fpu_zmm0: + case fpu_zmm1: + case fpu_zmm2: + case fpu_zmm3: + case fpu_zmm4: + case fpu_zmm5: + case fpu_zmm6: + case fpu_zmm7: + case fpu_zmm8: + case fpu_zmm9: + case fpu_zmm10: + case fpu_zmm11: + case fpu_zmm12: + case fpu_zmm13: + case fpu_zmm14: + case fpu_zmm15: + memcpy(&value->value.uint8, + &m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0), 16); + memcpy((&value->value.uint8) + 16, + &m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0), 16); + memcpy((&value->value.uint8) + 32, + &m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0), 32); + return true; + case fpu_zmm16: + case fpu_zmm17: + case fpu_zmm18: + case fpu_zmm19: + case fpu_zmm20: + case fpu_zmm21: + case fpu_zmm22: + case fpu_zmm23: + case fpu_zmm24: + case fpu_zmm25: + case fpu_zmm26: + case fpu_zmm27: + case fpu_zmm28: + case fpu_zmm29: + case fpu_zmm30: + case fpu_zmm31: + memcpy(&value->value.uint8, + &m_state.context.fpu.avx512f.__fpu_zmm16 + (reg - fpu_zmm16), 64); + 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; + if (reg > fpu_xmm15 && !(CPUHasAVX() || FORCE_AVX_REGS)) + return false; + if (reg > fpu_ymm15 && !(CPUHasAVX512f() || FORCE_AVX_REGS)) + return false; + case e_regSetFPU: + 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: + memcpy (&m_state.context.fpu.no_avx.__fpu_ftw, &value->value.uint8, 2); + 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; + + 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; + case fpu_k0: + case fpu_k1: + case fpu_k2: + case fpu_k3: + case fpu_k4: + case fpu_k5: + case fpu_k6: + case fpu_k7: + memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0), + &value->value.uint8, 8); + return true; + case fpu_zmm0: + case fpu_zmm1: + case fpu_zmm2: + case fpu_zmm3: + case fpu_zmm4: + case fpu_zmm5: + case fpu_zmm6: + case fpu_zmm7: + case fpu_zmm8: + case fpu_zmm9: + case fpu_zmm10: + case fpu_zmm11: + case fpu_zmm12: + case fpu_zmm13: + case fpu_zmm14: + case fpu_zmm15: + memcpy(&m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0), + &value->value.uint8, 16); + memcpy(&m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0), + &value->value.uint8 + 16, 16); + memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0), + &value->value.uint8 + 32, 32); + return true; + case fpu_zmm16: + case fpu_zmm17: + case fpu_zmm18: + case fpu_zmm19: + case fpu_zmm20: + case fpu_zmm21: + case fpu_zmm22: + case fpu_zmm23: + case fpu_zmm24: + case fpu_zmm25: + case fpu_zmm26: + case fpu_zmm27: + case fpu_zmm28: + case fpu_zmm29: + case fpu_zmm30: + case fpu_zmm31: + memcpy(&m_state.context.fpu.avx512f.__fpu_zmm16 + (reg - fpu_zmm16), + &value->value.uint8, 64); + return true; + } + 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 (CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < k_num_fpu_registers_avx512f; ++i) { + if (g_fpu_registers_avx512f[i].value_regs == NULL) + g_cached_size += g_fpu_registers_avx512f[i].size; + } + } else 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); + + // Walk around the gaps in the FPU regs + memcpy(p, &m_state.context.fpu.no_avx.__fpu_fcw, 5); + // We read 5 bytes, but we skip 6 to account for __fpu_rsrv1 + // to match the g_fpu_registers_* tables. + p += 6; + 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; + } + + if(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 8; ++i) { + memcpy(p, &m_state.context.fpu.avx512f.__fpu_k0 + i, 8); + p += 8; + } + } + + if (CPUHasAVX() || FORCE_AVX_REGS) { + // 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; + } + if(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 16; ++i) { + memcpy(p, &m_state.context.fpu.avx512f.__fpu_zmmh0 + i, 32); + p += 32; + } + for (size_t i = 0; i < 16; ++i) { + memcpy(p, &m_state.context.fpu.avx512f.__fpu_zmm16 + i, 64); + p += 64; + } + } + } else { + // 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); + + const uint8_t *p = (const uint8_t *)buf; + // Copy the GPR registers + memcpy(&m_state.context.gpr, p, sizeof(GPR)); + p += sizeof(GPR); + + // Copy fcw through mxcsrmask as there is no padding + memcpy(&m_state.context.fpu.no_avx.__fpu_fcw, p, 5); + // We wrote 5 bytes, but we skip 6 to account for __fpu_rsrv1 + // to match the g_fpu_registers_* tables. + p += 6; + 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; + } + + if(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 8; ++i) { + memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + i, p, 8); + p += 8; + } + } + + if (CPUHasAVX() || FORCE_AVX_REGS) { + // 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; + } + if(CPUHasAVX512f() || FORCE_AVX_REGS) { + for (size_t i = 0; i < 16; ++i) { + memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + i, p, 32); + p += 32; + } + for (size_t i = 0; i < 16; ++i) { + memcpy(&m_state.context.fpu.avx512f.__fpu_zmm16 + i, p, 64); + p += 64; + } + } + } else { + // 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 - (const 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h new file mode 100644 index 00000000000..62ce37d4c04 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h @@ -0,0 +1,241 @@ +//===-- DNBArchImplX86_64.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 __x86_64_avx512f_state_t AVX512F; + static const DNBRegisterInfo g_fpu_registers_avx512f[]; + static const DNBRegisterSetInfo g_reg_sets_avx512f[]; + static const size_t k_num_fpu_registers_avx512f; + static const size_t k_num_all_registers_avx512f; + + enum RegisterSet { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPU, + e_regSetEXC, + e_regSetDBG, + kNumRegisterSets + }; + + enum RegisterSetWordSize { + 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_regSetWordSizeAVX512f = sizeof(AVX512F) / sizeof(int), + e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int) + }; + + enum { Read = 0, Write = 1, kNumErrors = 2 }; + + struct Context { + GPR gpr; + union { + FPU no_avx; + AVX avx; + AVX512F avx512f; + } 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/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h new file mode 100644 index 00000000000..81839666837 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h @@ -0,0 +1,313 @@ +//===-- MachRegisterStatesX86_64.h --------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 +#define __x86_64_AVX512F_STATE 20 + +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 { + 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; +} __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 { uint8_t __ymm_reg[32]; } __x86_64_ymm_reg; +typedef struct { uint8_t __zmm_reg[64]; } __x86_64_zmm_reg; +typedef struct { uint8_t __opmask_reg[8]; } __x86_64_opmask_reg; + +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_opmask_reg __fpu_k0; + __x86_64_opmask_reg __fpu_k1; + __x86_64_opmask_reg __fpu_k2; + __x86_64_opmask_reg __fpu_k3; + __x86_64_opmask_reg __fpu_k4; + __x86_64_opmask_reg __fpu_k5; + __x86_64_opmask_reg __fpu_k6; + __x86_64_opmask_reg __fpu_k7; + __x86_64_ymm_reg __fpu_zmmh0; + __x86_64_ymm_reg __fpu_zmmh1; + __x86_64_ymm_reg __fpu_zmmh2; + __x86_64_ymm_reg __fpu_zmmh3; + __x86_64_ymm_reg __fpu_zmmh4; + __x86_64_ymm_reg __fpu_zmmh5; + __x86_64_ymm_reg __fpu_zmmh6; + __x86_64_ymm_reg __fpu_zmmh7; + __x86_64_ymm_reg __fpu_zmmh8; + __x86_64_ymm_reg __fpu_zmmh9; + __x86_64_ymm_reg __fpu_zmmh10; + __x86_64_ymm_reg __fpu_zmmh11; + __x86_64_ymm_reg __fpu_zmmh12; + __x86_64_ymm_reg __fpu_zmmh13; + __x86_64_ymm_reg __fpu_zmmh14; + __x86_64_ymm_reg __fpu_zmmh15; + __x86_64_zmm_reg __fpu_zmm16; + __x86_64_zmm_reg __fpu_zmm17; + __x86_64_zmm_reg __fpu_zmm18; + __x86_64_zmm_reg __fpu_zmm19; + __x86_64_zmm_reg __fpu_zmm20; + __x86_64_zmm_reg __fpu_zmm21; + __x86_64_zmm_reg __fpu_zmm22; + __x86_64_zmm_reg __fpu_zmm23; + __x86_64_zmm_reg __fpu_zmm24; + __x86_64_zmm_reg __fpu_zmm25; + __x86_64_zmm_reg __fpu_zmm26; + __x86_64_zmm_reg __fpu_zmm27; + __x86_64_zmm_reg __fpu_zmm28; + __x86_64_zmm_reg __fpu_zmm29; + __x86_64_zmm_reg __fpu_zmm30; + __x86_64_zmm_reg __fpu_zmm31; + +} __x86_64_avx512f_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/gnu/llvm/lldb/tools/debugserver/source/PThreadCondition.h b/gnu/llvm/lldb/tools/debugserver/source/PThreadCondition.h new file mode 100644 index 00000000000..2f9060da028 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadCondition.h @@ -0,0 +1,34 @@ +//===-- PThreadCondition.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp new file mode 100644 index 00000000000..82d1d227867 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp @@ -0,0 +1,195 @@ +//===-- PThreadEvent.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/16/07. +// +//===----------------------------------------------------------------------===// + +#include "PThreadEvent.h" +#include "DNBLog.h" +#include "errno.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, LLVM_PRETTY_FUNCTION); +} + +uint32_t PThreadEvent::NewEventBit() { + // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_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, LLVM_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/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.h b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.h new file mode 100644 index 00000000000..f81798de5c7 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.h @@ -0,0 +1,61 @@ +//===-- PThreadEvent.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/16/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __PThreadEvent_h__ +#define __PThreadEvent_h__ +#include "PThreadCondition.h" +#include "PThreadMutex.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 &) = delete; + PThreadEvent &operator=(const PThreadEvent &rhs) = delete; +}; + +#endif // #ifndef __PThreadEvent_h__ diff --git a/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.cpp b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.cpp new file mode 100644 index 00000000000..118921aee5a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.cpp @@ -0,0 +1,66 @@ +//===-- PThreadMutex.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/9/08. +// +//===----------------------------------------------------------------------===// + +#include "PThreadMutex.h" + +#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/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.h b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.h new file mode 100644 index 00000000000..075fdc9114f --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.h @@ -0,0 +1,119 @@ +//===-- PThreadMutex.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/16/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __PThreadMutex_h__ +#define __PThreadMutex_h__ + +#include <assert.h> +#include <pthread.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/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.cpp b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.cpp new file mode 100644 index 00000000000..cac8bda6248 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.cpp @@ -0,0 +1,179 @@ +//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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::Status 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::Status 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::Status &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/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.h b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.h new file mode 100644 index 00000000000..e3324c1c358 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.h @@ -0,0 +1,79 @@ +//===-- PseudoTerminal.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/8/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __PseudoTerminal_h__ +#define __PseudoTerminal_h__ + +#include <fcntl.h> +#include <string> +#include <termios.h> + +class PseudoTerminal { +public: + enum { invalid_fd = -1, invalid_pid = -1 }; + + enum Status { + 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(); + Status OpenFirstAvailableMaster(int oflag); + Status 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(Status &error); + +protected: + // Classes that inherit from PseudoTerminal can see and modify these + int m_master_fd; + int m_slave_fd; + +private: + PseudoTerminal(const PseudoTerminal &rhs) = delete; + PseudoTerminal &operator=(const PseudoTerminal &rhs) = delete; +}; + +#endif // #ifndef __PseudoTerminal_h__ diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp new file mode 100644 index 00000000000..3f1a37a1175 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp @@ -0,0 +1,290 @@ +//===-- RNBContext.cpp ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#include "RNBContext.h" + +#include <sstream> +#include <sys/stat.h> + +#if defined(__APPLE__) +#include <pthread.h> +#include <sched.h> +#endif + +#include "CFString.h" +#include "DNB.h" +#include "DNBLog.h" +#include "RNBRemote.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; +} + +static std::string GetEnvironmentKey(const std::string &env) { + std::string key = env.substr(0, env.find('=')); + if (!key.empty() && key.back() == '=') + key.pop_back(); + return key; +} + +void RNBContext::PushEnvironmentIfNeeded(const char *arg) { + if (!arg) + return; + std::string arg_key = GetEnvironmentKey(arg); + + for (const std::string &entry: m_env_vec) { + if (arg_key == GetEnvironmentKey(entry)) + return; + } + m_env_vec.push_back(arg); +} + +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_darwin_log_data_available) + s += "darwin_log_data_available "; + 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.Status()); + 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/gnu/llvm/lldb/tools/debugserver/source/RNBContext.h b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.h new file mode 100644 index 00000000000..946dfb2eb10 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.h @@ -0,0 +1,156 @@ +//===-- RNBContext.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBContext_h__ +#define __RNBContext_h__ + +#include "DNBError.h" +#include "PThreadEvent.h" +#include "RNBDefs.h" +#include <string> +#include <vector> + +class RNBContext { +public: + enum { + event_proc_state_changed = 0x001, + event_proc_thread_running = 0x002, // Sticky + event_proc_thread_exiting = 0x004, + event_proc_stdio_available = 0x008, + event_proc_profile_data = 0x010, + event_read_packet_available = 0x020, + event_read_thread_running = 0x040, // Sticky + event_read_thread_exiting = 0x080, + event_darwin_log_data_available = 0x100, + + 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 | + event_darwin_log_data_available, + + 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 PushEnvironmentIfNeeded(const char *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: + RNBContext(const RNBContext &rhs) = delete; + RNBContext &operator=(const RNBContext &rhs) = delete; +}; + +#endif // #ifndef __RNBContext_h__ diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBDefs.h b/gnu/llvm/lldb/tools/debugserver/source/RNBDefs.h new file mode 100644 index 00000000000..4cc7c220b7f --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBDefs.h @@ -0,0 +1,98 @@ +//===-- RNBDefs.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/14/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBDefs_h__ +#define __RNBDefs_h__ + +#include "DNBDefs.h" +#include <memory> + +#define CONCAT2(a, b) a##b +#define CONCAT(a, b) CONCAT2(a, b) +#define STRINGIZE2(x) #x +#define STRINGIZE(x) STRINGIZE2(x) + +#if !defined(DEBUGSERVER_PROGRAM_SYMBOL) +#define DEBUGSERVER_PROGRAM_SYMBOL debugserver +#endif + +#if !defined(DEBUGSERVER_PROGRAM_NAME) +#define DEBUGSERVER_PROGRAM_NAME STRINGIZE(DEBUGSERVER_PROGRAM_SYMBOL) +#endif + +#ifndef DEBUGSERVER_VERSION_NUM +extern "C" const unsigned char CONCAT(DEBUGSERVER_PROGRAM_SYMBOL, + VersionString)[]; +#define DEBUGSERVER_VERSION_NUM \ + CONCAT(DEBUGSERVER_PROGRAM_SYMBOL, VersionNumber) +#endif + +#ifndef DEBUGSERVER_VERSION_STR +extern "C" const double CONCAT(DEBUGSERVER_PROGRAM_SYMBOL, VersionNumber); +#define DEBUGSERVER_VERSION_STR \ + CONCAT(DEBUGSERVER_PROGRAM_SYMBOL, VersionString) +#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; + +enum rnb_err_t { rnb_success = 0, rnb_err = 1, rnb_not_connected = 2 }; + +// 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/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp new file mode 100644 index 00000000000..64e3bc49abc --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp @@ -0,0 +1,6313 @@ +//===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#include "RNBRemote.h" + +#include <errno.h> +#include <mach-o/loader.h> +#include <mach/exception_types.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <unistd.h> + +#if defined(__APPLE__) +#include <pthread.h> +#include <sched.h> +#endif + +#include "DNB.h" +#include "DNBDataRef.h" +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "DarwinLogCollector.h" +#include "DarwinLogEvent.h" +#include "JSON.h" +#include "JSONGenerator.h" +#include "JSONGenerator.h" +#include "MacOSX/Genealogy.h" +#include "OsLogger.h" +#include "RNBContext.h" +#include "RNBServices.h" +#include "RNBSocket.h" +#include "StdStringExtractor.h" + +#include <compression.h> + +#include <TargetConditionals.h> +#include <iomanip> +#include <memory> +#include <sstream> +#include <unordered_set> + +// constants + +static const std::string OS_LOG_EVENTS_KEY_NAME("events"); +static const std::string JSON_ASYNC_TYPE_KEY_NAME("type"); +static const DarwinLogEventVector::size_type DARWIN_LOG_MAX_EVENTS_PER_PACKET = + 10; + +// 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. + +// Prototypes + +static std::string binary_encode_string(const std::string &s); + +// 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(json_query_get_shared_cache_info, + &RNBRemote::HandlePacket_jGetSharedCacheInfo, NULL, + "jGetSharedCacheInfo", "Replies with JSON data about the " + "location and uuid of the shared " + "cache in the inferior process.")); + 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.")); + t.push_back( + Packet(query_supported_async_json_packets, + &RNBRemote::HandlePacket_qStructuredDataPlugins, NULL, + "qStructuredDataPlugins", + "Query for the structured data plugins supported by the remote.")); + t.push_back( + Packet(configure_darwin_log, &RNBRemote::HandlePacket_QConfigureDarwinLog, + NULL, "QConfigureDarwinLog:", + "Configure the DarwinLog structured data plugin support.")); +} + +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); + } +} + +void RNBRemote::SendAsyncDarwinLogData() { + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): enter", __FUNCTION__); + + if (!m_ctx.HasValidProcessID()) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): ignoring due to" + "invalid process id", + __FUNCTION__); + return; + } + + nub_process_t pid = m_ctx.ProcessID(); + DarwinLogEventVector::size_type entry_count = 0; + + // NOTE: the current looping structure here does nothing + // to guarantee that we can send off async packets faster + // than we generate them. It will keep sending as long + // as there's data to send. + do { + DarwinLogEventVector events = DNBProcessGetAvailableDarwinLogEvents(pid); + entry_count = events.size(); + + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop enter", + __FUNCTION__); + + for (DarwinLogEventVector::size_type base_entry = 0; + base_entry < entry_count; + base_entry += DARWIN_LOG_MAX_EVENTS_PER_PACKET) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): inner loop enter", + __FUNCTION__); + + // We limit the total number of entries we pack + // into a single JSON async packet just so it + // doesn't get too large. + JSONGenerator::Dictionary async_dictionary; + + // Specify the type of the JSON async data we're sending. + async_dictionary.AddStringItem(JSON_ASYNC_TYPE_KEY_NAME, "DarwinLog"); + + // Create an array entry in the dictionary to hold all + // the events going in this packet. + JSONGenerator::ArraySP events_array(new JSONGenerator::Array()); + async_dictionary.AddItem(OS_LOG_EVENTS_KEY_NAME, events_array); + + // We bundle up to DARWIN_LOG_MAX_EVENTS_PER_PACKET events in + // a single packet. + const auto inner_loop_bound = + std::min(base_entry + DARWIN_LOG_MAX_EVENTS_PER_PACKET, entry_count); + for (DarwinLogEventVector::size_type i = base_entry; i < inner_loop_bound; + ++i) { + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): adding " + "entry index %lu to the JSON packet", + __FUNCTION__, i); + events_array->AddItem(events[i]); + } + + // Send off the packet. + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): sending JSON " + "packet, %lu entries remain", + __FUNCTION__, entry_count - inner_loop_bound); + SendAsyncJSONPacket(async_dictionary); + } + + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop exit", + __FUNCTION__); + + } while (entry_count > 0); + + DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): exit", + __PRETTY_FUNCTION__); +} + +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); +} + +rnb_err_t +RNBRemote::SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary) { + std::ostringstream stream; + // We're choosing something that is easy to spot if we somehow get one + // of these coming out at the wrong time (i.e. when the remote side + // is not waiting for a process control completion response). + stream << "JSON-async:"; + dictionary.Dump(stream); + const std::string payload = binary_encode_string(stream.str()); + return SendPacket(payload); +} + +// 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; + + // Allocate a scratch buffer for libcompression the first + // time we see a different compression type; reuse it in + // all compression_encode_buffer calls so it doesn't need + // to allocate / free its own scratch buffer each time. + // This buffer will only be freed when compression type + // changes; otherwise it will persist until debugserver + // exit. + + static compression_types g_libcompress_scratchbuf_type = compression_types::none; + static void *g_libcompress_scratchbuf = nullptr; + + if (g_libcompress_scratchbuf_type != compression_type) { + if (g_libcompress_scratchbuf) { + free (g_libcompress_scratchbuf); + g_libcompress_scratchbuf = nullptr; + } + size_t scratchbuf_size = 0; + switch (compression_type) { + case compression_types::lz4: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZ4_RAW); + break; + case compression_types::zlib_deflate: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_ZLIB); + break; + case compression_types::lzma: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZMA); + break; + case compression_types::lzfse: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZFSE); + break; + default: + break; + } + if (scratchbuf_size > 0) { + g_libcompress_scratchbuf = (void*) malloc (scratchbuf_size); + g_libcompress_scratchbuf_type = compression_type; + } + } + + if (compression_type == compression_types::lz4) { + compressed_size = compression_encode_buffer( + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, + COMPRESSION_LZ4_RAW); + } + if (compression_type == compression_types::zlib_deflate) { + compressed_size = compression_encode_buffer( + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, + COMPRESSION_ZLIB); + } + if (compression_type == compression_types::lzma) { + compressed_size = compression_encode_buffer( + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, + COMPRESSION_LZMA); + } + if (compression_type == compression_types::lzfse) { + compressed_size = compression_encode_buffer( + encoded_data.data(), encoded_data_buf_size, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, + COMPRESSION_LZFSE); + } + + 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; + + 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 { +#if defined (__ARM64_ARCH_8_32__) + return CPU_TYPE_ARM64_32; +#endif + 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--; + 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. + +static 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().Status() == 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 == "set") { + std::string variable = get_identifier(line); + std::string op = get_operator(line); + std::string value = get_value(line); + if (variable == "logfile") { + FILE *log_file = fopen(value.c_str(), "w"); + if (log_file) { + DNBLogSetLogCallback(FileLogCallback, log_file); + return SendPacket("OK"); + } + return SendPacket("E71"); + } else if (variable == "logmask") { + 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_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_MEMORY", sizeof("LOG_MEMORY") - 1) == 0) { + p += sizeof("LOG_MEMORY") - 1; + bitmask |= LOG_MEMORY; + } 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_DARWIN_LOG", sizeof("LOG_DARWIN_LOG") - 1) == + 0) { + p += sizeof("LOG_DARWIN_LOG") - 1; + bitmask |= LOG_DARWIN_LOG; + } 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. + // Use the existing log callback if one was already configured. + if (!DNBLogGetLogCallback()) { + // Use the os_log()-based logger if available; otherwise, + // fallback to ASL. + auto log_callback = OsLogger::GetLogFunction(); + if (log_callback) + DNBLogSetLogCallback(log_callback, nullptr); + else + DNBLogSetLogCallback(ASLLogCallback, nullptr); + } + + // Update logging to use the configured log channel bitmask. + DNBLogSetLogMask(bitmask); + p++; + } + } +// We're not going to support logging to a file for now. All logging +// goes through ASL or the previously arranged log callback. +#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); + strlcpy (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 + StdStringExtractor 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()) { + StdStringExtractor 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_qStructuredDataPlugins(const char *p) { + // We'll return a JSON array of supported packet types. + // The type is significant. For each of the supported + // packet types that have been enabled, there will be a + // 'J' async packet sent to the client with payload data. + // This payload data will be a JSON dictionary, and the + // top level dictionary will contain a string field with + // its value set to the relevant packet type from this list. + JSONGenerator::Array supported_json_packets; + + // Check for DarwinLog (libtrace os_log/activity support). + if (DarwinLogCollector::IsSupported()) + supported_json_packets.AddItem( + JSONGenerator::StringSP(new JSONGenerator::String("DarwinLog"))); + + // Send back the array. + std::ostringstream stream; + supported_json_packets.Dump(stream); + return SendPacket(stream.str()); +} + +rnb_err_t RNBRemote::HandlePacket_QConfigureDarwinLog(const char *p) { + if (!DarwinLogCollector::IsSupported()) { + // We should never have been given this request. + return SendPacket("E89"); + } + + // Ensure we have a process. We expect a separate configure request for + // each process launched/attached. + const nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket("E94"); + + // Get the configuration dictionary. + p += strlen("QConfigureDarwinLog:"); + + // The configuration dictionary is binary encoded. + std::vector<uint8_t> unescaped_config_data = decode_binary_data(p, -1); + std::string unescaped_config_string((const char *)&unescaped_config_data[0], + unescaped_config_data.size()); + DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLog: received config data: \"%s\"", + unescaped_config_string.c_str()); + auto configuration_sp = + JSONParser(unescaped_config_string.c_str()).ParseJSONValue(); + if (!configuration_sp) { + // Malformed request - we require configuration data + // indicating whether we're enabling or disabling. + return SendPacket("E90"); + } + + if (!JSONObject::classof(configuration_sp.get())) { + // Configuration data is not of the right type. + return SendPacket("E91"); + } + JSONObject &config_dict = *static_cast<JSONObject *>(configuration_sp.get()); + + // Check if we're enabling or disabling. + auto enabled_sp = config_dict.GetObject("enabled"); + if (!enabled_sp) { + // Missing required "enabled" field. + return SendPacket("E92"); + } + if (!JSONTrue::classof(enabled_sp.get()) && + !JSONFalse::classof(enabled_sp.get())) { + // Should be a boolean type, but wasn't. + return SendPacket("E93"); + } + const bool enabling = JSONTrue::classof(enabled_sp.get()); + + // TODO - handle other configuration parameters here. + + // Shut down any active activity stream for the process. + DarwinLogCollector::CancelStreamForProcess(pid); + + if (enabling) { + // Look up the procecess. + if (!DarwinLogCollector::StartCollectingForProcess(pid, config_dict)) + return SendPacket("E95"); + } + + 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]); + } +} + +std::string cstring_to_asciihex_string(const char *str) { + std::string hex_str; + hex_str.reserve (strlen (str) * 2); + while (str && *str) { + char hexbuf[5]; + snprintf (hexbuf, sizeof(hexbuf), "%02x", *str++); + hex_str += hexbuf; + } + return hex_str; +} + +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, nub_addr_t &dispatch_queue_t, + 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) { + dispatch_queue_t = DNBProcessMemoryReadPointer(pid, dispatch_qaddr); + if (dispatch_queue_t) { + queue_width = DNBProcessMemoryReadInteger( + pid, dispatch_queue_t + dqo_width, dqo_width_size, 0); + queue_serialnum = DNBProcessMemoryReadInteger( + pid, dispatch_queue_t + 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_t + dqo_label; + nub_addr_t label_addr = + DNBProcessMemoryReadPointer(pid, pointer_to_label_address); + if (label_addr) + queue_name = DNBProcessMemoryReadCString(pid, label_addr); + } else { + // libdispatch versions 1-3, dispatch name is a fixed width char array + // in the queue structure. + queue_name = DNBProcessMemoryReadCStringFixed( + pid, dispatch_queue_t + 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:"; + const uint8_t *u_thread_name = (const uint8_t *)thread_name; + for (size_t i = 0; i < thread_name_len; i++) + ostrm << RAWHEX8(u_thread_name[i]); + ostrm << ';'; + } + } + + // 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]) { + strlcpy(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(); + + StdStringExtractor 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(); + return !remote->Comm().IsConnected(); + } + 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) { + StdStringExtractor packet(p); + packet.SetFilePos(2); // Skip the "_M" + + nub_addr_t size = packet.GetHexMaxU64(StdStringExtractor::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) { + StdStringExtractor packet(p); + packet.SetFilePos(2); // Skip the "_m" + nub_addr_t addr = + packet.GetHexMaxU64(StdStringExtractor::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"); + } + + StdStringExtractor 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); + + bool enable_compression = false; + (void)enable_compression; + +#if (defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1) \ + || (defined (TARGET_OS_IOS) && TARGET_OS_IOS == 1) \ + || (defined (TARGET_OS_TV) && TARGET_OS_TV == 1) \ + || (defined (TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1) + enable_compression = true; +#endif + + if (enable_compression) { + 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); + } + + 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) { + DNBThreadResumeActions thread_actions; + char *c = const_cast<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... + [[clang::fallthrough]]; + 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... + [[clang::fallthrough]]; + 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) { + 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) { + std::string return_message = "E96;"; + return_message += cstring_to_asciihex_string( + "Process attach denied, possibly because " + "System Integrity Protection is enabled and " + "process does not allow attaching."); + + SendPacket(return_message.c_str()); + 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(); + + StdStringExtractor 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"); + + StdStringExtractor packet(p += sizeof("qGetProfileData")); + DNBProfileDataScanType scan_type = eProfileAll; + std::string name; + std::string value; + while (packet.GetNameColonValue(name, value)) { + if (name == "scan_type") { + 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"); + + StdStringExtractor 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 == "enable") { + enable = strtoul(value.c_str(), NULL, 10) > 0; + } else if (name == "interval_usec") { + interval_usec = strtoul(value.c_str(), NULL, 10); + } else if (name == "scan_type") { + 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 = false; + } + + 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 (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"); + } + + 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 + DNBLogThreaded("RNBRemote::HandlePacket_stop_process() sending extra stop " + "reply because DNBProcessInterrupt returned false"); + 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_ARM64_32: + return "arm64_32"; + 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; + } + } +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + if (g_host_cputype == CPU_TYPE_ARM64 && sizeof (void*) == 4) + g_host_cputype = CPU_TYPE_ARM64_32; +#endif + } + + 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; + } +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + // on arm64_32 devices, the machine's native cpu type is + // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e. + // But we change the cputype to CPU_TYPE_ARM64_32 because + // the user processes are all ILP32 processes today. + // We also need to rewrite the cpusubtype so we vend + // a valid cputype + cpusubtype combination. + if (g_host_cputype == CPU_TYPE_ARM64_32) + g_host_cpusubtype = CPU_SUBTYPE_ARM64_32_V8; +#endif + } + + 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; +} + +static bool GetAddressingBits(uint32_t &addressing_bits) { + static uint32_t g_addressing_bits = 0; + static bool g_tried_addressing_bits_syscall = false; + if (g_tried_addressing_bits_syscall == false) { + size_t len = sizeof (uint32_t); + if (::sysctlbyname("machdep.virtual_address_size", + &g_addressing_bits, &len, NULL, 0) != 0) { + g_addressing_bits = 0; + } + } + g_tried_addressing_bits_syscall = true; + addressing_bits = g_addressing_bits; + if (addressing_bits > 0) + return true; + else + return false; +} + +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 << ';'; + } + + uint32_t addressing_bits = 0; + if (GetAddressingBits(addressing_bits)) { + strm << "addressing_bits:" << std::dec << addressing_bits << ';'; + } + + // 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 + || cputype == CPU_TYPE_ARM64_32) { +#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 + strm << "ostype:tvos;"; +#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + strm << "ostype:watchos;"; +#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 + strm << "ostype:bridgeos;"; +#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 << "os_version:" << major << "." << minor; + if (patch != UINT64_MAX) + strm << "." << patch; + strm << ";"; + } + + std::string maccatalyst_version = DNBGetMacCatalystVersionString(); + if (!maccatalyst_version.empty() && + std::all_of(maccatalyst_version.begin(), maccatalyst_version.end(), + [](char c) { return (c >= '0' && c <= '9') || c == '.'; })) + strm << "maccatalyst_version:" << maccatalyst_version << ";"; + +#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 + // + // On raw targets (no OS, vendor info), I've seen replies like + // <architecture>i386:x86-64</architecture> (for x86_64 systems - from vmware) + // <architecture>arm</architecture> (for an unspecified arm device - from a Segger JLink) + // For good interop, I'm not sure what's expected here. e.g. will anyone understand + // <architecture>x86_64</architecture> ? Or is i386:x86_64 the expected phrasing? + // + // 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; +} + +// A helper function that retrieves a boolean value from +// a one-level-deep JSON dictionary of key-value pairs. e.g. +// jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}] + +// Returns true if it was able to find the key name, and sets the 'value' +// argument to the value found. + +bool get_boolean_value_for_key_name_from_json(const char *key, + const char *json_string, + bool &value) { + 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++; + + if (strncmp(c, "true", 4) == 0) { + value = true; + return true; + } else if (strncmp(c, "false", 5) == 0) { + value = false; + return true; + } + } + } + return false; +} + +// A helper function that reads an array of uint64_t's from +// a one-level-deep JSON dictionary of key-value pairs. e.g. +// jGetLoadedDynamicLibrariesInfos:{"solib_addrs":[31345823,7768020384,7310483024]}] + +// Returns true if it was able to find the key name, false if it did not. +// "ints" will have all integers found in the array appended to it. + +bool get_array_of_ints_value_for_key_name_from_json( + const char *key, const char *json_string, std::vector<uint64_t> &ints) { + 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++; + + if (*c == '[') { + c++; + while (*c != '\0' && + (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) + c++; + while (true) { + if (!isdigit(*c)) { + return true; + } + + errno = 0; + char *endptr; + uint64_t value = strtoul(c, &endptr, 10); + if (errno == 0) { + ints.push_back(value); + } else { + break; + } + if (endptr == c || endptr == nullptr || *endptr == '\0') { + break; + } + c = endptr; + + 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++; + if (*c == ']') { + return true; + } + } + } + } + } + return false; +} + +JSONGenerator::ObjectSP +RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only) { + JSONGenerator::ArraySP threads_array_sp; + if (m_ctx.HasValidProcessID()) { + threads_array_sp = std::make_shared<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) { + 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; + nub_addr_t dispatch_queue_t = INVALID_NUB_ADDRESS; + dispatch_queue_offsets->GetThreadQueueInfo( + pid, thread_ident_info.dispatch_qaddr, dispatch_queue_t, + queue_name, queue_width, queue_serialnum); + if (dispatch_queue_t == 0 && queue_name.empty() && + queue_serialnum == 0) { + thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue", + false); + } else { + thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue", + true); + } + if (dispatch_queue_t != INVALID_NUB_ADDRESS && + dispatch_queue_t != 0) + thread_dict_sp->AddIntegerItem("dispatch_queue_t", + dispatch_queue_t); + 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("qserialnum", 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); + + 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) { + 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; + 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 << ","; + Genealogy::ProcessExecutableInfoSP image_info_sp; + uint32_t idx = *iter; + image_info_sp = DNBGetGenealogyImageInfo(pid, idx); + if (image_info_sp) { + if (!printed_one_process_info) { + json << "\"process_infos\":["; + printed_one_process_info = true; + } + + 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 << "}"; + } + } + if (printed_one_process_info) + 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"); +} + +// This packet may be called in one of three ways: +// +// jGetLoadedDynamicLibrariesInfos:{"image_count":40,"image_list_address":4295244704} +// Look for an array of the old dyld_all_image_infos style of binary infos +// at the image_list_address. +// This an array of {void* load_addr, void* mod_date, void* pathname} +// +// jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true} +// Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to +// get a list of all the +// libraries loaded +// +// jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]} +// Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to +// get the information +// about the libraries loaded at these addresses. +// +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); + + JSONGenerator::ObjectSP json_sp; + + std::vector<uint64_t> macho_addresses; + bool fetch_all_solibs = false; + if (get_boolean_value_for_key_name_from_json("fetch_all_solibs", p, + fetch_all_solibs) && + fetch_all_solibs) { + json_sp = DNBGetAllLoadedLibrariesInfos(pid); + } else if (get_array_of_ints_value_for_key_name_from_json( + "solib_addresses", p, macho_addresses)) { + json_sp = DNBGetLibrariesInfoForAddresses(pid, macho_addresses); + } else { + 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) { + 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"); +} + +// This packet does not currently take any arguments. So the behavior is +// jGetSharedCacheInfo:{} +// send information about the inferior's shared cache +// jGetSharedCacheInfo: +// send "OK" to indicate that this packet is supported +rnb_err_t RNBRemote::HandlePacket_jGetSharedCacheInfo(const char *p) { + nub_process_t pid; + // If we haven't run the process yet, return an error. + if (!m_ctx.HasValidProcessID()) { + return SendPacket("E85"); + } + + pid = m_ctx.ProcessID(); + + const char get_shared_cache_info_str[] = {"jGetSharedCacheInfo:{"}; + if (strncmp(p, get_shared_cache_info_str, + sizeof(get_shared_cache_info_str) - 1) == 0) { + JSONGenerator::ObjectSP json_sp = DNBGetSharedCacheInfo(pid); + + 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("E86"); + } + } + } + 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 = 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 = 12; // CPU_SUBTYPE_ARM_V7K + } + } +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + // on arm64_32 devices, the machine's native cpu type is + // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e. + // But we change the cputype to CPU_TYPE_ARM64_32 because + // the user processes are all ILP32 processes today. + // We also need to rewrite the cpusubtype so we vend + // a valid cputype + cpusubtype combination. + if (cputype == CPU_TYPE_ARM64_32 && cpusubtype == 2) + cpusubtype = CPU_SUBTYPE_ARM64_32_V8; +#endif + + 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); + (void)bytes_read; + + uint32_t major_version, minor_version, patch_version; + auto *platform = DNBGetDeploymentInfo(pid, lc, load_command_addr, + major_version, minor_version, + patch_version); + if (platform) { + os_handled = true; + rep << "ostype:" << platform << ";"; + break; + } + load_command_addr = load_command_addr + lc.cmdsize; + } + } +#endif // when compiling this on x86 targets + } + + // 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 + || cputype == CPU_TYPE_ARM64_32) { +#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 + rep << "ostype:tvos;"; +#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + rep << "ostype:watchos;"; +#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 + rep << "ostype:bridgeos;"; +#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 (true) { + 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;"; +#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 + rep << "ostype:bridgeos;"; +#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/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.h b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.h new file mode 100644 index 00000000000..88d82091130 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.h @@ -0,0 +1,431 @@ + +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBRemote_h__ +#define __RNBRemote_h__ + +#include "DNB.h" +#include "PThreadMutex.h" +#include "RNBContext.h" +#include "RNBDefs.h" +#include "RNBSocket.h" +#include <deque> +#include <map> +#include <string> +#include <vector> + +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: + enum PacketEnum { + 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' + json_query_get_shared_cache_info, // 'jGetSharedCacheInfo' + 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:' + query_supported_async_json_packets, // 'QSupportedAsyncJSONPackets' + configure_darwin_log, // 'ConfigureDarwinLog:' + unknown_type + }; + + 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_jGetSharedCacheInfo(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 HandlePacket_qStructuredDataPlugins(const char *p); + rnb_err_t HandlePacket_QConfigureDarwinLog(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); + void SendAsyncDarwinLogData(); + rnb_err_t SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary); + + RNBContext &Context() { return m_ctx; } + RNBSocket &Comm() { return m_comm; } + +private: + RNBRemote(const RNBRemote &) = delete; + +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, + nub_addr_t &dispatch_queue_t, + 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/gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp new file mode 100644 index 00000000000..085aaddfaf1 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp @@ -0,0 +1,234 @@ +//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Christopher Friesen on 3/21/08. +// +//===----------------------------------------------------------------------===// + +#include "RNBServices.h" + +#include "CFString.h" +#include "DNBLog.h" +#include "MacOSX/CFUtils.h" +#include <CoreFoundation/CoreFoundation.h> +#include <libproc.h> +#include <sys/sysctl.h> +#include <unistd.h> +#include <vector> + +// 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 || // 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 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 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((const 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/gnu/llvm/lldb/tools/debugserver/source/RNBServices.h b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.h new file mode 100644 index 00000000000..b7514f31b21 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.h @@ -0,0 +1,28 @@ +//===-- RNBServices.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Christopher Friesen on 3/21/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBServices_h__ +#define __RNBServices_h__ + +#include "RNBDefs.h" +#include <string> + +#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/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp new file mode 100644 index 00000000000..80b55b5de3b --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp @@ -0,0 +1,391 @@ +//===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#include "RNBSocket.h" +#include "DNBError.h" +#include "DNBLog.h" +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <map> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/event.h> +#include <termios.h> +#include <vector> + +#include "lldb/Host/SocketAddress.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); + + DNBError err; + int queue_id = kqueue(); + if (queue_id < 0) { + err.SetError(errno, DNBError::MachKernel); + err.LogThreaded("error: failed to create kqueue."); + return rnb_err; + } + + bool any_addr = (strcmp(listen_host, "*") == 0); + + // If the user wants to allow connections from any address we should create + // sockets on all families that can resolve localhost. This will allow us to + // listen for IPv6 and IPv4 connections from all addresses if those interfaces + // are available. + const char *local_addr = any_addr ? "localhost" : listen_host; + + std::map<int, lldb_private::SocketAddress> sockets; + auto addresses = lldb_private::SocketAddress::GetAddressInfo( + local_addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); + + for (auto address : addresses) { + int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); + if (sock_fd == -1) + continue; + + SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1); + + lldb_private::SocketAddress bind_address = address; + + if(any_addr || !bind_address.IsLocalhost()) + bind_address.SetToAnyAddress(bind_address.GetFamily(), port); + else + bind_address.SetPort(port); + + int error = + ::bind(sock_fd, &bind_address.sockaddr(), bind_address.GetLength()); + if (error == -1) { + ClosePort(sock_fd, false); + continue; + } + + error = ::listen(sock_fd, 5); + if (error == -1) { + ClosePort(sock_fd, false); + continue; + } + + // 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". This will only execute on the first socket created, + // subesquent sockets will reuse this port number. + if (port == 0) { + socklen_t sa_len = address.GetLength(); + if (getsockname(sock_fd, &address.sockaddr(), &sa_len) == 0) + port = address.GetPort(); + } + + sockets[sock_fd] = address; + } + + if (sockets.size() == 0) { + err.SetError(errno, DNBError::POSIX); + err.LogThreaded("::listen or ::bind failed"); + return rnb_err; + } + + if (callback) + callback(callback_baton, port); + + std::vector<struct kevent> events; + events.resize(sockets.size()); + int i = 0; + for (auto socket : sockets) { + EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0); + } + + bool accept_connection = false; + + // Loop until we are happy with our connection + while (!accept_connection) { + + struct kevent event_list[4]; + int num_events = + kevent(queue_id, events.data(), events.size(), event_list, 4, NULL); + + if (num_events < 0) { + err.SetError(errno, DNBError::MachKernel); + err.LogThreaded("error: kevent() failed."); + } + + for (int i = 0; i < num_events; ++i) { + auto sock_fd = event_list[i].ident; + auto socket_pair = sockets.find(sock_fd); + if (socket_pair == sockets.end()) + continue; + + lldb_private::SocketAddress &addr_in = socket_pair->second; + lldb_private::SocketAddress accept_addr; + socklen_t sa_len = accept_addr.GetMaxLength(); + m_fd = ::accept(sock_fd, &accept_addr.sockaddr(), &sa_len); + + if (m_fd == -1) { + err.SetError(errno, DNBError::POSIX); + err.LogThreaded("error: Socket accept failed."); + } + + if (addr_in.IsAnyAddr()) + accept_connection = true; + else { + if (accept_addr == addr_in) + accept_connection = true; + else { + ::close(m_fd); + m_fd = -1; + ::fprintf( + stderr, + "error: rejecting incoming connection from %s (expecting %s)\n", + accept_addr.GetIPAddress().c_str(), + addr_in.GetIPAddress().c_str()); + DNBLogThreaded("error: rejecting connection from %s (expecting %s)\n", + accept_addr.GetIPAddress().c_str(), + addr_in.GetIPAddress().c_str()); + err.Clear(); + } + } + } + if (err.Fail()) + break; + } + for (auto socket : sockets) { + int ListenFd = socket.first; + ClosePort(ListenFd, false); + } + + if (err.Fail()) + 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::Connect(const char *host, uint16_t port) { + auto result = rnb_err; + Disconnect(false); + + auto addresses = lldb_private::SocketAddress::GetAddressInfo( + host, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); + + for (auto address : addresses) { + m_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); + if (m_fd == -1) + continue; + + // Enable local address reuse + SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1); + + address.SetPort(port); + + if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) { + Disconnect(false); + continue; + } + SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1); + + result = rnb_success; + break; + } + return result; +} + +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, + (const char *) + buffer); // All data is string based in debugserver, so this is safe + DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, + (const 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/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.h b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.h new file mode 100644 index 00000000000..2f68eb00f37 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.h @@ -0,0 +1,77 @@ +//===-- RNBSocket.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBSocket_h__ +#define __RNBSocket_h__ + +#include "DNBTimer.h" +#include "RNBDefs.h" +#include <string> +#include <sys/socket.h> +#include <sys/types.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: + RNBSocket(const RNBSocket &) = delete; + +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/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.cpp b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.cpp new file mode 100644 index 00000000000..a4cad2d4bbf --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.cpp @@ -0,0 +1,344 @@ +//===-- StdStringExtractor.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "StdStringExtractor.h" + +#include <stdlib.h> + + +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; +} + +// StdStringExtractor constructor +StdStringExtractor::StdStringExtractor() : m_packet(), m_index(0) {} + +StdStringExtractor::StdStringExtractor(const char *packet_cstr) + : m_packet(), m_index(0) { + if (packet_cstr) + m_packet.assign(packet_cstr); +} + +// Destructor +StdStringExtractor::~StdStringExtractor() {} + +char StdStringExtractor::GetChar(char fail_value) { + if (m_index < m_packet.size()) { + char ch = m_packet[m_index]; + ++m_index; + return ch; + } + m_index = UINT64_MAX; + return fail_value; +} + +// If a pair of valid hex digits exist at the head of the +// StdStringExtractor they are decoded into an unsigned byte and returned +// by this function +// +// If there is not a pair of valid hex digits at the head of the +// StdStringExtractor, it is left unchanged and -1 is returned +int StdStringExtractor::DecodeHexU8() { + SkipSpaces(); + if (GetBytesLeft() < 2) { + return -1; + } + const int hi_nibble = xdigit_to_sint(m_packet[m_index]); + const int lo_nibble = xdigit_to_sint(m_packet[m_index + 1]); + if (hi_nibble == -1 || lo_nibble == -1) { + return -1; + } + m_index += 2; + return (uint8_t)((hi_nibble << 4) + lo_nibble); +} + +// Extract an unsigned character from two hex ASCII chars in the packet +// string, or return fail_value on failure +uint8_t StdStringExtractor::GetHexU8(uint8_t fail_value, bool set_eof_on_fail) { + // On success, fail_value will be overwritten with the next + // character in the stream + GetHexU8Ex(fail_value, set_eof_on_fail); + return fail_value; +} + +bool StdStringExtractor::GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail) { + int byte = DecodeHexU8(); + if (byte == -1) { + if (set_eof_on_fail || m_index >= m_packet.size()) + m_index = UINT64_MAX; + // ch should not be changed in case of failure + return false; + } + ch = (uint8_t)byte; + return true; +} + +uint32_t StdStringExtractor::GetU32(uint32_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + uint32_t result = static_cast<uint32_t>(::strtoul(cstr, &end, base)); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +int32_t StdStringExtractor::GetS32(int32_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + int32_t result = static_cast<int32_t>(::strtol(cstr, &end, base)); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +uint64_t StdStringExtractor::GetU64(uint64_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + uint64_t result = ::strtoull(cstr, &end, base); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +int64_t StdStringExtractor::GetS64(int64_t fail_value, int base) { + if (m_index < m_packet.size()) { + char *end = nullptr; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + int64_t result = ::strtoll(cstr, &end, base); + + if (end && end != cstr) { + m_index = end - start; + return result; + } + } + return fail_value; +} + +uint32_t StdStringExtractor::GetHexMaxU32(bool little_endian, + uint32_t fail_value) { + uint32_t result = 0; + uint32_t nibble_count = 0; + + SkipSpaces(); + if (little_endian) { + uint32_t shift_amount = 0; + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint32_t... + if (nibble_count >= (sizeof(uint32_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble_lo; + uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]); + ++m_index; + if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + nibble_lo = xdigit_to_sint(m_packet[m_index]); + ++m_index; + result |= ((uint32_t)nibble_hi << (shift_amount + 4)); + result |= ((uint32_t)nibble_lo << shift_amount); + nibble_count += 2; + shift_amount += 8; + } else { + result |= ((uint32_t)nibble_hi << shift_amount); + nibble_count += 1; + shift_amount += 4; + } + } + } else { + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint32_t... + if (nibble_count >= (sizeof(uint32_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble = xdigit_to_sint(m_packet[m_index]); + // Big Endian + result <<= 4; + result |= nibble; + + ++m_index; + ++nibble_count; + } + } + return result; +} + +uint64_t StdStringExtractor::GetHexMaxU64(bool little_endian, + uint64_t fail_value) { + uint64_t result = 0; + uint32_t nibble_count = 0; + + SkipSpaces(); + if (little_endian) { + uint32_t shift_amount = 0; + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint64_t... + if (nibble_count >= (sizeof(uint64_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble_lo; + uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]); + ++m_index; + if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + nibble_lo = xdigit_to_sint(m_packet[m_index]); + ++m_index; + result |= ((uint64_t)nibble_hi << (shift_amount + 4)); + result |= ((uint64_t)nibble_lo << shift_amount); + nibble_count += 2; + shift_amount += 8; + } else { + result |= ((uint64_t)nibble_hi << shift_amount); + nibble_count += 1; + shift_amount += 4; + } + } + } else { + while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { + // Make sure we don't exceed the size of a uint64_t... + if (nibble_count >= (sizeof(uint64_t) * 2)) { + m_index = UINT64_MAX; + return fail_value; + } + + uint8_t nibble = xdigit_to_sint(m_packet[m_index]); + // Big Endian + result <<= 4; + result |= nibble; + + ++m_index; + ++nibble_count; + } + } + return result; +} + +size_t StdStringExtractor::GetHexBytes(void *dst_void, size_t dst_len, + uint8_t fail_fill_value) { + uint8_t *dst = (uint8_t *)dst_void; + size_t bytes_extracted = 0; + while (bytes_extracted < dst_len && GetBytesLeft()) { + dst[bytes_extracted] = GetHexU8(fail_fill_value); + if (IsGood()) + ++bytes_extracted; + else + break; + } + + for (size_t i = bytes_extracted; i < dst_len; ++i) + dst[i] = fail_fill_value; + + return bytes_extracted; +} + +// Decodes all valid hex encoded bytes at the head of the +// StdStringExtractor, limited by dst_len. +// +// Returns the number of bytes successfully decoded +size_t StdStringExtractor::GetHexBytesAvail(void *dst_void, size_t dst_len) { + uint8_t *dst = (uint8_t *)dst_void; + size_t bytes_extracted = 0; + while (bytes_extracted < dst_len) { + int decode = DecodeHexU8(); + if (decode == -1) { + break; + } + dst[bytes_extracted++] = (uint8_t)decode; + } + return bytes_extracted; +} + +size_t StdStringExtractor::GetHexByteString(std::string &str) { + str.clear(); + str.reserve(GetBytesLeft() / 2); + char ch; + while ((ch = GetHexU8()) != '\0') + str.append(1, ch); + return str.size(); +} + +size_t StdStringExtractor::GetHexByteStringFixedLength(std::string &str, + uint32_t nibble_length) { + str.clear(); + + uint32_t nibble_count = 0; + for (const char *pch = Peek(); + (nibble_count < nibble_length) && (pch != nullptr); + str.append(1, GetHexU8(0, false)), pch = Peek(), nibble_count += 2) { + } + + return str.size(); +} + +size_t StdStringExtractor::GetHexByteStringTerminatedBy(std::string &str, + char terminator) { + str.clear(); + char ch; + while ((ch = GetHexU8(0, false)) != '\0') + str.append(1, ch); + if (Peek() && *Peek() == terminator) + return str.size(); + + str.clear(); + return str.size(); +} + +bool StdStringExtractor::GetNameColonValue(std::string &name, + std::string &value) { + // Read something in the form of NNNN:VVVV; where NNNN is any character + // that is not a colon, followed by a ':' character, then a value (one or + // more ';' chars), followed by a ';' + if (m_index < m_packet.size()) { + const size_t colon_idx = m_packet.find(':', m_index); + if (colon_idx != std::string::npos) { + const size_t semicolon_idx = m_packet.find(';', colon_idx); + if (semicolon_idx != std::string::npos) { + name.assign(m_packet, m_index, colon_idx - m_index); + value.assign(m_packet, colon_idx + 1, semicolon_idx - (colon_idx + 1)); + m_index = semicolon_idx + 1; + return true; + } + } + } + m_index = UINT64_MAX; + return false; +} + +void StdStringExtractor::SkipSpaces() { + const size_t n = m_packet.size(); + while (m_index < n && isspace(m_packet[m_index])) + ++m_index; +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.h b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.h new file mode 100644 index 00000000000..5165240bd20 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.h @@ -0,0 +1,105 @@ +//===-- StdStringExtractor.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef utility_StdStringExtractor_h_ +#define utility_StdStringExtractor_h_ + +#include <stdint.h> +#include <string> + + +// Based on StringExtractor, with the added limitation that this file should not +// take a dependency on LLVM, as it is used from debugserver. +class StdStringExtractor { +public: + enum { BigEndian = 0, LittleEndian = 1 }; + // Constructors and Destructors + StdStringExtractor(); + StdStringExtractor(const char *packet_cstr); + virtual ~StdStringExtractor(); + + // Returns true if the file position is still valid for the data + // contained in this string extractor object. + bool IsGood() const { return m_index != UINT64_MAX; } + + uint64_t GetFilePos() const { return m_index; } + + void SetFilePos(uint32_t idx) { m_index = idx; } + + void Clear() { + m_packet.clear(); + m_index = 0; + } + + void SkipSpaces(); + + const std::string &GetStringRef() const { return m_packet; } + + bool Empty() { return m_packet.empty(); } + + size_t GetBytesLeft() { + if (m_index < m_packet.size()) + return m_packet.size() - m_index; + return 0; + } + + char GetChar(char fail_value = '\0'); + + char PeekChar(char fail_value = '\0') { + const char *cstr = Peek(); + if (cstr) + return cstr[0]; + return fail_value; + } + + int DecodeHexU8(); + + uint8_t GetHexU8(uint8_t fail_value = 0, bool set_eof_on_fail = true); + + bool GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail = true); + + bool GetNameColonValue(std::string &name, std::string &value); + + int32_t GetS32(int32_t fail_value, int base = 0); + + uint32_t GetU32(uint32_t fail_value, int base = 0); + + int64_t GetS64(int64_t fail_value, int base = 0); + + uint64_t GetU64(uint64_t fail_value, int base = 0); + + uint32_t GetHexMaxU32(bool little_endian, uint32_t fail_value); + + uint64_t GetHexMaxU64(bool little_endian, uint64_t fail_value); + + size_t GetHexBytes(void *dst, size_t dst_len, uint8_t fail_fill_value); + + size_t GetHexBytesAvail(void *dst, size_t dst_len); + + size_t GetHexByteString(std::string &str); + + size_t GetHexByteStringFixedLength(std::string &str, uint32_t nibble_length); + + size_t GetHexByteStringTerminatedBy(std::string &str, char terminator); + + const char *Peek() { + if (m_index < m_packet.size()) + return m_packet.c_str() + m_index; + return nullptr; + } + +protected: + // For StdStringExtractor only + std::string m_packet; // The string in which to extract data. + uint64_t m_index; // When extracting data from a packet, this index + // will march along as things get extracted. If set + // to UINT64_MAX the end of the packet data was + // reached when decoding information +}; + +#endif // utility_StringExtractor_h_ diff --git a/gnu/llvm/lldb/tools/debugserver/source/SysSignal.cpp b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.cpp new file mode 100644 index 00000000000..9afbbec0987 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.cpp @@ -0,0 +1,94 @@ +//===-- SysSignal.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/SysSignal.h b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.h new file mode 100644 index 00000000000..b6fc67f9be7 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.h @@ -0,0 +1,21 @@ +//===-- SysSignal.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/gnu/llvm/lldb/tools/debugserver/source/TTYState.cpp b/gnu/llvm/lldb/tools/debugserver/source/TTYState.cpp new file mode 100644 index 00000000000..96699a36049 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/TTYState.cpp @@ -0,0 +1,93 @@ +//===-- TTYState.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/26/07. +// +//===----------------------------------------------------------------------===// + +#include "TTYState.h" +#include <fcntl.h> +#include <sys/signal.h> +#include <unistd.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/gnu/llvm/lldb/tools/debugserver/source/TTYState.h b/gnu/llvm/lldb/tools/debugserver/source/TTYState.h new file mode 100644 index 00000000000..01ef579cc6c --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/TTYState.h @@ -0,0 +1,58 @@ +//===-- TTYState.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/26/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __TTYState_h__ +#define __TTYState_h__ + +#include <stdint.h> +#include <termios.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 diff --git a/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist new file mode 100644 index 00000000000..e9a74bd0bf7 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist new file mode 100644 index 00000000000..002e90d98d1 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist new file mode 100644 index 00000000000..b9f57f73123 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.plist new file mode 100644 index 00000000000..c07466e27cd --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist new file mode 100644 index 00000000000..4083f8a75c6 --- /dev/null +++ b/gnu/llvm/lldb/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/gnu/llvm/lldb/tools/debugserver/source/com.apple.internal.xpc.remote.debugserver.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.internal.xpc.remote.debugserver.plist new file mode 100644 index 00000000000..837ebe7b59a --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/com.apple.internal.xpc.remote.debugserver.plist @@ -0,0 +1,35 @@ +<?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.internal.xpc.remote.debugserver</string> + <key>RemoteServices</key> + <dict> + <key>com.apple.internal.debugserver</key> + <dict> + <key>RequireEntitlement</key> + <string>AppleInternal</string> + <key>ExposedToUntrustedDevices</key> + <true/> + </dict> + </dict> + <key>ProgramArguments</key> + <array> + <string>/usr/libexec/remotectl</string> + <string>trampoline</string> + <string>-2</string> + <string>42</string> + <string>com.apple.internal.debugserver</string> + <string>/usr/local/bin/debugserver-nonui</string> + <string>--fd</string> + <string>42</string> + </array> + <key>POSIXSpawnType</key> + <string>Interactive</string> + <key>EnableTransactions</key> + <true/> + <key>EnablePressuredExit</key> + <true/> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver-entitlements.plist b/gnu/llvm/lldb/tools/debugserver/source/debugserver-entitlements.plist new file mode 100644 index 00000000000..1798610305d --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver-entitlements.plist @@ -0,0 +1,30 @@ +<?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>seatbelt-profiles</key> + <array> + <string>debugserver</string> + </array> + <key>com.apple.private.logging.diagnostic</key> + <true/> + <key>com.apple.security.network.server</key> + <true/> + <key>com.apple.security.network.client</key> + <true/> + <key>com.apple.private.memorystatus</key> + <true/> + <key>com.apple.private.cs.debugger</key> + <true/> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist b/gnu/llvm/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist new file mode 100644 index 00000000000..d09bd924bf2 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist @@ -0,0 +1,10 @@ +<?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.private.logging.diagnostic</key> + <true/> + <key>com.apple.private.cs.debugger</key> + <true/> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver.cpp b/gnu/llvm/lldb/tools/debugserver/source/debugserver.cpp new file mode 100644 index 00000000000..42205dedf4b --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver.cpp @@ -0,0 +1,1678 @@ +//===-- debugserver.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <arpa/inet.h> +#include <asl.h> +#include <crt_externs.h> +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <string> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <sys/un.h> + +#include <memory> +#include <vector> + +#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 "OsLogger.h" +#include "PseudoTerminal.h" +#include "RNBContext.h" +#include "RNBRemote.h" +#include "RNBServices.h" +#include "RNBSocket.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 +enum RNBRunLoopMode { + eRNBRunLoopModeInvalid = 0, + eRNBRunLoopModeGetStartModeFromRemoteProtocol, + eRNBRunLoopModeInferiorAttaching, + eRNBRunLoopModeInferiorLaunching, + eRNBRunLoopModeInferiorExecuting, + eRNBRunLoopModePlatformMode, + eRNBRunLoopModeExit +}; + +// 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 (true) { + 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))) + ::strlcpy(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); + 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(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; + // When we enable async structured data packets over another logical + // channel, + // this can be relaxed. + event_mask &= ~RNBContext::event_darwin_log_data_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_proc_profile_data) { + remote->SendAsyncProfileData(); + } + + if (set_events & RNBContext::event_darwin_log_data_available) { + remote->SendAsyncDarwinLogData(); + } + + 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; + ::strlcpy(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"); + ::fflush((FILE *)baton); +} + +void show_version_and_exit(int exit_code) { + printf("%s-%s for %s.\n", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_STR, + RNB_ARCH); + exit(exit_code); +} + +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'}, + {"version", 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 + {"fd", required_argument, NULL, + '2'}, // A file descriptor was passed to this process when spawned that + // is already open and ready for communication + {"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[]) { + // If debugserver is launched with DYLD_INSERT_LIBRARIES, unset it so we + // don't spawn child processes with this enabled. + unsetenv("DYLD_INSERT_LIBRARIES"); + + 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 = std::make_shared<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; + int communication_fd = -1; + 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++] = ':'; + short_options[short_options_idx++] = ':'; + break; + 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 + + bool forward_env = false; + 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; + break; + 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 'V': + show_version_and_exit(0); + 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': + forward_env = true; + break; + + case '2': + // File descriptor passed to this process during fork/exec and is already + // open and ready for communication. + communication_fd = atoi(optarg); + 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 + + // if os_log() support is available, log through that. + auto log_callback = OsLogger::GetLogFunction(); + if (log_callback) { + DNBLogSetLogCallback(log_callback, nullptr); + DNBLog("debugserver will use os_log for internal logging."); + } else { + // Fall back to ASL support. + DNBLogSetLogCallback(ASLLogCallback, NULL); + DNBLog("debugserver will use ASL for internal logging."); + } + 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 && communication_fd == -1) { + // Make sure we at least have port + if (argc < 1) { + show_usage_and_exit(1); + } + // accept 'localhost:' prefix on port number + std::string host_specifier = argv[0]; + auto colon_location = host_specifier.rfind(':'); + if (colon_location != std::string::npos) { + host = host_specifier.substr(0, colon_location); + std::string port_str = + host_specifier.substr(colon_location + 1, std::string::npos); + char *end_ptr; + port = strtoul(port_str.c_str(), &end_ptr, 0); + if (end_ptr < port_str.c_str() + port_str.size()) + show_usage_and_exit(2); + if (host.front() == '[' && host.back() == ']') + host = host.substr(1, host.size() - 2); + 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; + strlcpy(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; + + if (forward_env || start_mode == eRNBRunLoopModeInferiorLaunching) { + // Pass the current environment down to the process that gets launched + // This happens automatically in the "launching" mode. For the rest, we + // only do that if the user explicitly requested this via --forward-env + // argument. + char **host_env = *_NSGetEnviron(); + char *env_entry; + size_t i; + for (i = 0; (env_entry = host_env[i]) != NULL; ++i) + remote->Context().PushEnvironmentIfNeeded(env_entry); + } + + 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; + } else if (communication_fd >= 0) { + // We were passed a file descriptor to use during fork/exec that is + // already open + // in our process, so lets just use it! + if (remote->Comm().useFD(communication_fd)) + mode = eRNBRunLoopModeExit; + else + remote->StartReadRemoteDataThread(); + } + + 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; + } else if (communication_fd >= 0) { + // We were passed a file descriptor to use during fork/exec that is + // already open + // in our process, so lets just use it! + if (remote->Comm().useFD(communication_fd)) + mode = eRNBRunLoopModeExit; + else + remote->StartReadRemoteDataThread(); + } + + 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; + } else if (communication_fd >= 0) { + // We were passed a file descriptor to use during fork/exec that is + // already open + // in our process, so lets just use it! + if (remote->Comm().useFD(communication_fd)) + mode = eRNBRunLoopModeExit; + else + remote->StartReadRemoteDataThread(); + } + + 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; + } else if (communication_fd >= 0) { + // We were passed a file descriptor to use during fork/exec that is + // already open + // in our process, so lets just use it! + if (remote->Comm().useFD(communication_fd)) + mode = eRNBRunLoopModeExit; + else + remote->StartReadRemoteDataThread(); + } + + if (mode != eRNBRunLoopModeExit) + mode = RNBRunLoopPlatform(remote); + break; + + default: + mode = eRNBRunLoopModeExit; + break; + case eRNBRunLoopModeExit: + break; + } + } + + remote->StopReadRemoteDataThread(); + remote->Context().SetProcessID(INVALID_NUB_PROCESS); + RNBLogSTDOUT("Exiting.\n"); + + return 0; +} diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver_vers.c.in b/gnu/llvm/lldb/tools/debugserver/source/debugserver_vers.c.in new file mode 100644 index 00000000000..00e34c29b07 --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver_vers.c.in @@ -0,0 +1,2 @@ +const unsigned char debugserverVersionString[] __attribute__ ((used)) = "@(#)PROGRAM:LLDB PROJECT:lldb-@LLDB_VERSION_MAJOR@.@LLDB_VERSION_MINOR@.@LLDB_VERSION_PATCH@" "\n"; +const double debugserverVersionNumber __attribute__ ((used)) = (double)@LLDB_VERSION_MAJOR@.@LLDB_VERSION_MINOR@; diff --git a/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp new file mode 100644 index 00000000000..0c53fa4039c --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp @@ -0,0 +1,376 @@ +//===-- libdebugserver.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <errno.h> +#include <getopt.h> +#include <netinet/in.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +#include <memory> + +#include "DNB.h" +#include "DNBLog.h" +#include "DNBTimer.h" +#include "PseudoTerminal.h" +#include "RNBContext.h" +#include "RNBRemote.h" +#include "RNBServices.h" +#include "RNBSocket.h" +#include "SysSignal.h" + +// Run loop modes which determine which run loop function will be called +enum RNBRunLoopMode { + eRNBRunLoopModeInvalid = 0, + eRNBRunLoopModeGetStartModeFromRemoteProtocol, + eRNBRunLoopModeInferiorExecuting, + eRNBRunLoopModeExit +}; + +// 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 (true) { + 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) { + // 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 = std::make_shared<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/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.h b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.h new file mode 100644 index 00000000000..17b048bcbbc --- /dev/null +++ b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.h @@ -0,0 +1,14 @@ +//===-- libdebugserver.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef debugserver_libdebugserver_h +#define debugserver_libdebugserver_h + +int debug_server_main(int fd); + +#endif diff --git a/gnu/llvm/lldb/tools/driver/CMakeLists.txt b/gnu/llvm/lldb/tools/driver/CMakeLists.txt new file mode 100644 index 00000000000..c31863b205c --- /dev/null +++ b/gnu/llvm/lldb/tools/driver/CMakeLists.txt @@ -0,0 +1,48 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(LLDBOptionsTableGen) + +if(APPLE) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/lldb-Info.plist.in + ${CMAKE_CURRENT_BINARY_DIR}/lldb-Info.plist + ) + # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-Info.plist") +endif() + +add_lldb_tool(lldb + Driver.cpp + Platform.cpp + + LINK_LIBS + liblldb + + LINK_COMPONENTS + Option + Support + ) + +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + add_definitions( -DIMPORT_LIBLLDB ) +endif() + +add_dependencies(lldb + LLDBOptionsTableGen + ${tablegen_deps} +) + +set_target_properties(LLDBOptionsTableGen PROPERTIES FOLDER "lldb misc") + +if(LLDB_BUILD_FRAMEWORK) + # In the build-tree, we know the exact path to the framework directory. + # The installed framework can be in different locations. + lldb_setup_rpaths(lldb + BUILD_RPATH + "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}" + INSTALL_RPATH + "@loader_path/../../../SharedFrameworks" + "@loader_path/../../System/Library/PrivateFrameworks" + "@loader_path/../../Library/PrivateFrameworks" + ) +endif() diff --git a/gnu/llvm/lldb/tools/driver/Driver.cpp b/gnu/llvm/lldb/tools/driver/Driver.cpp new file mode 100644 index 00000000000..73874389aa1 --- /dev/null +++ b/gnu/llvm/lldb/tools/driver/Driver.cpp @@ -0,0 +1,896 @@ +//===-- Driver.cpp ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Driver.h" + +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBFile.h" +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBReproducer.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +#include <algorithm> +#include <atomic> +#include <bitset> +#include <csignal> +#include <string> +#include <thread> +#include <utility> + +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// Includes for pipe() +#if defined(_WIN32) +#include <fcntl.h> +#include <io.h> +#else +#include <unistd.h> +#endif + +#if !defined(__APPLE__) +#include "llvm/Support/DataTypes.h" +#endif + +using namespace lldb; +using namespace llvm; + +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Options.inc" +#undef OPTION +}; + +class LLDBOptTable : public opt::OptTable { +public: + LLDBOptTable() : OptTable(InfoTable) {} +}; +} // namespace + +static void reset_stdin_termios(); +static bool g_old_stdin_termios_is_valid = false; +static struct termios g_old_stdin_termios; + +static Driver *g_driver = nullptr; + +// In the Driver::MainLoop, we change the terminal settings. This function is +// added as an atexit handler to make sure we clean them up. +static void reset_stdin_termios() { + if (g_old_stdin_termios_is_valid) { + g_old_stdin_termios_is_valid = false; + ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios); + } +} + +Driver::Driver() + : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) { + // We want to be able to handle CTRL+D in the terminal to have it terminate + // certain input + m_debugger.SetCloseInputOnEOF(false); + g_driver = this; +} + +Driver::~Driver() { g_driver = nullptr; } + +void Driver::OptionData::AddInitialCommand(std::string command, + CommandPlacement placement, + bool is_file, SBError &error) { + std::vector<InitialCmdEntry> *command_set; + switch (placement) { + case eCommandPlacementBeforeFile: + command_set = &(m_initial_commands); + break; + case eCommandPlacementAfterFile: + command_set = &(m_after_file_commands); + break; + case eCommandPlacementAfterCrash: + command_set = &(m_after_crash_commands); + break; + } + + if (is_file) { + SBFileSpec file(command.c_str()); + if (file.Exists()) + command_set->push_back(InitialCmdEntry(command, is_file)); + else if (file.ResolveExecutableLocation()) { + char final_path[PATH_MAX]; + file.GetPath(final_path, sizeof(final_path)); + command_set->push_back(InitialCmdEntry(final_path, is_file)); + } else + error.SetErrorStringWithFormat( + "file specified in --source (-s) option doesn't exist: '%s'", + command.c_str()); + } else + command_set->push_back(InitialCmdEntry(command, is_file)); +} + +void Driver::WriteCommandsForSourcing(CommandPlacement placement, + SBStream &strm) { + std::vector<OptionData::InitialCmdEntry> *command_set; + switch (placement) { + case eCommandPlacementBeforeFile: + command_set = &m_option_data.m_initial_commands; + break; + case eCommandPlacementAfterFile: + command_set = &m_option_data.m_after_file_commands; + break; + case eCommandPlacementAfterCrash: + command_set = &m_option_data.m_after_crash_commands; + break; + } + + for (const auto &command_entry : *command_set) { + const char *command = command_entry.contents.c_str(); + if (command_entry.is_file) { + bool source_quietly = + m_option_data.m_source_quietly || command_entry.source_quietly; + strm.Printf("command source -s %i '%s'\n", + static_cast<int>(source_quietly), command); + } else + strm.Printf("%s\n", command); + } +} + +// Check the arguments that were passed to this program to make sure they are +// valid and to get their argument values (if any). Return a boolean value +// indicating whether or not to start up the full debugger (i.e. the Command +// Interpreter) or not. Return FALSE if the arguments were invalid OR if the +// user only wanted help or version information. +SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) { + SBError error; + + // This is kind of a pain, but since we make the debugger in the Driver's + // constructor, we can't know at that point whether we should read in init + // files yet. So we don't read them in in the Driver constructor, then set + // the flags back to "read them in" here, and then if we see the "-n" flag, + // we'll turn it off again. Finally we have to read them in by hand later in + // the main loop. + m_debugger.SkipLLDBInitFiles(false); + m_debugger.SkipAppInitFiles(false); + + if (args.hasArg(OPT_version)) { + m_option_data.m_print_version = true; + } + + if (args.hasArg(OPT_python_path)) { + m_option_data.m_print_python_path = true; + } + + if (args.hasArg(OPT_batch)) { + m_option_data.m_batch = true; + } + + if (auto *arg = args.getLastArg(OPT_core)) { + auto arg_value = arg->getValue(); + SBFileSpec file(arg_value); + if (!file.Exists()) { + error.SetErrorStringWithFormat( + "file specified in --core (-c) option doesn't exist: '%s'", + arg_value); + return error; + } + m_option_data.m_core_file = arg_value; + } + + if (args.hasArg(OPT_editor)) { + m_option_data.m_use_external_editor = true; + } + + if (args.hasArg(OPT_no_lldbinit)) { + m_debugger.SkipLLDBInitFiles(true); + m_debugger.SkipAppInitFiles(true); + } + + if (args.hasArg(OPT_local_lldbinit)) { + lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true", + m_debugger.GetInstanceName()); + } + + if (args.hasArg(OPT_no_use_colors)) { + m_debugger.SetUseColor(false); + m_option_data.m_debug_mode = true; + } + + if (auto *arg = args.getLastArg(OPT_file)) { + auto arg_value = arg->getValue(); + SBFileSpec file(arg_value); + if (file.Exists()) { + m_option_data.m_args.emplace_back(arg_value); + } else if (file.ResolveExecutableLocation()) { + char path[PATH_MAX]; + file.GetPath(path, sizeof(path)); + m_option_data.m_args.emplace_back(path); + } else { + error.SetErrorStringWithFormat( + "file specified in --file (-f) option doesn't exist: '%s'", + arg_value); + return error; + } + } + + if (auto *arg = args.getLastArg(OPT_arch)) { + auto arg_value = arg->getValue(); + if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) { + error.SetErrorStringWithFormat( + "invalid architecture in the -a or --arch option: '%s'", arg_value); + return error; + } + } + + if (auto *arg = args.getLastArg(OPT_script_language)) { + auto arg_value = arg->getValue(); + m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value)); + } + + if (args.hasArg(OPT_source_quietly)) { + m_option_data.m_source_quietly = true; + } + + if (auto *arg = args.getLastArg(OPT_attach_name)) { + auto arg_value = arg->getValue(); + m_option_data.m_process_name = arg_value; + } + + if (args.hasArg(OPT_wait_for)) { + m_option_data.m_wait_for = true; + } + + if (auto *arg = args.getLastArg(OPT_attach_pid)) { + auto arg_value = arg->getValue(); + char *remainder; + m_option_data.m_process_pid = strtol(arg_value, &remainder, 0); + if (remainder == arg_value || *remainder != '\0') { + error.SetErrorStringWithFormat( + "Could not convert process PID: \"%s\" into a pid.", arg_value); + return error; + } + } + + if (auto *arg = args.getLastArg(OPT_repl_language)) { + auto arg_value = arg->getValue(); + m_option_data.m_repl_lang = + SBLanguageRuntime::GetLanguageTypeFromString(arg_value); + if (m_option_data.m_repl_lang == eLanguageTypeUnknown) { + error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"", + arg_value); + return error; + } + } + + if (args.hasArg(OPT_repl)) { + m_option_data.m_repl = true; + } + + if (auto *arg = args.getLastArg(OPT_repl_)) { + m_option_data.m_repl = true; + if (auto arg_value = arg->getValue()) + m_option_data.m_repl_options = arg_value; + } + + // We need to process the options below together as their relative order + // matters. + for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash, + OPT_source, OPT_source_before_file, + OPT_one_line, OPT_one_line_before_file)) { + auto arg_value = arg->getValue(); + if (arg->getOption().matches(OPT_source_on_crash)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash, + true, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_one_line_on_crash)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash, + false, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_source)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile, + true, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_source_before_file)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile, + true, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_one_line)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile, + false, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_one_line_before_file)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile, + false, error); + if (error.Fail()) + return error; + } + } + + if (m_option_data.m_process_name.empty() && + m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) { + + // If the option data args array is empty that means the file was not + // specified with -f and we need to get it from the input args. + if (m_option_data.m_args.empty()) { + if (auto *arg = args.getLastArgNoClaim(OPT_INPUT)) { + m_option_data.m_args.push_back(arg->getAsString((args))); + } + } + + // Any argument following -- is an argument for the inferior. + if (auto *arg = args.getLastArgNoClaim(OPT_REM)) { + for (auto value : arg->getValues()) + m_option_data.m_args.emplace_back(value); + } + } else if (args.getLastArgNoClaim() != nullptr) { + WithColor::warning() << "program arguments are ignored when attaching.\n"; + } + + if (m_option_data.m_print_version) { + llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n'; + exiting = true; + return error; + } + + if (m_option_data.m_print_python_path) { + SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath(); + if (python_file_spec.IsValid()) { + char python_path[PATH_MAX]; + size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX); + if (num_chars < PATH_MAX) { + llvm::outs() << python_path << '\n'; + } else + llvm::outs() << "<PATH TOO LONG>\n"; + } else + llvm::outs() << "<COULD NOT FIND PATH>\n"; + exiting = true; + return error; + } + + return error; +} + +static inline int OpenPipe(int fds[2], std::size_t size) { +#ifdef _WIN32 + return _pipe(fds, size, O_BINARY); +#else + (void)size; + return pipe(fds); +#endif +} + +static ::FILE *PrepareCommandsForSourcing(const char *commands_data, + size_t commands_size) { + enum PIPES { READ, WRITE }; // Indexes for the read and write fds + int fds[2] = {-1, -1}; + + if (OpenPipe(fds, commands_size) != 0) { + WithColor::error() + << "can't create pipe file descriptors for LLDB commands\n"; + return nullptr; + } + + ssize_t nrwr = write(fds[WRITE], commands_data, commands_size); + if (size_t(nrwr) != commands_size) { + WithColor::error() + << format( + "write(%i, %p, %" PRIu64 + ") failed (errno = %i) when trying to open LLDB commands pipe", + fds[WRITE], static_cast<const void *>(commands_data), + static_cast<uint64_t>(commands_size), errno) + << '\n'; + llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]); + llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]); + return nullptr; + } + + // Close the write end of the pipe, so that the command interpreter will exit + // when it consumes all the data. + llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]); + + // Open the read file descriptor as a FILE * that we can return as an input + // handle. + ::FILE *commands_file = fdopen(fds[READ], "rb"); + if (commands_file == nullptr) { + WithColor::error() << format("fdopen(%i, \"rb\") failed (errno = %i) " + "when trying to open LLDB commands pipe", + fds[READ], errno) + << '\n'; + llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]); + return nullptr; + } + + // 'commands_file' now owns the read descriptor. + return commands_file; +} + +std::string EscapeString(std::string arg) { + std::string::size_type pos = 0; + while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) { + arg.insert(pos, 1, '\\'); + pos += 2; + } + return '"' + arg + '"'; +} + +int Driver::MainLoop() { + if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) { + g_old_stdin_termios_is_valid = true; + atexit(reset_stdin_termios); + } + +#ifndef _MSC_VER + // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets + // which causes it to miss newlines depending on whether there have been an + // odd or even number of characters. Bug has been reported to MS via Connect. + ::setbuf(stdin, nullptr); +#endif + ::setbuf(stdout, nullptr); + + m_debugger.SetErrorFileHandle(stderr, false); + m_debugger.SetOutputFileHandle(stdout, false); + // Don't take ownership of STDIN yet... + m_debugger.SetInputFileHandle(stdin, false); + + m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); + + struct winsize window_size; + if ((isatty(STDIN_FILENO) != 0) && + ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { + if (window_size.ws_col > 0) + m_debugger.SetTerminalWidth(window_size.ws_col); + } + + SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter(); + + // Before we handle any options from the command line, we parse the + // .lldbinit file in the user's home directory. + SBCommandReturnObject result; + sb_interpreter.SourceInitFileInHomeDirectory(result); + if (m_option_data.m_debug_mode) { + result.PutError(m_debugger.GetErrorFile()); + result.PutOutput(m_debugger.GetOutputFile()); + } + + // Source the local .lldbinit file if it exists and we're allowed to source. + // Here we want to always print the return object because it contains the + // warning and instructions to load local lldbinit files. + sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result); + result.PutError(m_debugger.GetErrorFile()); + result.PutOutput(m_debugger.GetOutputFile()); + + // We allow the user to specify an exit code when calling quit which we will + // return when exiting. + m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true); + + // Now we handle options we got from the command line + SBStream commands_stream; + + // First source in the commands specified to be run before the file arguments + // are processed. + WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream); + + // If we're not in --repl mode, add the commands to process the file + // arguments, and the commands specified to run afterwards. + if (!m_option_data.m_repl) { + const size_t num_args = m_option_data.m_args.size(); + if (num_args > 0) { + char arch_name[64]; + if (lldb::SBDebugger::GetDefaultArchitecture(arch_name, + sizeof(arch_name))) + commands_stream.Printf("target create --arch=%s %s", arch_name, + EscapeString(m_option_data.m_args[0]).c_str()); + else + commands_stream.Printf("target create %s", + EscapeString(m_option_data.m_args[0]).c_str()); + + if (!m_option_data.m_core_file.empty()) { + commands_stream.Printf(" --core %s", + EscapeString(m_option_data.m_core_file).c_str()); + } + commands_stream.Printf("\n"); + + if (num_args > 1) { + commands_stream.Printf("settings set -- target.run-args "); + for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx) + commands_stream.Printf( + " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str()); + commands_stream.Printf("\n"); + } + } else if (!m_option_data.m_core_file.empty()) { + commands_stream.Printf("target create --core %s\n", + EscapeString(m_option_data.m_core_file).c_str()); + } else if (!m_option_data.m_process_name.empty()) { + commands_stream.Printf( + "process attach --name %s", + EscapeString(m_option_data.m_process_name).c_str()); + + if (m_option_data.m_wait_for) + commands_stream.Printf(" --waitfor"); + + commands_stream.Printf("\n"); + + } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) { + commands_stream.Printf("process attach --pid %" PRIu64 "\n", + m_option_data.m_process_pid); + } + + WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream); + } else if (!m_option_data.m_after_file_commands.empty()) { + // We're in repl mode and after-file-load commands were specified. + WithColor::warning() << "commands specified to run after file load (via -o " + "or -s) are ignored in REPL mode.\n"; + } + + if (m_option_data.m_debug_mode) { + result.PutError(m_debugger.GetErrorFile()); + result.PutOutput(m_debugger.GetOutputFile()); + } + + const bool handle_events = true; + const bool spawn_thread = false; + + // Check if we have any data in the commands stream, and if so, save it to a + // temp file + // so we can then run the command interpreter using the file contents. + const char *commands_data = commands_stream.GetData(); + const size_t commands_size = commands_stream.GetSize(); + + // The command file might have requested that we quit, this variable will + // track that. + bool quit_requested = false; + bool stopped_for_crash = false; + if ((commands_data != nullptr) && (commands_size != 0u)) { + bool success = true; + FILE *commands_file = + PrepareCommandsForSourcing(commands_data, commands_size); + if (commands_file != nullptr) { + m_debugger.SetInputFileHandle(commands_file, true); + + // Set the debugger into Sync mode when running the command file. + // Otherwise command files + // that run the target won't run in a sensible way. + bool old_async = m_debugger.GetAsync(); + m_debugger.SetAsync(false); + int num_errors = 0; + + SBCommandInterpreterRunOptions options; + options.SetStopOnError(true); + if (m_option_data.m_batch) + options.SetStopOnCrash(true); + + m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options, + num_errors, quit_requested, + stopped_for_crash); + + if (m_option_data.m_batch && stopped_for_crash && + !m_option_data.m_after_crash_commands.empty()) { + SBStream crash_commands_stream; + WriteCommandsForSourcing(eCommandPlacementAfterCrash, + crash_commands_stream); + const char *crash_commands_data = crash_commands_stream.GetData(); + const size_t crash_commands_size = crash_commands_stream.GetSize(); + commands_file = PrepareCommandsForSourcing(crash_commands_data, + crash_commands_size); + if (commands_file != nullptr) { + bool local_quit_requested; + bool local_stopped_for_crash; + m_debugger.SetInputFileHandle(commands_file, true); + + m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options, + num_errors, local_quit_requested, + local_stopped_for_crash); + if (local_quit_requested) + quit_requested = true; + } + } + m_debugger.SetAsync(old_async); + } else + success = false; + + // Something went wrong with command pipe + if (!success) { + exit(1); + } + } + + // Now set the input file handle to STDIN and run the command + // interpreter again in interactive mode or repl mode and let the debugger + // take ownership of stdin + + bool go_interactive = true; + if (quit_requested) + go_interactive = false; + else if (m_option_data.m_batch && !stopped_for_crash) + go_interactive = false; + + if (go_interactive) { + m_debugger.SetInputFileHandle(stdin, true); + + if (m_option_data.m_repl) { + const char *repl_options = nullptr; + if (!m_option_data.m_repl_options.empty()) + repl_options = m_option_data.m_repl_options.c_str(); + SBError error( + m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options)); + if (error.Fail()) { + const char *error_cstr = error.GetCString(); + if ((error_cstr != nullptr) && (error_cstr[0] != 0)) + WithColor::error() << error_cstr << '\n'; + else + WithColor::error() << error.GetError() << '\n'; + } + } else { + m_debugger.RunCommandInterpreter(handle_events, spawn_thread); + } + } + + reset_stdin_termios(); + fclose(stdin); + + int exit_code = sb_interpreter.GetQuitStatus(); + SBDebugger::Destroy(m_debugger); + return exit_code; +} + +void Driver::ResizeWindow(unsigned short col) { + GetDebugger().SetTerminalWidth(col); +} + +void sigwinch_handler(int signo) { + struct winsize window_size; + if ((isatty(STDIN_FILENO) != 0) && + ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { + if ((window_size.ws_col > 0) && g_driver != nullptr) { + g_driver->ResizeWindow(window_size.ws_col); + } + } +} + +void sigint_handler(int signo) { +#ifdef _WIN32 // Restore handler as it is not persistent on Windows + signal(SIGINT, sigint_handler); +#endif + static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; + if (g_driver != nullptr) { + if (!g_interrupt_sent.test_and_set()) { + g_driver->GetDebugger().DispatchInputInterrupt(); + g_interrupt_sent.clear(); + return; + } + } + + _exit(signo); +} + +void sigtstp_handler(int signo) { + if (g_driver != nullptr) + g_driver->GetDebugger().SaveInputTerminalState(); + + signal(signo, SIG_DFL); + kill(getpid(), signo); + signal(signo, sigtstp_handler); +} + +void sigcont_handler(int signo) { + if (g_driver != nullptr) + g_driver->GetDebugger().RestoreInputTerminalState(); + + signal(signo, SIG_DFL); + kill(getpid(), signo); + signal(signo, sigcont_handler); +} + +void reproducer_handler(void *argv0) { + if (SBReproducer::Generate()) { + auto exe = static_cast<const char *>(argv0); + llvm::outs() << "********************\n"; + llvm::outs() << "Crash reproducer for "; + llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n'; + llvm::outs() << '\n'; + llvm::outs() << "Reproducer written to '" << SBReproducer::GetPath() + << "'\n"; + llvm::outs() << '\n'; + llvm::outs() << "Before attaching the reproducer to a bug report:\n"; + llvm::outs() << " - Look at the directory to ensure you're willing to " + "share its content.\n"; + llvm::outs() + << " - Make sure the reproducer works by replaying the reproducer.\n"; + llvm::outs() << '\n'; + llvm::outs() << "Replay the reproducer with the following command:\n"; + llvm::outs() << exe << " -replay " << SBReproducer::GetPath() << "\n"; + llvm::outs() << "********************\n"; + } +} + +static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { + std::string usage_str = tool_name.str() + " [options]"; + table.PrintHelp(llvm::outs(), usage_str.c_str(), "LLDB", false); + + std::string examples = R"___( +EXAMPLES: + The debugger can be started in several modes. + + Passing an executable as a positional argument prepares lldb to debug the + given executable. Arguments passed after -- are considered arguments to the + debugged executable. + + lldb --arch x86_64 /path/to/program -- --arch arvm7 + + Passing one of the attach options causes lldb to immediately attach to the + given process. + + lldb -p <pid> + lldb -n <process-name> + + Passing --repl starts lldb in REPL mode. + + lldb -r + + Passing --core causes lldb to debug the core file. + + lldb -c /path/to/core + + Command options can be combined with these modes and cause lldb to run the + specified commands before or after events, like loading the file or crashing, + in the order provided on the command line. + + lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt' + lldb -S /source/before/file -s /source/after/file + lldb -K /source/before/crash -k /source/after/crash + + Note: In REPL mode no file is loaded, so commands specified to run after + loading the file (via -o or -s) will be ignored.)___"; + llvm::outs() << examples << '\n'; +} + +llvm::Optional<int> InitializeReproducer(opt::InputArgList &input_args) { + if (auto *replay_path = input_args.getLastArg(OPT_replay)) { + const bool skip_version_check = input_args.hasArg(OPT_skip_version_check); + if (const char *error = + SBReproducer::Replay(replay_path->getValue(), skip_version_check)) { + WithColor::error() << "reproducer replay failed: " << error << '\n'; + return 1; + } + return 0; + } + + bool capture = input_args.hasArg(OPT_capture); + auto *capture_path = input_args.getLastArg(OPT_capture_path); + + if (capture || capture_path) { + if (capture_path) { + if (!capture) + WithColor::warning() << "-capture-path specified without -capture\n"; + if (const char *error = SBReproducer::Capture(capture_path->getValue())) { + WithColor::error() << "reproducer capture failed: " << error << '\n'; + return 1; + } + } else { + const char *error = SBReproducer::Capture(); + if (error) { + WithColor::error() << "reproducer capture failed: " << error << '\n'; + return 1; + } + } + } + + return llvm::None; +} + +int main(int argc, char const *argv[]) { + // Setup LLVM signal handlers and make sure we call llvm_shutdown() on + // destruction. + llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false); + + // Parse arguments. + LLDBOptTable T; + unsigned MAI; + unsigned MAC; + ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1); + opt::InputArgList input_args = T.ParseArgs(arg_arr, MAI, MAC); + + if (input_args.hasArg(OPT_help)) { + printHelp(T, llvm::sys::path::filename(argv[0])); + return 0; + } + + for (auto *arg : input_args.filtered(OPT_UNKNOWN)) { + WithColor::warning() << "ignoring unknown option: " << arg->getSpelling() + << '\n'; + } + + if (auto exit_code = InitializeReproducer(input_args)) { + return *exit_code; + } + + // Register the reproducer signal handler. + llvm::sys::AddSignalHandler(reproducer_handler, const_cast<char *>(argv[0])); + + SBError error = SBDebugger::InitializeWithErrorHandling(); + if (error.Fail()) { + WithColor::error() << "initialization failed: " << error.GetCString() + << '\n'; + return 1; + } + SBHostOS::ThreadCreated("<lldb.driver.main-thread>"); + + signal(SIGINT, sigint_handler); +#if !defined(_MSC_VER) + signal(SIGPIPE, SIG_IGN); + signal(SIGWINCH, sigwinch_handler); + signal(SIGTSTP, sigtstp_handler); + signal(SIGCONT, sigcont_handler); +#endif + + int exit_code = 0; + // Create a scope for driver so that the driver object will destroy itself + // before SBDebugger::Terminate() is called. + { + Driver driver; + + bool exiting = false; + SBError error(driver.ProcessArgs(input_args, exiting)); + if (error.Fail()) { + exit_code = 1; + if (const char *error_cstr = error.GetCString()) + WithColor::error() << error_cstr << '\n'; + } else if (!exiting) { + exit_code = driver.MainLoop(); + } + } + + SBDebugger::Terminate(); + return exit_code; +} diff --git a/gnu/llvm/lldb/tools/driver/Driver.h b/gnu/llvm/lldb/tools/driver/Driver.h new file mode 100644 index 00000000000..f442458ae40 --- /dev/null +++ b/gnu/llvm/lldb/tools/driver/Driver.h @@ -0,0 +1,102 @@ +//===-- Driver.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Driver_h_ +#define lldb_Driver_h_ + +#include "Platform.h" + +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" + +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" + +#include <set> +#include <string> +#include <vector> + +class Driver : public lldb::SBBroadcaster { +public: + enum CommandPlacement { + eCommandPlacementBeforeFile, + eCommandPlacementAfterFile, + eCommandPlacementAfterCrash, + }; + + Driver(); + + virtual ~Driver(); + + /// Runs the main loop. + /// + /// \return The exit code that the process should return. + int MainLoop(); + + lldb::SBError ProcessArgs(const llvm::opt::InputArgList &args, bool &exiting); + + void WriteCommandsForSourcing(CommandPlacement placement, + lldb::SBStream &strm); + + struct OptionData { + void AddInitialCommand(std::string command, CommandPlacement placement, + bool is_file, lldb::SBError &error); + + struct InitialCmdEntry { + InitialCmdEntry(std::string contents, bool in_is_file, + bool in_quiet = false) + : contents(std::move(contents)), is_file(in_is_file), + source_quietly(in_quiet) {} + + std::string contents; + bool is_file; + bool source_quietly; + }; + + std::vector<std::string> m_args; + + lldb::LanguageType m_repl_lang = lldb::eLanguageTypeUnknown; + lldb::pid_t m_process_pid = LLDB_INVALID_PROCESS_ID; + + std::string m_core_file; + std::string m_crash_log; + std::string m_repl_options; + std::string m_process_name; + + std::vector<InitialCmdEntry> m_initial_commands; + std::vector<InitialCmdEntry> m_after_file_commands; + std::vector<InitialCmdEntry> m_after_crash_commands; + + bool m_debug_mode = false; + bool m_source_quietly = false; + bool m_print_version = false; + bool m_print_python_path = false; + bool m_wait_for = false; + bool m_repl = false; + bool m_batch = false; + + // FIXME: When we have set/show variables we can remove this from here. + bool m_use_external_editor = false; + + using OptionSet = std::set<char>; + OptionSet m_seen_options; + }; + + lldb::SBDebugger &GetDebugger() { return m_debugger; } + + void ResizeWindow(unsigned short col); + +private: + lldb::SBDebugger m_debugger; + OptionData m_option_data; +}; + +#endif // lldb_Driver_h_ diff --git a/gnu/llvm/lldb/tools/driver/Options.td b/gnu/llvm/lldb/tools/driver/Options.td new file mode 100644 index 00000000000..c237f568f64 --- /dev/null +++ b/gnu/llvm/lldb/tools/driver/Options.td @@ -0,0 +1,238 @@ +include "llvm/Option/OptParser.td" + +class F<string name>: Flag<["--", "-"], name>; +class S<string name>: Separate<["--", "-"], name>; +class R<list<string> prefixes, string name> + : Option<prefixes, name, KIND_REMAINING_ARGS>; + +// Please keep this in sync with the man page in docs/man/lldb.rst + +// Attaching options. +def grp_attach : OptionGroup<"attaching">, HelpText<"ATTACHING">; + +def attach_name: Separate<["--", "-"], "attach-name">, + MetaVarName<"<name>">, + HelpText<"Tells the debugger to attach to a process with the given name.">, + Group<grp_attach>; +def: Separate<["-"], "n">, + Alias<attach_name>, + HelpText<"Alias for --attach-name">, + Group<grp_attach>; + +def wait_for: F<"wait-for">, + HelpText<"Tells the debugger to wait for a process with the given pid or name to launch before attaching.">, + Group<grp_attach>; +def: Flag<["-"], "w">, + Alias<wait_for>, + HelpText<"Alias for --wait-for">, + Group<grp_attach>; + +def attach_pid: Separate<["--", "-"], "attach-pid">, + MetaVarName<"<pid>">, + HelpText<"Tells the debugger to attach to a process with the given pid.">, + Group<grp_attach>; +def: Separate<["-"], "p">, + Alias<attach_pid>, + HelpText<"Alias for --attach-pid">, + Group<grp_attach>; + + +// Scripting options. +def grp_scripting : OptionGroup<"scripting">, HelpText<"SCRIPTING">; + +def python_path: F<"python-path">, + HelpText<"Prints out the path to the lldb.py file for this version of lldb.">, + Group<grp_scripting>; +def: Flag<["-"], "P">, + Alias<python_path>, + HelpText<"Alias for --python-path">, + Group<grp_scripting>; + +def script_language: Separate<["--", "-"], "script-language">, + MetaVarName<"<language>">, + HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">, + Group<grp_scripting>; +def: Separate<["-"], "l">, + Alias<script_language>, + HelpText<"Alias for --script-language">, + Group<grp_scripting>; + +// Repl options. +def grp_repl : OptionGroup<"repl">, HelpText<"REPL">; + +def repl: Flag<["--", "-"], "repl">, + HelpText<"Runs lldb in REPL mode with a stub process.">, + Group<grp_repl>; +def: Flag<["-"], "r">, + Alias<repl>, + HelpText<"Alias for --repl">, + Group<grp_repl>; +def repl_: Joined<["--", "-"], "repl=">, + MetaVarName<"<flags>">, + HelpText<"Runs lldb in REPL mode with a stub process with the given flags.">, + Group<grp_repl>; +def: Joined<["-"], "r=">, + MetaVarName<"<flags>">, + Alias<repl_>, + HelpText<"Alias for --repl=<flags>">, + Group<grp_repl>; + +def repl_language: Separate<["--", "-"], "repl-language">, + MetaVarName<"<language>">, + HelpText<"Chooses the language for the REPL.">, + Group<grp_repl>; +def: Separate<["-"], "R">, + Alias<repl_language>, + HelpText<"Alias for --repl-language">, + Group<grp_repl>; + + +// Command options. +def grp_command : OptionGroup<"command">, HelpText<"COMMANDS">; + +def no_lldbinit: F<"no-lldbinit">, + HelpText<"Do not automatically parse any '.lldbinit' files.">, + Group<grp_command>; +def: Flag<["-"], "x">, + Alias<no_lldbinit>, + HelpText<"Alias for --no-lldbinit">, + Group<grp_command>; +def local_lldbinit: F<"local-lldbinit">, + HelpText<"Allow the debugger to parse the .lldbinit files in the current working directory, unless --no-lldbinit is passed.">, + Group<grp_command>; + +def batch: F<"batch">, + HelpText<"Tells the debugger to run the commands from -s, -S, -o & -O, and then quit.">, + Group<grp_command>; +def: Flag<["-"], "b">, + Alias<batch>, + HelpText<"Alias for --batch">, + Group<grp_command>; + +def source_quietly: F<"source-quietly">, + HelpText<"Tells the debugger to execute this one-line lldb command before any file has been loaded.">, + Group<grp_command>; +def: Flag<["-"], "Q">, + Alias<source_quietly>, + HelpText<"Alias for --source-quietly">, + Group<grp_command>; + +def one_line_on_crash: Separate<["--", "-"], "one-line-on-crash">, + MetaVarName<"<command>">, + HelpText<"When in batch mode, tells the debugger to run this one-line lldb command if the target crashes.">, + Group<grp_command>; +def: Separate<["-"], "k">, + Alias<one_line_on_crash>, + HelpText<"Alias for --one-line-on-crash">, + Group<grp_command>; + +def source_on_crash: Separate<["--", "-"], "source-on-crash">, + MetaVarName<"<file>">, + HelpText<"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes.">, + Group<grp_command>; +def: Separate<["-"], "K">, + Alias<source_on_crash>, + HelpText<"Alias for --source-on-crash">, + Group<grp_command>; + +def source: Separate<["--", "-"], "source">, + MetaVarName<"<file>">, + HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, after any file has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "s">, + Alias<source>, + HelpText<"Alias for --source">, + Group<grp_command>; + +def source_before_file: Separate<["--", "-"], "source-before-file">, + MetaVarName<"<file>">, + HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, before any file has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "S">, + Alias<source_before_file>, + HelpText<"Alias for --source-before-file">, + Group<grp_command>; + +def one_line: Separate<["--", "-"], "one-line">, + MetaVarName<"<command>">, + HelpText<"Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "o">, + Alias<one_line>, + HelpText<"Alias for --one-line">, + Group<grp_command>; + +def one_line_before_file: Separate<["--", "-"], "one-line-before-file">, + MetaVarName<"<command>">, + HelpText<"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "O">, + Alias<one_line_before_file>, + HelpText<"Alias for --one-line-before-file">, + Group<grp_command>; + + +// General options. +def version: F<"version">, + HelpText<"Prints out the current version number of the LLDB debugger.">; +def: Flag<["-"], "v">, + Alias<version>, + HelpText<"Alias for --version">; + +def help: F<"help">, + HelpText<"Prints out the usage information for the LLDB debugger.">; +def: Flag<["-"], "h">, + Alias<help>, + HelpText<"Alias for --help">; + +def core: Separate<["--", "-"], "core">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to use the full path to <filename> as the core file.">; +def: Separate<["-"], "c">, + Alias<core>, + HelpText<"Alias for --core">; + +def editor: F<"editor">, + HelpText<"Tells the debugger to open source files using the host's \"external editor\" mechanism.">; +def: Flag<["-"], "e">, + Alias<editor>, + HelpText<"Alias for --editor">; + +def no_use_colors: F<"no-use-colors">, + HelpText<"Do not use colors.">; +def: Flag<["-"], "X">, + Alias<no_use_colors>, + HelpText<"Alias for --no-use-color">; + +def file: Separate<["--", "-"], "file">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to use the file <filename> as the program to be debugged.">; +def: Separate<["-"], "f">, + Alias<file>, + HelpText<"Alias for --file">; + +def arch: Separate<["--", "-"], "arch">, + MetaVarName<"<architecture>">, + HelpText<"Tells the debugger to use the specified architecture when starting and running the program.">; +def: Separate<["-"], "a">, + Alias<arch>, + HelpText<"Alias for --arch">; + +def debug: F<"debug">, + HelpText<"Tells the debugger to print out extra information for debugging itself.">; +def: Flag<["-"], "d">, + Alias<debug>, + HelpText<"Alias for --debug">; + +def capture: F<"capture">, + HelpText<"Tells the debugger to capture a reproducer.">; +def capture_path: Separate<["--", "-"], "capture-path">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to use the given filename for the reproducer.">; +def replay: Separate<["--", "-"], "replay">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to replay a reproducer from <filename>.">; +def skip_version_check: F<"reproducer-skip-version-check">, + HelpText<"Skip the reproducer version check.">; + +def REM : R<["--"], "">; diff --git a/gnu/llvm/lldb/tools/driver/Platform.cpp b/gnu/llvm/lldb/tools/driver/Platform.cpp new file mode 100644 index 00000000000..f3a71eb79de --- /dev/null +++ b/gnu/llvm/lldb/tools/driver/Platform.cpp @@ -0,0 +1,59 @@ +//===-- Platform.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// this file is only relevant for Visual C++ +#if defined(_WIN32) + +#include <assert.h> +#include <process.h> +#include <stdlib.h> + +#include "Platform.h" +#include "llvm/Support/ErrorHandling.h" + +int ioctl(int d, int request, ...) { + switch (request) { + // request the console windows size + case (TIOCGWINSZ): { + va_list vl; + va_start(vl, request); + // locate the window size structure on stack + winsize *ws = va_arg(vl, winsize *); + // get screen buffer information + CONSOLE_SCREEN_BUFFER_INFO info; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) == + TRUE) + // fill in the columns + ws->ws_col = info.dwMaximumWindowSize.X; + va_end(vl); + return 0; + } break; + default: + llvm_unreachable("Not implemented!"); + } +} + +int kill(pid_t pid, int sig) { + // is the app trying to kill itself + if (pid == getpid()) + exit(sig); + // + llvm_unreachable("Not implemented!"); +} + +int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { + llvm_unreachable("Not implemented!"); +} + +int tcgetattr(int fildes, struct termios *termios_p) { + // assert( !"Not implemented!" ); + // error return value (0=success) + return -1; +} + +#endif diff --git a/gnu/llvm/lldb/tools/driver/Platform.h b/gnu/llvm/lldb/tools/driver/Platform.h new file mode 100644 index 00000000000..cf6c4ec8e14 --- /dev/null +++ b/gnu/llvm/lldb/tools/driver/Platform.h @@ -0,0 +1,90 @@ +//===-- Platform.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Platform_h_ +#define lldb_Platform_h_ + +#include "lldb/Host/Config.h" + +#if defined(_WIN32) + +#include <io.h> +#if defined(_MSC_VER) +#include <signal.h> +#endif +#if HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include "lldb/Host/windows/windows.h" +#include <inttypes.h> + +struct winsize { + long ws_col; +}; + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +// fcntl.h +#define O_NOCTTY 0400 + +// ioctls.h +#define TIOCGWINSZ 0x5413 + +// signal.h +#define SIGPIPE 13 +#define SIGCONT 18 +#define SIGTSTP 20 +#define SIGWINCH 28 + +// tcsetattr arguments +#define TCSANOW 0 + +#define NCCS 32 +struct termios { + tcflag_t c_iflag; // input mode flags + tcflag_t c_oflag; // output mode flags + tcflag_t c_cflag; // control mode flags + tcflag_t c_lflag; // local mode flags + cc_t c_line; // line discipline + cc_t c_cc[NCCS]; // control characters + speed_t c_ispeed; // input speed + speed_t c_ospeed; // output speed +}; + +#ifdef _MSC_VER +struct timeval { + long tv_sec; + long tv_usec; +}; +typedef long pid_t; +#define PATH_MAX MAX_PATH +#endif + +#define STDIN_FILENO 0 + +extern int ioctl(int d, int request, ...); +extern int kill(pid_t pid, int sig); +extern int tcsetattr(int fd, int optional_actions, + const struct termios *termios_p); +extern int tcgetattr(int fildes, struct termios *termios_p); + +#else +#include <inttypes.h> + +#include <libgen.h> +#include <sys/ioctl.h> +#include <termios.h> +#include <unistd.h> + +#include <pthread.h> +#include <sys/time.h> +#endif + +#endif // lldb_Platform_h_ diff --git a/gnu/llvm/lldb/tools/driver/lldb-Info.plist.in b/gnu/llvm/lldb/tools/driver/lldb-Info.plist.in new file mode 100644 index 00000000000..a875129ef29 --- /dev/null +++ b/gnu/llvm/lldb/tools/driver/lldb-Info.plist.in @@ -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>${LLDB_VERSION}</string> + <key>SecTaskAccess</key> + <array> + <string>allowed</string> + <string>debug</string> + </array> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/intel-features/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/CMakeLists.txt new file mode 100644 index 00000000000..aff75d7db33 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/CMakeLists.txt @@ -0,0 +1,67 @@ +# Flags to control each individual feature +option(LLDB_BUILD_INTEL_MPX "Enable Building of Intel(R) Memory Protection Extensions" ON) +option(LLDB_BUILD_INTEL_PT "Enable Building of Intel(R) Processor Trace Tool" OFF) + +# Return if all features are OFF +if (NOT LLDB_BUILD_INTEL_MPX AND NOT LLDB_BUILD_INTEL_PT) + return() +endif() + +LIST (APPEND FEATURE_LIBS "") + +# Add feature specific subdirectories based on flags +if (LLDB_BUILD_INTEL_MPX AND CMAKE_SYSTEM_NAME MATCHES "Linux") + add_subdirectory(intel-mpx) + LIST (APPEND FEATURE_LIBS ${FEATURE_LIBS} lldbIntelMPX) + SET (CLI_WRAPPER_PREPROCESSORS "${CLI_WRAPPER_PREPROCESSORS} -DBUILD_INTEL_MPX") +endif() + +if (LLDB_BUILD_INTEL_PT) + add_subdirectory(intel-pt) + LIST (APPEND FEATURE_LIBS ${FEATURE_LIBS} lldbIntelPT) + SET (CLI_WRAPPER_PREPROCESSORS "${CLI_WRAPPER_PREPROCESSORS} -DBUILD_INTEL_PT") +endif() + +# Add python wrapper if python not disabled +if (LLDB_ENABLE_PYTHON AND LLDB_BUILD_INTEL_PT) + set(LLDB_INTEL_FEATURES_PYTHON_WRAP + ${LLDB_BINARY_DIR}/tools/intel-features/scripts/IntelFeaturesPythonWrap.cpp) + set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP} + PROPERTIES GENERATED 1) + + if (CLANG_CL) + set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP} + PROPERTIES COMPILE_FLAGS -Wno-unused-function) + endif() + + if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND + NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + set_property(SOURCE ${LLDB_INTEL_FEATURES_PYTHON_WRAP} + APPEND_STRING PROPERTY COMPILE_FLAGS + " -Wno-sequence-point -Wno-cast-qual") + endif () + add_subdirectory(scripts) +endif() + +if (NOT CLI_WRAPPER_PREPROCESSORS) + return() +endif() + +set_source_files_properties(cli-wrapper.cpp PROPERTIES + COMPILE_FLAGS ${CLI_WRAPPER_PREPROCESSORS}) + +add_lldb_library(lldbIntelFeatures SHARED + cli-wrapper.cpp + ${LLDB_INTEL_FEATURES_PYTHON_WRAP} + + LINK_LIBS + ${FEATURE_LIBS} + ) + +# Add link dependencies for python wrapper +if (LLDB_ENABLE_PYTHON AND LLDB_BUILD_INTEL_PT) + add_dependencies(lldbIntelFeatures intel-features-swig_wrapper) +endif() + +install(TARGETS lldbIntelFeatures + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}) diff --git a/gnu/llvm/lldb/tools/intel-features/README.txt b/gnu/llvm/lldb/tools/intel-features/README.txt new file mode 100644 index 00000000000..d46bf1392dc --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/README.txt @@ -0,0 +1,73 @@ +**************************************************************************** +* README * +* * +* This file provides all the information regarding new CLI commands that * +* enable using various hardware features of Intel(R) architecture based * +* processors from LLDB's CLI. * +**************************************************************************** + + +============ +Introduction +============ +A shared library has been developed to use various hardware features of +Intel(R) architecture based processors through LLDB's command line. The library +currently comprises of hardware features namely Intel(R) Processor Trace and +Intel(R) Memory Protection Extensions. + + +============ +Details +============ +A C++ based cli wrapper (cli-wrapper.cpp) has been developed here that +agglomerates all cli commands for various hardware features. This wrapper is +build to generate a shared library (lldbIntelFeatures) to provide all these +commands. + +For each hardware feature, separate cli commands have been developed that are +provided by wrappers (cli-wrapper-pt.cpp and cli-wrapper-mpxtable.cpp) residing +in feature specific folders ("intel-pt" and "intel-mpx" respectively). + +For details regarding cli commands of each feature, please refer to these +feature specific wrappers. + + + +============ +How to Build +============ +The shared library (lldbIntelFeatures) has a cmake based build and can be built +while building LLDB with cmake. "cli-wrapper.cpp" file is compiled along with all +the feature specific source files (residing in feature specific folders). + +Furthermore, flexibility is provided to the user to include/exclude a particular +feature while building lldbIntelFeatures library. This is done by flags described +below: + + - LLDB_BUILD_INTEL_PT - The flag enables building of Intel(R) Processor Trace + feature (inside intel-pt folder). This flag defaults to "OFF" meaning the + feature is excluded while building lldbIntelFeatures library. Set it to "ON" + in order to include it. + + - LLDB_BUILD_INTEL_MPX - Enables building Intel(R) Memory Protection Extensions + feature (inside intel-mpx folder). This flag defaults to "ON" meaning + the feature is excluded while building lldbIntelFeatures library. + +Please refer to README files in feature specific folders to know about additional +flags that need to be set in order to build that feature successfully. + + +============ +How to Use +============ +All CLI commands provided by this shared library can be used through the LLDB's +CLI by executing "plugin load <shared_lib_name>" on LLDB CLI. shared_lib_name here +is lldbIntelFeatures + + + +============ +Description +============ +Please refer to README_CLI file of each feature to know about details of CLI +commands. diff --git a/gnu/llvm/lldb/tools/intel-features/cli-wrapper.cpp b/gnu/llvm/lldb/tools/intel-features/cli-wrapper.cpp new file mode 100644 index 00000000000..f04e39a3be9 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/cli-wrapper.cpp @@ -0,0 +1,42 @@ +//===-- cli-wrapper.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// CLI Wrapper for hardware features of Intel(R) architecture based processors +// to enable them to be used through LLDB's CLI. For details, please refer to +// cli wrappers of each individual feature, residing in their respective +// folders. +// +// Compile this into a shared lib and load by placing at appropriate locations +// on disk or by using "plugin load" command at the LLDB command line. +// +//===----------------------------------------------------------------------===// + +#ifdef BUILD_INTEL_MPX +#include "intel-mpx/cli-wrapper-mpxtable.h" +#endif + +#ifdef BUILD_INTEL_PT +#include "intel-pt/cli-wrapper-pt.h" +#endif + +#include "lldb/API/SBDebugger.h" + +namespace lldb { +bool PluginInitialize(lldb::SBDebugger debugger); +} + +bool lldb::PluginInitialize(lldb::SBDebugger debugger) { + +#ifdef BUILD_INTEL_PT + PTPluginInitialize(debugger); +#endif + +#ifdef BUILD_INTEL_MPX + MPXPluginInitialize(debugger); +#endif + + return true; +} diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/intel-mpx/CMakeLists.txt new file mode 100644 index 00000000000..319f712b82b --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbIntelMPX + cli-wrapper-mpxtable.cpp + + LINK_LIBS + liblldb + + LINK_COMPONENTS + Support + ) diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp new file mode 100644 index 00000000000..5bffd27409e --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp @@ -0,0 +1,422 @@ +//===-- cli-wrapper-mpxtable.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// C++ includes +#include <cerrno> +#include <string> + +#include "cli-wrapper-mpxtable.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBMemoryRegionInfo.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" + +#include "llvm/ADT/Triple.h" + +static bool GetPtr(char *cptr, uint64_t &ptr, lldb::SBFrame &frame, + lldb::SBCommandReturnObject &result) { + if (!cptr) { + result.SetError("Bad argument."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBValue ptr_addr = frame.GetValueForVariablePath(cptr); + if (!ptr_addr.IsValid()) { + result.SetError("Invalid pointer."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + ptr = ptr_addr.GetLoadAddress(); + return true; +} + +enum { + mpx_base_mask_64 = ~(uint64_t)0xFFFULL, + mpx_bd_mask_64 = 0xFFFFFFF00000ULL, + bd_r_shift_64 = 20, + bd_l_shift_64 = 3, + bt_r_shift_64 = 3, + bt_l_shift_64 = 5, + bt_mask_64 = 0x0000000FFFF8ULL, + + mpx_base_mask_32 = 0xFFFFFFFFFFFFF000ULL, + mpx_bd_mask_32 = 0xFFFFF000ULL, + bd_r_shift_32 = 12, + bd_l_shift_32 = 2, + bt_r_shift_32 = 2, + bt_l_shift_32 = 4, + bt_mask_32 = 0x00000FFCULL, +}; + +static void PrintBTEntry(lldb::addr_t lbound, lldb::addr_t ubound, + uint64_t value, uint64_t meta, + lldb::SBCommandReturnObject &result) { + const lldb::addr_t one_cmpl64 = ~((lldb::addr_t)0); + const lldb::addr_t one_cmpl32 = ~((uint32_t)0); + + if ((lbound == one_cmpl64 || one_cmpl32) && ubound == 0) { + result.Printf("Null bounds on map: pointer value = 0x%lx\n", value); + } else { + result.Printf(" lbound = 0x%lx,", lbound); + result.Printf(" ubound = 0x%lx", ubound); + result.Printf(" (pointer value = 0x%lx,", value); + result.Printf(" metadata = 0x%lx)\n", meta); + } +} + +static bool GetBTEntryAddr(uint64_t bndcfgu, uint64_t ptr, + lldb::SBTarget &target, llvm::Triple::ArchType arch, + size_t &size, lldb::addr_t &bt_entry_addr, + lldb::SBCommandReturnObject &result, + lldb::SBError &error) { + lldb::addr_t mpx_base_mask; + lldb::addr_t mpx_bd_mask; + lldb::addr_t bd_r_shift; + lldb::addr_t bd_l_shift; + lldb::addr_t bt_r_shift; + lldb::addr_t bt_l_shift; + lldb::addr_t bt_mask; + + if (arch == llvm::Triple::ArchType::x86_64) { + mpx_base_mask = mpx_base_mask_64; + mpx_bd_mask = mpx_bd_mask_64; + bd_r_shift = bd_r_shift_64; + bd_l_shift = bd_l_shift_64; + bt_r_shift = bt_r_shift_64; + bt_l_shift = bt_l_shift_64; + bt_mask = bt_mask_64; + } else if (arch == llvm::Triple::ArchType::x86) { + mpx_base_mask = mpx_base_mask_32; + mpx_bd_mask = mpx_bd_mask_32; + bd_r_shift = bd_r_shift_32; + bd_l_shift = bd_l_shift_32; + bt_r_shift = bt_r_shift_32; + bt_l_shift = bt_l_shift_32; + bt_mask = bt_mask_32; + } else { + result.SetError("Invalid arch."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + size = target.GetAddressByteSize(); + lldb::addr_t mpx_bd_base = bndcfgu & mpx_base_mask; + lldb::addr_t bd_entry_offset = ((ptr & mpx_bd_mask) >> bd_r_shift) + << bd_l_shift; + lldb::addr_t bd_entry_addr = mpx_bd_base + bd_entry_offset; + + std::vector<uint8_t> bd_entry_v(size); + size_t ret = target.GetProcess().ReadMemory( + bd_entry_addr, static_cast<void *>(bd_entry_v.data()), size, error); + if (ret != size || !error.Success()) { + result.SetError("Failed access to BD entry."); + return false; + } + + lldb::SBData data; + data.SetData(error, bd_entry_v.data(), bd_entry_v.size(), + target.GetByteOrder(), size); + lldb::addr_t bd_entry = data.GetAddress(error, 0); + + if (!error.Success()) { + result.SetError("Failed access to BD entry."); + return false; + } + + if ((bd_entry & 0x01) == 0) { + result.SetError("Invalid bound directory."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + // Clear status bit. + // + bd_entry--; + + lldb::addr_t bt_addr = bd_entry & ~bt_r_shift; + lldb::addr_t bt_entry_offset = ((ptr & bt_mask) >> bt_r_shift) << bt_l_shift; + bt_entry_addr = bt_addr + bt_entry_offset; + + return true; +} + +static bool GetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::SBTarget &target, + llvm::Triple::ArchType arch, + lldb::SBCommandReturnObject &result, + lldb::SBError &error) { + lldb::addr_t bt_entry_addr; + size_t size; + if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result, + error)) + return false; + + // bt_entry_v must have space to store the 4 elements of the BT entry (lower + // boundary, + // upper boundary, pointer value and meta data), which all have the same size + // 'size'. + // + std::vector<uint8_t> bt_entry_v(size * 4); + size_t ret = target.GetProcess().ReadMemory( + bt_entry_addr, static_cast<void *>(bt_entry_v.data()), size * 4, error); + + if ((ret != (size * 4)) || !error.Success()) { + result.SetError("Unsuccessful. Failed access to BT entry."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::addr_t lbound; + lldb::addr_t ubound; + uint64_t value; + uint64_t meta; + lldb::SBData data; + data.SetData(error, bt_entry_v.data(), bt_entry_v.size(), + target.GetByteOrder(), size); + lbound = data.GetAddress(error, size * 0); + ubound = data.GetAddress(error, size * 1); + value = data.GetAddress(error, size * 2); + meta = data.GetAddress(error, size * 3); + // ubound is stored as one's complement. + if (arch == llvm::Triple::ArchType::x86) { + ubound = (~ubound) & 0x00000000FFFFFFFF; + } else { + ubound = ~ubound; + } + + if (!error.Success()) { + result.SetError("Failed access to BT entry."); + return false; + } + + PrintBTEntry(lbound, ubound, value, meta, result); + + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + return true; +} + +static std::vector<uint8_t> uIntToU8(uint64_t input, size_t size) { + std::vector<uint8_t> output; + for (size_t i = 0; i < size; i++) + output.push_back( + static_cast<uint8_t>((input & (0xFFULL << (i * 8))) >> (i * 8))); + + return output; +} + +static bool SetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::addr_t lbound, + lldb::addr_t ubound, lldb::SBTarget &target, + llvm::Triple::ArchType arch, + lldb::SBCommandReturnObject &result, + lldb::SBError &error) { + lldb::addr_t bt_entry_addr; + size_t size; + + if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result, + error)) + return false; + + // bt_entry_v must have space to store only 2 elements of the BT Entry, the + // lower boundary and the upper boundary, which both have size 'size'. + // + std::vector<uint8_t> bt_entry_v(size * 2); + + std::vector<uint8_t> lbound_v = uIntToU8(lbound, size); + bt_entry_v.insert(bt_entry_v.begin(), lbound_v.begin(), lbound_v.end()); + std::vector<uint8_t> ubound_v = uIntToU8(~ubound, size); + bt_entry_v.insert(bt_entry_v.begin() + size, ubound_v.begin(), + ubound_v.end()); + + size_t ret = target.GetProcess().WriteMemory( + bt_entry_addr, (void *)(bt_entry_v.data()), size * 2, error); + if ((ret != (size * 2)) || !error.Success()) { + result.SetError("Failed access to BT entry."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + return true; +} + +static bool GetInitInfo(lldb::SBDebugger debugger, lldb::SBTarget &target, + llvm::Triple::ArchType &arch, uint64_t &bndcfgu, + char *arg, uint64_t &ptr, + lldb::SBCommandReturnObject &result, + lldb::SBError &error) { + target = debugger.GetSelectedTarget(); + if (!target.IsValid()) { + result.SetError("Invalid target."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + const std::string triple_s(target.GetTriple()); + const llvm::Triple triple(triple_s); + + arch = triple.getArch(); + + if ((arch != llvm::Triple::ArchType::x86) && + (arch != llvm::Triple::ArchType::x86_64)) { + result.SetError("Platform not supported."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBFrame frame = + target.GetProcess().GetSelectedThread().GetSelectedFrame(); + if (!frame.IsValid()) { + result.SetError("No valid process, thread or frame."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBValue bndcfgu_val = frame.FindRegister("bndcfgu"); + if (!bndcfgu_val.IsValid()) { + result.SetError("Cannot access register BNDCFGU. Does the target support " + "Intel(R) Memory Protection Extensions (Intel(R) MPX)?"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBData bndcfgu_data = bndcfgu_val.GetData(); + bndcfgu = bndcfgu_data.GetUnsignedInt64(error, 0); + if (!error.Success()) { + result.SetError(error, "Invalid read of register BNDCFGU."); + return false; + } + + if (!GetPtr(arg, ptr, frame, result)) + return false; + + return true; +} + +class MPXTableShow : public lldb::SBCommandPluginInterface { +public: + bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) override { + + if (command) { + int arg_c = 0; + char *arg; + + while (*command) { + if (arg_c >= 1) { + result.SetError("Too many arguments. See help."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + arg_c++; + arg = *command; + command++; + } + + if (!debugger.IsValid()) { + result.SetError("Invalid debugger."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBTarget target; + llvm::Triple::ArchType arch; + lldb::SBError error; + uint64_t bndcfgu; + uint64_t ptr; + + if (!GetInitInfo(debugger, target, arch, bndcfgu, arg, ptr, result, + error)) + return false; + + return GetBTEntry(bndcfgu, ptr, target, arch, result, error); + } + + result.SetError("Too few arguments. See help."); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } +}; + +class MPXTableSet : public lldb::SBCommandPluginInterface { +public: + bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) override { + + if (command) { + int arg_c = 0; + char *arg[3]; + + while (*command) { + arg[arg_c] = *command; + command++; + arg_c++; + } + + if (arg_c != 3) { + result.SetError("Wrong arguments. See help."); + return false; + } + + if (!debugger.IsValid()) { + result.SetError("Invalid debugger."); + return false; + } + + lldb::SBTarget target; + llvm::Triple::ArchType arch; + lldb::SBError error; + uint64_t bndcfgu; + uint64_t ptr; + + if (!GetInitInfo(debugger, target, arch, bndcfgu, arg[0], ptr, result, + error)) + return false; + + char *endptr; + errno = 0; + uint64_t lbound = std::strtoul(arg[1], &endptr, 16); + if (endptr == arg[1] || errno == ERANGE) { + result.SetError("Lower Bound: bad argument format."); + errno = 0; + return false; + } + + uint64_t ubound = std::strtoul(arg[2], &endptr, 16); + if (endptr == arg[1] || errno == ERANGE) { + result.SetError("Upper Bound: bad argument format."); + errno = 0; + return false; + } + + return SetBTEntry(bndcfgu, ptr, lbound, ubound, target, arch, result, + error); + } + + result.SetError("Too few arguments. See help."); + return false; + } +}; + +bool MPXPluginInitialize(lldb::SBDebugger &debugger) { + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + lldb::SBCommand mpxTable = interpreter.AddMultiwordCommand( + "mpx-table", "A utility to access the Intel(R) MPX table entries."); + + const char *mpx_show_help = "Show the Intel(R) MPX table entry of a pointer." + "\nmpx-table show <pointer>"; + mpxTable.AddCommand("show", new MPXTableShow(), mpx_show_help); + + const char *mpx_set_help = + "Set the Intel(R) MPX table entry of a pointer.\n" + "mpx-table set <pointer> <lower bound> <upper bound>"; + mpxTable.AddCommand("set", new MPXTableSet(), mpx_set_help); + + return true; +} diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h new file mode 100644 index 00000000000..335c673fef8 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h @@ -0,0 +1,11 @@ +//===-- cli-wrapper-mpxtable.h----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBDebugger.h" + +bool MPXPluginInitialize(lldb::SBDebugger &debugger); diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/Makefile b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/Makefile new file mode 100644 index 00000000000..b18044407a7 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../make + +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS += -mmpx -fcheck-pointer-bounds -lmpxwrappers -lmpx -fuse-ld=bfd + +include $(LEVEL)/Makefile.rules diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/README.txt b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/README.txt new file mode 100644 index 00000000000..6797eff30a7 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/README.txt @@ -0,0 +1,6 @@ +In order to run this test, create the following directory: + + packages/Python/lldbsuite/test/functionalities/plugins/commands/mpxtablecmd + +and copy into it the contents of this directory. + diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/TestMPXTable.py b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/TestMPXTable.py new file mode 100644 index 00000000000..f571252e26f --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/TestMPXTable.py @@ -0,0 +1,169 @@ +""" +Test mpx-table command. +""" + +from __future__ import print_function + + +import os +import time +import re +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestMPXTable(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + + @skipIf(compiler="clang") + @skipIf(oslist=no_match(['linux'])) + @skipIf(archs=no_match(['i386', 'x86_64'])) + @skipIf(compiler="gcc", compiler_version=["<", "5"]) #GCC version >= 5 supports + #Intel(R) Memory Protection Extensions (Intel(R) MPX). + def test_show_command(self): + """Test 'mpx-table show' command""" + self.build() + + lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"] + lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib") + plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so") + if not os.path.isfile(plugin_file): + self.skipTest("features plugin missing.") + plugin_command = " " + seq = ("plugin", "load", plugin_file) + plugin_command = plugin_command.join(seq) + self.runCmd(plugin_command) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.b1 = line_number('main.cpp', '// Break 1.') + self.b2 = line_number('main.cpp', '// Break 2.') + self.b3 = line_number('main.cpp', '// Break 3.') + self.b4 = line_number('main.cpp', '// Break 4.') + lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b2, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b3, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b4, num_expected_locations=1) + self.runCmd("run", RUN_SUCCEEDED) + + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + if (process.GetState() == lldb.eStateExited): + self.skipTest("Intel(R) MPX is not supported.") + else: + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + self.expect("mpx-table show a", + substrs = ['lbound = 0x', + ', ubound = 0x', + '(pointer value = 0x', + ', metadata = 0x', + ')'], + error = False) + + self.expect("continue", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 2."]) + + # Check that out of scope pointer cannot be reached. + # + self.expect("mpx-table show a", + substrs = ['Invalid pointer.'], + error = True) + + self.expect("mpx-table show tmp", + substrs = ['lbound = 0x', + ', ubound = 0x', + '(pointer value = 0x', + ', metadata = 0x', + ')'], + error = False) + + self.expect("continue", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 3."]) + + # Check that the pointer value is correctly updated. + # + self.expect("mpx-table show tmp", + substrs = ['lbound = 0x', + ', ubound = 0x', + '(pointer value = 0x2', + ', metadata = 0x', + ')'], + error = False) + + self.expect("continue", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 4."]) + + # After going back to main(), check that out of scope pointer cannot be + # reached. + # + self.expect("mpx-table show tmp", + substrs = ['Invalid pointer.'], + error = True) + + self.expect("mpx-table show a", + substrs = ['lbound = 0x', + ', ubound = 0x', + '(pointer value = 0x', + ', metadata = 0x', + ')'], + error = False) + + def test_set_command(self): + """Test 'mpx-table set' command""" + self.build() + + lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"] + lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib") + plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so") + if not os.path.isfile(plugin_file): + self.skipTest("features plugin missing.") + plugin_command = " " + seq = ("plugin", "load", plugin_file) + plugin_command = plugin_command.join(seq) + self.runCmd(plugin_command) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.b1 = line_number('main.cpp', '// Break 1.') + lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b1, num_expected_locations=1) + self.runCmd("run", RUN_SUCCEEDED) + + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + if (process.GetState() == lldb.eStateExited): + self.skipTest("Intel(R) MPX is not supported.") + else: + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Check that the BT Entry doesn't already contain the test values. + # + self.expect("mpx-table show a", matching=False, + substrs = ['lbound = 0xcafecafe', + ', ubound = 0xbeefbeef']) + + # Set the test values. + # + self.expect("mpx-table set a 0xcafecafe 0xbeefbeef", error = False) + + # Verify that the test values have been correctly written in the BT + # entry. + # + self.expect("mpx-table show a", + substrs = ['lbound = 0xcafecafe', + ', ubound = 0xbeefbeef'], + error = False) + + diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/main.cpp b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/main.cpp new file mode 100644 index 00000000000..2f5253ed860 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/main.cpp @@ -0,0 +1,48 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +//// +//// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +//// See https://llvm.org/LICENSE.txt for license information. +//// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//// +////===----------------------------------------------------------------------===// +// + +const int size = 5; + +#include <cstddef> +#include <cstdlib> +#include <sys/prctl.h> + +void func(int *ptr) { + int *tmp; + +#if defined __GNUC__ && !defined __INTEL_COMPILER + __builtin___bnd_store_ptr_bounds ((void**)&ptr, ptr); +#endif + tmp = ptr + size - 1; +#if defined __GNUC__ && !defined __INTEL_COMPILER + __builtin___bnd_store_ptr_bounds ((void**)&tmp, tmp); +#endif + tmp = (int*)0x2; // Break 2. + + return; // Break 3. +} + +int +main(int argc, char const *argv[]) +{ + // This call returns 0 only if the CPU and the kernel support + // Intel(R) Memory Protection Extensions (Intel(R) MPX). + if (prctl(PR_MPX_ENABLE_MANAGEMENT, 0, 0, 0, 0) != 0) + return -1; + + int* a = (int *) calloc(size, sizeof(int)); +#if defined __GNUC__ && !defined __INTEL_COMPILER + __builtin___bnd_store_ptr_bounds ((void**)&a, a); +#endif + func(a); // Break 1. + + free(a); // Break 4. + + return 0; +} diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/intel-pt/CMakeLists.txt new file mode 100644 index 00000000000..9750ed83ce2 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/CMakeLists.txt @@ -0,0 +1,31 @@ +if (NOT LIBIPT_INCLUDE_PATH) + message (FATAL_ERROR "libipt include path not provided") +endif() + +if (NOT EXISTS "${LIBIPT_INCLUDE_PATH}") + message (FATAL_ERROR "invalid libipt include path provided") +endif() +include_directories(${LIBIPT_INCLUDE_PATH}) + +if (NOT LIBIPT_LIBRARY_PATH) + find_library(LIBIPT_LIBRARY ipt) +else() + if (NOT EXISTS "${LIBIPT_LIBRARY_PATH}") + message (FATAL_ERROR "invalid libipt library path provided") + endif() + find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH}) +endif() + +if (NOT LIBIPT_LIBRARY) + message (FATAL_ERROR "libipt library not found") +endif() + +add_lldb_library(lldbIntelPT + PTDecoder.cpp + Decoder.cpp + cli-wrapper-pt.cpp + + LINK_LIBS + ${LIBIPT_LIBRARY} + liblldb + ) diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.cpp b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.cpp new file mode 100644 index 00000000000..2ce5c985c54 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.cpp @@ -0,0 +1,901 @@ +//===-- Decoder.cpp ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Decoder.h" + +// C/C++ Includes +#include <cinttypes> +#include <cstring> + +#include "lldb/API/SBModule.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThread.h" + +using namespace ptdecoder_private; + +// This function removes entries of all the processes/threads which were once +// registered in the class but are not alive anymore because they died or +// finished executing. +void Decoder::RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess) { + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + lldb::SBDebugger sbdebugger = sbtarget.GetDebugger(); + uint32_t num_targets = sbdebugger.GetNumTargets(); + + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.begin(); + while (itr_process != m_mapProcessUID_mapThreadID_TraceInfo.end()) { + bool process_found = false; + lldb::SBTarget target; + lldb::SBProcess process; + for (uint32_t i = 0; i < num_targets; i++) { + target = sbdebugger.GetTargetAtIndex(i); + process = target.GetProcess(); + if (process.GetUniqueID() == itr_process->first) { + process_found = true; + break; + } + } + + // Remove the process's entry if it was not found in SBDebugger + if (!process_found) { + itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + continue; + } + + // If the state of the process is exited or detached then remove process's + // entry. If not then remove entry for all those registered threads of this + // process that are not alive anymore. + lldb::StateType state = process.GetState(); + if ((state == lldb::StateType::eStateDetached) || + (state == lldb::StateType::eStateExited)) + itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + else { + auto itr_thread = itr_process->second.begin(); + while (itr_thread != itr_process->second.end()) { + if (itr_thread->first == LLDB_INVALID_THREAD_ID) { + ++itr_thread; + continue; + } + + lldb::SBThread thread = process.GetThreadByID(itr_thread->first); + if (!thread.IsValid()) + itr_thread = itr_process->second.erase(itr_thread); + else + ++itr_thread; + } + ++itr_process; + } + } +} + +void Decoder::StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) + return; + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + if (sbtraceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("SBTraceOptions::TraceType not set to " + "eTraceTypeProcessorTrace; ProcessID = " + "%" PRIu64, + sbprocess.GetProcessID()); + return; + } + lldb::SBStructuredData sbstructdata = sbtraceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + + const char *trace_tech_key = "trace-tech"; + std::string trace_tech_value("intel-pt"); + lldb::SBStructuredData value = sbstructdata.GetValueForKey(trace_tech_key); + if (!value.IsValid()) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", trace_tech_key); + return; + } + + char string_value[9]; + size_t bytes_written = value.GetStringValue( + string_value, sizeof(string_value) / sizeof(*string_value)); + if (!bytes_written || + (bytes_written > (sizeof(string_value) / sizeof(*string_value)))) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", trace_tech_key); + return; + } + + std::size_t pos = + trace_tech_value.find((const char *)string_value, 0, bytes_written); + if ((pos == std::string::npos)) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set to \"%s\" in custom trace parameters", + trace_tech_key, trace_tech_value.c_str()); + return; + } + + // Start Tracing + lldb::SBError error; + uint32_t unique_id = sbprocess.GetUniqueID(); + lldb::tid_t tid = sbtraceoptions.getThreadID(); + lldb::SBTrace trace = sbprocess.StartTrace(sbtraceoptions, error); + if (!error.Success()) { + if (tid == LLDB_INVALID_THREAD_ID) + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error.GetCString(), + sbprocess.GetProcessID()); + else + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error.GetCString(), tid, sbprocess.GetProcessID()); + return; + } + + MapThreadID_TraceInfo &mapThreadID_TraceInfo = + m_mapProcessUID_mapThreadID_TraceInfo[unique_id]; + ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid]; + trace_info.SetUniqueTraceInstance(trace); + trace_info.SetStopID(sbprocess.GetStopID()); +} + +void Decoder::StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, lldb::tid_t tid) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this process; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBError error; + if (tid == LLDB_INVALID_THREAD_ID) { + // This implies to stop tracing on the whole process + lldb::user_id_t id_to_be_ignored = LLDB_INVALID_UID; + auto itr_thread = itr_process->second.begin(); + while (itr_thread != itr_process->second.end()) { + // In the case when user started trace on the entire process and then + // registered newly spawned threads of this process in the class later, + // these newly spawned threads will have same trace id. If we stopped + // trace on the entire process then tracing stops automatically for these + // newly spawned registered threads. Stopping trace on them again will + // return error and therefore we need to skip stopping trace on them + // again. + lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance(); + lldb::user_id_t lldb_pt_user_id = trace.GetTraceUID(); + if (lldb_pt_user_id != id_to_be_ignored) { + trace.StopTrace(error, itr_thread->first); + if (!error.Success()) { + std::string error_string(error.GetCString()); + if ((error_string.find("tracing not active for this process") == + std::string::npos) && + (error_string.find("tracing not active for this thread") == + std::string::npos)) { + sberror.SetErrorStringWithFormat( + "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), itr_thread->first, + sbprocess.GetProcessID()); + return; + } + } + + if (itr_thread->first == LLDB_INVALID_THREAD_ID) + id_to_be_ignored = lldb_pt_user_id; + } + itr_thread = itr_process->second.erase(itr_thread); + } + m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + } else { + // This implies to stop tracing on a single thread. + // if 'tid' is registered in the class then get the trace id and stop trace + // on it. If it is not then check if tracing was ever started on the entire + // process (because there is a possibility that trace is still running for + // 'tid' but it was not registered in the class because user had started + // trace on the whole process and 'tid' spawned later). In that case, get + // the trace id of the process trace instance and stop trace on this thread. + // If tracing was never started on the entire process then return error + // because there is no way tracing is active on 'tid'. + MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second; + lldb::SBTrace trace; + auto itr = mapThreadID_TraceInfo.find(tid); + if (itr != mapThreadID_TraceInfo.end()) { + trace = itr->second.GetUniqueTraceInstance(); + } else { + auto itr = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID); + if (itr != mapThreadID_TraceInfo.end()) { + trace = itr->second.GetUniqueTraceInstance(); + } else { + sberror.SetErrorStringWithFormat( + "tracing not active for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + } + + // Stop Tracing + trace.StopTrace(error, tid); + if (!error.Success()) { + std::string error_string(error.GetCString()); + sberror.SetErrorStringWithFormat( + "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), tid, sbprocess.GetProcessID()); + if (error_string.find("tracing not active") == std::string::npos) + return; + } + // Delete the entry of 'tid' from this class (if any) + mapThreadID_TraceInfo.erase(tid); + } +} + +void Decoder::ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, + lldb::tid_t tid, lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo) { + // Allocate trace data buffer and parse cpu info for 'tid' if it is registered + // for the first time in class + lldb::SBTrace &trace = threadTraceInfo.GetUniqueTraceInstance(); + Buffer &pt_buffer = threadTraceInfo.GetPTBuffer(); + lldb::SBError error; + if (pt_buffer.size() == 0) { + lldb::SBTraceOptions traceoptions; + traceoptions.setThreadID(tid); + trace.GetTraceConfig(traceoptions, error); + if (!error.Success()) { + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error.GetCString(), + sbprocess.GetProcessID()); + return; + } + if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB " + "for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + + threadTraceInfo.AllocatePTBuffer(traceoptions.getTraceBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo(); + ParseCPUInfo(pt_cpu, sbstructdata, sberror); + if (!sberror.Success()) + return; + } + + // Call LLDB API to get raw trace data for this thread + size_t bytes_written = trace.GetTraceData(error, (void *)pt_buffer.data(), + pt_buffer.size(), 0, tid); + if (!error.Success()) { + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error.GetCString(), tid, sbprocess.GetProcessID()); + return; + } + std::fill(pt_buffer.begin() + bytes_written, pt_buffer.end(), 0); + + // Get information of all the modules of the inferior + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + ReadExecuteSectionInfos &readExecuteSectionInfos = + threadTraceInfo.GetReadExecuteSectionInfos(); + GetTargetModulesInfo(sbtarget, readExecuteSectionInfos, sberror); + if (!sberror.Success()) + return; +} + +void Decoder::DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo) { + // Initialize instruction decoder + struct pt_insn_decoder *decoder = nullptr; + struct pt_config config; + Buffer &pt_buffer = threadTraceInfo.GetPTBuffer(); + CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo(); + ReadExecuteSectionInfos &readExecuteSectionInfos = + threadTraceInfo.GetReadExecuteSectionInfos(); + + InitializePTInstDecoder(&decoder, &config, pt_cpu, pt_buffer, + readExecuteSectionInfos, sberror); + if (!sberror.Success()) + return; + + // Start raw trace decoding + Instructions &instruction_list = threadTraceInfo.GetInstructionLog(); + instruction_list.clear(); + DecodeTrace(decoder, instruction_list, sberror); +} + +// Raw trace decoding requires information of Read & Execute sections of each +// module of the inferior. This function updates internal state of the class to +// store this information. +void Decoder::GetTargetModulesInfo( + lldb::SBTarget &sbtarget, ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) { + if (!sbtarget.IsValid()) { + sberror.SetErrorStringWithFormat("Can't get target's modules info from " + "LLDB; process has an invalid target"); + return; + } + + lldb::SBFileSpec target_file_spec = sbtarget.GetExecutable(); + if (!target_file_spec.IsValid()) { + sberror.SetErrorStringWithFormat("Target has an invalid file spec"); + return; + } + + uint32_t num_modules = sbtarget.GetNumModules(); + readExecuteSectionInfos.clear(); + + // Store information of all RX sections of each module of inferior + for (uint32_t i = 0; i < num_modules; i++) { + lldb::SBModule module = sbtarget.GetModuleAtIndex(i); + if (!module.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 + " ] of target \"%s\" from LLDB, invalid module", + i, target_file_spec.GetFilename()); + return; + } + + lldb::SBFileSpec module_file_spec = module.GetPlatformFileSpec(); + if (!module_file_spec.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 + " ] of target \"%s\" from LLDB, invalid file spec", + i, target_file_spec.GetFilename()); + return; + } + + const char *image(module_file_spec.GetFilename()); + lldb::SBError error; + char image_complete_path[1024]; + uint32_t path_length = module_file_spec.GetPath( + image_complete_path, sizeof(image_complete_path)); + size_t num_sections = module.GetNumSections(); + + // Store information of only RX sections + for (size_t idx = 0; idx < num_sections; idx++) { + lldb::SBSection section = module.GetSectionAtIndex(idx); + uint32_t section_permission = section.GetPermissions(); + if ((section_permission & lldb::Permissions::ePermissionsReadable) && + (section_permission & lldb::Permissions::ePermissionsExecutable)) { + lldb::SBData section_data = section.GetSectionData(); + if (!section_data.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 " ] \"%s\" of target " + "\"%s\" from LLDB, invalid " + "data in \"%s\" section", + i, image, target_file_spec.GetFilename(), section.GetName()); + return; + } + + // In case section has no data, skip it. + if (section_data.GetByteSize() == 0) + continue; + + if (!path_length) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 " ] \"%s\" of target " + "\"%s\" from LLDB, module " + "has an invalid path length", + i, image, target_file_spec.GetFilename()); + return; + } + + std::string image_path(image_complete_path, path_length); + readExecuteSectionInfos.emplace_back( + section.GetLoadAddress(sbtarget), section.GetFileOffset(), + section_data.GetByteSize(), image_path); + } + } + } +} + +// Raw trace decoding requires information of the target cpu on which inferior +// is running. This function gets the Trace Configuration from LLDB, parses it +// for cpu model, family, stepping and vendor id info and updates the internal +// state of the class to store this information. +void Decoder::ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s, + lldb::SBError &sberror) { + lldb::SBStructuredData custom_trace_params = s.GetValueForKey("intel-pt"); + if (!custom_trace_params.IsValid()) { + sberror.SetErrorStringWithFormat("lldb couldn't provide cpuinfo"); + return; + } + + uint64_t family = 0, model = 0, stepping = 0; + char vendor[32]; + const char *key_family = "cpu_family"; + const char *key_model = "cpu_model"; + const char *key_stepping = "cpu_stepping"; + const char *key_vendor = "cpu_vendor"; + + // parse family + lldb::SBStructuredData struct_family = + custom_trace_params.GetValueForKey(key_family); + if (!struct_family.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters", key_family); + return; + } + family = struct_family.GetIntegerValue(0x10000); + if (family > UINT16_MAX) { + sberror.SetErrorStringWithFormat( + "invalid CPU family value extracted from custom trace parameters"); + return; + } + pt_cpu.family = (uint16_t)family; + + // parse model + lldb::SBStructuredData struct_model = + custom_trace_params.GetValueForKey(key_model); + if (!struct_model.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16, + key_model, pt_cpu.family); + return; + } + model = struct_model.GetIntegerValue(0x100); + if (model > UINT8_MAX) { + sberror.SetErrorStringWithFormat("invalid CPU model value extracted from " + "custom trace parameters; family=%" PRIu16, + pt_cpu.family); + return; + } + pt_cpu.model = (uint8_t)model; + + // parse stepping + lldb::SBStructuredData struct_stepping = + custom_trace_params.GetValueForKey(key_stepping); + if (!struct_stepping.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16 + ", model=%" PRIu8, + key_stepping, pt_cpu.family, pt_cpu.model); + return; + } + stepping = struct_stepping.GetIntegerValue(0x100); + if (stepping > UINT8_MAX) { + sberror.SetErrorStringWithFormat("invalid CPU stepping value extracted " + "from custom trace parameters; " + "family=%" PRIu16 ", model=%" PRIu8, + pt_cpu.family, pt_cpu.model); + return; + } + pt_cpu.stepping = (uint8_t)stepping; + + // parse vendor info + pt_cpu.vendor = pcv_unknown; + lldb::SBStructuredData struct_vendor = + custom_trace_params.GetValueForKey(key_vendor); + if (!struct_vendor.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16 + ", model=%" PRIu8 ", stepping=%" PRIu8, + key_vendor, pt_cpu.family, pt_cpu.model, pt_cpu.stepping); + return; + } + auto length = struct_vendor.GetStringValue(vendor, sizeof(vendor)); + if (length && strstr(vendor, "GenuineIntel")) + pt_cpu.vendor = pcv_intel; +} + +// Initialize trace decoder with pt_config structure and populate its image +// structure with inferior's memory image information. pt_config structure is +// initialized with trace buffer and cpu info of the inferior before storing it +// in trace decoder. +void Decoder::InitializePTInstDecoder( + struct pt_insn_decoder **decoder, struct pt_config *config, + const CPUInfo &pt_cpu, Buffer &pt_buffer, + const ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) const { + if (!decoder || !config) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + // Load cpu info of inferior's target in pt_config struct + pt_config_init(config); + config->cpu = pt_cpu; + int errcode = pt_cpu_errata(&(config->errata), &(config->cpu)); + if (errcode < 0) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_cpu_errata() failed with error: " + "\"%s\"", + pt_errstr(pt_errcode(errcode))); + return; + } + + // Load trace buffer's starting and end address in pt_config struct + config->begin = pt_buffer.data(); + config->end = pt_buffer.data() + pt_buffer.size(); + + // Fill trace decoder with pt_config struct + *decoder = pt_insn_alloc_decoder(config); + if (*decoder == nullptr) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_insn_alloc_decoder() returned null " + "pointer"); + return; + } + + // Fill trace decoder's image with inferior's memory image information + struct pt_image *image = pt_insn_get_image(*decoder); + if (!image) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_insn_get_image() returned null " + "pointer"); + pt_insn_free_decoder(*decoder); + return; + } + + for (auto &itr : readExecuteSectionInfos) { + errcode = pt_image_add_file(image, itr.image_path.c_str(), itr.file_offset, + itr.size, nullptr, itr.load_address); + if (errcode < 0) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_image_add_file() failed with error: " + "\"%s\"", + pt_errstr(pt_errcode(errcode))); + pt_insn_free_decoder(*decoder); + return; + } + } +} + +// Start actual decoding of raw trace +void Decoder::DecodeTrace(struct pt_insn_decoder *decoder, + Instructions &instruction_list, + lldb::SBError &sberror) { + uint64_t decoder_offset = 0; + + while (1) { + struct pt_insn insn; + + // Try to sync the decoder. If it fails then get the decoder_offset and try + // to sync again. If the new_decoder_offset is same as decoder_offset then + // we will not succeed in syncing for any number of pt_insn_sync_forward() + // operations. Return in that case. Else keep resyncing until either end of + // trace stream is reached or pt_insn_sync_forward() passes. + int errcode = pt_insn_sync_forward(decoder); + if (errcode < 0) { + if (errcode == -pte_eos) + return; + + int errcode_off = pt_insn_get_offset(decoder, &decoder_offset); + if (errcode_off < 0) { + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\"", + pt_errstr(pt_errcode(errcode))); + instruction_list.emplace_back(sberror.GetCString()); + return; + } + + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset] => " + "[0x%" PRIu64 "]", + pt_errstr(pt_errcode(errcode)), decoder_offset); + instruction_list.emplace_back(sberror.GetCString()); + while (1) { + errcode = pt_insn_sync_forward(decoder); + if (errcode >= 0) + break; + + if (errcode == -pte_eos) + return; + + uint64_t new_decoder_offset = 0; + errcode_off = pt_insn_get_offset(decoder, &new_decoder_offset); + if (errcode_off < 0) { + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\"", + pt_errstr(pt_errcode(errcode))); + instruction_list.emplace_back(sberror.GetCString()); + return; + } else if (new_decoder_offset <= decoder_offset) { + // We tried resyncing the decoder and decoder didn't make any + // progress because the offset didn't change. We will not make any + // progress further. Hence, returning in this situation. + return; + } + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset] => " + "[0x%" PRIu64 "]", + pt_errstr(pt_errcode(errcode)), new_decoder_offset); + instruction_list.emplace_back(sberror.GetCString()); + decoder_offset = new_decoder_offset; + } + } + + while (1) { + errcode = pt_insn_next(decoder, &insn, sizeof(insn)); + if (errcode < 0) { + if (insn.iclass == ptic_error) + break; + + instruction_list.emplace_back(insn); + + if (errcode == -pte_eos) + return; + + Diagnose(decoder, errcode, sberror, &insn); + instruction_list.emplace_back(sberror.GetCString()); + break; + } + instruction_list.emplace_back(insn); + if (errcode & pts_eos) + return; + } + } +} + +// Function to diagnose and indicate errors during raw trace decoding +void Decoder::Diagnose(struct pt_insn_decoder *decoder, int decode_error, + lldb::SBError &sberror, const struct pt_insn *insn) { + int errcode; + uint64_t offset; + + errcode = pt_insn_get_offset(decoder, &offset); + if (insn) { + if (errcode < 0) + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset, " + "last_successful_decoded_ip] => [?, 0x%" PRIu64 "]", + pt_errstr(pt_errcode(decode_error)), insn->ip); + else + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset, " + "last_successful_decoded_ip] => [0x%" PRIu64 ", 0x%" PRIu64 "]", + pt_errstr(pt_errcode(decode_error)), offset, insn->ip); + } else { + if (errcode < 0) + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\"", + pt_errstr(pt_errcode(decode_error))); + else + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset] => " + "[0x%" PRIu64 "]", + pt_errstr(pt_errcode(decode_error)), offset); + } +} + +void Decoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, + lldb::tid_t tid, uint32_t offset, + uint32_t count, + InstructionList &result_list, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + ThreadTraceInfo *threadTraceInfo = nullptr; + FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo); + if (!sberror.Success()) { + return; + } + if (threadTraceInfo == nullptr) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + // Return instruction log by populating 'result_list' + Instructions &insn_list = threadTraceInfo->GetInstructionLog(); + uint64_t sum = (uint64_t)offset + 1; + if (((insn_list.size() <= offset) && (count <= sum) && + ((sum - count) >= insn_list.size())) || + (count < 1)) { + sberror.SetErrorStringWithFormat( + "Instruction Log not available for offset=%" PRIu32 + " and count=%" PRIu32 ", ProcessID = %" PRIu64, + offset, count, sbprocess.GetProcessID()); + return; + } + + Instructions::iterator itr_first = + (insn_list.size() <= offset) ? insn_list.begin() + : insn_list.begin() + insn_list.size() - sum; + Instructions::iterator itr_last = + (count <= sum) ? insn_list.begin() + insn_list.size() - (sum - count) + : insn_list.end(); + Instructions::iterator itr = itr_first; + while (itr != itr_last) { + result_list.AppendInstruction(*itr); + ++itr; + } +} + +void Decoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + TraceOptions &options, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard<std::mutex> guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + ThreadTraceInfo *threadTraceInfo = nullptr; + FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo); + if (!sberror.Success()) { + return; + } + if (threadTraceInfo == nullptr) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + // Get SBTraceOptions from LLDB for 'tid', populate 'traceoptions' with it + lldb::SBTrace &trace = threadTraceInfo->GetUniqueTraceInstance(); + lldb::SBTraceOptions traceoptions; + lldb::SBError error; + traceoptions.setThreadID(tid); + trace.GetTraceConfig(traceoptions, error); + if (!error.Success()) { + std::string error_string(error.GetCString()); + if (error_string.find("tracing not active") != std::string::npos) { + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) + return; + itr_process->second.erase(tid); + } + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error_string.c_str(), + sbprocess.GetProcessID()); + return; + } + if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB " + "for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + options.setType(traceoptions.getType()); + options.setTraceBufferSize(traceoptions.getTraceBufferSize()); + options.setMetaDataBufferSize(traceoptions.getMetaDataBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + options.setTraceParams(sbstructdata); + options.setInstructionLogSize(threadTraceInfo->GetInstructionLog().size()); +} + +void Decoder::FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo **threadTraceInfo) { + // Return with error if 'sbprocess' is not registered in the class + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this process; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + if (tid == LLDB_INVALID_THREAD_ID) { + sberror.SetErrorStringWithFormat( + "invalid thread id provided; thread_id = %" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + + // Check whether 'tid' thread is registered in the class. If it is then in + // case StopID didn't change then return without doing anything (no need to + // read and decode trace data then). Otherwise, save new StopID and proceed + // with reading and decoding trace. + if (threadTraceInfo == nullptr) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second; + auto itr_thread = mapThreadID_TraceInfo.find(tid); + if (itr_thread != mapThreadID_TraceInfo.end()) { + if (itr_thread->second.GetStopID() == sbprocess.GetStopID()) { + *threadTraceInfo = &(itr_thread->second); + return; + } + itr_thread->second.SetStopID(sbprocess.GetStopID()); + } else { + // Implies 'tid' is not registered in the class. If tracing was never + // started on the entire process then return an error. Else try to register + // this thread and proceed with reading and decoding trace. + lldb::SBError error; + itr_thread = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID); + if (itr_thread == mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this thread; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance(); + ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid]; + trace_info.SetUniqueTraceInstance(trace); + trace_info.SetStopID(sbprocess.GetStopID()); + itr_thread = mapThreadID_TraceInfo.find(tid); + } + + // Get raw trace data and inferior image from LLDB for the registered thread + ReadTraceDataAndImageInfo(sbprocess, tid, sberror, itr_thread->second); + if (!sberror.Success()) { + std::string error_string(sberror.GetCString()); + if (error_string.find("tracing not active") != std::string::npos) + mapThreadID_TraceInfo.erase(itr_thread); + return; + } + // Decode raw trace data + DecodeProcessorTrace(sbprocess, tid, sberror, itr_thread->second); + if (!sberror.Success()) { + return; + } + *threadTraceInfo = &(itr_thread->second); +} + +// This function checks whether the provided SBProcess instance belongs to same +// SBDebugger with which this tool instance is associated. +void Decoder::CheckDebuggerID(lldb::SBProcess &sbprocess, + lldb::SBError &sberror) { + if (!sbprocess.IsValid()) { + sberror.SetErrorStringWithFormat("invalid process instance"); + return; + } + + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + if (!sbtarget.IsValid()) { + sberror.SetErrorStringWithFormat( + "process contains an invalid target; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBDebugger sbdebugger = sbtarget.GetDebugger(); + if (!sbdebugger.IsValid()) { + sberror.SetErrorStringWithFormat("process's target contains an invalid " + "debugger instance; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + if (sbdebugger.GetID() != m_debugger_user_id) { + sberror.SetErrorStringWithFormat( + "process belongs to a different SBDebugger instance than the one for " + "which the tool is instantiated; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } +} diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.h b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.h new file mode 100644 index 00000000000..2ae89653df5 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.h @@ -0,0 +1,309 @@ +//===-- Decoder.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef Decoder_h_ +#define Decoder_h_ + +// C/C++ Includes +#include <map> +#include <mutex> +#include <string> +#include <vector> + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBTrace.h" +#include "lldb/API/SBTraceOptions.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +#include "intel-pt.h" + +namespace ptdecoder_private { +/// \class Instruction +/// Represents an assembly instruction containing raw +/// instruction bytes, instruction address along with information +/// regarding execution flow context and Intel(R) Processor Trace +/// context. +class Instruction { +public: + Instruction() : ip(0), data(), error(), iclass(ptic_error), speculative(0) {} + + Instruction(const Instruction &insn) = default; + + Instruction(const struct pt_insn &insn) + : ip(insn.ip), data(), error(insn.size == 0 ? "invalid instruction" : ""), + iclass(insn.iclass), speculative(insn.speculative) { + if (insn.size != 0) + data.assign(insn.raw, insn.raw + insn.size); + } + + Instruction(const char *err) + : ip(0), data(), error(err ? err : "unknown error"), iclass(ptic_error), + speculative(0) {} + + ~Instruction() {} + + uint64_t GetInsnAddress() const { return ip; } + + size_t GetRawBytes(void *buf, size_t size) const { + if ((buf == nullptr) || (size == 0)) + return data.size(); + + size_t bytes_to_read = ((size <= data.size()) ? size : data.size()); + ::memcpy(buf, data.data(), bytes_to_read); + return bytes_to_read; + } + + const std::string &GetError() const { return error; } + + bool GetSpeculative() const { return speculative; } + +private: + uint64_t ip; // instruction address in inferior's memory image + std::vector<uint8_t> data; // raw bytes + std::string error; // Error string if instruction is invalid + enum pt_insn_class iclass; // classification of the instruction + // A collection of flags giving additional information about instruction + uint32_t speculative : 1; // Instruction was executed speculatively or not +}; + +/// \class InstructionList +/// Represents a list of assembly instructions. Each instruction is of +/// type Instruction. +class InstructionList { +public: + InstructionList() : m_insn_vec() {} + + InstructionList(const InstructionList &insn_list) + : m_insn_vec(insn_list.m_insn_vec) {} + + ~InstructionList() {} + + // Get number of instructions in the list + size_t GetSize() const { return m_insn_vec.size(); } + + // Get instruction at index + Instruction GetInstructionAtIndex(uint32_t idx) { + return (idx < m_insn_vec.size() ? m_insn_vec[idx] + : Instruction("invalid instruction")); + } + + // Append intruction at the end of the list + void AppendInstruction(Instruction inst) { m_insn_vec.push_back(inst); } + +private: + std::vector<Instruction> m_insn_vec; +}; + +/// \class TraceOptions +/// Provides Intel(R) Processor Trace specific configuration options and +/// other information obtained by decoding and post-processing the trace +/// data. Currently, this information comprises of the total number of +/// assembly instructions executed for an inferior. +class TraceOptions : public lldb::SBTraceOptions { +public: + TraceOptions() : lldb::SBTraceOptions(), m_insn_log_size(0) {} + + ~TraceOptions() {} + + /// Get total number of assembly instructions obtained after decoding the + /// complete Intel(R) Processor Trace data obtained from LLDB. + /// + /// \return + /// Total number of instructions. + uint32_t getInstructionLogSize() const { return m_insn_log_size; } + + /// Set total number of assembly instructions. + /// + /// \param[in] size + /// Value to be set. + void setInstructionLogSize(uint32_t size) { m_insn_log_size = size; } + +private: + uint32_t m_insn_log_size; +}; + +/// \class Decoder +/// This class makes use of Intel(R) Processor Trace hardware feature +/// (implememted inside LLDB) to gather trace data for an inferior (being +/// debugged with LLDB) to provide meaningful information out of it. +/// +/// Currently the meaningful information comprises of the execution flow +/// of the inferior (in terms of assembly instructions executed). The class +/// enables user to: +/// - start the trace with configuration options for a thread/process, +/// - stop the trace for a thread/process, +/// - get the execution flow (assembly instructions) for a thread and +/// - get trace specific information for a thread +class Decoder { +public: + typedef std::vector<Instruction> Instructions; + + Decoder(lldb::SBDebugger &sbdebugger) + : m_mapProcessUID_mapThreadID_TraceInfo_mutex(), + m_mapProcessUID_mapThreadID_TraceInfo(), + m_debugger_user_id(sbdebugger.GetID()) {} + + ~Decoder() {} + + void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror); + + void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID); + + void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + InstructionList &result_list, + lldb::SBError &sberror); + + void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + TraceOptions &traceinfo, lldb::SBError &sberror); + +private: + class ThreadTraceInfo; + typedef std::vector<uint8_t> Buffer; + + // internal class to manage inferior's read-execute section information + class ReadExecuteSectionInfo { + public: + uint64_t load_address; + uint64_t file_offset; + uint64_t size; + std::string image_path; + + ReadExecuteSectionInfo(const uint64_t addr, const uint64_t offset, + const uint64_t sz, const std::string &path) + : load_address(addr), file_offset(offset), size(sz), image_path(path) {} + + ReadExecuteSectionInfo(const ReadExecuteSectionInfo &rxsection) = default; + }; + + typedef struct pt_cpu CPUInfo; + typedef std::vector<ReadExecuteSectionInfo> ReadExecuteSectionInfos; + + // Check whether the provided SBProcess belongs to the same SBDebugger with + // which Decoder class instance was constructed. + void CheckDebuggerID(lldb::SBProcess &sbprocess, lldb::SBError &sberror); + + // Function to remove entries of finished processes/threads in the class + void RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess); + + // Parse cpu information from trace configuration received from LLDB + void ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s, + lldb::SBError &sberror); + + /// Function performs following tasks for a given process and thread: + /// - Checks if the given thread is registered in the class or not. If not + /// then tries to register it if trace was ever started on the entire + /// process. Else returns error. + /// - fetches trace and other necessary information from LLDB (using + /// ReadTraceDataAndImageInfo()) and decodes the trace (using + /// DecodeProcessorTrace()) + void FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo **threadTraceInfo); + + // Helper function of FetchAndDecode() to get raw trace data and memory image + // info of inferior from LLDB + void ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo); + + // Helper function of FetchAndDecode() to initialize raw trace decoder and + // start trace decoding + void DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo); + + // Helper function of ReadTraceDataAndImageInfo() function for gathering + // inferior's memory image info along with all dynamic libraries linked with + // it + void GetTargetModulesInfo(lldb::SBTarget &sbtarget, + ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror); + + /// Helper functions of DecodeProcessorTrace() function for: + /// - initializing raw trace decoder (provided by Intel(R) Processor Trace + /// Decoding library) + /// - start trace decoding + void InitializePTInstDecoder( + struct pt_insn_decoder **decoder, struct pt_config *config, + const CPUInfo &pt_cpu, Buffer &pt_buffer, + const ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) const; + void DecodeTrace(struct pt_insn_decoder *decoder, + Instructions &instruction_list, lldb::SBError &sberror); + + // Function to diagnose and indicate errors during raw trace decoding + void Diagnose(struct pt_insn_decoder *decoder, int errcode, + lldb::SBError &sberror, const struct pt_insn *insn = nullptr); + + class ThreadTraceInfo { + public: + ThreadTraceInfo() + : m_pt_buffer(), m_readExecuteSectionInfos(), m_thread_stop_id(0), + m_trace(), m_pt_cpu(), m_instruction_log() {} + + ThreadTraceInfo(const ThreadTraceInfo &trace_info) = default; + + ~ThreadTraceInfo() {} + + Buffer &GetPTBuffer() { return m_pt_buffer; } + + void AllocatePTBuffer(uint64_t size) { m_pt_buffer.assign(size, 0); } + + ReadExecuteSectionInfos &GetReadExecuteSectionInfos() { + return m_readExecuteSectionInfos; + } + + CPUInfo &GetCPUInfo() { return m_pt_cpu; } + + Instructions &GetInstructionLog() { return m_instruction_log; } + + uint32_t GetStopID() const { return m_thread_stop_id; } + + void SetStopID(uint32_t stop_id) { m_thread_stop_id = stop_id; } + + lldb::SBTrace &GetUniqueTraceInstance() { return m_trace; } + + void SetUniqueTraceInstance(lldb::SBTrace &trace) { m_trace = trace; } + + friend class Decoder; + + private: + Buffer m_pt_buffer; // raw trace buffer + ReadExecuteSectionInfos + m_readExecuteSectionInfos; // inferior's memory image info + uint32_t m_thread_stop_id; // stop id for thread + lldb::SBTrace m_trace; // unique tracing instance of a thread/process + CPUInfo m_pt_cpu; // cpu info of the target on which inferior is running + Instructions m_instruction_log; // complete instruction log + }; + + typedef std::map<lldb::user_id_t, ThreadTraceInfo> MapThreadID_TraceInfo; + typedef std::map<uint32_t, MapThreadID_TraceInfo> + MapProcessUID_MapThreadID_TraceInfo; + + std::mutex m_mapProcessUID_mapThreadID_TraceInfo_mutex; + MapProcessUID_MapThreadID_TraceInfo + m_mapProcessUID_mapThreadID_TraceInfo; // to store trace information for + // each process and its associated + // threads + lldb::user_id_t m_debugger_user_id; // SBDebugger instance which is associated + // to this Decoder instance +}; + +} // namespace ptdecoder_private +#endif // Decoder_h_ diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.cpp b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.cpp new file mode 100644 index 00000000000..f09307d0ffe --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.cpp @@ -0,0 +1,149 @@ +//===-- PTDecoder.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PTDecoder.h" +#include "Decoder.h" + +using namespace ptdecoder; +using namespace ptdecoder_private; + +// PTInstruction class member functions definitions +PTInstruction::PTInstruction( + const std::shared_ptr<ptdecoder_private::Instruction> &ptr) + : m_opaque_sp(ptr) {} + +PTInstruction::~PTInstruction() {} + +uint64_t PTInstruction::GetInsnAddress() const { + return (m_opaque_sp ? m_opaque_sp->GetInsnAddress() : 0); +} + +size_t PTInstruction::GetRawBytes(void *buf, size_t size) const { + return (m_opaque_sp ? m_opaque_sp->GetRawBytes(buf, size) : 0); +} + +std::string PTInstruction::GetError() const { + return (m_opaque_sp ? m_opaque_sp->GetError() : "null pointer"); +} + +bool PTInstruction::GetSpeculative() const { + return (m_opaque_sp ? m_opaque_sp->GetSpeculative() : 0); +} + +// PTInstructionList class member functions definitions +size_t PTInstructionList::GetSize() const { + return (m_opaque_sp ? m_opaque_sp->GetSize() : 0); +} + +PTInstruction PTInstructionList::GetInstructionAtIndex(uint32_t idx) { + if (m_opaque_sp) + return PTInstruction(std::shared_ptr<ptdecoder_private::Instruction>( + new Instruction(m_opaque_sp->GetInstructionAtIndex(idx)))); + + return PTInstruction(std::shared_ptr<ptdecoder_private::Instruction>( + new Instruction("invalid instruction"))); +} + +void PTInstructionList::SetSP( + const std::shared_ptr<ptdecoder_private::InstructionList> &ptr) { + m_opaque_sp = ptr; +} +void PTInstructionList::Clear() { + if (!m_opaque_sp) + return; + m_opaque_sp.reset(); +} + +// PTTraceOptions class member functions definitions +lldb::TraceType PTTraceOptions::GetType() const { + return (m_opaque_sp ? m_opaque_sp->getType() + : lldb::TraceType::eTraceTypeNone); +} + +uint64_t PTTraceOptions::GetTraceBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getTraceBufferSize() : 0); +} + +uint64_t PTTraceOptions::GetMetaDataBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getMetaDataBufferSize() : 0); +} + +lldb::SBStructuredData PTTraceOptions::GetTraceParams(lldb::SBError &error) { + if (!m_opaque_sp) + error.SetErrorString("null pointer"); + return (m_opaque_sp ? m_opaque_sp->getTraceParams(error) + : lldb::SBStructuredData()); +} + +void PTTraceOptions::SetSP( + const std::shared_ptr<ptdecoder_private::TraceOptions> &ptr) { + m_opaque_sp = ptr; +} + +// PTDecoder class member functions definitions +PTDecoder::PTDecoder(lldb::SBDebugger &sbdebugger) + : m_opaque_sp(new ptdecoder_private::Decoder(sbdebugger)) {} + +void PTDecoder::StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + m_opaque_sp->StartProcessorTrace(sbprocess, sbtraceoptions, sberror); +} + +void PTDecoder::StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, lldb::tid_t tid) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + m_opaque_sp->StopProcessorTrace(sbprocess, sberror, tid); +} + +void PTDecoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, + lldb::tid_t tid, uint32_t offset, + uint32_t count, + PTInstructionList &result_list, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + std::shared_ptr<ptdecoder_private::InstructionList> insn_list_ptr( + new InstructionList()); + m_opaque_sp->GetInstructionLogAtOffset(sbprocess, tid, offset, count, + *insn_list_ptr, sberror); + if (!sberror.Success()) + return; + + result_list.SetSP(insn_list_ptr); +} + +void PTDecoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, + lldb::tid_t tid, PTTraceOptions &options, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + std::shared_ptr<ptdecoder_private::TraceOptions> trace_options_ptr( + new TraceOptions()); + m_opaque_sp->GetProcessorTraceInfo(sbprocess, tid, *trace_options_ptr, + sberror); + if (!sberror.Success()) + return; + + options.SetSP(trace_options_ptr); +} diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.h b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.h new file mode 100644 index 00000000000..f44c152747b --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.h @@ -0,0 +1,270 @@ +//===-- PTDecoder.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef PTDecoder_h_ +#define PTDecoder_h_ + +// C/C++ Includes +#include <vector> + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTraceOptions.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +namespace ptdecoder_private { +class Instruction; +class InstructionList; +class TraceOptions; +class Decoder; +} // namespace ptdecoder_private + +namespace ptdecoder { + +/// \class PTInstruction +/// Represents an assembly instruction containing raw +/// instruction bytes, instruction address along with information +/// regarding execution flow context and Intel(R) Processor Trace +/// context. +class PTInstruction { +public: + PTInstruction() = default; + + PTInstruction(const std::shared_ptr<ptdecoder_private::Instruction> &ptr); + + ~PTInstruction(); + + // Get instruction address in inferior's memory image + uint64_t GetInsnAddress() const; + + /// Get raw bytes of the instruction in the buffer. + /// + /// \param[out] buf + /// The buffer where the raw bytes will be written. This buffer should be + /// allocated by the caller of this API. Providing an unallocated buffer + /// is an error. In case of errors, the content of the buffer is not + /// valid. + /// + /// \param[in] size + /// Number of raw bytes to be written to @buf. Atleast @size bytes of + /// memory should be allocated to @buf otherwise the behaviour of the API + /// is undefined. Providing 0 for this argument is an error. + /// + /// \return + /// Number of bytes of the instruction actually written to @buf if API + /// succeeds. In case of errors, total number of raw bytes of the + /// instruction is returned. + size_t GetRawBytes(void *buf, size_t size) const; + + // Get error string if it represents an invalid instruction. For a valid + // instruction, an empty string is returned + std::string GetError() const; + + // Instruction was executed speculatively or not + bool GetSpeculative() const; + +private: + std::shared_ptr<ptdecoder_private::Instruction> m_opaque_sp; +}; + +/// \class PTInstructionList +/// Represents a list of assembly instructions. Each instruction is of +/// type PTInstruction. +class PTInstructionList { +public: + // Get number of instructions in the list + size_t GetSize() const; + + // Get instruction at index + PTInstruction GetInstructionAtIndex(uint32_t idx); + + void Clear(); + +private: + friend class PTDecoder; + + void SetSP(const std::shared_ptr<ptdecoder_private::InstructionList> &ptr); + + std::shared_ptr<ptdecoder_private::InstructionList> m_opaque_sp; +}; + +/// \class PTTraceOptions +/// Provides configuration options like trace type, trace buffer size, +/// meta data buffer size along with other Intel(R) Processor Trace +/// specific options. +class PTTraceOptions { +public: + lldb::TraceType GetType() const; + + uint64_t GetTraceBufferSize() const; + + uint64_t GetMetaDataBufferSize() const; + + /// Get Intel(R) Processor Trace specific configuration options (apart from + /// trace buffer size, meta data buffer size and TraceType) formatted as + /// json text i.e. {"Name":Value,"Name":Value} pairs, where "Value" is a + /// 64-bit unsigned integer in hex format. For "Name", please refer to + /// SBProcess::StartTrace API description for setting SBTraceOptions. + /// + /// \return + /// A string formatted as json text {"Name":Value,"Name":Value} + lldb::SBStructuredData GetTraceParams(lldb::SBError &error); + +private: + friend class PTDecoder; + + void SetSP(const std::shared_ptr<ptdecoder_private::TraceOptions> &ptr); + + std::shared_ptr<ptdecoder_private::TraceOptions> m_opaque_sp; +}; + +/// \class PTDecoder +/// This class makes use of Intel(R) Processor Trace hardware feature +/// (implememted inside LLDB) to gather trace data for an inferior (being +/// debugged with LLDB) to provide meaningful information out of it. +/// +/// Currently the meaningful information comprises of the execution flow +/// of the inferior (in terms of assembly instructions executed). The class +/// enables user to: +/// - start the trace with configuration options for a thread/process, +/// - stop the trace for a thread/process, +/// - get the execution flow (assembly instructions) for a thread and +/// - get trace specific information for a thread +class PTDecoder { +public: + PTDecoder(lldb::SBDebugger &sbdebugger); + + /// Start Intel(R) Processor Trace on a thread or complete process with + /// Intel(R) Processor Trace specific configuration options + /// + /// \param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// \param[in] sbtraceoptions + /// Contains thread id information and configuration options: + /// + /// For tracing a single thread, provide a valid thread id. If sbprocess + /// doesn't contain this thread id, error will be returned. For tracing + /// complete process, set it to lldb::LLDB_INVALID_THREAD_ID + /// Configuration options comprises of: + /// a) trace buffer size, meta data buffer size, TraceType and + /// b) All other possible Intel(R) Processor Trace specific configuration + /// options (hereafter collectively referred as CUSTOM_OPTIONS), formatted + /// as json text i.e. {"Name":Value,"Name":Value,..} inside + /// sbtraceoptions, where "Value" should be a 64-bit unsigned integer in + /// hex format. For information regarding what all configuration options + /// are currently supported by LLDB and detailed information about + /// CUSTOM_OPTIONS usage, please refer to SBProcess::StartTrace() API + /// description. To know about all possible configuration options of + /// Intel(R) Processor Trace, please refer to Intel(R) 64 and IA-32 + /// Architectures Software Developer's Manual. + /// + /// TraceType should be set to lldb::TraceType::eTraceTypeProcessorTrace, + /// else error is returned. To find out any other requirement to start + /// tracing successfully, please refer to SBProcess::StartTrace() API + /// description. LLDB's current implementation of Intel(R) Processor Trace + /// feature may round off invalid values for configuration options. + /// Therefore, the configuration options with which the trace was actually + /// started, might be different to the ones with which trace was asked to + /// be started by user. The actual used configuration options can be + /// obtained from GetProcessorTraceInfo() API. + /// + /// \param[out] sberror + /// An error with the failure reason if API fails. Else success. + void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror); + + /// Stop Intel(R) Processor Trace on a thread or complete process. + /// + /// \param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// \param[in] tid + /// Case 1: To stop tracing a single thread, provide a valid thread id. If + /// sbprocess doesn't contain the thread tid, error will be returned. + /// Case 2: To stop tracing complete process, use + /// lldb::LLDB_INVALID_THREAD_ID. + /// + /// \param[out] sberror + /// An error with the failure reason if API fails. Else success. + void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID); + + /// Get instruction log containing the execution flow for a thread of a + /// process in terms of assembly instructions executed. + /// + /// \param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// \param[in] tid + /// A valid thread id of the thread for which instruction log is desired. + /// If sbprocess doesn't contain the thread tid, error will be returned. + /// + /// \param[in] count + /// The number of instructions requested by the user to be returned from + /// the complete instruction log. Complete instruction log refers to all + /// the assembly instructions obtained after decoding the complete raw + /// trace data obtained from LLDB. The length of the complete instruction + /// log is dependent on the trace buffer size with which processor tracing + /// was started for this thread. + /// The number of instructions actually returned are dependent on 'count' + /// and 'offset' parameters of this API. + /// + /// \param[in] offset + /// The offset in the complete instruction log from where 'count' number + /// of instructions are requested by the user. offset is counted from the + /// end of of this complete instruction log (which means the last executed + /// instruction is at offset 0 (zero)). + /// + /// \param[out] result_list + /// Depending upon 'count' and 'offset' values, list will be overwritten + /// with the new instructions. + /// + /// \param[out] sberror + /// An error with the failure reason if API fails. Else success. + void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + PTInstructionList &result_list, + lldb::SBError &sberror); + + /// Get Intel(R) Processor Trace specific information for a thread of a + /// process. The information contains the actual configuration options with + /// which the trace was started for this thread. + /// + /// \param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// \param[in] tid + /// A valid thread id of the thread for which the trace specific + /// information is required. If sbprocess doesn't contain the thread tid, + /// an error will be returned. + /// + /// \param[out] options + /// Contains actual configuration options (they may be different to the + /// ones with which tracing was asked to be started for this thread during + /// StartProcessorTrace() API call). + /// + /// \param[out] sberror + /// An error with the failure reason if API fails. Else success. + void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + PTTraceOptions &options, lldb::SBError &sberror); + +private: + std::shared_ptr<ptdecoder_private::Decoder> m_opaque_sp; +}; + +} // namespace ptdecoder +#endif // PTDecoder_h_ diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/README_CLI.txt b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_CLI.txt new file mode 100644 index 00000000000..2a497c4b3ef --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_CLI.txt @@ -0,0 +1,123 @@ +**************************************************************************** +* README * +* * +* This file provides all the information regarding 4 new CLI commands that * +* enable using Intel(R) Processor Trace Tool from LLDB's CLI. * +**************************************************************************** + + +============ +Introduction +============ +A C++ based cli wrapper has been developed to use Intel(R) Processor Trace Tool +through LLDB's command line. This also provides an idea to all developers on how +to integrate the Tool into various IDEs providing LLDB as a debugger. + + + +============ +How to Build +============ +The wrapper cli-wrapper-pt.cpp needs to be compiled and linked with the shared +library of the Intel(R) Processor Trace Tool in order to be used through LLDB's +CLI. The procedure to build shared library of the Intel(R) Processor Trace Tool +is given in README_TOOL.txt file. + + + +============ +How to Use +============ +All these commands are available via shared library (lldbIntelFeatures) +obtained after building intel-features folder from top. Please refer to +cli-wrapper.cpp and README files of "intel-features" folder for this purpose. + + + +============ +Description +============ +4 CLI commands have been designed keeping the LLDB's existing CLI command syntax +in mind. + + 1) processor-trace start [-b <buffer-size>] [<thread-index>] + + Start Intel(R) Processor Trace on a specific thread or on the whole process + + Syntax: processor-trace start <cmd-options> + + cmd-options Usage: + processor-trace start [-b <buffer-size>] [<thread-index>] + + -b <buffer-size> + size of the trace buffer to store the trace data. If not specified + then a default value (=4KB) will be taken + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to start + tracing the whole process + + + + 2) processor-trace stop [<thread-index>] + + Stop Intel(R) Processor Trace on a specific thread or on the whole process + + Syntax: processor-trace stop <cmd-options> + + cmd-options Usage: + processor-trace stop [<thread-index>] + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to stop + tracing the whole process + + + + 3) processor-trace show-trace-options [<thread-index>] + + Display all the information regarding Intel(R) Processor Trace for a specific + thread or for the whole process. The information contains trace buffer + size and configuration options of Intel(R) Processor Trace. + + Syntax: processor-trace show-trace-options <cmd-options> + + cmd-options Usage: + processor-trace show-trace-options [<thread-index>] + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to display + information for all threads of the process + + + + 4) processor-trace show-instr-log [-o <offset>] [-c <count>] [<thread-index>] + + Display a log of assembly instructions executed for a specific thread or + for the whole process. The length of the log to be displayed and the + offset in the whole instruction log from where the log needs to be + displayed can also be provided. The offset is counted from the end of this + whole instruction log which means the last executed instruction is at + offset 0 (zero). + + Syntax: processor-trace show-instr-log <cmd-options> + + cmd-options Usage: + processor-trace show-instr-log [-o <offset>] [-c <count>] [<thread-index>] + + -c <count> + number of instructions to be displayed. If not specified then a + default value (=10) will be taken + + -o <offset> + offset in the whole instruction log from where the log will be + displayed. If not specified then default value is calculated as + offset = count -1 + + <thread-index> + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to show + instruction log for all the threads of the process diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/README_TOOL.txt b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_TOOL.txt new file mode 100644 index 00000000000..d1ec1caf73c --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_TOOL.txt @@ -0,0 +1,311 @@ +******************************************************************************* +* README * +* * +* This file provides all the information regarding Intel(R) Processor Trace * +* Tool. It consists explanation about how Tool internally works, its hardware * +* and software dependencies, build procedure and usage of the API. * +******************************************************************************* + + + +============ +Introduction +============ +The Intel(R) Processor Trace Tool is developed on top of LLDB and provides its +its users execution trace of the debugged applications. Tool makes use of +Intel(R) Processor Trace hardware feature implementation inside LLDB for this +purpose. This hardware feature generates a set of trace packets that +encapsulates program flow information. These trace packets along with the binary +of the application can be decoded with the help of a software decoder to +construct the execution trace of the application. + +More information about Intel(R) Processor Trace feature can be obtained from +website: https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing + + + + +========= +Details +========= +The functionality of the Tool consists three parts: + +1. Raw Trace Collection from LLDB + With the help of API of this Tool (given below), Intel(R) Processor Trace + can be started on the application being debugged with LLDB. The generated + trace of the application is gathered inside LLDB and is collected by the + Tool from LLDB through LLDB's public API. + +2. Raw Trace Decoding + For decoding the raw trace data, the Tool makes use of "libipt", an + Intel(R) Processor Trace Decoder Library. The library needs binary of + the application and information about the cpu on which the application is + running in order to decode the raw trace. The Tool gathers this + information from LLDB public API and provide it to "libipt". More + information about "libipt" can be found at: + https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing and + https://github.com/01org/processor-trace + +3. Decoded Trace Post-processing + The decoded trace is post-processed to reconstruct the execution flow of + the application. The execution flow contains the list of assembly + instructions (called instruction log hereafter). + + + + +============= +Dependencies +============= +The Tool has following hardware and software dependencies: + + - Hardware dependency: The Tool makes use of this hardware feature to capture + raw trace of an application from LLDB. This hardware feature may not be + present in all processors. The hardware feature is supported on Broadwell + and other succeeding CPUs such as Skylake etc. In order for Tool to provide + something meaningful, the target machine on which the application is running + should have this feature. + + - Software dependency: The Tool has an indirect dependency on the Operating + System level software support for Intel(R) Processor Trace on the target + machine where the application is running and being debugged by LLDB. This + support is required to enable raw trace generation on the target machine. + Currently, the Tool works for applications running on Linux OS as till now + the Operating System level support for the feature is present only in Linux + (more specifically starting from the 4.1 kernel). In Linux, this feature is + implemented in perf_events subsystem and is usable through perf_event_open + system call. In the User space level, the Tool has a direct dependency on + "libipt" to decode the captured raw trace. This library might be + pre-installed on host systems. If not then the library can be built from + its sources (available at): https://github.com/01org/processor-trace + + + + +============ +How to Build +============ +The Tool has a cmake based build and can be built by specifying some extra flags +while building LLDB with cmake. The following cmake flags need to be provided to +build the Tool: + + - LIBIPT_INCLUDE_PATH - The flag specifies the directory where the header + file of "libipt" resides. If the library is not pre-installed on the host + system and is built directly from "libipt" project sources then this file + may either come as a part of the sources itself or will be generated in + build folder while building library. + + - LIBIPT_LIBRARY_PATH - The flag points to the location of "libipt" shared + library. + +The Tool currently works successfully with following versions of this library: + - v1.4, v1.5, v1.6 + + + +============ +How to Use +============ +The Tool's API are exposed as a C++ object oriented interface (file PTDecoder.h) +in a shared library. The main class that implements the whole functionality is +PTDecoder. This class makes use of 3 other classes, + - PTInstruction to represent an assembly instruction + - PTInstructionList to return instruction log + - PTTraceOptions to return trace specific information +The users can use these API to develop their own products. All API are also +available as python functions through a script bridging interface, allowing +them to be used directly from python either interactively or to build python +apps. + +Currently, cli wrapper has been developed on top of the Tool to use it through +LLDB's command line. Please refer to README_CLI.txt file for command line usage. + + +A brief introduction about the classes and their API are given below. + + class PTDecoder + =============== + This class makes use of Intel(R) Processor Trace hardware feature + (implemented inside LLDB) to gather trace data for an inferior (being + debugged with LLDB) to provide meaningful information out of it. Currently + the meaningful information comprises of the execution flow of the inferior + (in terms of assembly instructions executed). The class enables user to: + + - start the trace with configuration options for a thread/process, + - stop the trace for a thread/process, + - get the execution flow (assembly instructions) for a thread and + - get trace specific information for a thread + + Corresponding API are explained below: + a) void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API allows the user to start trace on a particular thread or on + the whole process with Intel(R) Processor Trace specific + configuration options. + + @param[in] sbprocess : A valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[out] sberror : An error with the failure reason if API + fails. Else success. + + @param[in] sbtraceoptions : Contains thread id information and + configuration options: + For tracing a single thread, provide a valid thread id. If + sbprocess doesn't contain this thread id, error will be returned. + For tracing complete process, set to lldb::LLDB_INVALID_THREAD_ID + Configuration options comprises of: + - trace buffer size, meta data buffer size, TraceType and + - All other possible Intel(R) Processor Trace specific + configuration options (hereafter collectively referred as + CUSTOM_OPTIONS) + + Trace buffer, meant to store the trace data read from target + machine, inside LLDB is configured as a cyclic buffer. Hence, + depending upon the trace buffer size provided here, buffer + overwrites may happen while LLDB writes trace data into it. + CUSTOM_OPTIONS are formatted as json text i.e. {"Name":Value, + "Name":Value,...} inside sbtraceoptions, where "Value" should be + a 64-bit unsigned integer in hex format. For information + regarding what all configuration options are currently supported + by LLDB and detailed information about CUSTOM_OPTIONS usage, + please refer to SBProcess::StartTrace() API description. An + overview of some of the various CUSTOM_OPTIONS are briefly given + below. Please refer to "Intel(R) 64 and IA-32 Architectures + Software Developer's Manual" for more details about them. + - CYCEn Enable/Disable Cycle Count Packet (CYC) Packet + - OS Packet generation enabled/disabled if + Current Privilege Level (CPL)=0 + - User Packet generation enabled/disabled if CPL>0 + - CR3Filter Enable/Disable CR3 Filtering + - MTCEn Enable/disable MTC packets + - TSCEn Enable/disable TSC packets + - DisRETC Enable/disable RET Compression + - BranchEn Enable/disable COFI-based packets + - MTCFreq Defines MTC Packet Frequency + - CycThresh CYC Packet threshold + - PSBFreq Frequency of PSB Packets + + TraceType should be set to + lldb::TraceType::eTraceTypeProcessorTrace, else error is + returned. To find out any other requirement to start tracing + successfully, refer to SBProcess::StartTrace() API description. + LLDB's current implementation of Intel(R) Processor Trace + feature may round off invalid values for configuration options. + Therefore, the configuration options with which the trace was + actually started, might be different to the ones with which + trace was asked to be started by user. The actual used + configuration options can be obtained from + GetProcessorTraceInfo() API. + + + + b) void StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID) + ------------------------------------------------------------------------ + This API allows the user to Stop trace on a particular thread or on + the whole process. + + @param[in] sbprocess : A valid process on which this operation will + be performed. An error is returned in case of an invalid process. + + @param[in] tid : To stop tracing a single thread, provide a + valid thread id. If sbprocess doesn't contain the thread tid, + error will be returned. To stop tracing complete process, use + lldb::LLDB_INVALID_THREAD_ID + + @param[out] sberror : An error with the failure reason if API fails. + Else success + + + + c) void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + PTInstructionList &result_list, + lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API provides instruction log that contains the execution flow + for a thread of a process in terms of assembly instruction executed. + The API works on only 1 thread at a time. To gather this information + for whole process, this API needs to be called for each thread. + + @param[in] sbprocess : A valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[in] tid : A valid thread id of the thread for which + instruction log is desired. If sbprocess doesn't contain the + thread tid, error will be returned. + + @param[in] count : Number of instructions requested by the + user to be returned from the complete instruction log. Complete + instruction log refers to all the assembly instructions obtained + after decoding the complete raw trace data obtained from LLDB. + The length of the complete instruction log is dependent on the + trace buffer size with which processor tracing was started for + this thread. + The number of instructions actually returned are dependent on + 'count' and 'offset' parameters of this API. + + @param[in] offset : The offset in the complete instruction log + from where 'count' number of instructions are requested by the + user. offset is counted from the end of of this complete + instruction log (which means the last executed instruction + is at offset 0 (zero)). + + @param[out] result_list : Depending upon 'count' and 'offset' values, + list will be overwritten with the instructions. + + @param[out] sberror : An error with the failure reason if API + fails. Else success + + + + d) void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + PTTraceOptions &options, lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API provides Intel(R) Processor Trace specific information for + a thread of a process. The API works on only 1 thread at a time. To + gather this information for whole process, this API needs to be + called for each thread. The information contains the actual + configuration options with which the trace was started for this + thread. + + @param[in] sbprocess : The valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[in] tid : A valid thread id of the thread for which the + trace specific information is required. If sbprocess doesn't + contain the thread tid, an error will be returned. + + @param[out] options : Contains actual configuration options (they + may be different to the ones with which tracing was asked to be + started for this thread during StartProcessorTrace() API call). + + @param[out] sberror : An error with the failure reason if API + fails. Else success + + + class PTInstruction + =================== + This class represents an assembly instruction containing raw instruction + bytes, instruction address along with execution flow context and + Intel(R) Processor Trace context. For more details, please refer to + PTDecoder.h file. + + class PTInstructionList + ======================= + This class represents a list of assembly instructions. Each assembly + instruction is of type PTInstruction. + + class PTTraceOptions + ==================== + This class provides Intel(R) Processor Trace specific configuration + options like trace type, trace buffer size, meta data buffer size along + with other trace specific options. For more details, please refer to + PTDecoder.h file. diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.cpp b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.cpp new file mode 100644 index 00000000000..8db1c0f82d6 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.cpp @@ -0,0 +1,582 @@ +//===-- cli-wrapper-pt.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// CLI Wrapper of PTDecoder Tool to enable it to be used through LLDB's CLI. The +// wrapper provides a new command called processor-trace with 4 child +// subcommands as follows: +// processor-trace start +// processor-trace stop +// processor-trace show-trace-options +// processor-trace show-instr-log +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#include <cinttypes> +#include <cstring> +#include <string> +#include <vector> + +#include "PTDecoder.h" +#include "cli-wrapper-pt.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" + +static bool GetProcess(lldb::SBDebugger &debugger, + lldb::SBCommandReturnObject &result, + lldb::SBProcess &process) { + if (!debugger.IsValid()) { + result.Printf("error: invalid debugger\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBTarget target = debugger.GetSelectedTarget(); + if (!target.IsValid()) { + result.Printf("error: invalid target inside debugger\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + process = target.GetProcess(); + if (!process.IsValid() || + (process.GetState() == lldb::StateType::eStateDetached) || + (process.GetState() == lldb::StateType::eStateExited) || + (process.GetState() == lldb::StateType::eStateInvalid)) { + result.Printf("error: invalid process inside debugger's target\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + return true; +} + +static bool ParseCommandOption(char **command, + lldb::SBCommandReturnObject &result, + uint32_t &index, const std::string &arg, + uint32_t &parsed_result) { + char *endptr; + if (!command[++index]) { + result.Printf("error: option \"%s\" requires an argument\n", arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + errno = 0; + unsigned long output = strtoul(command[index], &endptr, 0); + if ((errno != 0) || (*endptr != '\0')) { + result.Printf("error: invalid value \"%s\" provided for option \"%s\"\n", + command[index], arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + if (output > UINT32_MAX) { + result.Printf("error: value \"%s\" for option \"%s\" exceeds UINT32_MAX\n", + command[index], arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + parsed_result = (uint32_t)output; + return true; +} + +static bool ParseCommandArgThread(char **command, + lldb::SBCommandReturnObject &result, + lldb::SBProcess &process, uint32_t &index, + lldb::tid_t &thread_id) { + char *endptr; + if (!strcmp(command[index], "all")) + thread_id = LLDB_INVALID_THREAD_ID; + else { + uint32_t thread_index_id; + errno = 0; + unsigned long output = strtoul(command[index], &endptr, 0); + if ((errno != 0) || (*endptr != '\0') || (output > UINT32_MAX)) { + result.Printf("error: invalid thread specification: \"%s\"\n", + command[index]); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_index_id = (uint32_t)output; + + lldb::SBThread thread = process.GetThreadByIndexID(thread_index_id); + if (!thread.IsValid()) { + result.Printf( + "error: process has no thread with thread specification: \"%s\"\n", + command[index]); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + return true; +} + +class ProcessorTraceStart : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceStart(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceStart() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + // Default initialize API's arguments + lldb::SBTraceOptions lldb_SBTraceOptions; + uint32_t trace_buffer_size = m_default_trace_buff_size; + lldb::tid_t thread_id; + + // Parse Command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + if (!strcmp(command[i], "-b")) { + if (!ParseCommandOption(command, result, i, "-b", trace_buffer_size)) + return false; + } else { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + if (trace_buffer_size > m_max_trace_buff_size) + trace_buffer_size = m_max_trace_buff_size; + + // Set API's arguments with parsed values + lldb_SBTraceOptions.setType(lldb::TraceType::eTraceTypeProcessorTrace); + lldb_SBTraceOptions.setTraceBufferSize(trace_buffer_size); + lldb_SBTraceOptions.setMetaDataBufferSize(0); + lldb_SBTraceOptions.setThreadID(thread_id); + lldb::SBStream sb_stream; + sb_stream.Printf("{\"trace-tech\":\"intel-pt\"}"); + lldb::SBStructuredData custom_params; + lldb::SBError error = custom_params.SetFromJSON(sb_stream); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + lldb_SBTraceOptions.setTraceParams(custom_params); + + // Start trace + pt_decoder_sp->StartProcessorTrace(process, lldb_SBTraceOptions, error); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; + const uint32_t m_max_trace_buff_size = 0x3fff; + const uint32_t m_default_trace_buff_size = 4096; +}; + +class ProcessorTraceInfo : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceInfo(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceInfo() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + size_t loop_count = 1; + bool entire_process_tracing = false; + if (thread_id == LLDB_INVALID_THREAD_ID) { + entire_process_tracing = true; + loop_count = process.GetNumThreads(); + } + + // Get trace information + lldb::SBError error; + lldb::SBCommandReturnObject res; + for (size_t i = 0; i < loop_count; i++) { + error.Clear(); + res.Clear(); + + if (entire_process_tracing) + thread = process.GetThreadAtIndex(i); + else + thread = process.GetThreadByID(thread_id); + thread_id = thread.GetThreadID(); + + ptdecoder::PTTraceOptions options; + pt_decoder_sp->GetProcessorTraceInfo(process, thread_id, options, error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + lldb::SBStructuredData data = options.GetTraceParams(error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + lldb::SBStream s; + error = data.GetAsJSON(s); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 + ", trace buffer size=%" PRIu64 ", meta buffer size=%" PRIu64 + ", trace type=%" PRIu32 ", custom trace params=%s", + thread.GetIndexID(), thread_id, options.GetTraceBufferSize(), + options.GetMetaDataBufferSize(), options.GetType(), + s.GetData()); + result.AppendMessage(res.GetOutput()); + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; +}; + +class ProcessorTraceShowInstrLog : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceShowInstrLog(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceShowInstrLog() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + // Default initialize API's arguments + uint32_t offset; + bool offset_provided = false; + uint32_t count = m_default_count; + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + if (!strcmp(command[i], "-o")) { + if (!ParseCommandOption(command, result, i, "-o", offset)) + return false; + offset_provided = true; + } else if (!strcmp(command[i], "-c")) { + if (!ParseCommandOption(command, result, i, "-c", count)) + return false; + } else { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + size_t loop_count = 1; + bool entire_process_tracing = false; + if (thread_id == LLDB_INVALID_THREAD_ID) { + entire_process_tracing = true; + loop_count = process.GetNumThreads(); + } + + // Get instruction log and disassemble it + lldb::SBError error; + lldb::SBCommandReturnObject res; + for (size_t i = 0; i < loop_count; i++) { + error.Clear(); + res.Clear(); + + if (entire_process_tracing) + thread = process.GetThreadAtIndex(i); + else + thread = process.GetThreadByID(thread_id); + thread_id = thread.GetThreadID(); + + // If offset is not provided then calculate a default offset (to display + // last 'count' number of instructions) + if (!offset_provided) + offset = count - 1; + + // Get the instruction log + ptdecoder::PTInstructionList insn_list; + pt_decoder_sp->GetInstructionLogAtOffset(process, thread_id, offset, + count, insn_list, error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + // Disassemble the instruction log + std::string disassembler_command("dis -c 1 -s "); + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 "\n", thread.GetIndexID(), + thread_id); + lldb::SBCommandInterpreter sb_cmnd_interpreter( + debugger.GetCommandInterpreter()); + lldb::SBCommandReturnObject result_obj; + for (size_t i = 0; i < insn_list.GetSize(); i++) { + ptdecoder::PTInstruction insn = insn_list.GetInstructionAtIndex(i); + uint64_t addr = insn.GetInsnAddress(); + std::string error = insn.GetError(); + if (!error.empty()) { + res.AppendMessage(error.c_str()); + continue; + } + + result_obj.Clear(); + std::string complete_disassembler_command = + disassembler_command + std::to_string(addr); + sb_cmnd_interpreter.HandleCommand(complete_disassembler_command.c_str(), + result_obj, false); + std::string result_str(result_obj.GetOutput()); + if (result_str.empty()) { + lldb::SBCommandReturnObject output; + output.Printf(" Disassembly not found for address: %" PRIu64, addr); + res.AppendMessage(output.GetOutput()); + continue; + } + + // LLDB's disassemble command displays assembly instructions along with + // the names of the functions they belong to. Parse this result to + // display only the assembly instructions and not the function names + // in an instruction log + std::size_t first_new_line_index = result_str.find_first_of('\n'); + std::size_t last_new_line_index = result_str.find_last_of('\n'); + if (first_new_line_index != last_new_line_index) + res.AppendMessage((result_str.substr(first_new_line_index + 1, + last_new_line_index - + first_new_line_index - 1)) + .c_str()); + else + res.AppendMessage( + (result_str.substr(0, result_str.length() - 1)).c_str()); + } + result.AppendMessage(res.GetOutput()); + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; + const uint32_t m_default_count = 10; +}; + +class ProcessorTraceStop : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceStop(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceStop() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + // Stop trace + lldb::SBError error; + pt_decoder_sp->StopProcessorTrace(process, error, thread_id); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + return true; + } + +private: + std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp; +}; + +bool PTPluginInitialize(lldb::SBDebugger &debugger) { + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + lldb::SBCommand proc_trace = interpreter.AddMultiwordCommand( + "processor-trace", "Intel(R) Processor Trace for thread/process"); + + std::shared_ptr<ptdecoder::PTDecoder> PTDecoderSP( + new ptdecoder::PTDecoder(debugger)); + + lldb::SBCommandPluginInterface *proc_trace_start = + new ProcessorTraceStart(PTDecoderSP); + const char *help_proc_trace_start = "start Intel(R) Processor Trace on a " + "specific thread or on the whole process"; + const char *syntax_proc_trace_start = + "processor-trace start <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace start [-b <buffer-size>] [<thread-index>]\n\n" + "\t\b-b <buffer-size>\n" + "\t size of the trace buffer to store the trace data. If not " + "specified then a default value will be taken\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to start tracing the whole process\n"; + proc_trace.AddCommand("start", proc_trace_start, help_proc_trace_start, + syntax_proc_trace_start); + + lldb::SBCommandPluginInterface *proc_trace_stop = + new ProcessorTraceStop(PTDecoderSP); + const char *help_proc_trace_stop = + "stop Intel(R) Processor Trace on a specific thread or on whole process"; + const char *syntax_proc_trace_stop = + "processor-trace stop <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace stop [<thread-index>]\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to stop tracing the whole process\n"; + proc_trace.AddCommand("stop", proc_trace_stop, help_proc_trace_stop, + syntax_proc_trace_stop); + + lldb::SBCommandPluginInterface *proc_trace_show_instr_log = + new ProcessorTraceShowInstrLog(PTDecoderSP); + const char *help_proc_trace_show_instr_log = + "display a log of assembly instructions executed for a specific thread " + "or for the whole process.\n" + "The length of the log to be displayed and the offset in the whole " + "instruction log from where the log needs to be displayed can also be " + "provided. The offset is counted from the end of this whole " + "instruction log which means the last executed instruction is at offset " + "0 (zero)"; + const char *syntax_proc_trace_show_instr_log = + "processor-trace show-instr-log <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace show-instr-log [-o <offset>] [-c <count>] " + "[<thread-index>]\n\n" + "\t\b-o <offset>\n" + "\t offset in the whole instruction log from where the log will be " + "displayed. If not specified then a default value will be taken\n\n" + "\t\b-c <count>\n" + "\t number of instructions to be displayed. If not specified then a " + "default value will be taken\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to show instruction log for all the " + "threads of the process\n"; + proc_trace.AddCommand("show-instr-log", proc_trace_show_instr_log, + help_proc_trace_show_instr_log, + syntax_proc_trace_show_instr_log); + + lldb::SBCommandPluginInterface *proc_trace_options = + new ProcessorTraceInfo(PTDecoderSP); + const char *help_proc_trace_show_options = + "display all the information regarding Intel(R) Processor Trace for a " + "specific thread or for the whole process.\n" + "The information contains trace buffer size and configuration options" + " of Intel(R) Processor Trace."; + const char *syntax_proc_trace_show_options = + "processor-trace show-options <cmd-options>\n\n" + "\rcmd-options Usage:\n" + "\r processor-trace show-options [<thread-index>]\n\n" + "\t\b<thread-index>\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to display information for all threads " + "of the process\n"; + proc_trace.AddCommand("show-trace-options", proc_trace_options, + help_proc_trace_show_options, + syntax_proc_trace_show_options); + + return true; +} diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.h b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.h new file mode 100644 index 00000000000..3d2fe69b015 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.h @@ -0,0 +1,12 @@ +//===-- cli-wrapper-pt.h----------------------------------*- C++ -*-==========// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// CLI Wrapper of PTDecoder Tool to enable it to be used through LLDB's CLI. +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBDebugger.h" + +bool PTPluginInitialize(lldb::SBDebugger &debugger); diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/interface/PTDecoder.i b/gnu/llvm/lldb/tools/intel-features/intel-pt/interface/PTDecoder.i new file mode 100644 index 00000000000..f662b8ff359 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/interface/PTDecoder.i @@ -0,0 +1,10 @@ +%include "stdint.i" + +%include "lldb/lldb-defines.h" +%include "lldb/lldb-enumerations.h" +%include "lldb/lldb-forward.h" +%include "lldb/lldb-types.h" + +%include "lldb/API/SBDefines.h" + +%include "../PTDecoder.h" diff --git a/gnu/llvm/lldb/tools/intel-features/scripts/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/scripts/CMakeLists.txt new file mode 100644 index 00000000000..6df97a4b006 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/scripts/CMakeLists.txt @@ -0,0 +1,37 @@ +file(GLOB_RECURSE SWIG_SOURCES *.swig) + +set(FLAGS + -c++ + -shadow + -python + -D__STDC_LIMIT_MACROS + -D__STDC_CONSTANT_MACROS + ) + +set(INCLUDES + -I${LLDB_SOURCE_DIR}/include + -I${LLDB_SOURCE_DIR}/tools/intel-features/intel-pt + ) + +set(OUTPUT_PYTHON_WRAPPER + ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + ) + +set(OUTPUT_PYTHON_SCRIPT_DIR + ${CMAKE_CURRENT_BINARY_DIR} + ) + +find_package(SWIG REQUIRED) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py + DEPENDS ${SWIG_SOURCES} + COMMAND ${SWIG_EXECUTABLE} ${FLAGS} ${INCLUDES} -o ${OUTPUT_PYTHON_WRAPPER} -outdir ${OUTPUT_PYTHON_SCRIPT_DIR} ${SWIG_SOURCES} + COMMENT "Generating python wrapper for features library") + +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp PROPERTIES GENERATED 1) +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py PROPERTIES GENERATED 1) + +add_custom_target(intel-features-swig_wrapper ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + ) diff --git a/gnu/llvm/lldb/tools/intel-features/scripts/lldb-intel-features.swig b/gnu/llvm/lldb/tools/intel-features/scripts/lldb-intel-features.swig new file mode 100644 index 00000000000..c035fb6132d --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/scripts/lldb-intel-features.swig @@ -0,0 +1,16 @@ +%module lldbIntelFeatures + +%{ +#include "lldb/lldb-public.h" +#include "intel-pt/PTDecoder.h" +using namespace ptdecoder; +%} + +/* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h */ +#define __extension__ + +/* Combined python typemap for all features */ +%include "python-typemaps.txt" + +/* Feature specific python interface files*/ +%include "../intel-pt/interface/PTDecoder.i" diff --git a/gnu/llvm/lldb/tools/intel-features/scripts/python-typemaps.txt b/gnu/llvm/lldb/tools/intel-features/scripts/python-typemaps.txt new file mode 100644 index 00000000000..888d5321393 --- /dev/null +++ b/gnu/llvm/lldb/tools/intel-features/scripts/python-typemaps.txt @@ -0,0 +1,31 @@ +/* Typemap definitions to allow SWIG to properly handle some data types */ + +// typemap for an incoming buffer +%typemap(in) (void *buf, size_t size) { + if (PyInt_Check($input)) { + $2 = PyInt_AsLong($input); + } else if (PyLong_Check($input)) { + $2 = PyLong_AsLong($input); + } else { + PyErr_SetString(PyExc_ValueError, "Expecting an integer or long object"); + return NULL; + } + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (void *) malloc($2); +} + +// Return the buffer. Discarding any previous return result +%typemap(argout) (void *buf, size_t size) { + Py_XDECREF($result); /* Blow away any previous result */ + if (result == 0) { + $result = Py_None; + Py_INCREF($result); + } else { + PyObject *py_bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char*>($1), result); + $result = py_bytes; + } + free($1); +} diff --git a/gnu/llvm/lldb/tools/lldb-instr/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-instr/CMakeLists.txt new file mode 100644 index 00000000000..8da453b2894 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-instr/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lldb_tool(lldb-instr + Instrument.cpp + + CLANG_LIBS + clangAST + clangBasic + clangCodeGen + clangFrontend + clangLex + clangRewrite + clangSerialization + clangTooling + + LINK_COMPONENTS + Support + ) diff --git a/gnu/llvm/lldb/tools/lldb-instr/Instrument.cpp b/gnu/llvm/lldb/tools/lldb-instr/Instrument.cpp new file mode 100644 index 00000000000..9b2970030cb --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-instr/Instrument.cpp @@ -0,0 +1,356 @@ +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include <sstream> +#include <string> + +using namespace clang; +using namespace clang::driver; +using namespace clang::tooling; + +static llvm::cl::OptionCategory InstrCategory("LLDB Instrumentation Generator"); + +/// Get the macro name for recording method calls. +/// +/// LLDB_RECORD_METHOD +/// LLDB_RECORD_METHOD_CONST +/// LLDB_RECORD_METHOD_NO_ARGS +/// LLDB_RECORD_METHOD_CONST_NO_ARGS +/// LLDB_RECORD_STATIC_METHOD +/// LLDB_RECORD_STATIC_METHOD_NO_ARGS +static std::string GetRecordMethodMacroName(bool Static, bool Const, + bool NoArgs) { + std::string Macro; + llvm::raw_string_ostream OS(Macro); + + OS << "LLDB_RECORD"; + if (Static) + OS << "_STATIC"; + OS << "_METHOD"; + if (Const) + OS << "_CONST"; + if (NoArgs) + OS << "_NO_ARGS"; + + return OS.str(); +} + +/// Get the macro name for register methods. +/// +/// LLDB_REGISTER_CONSTRUCTOR +/// LLDB_REGISTER_METHOD +/// LLDB_REGISTER_METHOD_CONST +/// LLDB_REGISTER_STATIC_METHOD +static std::string GetRegisterMethodMacroName(bool Static, bool Const) { + std::string Macro; + llvm::raw_string_ostream OS(Macro); + + OS << "LLDB_REGISTER"; + if (Static) + OS << "_STATIC"; + OS << "_METHOD"; + if (Const) + OS << "_CONST"; + + return OS.str(); +} + +static std::string GetRecordMethodMacro(StringRef Result, StringRef Class, + StringRef Method, StringRef Signature, + StringRef Values, bool Static, + bool Const) { + std::string Macro; + llvm::raw_string_ostream OS(Macro); + + OS << GetRecordMethodMacroName(Static, Const, Values.empty()); + OS << "(" << Result << ", " << Class << ", " << Method; + + if (!Values.empty()) { + OS << ", (" << Signature << "), " << Values << ");\n\n"; + } else { + OS << ");\n\n"; + } + + return OS.str(); +} + +static std::string GetRecordConstructorMacro(StringRef Class, + StringRef Signature, + StringRef Values) { + std::string Macro; + llvm::raw_string_ostream OS(Macro); + if (!Values.empty()) { + OS << "LLDB_RECORD_CONSTRUCTOR(" << Class << ", (" << Signature << "), " + << Values << ");\n\n"; + } else { + OS << "LLDB_RECORD_CONSTRUCTOR_NO_ARGS(" << Class << ");\n\n"; + } + return OS.str(); +} + +static std::string GetRecordDummyMacro(StringRef Result, StringRef Class, + StringRef Method, StringRef Signature, + StringRef Values) { + assert(!Values.empty()); + std::string Macro; + llvm::raw_string_ostream OS(Macro); + + OS << "LLDB_RECORD_DUMMY(" << Result << ", " << Class << ", " << Method; + OS << ", (" << Signature << "), " << Values << ");\n\n"; + + return OS.str(); +} + +static std::string GetRegisterConstructorMacro(StringRef Class, + StringRef Signature) { + std::string Macro; + llvm::raw_string_ostream OS(Macro); + OS << "LLDB_REGISTER_CONSTRUCTOR(" << Class << ", (" << Signature << "));\n"; + return OS.str(); +} + +static std::string GetRegisterMethodMacro(StringRef Result, StringRef Class, + StringRef Method, StringRef Signature, + bool Static, bool Const) { + std::string Macro; + llvm::raw_string_ostream OS(Macro); + OS << GetRegisterMethodMacroName(Static, Const); + OS << "(" << Result << ", " << Class << ", " << Method << ", (" << Signature + << "));\n"; + return OS.str(); +} + +class SBReturnVisitor : public RecursiveASTVisitor<SBReturnVisitor> { +public: + SBReturnVisitor(Rewriter &R) : MyRewriter(R) {} + + bool VisitReturnStmt(ReturnStmt *Stmt) { + Expr *E = Stmt->getRetValue(); + + if (E->getBeginLoc().isMacroID()) + return false; + + SourceRange R(E->getBeginLoc(), E->getEndLoc()); + + StringRef WrittenExpr = Lexer::getSourceText( + CharSourceRange::getTokenRange(R), MyRewriter.getSourceMgr(), + MyRewriter.getLangOpts()); + + std::string ReplacementText = + "LLDB_RECORD_RESULT(" + WrittenExpr.str() + ")"; + MyRewriter.ReplaceText(R, ReplacementText); + + return true; + } + +private: + Rewriter &MyRewriter; +}; + +class SBVisitor : public RecursiveASTVisitor<SBVisitor> { +public: + SBVisitor(Rewriter &R, ASTContext &Context) + : MyRewriter(R), Context(Context) {} + + bool VisitCXXMethodDecl(CXXMethodDecl *Decl) { + // Not all decls should be registered. Please refer to that method's + // comment for details. + if (ShouldSkip(Decl)) + return false; + + // Skip CXXMethodDecls that already starts with a macro. This should make + // it easier to rerun the tool to find missing macros. + Stmt *Body = Decl->getBody(); + for (auto &C : Body->children()) { + if (C->getBeginLoc().isMacroID()) + return false; + break; + } + + // Print 'bool' instead of '_Bool'. + PrintingPolicy Policy(Context.getLangOpts()); + Policy.Bool = true; + + // Unsupported signatures get a dummy macro. + bool ShouldInsertDummy = false; + + // Collect the functions parameter types and names. + std::vector<std::string> ParamTypes; + std::vector<std::string> ParamNames; + for (auto *P : Decl->parameters()) { + QualType T = P->getType(); + ParamTypes.push_back(T.getAsString(Policy)); + ParamNames.push_back(P->getNameAsString()); + + // Currently we don't support functions that have void pointers or + // function pointers as an argument, in which case we insert a dummy + // macro. + ShouldInsertDummy |= T->isFunctionPointerType() || T->isVoidPointerType(); + } + + // Convert the two lists to string for the macros. + std::string ParamTypesStr = llvm::join(ParamTypes, ", "); + std::string ParamNamesStr = llvm::join(ParamNames, ", "); + + CXXRecordDecl *Record = Decl->getParent(); + QualType ReturnType = Decl->getReturnType(); + + // Construct the macros. + std::string Macro; + if (ShouldInsertDummy) { + // Don't insert a register call for dummy macros. + Macro = GetRecordDummyMacro( + ReturnType.getAsString(Policy), Record->getNameAsString(), + Decl->getNameAsString(), ParamTypesStr, ParamNamesStr); + + } else if (isa<CXXConstructorDecl>(Decl)) { + llvm::outs() << GetRegisterConstructorMacro(Record->getNameAsString(), + ParamTypesStr); + + Macro = GetRecordConstructorMacro(Record->getNameAsString(), + ParamTypesStr, ParamNamesStr); + } else { + llvm::outs() << GetRegisterMethodMacro( + ReturnType.getAsString(Policy), Record->getNameAsString(), + Decl->getNameAsString(), ParamTypesStr, Decl->isStatic(), + Decl->isConst()); + + Macro = GetRecordMethodMacro( + ReturnType.getAsString(Policy), Record->getNameAsString(), + Decl->getNameAsString(), ParamTypesStr, ParamNamesStr, + Decl->isStatic(), Decl->isConst()); + } + + // Insert the macro at the beginning of the function. We don't attempt to + // fix the formatting and instead rely on clang-format to fix it after the + // tool has run. This is also the reason that the macros end with two + // newlines, counting on clang-format to normalize this in case the macro + // got inserted before an existing newline. + SourceLocation InsertLoc = Lexer::getLocForEndOfToken( + Body->getBeginLoc(), 0, MyRewriter.getSourceMgr(), + MyRewriter.getLangOpts()); + MyRewriter.InsertTextAfter(InsertLoc, Macro); + + // If the function returns a class or struct, we need to wrap its return + // statement(s). + bool ShouldRecordResult = ReturnType->isStructureOrClassType() || + ReturnType->getPointeeCXXRecordDecl(); + if (!ShouldInsertDummy && ShouldRecordResult) { + SBReturnVisitor Visitor(MyRewriter); + Visitor.TraverseDecl(Decl); + } + + return true; + } + +private: + /// Determine whether we need to consider the given CXXMethodDecl. + /// + /// Currently we skip the following cases: + /// 1. Decls outside the main source file, + /// 2. Decls that are only present in the source file, + /// 3. Decls that are not definitions, + /// 4. Non-public methods, + /// 5. Variadic methods. + /// 6. Destructors. + bool ShouldSkip(CXXMethodDecl *Decl) { + // Skip anything outside the main file. + if (!MyRewriter.getSourceMgr().isInMainFile(Decl->getBeginLoc())) + return true; + + // Skip if the canonical decl in the current decl. It means that the method + // is declared in the implementation and is therefore not exposed as part + // of the API. + if (Decl == Decl->getCanonicalDecl()) + return true; + + // Skip decls that have no body, i.e. are just declarations. + Stmt *Body = Decl->getBody(); + if (!Body) + return true; + + // Skip non-public methods. + AccessSpecifier AS = Decl->getAccess(); + if (AS != AccessSpecifier::AS_public) + return true; + + // Skip variadic methods. + if (Decl->isVariadic()) + return true; + + // Skip destructors. + if (isa<CXXDestructorDecl>(Decl)) + return true; + + return false; + } + + Rewriter &MyRewriter; + ASTContext &Context; +}; + +class SBConsumer : public ASTConsumer { +public: + SBConsumer(Rewriter &R, ASTContext &Context) : Visitor(R, Context) {} + + // Override the method that gets called for each parsed top-level + // declaration. + bool HandleTopLevelDecl(DeclGroupRef DR) override { + for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) { + Visitor.TraverseDecl(*b); + } + return true; + } + +private: + SBVisitor Visitor; +}; + +class SBAction : public ASTFrontendAction { +public: + SBAction() = default; + + bool BeginSourceFileAction(CompilerInstance &CI) override { + llvm::outs() << "{\n"; + return true; + } + + void EndSourceFileAction() override { + llvm::outs() << "}\n"; + MyRewriter.overwriteChangedFiles(); + } + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef File) override { + MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); + return std::make_unique<SBConsumer>(MyRewriter, CI.getASTContext()); + } + +private: + Rewriter MyRewriter; +}; + +int main(int argc, const char **argv) { + CommonOptionsParser OP(argc, argv, InstrCategory, + "Utility for generating the macros for LLDB's " + "instrumentation framework."); + + auto PCHOpts = std::make_shared<PCHContainerOperations>(); + PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>()); + PCHOpts->registerReader(std::make_unique<ObjectFilePCHContainerReader>()); + + ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts); + return T.run(newFrontendActionFactory<SBAction>().get()); +} diff --git a/gnu/llvm/lldb/tools/lldb-mi/lldb-mi.exports b/gnu/llvm/lldb/tools/lldb-mi/lldb-mi.exports new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-mi/lldb-mi.exports diff --git a/gnu/llvm/lldb/tools/lldb-perf/darwin/sketch/foobar.sketch2 b/gnu/llvm/lldb/tools/lldb-perf/darwin/sketch/foobar.sketch2 Binary files differnew file mode 100644 index 00000000000..553c698b180 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-perf/darwin/sketch/foobar.sketch2 diff --git a/gnu/llvm/lldb/tools/lldb-server/Acceptor.cpp b/gnu/llvm/lldb/tools/lldb-server/Acceptor.cpp new file mode 100644 index 00000000000..2cfb34d215c --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/Acceptor.cpp @@ -0,0 +1,135 @@ +//===-- Acceptor.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Acceptor.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UriParser.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace llvm; + +namespace { + +struct SocketScheme { + const char *m_scheme; + const Socket::SocketProtocol m_protocol; +}; + +SocketScheme socket_schemes[] = { + {"tcp", Socket::ProtocolTcp}, + {"udp", Socket::ProtocolUdp}, + {"unix", Socket::ProtocolUnixDomain}, + {"unix-abstract", Socket::ProtocolUnixAbstract}, +}; + +bool FindProtocolByScheme(const char *scheme, + Socket::SocketProtocol &protocol) { + for (auto s : socket_schemes) { + if (!strcmp(s.m_scheme, scheme)) { + protocol = s.m_protocol; + return true; + } + } + return false; +} + +const char *FindSchemeByProtocol(const Socket::SocketProtocol protocol) { + for (auto s : socket_schemes) { + if (s.m_protocol == protocol) + return s.m_scheme; + } + return nullptr; +} +} + +Status Acceptor::Listen(int backlog) { + return m_listener_socket_up->Listen(StringRef(m_name), backlog); +} + +Status Acceptor::Accept(const bool child_processes_inherit, Connection *&conn) { + Socket *conn_socket = nullptr; + auto error = m_listener_socket_up->Accept(conn_socket); + if (error.Success()) + conn = new ConnectionFileDescriptor(conn_socket); + + return error; +} + +Socket::SocketProtocol Acceptor::GetSocketProtocol() const { + return m_listener_socket_up->GetSocketProtocol(); +} + +const char *Acceptor::GetSocketScheme() const { + return FindSchemeByProtocol(GetSocketProtocol()); +} + +std::string Acceptor::GetLocalSocketId() const { return m_local_socket_id(); } + +std::unique_ptr<Acceptor> Acceptor::Create(StringRef name, + const bool child_processes_inherit, + Status &error) { + error.Clear(); + + Socket::SocketProtocol socket_protocol = Socket::ProtocolUnixDomain; + int port; + StringRef scheme, host, path; + // Try to match socket name as URL - e.g., tcp://localhost:5555 + if (UriParser::Parse(name, scheme, host, port, path)) { + if (!FindProtocolByScheme(scheme.str().c_str(), socket_protocol)) + error.SetErrorStringWithFormat("Unknown protocol scheme \"%s\"", + scheme.str().c_str()); + else + name = name.drop_front(scheme.size() + strlen("://")); + } else { + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + // Try to match socket name as $host:port - e.g., localhost:5555 + if (Socket::DecodeHostAndPort(name, host_str, port_str, port, nullptr)) + socket_protocol = Socket::ProtocolTcp; + } + + if (error.Fail()) + return std::unique_ptr<Acceptor>(); + + std::unique_ptr<Socket> listener_socket_up = + Socket::Create(socket_protocol, child_processes_inherit, error); + + LocalSocketIdFunc local_socket_id; + if (error.Success()) { + if (listener_socket_up->GetSocketProtocol() == Socket::ProtocolTcp) { + TCPSocket *tcp_socket = + static_cast<TCPSocket *>(listener_socket_up.get()); + local_socket_id = [tcp_socket]() { + auto local_port = tcp_socket->GetLocalPortNumber(); + return (local_port != 0) ? llvm::to_string(local_port) : ""; + }; + } else { + const std::string socket_name = name; + local_socket_id = [socket_name]() { return socket_name; }; + } + + return std::unique_ptr<Acceptor>( + new Acceptor(std::move(listener_socket_up), name, local_socket_id)); + } + + return std::unique_ptr<Acceptor>(); +} + +Acceptor::Acceptor(std::unique_ptr<Socket> &&listener_socket, StringRef name, + const LocalSocketIdFunc &local_socket_id) + : m_listener_socket_up(std::move(listener_socket)), m_name(name.str()), + m_local_socket_id(local_socket_id) {} diff --git a/gnu/llvm/lldb/tools/lldb-server/Acceptor.h b/gnu/llvm/lldb/tools/lldb-server/Acceptor.h new file mode 100644 index 00000000000..1e7337f1411 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/Acceptor.h @@ -0,0 +1,60 @@ +//===-- Acceptor.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef lldb_server_Acceptor_h_ +#define lldb_server_Acceptor_h_ + +#include "lldb/Host/Socket.h" +#include "lldb/Utility/Connection.h" +#include "lldb/Utility/Status.h" + +#include <functional> +#include <memory> +#include <string> + +namespace llvm { +class StringRef; +} + +namespace lldb_private { +namespace lldb_server { + +class Acceptor { +public: + virtual ~Acceptor() = default; + + Status Listen(int backlog); + + Status Accept(const bool child_processes_inherit, Connection *&conn); + + static std::unique_ptr<Acceptor> Create(llvm::StringRef name, + const bool child_processes_inherit, + Status &error); + + Socket::SocketProtocol GetSocketProtocol() const; + + const char *GetSocketScheme() const; + + // Returns either TCP port number as string or domain socket path. + // Empty string is returned in case of error. + std::string GetLocalSocketId() const; + +private: + typedef std::function<std::string()> LocalSocketIdFunc; + + Acceptor(std::unique_ptr<Socket> &&listener_socket, llvm::StringRef name, + const LocalSocketIdFunc &local_socket_id); + + const std::unique_ptr<Socket> m_listener_socket_up; + const std::string m_name; + const LocalSocketIdFunc m_local_socket_id; +}; + +} // namespace lldb_server +} // namespace lldb_private + +#endif // lldb_server_Acceptor_h_ diff --git a/gnu/llvm/lldb/tools/lldb-server/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-server/CMakeLists.txt new file mode 100644 index 00000000000..739bdc3c422 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/CMakeLists.txt @@ -0,0 +1,58 @@ +set(LLDB_PLUGINS) + +if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android") + list(APPEND LLDB_PLUGINS lldbPluginProcessLinux) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "NetBSD") + list(APPEND LLDB_PLUGINS lldbPluginProcessNetBSD) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + list(APPEND LLDB_PLUGINS lldbPluginProcessOpenBSD) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + list(APPEND LLDB_PLUGINS lldbPluginObjectFileMachO) +elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") + list(APPEND LLDB_PLUGINS lldbPluginObjectFilePECOFF) +else() + list(APPEND LLDB_PLUGINS lldbPluginObjectFileELF) +endif() + +if(IOS) + if(LLDB_CODESIGN_IDENTITY) + # Use explicit LLDB identity + set(LLVM_CODESIGNING_IDENTITY ${LLDB_CODESIGN_IDENTITY}) + else() + # Use explicit LLVM identity or default to lldb_codesign if empty + if(NOT LLVM_CODESIGNING_IDENTITY) + set(LLVM_CODESIGNING_IDENTITY lldb_codesign) + endif() + endif() +endif() + +add_lldb_tool(lldb-server + Acceptor.cpp + lldb-gdbserver.cpp + lldb-platform.cpp + lldb-server.cpp + LLDBServerUtilities.cpp + SystemInitializerLLGS.cpp + + LINK_LIBS + lldbBase + lldbHost + lldbInitialization + ${LLDB_PLUGINS} + lldbPluginInstructionARM + lldbPluginInstructionMIPS + lldbPluginInstructionMIPS64 + ${LLDB_SYSTEM_LIBS} + + LINK_COMPONENTS + Support +) + +target_include_directories(lldb-server PRIVATE "${LLDB_SOURCE_DIR}/source") +target_link_libraries(lldb-server PRIVATE ${LLDB_SYSTEM_LIBS}) diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist new file mode 100644 index 00000000000..58a34ca5e43 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-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-server</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>lldb-server</string> + <key>CFBundleVersion</key> + <string>2</string> + <key>SecTaskAccess</key> + <array> + <string>allowed</string> + <string>debug</string> + </array> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist new file mode 100644 index 00000000000..e05a9baa5d0 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-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.private.logging.diagnostic</key> + <true/> + <key>com.apple.security.network.server</key> + <true/> + <key>com.apple.security.network.client</key> + <true/> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist new file mode 100644 index 00000000000..edf79b3b3ee --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-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.private.logging.diagnostic</key> + <true/> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs new file mode 100644 index 00000000000..cd5be170070 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs @@ -0,0 +1,5 @@ +/* + * nub.defs + */ + +#import <mach/mach_exc.defs> diff --git a/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.cpp b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.cpp new file mode 100644 index 00000000000..7f9271e36b6 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.cpp @@ -0,0 +1,63 @@ +//===-- LLDBServerUtilities.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LLDBServerUtilities.h" + +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" + +using namespace lldb; +using namespace lldb_private::lldb_server; +using namespace llvm; + +static std::shared_ptr<raw_ostream> GetLogStream(StringRef log_file) { + if (!log_file.empty()) { + std::error_code EC; + std::shared_ptr<raw_ostream> stream_sp = std::make_shared<raw_fd_ostream>( + log_file, EC, sys::fs::OF_Text | sys::fs::OF_Append); + if (!EC) + return stream_sp; + errs() << llvm::formatv( + "Failed to open log file `{0}`: {1}\nWill log to stderr instead.\n", + log_file, EC.message()); + } + // No need to delete the stderr stream. + return std::shared_ptr<raw_ostream>(&errs(), [](raw_ostream *) {}); +} + +bool LLDBServerUtilities::SetupLogging(const std::string &log_file, + const StringRef &log_channels, + uint32_t log_options) { + + auto log_stream_sp = GetLogStream(log_file); + + SmallVector<StringRef, 32> channel_array; + log_channels.split(channel_array, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false); + for (auto channel_with_categories : channel_array) { + std::string error; + llvm::raw_string_ostream error_stream(error); + Args channel_then_categories(channel_with_categories); + std::string channel(channel_then_categories.GetArgumentAtIndex(0)); + channel_then_categories.Shift(); // Shift off the channel + + bool success = Log::EnableLogChannel( + log_stream_sp, log_options, channel, + channel_then_categories.GetArgumentArrayRef(), error_stream); + if (!success) { + errs() << formatv("Unable to setup logging for channel \"{0}\": {1}", + channel, error_stream.str()); + return false; + } + } + return true; +} diff --git a/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.h b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.h new file mode 100644 index 00000000000..3ade1f9f5b8 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.h @@ -0,0 +1,23 @@ +//===-- LLDBServerUtilities.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" + +#include <string> + +namespace lldb_private { +namespace lldb_server { + +class LLDBServerUtilities { +public: + static bool SetupLogging(const std::string &log_file, + const llvm::StringRef &log_channels, + uint32_t log_options); +}; +} +} diff --git a/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.cpp new file mode 100644 index 00000000000..b93e6b40dcd --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -0,0 +1,79 @@ +//===-- SystemInitializerLLGS.cpp -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SystemInitializerLLGS.h" + +#if defined(__APPLE__) +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" +using HostObjectFile = ObjectFileMachO; +#elif defined(_WIN32) +#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +using HostObjectFile = ObjectFilePECOFF; +#else +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +using HostObjectFile = ObjectFileELF; +#endif + +#if defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define LLDB_TARGET_ARM64 +#endif + +#if defined(__arm__) || defined(__arm) || defined(_ARM) || defined(_M_ARM) || \ + defined(LLDB_TARGET_ARM64) +#define LLDB_TARGET_ARM +#include "Plugins/Instruction/ARM/EmulateInstructionARM.h" +#endif + +#if defined(__mips64__) || defined(mips64) || defined(__mips64) || \ + defined(__MIPS64__) || defined(_M_MIPS64) +#define LLDB_TARGET_MIPS64 +#include "Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h" +#endif + +#if defined(__mips__) || defined(mips) || defined(__mips) || \ + defined(__MIPS__) || defined(_M_MIPS) || defined(LLDB_TARGET_MIPS64) +#define LLDB_TARGET_MIPS +#include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h" +#endif + +using namespace lldb_private; + +llvm::Error SystemInitializerLLGS::Initialize() { + if (auto e = SystemInitializerCommon::Initialize()) + return e; + + HostObjectFile::Initialize(); + +#if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64) + EmulateInstructionARM::Initialize(); +#endif +#if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS::Initialize(); +#endif +#if defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS64::Initialize(); +#endif + + return llvm::Error::success(); +} + +void SystemInitializerLLGS::Terminate() { + HostObjectFile::Terminate(); + +#if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64) + EmulateInstructionARM::Terminate(); +#endif +#if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS::Terminate(); +#endif +#if defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS64::Terminate(); +#endif + + SystemInitializerCommon::Terminate(); +} diff --git a/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.h b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.h new file mode 100644 index 00000000000..59a1fa14e81 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.h @@ -0,0 +1,21 @@ +//===-- SystemInitializerLLGS.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SYSTEMINITIALIZERLLGS_H +#define LLDB_SYSTEMINITIALIZERLLGS_H + +#include "lldb/Initialization/SystemInitializer.h" +#include "lldb/Initialization/SystemInitializerCommon.h" + +class SystemInitializerLLGS : public lldb_private::SystemInitializerCommon { +public: + llvm::Error Initialize() override; + void Terminate() override; +}; + +#endif // LLDB_SYSTEMINITIALIZERLLGS_H diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-gdbserver.cpp b/gnu/llvm/lldb/tools/lldb-server/lldb-gdbserver.cpp new file mode 100644 index 00000000000..9648a6a4426 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -0,0 +1,547 @@ +//===-- lldb-gdbserver.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef _WIN32 +#include <signal.h> +#include <unistd.h> +#endif + + +#include "Acceptor.h" +#include "LLDBServerUtilities.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostGetOpt.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/Status.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Errno.h" + +#if defined(__linux__) +#include "Plugins/Process/Linux/NativeProcessLinux.h" +#elif defined(__NetBSD__) +#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" +#elif defined(__OpenBSD__) +#include "Plugins/Process/OpenBSD/NativeProcessOpenBSD.h" +#elif defined(_WIN32) +#include "Plugins/Process/Windows/Common/NativeProcessWindows.h" +#endif + +#ifndef LLGS_PROGRAM_NAME +#define LLGS_PROGRAM_NAME "lldb-server" +#endif + +#ifndef LLGS_VERSION_STR +#define LLGS_VERSION_STR "local_build" +#endif + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace lldb_private::process_gdb_remote; + +namespace { +#if defined(__linux__) +typedef process_linux::NativeProcessLinux::Factory NativeProcessFactory; +#elif defined(__NetBSD__) +typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory; +#elif defined(__OpenBSD__) +typedef process_openbsd::NativeProcessOpenBSD::Factory NativeProcessFactory; +#elif defined(_WIN32) +typedef NativeProcessWindows::Factory NativeProcessFactory; +#else +// Dummy implementation to make sure the code compiles +class NativeProcessFactory : public NativeProcessProtocol::Factory { +public: + llvm::Expected<std::unique_ptr<NativeProcessProtocol>> + Launch(ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &delegate, + MainLoop &mainloop) const override { + llvm_unreachable("Not implemented"); + } + llvm::Expected<std::unique_ptr<NativeProcessProtocol>> + Attach(lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &delegate, + MainLoop &mainloop) const override { + llvm_unreachable("Not implemented"); + } +}; +#endif +} + +// option descriptors for getopt_long_only() + +static int g_debug = 0; +static int g_verbose = 0; + +static struct option g_long_options[] = { + {"debug", no_argument, &g_debug, 1}, + {"verbose", no_argument, &g_verbose, 1}, + {"log-file", required_argument, nullptr, 'l'}, + {"log-channels", required_argument, nullptr, 'c'}, + {"attach", required_argument, nullptr, 'a'}, + {"named-pipe", required_argument, nullptr, 'N'}, + {"pipe", required_argument, nullptr, 'U'}, + {"native-regs", no_argument, nullptr, + 'r'}, // Specify to use the native registers instead of the gdb defaults + // for the architecture. NOTE: this is a do-nothing arg as it's + // behavior is default now. FIXME remove call from lldb-platform. + {"reverse-connect", no_argument, nullptr, + 'R'}, // Specifies that llgs attaches to the client address:port rather + // than llgs listening for a connection from address on port. + {"setsid", no_argument, nullptr, + 'S'}, // Call setsid() to make llgs run in its own session. + {"fd", required_argument, nullptr, 'F'}, + {nullptr, 0, nullptr, 0}}; + +#ifndef _WIN32 +// Watch for signals +static int g_sighup_received_count = 0; + +static void sighup_handler(MainLoopBase &mainloop) { + ++g_sighup_received_count; + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "lldb-server:%s swallowing SIGHUP (receive count=%d)", + __FUNCTION__, g_sighup_received_count); + + if (g_sighup_received_count >= 2) + mainloop.RequestTermination(); +} +#endif // #ifndef _WIN32 + +static void display_usage(const char *progname, const char *subcommand) { + fprintf(stderr, "Usage:\n %s %s " + "[--log-file log-file-name] " + "[--log-channels log-channel-list] " + "[--setsid] " + "[--fd file-descriptor]" + "[--named-pipe named-pipe-path] " + "[--native-regs] " + "[--attach pid] " + "[[HOST]:PORT] " + "[-- PROGRAM ARG1 ARG2 ...]\n", + progname, subcommand); +} + +void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server, + lldb::pid_t pid) { + Status error = gdb_server.AttachToProcess(pid); + if (error.Fail()) { + fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid, + error.AsCString()); + exit(1); + } +} + +void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server, + const std::string &process_name) { + // FIXME implement. +} + +void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server, + const std::string &attach_target) { + assert(!attach_target.empty() && "attach_target cannot be empty"); + + // First check if the attach_target is convertible to a long. If so, we'll use + // it as a pid. + char *end_p = nullptr; + const long int pid = strtol(attach_target.c_str(), &end_p, 10); + + // We'll call it a match if the entire argument is consumed. + if (end_p && + static_cast<size_t>(end_p - attach_target.c_str()) == + attach_target.size()) + handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid)); + else + handle_attach_to_process_name(gdb_server, attach_target); +} + +void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, int argc, + const char *const argv[]) { + ProcessLaunchInfo info; + info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug | + eLaunchFlagDisableASLR); + info.SetArguments(const_cast<const char **>(argv), true); + + llvm::SmallString<64> cwd; + if (std::error_code ec = llvm::sys::fs::current_path(cwd)) { + llvm::errs() << "Error getting current directory: " << ec.message() << "\n"; + exit(1); + } + FileSpec cwd_spec(cwd); + FileSystem::Instance().Resolve(cwd_spec); + info.SetWorkingDirectory(cwd_spec); + info.GetEnvironment() = Host::GetEnvironment(); + + gdb_server.SetLaunchInfo(info); + + Status error = gdb_server.LaunchProcess(); + if (error.Fail()) { + llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n", + argv[0], error); + exit(1); + } +} + +Status writeSocketIdToPipe(Pipe &port_pipe, const std::string &socket_id) { + size_t bytes_written = 0; + // Write the port number as a C string with the NULL terminator. + return port_pipe.Write(socket_id.c_str(), socket_id.size() + 1, + bytes_written); +} + +Status writeSocketIdToPipe(const char *const named_pipe_path, + const std::string &socket_id) { + Pipe port_name_pipe; + // Wait for 10 seconds for pipe to be opened. + auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false, + std::chrono::seconds{10}); + if (error.Fail()) + return error; + return writeSocketIdToPipe(port_name_pipe, socket_id); +} + +Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe, + const std::string &socket_id) { + Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe}; + return writeSocketIdToPipe(port_pipe, socket_id); +} + +void ConnectToRemote(MainLoop &mainloop, + GDBRemoteCommunicationServerLLGS &gdb_server, + bool reverse_connect, const char *const host_and_port, + const char *const progname, const char *const subcommand, + const char *const named_pipe_path, pipe_t unnamed_pipe, + int connection_fd) { + Status error; + + std::unique_ptr<Connection> connection_up; + if (connection_fd != -1) { + // Build the connection string. + char connection_url[512]; + snprintf(connection_url, sizeof(connection_url), "fd://%d", connection_fd); + + // Create the connection. +#if LLDB_ENABLE_POSIX && !defined _WIN32 + ::fcntl(connection_fd, F_SETFD, FD_CLOEXEC); +#endif + connection_up.reset(new ConnectionFileDescriptor); + auto connection_result = connection_up->Connect(connection_url, &error); + if (connection_result != eConnectionStatusSuccess) { + fprintf(stderr, "error: failed to connect to client at '%s' " + "(connection status: %d)\n", + connection_url, static_cast<int>(connection_result)); + exit(-1); + } + if (error.Fail()) { + fprintf(stderr, "error: failed to connect to client at '%s': %s\n", + connection_url, error.AsCString()); + exit(-1); + } + } else if (host_and_port && host_and_port[0]) { + // Parse out host and port. + std::string final_host_and_port; + std::string connection_host; + std::string connection_port; + uint32_t connection_portno = 0; + + // If host_and_port starts with ':', default the host to be "localhost" and + // expect the remainder to be the port. + if (host_and_port[0] == ':') + final_host_and_port.append("localhost"); + final_host_and_port.append(host_and_port); + + const std::string::size_type colon_pos = final_host_and_port.find(':'); + if (colon_pos != std::string::npos) { + connection_host = final_host_and_port.substr(0, colon_pos); + connection_port = final_host_and_port.substr(colon_pos + 1); + connection_portno = StringConvert::ToUInt32(connection_port.c_str(), 0); + } + + + if (reverse_connect) { + // llgs will connect to the gdb-remote client. + + // Ensure we have a port number for the connection. + if (connection_portno == 0) { + fprintf(stderr, "error: port number must be specified on when using " + "reverse connect\n"); + exit(1); + } + + // Build the connection string. + char connection_url[512]; + snprintf(connection_url, sizeof(connection_url), "connect://%s", + final_host_and_port.c_str()); + + // Create the connection. + connection_up.reset(new ConnectionFileDescriptor); + auto connection_result = connection_up->Connect(connection_url, &error); + if (connection_result != eConnectionStatusSuccess) { + fprintf(stderr, "error: failed to connect to client at '%s' " + "(connection status: %d)\n", + connection_url, static_cast<int>(connection_result)); + exit(-1); + } + if (error.Fail()) { + fprintf(stderr, "error: failed to connect to client at '%s': %s\n", + connection_url, error.AsCString()); + exit(-1); + } + } else { + std::unique_ptr<Acceptor> acceptor_up( + Acceptor::Create(final_host_and_port, false, error)); + if (error.Fail()) { + fprintf(stderr, "failed to create acceptor: %s\n", error.AsCString()); + exit(1); + } + error = acceptor_up->Listen(1); + if (error.Fail()) { + fprintf(stderr, "failed to listen: %s\n", error.AsCString()); + exit(1); + } + const std::string socket_id = acceptor_up->GetLocalSocketId(); + if (!socket_id.empty()) { + // If we have a named pipe to write the socket id back to, do that now. + if (named_pipe_path && named_pipe_path[0]) { + error = writeSocketIdToPipe(named_pipe_path, socket_id); + if (error.Fail()) + fprintf(stderr, "failed to write to the named pipe \'%s\': %s\n", + named_pipe_path, error.AsCString()); + } + // If we have an unnamed pipe to write the socket id back to, do that + // now. + else if (unnamed_pipe != LLDB_INVALID_PIPE) { + error = writeSocketIdToPipe(unnamed_pipe, socket_id); + if (error.Fail()) + fprintf(stderr, "failed to write to the unnamed pipe: %s\n", + error.AsCString()); + } + } else { + fprintf(stderr, + "unable to get the socket id for the listening connection\n"); + } + + Connection *conn = nullptr; + error = acceptor_up->Accept(false, conn); + if (error.Fail()) { + printf("failed to accept new connection: %s\n", error.AsCString()); + exit(1); + } + connection_up.reset(conn); + } + } + error = gdb_server.InitializeConnection(std::move(connection_up)); + if (error.Fail()) { + fprintf(stderr, "Failed to initialize connection: %s\n", + error.AsCString()); + exit(-1); + } + printf("Connection established.\n"); +} + +// main +int main_gdbserver(int argc, char *argv[]) { + Status error; + MainLoop mainloop; +#ifndef _WIN32 + // Setup signal handlers first thing. + signal(SIGPIPE, SIG_IGN); + MainLoop::SignalHandleUP sighup_handle = + mainloop.RegisterSignal(SIGHUP, sighup_handler, error); +#endif + + const char *progname = argv[0]; + const char *subcommand = argv[1]; + argc--; + argv++; + int long_option_index = 0; + int ch; + std::string attach_target; + std::string named_pipe_path; + std::string log_file; + StringRef + log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" + lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE; + bool reverse_connect = false; + int connection_fd = -1; + + // ProcessLaunchInfo launch_info; + ProcessAttachInfo attach_info; + + bool show_usage = false; + int option_error = 0; +#if __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + + std::string short_options(OptionParser::GetShortOptionString(g_long_options)); + + while ((ch = getopt_long_only(argc, argv, short_options.c_str(), + g_long_options, &long_option_index)) != -1) { + switch (ch) { + case 0: // Any optional that auto set themselves will return 0 + break; + + case 'l': // Set Log File + if (optarg && optarg[0]) + log_file.assign(optarg); + break; + + case 'c': // Log Channels + if (optarg && optarg[0]) + log_channels = StringRef(optarg); + break; + + case 'N': // named pipe + if (optarg && optarg[0]) + named_pipe_path = optarg; + break; + + case 'U': // unnamed pipe + if (optarg && optarg[0]) + unnamed_pipe = (pipe_t)StringConvert::ToUInt64(optarg, -1); + break; + + case 'r': + // Do nothing, native regs is the default these days + break; + + case 'R': + reverse_connect = true; + break; + + case 'F': + connection_fd = StringConvert::ToUInt32(optarg, -1); + break; + +#ifndef _WIN32 + case 'S': + // Put llgs 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 llgs is spawned from a command + // line application that uses llgs to do the debugging, + // yet that application doesn't want llgs receiving the + // signals sent to the session (i.e. dying when anyone hits ^C). + { + const ::pid_t new_sid = setsid(); + if (new_sid == -1) { + llvm::errs() << llvm::formatv( + "failed to set new session id for {0} ({1})\n", LLGS_PROGRAM_NAME, + llvm::sys::StrError()); + } + } + break; +#endif + + case 'a': // attach {pid|process_name} + if (optarg && optarg[0]) + attach_target = optarg; + break; + + case 'h': /* fall-through is intentional */ + case '?': + show_usage = true; + break; + } + } + + if (show_usage || option_error) { + display_usage(progname, subcommand); + exit(option_error); + } + + if (!LLDBServerUtilities::SetupLogging( + log_file, log_channels, + LLDB_LOG_OPTION_PREPEND_TIMESTAMP | + LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION)) + return -1; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(GDBR_LOG_PROCESS)); + if (log) { + LLDB_LOGF(log, "lldb-server launch"); + for (int i = 0; i < argc; i++) { + LLDB_LOGF(log, "argv[%i] = '%s'", i, argv[i]); + } + } + + // Skip any options we consumed with getopt_long_only. + argc -= optind; + argv += optind; + + if (argc == 0 && connection_fd == -1) { + fputs("No arguments\n", stderr); + display_usage(progname, subcommand); + exit(255); + } + + NativeProcessFactory factory; + GDBRemoteCommunicationServerLLGS gdb_server(mainloop, factory); + + const char *const host_and_port = argv[0]; + argc -= 1; + argv += 1; + + // Any arguments left over are for the program that we need to launch. If + // there + // are no arguments, then the GDB server will start up and wait for an 'A' + // packet + // to launch a program, or a vAttach packet to attach to an existing process, + // unless + // explicitly asked to attach with the --attach={pid|program_name} form. + if (!attach_target.empty()) + handle_attach(gdb_server, attach_target); + else if (argc > 0) + handle_launch(gdb_server, argc, argv); + + // Print version info. + printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR); + + ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port, + progname, subcommand, named_pipe_path.c_str(), + unnamed_pipe, connection_fd); + + if (!gdb_server.IsConnected()) { + fprintf(stderr, "no connection information provided, unable to run\n"); + display_usage(progname, subcommand); + return 1; + } + + Status ret = mainloop.Run(); + if (ret.Fail()) { + fprintf(stderr, "lldb-server terminating due to error: %s\n", + ret.AsCString()); + return 1; + } + fprintf(stderr, "lldb-server exiting...\n"); + + return 0; +} diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp b/gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp new file mode 100644 index 00000000000..a6fb5639d64 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp @@ -0,0 +1,385 @@ +//===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <errno.h> +#if defined(__APPLE__) +#include <netinet/in.h> +#endif +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if !defined(_WIN32) +#include <sys/wait.h> +#endif +#include <fstream> + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/raw_ostream.h" + +#include "Acceptor.h" +#include "LLDBServerUtilities.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/HostGetOpt.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Status.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace lldb_private::process_gdb_remote; +using namespace llvm; + +// option descriptors for getopt_long_only() + +static int g_debug = 0; +static int g_verbose = 0; +static int g_server = 0; + +static struct option g_long_options[] = { + {"debug", no_argument, &g_debug, 1}, + {"verbose", no_argument, &g_verbose, 1}, + {"log-file", required_argument, nullptr, 'l'}, + {"log-channels", required_argument, nullptr, 'c'}, + {"listen", required_argument, nullptr, 'L'}, + {"port-offset", required_argument, nullptr, 'p'}, + {"gdbserver-port", required_argument, nullptr, 'P'}, + {"min-gdbserver-port", required_argument, nullptr, 'm'}, + {"max-gdbserver-port", required_argument, nullptr, 'M'}, + {"socket-file", required_argument, nullptr, 'f'}, + {"server", no_argument, &g_server, 1}, + {nullptr, 0, nullptr, 0}}; + +#if defined(__APPLE__) +#define LOW_PORT (IPPORT_RESERVED) +#define HIGH_PORT (IPPORT_HIFIRSTAUTO) +#else +#define LOW_PORT (1024u) +#define HIGH_PORT (49151u) +#endif + +#if !defined(_WIN32) +// Watch for signals +static void signal_handler(int signo) { + switch (signo) { + case SIGHUP: + // Use SIGINT first, if that does not work, use SIGHUP as a last resort. + // And we should not call exit() here because it results in the global + // destructors + // to be invoked and wreaking havoc on the threads still running. + Host::SystemLog(Host::eSystemLogWarning, + "SIGHUP received, exiting lldb-server...\n"); + abort(); + break; + } +} +#endif + +static void display_usage(const char *progname, const char *subcommand) { + fprintf(stderr, "Usage:\n %s %s [--log-file log-file-name] [--log-channels " + "log-channel-list] [--port-file port-file-path] --server " + "--listen port\n", + progname, subcommand); + exit(0); +} + +static Status save_socket_id_to_file(const std::string &socket_id, + const FileSpec &file_spec) { + FileSpec temp_file_spec(file_spec.GetDirectory().AsCString()); + Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath())); + if (error.Fail()) + return Status("Failed to create directory %s: %s", + temp_file_spec.GetCString(), error.AsCString()); + + llvm::SmallString<64> temp_file_path; + temp_file_spec.AppendPathComponent("port-file.%%%%%%"); + + Status status; + if (auto Err = + handleErrors(llvm::writeFileAtomically( + temp_file_path, temp_file_spec.GetPath(), socket_id), + [&status, &file_spec](const AtomicFileWriteError &E) { + std::string ErrorMsgBuffer; + llvm::raw_string_ostream S(ErrorMsgBuffer); + E.log(S); + + switch (E.Error) { + case atomic_write_error::failed_to_create_uniq_file: + status = Status("Failed to create temp file: %s", + ErrorMsgBuffer.c_str()); + break; + case atomic_write_error::output_stream_error: + status = Status("Failed to write to port file."); + break; + case atomic_write_error::failed_to_rename_temp_file: + status = Status("Failed to rename file %s to %s: %s", + ErrorMsgBuffer.c_str(), + file_spec.GetPath().c_str(), + ErrorMsgBuffer.c_str()); + break; + } + })) { + return Status("Failed to atomically write file %s", + file_spec.GetPath().c_str()); + } + return status; +} + +// main +int main_platform(int argc, char *argv[]) { + const char *progname = argv[0]; + const char *subcommand = argv[1]; + argc--; + argv++; +#if !defined(_WIN32) + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, signal_handler); +#endif + int long_option_index = 0; + Status error; + std::string listen_host_port; + int ch; + + std::string log_file; + StringRef + log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" + + GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; + int min_gdbserver_port = 0; + int max_gdbserver_port = 0; + uint16_t port_offset = 0; + + FileSpec socket_file; + bool show_usage = false; + int option_error = 0; + int socket_error = -1; + + std::string short_options(OptionParser::GetShortOptionString(g_long_options)); + +#if __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + + while ((ch = getopt_long_only(argc, argv, short_options.c_str(), + g_long_options, &long_option_index)) != -1) { + switch (ch) { + case 0: // Any optional that auto set themselves will return 0 + break; + + case 'L': + listen_host_port.append(optarg); + break; + + case 'l': // Set Log File + if (optarg && optarg[0]) + log_file.assign(optarg); + break; + + case 'c': // Log Channels + if (optarg && optarg[0]) + log_channels = StringRef(optarg); + break; + + case 'f': // Socket file + if (optarg && optarg[0]) + socket_file.SetFile(optarg, FileSpec::Style::native); + break; + + case 'p': { + if (!llvm::to_integer(optarg, port_offset)) { + llvm::errs() << "error: invalid port offset string " << optarg << "\n"; + option_error = 4; + break; + } + if (port_offset < LOW_PORT || port_offset > HIGH_PORT) { + llvm::errs() << llvm::formatv("error: port offset {0} is not in the " + "valid user port range of {1} - {2}\n", + port_offset, LOW_PORT, HIGH_PORT); + option_error = 5; + } + } break; + + case 'P': + case 'm': + case 'M': { + uint16_t portnum; + if (!llvm::to_integer(optarg, portnum)) { + llvm::errs() << "error: invalid port number string " << optarg << "\n"; + option_error = 2; + break; + } + if (portnum < LOW_PORT || portnum > HIGH_PORT) { + llvm::errs() << llvm::formatv("error: port number {0} is not in the " + "valid user port range of {1} - {2}\n", + portnum, LOW_PORT, HIGH_PORT); + option_error = 1; + break; + } + if (ch == 'P') + gdbserver_portmap[portnum] = LLDB_INVALID_PROCESS_ID; + else if (ch == 'm') + min_gdbserver_port = portnum; + else + max_gdbserver_port = portnum; + } break; + + case 'h': /* fall-through is intentional */ + case '?': + show_usage = true; + break; + } + } + + if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0)) + return -1; + + // Make a port map for a port range that was specified. + if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) { + for (uint16_t port = min_gdbserver_port; port < max_gdbserver_port; ++port) + gdbserver_portmap[port] = LLDB_INVALID_PROCESS_ID; + } else if (min_gdbserver_port || max_gdbserver_port) { + fprintf(stderr, "error: --min-gdbserver-port (%u) is not lower than " + "--max-gdbserver-port (%u)\n", + min_gdbserver_port, max_gdbserver_port); + option_error = 3; + } + + // Print usage and exit if no listening port is specified. + if (listen_host_port.empty()) + show_usage = true; + + if (show_usage || option_error) { + display_usage(progname, subcommand); + exit(option_error); + } + + // Skip any options we consumed with getopt_long_only. + argc -= optind; + argv += optind; + lldb_private::Args inferior_arguments; + inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); + + const bool children_inherit_listen_socket = false; + // the test suite makes many connections in parallel, let's not miss any. + // The highest this should get reasonably is a function of the number + // of target CPUs. For now, let's just use 100. + const int backlog = 100; + + std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create( + listen_host_port, children_inherit_listen_socket, error)); + if (error.Fail()) { + fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); + exit(socket_error); + } + + error = acceptor_up->Listen(backlog); + if (error.Fail()) { + printf("failed to listen: %s\n", error.AsCString()); + exit(socket_error); + } + if (socket_file) { + error = + save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); + if (error.Fail()) { + fprintf(stderr, "failed to write socket id to %s: %s\n", + socket_file.GetPath().c_str(), error.AsCString()); + return 1; + } + } + + do { + GDBRemoteCommunicationServerPlatform platform( + acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); + + if (port_offset > 0) + platform.SetPortOffset(port_offset); + + if (!gdbserver_portmap.empty()) { + platform.SetPortMap(std::move(gdbserver_portmap)); + } + + const bool children_inherit_accept_socket = true; + Connection *conn = nullptr; + error = acceptor_up->Accept(children_inherit_accept_socket, conn); + if (error.Fail()) { + printf("error: %s\n", error.AsCString()); + exit(socket_error); + } + printf("Connection established.\n"); + if (g_server) { + // Collect child zombie processes. +#if !defined(_WIN32) + while (waitpid(-1, nullptr, WNOHANG) > 0) + ; +#endif + if (fork()) { + // Parent doesn't need a connection to the lldb client + delete conn; + + // Parent will continue to listen for new connections. + continue; + } else { + // Child process will handle the connection and exit. + g_server = 0; + // Listening socket is owned by parent process. + acceptor_up.release(); + } + } else { + // If not running as a server, this process will not accept + // connections while a connection is active. + acceptor_up.reset(); + } + platform.SetConnection(conn); + + if (platform.IsConnected()) { + if (inferior_arguments.GetArgumentCount() > 0) { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + uint16_t port = 0; + std::string socket_name; + Status error = platform.LaunchGDBServer(inferior_arguments, + "", // hostname + pid, port, socket_name); + if (error.Success()) + platform.SetPendingGdbServer(pid, port, socket_name); + else + fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); + } + + // After we connected, we need to get an initial ack from... + if (platform.HandshakeWithClient()) { + bool interrupt = false; + bool done = false; + while (!interrupt && !done) { + if (platform.GetPacketAndSendResponse(llvm::None, error, interrupt, + done) != + GDBRemoteCommunication::PacketResult::Success) + break; + } + + if (error.Fail()) { + fprintf(stderr, "error: %s\n", error.AsCString()); + } + } else { + fprintf(stderr, "error: handshake with client failed\n"); + } + } + } while (g_server); + + fprintf(stderr, "lldb-server exiting...\n"); + + return 0; +} diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-server.cpp b/gnu/llvm/lldb/tools/lldb-server/lldb-server.cpp new file mode 100644 index 00000000000..749a381ebca --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/lldb-server.cpp @@ -0,0 +1,82 @@ +//===-- lldb-server.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SystemInitializerLLGS.h" +#include "lldb/Initialization/SystemLifetimeManager.h" +#include "lldb/lldb-private.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" + +#include <stdio.h> +#include <stdlib.h> + +static llvm::ManagedStatic<lldb_private::SystemLifetimeManager> + g_debugger_lifetime; + +static void display_usage(const char *progname) { + fprintf(stderr, "Usage:\n" + " %s v[ersion]\n" + " %s g[dbserver] [options]\n" + " %s p[latform] [options]\n" + "Invoke subcommand for additional help\n", + progname, progname, progname); + exit(0); +} + +// Forward declarations of subcommand main methods. +int main_gdbserver(int argc, char *argv[]); +int main_platform(int argc, char *argv[]); + +namespace llgs { +static void initialize() { + if (auto e = g_debugger_lifetime->Initialize( + std::make_unique<SystemInitializerLLGS>(), nullptr)) + llvm::consumeError(std::move(e)); +} + +static void terminate_debugger() { g_debugger_lifetime->Terminate(); } +} // namespace llgs + +// main +int main(int argc, char *argv[]) { + llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false); + llvm::StringRef ToolName = argv[0]; + llvm::sys::PrintStackTraceOnErrorSignal(ToolName); + llvm::PrettyStackTraceProgram X(argc, argv); + + int option_error = 0; + const char *progname = argv[0]; + if (argc < 2) { + display_usage(progname); + exit(option_error); + } + + switch (argv[1][0]) { + case 'g': + llgs::initialize(); + main_gdbserver(argc, argv); + llgs::terminate_debugger(); + break; + case 'p': + llgs::initialize(); + main_platform(argc, argv); + llgs::terminate_debugger(); + break; + case 'v': + fprintf(stderr, "%s\n", lldb_private::GetVersion()); + break; + default: + display_usage(progname); + exit(option_error); + } +} diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-server.exports b/gnu/llvm/lldb/tools/lldb-server/lldb-server.exports new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-server/lldb-server.exports diff --git a/gnu/llvm/lldb/tools/lldb-test/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-test/CMakeLists.txt new file mode 100644 index 00000000000..2ab1ceacdcd --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-test/CMakeLists.txt @@ -0,0 +1,27 @@ +get_property(LLDB_ALL_PLUGINS GLOBAL PROPERTY LLDB_PLUGINS) + +add_lldb_tool(lldb-test + FormatUtil.cpp + lldb-test.cpp + SystemInitializerTest.cpp + + LINK_LIBS + lldbBase + lldbBreakpoint + lldbCore + lldbDataFormatters + lldbExpression + lldbHost + lldbInitialization + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + ${LLDB_ALL_PLUGINS} + ${host_lib} + + LINK_COMPONENTS + Support + ) + +include_directories(${LLDB_SOURCE_DIR}/source) diff --git a/gnu/llvm/lldb/tools/lldb-test/FormatUtil.cpp b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.cpp new file mode 100644 index 00000000000..1e5e1ee0874 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.cpp @@ -0,0 +1,65 @@ +//===- FormatUtil.cpp ----------------------------------------- *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatUtil.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace lldb_private; +using namespace llvm; + +LinePrinter::Line::~Line() { + if (P) + P->NewLine(); +} + +LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream) + : OS(Stream), IndentSpaces(Indent), CurrentIndent(0) {} + +void LinePrinter::Indent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent += Amount; +} + +void LinePrinter::Unindent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent = std::max<int>(0, CurrentIndent - Amount); +} + +void LinePrinter::NewLine() { + OS << "\n"; +} + +void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint32_t StartOffset) { + if (Data.empty()) { + line() << Label << " ()"; + return; + } + line() << Label << " ("; + OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, + CurrentIndent + IndentSpaces, true); + NewLine(); + line() << ")"; +} + +void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint64_t Base, uint32_t StartOffset) { + if (Data.empty()) { + line() << Label << " ()"; + return; + } + line() << Label << " ("; + Base += StartOffset; + OS << format_bytes_with_ascii(Data, Base, 32, 4, CurrentIndent + IndentSpaces, + true); + NewLine(); + line() << ")"; +} diff --git a/gnu/llvm/lldb/tools/lldb-test/FormatUtil.h b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.h new file mode 100644 index 00000000000..91b3c085b60 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.h @@ -0,0 +1,77 @@ +//===- FormatUtil.h ------------------------------------------- *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLDBTEST_FORMATUTIL_H +#define LLVM_TOOLS_LLDBTEST_FORMATUTIL_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include <list> + +namespace lldb_private { + +class LinePrinter { + llvm::raw_ostream &OS; + int IndentSpaces; + int CurrentIndent; + +public: + class Line { + LinePrinter *P; + + public: + Line(LinePrinter &P) : P(&P) { P.OS.indent(P.CurrentIndent); } + ~Line(); + + Line(Line &&RHS) : P(RHS.P) { RHS.P = nullptr; } + void operator=(Line &&) = delete; + + operator llvm::raw_ostream &() { return P->OS; } + }; + + LinePrinter(int Indent, llvm::raw_ostream &Stream); + + void Indent(uint32_t Amount = 0); + void Unindent(uint32_t Amount = 0); + void NewLine(); + + void printLine(const llvm::Twine &T) { line() << T; } + template <typename... Ts> void formatLine(const char *Fmt, Ts &&... Items) { + printLine(llvm::formatv(Fmt, std::forward<Ts>(Items)...)); + } + + void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data, + uint32_t StartOffset); + void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data, + uint64_t BaseAddr, uint32_t StartOffset); + + Line line() { return Line(*this); } + int getIndentLevel() const { return CurrentIndent; } +}; + +struct AutoIndent { + explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0) + : L(&L), Amount(Amount) { + L.Indent(Amount); + } + ~AutoIndent() { + if (L) + L->Unindent(Amount); + } + + LinePrinter *L = nullptr; + uint32_t Amount = 0; +}; + +} // namespace lldb_private + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.cpp b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.cpp new file mode 100644 index 00000000000..8841e4d6583 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.cpp @@ -0,0 +1,351 @@ +//===-- SystemInitializerTest.cpp -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SystemInitializerTest.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Host/Host.h" +#include "lldb/Initialization/SystemInitializerCommon.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Utility/Timer.h" + +#include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h" +#include "Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h" +#include "Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h" +#include "Plugins/ABI/SysV-arm/ABISysV_arm.h" +#include "Plugins/ABI/SysV-arm64/ABISysV_arm64.h" +#include "Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h" +#include "Plugins/ABI/SysV-i386/ABISysV_i386.h" +#include "Plugins/ABI/SysV-mips/ABISysV_mips.h" +#include "Plugins/ABI/SysV-mips64/ABISysV_mips64.h" +#include "Plugins/ABI/SysV-ppc/ABISysV_ppc.h" +#include "Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h" +#include "Plugins/ABI/SysV-s390x/ABISysV_s390x.h" +#include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h" +#include "Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h" +#include "Plugins/Architecture/Arm/ArchitectureArm.h" +#include "Plugins/Architecture/PPC64/ArchitecturePPC64.h" +#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h" +#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h" +#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" +#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" +#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" +#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" +#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" +#include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h" +#include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h" +#include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h" +#include "Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h" +#include "Plugins/JITLoader/GDB/JITLoaderGDB.h" +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" +#include "Plugins/Language/ObjC/ObjCLanguage.h" +#include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h" +#include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h" +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" +#include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" +#include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" +#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" +#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +#include "Plugins/Platform/Android/PlatformAndroid.h" +#include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h" +#include "Plugins/Platform/Linux/PlatformLinux.h" +#include "Plugins/Platform/MacOSX/PlatformMacOSX.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" +#include "Plugins/Platform/NetBSD/PlatformNetBSD.h" +#include "Plugins/Platform/OpenBSD/PlatformOpenBSD.h" +#include "Plugins/Platform/Windows/PlatformWindows.h" +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" +#include "Plugins/Process/elf-core/ProcessElfCore.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "Plugins/Process/minidump/ProcessMinidump.h" +#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" +#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" +#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" +#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" +#include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h" +#include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h" +#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" +#include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h" + +#if defined(__APPLE__) +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h" +#include "Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h" +#include "Plugins/Platform/MacOSX/PlatformDarwinKernel.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h" +#include "Plugins/Platform/MacOSX/PlatformiOSSimulator.h" +#include "Plugins/Process/MacOSX-Kernel/ProcessKDP.h" +#include "Plugins/Process/mach-core/ProcessMachCore.h" +#include "Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h" +#endif +#include "Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h" + +#if defined(__FreeBSD__) +#include "Plugins/Process/FreeBSD/ProcessFreeBSD.h" +#endif + +#if defined(_WIN32) +#include "Plugins/Process/Windows/Common/ProcessWindows.h" +#include "lldb/Host/windows/windows.h" +#endif + +#include "llvm/Support/TargetSelect.h" + +#include <string> + +using namespace lldb_private; + +SystemInitializerTest::SystemInitializerTest() {} + +SystemInitializerTest::~SystemInitializerTest() {} + +#define LLDB_PROCESS_AArch64(op) \ + ABIMacOSX_arm64::op(); \ + ABISysV_arm64::op(); +#define LLDB_PROCESS_ARM(op) \ + ABIMacOSX_arm::op(); \ + ABISysV_arm::op(); +#define LLDB_PROCESS_Hexagon(op) ABISysV_hexagon::op(); +#define LLDB_PROCESS_Mips(op) \ + ABISysV_mips::op(); \ + ABISysV_mips64::op(); +#define LLDB_PROCESS_PowerPC(op) \ + ABISysV_ppc::op(); \ + ABISysV_ppc64::op(); +#define LLDB_PROCESS_SystemZ(op) ABISysV_s390x::op(); +#define LLDB_PROCESS_X86(op) \ + ABIMacOSX_i386::op(); \ + ABISysV_i386::op(); \ + ABISysV_x86_64::op(); \ + ABIWindows_x86_64::op(); + +#define LLDB_PROCESS_AMDGPU(op) +#define LLDB_PROCESS_ARC(op) +#define LLDB_PROCESS_AVR(op) +#define LLDB_PROCESS_BPF(op) +#define LLDB_PROCESS_Lanai(op) +#define LLDB_PROCESS_MSP430(op) +#define LLDB_PROCESS_NVPTX(op) +#define LLDB_PROCESS_RISCV(op) +#define LLDB_PROCESS_Sparc(op) +#define LLDB_PROCESS_WebAssembly(op) +#define LLDB_PROCESS_XCore(op) + +llvm::Error SystemInitializerTest::Initialize() { + if (auto e = SystemInitializerCommon::Initialize()) + return e; + + breakpad::ObjectFileBreakpad::Initialize(); + ObjectFileELF::Initialize(); + ObjectFileMachO::Initialize(); + ObjectFilePECOFF::Initialize(); + + ScriptInterpreterNone::Initialize(); + + + platform_freebsd::PlatformFreeBSD::Initialize(); + platform_linux::PlatformLinux::Initialize(); + platform_netbsd::PlatformNetBSD::Initialize(); + platform_openbsd::PlatformOpenBSD::Initialize(); + PlatformWindows::Initialize(); + platform_android::PlatformAndroid::Initialize(); + PlatformRemoteiOS::Initialize(); + PlatformMacOSX::Initialize(); +#if defined(__APPLE__) + PlatformiOSSimulator::Initialize(); + PlatformDarwinKernel::Initialize(); +#endif + + // Initialize LLVM and Clang + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + ClangASTContext::Initialize(); + +#define LLVM_TARGET(t) LLDB_PROCESS_ ## t(Initialize) +#include "llvm/Config/Targets.def" + + ArchitectureArm::Initialize(); + ArchitecturePPC64::Initialize(); + + DisassemblerLLVMC::Initialize(); + + JITLoaderGDB::Initialize(); + ProcessElfCore::Initialize(); + minidump::ProcessMinidump::Initialize(); + MemoryHistoryASan::Initialize(); + AddressSanitizerRuntime::Initialize(); + ThreadSanitizerRuntime::Initialize(); + UndefinedBehaviorSanitizerRuntime::Initialize(); + MainThreadCheckerRuntime::Initialize(); + + SymbolVendorELF::Initialize(); + breakpad::SymbolFileBreakpad::Initialize(); + SymbolFileDWARF::Initialize(); + SymbolFilePDB::Initialize(); + SymbolFileSymtab::Initialize(); + UnwindAssemblyInstEmulation::Initialize(); + UnwindAssembly_x86::Initialize(); + EmulateInstructionARM64::Initialize(); + EmulateInstructionPPC64::Initialize(); + SymbolFileDWARFDebugMap::Initialize(); + ItaniumABILanguageRuntime::Initialize(); + AppleObjCRuntimeV2::Initialize(); + AppleObjCRuntimeV1::Initialize(); + SystemRuntimeMacOSX::Initialize(); + RenderScriptRuntime::Initialize(); + + CPlusPlusLanguage::Initialize(); + ObjCLanguage::Initialize(); + ObjCPlusPlusLanguage::Initialize(); + +#if defined(_WIN32) + ProcessWindows::Initialize(); +#endif +#if defined(__FreeBSD__) + ProcessFreeBSD::Initialize(); +#endif +#if defined(__APPLE__) + SymbolVendorMacOSX::Initialize(); + ProcessKDP::Initialize(); + ProcessMachCore::Initialize(); + PlatformAppleTVSimulator::Initialize(); + PlatformAppleWatchSimulator::Initialize(); + PlatformRemoteAppleTV::Initialize(); + PlatformRemoteAppleWatch::Initialize(); + DynamicLoaderDarwinKernel::Initialize(); +#endif + + // This plugin is valid on any host that talks to a Darwin remote. + // It shouldn't be limited to __APPLE__. + StructuredDataDarwinLog::Initialize(); + + // Platform agnostic plugins + platform_gdb_server::PlatformRemoteGDBServer::Initialize(); + + process_gdb_remote::ProcessGDBRemote::Initialize(); + DynamicLoaderMacOSXDYLD::Initialize(); + DynamicLoaderMacOS::Initialize(); + DynamicLoaderPOSIXDYLD::Initialize(); + DynamicLoaderStatic::Initialize(); + DynamicLoaderWindowsDYLD::Initialize(); + + // Scan for any system or user LLDB plug-ins + PluginManager::Initialize(); + + // The process settings need to know about installed plug-ins, so the Settings + // must be initialized + // AFTER PluginManager::Initialize is called. + + Debugger::SettingsInitialize(); + + return llvm::Error::success(); +} + +void SystemInitializerTest::Terminate() { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); + + Debugger::SettingsTerminate(); + + // Terminate and unload and loaded system or user LLDB plug-ins + PluginManager::Terminate(); + + ClangASTContext::Terminate(); + +#define LLVM_TARGET(t) LLDB_PROCESS_ ## t(Terminate) +#include "llvm/Config/Targets.def" + + DisassemblerLLVMC::Terminate(); + + JITLoaderGDB::Terminate(); + ProcessElfCore::Terminate(); + minidump::ProcessMinidump::Terminate(); + MemoryHistoryASan::Terminate(); + AddressSanitizerRuntime::Terminate(); + ThreadSanitizerRuntime::Terminate(); + UndefinedBehaviorSanitizerRuntime::Terminate(); + MainThreadCheckerRuntime::Terminate(); + SymbolVendorELF::Terminate(); + breakpad::SymbolFileBreakpad::Terminate(); + SymbolFileDWARF::Terminate(); + SymbolFilePDB::Terminate(); + SymbolFileSymtab::Terminate(); + UnwindAssembly_x86::Terminate(); + UnwindAssemblyInstEmulation::Terminate(); + EmulateInstructionARM64::Terminate(); + EmulateInstructionPPC64::Terminate(); + SymbolFileDWARFDebugMap::Terminate(); + ItaniumABILanguageRuntime::Terminate(); + AppleObjCRuntimeV2::Terminate(); + AppleObjCRuntimeV1::Terminate(); + SystemRuntimeMacOSX::Terminate(); + RenderScriptRuntime::Terminate(); + + CPlusPlusLanguage::Terminate(); + ObjCLanguage::Terminate(); + ObjCPlusPlusLanguage::Terminate(); + +#if defined(__APPLE__) + DynamicLoaderDarwinKernel::Terminate(); + ProcessMachCore::Terminate(); + ProcessKDP::Terminate(); + SymbolVendorMacOSX::Terminate(); + PlatformAppleTVSimulator::Terminate(); + PlatformAppleWatchSimulator::Terminate(); + PlatformRemoteAppleTV::Terminate(); + PlatformRemoteAppleWatch::Terminate(); +#endif + +#if defined(__FreeBSD__) + ProcessFreeBSD::Terminate(); +#endif + Debugger::SettingsTerminate(); + + platform_gdb_server::PlatformRemoteGDBServer::Terminate(); + process_gdb_remote::ProcessGDBRemote::Terminate(); + StructuredDataDarwinLog::Terminate(); + + DynamicLoaderMacOSXDYLD::Terminate(); + DynamicLoaderMacOS::Terminate(); + DynamicLoaderPOSIXDYLD::Terminate(); + DynamicLoaderStatic::Terminate(); + DynamicLoaderWindowsDYLD::Terminate(); + + + platform_freebsd::PlatformFreeBSD::Terminate(); + platform_linux::PlatformLinux::Terminate(); + platform_netbsd::PlatformNetBSD::Terminate(); + platform_openbsd::PlatformOpenBSD::Terminate(); + PlatformWindows::Terminate(); + platform_android::PlatformAndroid::Terminate(); + PlatformMacOSX::Terminate(); + PlatformRemoteiOS::Terminate(); +#if defined(__APPLE__) + PlatformiOSSimulator::Terminate(); + PlatformDarwinKernel::Terminate(); +#endif + + breakpad::ObjectFileBreakpad::Terminate(); + ObjectFileELF::Terminate(); + ObjectFileMachO::Terminate(); + ObjectFilePECOFF::Terminate(); + + // Now shutdown the common parts, in reverse order. + SystemInitializerCommon::Terminate(); +} diff --git a/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.h b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.h new file mode 100644 index 00000000000..4f58d2a64a6 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.h @@ -0,0 +1,32 @@ +//===-- SystemInitializerTest.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_API_SYSTEM_INITIALIZER_TEST_H +#define LLDB_API_SYSTEM_INITIALIZER_TEST_H + +#include "lldb/Initialization/SystemInitializerCommon.h" + +namespace lldb_private { +/// Initializes lldb. +/// +/// This class is responsible for initializing all of lldb system +/// services needed to use the full LLDB application. This class is +/// not intended to be used externally, but is instead used +/// internally by SBDebugger to initialize the system. +class SystemInitializerTest : public SystemInitializerCommon { +public: + SystemInitializerTest(); + ~SystemInitializerTest() override; + + llvm::Error Initialize() override; + void Terminate() override; +}; + +} // namespace lldb_private + +#endif // LLDB_API_SYSTEM_INITIALIZER_FULL_H diff --git a/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp b/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp new file mode 100644 index 00000000000..66c9a87301e --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp @@ -0,0 +1,1103 @@ +//===- lldb-test.cpp ------------------------------------------ *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatUtil.h" +#include "SystemInitializerTest.h" + +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Initialization/SystemLifetimeManager.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" + +#include <cstdio> +#include <thread> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace opts { +static cl::SubCommand BreakpointSubcommand("breakpoints", + "Test breakpoint resolution"); +cl::SubCommand ObjectFileSubcommand("object-file", + "Display LLDB object file information"); +cl::SubCommand SymbolsSubcommand("symbols", "Dump symbols for an object file"); +cl::SubCommand IRMemoryMapSubcommand("ir-memory-map", "Test IRMemoryMap"); + +cl::opt<std::string> Log("log", cl::desc("Path to a log file"), cl::init(""), + cl::sub(BreakpointSubcommand), + cl::sub(ObjectFileSubcommand), + cl::sub(SymbolsSubcommand), + cl::sub(IRMemoryMapSubcommand)); + +/// Create a target using the file pointed to by \p Filename, or abort. +TargetSP createTarget(Debugger &Dbg, const std::string &Filename); + +/// Read \p Filename into a null-terminated buffer, or abort. +std::unique_ptr<MemoryBuffer> openFile(const std::string &Filename); + +namespace breakpoint { +static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>"), + cl::Required, cl::sub(BreakpointSubcommand)); +static cl::opt<std::string> CommandFile(cl::Positional, + cl::desc("<command-file>"), + cl::init("-"), + cl::sub(BreakpointSubcommand)); +static cl::opt<bool> Persistent( + "persistent", + cl::desc("Don't automatically remove all breakpoints before each command"), + cl::sub(BreakpointSubcommand)); + +static llvm::StringRef plural(uintmax_t value) { return value == 1 ? "" : "s"; } +static void dumpState(const BreakpointList &List, LinePrinter &P); +static std::string substitute(StringRef Cmd); +static int evaluateBreakpoints(Debugger &Dbg); +} // namespace breakpoint + +namespace object { +cl::opt<bool> SectionContents("contents", + cl::desc("Dump each section's contents"), + cl::sub(ObjectFileSubcommand)); +cl::opt<bool> SectionDependentModules("dep-modules", + cl::desc("Dump each dependent module"), + cl::sub(ObjectFileSubcommand)); +cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), + cl::OneOrMore, + cl::sub(ObjectFileSubcommand)); +} // namespace object + +namespace symbols { +static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>"), + cl::Required, cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> + SymbolPath("symbol-file", + cl::desc("The file from which to fetch symbol information."), + cl::value_desc("file"), cl::sub(SymbolsSubcommand)); + +enum class FindType { + None, + Function, + Block, + Namespace, + Type, + Variable, +}; +static cl::opt<FindType> Find( + "find", cl::desc("Choose search type:"), + cl::values( + clEnumValN(FindType::None, "none", "No search, just dump the module."), + clEnumValN(FindType::Function, "function", "Find functions."), + clEnumValN(FindType::Block, "block", "Find blocks."), + clEnumValN(FindType::Namespace, "namespace", "Find namespaces."), + clEnumValN(FindType::Type, "type", "Find types."), + clEnumValN(FindType::Variable, "variable", "Find global variables.")), + cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> Name("name", cl::desc("Name to find."), + cl::sub(SymbolsSubcommand)); +static cl::opt<bool> + Regex("regex", + cl::desc("Search using regular expressions (avaliable for variables " + "and functions only)."), + cl::sub(SymbolsSubcommand)); +static cl::opt<std::string> + Context("context", + cl::desc("Restrict search to the context of the given variable."), + cl::value_desc("variable"), cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> CompilerContext( + "compiler-context", + cl::desc("Specify a compiler context as \"kind:name,...\"."), + cl::value_desc("context"), cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> + Language("language", cl::desc("Specify a language type, like C99."), + cl::value_desc("language"), cl::sub(SymbolsSubcommand)); + +static cl::list<FunctionNameType> FunctionNameFlags( + "function-flags", cl::desc("Function search flags:"), + cl::values(clEnumValN(eFunctionNameTypeAuto, "auto", + "Automatically deduce flags based on name."), + clEnumValN(eFunctionNameTypeFull, "full", "Full function name."), + clEnumValN(eFunctionNameTypeBase, "base", "Base name."), + clEnumValN(eFunctionNameTypeMethod, "method", "Method name."), + clEnumValN(eFunctionNameTypeSelector, "selector", + "Selector name.")), + cl::sub(SymbolsSubcommand)); +static FunctionNameType getFunctionNameFlags() { + FunctionNameType Result = FunctionNameType(0); + for (FunctionNameType Flag : FunctionNameFlags) + Result = FunctionNameType(Result | Flag); + return Result; +} + +static cl::opt<bool> DumpAST("dump-ast", + cl::desc("Dump AST restored from symbols."), + cl::sub(SymbolsSubcommand)); +static cl::opt<bool> + DumpClangAST("dump-clang-ast", + cl::desc("Dump clang AST restored from symbols."), + cl::sub(SymbolsSubcommand)); + +static cl::opt<bool> Verify("verify", cl::desc("Verify symbol information."), + cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> File("file", + cl::desc("File (compile unit) to search."), + cl::sub(SymbolsSubcommand)); +static cl::opt<int> Line("line", cl::desc("Line to search."), + cl::sub(SymbolsSubcommand)); + +static Expected<CompilerDeclContext> getDeclContext(SymbolFile &Symfile); + +static Error findFunctions(lldb_private::Module &Module); +static Error findBlocks(lldb_private::Module &Module); +static Error findNamespaces(lldb_private::Module &Module); +static Error findTypes(lldb_private::Module &Module); +static Error findVariables(lldb_private::Module &Module); +static Error dumpModule(lldb_private::Module &Module); +static Error dumpAST(lldb_private::Module &Module); +static Error dumpClangAST(lldb_private::Module &Module); +static Error verify(lldb_private::Module &Module); + +static Expected<Error (*)(lldb_private::Module &)> getAction(); +static int dumpSymbols(Debugger &Dbg); +} // namespace symbols + +namespace irmemorymap { +static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>"), + cl::Required, + cl::sub(IRMemoryMapSubcommand)); +static cl::opt<std::string> CommandFile(cl::Positional, + cl::desc("<command-file>"), + cl::init("-"), + cl::sub(IRMemoryMapSubcommand)); +static cl::opt<bool> UseHostOnlyAllocationPolicy( + "host-only", cl::desc("Use the host-only allocation policy"), + cl::init(false), cl::sub(IRMemoryMapSubcommand)); + +using AllocationT = std::pair<addr_t, addr_t>; +using AddrIntervalMap = + IntervalMap<addr_t, unsigned, 8, IntervalMapHalfOpenInfo<addr_t>>; + +struct IRMemoryMapTestState { + TargetSP Target; + IRMemoryMap Map; + + AddrIntervalMap::Allocator IntervalMapAllocator; + AddrIntervalMap Allocations; + + StringMap<addr_t> Label2AddrMap; + + IRMemoryMapTestState(TargetSP Target) + : Target(Target), Map(Target), Allocations(IntervalMapAllocator) {} +}; + +bool evalMalloc(StringRef Line, IRMemoryMapTestState &State); +bool evalFree(StringRef Line, IRMemoryMapTestState &State); +int evaluateMemoryMapCommands(Debugger &Dbg); +} // namespace irmemorymap + +} // namespace opts + +std::vector<CompilerContext> parseCompilerContext() { + std::vector<CompilerContext> result; + if (opts::symbols::CompilerContext.empty()) + return result; + + StringRef str{opts::symbols::CompilerContext}; + SmallVector<StringRef, 8> entries_str; + str.split(entries_str, ',', /*maxSplit*/-1, /*keepEmpty=*/false); + for (auto entry_str : entries_str) { + StringRef key, value; + std::tie(key, value) = entry_str.split(':'); + auto kind = + StringSwitch<CompilerContextKind>(key) + .Case("TranslationUnit", CompilerContextKind::TranslationUnit) + .Case("Module", CompilerContextKind::Module) + .Case("Namespace", CompilerContextKind::Namespace) + .Case("Class", CompilerContextKind::Class) + .Case("Struct", CompilerContextKind::Struct) + .Case("Union", CompilerContextKind::Union) + .Case("Function", CompilerContextKind::Function) + .Case("Variable", CompilerContextKind::Variable) + .Case("Enum", CompilerContextKind::Enum) + .Case("Typedef", CompilerContextKind::Typedef) + .Case("AnyModule", CompilerContextKind::AnyModule) + .Case("AnyType", CompilerContextKind::AnyType) + .Default(CompilerContextKind::Invalid); + if (value.empty()) { + WithColor::error() << "compiler context entry has no \"name\"\n"; + exit(1); + } + result.push_back({kind, ConstString{value}}); + } + outs() << "Search context: {\n"; + for (auto entry: result) + entry.Dump(); + outs() << "}\n"; + + return result; +} + +template <typename... Args> +static Error make_string_error(const char *Format, Args &&... args) { + return llvm::make_error<llvm::StringError>( + llvm::formatv(Format, std::forward<Args>(args)...).str(), + llvm::inconvertibleErrorCode()); +} + +TargetSP opts::createTarget(Debugger &Dbg, const std::string &Filename) { + TargetSP Target; + Status ST = Dbg.GetTargetList().CreateTarget( + Dbg, Filename, /*triple*/ "", eLoadDependentsNo, + /*platform_options*/ nullptr, Target); + if (ST.Fail()) { + errs() << formatv("Failed to create target '{0}: {1}\n", Filename, ST); + exit(1); + } + return Target; +} + +std::unique_ptr<MemoryBuffer> opts::openFile(const std::string &Filename) { + auto MB = MemoryBuffer::getFileOrSTDIN(Filename); + if (!MB) { + errs() << formatv("Could not open file '{0}: {1}\n", Filename, + MB.getError().message()); + exit(1); + } + return std::move(*MB); +} + +void opts::breakpoint::dumpState(const BreakpointList &List, LinePrinter &P) { + P.formatLine("{0} breakpoint{1}", List.GetSize(), plural(List.GetSize())); + if (List.GetSize() > 0) + P.formatLine("At least one breakpoint."); + for (size_t i = 0, e = List.GetSize(); i < e; ++i) { + BreakpointSP BP = List.GetBreakpointAtIndex(i); + P.formatLine("Breakpoint ID {0}:", BP->GetID()); + AutoIndent Indent(P, 2); + P.formatLine("{0} location{1}.", BP->GetNumLocations(), + plural(BP->GetNumLocations())); + if (BP->GetNumLocations() > 0) + P.formatLine("At least one location."); + P.formatLine("{0} resolved location{1}.", BP->GetNumResolvedLocations(), + plural(BP->GetNumResolvedLocations())); + if (BP->GetNumResolvedLocations() > 0) + P.formatLine("At least one resolved location."); + for (size_t l = 0, le = BP->GetNumLocations(); l < le; ++l) { + BreakpointLocationSP Loc = BP->GetLocationAtIndex(l); + P.formatLine("Location ID {0}:", Loc->GetID()); + AutoIndent Indent(P, 2); + P.formatLine("Enabled: {0}", Loc->IsEnabled()); + P.formatLine("Resolved: {0}", Loc->IsResolved()); + SymbolContext sc; + Loc->GetAddress().CalculateSymbolContext(&sc); + lldb_private::StreamString S; + sc.DumpStopContext(&S, BP->GetTarget().GetProcessSP().get(), + Loc->GetAddress(), false, true, false, true, true); + P.formatLine("Address: {0}", S.GetString()); + } + } + P.NewLine(); +} + +std::string opts::breakpoint::substitute(StringRef Cmd) { + std::string Result; + raw_string_ostream OS(Result); + while (!Cmd.empty()) { + switch (Cmd[0]) { + case '%': + if (Cmd.consume_front("%p") && (Cmd.empty() || !isalnum(Cmd[0]))) { + OS << sys::path::parent_path(breakpoint::CommandFile); + break; + } + LLVM_FALLTHROUGH; + default: + size_t pos = Cmd.find('%'); + OS << Cmd.substr(0, pos); + Cmd = Cmd.substr(pos); + break; + } + } + return std::move(OS.str()); +} + +int opts::breakpoint::evaluateBreakpoints(Debugger &Dbg) { + TargetSP Target = opts::createTarget(Dbg, breakpoint::Target); + std::unique_ptr<MemoryBuffer> MB = opts::openFile(breakpoint::CommandFile); + + LinePrinter P(4, outs()); + StringRef Rest = MB->getBuffer(); + int HadErrors = 0; + while (!Rest.empty()) { + StringRef Line; + std::tie(Line, Rest) = Rest.split('\n'); + Line = Line.ltrim().rtrim(); + if (Line.empty() || Line[0] == '#') + continue; + + if (!Persistent) + Target->RemoveAllBreakpoints(/*internal_also*/ true); + + std::string Command = substitute(Line); + P.formatLine("Command: {0}", Command); + CommandReturnObject Result; + if (!Dbg.GetCommandInterpreter().HandleCommand( + Command.c_str(), /*add_to_history*/ eLazyBoolNo, Result)) { + P.formatLine("Failed: {0}", Result.GetErrorData()); + HadErrors = 1; + continue; + } + + dumpState(Target->GetBreakpointList(/*internal*/ false), P); + } + return HadErrors; +} + +Expected<CompilerDeclContext> +opts::symbols::getDeclContext(SymbolFile &Symfile) { + if (Context.empty()) + return CompilerDeclContext(); + VariableList List; + Symfile.FindGlobalVariables(ConstString(Context), nullptr, UINT32_MAX, List); + if (List.Empty()) + return make_string_error("Context search didn't find a match."); + if (List.GetSize() > 1) + return make_string_error("Context search found multiple matches."); + return List.GetVariableAtIndex(0)->GetDeclContext(); +} + +Error opts::symbols::findFunctions(lldb_private::Module &Module) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + SymbolContextList List; + if (!File.empty()) { + assert(Line != 0); + + FileSpec src_file(File); + size_t cu_count = Module.GetNumCompileUnits(); + for (size_t i = 0; i < cu_count; i++) { + lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i); + if (!cu_sp) + continue; + + LineEntry le; + cu_sp->FindLineEntry(0, Line, &src_file, false, &le); + if (!le.IsValid()) + continue; + const bool include_inlined_functions = false; + auto addr = + le.GetSameLineContiguousAddressRange(include_inlined_functions) + .GetBaseAddress(); + if (!addr.IsValid()) + continue; + + SymbolContext sc; + uint32_t resolved = + addr.CalculateSymbolContext(&sc, eSymbolContextFunction); + if (resolved & eSymbolContextFunction) + List.Append(sc); + } + } else if (Regex) { + RegularExpression RE(Name); + assert(RE.IsValid()); + List.Clear(); + Symfile.FindFunctions(RE, true, List); + } else { + Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + List.Clear(); + Symfile.FindFunctions(ConstString(Name), ContextPtr, getFunctionNameFlags(), + true, List); + } + outs() << formatv("Found {0} functions:\n", List.GetSize()); + StreamString Stream; + List.Dump(&Stream, nullptr); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +Error opts::symbols::findBlocks(lldb_private::Module &Module) { + assert(!Regex); + assert(!File.empty()); + assert(Line != 0); + + SymbolContextList List; + + FileSpec src_file(File); + size_t cu_count = Module.GetNumCompileUnits(); + for (size_t i = 0; i < cu_count; i++) { + lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i); + if (!cu_sp) + continue; + + LineEntry le; + cu_sp->FindLineEntry(0, Line, &src_file, false, &le); + if (!le.IsValid()) + continue; + const bool include_inlined_functions = false; + auto addr = le.GetSameLineContiguousAddressRange(include_inlined_functions) + .GetBaseAddress(); + if (!addr.IsValid()) + continue; + + SymbolContext sc; + uint32_t resolved = addr.CalculateSymbolContext(&sc, eSymbolContextBlock); + if (resolved & eSymbolContextBlock) + List.Append(sc); + } + + outs() << formatv("Found {0} blocks:\n", List.GetSize()); + StreamString Stream; + List.Dump(&Stream, nullptr); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +Error opts::symbols::findNamespaces(lldb_private::Module &Module) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + CompilerDeclContext Result = + Symfile.FindNamespace(ConstString(Name), ContextPtr); + if (Result) + outs() << "Found namespace: " + << Result.GetScopeQualifiedName().GetStringRef() << "\n"; + else + outs() << "Namespace not found.\n"; + return Error::success(); +} + +Error opts::symbols::findTypes(lldb_private::Module &Module) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + LanguageSet languages; + if (!Language.empty()) + languages.Insert(Language::GetLanguageTypeFromString(Language)); + + DenseSet<SymbolFile *> SearchedFiles; + TypeMap Map; + if (!Name.empty()) + Symfile.FindTypes(ConstString(Name), ContextPtr, UINT32_MAX, SearchedFiles, + Map); + else + Module.FindTypes(parseCompilerContext(), languages, SearchedFiles, Map); + + outs() << formatv("Found {0} types:\n", Map.GetSize()); + StreamString Stream; + Map.Dump(&Stream, false); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +Error opts::symbols::findVariables(lldb_private::Module &Module) { + SymbolFile &Symfile = *Module.GetSymbolFile(); + VariableList List; + if (Regex) { + RegularExpression RE(Name); + assert(RE.IsValid()); + Symfile.FindGlobalVariables(RE, UINT32_MAX, List); + } else if (!File.empty()) { + CompUnitSP CU; + for (size_t Ind = 0; !CU && Ind < Module.GetNumCompileUnits(); ++Ind) { + CompUnitSP Candidate = Module.GetCompileUnitAtIndex(Ind); + if (!Candidate || + Candidate->GetPrimaryFile().GetFilename().GetStringRef() != File) + continue; + if (CU) + return make_string_error("Multiple compile units for file `{0}` found.", + File); + CU = std::move(Candidate); + } + + if (!CU) + return make_string_error("Compile unit `{0}` not found.", File); + + List.AddVariables(CU->GetVariableList(true).get()); + } else { + Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile); + if (!ContextOr) + return ContextOr.takeError(); + CompilerDeclContext *ContextPtr = + ContextOr->IsValid() ? &*ContextOr : nullptr; + + Symfile.FindGlobalVariables(ConstString(Name), ContextPtr, UINT32_MAX, List); + } + outs() << formatv("Found {0} variables:\n", List.GetSize()); + StreamString Stream; + List.Dump(&Stream, false); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +Error opts::symbols::dumpModule(lldb_private::Module &Module) { + StreamString Stream; + Module.ParseAllDebugSymbols(); + Module.Dump(&Stream); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + +Error opts::symbols::dumpAST(lldb_private::Module &Module) { + Module.ParseAllDebugSymbols(); + + SymbolFile *symfile = Module.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + llvm::Expected<TypeSystem &> type_system_or_err = + symfile->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); + if (!type_system_or_err) + return make_string_error("Can't retrieve ClangASTContext"); + + auto *clang_ast_ctx = + llvm::dyn_cast_or_null<ClangASTContext>(&type_system_or_err.get()); + if (!clang_ast_ctx) + return make_string_error("Retrieved TypeSystem was not a ClangASTContext"); + + clang::ASTContext &ast_ctx = clang_ast_ctx->getASTContext(); + + clang::TranslationUnitDecl *tu = ast_ctx.getTranslationUnitDecl(); + if (!tu) + return make_string_error("Can't retrieve translation unit declaration."); + + tu->print(outs()); + + return Error::success(); +} + +Error opts::symbols::dumpClangAST(lldb_private::Module &Module) { + Module.ParseAllDebugSymbols(); + + SymbolFile *symfile = Module.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + llvm::Expected<TypeSystem &> type_system_or_err = + symfile->GetTypeSystemForLanguage(eLanguageTypeObjC_plus_plus); + if (!type_system_or_err) + return make_string_error("Can't retrieve ClangASTContext"); + + auto *clang_ast_ctx = + llvm::dyn_cast_or_null<ClangASTContext>(&type_system_or_err.get()); + if (!clang_ast_ctx) + return make_string_error("Retrieved TypeSystem was not a ClangASTContext"); + + StreamString Stream; + clang_ast_ctx->DumpFromSymbolFile(Stream, Name); + outs() << Stream.GetData() << "\n"; + + return Error::success(); +} + +Error opts::symbols::verify(lldb_private::Module &Module) { + SymbolFile *symfile = Module.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + uint32_t comp_units_count = symfile->GetNumCompileUnits(); + + outs() << "Found " << comp_units_count << " compile units.\n"; + + for (uint32_t i = 0; i < comp_units_count; i++) { + lldb::CompUnitSP comp_unit = symfile->GetCompileUnitAtIndex(i); + if (!comp_unit) + return make_string_error("Connot parse compile unit {0}.", i); + + outs() << "Processing '" + << comp_unit->GetPrimaryFile().GetFilename().AsCString() + << "' compile unit.\n"; + + LineTable *lt = comp_unit->GetLineTable(); + if (!lt) + return make_string_error("Can't get a line table of a compile unit."); + + uint32_t count = lt->GetSize(); + + outs() << "The line table contains " << count << " entries.\n"; + + if (count == 0) + continue; + + LineEntry le; + if (!lt->GetLineEntryAtIndex(0, le)) + return make_string_error("Can't get a line entry of a compile unit."); + + for (uint32_t i = 1; i < count; i++) { + lldb::addr_t curr_end = + le.range.GetBaseAddress().GetFileAddress() + le.range.GetByteSize(); + + if (!lt->GetLineEntryAtIndex(i, le)) + return make_string_error("Can't get a line entry of a compile unit"); + + if (curr_end > le.range.GetBaseAddress().GetFileAddress()) + return make_string_error( + "Line table of a compile unit is inconsistent."); + } + } + + outs() << "The symbol information is verified.\n"; + + return Error::success(); +} + +Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { + if (Verify && DumpAST) + return make_string_error( + "Cannot both verify symbol information and dump AST."); + + if (Verify) { + if (Find != FindType::None) + return make_string_error( + "Cannot both search and verify symbol information."); + if (Regex || !Context.empty() || !Name.empty() || !File.empty() || + Line != 0) + return make_string_error( + "-regex, -context, -name, -file and -line options are not " + "applicable for symbol verification."); + return verify; + } + + if (DumpAST) { + if (Find != FindType::None) + return make_string_error("Cannot both search and dump AST."); + if (Regex || !Context.empty() || !Name.empty() || !File.empty() || + Line != 0) + return make_string_error( + "-regex, -context, -name, -file and -line options are not " + "applicable for dumping AST."); + return dumpAST; + } + + if (DumpClangAST) { + if (Find != FindType::None) + return make_string_error("Cannot both search and dump clang AST."); + if (Regex || !Context.empty() || !File.empty() || Line != 0) + return make_string_error( + "-regex, -context, -name, -file and -line options are not " + "applicable for dumping clang AST."); + return dumpClangAST; + } + + if (Regex && !Context.empty()) + return make_string_error( + "Cannot search using both regular expressions and context."); + + if (Regex && !RegularExpression(Name).IsValid()) + return make_string_error("`{0}` is not a valid regular expression.", Name); + + if (Regex + !Context.empty() + !File.empty() >= 2) + return make_string_error( + "Only one of -regex, -context and -file may be used simultaneously."); + if (Regex && Name.empty()) + return make_string_error("-regex used without a -name"); + + switch (Find) { + case FindType::None: + if (!Context.empty() || !Name.empty() || !File.empty() || Line != 0) + return make_string_error( + "Specify search type (-find) to use search options."); + return dumpModule; + + case FindType::Function: + if (!File.empty() + (Line != 0) == 1) + return make_string_error("Both file name and line number must be " + "specified when searching a function " + "by file position."); + if (Regex + (getFunctionNameFlags() != 0) + !File.empty() >= 2) + return make_string_error("Only one of regular expression, function-flags " + "and file position may be used simultaneously " + "when searching a function."); + return findFunctions; + + case FindType::Block: + if (File.empty() || Line == 0) + return make_string_error("Both file name and line number must be " + "specified when searching a block."); + if (Regex || getFunctionNameFlags() != 0) + return make_string_error("Cannot use regular expression or " + "function-flags for searching a block."); + return findBlocks; + + case FindType::Namespace: + if (Regex || !File.empty() || Line != 0) + return make_string_error("Cannot search for namespaces using regular " + "expressions, file names or line numbers."); + return findNamespaces; + + case FindType::Type: + if (Regex || !File.empty() || Line != 0) + return make_string_error("Cannot search for types using regular " + "expressions, file names or line numbers."); + if (!Name.empty() && !CompilerContext.empty()) + return make_string_error("Name is ignored if compiler context present."); + + return findTypes; + + case FindType::Variable: + if (Line != 0) + return make_string_error("Cannot search for variables " + "using line numbers."); + return findVariables; + } + + llvm_unreachable("Unsupported symbol action."); +} + +int opts::symbols::dumpSymbols(Debugger &Dbg) { + auto ActionOr = getAction(); + if (!ActionOr) { + logAllUnhandledErrors(ActionOr.takeError(), WithColor::error(), ""); + return 1; + } + auto Action = *ActionOr; + + outs() << "Module: " << InputFile << "\n"; + ModuleSpec Spec{FileSpec(InputFile)}; + StringRef Symbols = SymbolPath.empty() ? InputFile : SymbolPath; + Spec.GetSymbolFileSpec().SetFile(Symbols, FileSpec::Style::native); + + auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + SymbolFile *Symfile = ModulePtr->GetSymbolFile(); + if (!Symfile) { + WithColor::error() << "Module has no symbol vendor.\n"; + return 1; + } + + if (Error E = Action(*ModulePtr)) { + WithColor::error() << toString(std::move(E)) << "\n"; + return 1; + } + + return 0; +} + +static void dumpSectionList(LinePrinter &Printer, const SectionList &List, bool is_subsection) { + size_t Count = List.GetNumSections(0); + if (Count == 0) { + Printer.formatLine("There are no {0}sections", is_subsection ? "sub" : ""); + return; + } + Printer.formatLine("Showing {0} {1}sections", Count, + is_subsection ? "sub" : ""); + for (size_t I = 0; I < Count; ++I) { + auto S = List.GetSectionAtIndex(I); + assert(S); + AutoIndent Indent(Printer, 2); + Printer.formatLine("Index: {0}", I); + Printer.formatLine("ID: {0:x}", S->GetID()); + Printer.formatLine("Name: {0}", S->GetName().GetStringRef()); + Printer.formatLine("Type: {0}", S->GetTypeAsCString()); + Printer.formatLine("Permissions: {0}", GetPermissionsAsCString(S->GetPermissions())); + Printer.formatLine("Thread specific: {0:y}", S->IsThreadSpecific()); + Printer.formatLine("VM address: {0:x}", S->GetFileAddress()); + Printer.formatLine("VM size: {0}", S->GetByteSize()); + Printer.formatLine("File size: {0}", S->GetFileSize()); + + if (opts::object::SectionContents) { + lldb_private::DataExtractor Data; + S->GetSectionData(Data); + ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; + Printer.formatBinary("Data: ", Bytes, 0); + } + + if (S->GetType() == eSectionTypeContainer) + dumpSectionList(Printer, S->GetChildren(), true); + Printer.NewLine(); + } +} + +static int dumpObjectFiles(Debugger &Dbg) { + LinePrinter Printer(4, llvm::outs()); + + int HadErrors = 0; + for (const auto &File : opts::object::InputFilenames) { + ModuleSpec Spec{FileSpec(File)}; + + auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + + ObjectFile *ObjectPtr = ModulePtr->GetObjectFile(); + if (!ObjectPtr) { + WithColor::error() << File << " not recognised as an object file\n"; + HadErrors = 1; + continue; + } + + // Fetch symbol vendor before we get the section list to give the symbol + // vendor a chance to populate it. + ModulePtr->GetSymbolFile(); + SectionList *Sections = ModulePtr->GetSectionList(); + if (!Sections) { + llvm::errs() << "Could not load sections for module " << File << "\n"; + HadErrors = 1; + continue; + } + + Printer.formatLine("Plugin name: {0}", ObjectPtr->GetPluginName()); + Printer.formatLine("Architecture: {0}", + ModulePtr->GetArchitecture().GetTriple().getTriple()); + Printer.formatLine("UUID: {0}", ModulePtr->GetUUID().GetAsString()); + Printer.formatLine("Executable: {0}", ObjectPtr->IsExecutable()); + Printer.formatLine("Stripped: {0}", ObjectPtr->IsStripped()); + Printer.formatLine("Type: {0}", ObjectPtr->GetType()); + Printer.formatLine("Strata: {0}", ObjectPtr->GetStrata()); + Printer.formatLine("Base VM address: {0:x}", + ObjectPtr->GetBaseAddress().GetFileAddress()); + + dumpSectionList(Printer, *Sections, /*is_subsection*/ false); + + if (opts::object::SectionDependentModules) { + // A non-empty section list ensures a valid object file. + auto Obj = ModulePtr->GetObjectFile(); + FileSpecList Files; + auto Count = Obj->GetDependentModules(Files); + Printer.formatLine("Showing {0} dependent module(s)", Count); + for (size_t I = 0; I < Files.GetSize(); ++I) { + AutoIndent Indent(Printer, 2); + Printer.formatLine("Name: {0}", + Files.GetFileSpecAtIndex(I).GetCString()); + } + Printer.NewLine(); + } + } + return HadErrors; +} + +bool opts::irmemorymap::evalMalloc(StringRef Line, + IRMemoryMapTestState &State) { + // ::= <label> = malloc <size> <alignment> + StringRef Label; + std::tie(Label, Line) = Line.split('='); + if (Line.empty()) + return false; + Label = Label.trim(); + Line = Line.trim(); + size_t Size; + uint8_t Alignment; + int Matches = sscanf(Line.data(), "malloc %zu %hhu", &Size, &Alignment); + if (Matches != 2) + return false; + + outs() << formatv("Command: {0} = malloc(size={1}, alignment={2})\n", Label, + Size, Alignment); + if (!isPowerOf2_32(Alignment)) { + outs() << "Malloc error: alignment is not a power of 2\n"; + exit(1); + } + + IRMemoryMap::AllocationPolicy AP = + UseHostOnlyAllocationPolicy ? IRMemoryMap::eAllocationPolicyHostOnly + : IRMemoryMap::eAllocationPolicyProcessOnly; + + // Issue the malloc in the target process with "-rw" permissions. + const uint32_t Permissions = 0x3; + const bool ZeroMemory = false; + Status ST; + addr_t Addr = + State.Map.Malloc(Size, Alignment, Permissions, AP, ZeroMemory, ST); + if (ST.Fail()) { + outs() << formatv("Malloc error: {0}\n", ST); + return true; + } + + // Print the result of the allocation before checking its validity. + outs() << formatv("Malloc: address = {0:x}\n", Addr); + + // Check that the allocation is aligned. + if (!Addr || Addr % Alignment != 0) { + outs() << "Malloc error: zero or unaligned allocation detected\n"; + exit(1); + } + + // In case of Size == 0, we still expect the returned address to be unique and + // non-overlapping. + addr_t EndOfRegion = Addr + std::max<size_t>(Size, 1); + if (State.Allocations.overlaps(Addr, EndOfRegion)) { + auto I = State.Allocations.find(Addr); + outs() << "Malloc error: overlapping allocation detected" + << formatv(", previous allocation at [{0:x}, {1:x})\n", I.start(), + I.stop()); + exit(1); + } + + // Insert the new allocation into the interval map. Use unique allocation + // IDs to inhibit interval coalescing. + static unsigned AllocationID = 0; + State.Allocations.insert(Addr, EndOfRegion, AllocationID++); + + // Store the label -> address mapping. + State.Label2AddrMap[Label] = Addr; + + return true; +} + +bool opts::irmemorymap::evalFree(StringRef Line, IRMemoryMapTestState &State) { + // ::= free <label> + if (!Line.consume_front("free")) + return false; + StringRef Label = Line.trim(); + + outs() << formatv("Command: free({0})\n", Label); + auto LabelIt = State.Label2AddrMap.find(Label); + if (LabelIt == State.Label2AddrMap.end()) { + outs() << "Free error: Invalid allocation label\n"; + exit(1); + } + + Status ST; + addr_t Addr = LabelIt->getValue(); + State.Map.Free(Addr, ST); + if (ST.Fail()) { + outs() << formatv("Free error: {0}\n", ST); + exit(1); + } + + // Erase the allocation from the live interval map. + auto Interval = State.Allocations.find(Addr); + if (Interval != State.Allocations.end()) { + outs() << formatv("Free: [{0:x}, {1:x})\n", Interval.start(), + Interval.stop()); + Interval.erase(); + } + + return true; +} + +int opts::irmemorymap::evaluateMemoryMapCommands(Debugger &Dbg) { + // Set up a Target. + TargetSP Target = opts::createTarget(Dbg, irmemorymap::Target); + + // Set up a Process. In order to allocate memory within a target, this + // process must be alive and must support JIT'ing. + CommandReturnObject Result; + Dbg.SetAsyncExecution(false); + CommandInterpreter &CI = Dbg.GetCommandInterpreter(); + auto IssueCmd = [&](const char *Cmd) -> bool { + return CI.HandleCommand(Cmd, eLazyBoolNo, Result); + }; + if (!IssueCmd("b main") || !IssueCmd("run")) { + outs() << formatv("Failed: {0}\n", Result.GetErrorData()); + exit(1); + } + + ProcessSP Process = Target->GetProcessSP(); + if (!Process || !Process->IsAlive() || !Process->CanJIT()) { + outs() << "Cannot use process to test IRMemoryMap\n"; + exit(1); + } + + // Set up an IRMemoryMap and associated testing state. + IRMemoryMapTestState State(Target); + + // Parse and apply commands from the command file. + std::unique_ptr<MemoryBuffer> MB = opts::openFile(irmemorymap::CommandFile); + StringRef Rest = MB->getBuffer(); + while (!Rest.empty()) { + StringRef Line; + std::tie(Line, Rest) = Rest.split('\n'); + Line = Line.ltrim().rtrim(); + + if (Line.empty() || Line[0] == '#') + continue; + + if (evalMalloc(Line, State)) + continue; + + if (evalFree(Line, State)) + continue; + + errs() << "Could not parse line: " << Line << "\n"; + exit(1); + } + return 0; +} + +int main(int argc, const char *argv[]) { + StringRef ToolName = argv[0]; + sys::PrintStackTraceOnErrorSignal(ToolName); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; + + cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); + + SystemLifetimeManager DebuggerLifetime; + if (auto e = DebuggerLifetime.Initialize( + std::make_unique<SystemInitializerTest>(), nullptr)) { + WithColor::error() << "initialization failed: " << toString(std::move(e)) + << '\n'; + return 1; + } + + auto TerminateDebugger = + llvm::make_scope_exit([&] { DebuggerLifetime.Terminate(); }); + + auto Dbg = lldb_private::Debugger::CreateInstance(); + ModuleList::GetGlobalModuleListProperties().SetEnableExternalLookup(false); + CommandReturnObject Result; + Dbg->GetCommandInterpreter().HandleCommand( + "settings set plugin.process.gdb-remote.packet-timeout 60", + /*add_to_history*/ eLazyBoolNo, Result); + + if (!opts::Log.empty()) + Dbg->EnableLog("lldb", {"all"}, opts::Log, 0, errs()); + + if (opts::BreakpointSubcommand) + return opts::breakpoint::evaluateBreakpoints(*Dbg); + if (opts::ObjectFileSubcommand) + return dumpObjectFiles(*Dbg); + if (opts::SymbolsSubcommand) + return opts::symbols::dumpSymbols(*Dbg); + if (opts::IRMemoryMapSubcommand) + return opts::irmemorymap::evaluateMemoryMapCommands(*Dbg); + + WithColor::error() << "No command specified.\n"; + return 1; +} diff --git a/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.cpp b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.cpp new file mode 100644 index 00000000000..af775250cb9 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.cpp @@ -0,0 +1,36 @@ +//===-- BreakpointBase.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "BreakpointBase.h" +#include "llvm/ADT/StringExtras.h" + +using namespace lldb_vscode; + +BreakpointBase::BreakpointBase(const llvm::json::Object &obj) + : condition(GetString(obj, "condition")), + hitCondition(GetString(obj, "hitCondition")), + logMessage(GetString(obj, "logMessage")) {} + +void BreakpointBase::SetCondition() { bp.SetCondition(condition.c_str()); } + +void BreakpointBase::SetHitCondition() { + uint64_t hitCount = 0; + if (llvm::to_integer(hitCondition, hitCount)) + bp.SetIgnoreCount(hitCount - 1); +} + +void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) { + if (condition != request_bp.condition) { + condition = request_bp.condition; + SetCondition(); + } + if (hitCondition != request_bp.hitCondition) { + hitCondition = request_bp.hitCondition; + SetHitCondition(); + } +} diff --git a/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.h b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.h new file mode 100644 index 00000000000..ee241a9713a --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.h @@ -0,0 +1,43 @@ +//===-- BreakpointBase.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_BREAKPOINTBASE_H_ +#define LLDBVSCODE_BREAKPOINTBASE_H_ + +#include "JSONUtils.h" +#include "lldb/API/SBBreakpoint.h" +#include "llvm/Support/JSON.h" +#include <string> + +namespace lldb_vscode { + +struct BreakpointBase { + + // An optional expression for conditional breakpoints. + std::string condition; + // An optional expression that controls how many hits of the breakpoint are + // ignored. The backend is expected to interpret the expression as needed + std::string hitCondition; + // If this attribute exists and is non-empty, the backend must not 'break' + // (stop) but log the message instead. Expressions within {} are + // interpolated. + std::string logMessage; + // The LLDB breakpoint associated wit this source breakpoint + lldb::SBBreakpoint bp; + + BreakpointBase() = default; + BreakpointBase(const llvm::json::Object &obj); + + void SetCondition(); + void SetHitCondition(); + void UpdateBreakpoint(const BreakpointBase &request_bp); +}; + +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-vscode/CMakeLists.txt new file mode 100644 index 00000000000..b527addb6ba --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/CMakeLists.txt @@ -0,0 +1,54 @@ +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) + add_definitions( -DIMPORT_LIBLLDB ) + list(APPEND extra_libs lldbHost) +endif () + +if (HAVE_LIBPTHREAD) + list(APPEND extra_libs pthread) +endif () + + +if(APPLE) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/lldb-vscode-Info.plist.in + ${CMAKE_CURRENT_BINARY_DIR}/lldb-vscode-Info.plist + ) + # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-vscode-Info.plist") +endif() + +# We need to include the llvm components we depend on manually, as liblldb does +# not re-export those. +set(LLVM_LINK_COMPONENTS Support) +add_lldb_tool(lldb-vscode + lldb-vscode.cpp + BreakpointBase.cpp + ExceptionBreakpoint.cpp + FunctionBreakpoint.cpp + IOStream.cpp + JSONUtils.cpp + LLDBUtils.cpp + SourceBreakpoint.cpp + VSCode.cpp + + LINK_LIBS + liblldb + ${host_lib} + ${extra_libs} + + LINK_COMPONENTS + Support + ) + +if(LLDB_BUILD_FRAMEWORK) + # In the build-tree, we know the exact path to the framework directory. + # The installed framework can be in different locations. + lldb_setup_rpaths(lldb-vscode + BUILD_RPATH + "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}" + INSTALL_RPATH + "@loader_path/../../../SharedFrameworks" + "@loader_path/../../System/Library/PrivateFrameworks" + "@loader_path/../../Library/PrivateFrameworks" + ) +endif() diff --git a/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp new file mode 100644 index 00000000000..090463ff60c --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp @@ -0,0 +1,31 @@ +//===-- ExceptionBreakpoint.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ExceptionBreakpoint.h" +#include "VSCode.h" + +namespace lldb_vscode { + +void ExceptionBreakpoint::SetBreakpoint() { + if (bp.IsValid()) + return; + bool catch_value = filter.find("_catch") != std::string::npos; + bool throw_value = filter.find("_throw") != std::string::npos; + bp = g_vsc.target.BreakpointCreateForException(language, catch_value, + throw_value); +} + +void ExceptionBreakpoint::ClearBreakpoint() { + if (!bp.IsValid()) + return; + g_vsc.target.BreakpointDelete(bp.GetID()); + bp = lldb::SBBreakpoint(); +} + +} // namespace lldb_vscode + diff --git a/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.h b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.h new file mode 100644 index 00000000000..fd9f8efe237 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.h @@ -0,0 +1,37 @@ +//===-- ExceptionBreakpoint.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_EXCEPTIONBREAKPOINT_H_ +#define LLDBVSCODE_EXCEPTIONBREAKPOINT_H_ + +#include <string> + +#include "lldb/API/SBBreakpoint.h" + +namespace lldb_vscode { + +struct ExceptionBreakpoint { + std::string filter; + std::string label; + lldb::LanguageType language; + bool default_value; + lldb::SBBreakpoint bp; + ExceptionBreakpoint(std::string f, std::string l, lldb::LanguageType lang) : + filter(std::move(f)), + label(std::move(l)), + language(lang), + default_value(false), + bp() {} + + void SetBreakpoint(); + void ClearBreakpoint(); +}; + +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp new file mode 100644 index 00000000000..1ac2e856f16 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp @@ -0,0 +1,27 @@ +//===-- FunctionBreakpoint.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FunctionBreakpoint.h" +#include "VSCode.h" + +namespace lldb_vscode { + +FunctionBreakpoint::FunctionBreakpoint(const llvm::json::Object &obj) + : BreakpointBase(obj), functionName(GetString(obj, "name")) {} + +void FunctionBreakpoint::SetBreakpoint() { + if (functionName.empty()) + return; + bp = g_vsc.target.BreakpointCreateByName(functionName.c_str()); + if (!condition.empty()) + SetCondition(); + if (!hitCondition.empty()) + SetHitCondition(); +} + +} diff --git a/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.h b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.h new file mode 100644 index 00000000000..dbc184914f0 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.h @@ -0,0 +1,28 @@ +//===-- FunctionBreakpoint.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_FUNCTIONBREAKPOINT_H_ +#define LLDBVSCODE_FUNCTIONBREAKPOINT_H_ + +#include "BreakpointBase.h" + +namespace lldb_vscode { + +struct FunctionBreakpoint : public BreakpointBase { + std::string functionName; + + FunctionBreakpoint() = default; + FunctionBreakpoint(const llvm::json::Object &obj); + + // Set this breakpoint in LLDB as a new breakpoint + void SetBreakpoint(); +}; + +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/IOStream.cpp b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.cpp new file mode 100644 index 00000000000..4b11b90b4c2 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.cpp @@ -0,0 +1,158 @@ +//===-- IOStream.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IOStream.h" + +#if defined(_WIN32) +#include <io.h> +#else +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> +#endif + +#include <fstream> +#include <string> +#include <vector> + +using namespace lldb_vscode; + +StreamDescriptor::StreamDescriptor() {} + +StreamDescriptor::StreamDescriptor(StreamDescriptor &&other) { + *this = std::move(other); +} + +StreamDescriptor::~StreamDescriptor() { + if (!m_close) + return; + + if (m_is_socket) +#if defined(_WIN32) + ::closesocket(m_socket); +#else + ::close(m_socket); +#endif + else + ::close(m_fd); +} + +StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) { + m_close = other.m_close; + other.m_close = false; + m_is_socket = other.m_is_socket; + if (m_is_socket) + m_socket = other.m_socket; + else + m_fd = other.m_fd; + return *this; +} + +StreamDescriptor StreamDescriptor::from_socket(SOCKET s, bool close) { + StreamDescriptor sd; + sd.m_is_socket = true; + sd.m_socket = s; + sd.m_close = close; + return sd; +} + +StreamDescriptor StreamDescriptor::from_file(int fd, bool close) { + StreamDescriptor sd; + sd.m_is_socket = false; + sd.m_fd = fd; + sd.m_close = close; + return sd; +} + +bool OutputStream::write_full(llvm::StringRef str) { + while (!str.empty()) { + int bytes_written = 0; + if (descriptor.m_is_socket) + bytes_written = ::send(descriptor.m_socket, str.data(), str.size(), 0); + else + bytes_written = ::write(descriptor.m_fd, str.data(), str.size()); + + if (bytes_written < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return false; + } + str = str.drop_front(bytes_written); + } + + return true; +} + +bool InputStream::read_full(std::ofstream *log, size_t length, + std::string &text) { + std::string data; + data.resize(length); + + char *ptr = &data[0]; + while (length != 0) { + int bytes_read = 0; + if (descriptor.m_is_socket) + bytes_read = ::recv(descriptor.m_socket, ptr, length, 0); + else + bytes_read = ::read(descriptor.m_fd, ptr, length); + + if (bytes_read == 0) { + if (log) + *log << "End of file (EOF) reading from input file.\n"; + return false; + } + if (bytes_read < 0) { + int reason = 0; +#if defined(_WIN32) + if (descriptor.m_is_socket) + reason = WSAGetLastError(); + else + reason = errno; +#else + reason = errno; + if (reason == EINTR || reason == EAGAIN) + continue; +#endif + + if (log) + *log << "Error " << reason << " reading from input file.\n"; + return false; + } + + assert(bytes_read >= 0 && (size_t)bytes_read <= length); + ptr += bytes_read; + length -= bytes_read; + } + text += data; + return true; +} + +bool InputStream::read_line(std::ofstream *log, std::string &line) { + line.clear(); + while (true) { + if (!read_full(log, 1, line)) + return false; + + if (llvm::StringRef(line).endswith("\r\n")) + break; + } + line.erase(line.size() - 2); + return true; +} + +bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) { + std::string result; + if (!read_full(log, expected.size(), result)) + return false; + if (expected != result) { + if (log) + *log << "Warning: Expected '" << expected.str() << "', got '" << result + << "\n"; + } + return true; +} diff --git a/gnu/llvm/lldb/tools/lldb-vscode/IOStream.h b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.h new file mode 100644 index 00000000000..8414c09e9fe --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.h @@ -0,0 +1,69 @@ +//===-- IOStream.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_IOSTREAM_H_ +#define LLDBVSCODE_IOSTREAM_H_ + +#if defined(_WIN32) +// We need to #define NOMINMAX in order to skip `min()` and `max()` macro +// definitions that conflict with other system headers. +// We also need to #undef GetObject (which is defined to GetObjectW) because +// the JSON code we use also has methods named `GetObject()` and we conflict +// against these. +#define NOMINMAX +#include <windows.h> +#else +typedef int SOCKET; +#endif + +#include "llvm/ADT/StringRef.h" + +#include <fstream> +#include <string> + +// Windows requires different system calls for dealing with sockets and other +// types of files, so we can't simply have one code path that just uses read +// and write everywhere. So we need an abstraction in order to allow us to +// treat them identically. +namespace lldb_vscode { +struct StreamDescriptor { + StreamDescriptor(); + ~StreamDescriptor(); + StreamDescriptor(StreamDescriptor &&other); + + StreamDescriptor &operator=(StreamDescriptor &&other); + + static StreamDescriptor from_socket(SOCKET s, bool close); + static StreamDescriptor from_file(int fd, bool close); + + bool m_is_socket = false; + bool m_close = false; + union { + int m_fd; + SOCKET m_socket; + }; +}; + +struct InputStream { + StreamDescriptor descriptor; + + bool read_full(std::ofstream *log, size_t length, std::string &text); + + bool read_line(std::ofstream *log, std::string &line); + + bool read_expected(std::ofstream *log, llvm::StringRef expected); +}; + +struct OutputStream { + StreamDescriptor descriptor; + + bool write_full(llvm::StringRef str); +}; +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp new file mode 100644 index 00000000000..b83f56445e2 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -0,0 +1,873 @@ +//===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <algorithm> + +#include "llvm/Support/FormatAdapters.h" + +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBValue.h" +#include "lldb/Host/PosixApi.h" + +#include "ExceptionBreakpoint.h" +#include "JSONUtils.h" +#include "LLDBUtils.h" +#include "VSCode.h" + +namespace lldb_vscode { + +void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, + llvm::StringRef str) { + if (LLVM_LIKELY(llvm::json::isUTF8(str))) + obj.try_emplace(key, str.str()); + else + obj.try_emplace(key, llvm::json::fixUTF8(str)); +} + +llvm::StringRef GetAsString(const llvm::json::Value &value) { + if (auto s = value.getAsString()) + return *s; + return llvm::StringRef(); +} + +// Gets a string from a JSON object using the key, or returns an empty string. +llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) { + if (auto value = obj.getString(key)) + return GetAsString(*value); + return llvm::StringRef(); +} + +llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) { + if (obj == nullptr) + return llvm::StringRef(); + return GetString(*obj, key); +} + +// Gets an unsigned integer from a JSON object using the key, or returns the +// specified fail value. +uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key, + uint64_t fail_value) { + if (auto value = obj.getInteger(key)) + return (uint64_t)*value; + return fail_value; +} + +uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key, + uint64_t fail_value) { + if (obj == nullptr) + return fail_value; + return GetUnsigned(*obj, key, fail_value); +} + +bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key, + bool fail_value) { + if (auto value = obj.getBoolean(key)) + return *value; + if (auto value = obj.getInteger(key)) + return *value != 0; + return fail_value; +} + +bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key, + bool fail_value) { + if (obj == nullptr) + return fail_value; + return GetBoolean(*obj, key, fail_value); +} + +int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key, + int64_t fail_value) { + if (auto value = obj.getInteger(key)) + return *value; + return fail_value; +} + +int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key, + int64_t fail_value) { + if (obj == nullptr) + return fail_value; + return GetSigned(*obj, key, fail_value); +} + +bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) { + return obj.find(key) != obj.end(); +} + +std::vector<std::string> GetStrings(const llvm::json::Object *obj, + llvm::StringRef key) { + std::vector<std::string> strs; + auto json_array = obj->getArray(key); + if (!json_array) + return strs; + for (const auto &value : *json_array) { + switch (value.kind()) { + case llvm::json::Value::String: + strs.push_back(value.getAsString()->str()); + break; + case llvm::json::Value::Number: + case llvm::json::Value::Boolean: { + std::string s; + llvm::raw_string_ostream strm(s); + strm << value; + strs.push_back(strm.str()); + break; + } + case llvm::json::Value::Null: + case llvm::json::Value::Object: + case llvm::json::Value::Array: + break; + } + } + return strs; +} + +void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object, + llvm::StringRef key) { + + llvm::StringRef value = v.GetValue(); + llvm::StringRef summary = v.GetSummary(); + llvm::StringRef type_name = v.GetType().GetDisplayTypeName(); + + std::string result; + llvm::raw_string_ostream strm(result); + if (!value.empty()) { + strm << value; + if (!summary.empty()) + strm << ' ' << summary; + } else if (!summary.empty()) { + strm << ' ' << summary; + } else if (!type_name.empty()) { + strm << type_name; + lldb::addr_t address = v.GetLoadAddress(); + if (address != LLDB_INVALID_ADDRESS) + strm << " @ " << llvm::format_hex(address, 0); + } + strm.flush(); + EmplaceSafeString(object, key, result); +} + +void FillResponse(const llvm::json::Object &request, + llvm::json::Object &response) { + // Fill in all of the needed response fields to a "request" and set "success" + // to true by default. + response.try_emplace("type", "response"); + response.try_emplace("seq", (int64_t)0); + EmplaceSafeString(response, "command", GetString(request, "command")); + const int64_t seq = GetSigned(request, "seq", 0); + response.try_emplace("request_seq", seq); + response.try_emplace("success", true); +} + +// "Scope": { +// "type": "object", +// "description": "A Scope is a named container for variables. Optionally +// a scope can map to a source or a range within a source.", +// "properties": { +// "name": { +// "type": "string", +// "description": "Name of the scope such as 'Arguments', 'Locals'." +// }, +// "variablesReference": { +// "type": "integer", +// "description": "The variables of this scope can be retrieved by +// passing the value of variablesReference to the +// VariablesRequest." +// }, +// "namedVariables": { +// "type": "integer", +// "description": "The number of named variables in this scope. The +// client can use this optional information to present +// the variables in a paged UI and fetch them in chunks." +// }, +// "indexedVariables": { +// "type": "integer", +// "description": "The number of indexed variables in this scope. The +// client can use this optional information to present +// the variables in a paged UI and fetch them in chunks." +// }, +// "expensive": { +// "type": "boolean", +// "description": "If true, the number of variables in this scope is +// large or expensive to retrieve." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "Optional source for this scope." +// }, +// "line": { +// "type": "integer", +// "description": "Optional start line of the range covered by this +// scope." +// }, +// "column": { +// "type": "integer", +// "description": "Optional start column of the range covered by this +// scope." +// }, +// "endLine": { +// "type": "integer", +// "description": "Optional end line of the range covered by this scope." +// }, +// "endColumn": { +// "type": "integer", +// "description": "Optional end column of the range covered by this +// scope." +// } +// }, +// "required": [ "name", "variablesReference", "expensive" ] +// } +llvm::json::Value CreateScope(const llvm::StringRef name, + int64_t variablesReference, + int64_t namedVariables, bool expensive) { + llvm::json::Object object; + EmplaceSafeString(object, "name", name.str()); + object.try_emplace("variablesReference", variablesReference); + object.try_emplace("expensive", expensive); + object.try_emplace("namedVariables", namedVariables); + return llvm::json::Value(std::move(object)); +} + +// "Breakpoint": { +// "type": "object", +// "description": "Information about a Breakpoint created in setBreakpoints +// or setFunctionBreakpoints.", +// "properties": { +// "id": { +// "type": "integer", +// "description": "An optional unique identifier for the breakpoint." +// }, +// "verified": { +// "type": "boolean", +// "description": "If true breakpoint could be set (but not necessarily +// at the desired location)." +// }, +// "message": { +// "type": "string", +// "description": "An optional message about the state of the breakpoint. +// This is shown to the user and can be used to explain +// why a breakpoint could not be verified." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "The source where the breakpoint is located." +// }, +// "line": { +// "type": "integer", +// "description": "The start line of the actual range covered by the +// breakpoint." +// }, +// "column": { +// "type": "integer", +// "description": "An optional start column of the actual range covered +// by the breakpoint." +// }, +// "endLine": { +// "type": "integer", +// "description": "An optional end line of the actual range covered by +// the breakpoint." +// }, +// "endColumn": { +// "type": "integer", +// "description": "An optional end column of the actual range covered by +// the breakpoint. If no end line is given, then the end +// column is assumed to be in the start line." +// } +// }, +// "required": [ "verified" ] +// } +llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc) { + // Each breakpoint location is treated as a separate breakpoint for VS code. + // They don't have the notion of a single breakpoint with multiple locations. + llvm::json::Object object; + if (!bp_loc.IsValid()) + return llvm::json::Value(std::move(object)); + + object.try_emplace("verified", true); + const auto vs_id = MakeVSCodeBreakpointID(bp_loc); + object.try_emplace("id", vs_id); + auto bp_addr = bp_loc.GetAddress(); + if (bp_addr.IsValid()) { + auto line_entry = bp_addr.GetLineEntry(); + const auto line = line_entry.GetLine(); + if (line != UINT32_MAX) + object.try_emplace("line", line); + object.try_emplace("source", CreateSource(line_entry)); + } + return llvm::json::Value(std::move(object)); +} + +void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints) { + if (!bp.IsValid()) + return; + const auto num_locations = bp.GetNumLocations(); + if (num_locations == 0) + return; + for (size_t i = 0; i < num_locations; ++i) { + auto bp_loc = bp.GetLocationAtIndex(i); + breakpoints.emplace_back(CreateBreakpoint(bp_loc)); + } +} + +// "Event": { +// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, { +// "type": "object", +// "description": "Server-initiated event.", +// "properties": { +// "type": { +// "type": "string", +// "enum": [ "event" ] +// }, +// "event": { +// "type": "string", +// "description": "Type of event." +// }, +// "body": { +// "type": [ "array", "boolean", "integer", "null", "number" , +// "object", "string" ], +// "description": "Event-specific information." +// } +// }, +// "required": [ "type", "event" ] +// }] +// }, +// "ProtocolMessage": { +// "type": "object", +// "description": "Base class of requests, responses, and events.", +// "properties": { +// "seq": { +// "type": "integer", +// "description": "Sequence number." +// }, +// "type": { +// "type": "string", +// "description": "Message type.", +// "_enum": [ "request", "response", "event" ] +// } +// }, +// "required": [ "seq", "type" ] +// } +llvm::json::Object CreateEventObject(const llvm::StringRef event_name) { + llvm::json::Object event; + event.try_emplace("seq", 0); + event.try_emplace("type", "event"); + EmplaceSafeString(event, "event", event_name); + return event; +} + +// "ExceptionBreakpointsFilter": { +// "type": "object", +// "description": "An ExceptionBreakpointsFilter is shown in the UI as an +// option for configuring how exceptions are dealt with.", +// "properties": { +// "filter": { +// "type": "string", +// "description": "The internal ID of the filter. This value is passed +// to the setExceptionBreakpoints request." +// }, +// "label": { +// "type": "string", +// "description": "The name of the filter. This will be shown in the UI." +// }, +// "default": { +// "type": "boolean", +// "description": "Initial value of the filter. If not specified a value +// 'false' is assumed." +// } +// }, +// "required": [ "filter", "label" ] +// } +llvm::json::Value +CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { + llvm::json::Object object; + EmplaceSafeString(object, "filter", bp.filter); + EmplaceSafeString(object, "label", bp.label); + object.try_emplace("default", bp.default_value); + return llvm::json::Value(std::move(object)); +} + +// "Source": { +// "type": "object", +// "description": "A Source is a descriptor for source code. It is returned +// from the debug adapter as part of a StackFrame and it is +// used by clients when specifying breakpoints.", +// "properties": { +// "name": { +// "type": "string", +// "description": "The short name of the source. Every source returned +// from the debug adapter has a name. When sending a +// source to the debug adapter this name is optional." +// }, +// "path": { +// "type": "string", +// "description": "The path of the source to be shown in the UI. It is +// only used to locate and load the content of the +// source if no sourceReference is specified (or its +// value is 0)." +// }, +// "sourceReference": { +// "type": "number", +// "description": "If sourceReference > 0 the contents of the source must +// be retrieved through the SourceRequest (even if a path +// is specified). A sourceReference is only valid for a +// session, so it must not be used to persist a source." +// }, +// "presentationHint": { +// "type": "string", +// "description": "An optional hint for how to present the source in the +// UI. A value of 'deemphasize' can be used to indicate +// that the source is not available or that it is +// skipped on stepping.", +// "enum": [ "normal", "emphasize", "deemphasize" ] +// }, +// "origin": { +// "type": "string", +// "description": "The (optional) origin of this source: possible values +// 'internal module', 'inlined content from source map', +// etc." +// }, +// "sources": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Source" +// }, +// "description": "An optional list of sources that are related to this +// source. These may be the source that generated this +// source." +// }, +// "adapterData": { +// "type":["array","boolean","integer","null","number","object","string"], +// "description": "Optional data that a debug adapter might want to loop +// through the client. The client should leave the data +// intact and persist it across sessions. The client +// should not interpret the data." +// }, +// "checksums": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Checksum" +// }, +// "description": "The checksums associated with this file." +// } +// } +// } +llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) { + llvm::json::Object object; + lldb::SBFileSpec file = line_entry.GetFileSpec(); + if (file.IsValid()) { + const char *name = file.GetFilename(); + if (name) + EmplaceSafeString(object, "name", name); + char path[PATH_MAX] = ""; + file.GetPath(path, sizeof(path)); + if (path[0]) { + EmplaceSafeString(object, "path", std::string(path)); + } + } + return llvm::json::Value(std::move(object)); +} + +llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) { + disasm_line = 0; + auto line_entry = frame.GetLineEntry(); + if (line_entry.GetFileSpec().IsValid()) + return CreateSource(line_entry); + + llvm::json::Object object; + const auto pc = frame.GetPC(); + + lldb::SBInstructionList insts; + lldb::SBFunction function = frame.GetFunction(); + lldb::addr_t low_pc = LLDB_INVALID_ADDRESS; + if (function.IsValid()) { + low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target); + auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); + if (addr_srcref != g_vsc.addr_to_source_ref.end()) { + // We have this disassembly cached already, return the existing + // sourceReference + object.try_emplace("sourceReference", addr_srcref->second); + disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); + } else { + insts = function.GetInstructions(g_vsc.target); + } + } else { + lldb::SBSymbol symbol = frame.GetSymbol(); + if (symbol.IsValid()) { + low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target); + auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); + if (addr_srcref != g_vsc.addr_to_source_ref.end()) { + // We have this disassembly cached already, return the existing + // sourceReference + object.try_emplace("sourceReference", addr_srcref->second); + disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); + } else { + insts = symbol.GetInstructions(g_vsc.target); + } + } + } + const auto num_insts = insts.GetSize(); + if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) { + EmplaceSafeString(object, "name", frame.GetFunctionName()); + SourceReference source; + llvm::raw_string_ostream src_strm(source.content); + std::string line; + for (size_t i = 0; i < num_insts; ++i) { + lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); + const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target); + const char *m = inst.GetMnemonic(g_vsc.target); + const char *o = inst.GetOperands(g_vsc.target); + const char *c = inst.GetComment(g_vsc.target); + if (pc == inst_addr) + disasm_line = i + 1; + const auto inst_offset = inst_addr - low_pc; + int spaces = 0; + if (inst_offset < 10) + spaces = 3; + else if (inst_offset < 100) + spaces = 2; + else if (inst_offset < 1000) + spaces = 1; + line.clear(); + llvm::raw_string_ostream line_strm(line); + line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr, + inst_offset, llvm::fmt_repeat(' ', spaces), m, + o); + + // If there is a comment append it starting at column 60 or after one + // space past the last char + const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60); + if (c && c[0]) { + if (line.size() < comment_row) + line_strm.indent(comment_row - line_strm.str().size()); + line_strm << " # " << c; + } + src_strm << line_strm.str() << "\n"; + source.addr_to_line[inst_addr] = i + 1; + } + // Flush the source stream + src_strm.str(); + auto sourceReference = VSCode::GetNextSourceReference(); + g_vsc.source_map[sourceReference] = std::move(source); + g_vsc.addr_to_source_ref[low_pc] = sourceReference; + object.try_emplace("sourceReference", sourceReference); + } + return llvm::json::Value(std::move(object)); +} + +// "StackFrame": { +// "type": "object", +// "description": "A Stackframe contains the source location.", +// "properties": { +// "id": { +// "type": "integer", +// "description": "An identifier for the stack frame. It must be unique +// across all threads. This id can be used to retrieve +// the scopes of the frame with the 'scopesRequest' or +// to restart the execution of a stackframe." +// }, +// "name": { +// "type": "string", +// "description": "The name of the stack frame, typically a method name." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "The optional source of the frame." +// }, +// "line": { +// "type": "integer", +// "description": "The line within the file of the frame. If source is +// null or doesn't exist, line is 0 and must be ignored." +// }, +// "column": { +// "type": "integer", +// "description": "The column within the line. If source is null or +// doesn't exist, column is 0 and must be ignored." +// }, +// "endLine": { +// "type": "integer", +// "description": "An optional end line of the range covered by the +// stack frame." +// }, +// "endColumn": { +// "type": "integer", +// "description": "An optional end column of the range covered by the +// stack frame." +// }, +// "moduleId": { +// "type": ["integer", "string"], +// "description": "The module associated with this frame, if any." +// }, +// "presentationHint": { +// "type": "string", +// "enum": [ "normal", "label", "subtle" ], +// "description": "An optional hint for how to present this frame in +// the UI. A value of 'label' can be used to indicate +// that the frame is an artificial frame that is used +// as a visual label or separator. A value of 'subtle' +// can be used to change the appearance of a frame in +// a 'subtle' way." +// } +// }, +// "required": [ "id", "name", "line", "column" ] +// } +llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) { + llvm::json::Object object; + int64_t frame_id = MakeVSCodeFrameID(frame); + object.try_emplace("id", frame_id); + EmplaceSafeString(object, "name", frame.GetFunctionName()); + int64_t disasm_line = 0; + object.try_emplace("source", CreateSource(frame, disasm_line)); + + auto line_entry = frame.GetLineEntry(); + if (disasm_line > 0) { + object.try_emplace("line", disasm_line); + } else { + auto line = line_entry.GetLine(); + if (line == UINT32_MAX) + line = 0; + object.try_emplace("line", line); + } + object.try_emplace("column", line_entry.GetColumn()); + return llvm::json::Value(std::move(object)); +} + +// "Thread": { +// "type": "object", +// "description": "A Thread", +// "properties": { +// "id": { +// "type": "integer", +// "description": "Unique identifier for the thread." +// }, +// "name": { +// "type": "string", +// "description": "A name of the thread." +// } +// }, +// "required": [ "id", "name" ] +// } +llvm::json::Value CreateThread(lldb::SBThread &thread) { + llvm::json::Object object; + object.try_emplace("id", (int64_t)thread.GetThreadID()); + char thread_str[64]; + snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID()); + const char *name = thread.GetName(); + if (name) { + std::string thread_with_name(thread_str); + thread_with_name += ' '; + thread_with_name += name; + EmplaceSafeString(object, "name", thread_with_name); + } else { + EmplaceSafeString(object, "name", std::string(thread_str)); + } + return llvm::json::Value(std::move(object)); +} + +// "StoppedEvent": { +// "allOf": [ { "$ref": "#/definitions/Event" }, { +// "type": "object", +// "description": "Event message for 'stopped' event type. The event +// indicates that the execution of the debuggee has stopped +// due to some condition. This can be caused by a break +// point previously set, a stepping action has completed, +// by executing a debugger statement etc.", +// "properties": { +// "event": { +// "type": "string", +// "enum": [ "stopped" ] +// }, +// "body": { +// "type": "object", +// "properties": { +// "reason": { +// "type": "string", +// "description": "The reason for the event. For backward +// compatibility this string is shown in the UI if +// the 'description' attribute is missing (but it +// must not be translated).", +// "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ] +// }, +// "description": { +// "type": "string", +// "description": "The full reason for the event, e.g. 'Paused +// on exception'. This string is shown in the UI +// as is." +// }, +// "threadId": { +// "type": "integer", +// "description": "The thread which was stopped." +// }, +// "text": { +// "type": "string", +// "description": "Additional information. E.g. if reason is +// 'exception', text contains the exception name. +// This string is shown in the UI." +// }, +// "allThreadsStopped": { +// "type": "boolean", +// "description": "If allThreadsStopped is true, a debug adapter +// can announce that all threads have stopped. +// The client should use this information to +// enable that all threads can be expanded to +// access their stacktraces. If the attribute +// is missing or false, only the thread with the +// given threadId can be expanded." +// } +// }, +// "required": [ "reason" ] +// } +// }, +// "required": [ "event", "body" ] +// }] +// } +llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, + uint32_t stop_id) { + llvm::json::Object event(CreateEventObject("stopped")); + llvm::json::Object body; + switch (thread.GetStopReason()) { + case lldb::eStopReasonTrace: + case lldb::eStopReasonPlanComplete: + body.try_emplace("reason", "step"); + break; + case lldb::eStopReasonBreakpoint: { + ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread); + if (exc_bp) { + body.try_emplace("reason", "exception"); + EmplaceSafeString(body, "description", exc_bp->label); + } else { + body.try_emplace("reason", "breakpoint"); + } + } break; + case lldb::eStopReasonWatchpoint: + case lldb::eStopReasonInstrumentation: + body.try_emplace("reason", "breakpoint"); + break; + case lldb::eStopReasonSignal: + body.try_emplace("reason", "exception"); + break; + case lldb::eStopReasonException: + body.try_emplace("reason", "exception"); + break; + case lldb::eStopReasonExec: + body.try_emplace("reason", "entry"); + break; + case lldb::eStopReasonThreadExiting: + case lldb::eStopReasonInvalid: + case lldb::eStopReasonNone: + break; + } + if (stop_id == 0) + body.try_emplace("reason", "entry"); + const lldb::tid_t tid = thread.GetThreadID(); + body.try_emplace("threadId", (int64_t)tid); + // If no description has been set, then set it to the default thread stopped + // description. If we have breakpoints that get hit and shouldn't be reported + // as breakpoints, then they will set the description above. + if (ObjectContainsKey(body, "description")) { + char description[1024]; + if (thread.GetStopDescription(description, sizeof(description))) { + EmplaceSafeString(body, "description", std::string(description)); + } + } + if (tid == g_vsc.focus_tid) { + body.try_emplace("threadCausedFocus", true); + } + body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid); + body.try_emplace("allThreadsStopped", true); + event.try_emplace("body", std::move(body)); + return llvm::json::Value(std::move(event)); +} + +// "Variable": { +// "type": "object", +// "description": "A Variable is a name/value pair. Optionally a variable +// can have a 'type' that is shown if space permits or when +// hovering over the variable's name. An optional 'kind' is +// used to render additional properties of the variable, +// e.g. different icons can be used to indicate that a +// variable is public or private. If the value is +// structured (has children), a handle is provided to +// retrieve the children with the VariablesRequest. If +// the number of named or indexed children is large, the +// numbers should be returned via the optional +// 'namedVariables' and 'indexedVariables' attributes. The +// client can use this optional information to present the +// children in a paged UI and fetch them in chunks.", +// "properties": { +// "name": { +// "type": "string", +// "description": "The variable's name." +// }, +// "value": { +// "type": "string", +// "description": "The variable's value. This can be a multi-line text, +// e.g. for a function the body of a function." +// }, +// "type": { +// "type": "string", +// "description": "The type of the variable's value. Typically shown in +// the UI when hovering over the value." +// }, +// "presentationHint": { +// "$ref": "#/definitions/VariablePresentationHint", +// "description": "Properties of a variable that can be used to determine +// how to render the variable in the UI." +// }, +// "evaluateName": { +// "type": "string", +// "description": "Optional evaluatable name of this variable which can +// be passed to the 'EvaluateRequest' to fetch the +// variable's value." +// }, +// "variablesReference": { +// "type": "integer", +// "description": "If variablesReference is > 0, the variable is +// structured and its children can be retrieved by +// passing variablesReference to the VariablesRequest." +// }, +// "namedVariables": { +// "type": "integer", +// "description": "The number of named child variables. The client can +// use this optional information to present the children +// in a paged UI and fetch them in chunks." +// }, +// "indexedVariables": { +// "type": "integer", +// "description": "The number of indexed child variables. The client +// can use this optional information to present the +// children in a paged UI and fetch them in chunks." +// } +// }, +// "required": [ "name", "value", "variablesReference" ] +// } +llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, + int64_t varID, bool format_hex) { + llvm::json::Object object; + auto name = v.GetName(); + EmplaceSafeString(object, "name", name ? name : "<null>"); + if (format_hex) + v.SetFormat(lldb::eFormatHex); + SetValueForKey(v, object, "value"); + auto type_cstr = v.GetType().GetDisplayTypeName(); + EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME); + if (varID != INT64_MAX) + object.try_emplace("id", varID); + if (v.MightHaveChildren()) + object.try_emplace("variablesReference", variablesReference); + else + object.try_emplace("variablesReference", (int64_t)0); + lldb::SBStream evaluateStream; + v.GetExpressionPath(evaluateStream); + const char *evaluateName = evaluateStream.GetData(); + if (evaluateName && evaluateName[0]) + EmplaceSafeString(object, "evaluateName", std::string(evaluateName)); + return llvm::json::Value(std::move(object)); +} + +} // namespace lldb_vscode + diff --git a/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.h b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.h new file mode 100644 index 00000000000..2391ac32b5c --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.h @@ -0,0 +1,395 @@ +//===-- JSONUtils.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_JSONUTILS_H_ +#define LLDBVSCODE_JSONUTILS_H_ + +#include <stdint.h> +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" +#include "VSCodeForward.h" + +namespace lldb_vscode { + +/// Emplace a StringRef in a json::Object after enusring that the +/// string is valid UTF8. If not, first call llvm::json::fixUTF8 +/// before emplacing. +/// +/// \param[in] obj +/// A JSON object that we will attempt to emplace the value in +/// +/// \param[in] key +/// The key to use when emplacing the value +/// +/// \param[in] str +/// The string to emplace +void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, + llvm::StringRef str); + +/// Extract simple values as a string. +/// +/// \param[in] value +/// A JSON value to extract the string from. +/// +/// \return +/// A llvm::StringRef that contains the string value, or an empty +/// string if \a value isn't a string. +llvm::StringRef GetAsString(const llvm::json::Value &value); + +/// Extract the string value for the specified key from the +/// specified object. +/// +/// \param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// \param[in] key +/// The key to use when extracting the value +/// +/// \return +/// A llvm::StringRef that contains the string value for the +/// specified \a key, or an empty string if there is no key that +/// matches or if the value is not a string. +llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key); +llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key); + +/// Extract the unsigned integer value for the specified key from +/// the specified object. +/// +/// \param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// \param[in] key +/// The key to use when extracting the value +/// +/// \return +/// The unsigned integer value for the specified \a key, or +/// \a fail_value if there is no key that matches or if the +/// value is not an integer. +uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key, + uint64_t fail_value); +uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key, + uint64_t fail_value); + +/// Extract the boolean value for the specified key from the +/// specified object. +/// +/// \param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// \param[in] key +/// The key to use when extracting the value +/// +/// \return +/// The boolean value for the specified \a key, or \a fail_value +/// if there is no key that matches or if the value is not a +/// boolean value of an integer. +bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key, + bool fail_value); +bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key, + bool fail_value); + +/// Extract the signed integer for the specified key from the +/// specified object. +/// +/// \param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// \param[in] key +/// The key to use when extracting the value +/// +/// \return +/// The signed integer value for the specified \a key, or +/// \a fail_value if there is no key that matches or if the +/// value is not an integer. +int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key, + int64_t fail_value); +int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key, + int64_t fail_value); + +/// Check if the specified key exists in the specified object. +/// +/// \param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// \param[in] key +/// The key to check for +/// +/// \return +/// \b True if the key exists in the \a obj, \b False otherwise. +bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key); + +/// Extract an array of strings for the specified key from an object. +/// +/// String values in the array will be extracted without any quotes +/// around them. Numbers and Booleans will be converted into +/// strings. Any NULL, array or objects values in the array will be +/// ignored. +/// +/// \param[in] obj +/// A JSON object that we will attempt to extract the array from +/// +/// \param[in] key +/// The key to use when extracting the value +/// +/// \return +/// An array of string values for the specified \a key, or +/// \a fail_value if there is no key that matches or if the +/// value is not an array or all items in the array are not +/// strings, numbers or booleans. +std::vector<std::string> GetStrings(const llvm::json::Object *obj, + llvm::StringRef key); + +/// Fill a response object given the request object. +/// +/// The \a response object will get its "type" set to "response", +/// the "seq" set to zero, "response_seq" set to the "seq" value from +/// \a request, "command" set to the "command" from \a request, +/// and "success" set to true. +/// +/// \param[in] request +/// The request object received from a call to VSCode::ReadJSON(). +/// +/// \param[in,out] response +/// An empty llvm::json::Object object that will be filled +/// in as noted in description. +void FillResponse(const llvm::json::Object &request, + llvm::json::Object &response); + +/// Emplace the string value from an SBValue into the supplied object +/// using \a key as the key that will contain the value. +/// +/// The value is what we will display in VS Code. Some SBValue objects +/// can have a value and/or a summary. If a value has both, we +/// combine the value and the summary into one string. If we only have a +/// value or summary, then that is considered the value. If there is +/// no value and no summary then the value is the type name followed by +/// the address of the type if it has an address. +/// +/// +/// \param[in] v +/// A lldb::SBValue object to extract the string value from +/// +/// +/// \param[in] object +/// The object to place the value object into +/// +/// +/// \param[in] key +/// The key name to use when inserting the value object we create +void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object, + llvm::StringRef key); + +/// Converts \a bp to a JSON value and appends all locations to the +/// \a breakpoints array. +/// +/// \param[in] bp +/// A LLDB breakpoint object which will get all locations extracted +/// and converted into a JSON objects in the \a breakpoints array +/// +/// \param[in] breakpoints +/// A JSON array that will get a llvm::json::Value for \a bp +/// appended to it. +void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints); + +/// Converts breakpoint location to a Visual Studio Code "Breakpoint" +/// JSON object and appends it to the \a breakpoints array. +/// +/// \param[in] bp_loc +/// A LLDB breakpoint location object to convert into a JSON value +/// +/// \return +/// A "Breakpoint" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc); + +/// Create a "Event" JSON object using \a event_name as the event name +/// +/// \param[in] event_name +/// The string value to use for the "event" key in the JSON object. +/// +/// \return +/// A "Event" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Object CreateEventObject(const llvm::StringRef event_name); + +/// Create a "ExceptionBreakpointsFilter" JSON object as described in +/// the Visual Studio Code debug adaptor definition. +/// +/// \param[in] bp +/// The exception breakppoint object to use +/// +/// \return +/// A "ExceptionBreakpointsFilter" JSON object with that follows +/// the formal JSON definition outlined by Microsoft. +llvm::json::Value +CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp); + +/// Create a "Scope" JSON object as described in the Visual Studio Code +/// debug adaptor definition. +/// +/// \param[in] name +/// The value to place into the "name" key +// +/// \param[in] variablesReference +/// The value to place into the "variablesReference" key +// +/// \param[in] namedVariables +/// The value to place into the "namedVariables" key +// +/// \param[in] expensive +/// The value to place into the "expensive" key +/// +/// \return +/// A "Scope" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateScope(const llvm::StringRef name, + int64_t variablesReference, + int64_t namedVariables, bool expensive); + +/// Create a "Source" JSON object as described in the Visual Studio Code +/// debug adaptor definition. +/// +/// \param[in] line_entry +/// The LLDB line table to use when populating out the "Source" +/// object +/// +/// \return +/// A "Source" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry); + +/// Create a "Source" object for a given frame. +/// +/// When there is no source file information for a stack frame, we will +/// create disassembly for a function and store a permanent +/// "sourceReference" that contains the textual disassembly for a +/// function along with address to line information. The "Source" object +/// that is created will contain a "sourceReference" that the VSCode +/// protocol can later fetch as text in order to display disassembly. +/// The PC will be extracted from the frame and the disassembly line +/// within the source referred to by "sourceReference" will be filled +/// in. +/// +/// \param[in] frame +/// The LLDB stack frame to use when populating out the "Source" +/// object. +/// +/// \param[out] disasm_line +/// The line within the "sourceReference" file that the PC from +/// \a frame matches. +/// +/// \return +/// A "Source" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line); + +/// Create a "StackFrame" object for a LLDB frame object. +/// +/// This function will fill in the following keys in the returned +/// object: +/// "id" - the stack frame ID as an integer +/// "name" - the function name as a string +/// "source" - source file information as a "Source" VSCode object +/// "line" - the source file line number as an integer +/// "column" - the source file column number as an integer +/// +/// \param[in] frame +/// The LLDB stack frame to use when populating out the "StackFrame" +/// object. +/// +/// \return +/// A "StackFrame" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateStackFrame(lldb::SBFrame &frame); + +/// Create a "Thread" object for a LLDB thread object. +/// +/// This function will fill in the following keys in the returned +/// object: +/// "id" - the thread ID as an integer +/// "name" - the thread name as a string which combines the LLDB +/// thread index ID along with the string name of the thread +/// from the OS if it has a name. +/// +/// \param[in] thread +/// The LLDB thread to use when populating out the "Thread" +/// object. +/// +/// \return +/// A "Thread" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateThread(lldb::SBThread &thread); + +/// Create a "StoppedEvent" object for a LLDB thread object. +/// +/// This function will fill in the following keys in the returned +/// object's "body" object: +/// "reason" - With a valid stop reason enumeration string value +/// that Microsoft specifies +/// "threadId" - The thread ID as an integer +/// "description" - a stop description (like "breakpoint 12.3") as a +/// string +/// "preserveFocusHint" - a boolean value that states if this thread +/// should keep the focus in the GUI. +/// "allThreadsStopped" - set to True to indicate that all threads +/// stop when any thread stops. +/// +/// \param[in] thread +/// The LLDB thread to use when populating out the "StoppedEvent" +/// object. +/// +/// \return +/// A "StoppedEvent" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, uint32_t stop_id); + +/// Create a "Variable" object for a LLDB thread object. +/// +/// This function will fill in the following keys in the returned +/// object: +/// "name" - the name of the variable +/// "value" - the value of the variable as a string +/// "type" - the typename of the variable as a string +/// "id" - a unique identifier for a value in case there are multiple +/// variables with the same name. Other parts of the VSCode +/// protocol refer to values by name so this can help +/// disambiguate such cases if a IDE passes this "id" value +/// back down. +/// "variablesReference" - Zero if the variable has no children, +/// non-zero integer otherwise which can be used to expand +/// the variable. +/// "evaluateName" - The name of the variable to use in expressions +/// as a string. +/// +/// \param[in] v +/// The LLDB value to use when populating out the "Variable" +/// object. +/// +/// \param[in] variablesReference +/// The variable reference. Zero if this value isn't structured +/// and has no children, non-zero if it does have children and +/// might be asked to expand itself. +/// +/// \param[in] varID +/// A unique variable identifier to help in properly identifying +/// variables with the same name. This is an extension to the +/// VS protocol. +/// +/// \param[in] format_hex +/// It set to true the variable will be formatted as hex in +/// the "value" key value pair for the value of the variable. +/// +/// \return +/// A "Variable" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, + int64_t varID, bool format_hex); + +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.cpp b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.cpp new file mode 100644 index 00000000000..5be293dab4e --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.cpp @@ -0,0 +1,97 @@ +//===-- LLDBUtils.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LLDBUtils.h" +#include "VSCode.h" + +namespace lldb_vscode { + +void RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands, + llvm::raw_ostream &strm) { + if (commands.empty()) + return; + lldb::SBCommandInterpreter interp = g_vsc.debugger.GetCommandInterpreter(); + if (!prefix.empty()) + strm << prefix << "\n"; + for (const auto &command : commands) { + lldb::SBCommandReturnObject result; + strm << "(lldb) " << command << "\n"; + interp.HandleCommand(command.c_str(), result); + auto output_len = result.GetOutputSize(); + if (output_len) { + const char *output = result.GetOutput(); + strm << output; + } + auto error_len = result.GetErrorSize(); + if (error_len) { + const char *error = result.GetError(); + strm << error; + } + } +} + +std::string RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands) { + std::string s; + llvm::raw_string_ostream strm(s); + RunLLDBCommands(prefix, commands, strm); + strm.flush(); + return s; +} + +bool ThreadHasStopReason(lldb::SBThread &thread) { + switch (thread.GetStopReason()) { + case lldb::eStopReasonTrace: + case lldb::eStopReasonPlanComplete: + case lldb::eStopReasonBreakpoint: + case lldb::eStopReasonWatchpoint: + case lldb::eStopReasonInstrumentation: + case lldb::eStopReasonSignal: + case lldb::eStopReasonException: + case lldb::eStopReasonExec: + return true; + case lldb::eStopReasonThreadExiting: + case lldb::eStopReasonInvalid: + case lldb::eStopReasonNone: + break; + } + return false; +} + +static uint32_t constexpr THREAD_INDEX_SHIFT = 19; + +uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id) { + return dap_frame_id >> THREAD_INDEX_SHIFT; +} + +uint32_t GetLLDBFrameID(uint64_t dap_frame_id) { + return dap_frame_id & ((1u << THREAD_INDEX_SHIFT) - 1); +} + +int64_t MakeVSCodeFrameID(lldb::SBFrame &frame) { + return (int64_t)(frame.GetThread().GetIndexID() << THREAD_INDEX_SHIFT | + frame.GetFrameID()); +} + +static uint32_t constexpr BREAKPOINT_ID_SHIFT = 22; + +uint32_t GetLLDBBreakpointID(uint64_t dap_breakpoint_id) { + return dap_breakpoint_id >> BREAKPOINT_ID_SHIFT; +} + +uint32_t GetLLDBBreakpointLocationID(uint64_t dap_breakpoint_id) { + return dap_breakpoint_id & ((1u << BREAKPOINT_ID_SHIFT) - 1); +} + +int64_t MakeVSCodeBreakpointID(lldb::SBBreakpointLocation &bp_loc) { + return (int64_t)(bp_loc.GetBreakpoint().GetID() << BREAKPOINT_ID_SHIFT | + bp_loc.GetID()); +} + +} // namespace lldb_vscode diff --git a/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.h b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.h new file mode 100644 index 00000000000..82c17eb3cdc --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.h @@ -0,0 +1,151 @@ +//===-- LLDBUtils.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_LLDBUTILS_H_ +#define LLDBVSCODE_LLDBUTILS_H_ + +#include "VSCodeForward.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <string> +#include <vector> + +namespace lldb_vscode { + +/// Run a list of LLDB commands in the LLDB command interpreter. +/// +/// All output from every command, including the prompt + the command +/// is placed into the "strm" argument. +/// +/// \param[in] prefix +/// A string that will be printed into \a strm prior to emitting +/// the prompt + command and command output. Can be NULL. +/// +/// \param[in] commands +/// An array of LLDB commands to execute. +/// +/// \param[in] strm +/// The stream that will receive the prefix, prompt + command and +/// all command output. +void RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands, + llvm::raw_ostream &strm); + +/// Run a list of LLDB commands in the LLDB command interpreter. +/// +/// All output from every command, including the prompt + the command +/// is returned in the std::string return value. +/// +/// \param[in] prefix +/// A string that will be printed into \a strm prior to emitting +/// the prompt + command and command output. Can be NULL. +/// +/// \param[in] commands +/// An array of LLDB commands to execute. +/// +/// \return +/// A std::string that contains the prefix and all commands and +/// command output +std::string RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands); + +/// Check if a thread has a stop reason. +/// +/// \param[in] thread +/// The LLDB thread object to check +/// +/// \return +/// \b True if the thread has a valid stop reason, \b false +/// otherwise. +bool ThreadHasStopReason(lldb::SBThread &thread); + +/// Given a LLDB frame, make a frame ID that is unique to a specific +/// thread and frame. +/// +/// VSCode requires a Stackframe "id" to be unique, so we use the frame +/// index in the lower 32 bits and the thread index ID in the upper 32 +/// bits. +/// +/// \param[in] frame +/// The LLDB stack frame object generate the ID for +/// +/// \return +/// A unique integer that allows us to easily find the right +/// stack frame within a thread on subsequent VS code requests. +int64_t MakeVSCodeFrameID(lldb::SBFrame &frame); + +/// Given a VSCode frame ID, convert to a LLDB thread index id. +/// +/// VSCode requires a Stackframe "id" to be unique, so we use the frame +/// index in the lower THREAD_INDEX_SHIFT bits and the thread index ID in +/// the upper 32 - THREAD_INDEX_SHIFT bits. +/// +/// \param[in] dap_frame_id +/// The VSCode frame ID to convert to a thread index ID. +/// +/// \return +/// The LLDB thread index ID. +uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id); + +/// Given a VSCode frame ID, convert to a LLDB frame ID. +/// +/// VSCode requires a Stackframe "id" to be unique, so we use the frame +/// index in the lower THREAD_INDEX_SHIFT bits and the thread index ID in +/// the upper 32 - THREAD_INDEX_SHIFT bits. +/// +/// \param[in] dap_frame_id +/// The VSCode frame ID to convert to a frame ID. +/// +/// \return +/// The LLDB frame index ID. +uint32_t GetLLDBFrameID(uint64_t dap_frame_id); + +/// Given a LLDB breakpoint, make a breakpoint ID that is unique to a +/// specific breakpoint and breakpoint location. +/// +/// VSCode requires a Breakpoint "id" to be unique, so we use the +/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the +/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits. +/// +/// \param[in] bp_loc +/// The LLDB break point location. +/// +/// \return +/// A unique integer that allows us to easily find the right +/// stack frame within a thread on subsequent VS code requests. +int64_t MakeVSCodeBreakpointID(lldb::SBBreakpointLocation &bp_loc); + +/// Given a VSCode breakpoint ID, convert to a LLDB breakpoint ID. +/// +/// VSCode requires a Breakpoint "id" to be unique, so we use the +/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the +/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits. +/// +/// \param[in] dap_breakpoint_id +/// The VSCode breakpoint ID to convert to an LLDB breakpoint ID. +/// +/// \return +/// The LLDB breakpoint ID. +uint32_t GetLLDBBreakpointID(uint64_t dap_breakpoint_id); + +/// Given a VSCode breakpoint ID, convert to a LLDB breakpoint location ID. +/// +/// VSCode requires a Breakpoint "id" to be unique, so we use the +/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the +/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits. +/// +/// \param[in] dap_breakpoint_id +/// The VSCode frame ID to convert to a breakpoint location ID. +/// +/// \return +/// The LLDB breakpoint location ID. +uint32_t GetLLDBBreakpointLocationID(uint64_t dap_breakpoint_id); +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/README.md b/gnu/llvm/lldb/tools/lldb-vscode/README.md new file mode 100644 index 00000000000..2294659fc29 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/README.md @@ -0,0 +1,195 @@ + +# Table of Contents + +- [Introduction](#Introduction) +- [Installation](#Installation-Visual-Studio-Code) +- [Configurations](#configurations) + - [Launch Configuration Settings](#launch-configuration-settings) + - [Attach Configuration Settings](#attach-configuration-settings) + - [Example configurations](#example-configurations) + - [Launching](#launching) + - [Attach to process using process ID](#attach-using-pid) + - [Attach to process by name](#attach-by-name) + - [Loading a core file](#loading-a-core-file) + +# Introduction + +The `lldb-vscode` tool creates a command line tool that implements the [Visual +Studio Code Debug API](https://code.visualstudio.com/docs/extensionAPI/api-debugging). +It can be installed as an extension for the Visual Studio Code and Nuclide IDE. +The protocol is easy to run remotely and also can allow other tools and IDEs to +get a full featured debugger with a well defined protocol. + +# Installation for Visual Studio Code + +Installing the plug-in involves creating a directory in the `~/.vscode/extensions` folder and copying the package.json file that is in the same directory as this +documentation into it, and copying to symlinking a lldb-vscode binary into +the `bin` directory inside the plug-in directory. + +If you want to make a stand alone plug-in that you can send to others on unix systems: + +``` +$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0 +$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp /path/to/a/built/lldb-vscode . +$ cp /path/to/a/built/liblldb.so . +``` + + +If you want to make a stand alone plug-in that you can send to others on macOS systems: + +``` +$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0 +$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp /path/to/a/built/lldb-vscode . +$ rsync -av /path/to/a/built/LLDB.framework LLDB.framework +``` + +You might need to create additional directories for the `liblldb.so` or `LLDB.framework` inside or next to the `bin` folder depending on how the [rpath](https://en.wikipedia.org/wiki/Rpath) is set in your `lldb-vscode` binary. By default the `Debug` builds of LLDB usually includes +the current executable directory in the rpath, so these steps should work for most people. + +To create a plug-in that symlinks into your `lldb-vscode` in your build directory: + +``` +$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0 +$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ ln -s /path/to/a/built/lldb-vscode +``` + +This is handy if you want to debug and develope the `lldb-vscode` executable when adding features or fixing bugs. + +# Configurations + +Launching to attaching require you to create a [launch configuration](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations). This file +defines arguments that get passed to `lldb-vscode` and the configuration settings +control how the launch or attach happens. + +## Launch Configuration Settings + +When you launch a program with Visual Studio Code you will need to create a [launch.json](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations) +file that defines how your program will be run. The JSON configuration file can contain the following `lldb-vscode` specific launch key/value pairs: + +|parameter |type|req | | +|-------------------|----|:--:|---------| +|**name** |string|Y| A configuration name that will be displayed in the IDE. +|**type** |string|Y| Must be "lldb-vscode". +|**request** |string|Y| Must be "launch". +|**program** |string|Y| Path to the executable to launch. +|**args** |[string]|| An array of command line argument strings to be passed to the program being launched. +|**cwd** |string| | The program working directory. +|**env** |dictionary| | Environment variables to set when launching the program. The format of each environment variable string is "VAR=VALUE" for environment variables with values or just "VAR" for environment variables with no values. +|**stopOnEntry** |boolean| | Whether to stop program immediately after launching. +|**initCommands** |[string]| | LLDB commands executed upon debugger startup prior to creating the LLDB target. Commands and command output will be sent to the debugger console when they are executed. +|**preRunCommands** |[string]| | LLDB commands executed just before launching after the LLDB target has been created. Commands and command output will be sent to the debugger console when they are executed. +|**stopCommands** |[string]| | LLDB commands executed just after each stop. Commands and command output will be sent to the debugger console when they are executed. +|**exitCommands** |[string]| | LLDB commands executed when the program exits. Commands and command output will be sent to the debugger console when they are executed. +|**sourceMap** |[string[2]]| | Specify an array of path re-mappings. Each element in the array must be a two element array containing a source and destination pathname. +|**debuggerRoot** | string| |Specify a working directory to use when launching lldb-vscode. If the debug information in your executable contains relative paths, this option can be used so that `lldb-vscode` can find source files and object files that have relative paths. + +## Attaching Settings + +When attaching to a process using LLDB you can attach in a few ways + +1. Attach to an existing process using the process ID +2. Attach to an existing process by name +3. Attach by name by waiting for the next instance of a process to launch + +The JSON configuration file can contain the following `lldb-vscode` specific launch key/value pairs: + +|parameter |type |req | | +|-------------------|--------|:--:|---------| +|**name** |string |Y| A configuration name that will be displayed in the IDE. +|**type** |string |Y| Must be "lldb-vscode". +|**request** |string |Y| Must be "attach". +|**program** |string | | Path to the executable to attach to. This value is optional but can help to resolve breakpoints prior the attaching to the program. +|**pid** |number | | The process id of the process you wish to attach to. If **pid** is omitted, the debugger will attempt to attach to the program by finding a process whose file name matches the file name from **porgram**. Setting this value to `${command:pickMyProcess}` will allow interactive process selection in the IDE. +|**stopOnEntry** |boolean| | Whether to stop program immediately after launching. +|**waitFor** |boolean | | Wait for the process to launch. +|**initCommands** |[string]| | LLDB commands executed upon debugger startup prior to creating the LLDB target. Commands and command output will be sent to the debugger console when they are executed. +|**preRunCommands** |[string]| | LLDB commands executed just before launching after the LLDB target has been created. Commands and command output will be sent to the debugger console when they are executed. +|**stopCommands** |[string]| | LLDB commands executed just after each stop. Commands and command output will be sent to the debugger console when they are executed. +|**exitCommands** |[string]| | LLDB commands executed when the program exits. Commands and command output will be sent to the debugger console when they are executed. +|**attachCommands** |[string]| | LLDB commands that will be executed after **preRunCommands** which take place of the code that normally does the attach. The commands can create a new target and attach or launch it however desired. This allows custom launch and attach configurations. Core files can use `target create --core /path/to/core` to attach to core files. + + +## Example configurations + +### Launching + +This will launch `/tmp/a.out` with arguments `one`, `two`, and `three` and +adds `FOO=1` and `bar` to the environment: + +```javascript +{ + "type": "lldb-vscode", + "request": "launch", + "name": "Debug", + "program": "/tmp/a.out", + "args": [ "one", "two", "three" ], + "env": [ "FOO=1", "BAR" ], +} +``` + +### Attach using PID + +This will attach to a process `a.out` whose process ID is 123: + +```javascript +{ + "type": "lldb-vscode", + "request": "attach", + "name": "Attach to PID", + "program": "/tmp/a.out", + "pid": 123 +} +``` + +### Attach by Name + +This will attach to an existing process whose base +name matches `a.out`. All we have to do is leave the `pid` value out of the +above configuration: + +```javascript +{ + "name": "Attach to Name", + "type": "lldb-vscode", + "request": "attach", + "program": "/tmp/a.out", +} +``` + +If you want to ignore any existing a.out processes and wait for the next instance +to be launched you can add the "waitFor" key value pair: + +```javascript +{ + "name": "Attach to Name (wait)", + "type": "lldb-vscode", + "request": "attach", + "program": "/tmp/a.out", + "waitFor": true +} +``` + +This will work as long as the architecture, vendor and OS supports waiting +for processes. Currently MacOS is the only platform that supports this. + + +### Loading a Core File + +Loading a core file can use the `"attach"` request along with the +`"attachCommands"` to implement a custom attach: + +```javascript +{ + "name": "Attach to Name (wait)", + "type": "lldb-vscode", + "request": "attach", + "attachCommands": ["target create -c /path/to/123.core /path/to/executable"], + "stopOnEntry": false +} +``` diff --git a/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.cpp b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.cpp new file mode 100644 index 00000000000..91d1ad70ecd --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.cpp @@ -0,0 +1,26 @@ +//===-- SourceBreakpoint.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SourceBreakpoint.h" +#include "VSCode.h" + +namespace lldb_vscode { + +SourceBreakpoint::SourceBreakpoint(const llvm::json::Object &obj) + : BreakpointBase(obj), line(GetUnsigned(obj, "line", 0)), + column(GetUnsigned(obj, "column", 0)) {} + +void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) { + bp = g_vsc.target.BreakpointCreateByLocation(source_path.str().c_str(), line); + if (!condition.empty()) + SetCondition(); + if (!hitCondition.empty()) + SetHitCondition(); +} + +} // namespace lldb_vscode diff --git a/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.h b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.h new file mode 100644 index 00000000000..f17557ef1f8 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.h @@ -0,0 +1,38 @@ +//===-- SourceBreakpoint.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_SOURCEBREAKPOINT_H_ +#define LLDBVSCODE_SOURCEBREAKPOINT_H_ + +#include "llvm/ADT/StringRef.h" +#include "BreakpointBase.h" + +namespace lldb_vscode { + +struct SourceBreakpoint : public BreakpointBase { + + uint32_t line; ///< The source line of the breakpoint or logpoint + uint32_t column; ///< An optional source column of the breakpoint + + SourceBreakpoint() : BreakpointBase(), line(0), column(0) {} + SourceBreakpoint(const llvm::json::Object &obj); + + // Set this breakpoint in LLDB as a new breakpoint + void SetBreakpoint(const llvm::StringRef source_path); +}; + +inline bool operator<(const SourceBreakpoint &lhs, + const SourceBreakpoint &rhs) { + if (lhs.line == rhs.line) + return lhs.column < rhs.column; + return lhs.line < rhs.line; +} + +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/SourceReference.h b/gnu/llvm/lldb/tools/lldb-vscode/SourceReference.h new file mode 100644 index 00000000000..55f5b9b9c40 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/SourceReference.h @@ -0,0 +1,32 @@ +//===-- SourceReference.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_SOURCEREFERENCE_H_ +#define LLDBVSCODE_SOURCEREFERENCE_H_ + +#include "lldb/lldb-types.h" +#include "llvm/ADT/DenseMap.h" +#include <string> + +namespace lldb_vscode { + +struct SourceReference { + std::string content; + llvm::DenseMap<lldb::addr_t, int64_t> addr_to_line; + + int64_t GetLineForPC(lldb::addr_t pc) const { + auto addr_line = addr_to_line.find(pc); + if (addr_line != addr_to_line.end()) + return addr_line->second; + return 0; + } +}; + +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp new file mode 100644 index 00000000000..2f85627da4b --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp @@ -0,0 +1,306 @@ +//===-- VSCode.cpp ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <stdarg.h> +#include <fstream> +#include <mutex> + +#include "LLDBUtils.h" +#include "VSCode.h" +#include "llvm/Support/FormatVariadic.h" + +#if defined(_WIN32) +#define NOMINMAX +#include <windows.h> +#include <fcntl.h> +#include <io.h> +#endif + +using namespace lldb_vscode; + +namespace lldb_vscode { + +VSCode g_vsc; + +VSCode::VSCode() + : launch_info(nullptr), variables(), broadcaster("lldb-vscode"), + num_regs(0), num_locals(0), num_globals(0), log(), + exception_breakpoints( + {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus}, + {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus}, + {"objc_catch", "Objective C Catch", lldb::eLanguageTypeObjC}, + {"objc_throw", "Objective C Throw", lldb::eLanguageTypeObjC}, + {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift}, + {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}), + focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false), + stop_at_entry(false) { + const char *log_file_path = getenv("LLDBVSCODE_LOG"); +#if defined(_WIN32) +// Windows opens stdout and stdin in text mode which converts \n to 13,10 +// while the value is just 10 on Darwin/Linux. Setting the file mode to binary +// fixes this. + int result = _setmode(fileno(stdout), _O_BINARY); + assert(result); + result = _setmode(fileno(stdin), _O_BINARY); + (void)result; + assert(result); +#endif + if (log_file_path) + log.reset(new std::ofstream(log_file_path)); +} + +VSCode::~VSCode() { +} + +int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const { + auto pos = source_map.find(sourceReference); + if (pos != source_map.end()) + return pos->second.GetLineForPC(pc); + return 0; +} + +ExceptionBreakpoint *VSCode::GetExceptionBreakpoint(const std::string &filter) { + for (auto &bp : exception_breakpoints) { + if (bp.filter == filter) + return &bp; + } + return nullptr; +} + +ExceptionBreakpoint * +VSCode::GetExceptionBreakpoint(const lldb::break_id_t bp_id) { + for (auto &bp : exception_breakpoints) { + if (bp.bp.GetID() == bp_id) + return &bp; + } + return nullptr; +} + +// Send the JSON in "json_str" to the "out" stream. Correctly send the +// "Content-Length:" field followed by the length, followed by the raw +// JSON bytes. +void VSCode::SendJSON(const std::string &json_str) { + output.write_full("Content-Length: "); + output.write_full(llvm::utostr(json_str.size())); + output.write_full("\r\n\r\n"); + output.write_full(json_str); + + if (log) { + *log << "<-- " << std::endl + << "Content-Length: " << json_str.size() << "\r\n\r\n" + << json_str << std::endl; + } +} + +// Serialize the JSON value into a string and send the JSON packet to +// the "out" stream. +void VSCode::SendJSON(const llvm::json::Value &json) { + std::string s; + llvm::raw_string_ostream strm(s); + strm << json; + static std::mutex mutex; + std::lock_guard<std::mutex> locker(mutex); + SendJSON(strm.str()); +} + +// Read a JSON packet from the "in" stream. +std::string VSCode::ReadJSON() { + std::string length_str; + std::string json_str; + int length; + + if (!input.read_expected(log.get(), "Content-Length: ")) + return json_str; + + if (!input.read_line(log.get(), length_str)) + return json_str; + + if (!llvm::to_integer(length_str, length)) + return json_str; + + if (!input.read_expected(log.get(), "\r\n")) + return json_str; + + if (!input.read_full(log.get(), length, json_str)) + return json_str; + + return json_str; +} + +// "OutputEvent": { +// "allOf": [ { "$ref": "#/definitions/Event" }, { +// "type": "object", +// "description": "Event message for 'output' event type. The event +// indicates that the target has produced some output.", +// "properties": { +// "event": { +// "type": "string", +// "enum": [ "output" ] +// }, +// "body": { +// "type": "object", +// "properties": { +// "category": { +// "type": "string", +// "description": "The output category. If not specified, +// 'console' is assumed.", +// "_enum": [ "console", "stdout", "stderr", "telemetry" ] +// }, +// "output": { +// "type": "string", +// "description": "The output to report." +// }, +// "variablesReference": { +// "type": "number", +// "description": "If an attribute 'variablesReference' exists +// and its value is > 0, the output contains +// objects which can be retrieved by passing +// variablesReference to the VariablesRequest." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "An optional source location where the output +// was produced." +// }, +// "line": { +// "type": "integer", +// "description": "An optional source location line where the +// output was produced." +// }, +// "column": { +// "type": "integer", +// "description": "An optional source location column where the +// output was produced." +// }, +// "data": { +// "type":["array","boolean","integer","null","number","object", +// "string"], +// "description": "Optional data to report. For the 'telemetry' +// category the data will be sent to telemetry, for +// the other categories the data is shown in JSON +// format." +// } +// }, +// "required": ["output"] +// } +// }, +// "required": [ "event", "body" ] +// }] +// } +void VSCode::SendOutput(OutputType o, const llvm::StringRef output) { + if (output.empty()) + return; + + llvm::json::Object event(CreateEventObject("output")); + llvm::json::Object body; + const char *category = nullptr; + switch (o) { + case OutputType::Console: + category = "console"; + break; + case OutputType::Stdout: + category = "stdout"; + break; + case OutputType::Stderr: + category = "stderr"; + break; + case OutputType::Telemetry: + category = "telemetry"; + break; + } + body.try_emplace("category", category); + EmplaceSafeString(body, "output", output.str()); + event.try_emplace("body", std::move(body)); + SendJSON(llvm::json::Value(std::move(event))); +} + +void __attribute__((format(printf, 3, 4))) +VSCode::SendFormattedOutput(OutputType o, const char *format, ...) { + char buffer[1024]; + va_list args; + va_start(args, format); + int actual_length = vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + SendOutput(o, llvm::StringRef(buffer, + std::min<int>(actual_length, sizeof(buffer)))); +} + +int64_t VSCode::GetNextSourceReference() { + static int64_t ref = 0; + return ++ref; +} + +ExceptionBreakpoint * +VSCode::GetExceptionBPFromStopReason(lldb::SBThread &thread) { + const auto num = thread.GetStopReasonDataCount(); + // Check to see if have hit an exception breakpoint and change the + // reason to "exception", but only do so if all breakpoints that were + // hit are exception breakpoints. + ExceptionBreakpoint *exc_bp = nullptr; + for (size_t i = 0; i < num; i += 2) { + // thread.GetStopReasonDataAtIndex(i) will return the bp ID and + // thread.GetStopReasonDataAtIndex(i+1) will return the location + // within that breakpoint. We only care about the bp ID so we can + // see if this is an exception breakpoint that is getting hit. + lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i); + exc_bp = GetExceptionBreakpoint(bp_id); + // If any breakpoint is not an exception breakpoint, then stop and + // report this as a normal breakpoint + if (exc_bp == nullptr) + return nullptr; + } + return exc_bp; +} + +lldb::SBThread VSCode::GetLLDBThread(const llvm::json::Object &arguments) { + auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID); + return target.GetProcess().GetThreadByID(tid); +} + +lldb::SBFrame VSCode::GetLLDBFrame(const llvm::json::Object &arguments) { + const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX); + lldb::SBProcess process = target.GetProcess(); + // Upper 32 bits is the thread index ID + lldb::SBThread thread = + process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id)); + // Lower 32 bits is the frame index + return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id)); +} + +llvm::json::Value VSCode::CreateTopLevelScopes() { + llvm::json::Array scopes; + scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS, num_locals, false)); + scopes.emplace_back( + CreateScope("Globals", VARREF_GLOBALS, num_globals, false)); + scopes.emplace_back(CreateScope("Registers", VARREF_REGS, num_regs, false)); + return llvm::json::Value(std::move(scopes)); +} + +void VSCode::RunLLDBCommands(llvm::StringRef prefix, + const std::vector<std::string> &commands) { + SendOutput(OutputType::Console, + llvm::StringRef(::RunLLDBCommands(prefix, commands))); +} + +void VSCode::RunInitCommands() { + RunLLDBCommands("Running initCommands:", init_commands); +} + +void VSCode::RunPreRunCommands() { + RunLLDBCommands("Running preRunCommands:", pre_run_commands); +} + +void VSCode::RunStopCommands() { + RunLLDBCommands("Running stopCommands:", stop_commands); +} + +void VSCode::RunExitCommands() { + RunLLDBCommands("Running exitCommands:", exit_commands); +} + +} // namespace lldb_vscode diff --git a/gnu/llvm/lldb/tools/lldb-vscode/VSCode.h b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.h new file mode 100644 index 00000000000..be8b22806a4 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.h @@ -0,0 +1,141 @@ +//===-- VSCode.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_VSCODE_H_ +#define LLDBVSCODE_VSCODE_H_ + +#include <iosfwd> +#include <map> +#include <set> +#include <stdio.h> +#include <thread> + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include "lldb/API/SBAttachInfo.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCommunication.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBLaunchInfo.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" + +#include "ExceptionBreakpoint.h" +#include "FunctionBreakpoint.h" +#include "IOStream.h" +#include "SourceBreakpoint.h" +#include "SourceReference.h" + +#define VARREF_LOCALS (int64_t)1 +#define VARREF_GLOBALS (int64_t)2 +#define VARREF_REGS (int64_t)3 +#define VARREF_FIRST_VAR_IDX (int64_t)4 +#define VARREF_IS_SCOPE(v) (VARREF_LOCALS <= 1 && v < VARREF_FIRST_VAR_IDX) +#define VARIDX_TO_VARREF(i) ((i) + VARREF_FIRST_VAR_IDX) +#define VARREF_TO_VARIDX(v) ((v)-VARREF_FIRST_VAR_IDX) +#define NO_TYPENAME "<no-type>" + +namespace lldb_vscode { + +typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap; +typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; +enum class OutputType { Console, Stdout, Stderr, Telemetry }; + +struct VSCode { + InputStream input; + OutputStream output; + lldb::SBDebugger debugger; + lldb::SBTarget target; + lldb::SBAttachInfo attach_info; + lldb::SBLaunchInfo launch_info; + lldb::SBValueList variables; + lldb::SBBroadcaster broadcaster; + int64_t num_regs; + int64_t num_locals; + int64_t num_globals; + std::thread event_thread; + std::unique_ptr<std::ofstream> log; + llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref; + llvm::DenseMap<int64_t, SourceReference> source_map; + llvm::StringMap<SourceBreakpointMap> source_breakpoints; + FunctionBreakpointMap function_breakpoints; + std::vector<ExceptionBreakpoint> exception_breakpoints; + std::vector<std::string> init_commands; + std::vector<std::string> pre_run_commands; + std::vector<std::string> exit_commands; + std::vector<std::string> stop_commands; + lldb::tid_t focus_tid; + bool sent_terminated_event; + bool stop_at_entry; + // Keep track of the last stop thread index IDs as threads won't go away + // unless we send a "thread" event to indicate the thread exited. + llvm::DenseSet<lldb::tid_t> thread_ids; + VSCode(); + ~VSCode(); + VSCode(const VSCode &rhs) = delete; + void operator=(const VSCode &rhs) = delete; + int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const; + ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter); + ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); + // Send the JSON in "json_str" to the "out" stream. Correctly send the + // "Content-Length:" field followed by the length, followed by the raw + // JSON bytes. + void SendJSON(const std::string &json_str); + + // Serialize the JSON value into a string and send the JSON packet to + // the "out" stream. + void SendJSON(const llvm::json::Value &json); + + std::string ReadJSON(); + + void SendOutput(OutputType o, const llvm::StringRef output); + + void __attribute__((format(printf, 3, 4))) + SendFormattedOutput(OutputType o, const char *format, ...); + + static int64_t GetNextSourceReference(); + + ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread); + + lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); + + lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); + + llvm::json::Value CreateTopLevelScopes(); + + void RunLLDBCommands(llvm::StringRef prefix, + const std::vector<std::string> &commands); + + void RunInitCommands(); + void RunPreRunCommands(); + void RunStopCommands(); + void RunExitCommands(); +}; + +extern VSCode g_vsc; + +} // namespace lldb_vscode + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/VSCodeForward.h b/gnu/llvm/lldb/tools/lldb-vscode/VSCodeForward.h new file mode 100644 index 00000000000..4a89120d1ec --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/VSCodeForward.h @@ -0,0 +1,46 @@ +//===-- VSCodeForward.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_VSCODEFORWARD_H_ +#define LLDBVSCODE_VSCODEFORWARD_H_ + + +namespace lldb_vscode { +struct BreakpointBase; +struct ExceptionBreakpoint; +struct FunctionBreakpoint; +struct SourceBreakpoint; +struct SourceReference; +} // namespace lldb_vscode + +namespace lldb { +class SBAttachInfo; +class SBBreakpoint; +class SBBreakpointLocation; +class SBCommandInterpreter; +class SBCommandReturnObject; +class SBCommunication; +class SBDebugger; +class SBEvent; +class SBFrame; +class SBHostOS; +class SBInstruction; +class SBInstructionList; +class SBLanguageRuntime; +class SBLaunchInfo; +class SBLineEntry; +class SBListener; +class SBProcess; +class SBStream; +class SBStringList; +class SBTarget; +class SBThread; +class SBValue; +} // namespace lldb + +#endif diff --git a/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode-Info.plist.in b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode-Info.plist.in new file mode 100644 index 00000000000..2098e190d6b --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode-Info.plist.in @@ -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-vscode</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>lldb-vscode</string> + <key>CFBundleVersion</key> + <string>${LLDB_VERSION}</string> + <key>SecTaskAccess</key> + <array> + <string>allowed</string> + <string>debug</string> + </array> +</dict> +</plist> diff --git a/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode.cpp b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode.cpp new file mode 100644 index 00000000000..6d638a6a8f8 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -0,0 +1,2817 @@ +//===-- lldb-vscode.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#if defined(_WIN32) +// We need to #define NOMINMAX in order to skip `min()` and `max()` macro +// definitions that conflict with other system headers. +// We also need to #undef GetObject (which is defined to GetObjectW) because +// the JSON code we use also has methods named `GetObject()` and we conflict +// against these. +#define NOMINMAX +#include <windows.h> +#undef GetObject +#include <io.h> +#else +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> +#endif + +#include <algorithm> +#include <chrono> +#include <fstream> +#include <map> +#include <memory> +#include <mutex> +#include <set> +#include <sstream> +#include <thread> + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" + +#include "JSONUtils.h" +#include "LLDBUtils.h" +#include "VSCode.h" + +#if defined(_WIN32) +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif +typedef int socklen_t; +constexpr const char *dev_null_path = "nul"; + +#else +constexpr const char *dev_null_path = "/dev/null"; + +#endif + +using namespace lldb_vscode; + +namespace { + +typedef void (*RequestCallback)(const llvm::json::Object &command); + +enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch }; + +enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 }; + +SOCKET AcceptConnection(int portno) { + // Accept a socket connection from any host on "portno". + SOCKET newsockfd = -1; + struct sockaddr_in serv_addr, cli_addr; + SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + if (g_vsc.log) + *g_vsc.log << "error: opening socket (" << strerror(errno) << ")" + << std::endl; + } else { + memset((char *)&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + serv_addr.sin_port = htons(portno); + if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + if (g_vsc.log) + *g_vsc.log << "error: binding socket (" << strerror(errno) << ")" + << std::endl; + } else { + listen(sockfd, 5); + socklen_t clilen = sizeof(cli_addr); + newsockfd = + llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd, + (struct sockaddr *)&cli_addr, &clilen); + if (newsockfd < 0) + if (g_vsc.log) + *g_vsc.log << "error: accept (" << strerror(errno) << ")" + << std::endl; + } +#if defined(_WIN32) + closesocket(sockfd); +#else + close(sockfd); +#endif + } + return newsockfd; +} + +std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) { + // Create and return an array of "const char *", one for each C string in + // "strs" and terminate the list with a NULL. This can be used for argument + // vectors (argv) or environment vectors (envp) like those passed to the + // "main" function in C programs. + std::vector<const char *> argv; + for (const auto &s : strs) + argv.push_back(s.c_str()); + argv.push_back(nullptr); + return argv; +} + +// Send a "exited" event to indicate the process has exited. +void SendProcessExitedEvent(lldb::SBProcess &process) { + llvm::json::Object event(CreateEventObject("exited")); + llvm::json::Object body; + body.try_emplace("exitCode", (int64_t)process.GetExitStatus()); + event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); +} + +void SendThreadExitedEvent(lldb::tid_t tid) { + llvm::json::Object event(CreateEventObject("thread")); + llvm::json::Object body; + body.try_emplace("reason", "exited"); + body.try_emplace("threadId", (int64_t)tid); + event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); +} + +// Send a "terminated" event to indicate the process is done being +// debugged. +void SendTerminatedEvent() { + if (!g_vsc.sent_terminated_event) { + g_vsc.sent_terminated_event = true; + // Send a "terminated" event + llvm::json::Object event(CreateEventObject("terminated")); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); + } +} + +// Send a thread stopped event for all threads as long as the process +// is stopped. +void SendThreadStoppedEvent() { + lldb::SBProcess process = g_vsc.target.GetProcess(); + if (process.IsValid()) { + auto state = process.GetState(); + if (state == lldb::eStateStopped) { + llvm::DenseSet<lldb::tid_t> old_thread_ids; + old_thread_ids.swap(g_vsc.thread_ids); + uint32_t stop_id = process.GetStopID(); + const uint32_t num_threads = process.GetNumThreads(); + + // First make a pass through the threads to see if the focused thread + // has a stop reason. In case the focus thread doesn't have a stop + // reason, remember the first thread that has a stop reason so we can + // set it as the focus thread if below if needed. + lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID; + uint32_t num_threads_with_reason = 0; + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); + const lldb::tid_t tid = thread.GetThreadID(); + const bool has_reason = ThreadHasStopReason(thread); + // If the focus thread doesn't have a stop reason, clear the thread ID + if (tid == g_vsc.focus_tid && !has_reason) + g_vsc.focus_tid = LLDB_INVALID_THREAD_ID; + if (has_reason) { + ++num_threads_with_reason; + if (first_tid_with_reason == LLDB_INVALID_THREAD_ID) + first_tid_with_reason = tid; + } + } + + // We will have cleared g_vsc.focus_tid if he focus thread doesn't + // have a stop reason, so if it was cleared, or wasn't set, then set the + // focus thread to the first thread with a stop reason. + if (g_vsc.focus_tid == LLDB_INVALID_THREAD_ID) + g_vsc.focus_tid = first_tid_with_reason; + + // If no threads stopped with a reason, then report the first one so + // we at least let the UI know we stopped. + if (num_threads_with_reason == 0) { + lldb::SBThread thread = process.GetThreadAtIndex(0); + g_vsc.SendJSON(CreateThreadStopped(thread, stop_id)); + } else { + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); + g_vsc.thread_ids.insert(thread.GetThreadID()); + if (ThreadHasStopReason(thread)) { + g_vsc.SendJSON(CreateThreadStopped(thread, stop_id)); + } + } + } + + for (auto tid : old_thread_ids) { + auto end = g_vsc.thread_ids.end(); + auto pos = g_vsc.thread_ids.find(tid); + if (pos == end) + SendThreadExitedEvent(tid); + } + } else { + if (g_vsc.log) + *g_vsc.log << "error: SendThreadStoppedEvent() when process" + " isn't stopped (" + << lldb::SBDebugger::StateAsCString(state) << ')' + << std::endl; + } + } else { + if (g_vsc.log) + *g_vsc.log << "error: SendThreadStoppedEvent() invalid process" + << std::endl; + } + g_vsc.RunStopCommands(); +} + +// "ProcessEvent": { +// "allOf": [ +// { "$ref": "#/definitions/Event" }, +// { +// "type": "object", +// "description": "Event message for 'process' event type. The event +// indicates that the debugger has begun debugging a +// new process. Either one that it has launched, or one +// that it has attached to.", +// "properties": { +// "event": { +// "type": "string", +// "enum": [ "process" ] +// }, +// "body": { +// "type": "object", +// "properties": { +// "name": { +// "type": "string", +// "description": "The logical name of the process. This is +// usually the full path to process's executable +// file. Example: /home/myproj/program.js." +// }, +// "systemProcessId": { +// "type": "integer", +// "description": "The system process id of the debugged process. +// This property will be missing for non-system +// processes." +// }, +// "isLocalProcess": { +// "type": "boolean", +// "description": "If true, the process is running on the same +// computer as the debug adapter." +// }, +// "startMethod": { +// "type": "string", +// "enum": [ "launch", "attach", "attachForSuspendedLaunch" ], +// "description": "Describes how the debug engine started +// debugging this process.", +// "enumDescriptions": [ +// "Process was launched under the debugger.", +// "Debugger attached to an existing process.", +// "A project launcher component has launched a new process in +// a suspended state and then asked the debugger to attach." +// ] +// } +// }, +// "required": [ "name" ] +// } +// }, +// "required": [ "event", "body" ] +// } +// ] +// } +void SendProcessEvent(LaunchMethod launch_method) { + lldb::SBFileSpec exe_fspec = g_vsc.target.GetExecutable(); + char exe_path[PATH_MAX]; + exe_fspec.GetPath(exe_path, sizeof(exe_path)); + llvm::json::Object event(CreateEventObject("process")); + llvm::json::Object body; + EmplaceSafeString(body, "name", std::string(exe_path)); + const auto pid = g_vsc.target.GetProcess().GetProcessID(); + body.try_emplace("systemProcessId", (int64_t)pid); + body.try_emplace("isLocalProcess", true); + const char *startMethod = nullptr; + switch (launch_method) { + case Launch: + startMethod = "launch"; + break; + case Attach: + startMethod = "attach"; + break; + case AttachForSuspendedLaunch: + startMethod = "attachForSuspendedLaunch"; + break; + } + body.try_emplace("startMethod", startMethod); + event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); +} + +// Grab any STDOUT and STDERR from the process and send it up to VS Code +// via an "output" event to the "stdout" and "stderr" categories. +void SendStdOutStdErr(lldb::SBProcess &process) { + char buffer[1024]; + size_t count; + while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0) + g_vsc.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count)); + while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0) + g_vsc.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count)); +} + +// All events from the debugger, target, process, thread and frames are +// received in this function that runs in its own thread. We are using a +// "FILE *" to output packets back to VS Code and they have mutexes in them +// them prevent multiple threads from writing simultaneously so no locking +// is required. +void EventThreadFunction() { + lldb::SBEvent event; + lldb::SBListener listener = g_vsc.debugger.GetListener(); + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (lldb::SBProcess::EventIsProcessEvent(event)) { + lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); + if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { + auto state = lldb::SBProcess::GetStateFromEvent(event); + switch (state) { + case lldb::eStateInvalid: + // Not a state event + break; + case lldb::eStateUnloaded: + break; + case lldb::eStateConnected: + break; + case lldb::eStateAttaching: + break; + case lldb::eStateLaunching: + break; + case lldb::eStateStepping: + break; + case lldb::eStateCrashed: + break; + case lldb::eStateDetached: + break; + case lldb::eStateSuspended: + break; + case lldb::eStateStopped: + // Only report a stopped event if the process was not restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(process); + SendThreadStoppedEvent(); + } + break; + case lldb::eStateRunning: + break; + case lldb::eStateExited: { + // Run any exit LLDB commands the user specified in the + // launch.json + g_vsc.RunExitCommands(); + SendProcessExitedEvent(process); + SendTerminatedEvent(); + done = true; + } break; + } + } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || + (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { + SendStdOutStdErr(process); + } + } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { + auto event_type = + lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); + const auto num_locs = + lldb::SBBreakpoint::GetNumBreakpointLocationsFromEvent(event); + auto bp = lldb::SBBreakpoint::GetBreakpointFromEvent(event); + bool added = event_type & lldb::eBreakpointEventTypeLocationsAdded; + bool removed = + event_type & lldb::eBreakpointEventTypeLocationsRemoved; + if (added || removed) { + for (size_t i = 0; i < num_locs; ++i) { + auto bp_loc = + lldb::SBBreakpoint::GetBreakpointLocationAtIndexFromEvent( + event, i); + auto bp_event = CreateEventObject("breakpoint"); + llvm::json::Object body; + body.try_emplace("breakpoint", CreateBreakpoint(bp_loc)); + if (added) + body.try_emplace("reason", "new"); + else + body.try_emplace("reason", "removed"); + bp_event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(bp_event))); + } + } + } + } else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) { + if (event_mask & eBroadcastBitStopEventThread) { + done = true; + } + } + } + } +} + +// Both attach and launch take a either a sourcePath or sourceMap +// argument (or neither), from which we need to set the target.source-map. +void SetSourceMapFromArguments(const llvm::json::Object &arguments) { + const char *sourceMapHelp = + "source must be be an array of two-element arrays, " + "each containing a source and replacement path string.\n"; + + std::string sourceMapCommand; + llvm::raw_string_ostream strm(sourceMapCommand); + strm << "settings set target.source-map "; + auto sourcePath = GetString(arguments, "sourcePath"); + + // sourceMap is the new, more general form of sourcePath and overrides it. + auto sourceMap = arguments.getArray("sourceMap"); + if (sourceMap) { + for (const auto &value : *sourceMap) { + auto mapping = value.getAsArray(); + if (mapping == nullptr || mapping->size() != 2 || + (*mapping)[0].kind() != llvm::json::Value::String || + (*mapping)[1].kind() != llvm::json::Value::String) { + g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); + return; + } + auto mapFrom = GetAsString((*mapping)[0]); + auto mapTo = GetAsString((*mapping)[1]); + strm << "\"" << mapFrom << "\" \"" << mapTo << "\" "; + } + } else { + if (ObjectContainsKey(arguments, "sourceMap")) { + g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); + return; + } + if (sourcePath.empty()) + return; + // Do any source remapping needed before we create our targets + strm << "\".\" \"" << sourcePath << "\""; + } + strm.flush(); + if (!sourceMapCommand.empty()) { + g_vsc.RunLLDBCommands("Setting source map:", {sourceMapCommand}); + } +} + +// "AttachRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Attach request; value of command field is 'attach'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "attach" ] +// }, +// "arguments": { +// "$ref": "#/definitions/AttachRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "AttachRequestArguments": { +// "type": "object", +// "description": "Arguments for 'attach' request.\nThe attach request has no +// standardized attributes." +// }, +// "AttachResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'attach' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void request_attach(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + const lldb::pid_t pid = + GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID); + if (pid != LLDB_INVALID_PROCESS_ID) + g_vsc.attach_info.SetProcessID(pid); + const auto wait_for = GetBoolean(arguments, "waitFor", false); + g_vsc.attach_info.SetWaitForLaunch(wait_for, false /*async*/); + g_vsc.init_commands = GetStrings(arguments, "initCommands"); + g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands"); + g_vsc.stop_commands = GetStrings(arguments, "stopCommands"); + g_vsc.exit_commands = GetStrings(arguments, "exitCommands"); + auto attachCommands = GetStrings(arguments, "attachCommands"); + g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); + const auto debuggerRoot = GetString(arguments, "debuggerRoot"); + + // This is a hack for loading DWARF in .o files on Mac where the .o files + // in the debug map of the main executable have relative paths which require + // the lldb-vscode binary to have its working directory set to that relative + // root for the .o files in order to be able to load debug info. + if (!debuggerRoot.empty()) { + llvm::sys::fs::set_current_path(debuggerRoot.data()); + } + + // Run any initialize LLDB commands the user specified in the launch.json + g_vsc.RunInitCommands(); + + // Grab the name of the program we need to debug and set it as the first + // argument that will be passed to the program we will debug. + const auto program = GetString(arguments, "program"); + if (!program.empty()) { + lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/); + + g_vsc.launch_info.SetExecutableFile(program_fspec, + false /*add_as_first_arg*/); + const char *target_triple = nullptr; + const char *uuid_cstr = nullptr; + // Stand alone debug info file if different from executable + const char *symfile = nullptr; + g_vsc.target.AddModule(program.data(), target_triple, uuid_cstr, symfile); + if (error.Fail()) { + response["success"] = llvm::json::Value(false); + EmplaceSafeString(response, "message", std::string(error.GetCString())); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; + } + } + + const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); + g_vsc.launch_info.SetDetachOnError(detatchOnError); + + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); + + if (pid == LLDB_INVALID_PROCESS_ID && wait_for) { + char attach_info[256]; + auto attach_info_len = + snprintf(attach_info, sizeof(attach_info), + "Waiting to attach to \"%s\"...", program.data()); + g_vsc.SendOutput(OutputType::Console, llvm::StringRef(attach_info, + attach_info_len)); + } + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + // Disable async events so the attach will be successful when we return from + // the launch call and the launch will happen synchronously + g_vsc.debugger.SetAsync(false); + g_vsc.target.Attach(g_vsc.attach_info, error); + // Reenable async events + g_vsc.debugger.SetAsync(true); + } else { + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If there + // is no valid process after running these commands, we have failed. + g_vsc.RunLLDBCommands("Running attachCommands:", attachCommands); + // The custom commands might have created a new target so we should use the + // selected target after these commands are run. + g_vsc.target = g_vsc.debugger.GetSelectedTarget(); + } + + SetSourceMapFromArguments(*arguments); + + if (error.Success()) { + auto attached_pid = g_vsc.target.GetProcess().GetProcessID(); + if (attached_pid == LLDB_INVALID_PROCESS_ID) { + if (attachCommands.empty()) + error.SetErrorString("failed to attach to a process"); + else + error.SetErrorString("attachCommands failed to attach to a process"); + } + } + + if (error.Fail()) { + response["success"] = llvm::json::Value(false); + EmplaceSafeString(response, "message", std::string(error.GetCString())); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + if (error.Success()) { + SendProcessEvent(Attach); + g_vsc.SendJSON(CreateEventObject("initialized")); + // SendThreadStoppedEvent(); + } +} + +// "ContinueRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Continue request; value of command field is 'continue'. +// The request starts the debuggee to run again.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "continue" ] +// }, +// "arguments": { +// "$ref": "#/definitions/ContinueArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "ContinueArguments": { +// "type": "object", +// "description": "Arguments for 'continue' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Continue execution for the specified thread (if +// possible). If the backend cannot continue on a single +// thread but will continue on all threads, it should +// set the allThreadsContinued attribute in the response +// to true." +// } +// }, +// "required": [ "threadId" ] +// }, +// "ContinueResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'continue' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "allThreadsContinued": { +// "type": "boolean", +// "description": "If true, the continue request has ignored the +// specified thread and continued all threads +// instead. If this attribute is missing a value +// of 'true' is assumed for backward +// compatibility." +// } +// } +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_continue(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + lldb::SBProcess process = g_vsc.target.GetProcess(); + auto arguments = request.getObject("arguments"); + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = GetUnsigned(arguments, "threadId", LLDB_INVALID_THREAD_ID); + lldb::SBError error = process.Continue(); + llvm::json::Object body; + body.try_emplace("allThreadsContinued", true); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "ConfigurationDoneRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "ConfigurationDone request; value of command field +// is 'configurationDone'.\nThe client of the debug protocol must +// send this request at the end of the sequence of configuration +// requests (which was started by the InitializedEvent).", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "configurationDone" ] +// }, +// "arguments": { +// "$ref": "#/definitions/ConfigurationDoneArguments" +// } +// }, +// "required": [ "command" ] +// }] +// }, +// "ConfigurationDoneArguments": { +// "type": "object", +// "description": "Arguments for 'configurationDone' request.\nThe +// configurationDone request has no standardized attributes." +// }, +// "ConfigurationDoneResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'configurationDone' request. This is +// just an acknowledgement, so no body field is required." +// }] +// }, +void request_configurationDone(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + if (g_vsc.stop_at_entry) + SendThreadStoppedEvent(); + else + g_vsc.target.GetProcess().Continue(); +} + +// "DisconnectRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Disconnect request; value of command field is +// 'disconnect'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "disconnect" ] +// }, +// "arguments": { +// "$ref": "#/definitions/DisconnectArguments" +// } +// }, +// "required": [ "command" ] +// }] +// }, +// "DisconnectArguments": { +// "type": "object", +// "description": "Arguments for 'disconnect' request.", +// "properties": { +// "terminateDebuggee": { +// "type": "boolean", +// "description": "Indicates whether the debuggee should be terminated +// when the debugger is disconnected. If unspecified, +// the debug adapter is free to do whatever it thinks +// is best. A client can only rely on this attribute +// being properly honored if a debug adapter returns +// true for the 'supportTerminateDebuggee' capability." +// }, +// "restart": { +// "type": "boolean", +// "description": "Indicates whether the debuggee should be restart +// the process." +// } +// } +// }, +// "DisconnectResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'disconnect' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void request_disconnect(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + + bool terminateDebuggee = GetBoolean(arguments, "terminateDebuggee", false); + lldb::SBProcess process = g_vsc.target.GetProcess(); + auto state = process.GetState(); + + switch (state) { + case lldb::eStateInvalid: + case lldb::eStateUnloaded: + case lldb::eStateDetached: + case lldb::eStateExited: + break; + case lldb::eStateConnected: + case lldb::eStateAttaching: + case lldb::eStateLaunching: + case lldb::eStateStepping: + case lldb::eStateCrashed: + case lldb::eStateSuspended: + case lldb::eStateStopped: + case lldb::eStateRunning: + g_vsc.debugger.SetAsync(false); + if (terminateDebuggee) + process.Kill(); + else + process.Detach(); + g_vsc.debugger.SetAsync(true); + break; + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + SendTerminatedEvent(); + if (g_vsc.event_thread.joinable()) { + g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread); + g_vsc.event_thread.join(); + } +} + +void request_exceptionInfo(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + llvm::json::Object body; + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + auto stopReason = thread.GetStopReason(); + if (stopReason == lldb::eStopReasonSignal) + body.try_emplace("exceptionId", "signal"); + else if (stopReason == lldb::eStopReasonBreakpoint) { + ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread); + if (exc_bp) { + EmplaceSafeString(body, "exceptionId", exc_bp->filter); + EmplaceSafeString(body, "description", exc_bp->label); + } else { + body.try_emplace("exceptionId", "exception"); + } + } else { + body.try_emplace("exceptionId", "exception"); + } + if (!ObjectContainsKey(body, "description")) { + char description[1024]; + if (thread.GetStopDescription(description, sizeof(description))) { + EmplaceSafeString(body, "description", std::string(description)); + } + } + body.try_emplace("breakMode", "always"); + // auto excInfoCount = thread.GetStopReasonDataCount(); + // for (auto i=0; i<excInfoCount; ++i) { + // uint64_t exc_data = thread.GetStopReasonDataAtIndex(i); + // } + } else { + response["success"] = llvm::json::Value(false); + } + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "CompletionsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Returns a list of possible completions for a given caret position and text.\nThe CompletionsRequest may only be called if the 'supportsCompletionsRequest' capability exists and is true.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "completions" ] +// }, +// "arguments": { +// "$ref": "#/definitions/CompletionsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "CompletionsArguments": { +// "type": "object", +// "description": "Arguments for 'completions' request.", +// "properties": { +// "frameId": { +// "type": "integer", +// "description": "Returns completions in the scope of this stack frame. If not specified, the completions are returned for the global scope." +// }, +// "text": { +// "type": "string", +// "description": "One or more source lines. Typically this is the text a user has typed into the debug console before he asked for completion." +// }, +// "column": { +// "type": "integer", +// "description": "The character position for which to determine the completion proposals." +// }, +// "line": { +// "type": "integer", +// "description": "An optional line for which to determine the completion proposals. If missing the first line of the text is assumed." +// } +// }, +// "required": [ "text", "column" ] +// }, +// "CompletionsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'completions' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "targets": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/CompletionItem" +// }, +// "description": "The possible completions for ." +// } +// }, +// "required": [ "targets" ] +// } +// }, +// "required": [ "body" ] +// }] +// }, +// "CompletionItem": { +// "type": "object", +// "description": "CompletionItems are the suggestions returned from the CompletionsRequest.", +// "properties": { +// "label": { +// "type": "string", +// "description": "The label of this completion item. By default this is also the text that is inserted when selecting this completion." +// }, +// "text": { +// "type": "string", +// "description": "If text is not falsy then it is inserted instead of the label." +// }, +// "sortText": { +// "type": "string", +// "description": "A string that should be used when comparing this item with other items. When `falsy` the label is used." +// }, +// "type": { +// "$ref": "#/definitions/CompletionItemType", +// "description": "The item's type. Typically the client uses this information to render the item in the UI with an icon." +// }, +// "start": { +// "type": "integer", +// "description": "This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added.\nIf missing the text is added at the location specified by the CompletionsRequest's 'column' attribute." +// }, +// "length": { +// "type": "integer", +// "description": "This value determines how many characters are overwritten by the completion text.\nIf missing the value 0 is assumed which results in the completion text being inserted." +// } +// }, +// "required": [ "label" ] +// }, +// "CompletionItemType": { +// "type": "string", +// "description": "Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them.", +// "enum": [ "method", "function", "constructor", "field", "variable", "class", "interface", "module", "property", "unit", "value", "enum", "keyword", "snippet", "text", "color", "file", "reference", "customcolor" ] +// } +void request_completions(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + auto arguments = request.getObject("arguments"); + std::string text = GetString(arguments, "text"); + auto original_column = GetSigned(arguments, "column", text.size()); + auto actual_column = original_column - 1; + llvm::json::Array targets; + // NOTE: the 'line' argument is not needed, as multiline expressions + // work well already + // TODO: support frameID. Currently + // g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions + // is frame-unaware. + + if (!text.empty() && text[0] == '`') { + text = text.substr(1); + actual_column--; + } else { + text = "p " + text; + actual_column += 2; + } + lldb::SBStringList matches; + lldb::SBStringList descriptions; + g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions( + text.c_str(), + actual_column, + 0, -1, matches, descriptions); + size_t count = std::min((uint32_t)50, matches.GetSize()); + targets.reserve(count); + for (size_t i = 0; i < count; i++) { + std::string match = matches.GetStringAtIndex(i); + std::string description = descriptions.GetStringAtIndex(i); + + llvm::json::Object item; + EmplaceSafeString(item, "text", match); + if (description.empty()) + EmplaceSafeString(item, "label", match); + else + EmplaceSafeString(item, "label", match + " -- " + description); + + targets.emplace_back(std::move(item)); + } + + body.try_emplace("targets", std::move(targets)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "EvaluateRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Evaluate request; value of command field is 'evaluate'. +// Evaluates the given expression in the context of the +// top most stack frame. The expression has access to any +// variables and arguments that are in scope.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "evaluate" ] +// }, +// "arguments": { +// "$ref": "#/definitions/EvaluateArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "EvaluateArguments": { +// "type": "object", +// "description": "Arguments for 'evaluate' request.", +// "properties": { +// "expression": { +// "type": "string", +// "description": "The expression to evaluate." +// }, +// "frameId": { +// "type": "integer", +// "description": "Evaluate the expression in the scope of this stack +// frame. If not specified, the expression is evaluated +// in the global scope." +// }, +// "context": { +// "type": "string", +// "_enum": [ "watch", "repl", "hover" ], +// "enumDescriptions": [ +// "evaluate is run in a watch.", +// "evaluate is run from REPL console.", +// "evaluate is run from a data hover." +// ], +// "description": "The context in which the evaluate request is run." +// }, +// "format": { +// "$ref": "#/definitions/ValueFormat", +// "description": "Specifies details on how to format the Evaluate +// result." +// } +// }, +// "required": [ "expression" ] +// }, +// "EvaluateResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'evaluate' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "result": { +// "type": "string", +// "description": "The result of the evaluate request." +// }, +// "type": { +// "type": "string", +// "description": "The optional type of the evaluate result." +// }, +// "presentationHint": { +// "$ref": "#/definitions/VariablePresentationHint", +// "description": "Properties of a evaluate result that can be +// used to determine how to render the result in +// the UI." +// }, +// "variablesReference": { +// "type": "number", +// "description": "If variablesReference is > 0, the evaluate +// result is structured and its children can be +// retrieved by passing variablesReference to the +// VariablesRequest." +// }, +// "namedVariables": { +// "type": "number", +// "description": "The number of named child variables. The +// client can use this optional information to +// present the variables in a paged UI and fetch +// them in chunks." +// }, +// "indexedVariables": { +// "type": "number", +// "description": "The number of indexed child variables. The +// client can use this optional information to +// present the variables in a paged UI and fetch +// them in chunks." +// } +// }, +// "required": [ "result", "variablesReference" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_evaluate(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + auto arguments = request.getObject("arguments"); + lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments); + const auto expression = GetString(arguments, "expression"); + + if (!expression.empty() && expression[0] == '`') { + auto result = RunLLDBCommands(llvm::StringRef(), + {expression.substr(1)}); + EmplaceSafeString(body, "result", result); + body.try_emplace("variablesReference", (int64_t)0); + } else { + // Always try to get the answer from the local variables if possible. If + // this fails, then actually evaluate an expression using the expression + // parser. "frame variable" is more reliable than the expression parser in + // many cases and it is faster. + lldb::SBValue value = frame.GetValueForVariablePath( + expression.data(), lldb::eDynamicDontRunTarget); + if (value.GetError().Fail()) + value = frame.EvaluateExpression(expression.data()); + if (value.GetError().Fail()) { + response["success"] = llvm::json::Value(false); + // This error object must live until we're done with the pointer returned + // by GetCString(). + lldb::SBError error = value.GetError(); + const char *error_cstr = error.GetCString(); + if (error_cstr && error_cstr[0]) + EmplaceSafeString(response, "message", std::string(error_cstr)); + else + EmplaceSafeString(response, "message", "evaluate failed"); + } else { + SetValueForKey(value, body, "result"); + auto value_typename = value.GetType().GetDisplayTypeName(); + EmplaceSafeString(body, "type", value_typename ? value_typename : NO_TYPENAME); + if (value.MightHaveChildren()) { + auto variablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize()); + g_vsc.variables.Append(value); + body.try_emplace("variablesReference", variablesReference); + } else { + body.try_emplace("variablesReference", (int64_t)0); + } + } + } + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "InitializeRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Initialize request; value of command field is +// 'initialize'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "initialize" ] +// }, +// "arguments": { +// "$ref": "#/definitions/InitializeRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "InitializeRequestArguments": { +// "type": "object", +// "description": "Arguments for 'initialize' request.", +// "properties": { +// "clientID": { +// "type": "string", +// "description": "The ID of the (frontend) client using this adapter." +// }, +// "adapterID": { +// "type": "string", +// "description": "The ID of the debug adapter." +// }, +// "locale": { +// "type": "string", +// "description": "The ISO-639 locale of the (frontend) client using +// this adapter, e.g. en-US or de-CH." +// }, +// "linesStartAt1": { +// "type": "boolean", +// "description": "If true all line numbers are 1-based (default)." +// }, +// "columnsStartAt1": { +// "type": "boolean", +// "description": "If true all column numbers are 1-based (default)." +// }, +// "pathFormat": { +// "type": "string", +// "_enum": [ "path", "uri" ], +// "description": "Determines in what format paths are specified. The +// default is 'path', which is the native format." +// }, +// "supportsVariableType": { +// "type": "boolean", +// "description": "Client supports the optional type attribute for +// variables." +// }, +// "supportsVariablePaging": { +// "type": "boolean", +// "description": "Client supports the paging of variables." +// }, +// "supportsRunInTerminalRequest": { +// "type": "boolean", +// "description": "Client supports the runInTerminal request." +// } +// }, +// "required": [ "adapterID" ] +// }, +// "InitializeResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'initialize' request.", +// "properties": { +// "body": { +// "$ref": "#/definitions/Capabilities", +// "description": "The capabilities of this debug adapter." +// } +// } +// }] +// } +void request_initialize(const llvm::json::Object &request) { + g_vsc.debugger = lldb::SBDebugger::Create(true /*source_init_files*/); + // Create an empty target right away since we might get breakpoint requests + // before we are given an executable to launch in a "launch" request, or a + // executable when attaching to a process by process ID in a "attach" + // request. + FILE *out = llvm::sys::RetryAfterSignal(nullptr, fopen, dev_null_path, "w"); + if (out) { + // Set the output and error file handles to redirect into nothing otherwise + // if any code in LLDB prints to the debugger file handles, the output and + // error file handles are initialized to STDOUT and STDERR and any output + // will kill our debug session. + g_vsc.debugger.SetOutputFileHandle(out, true); + g_vsc.debugger.SetErrorFileHandle(out, false); + } + + g_vsc.target = g_vsc.debugger.CreateTarget(nullptr); + lldb::SBListener listener = g_vsc.debugger.GetListener(); + listener.StartListeningForEvents( + g_vsc.target.GetBroadcaster(), + lldb::SBTarget::eBroadcastBitBreakpointChanged); + listener.StartListeningForEvents(g_vsc.broadcaster, + eBroadcastBitStopEventThread); + // Start our event thread so we can receive events from the debugger, target, + // process and more. + g_vsc.event_thread = std::thread(EventThreadFunction); + + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + // The debug adapter supports the configurationDoneRequest. + body.try_emplace("supportsConfigurationDoneRequest", true); + // The debug adapter supports function breakpoints. + body.try_emplace("supportsFunctionBreakpoints", true); + // The debug adapter supports conditional breakpoints. + body.try_emplace("supportsConditionalBreakpoints", true); + // The debug adapter supports breakpoints that break execution after a + // specified number of hits. + body.try_emplace("supportsHitConditionalBreakpoints", true); + // The debug adapter supports a (side effect free) evaluate request for + // data hovers. + body.try_emplace("supportsEvaluateForHovers", true); + // Available filters or options for the setExceptionBreakpoints request. + llvm::json::Array filters; + for (const auto &exc_bp : g_vsc.exception_breakpoints) { + filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp)); + } + body.try_emplace("exceptionBreakpointFilters", std::move(filters)); + // The debug adapter supports stepping back via the stepBack and + // reverseContinue requests. + body.try_emplace("supportsStepBack", false); + // The debug adapter supports setting a variable to a value. + body.try_emplace("supportsSetVariable", true); + // The debug adapter supports restarting a frame. + body.try_emplace("supportsRestartFrame", false); + // The debug adapter supports the gotoTargetsRequest. + body.try_emplace("supportsGotoTargetsRequest", false); + // The debug adapter supports the stepInTargetsRequest. + body.try_emplace("supportsStepInTargetsRequest", false); + // The debug adapter supports the completionsRequest. + body.try_emplace("supportsCompletionsRequest", true); + // The debug adapter supports the modules request. + body.try_emplace("supportsModulesRequest", false); + // The set of additional module information exposed by the debug adapter. + // body.try_emplace("additionalModuleColumns"] = ColumnDescriptor + // Checksum algorithms supported by the debug adapter. + // body.try_emplace("supportedChecksumAlgorithms"] = ChecksumAlgorithm + // The debug adapter supports the RestartRequest. In this case a client + // should not implement 'restart' by terminating and relaunching the adapter + // but by calling the RestartRequest. + body.try_emplace("supportsRestartRequest", false); + // The debug adapter supports 'exceptionOptions' on the + // setExceptionBreakpoints request. + body.try_emplace("supportsExceptionOptions", true); + // The debug adapter supports a 'format' attribute on the stackTraceRequest, + // variablesRequest, and evaluateRequest. + body.try_emplace("supportsValueFormattingOptions", true); + // The debug adapter supports the exceptionInfo request. + body.try_emplace("supportsExceptionInfoRequest", true); + // The debug adapter supports the 'terminateDebuggee' attribute on the + // 'disconnect' request. + body.try_emplace("supportTerminateDebuggee", true); + // The debug adapter supports the delayed loading of parts of the stack, + // which requires that both the 'startFrame' and 'levels' arguments and the + // 'totalFrames' result of the 'StackTrace' request are supported. + body.try_emplace("supportsDelayedStackTraceLoading", true); + // The debug adapter supports the 'loadedSources' request. + body.try_emplace("supportsLoadedSourcesRequest", false); + + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "LaunchRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Launch request; value of command field is 'launch'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "launch" ] +// }, +// "arguments": { +// "$ref": "#/definitions/LaunchRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "LaunchRequestArguments": { +// "type": "object", +// "description": "Arguments for 'launch' request.", +// "properties": { +// "noDebug": { +// "type": "boolean", +// "description": "If noDebug is true the launch request should launch +// the program without enabling debugging." +// } +// } +// }, +// "LaunchResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'launch' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void request_launch(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + g_vsc.init_commands = GetStrings(arguments, "initCommands"); + g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands"); + g_vsc.stop_commands = GetStrings(arguments, "stopCommands"); + g_vsc.exit_commands = GetStrings(arguments, "exitCommands"); + auto launchCommands = GetStrings(arguments, "launchCommands"); + g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); + const auto debuggerRoot = GetString(arguments, "debuggerRoot"); + + // This is a hack for loading DWARF in .o files on Mac where the .o files + // in the debug map of the main executable have relative paths which require + // the lldb-vscode binary to have its working directory set to that relative + // root for the .o files in order to be able to load debug info. + if (!debuggerRoot.empty()) { + llvm::sys::fs::set_current_path(debuggerRoot.data()); + } + + SetSourceMapFromArguments(*arguments); + + // Run any initialize LLDB commands the user specified in the launch.json + g_vsc.RunInitCommands(); + + // Grab the current working directory if there is one and set it in the + // launch info. + const auto cwd = GetString(arguments, "cwd"); + if (!cwd.empty()) + g_vsc.launch_info.SetWorkingDirectory(cwd.data()); + + // Grab the name of the program we need to debug and set it as the first + // argument that will be passed to the program we will debug. + llvm::StringRef program = GetString(arguments, "program"); + if (!program.empty()) { + lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/); + g_vsc.launch_info.SetExecutableFile(program_fspec, + true /*add_as_first_arg*/); + const char *target_triple = nullptr; + const char *uuid_cstr = nullptr; + // Stand alone debug info file if different from executable + const char *symfile = nullptr; + lldb::SBModule module = g_vsc.target.AddModule( + program.data(), target_triple, uuid_cstr, symfile); + if (!module.IsValid()) { + response["success"] = llvm::json::Value(false); + + EmplaceSafeString( + response, "message", + llvm::formatv("Could not load program '{0}'.", program).str()); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; + } + } + + // Extract any extra arguments and append them to our program arguments for + // when we launch + auto args = GetStrings(arguments, "args"); + if (!args.empty()) + g_vsc.launch_info.SetArguments(MakeArgv(args).data(), true); + + // Pass any environment variables along that the user specified. + auto envs = GetStrings(arguments, "env"); + if (!envs.empty()) + g_vsc.launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true); + + auto flags = g_vsc.launch_info.GetLaunchFlags(); + + if (GetBoolean(arguments, "disableASLR", true)) + flags |= lldb::eLaunchFlagDisableASLR; + if (GetBoolean(arguments, "disableSTDIO", false)) + flags |= lldb::eLaunchFlagDisableSTDIO; + if (GetBoolean(arguments, "shellExpandArguments", false)) + flags |= lldb::eLaunchFlagShellExpandArguments; + const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); + g_vsc.launch_info.SetDetachOnError(detatchOnError); + g_vsc.launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | + lldb::eLaunchFlagStopAtEntry); + + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); + if (launchCommands.empty()) { + // Disable async events so the launch will be successful when we return from + // the launch call and the launch will happen synchronously + g_vsc.debugger.SetAsync(false); + g_vsc.target.Launch(g_vsc.launch_info, error); + g_vsc.debugger.SetAsync(true); + } else { + g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands); + // The custom commands might have created a new target so we should use the + // selected target after these commands are run. + g_vsc.target = g_vsc.debugger.GetSelectedTarget(); + } + + if (error.Fail()) { + response["success"] = llvm::json::Value(false); + EmplaceSafeString(response, "message", std::string(error.GetCString())); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + + SendProcessEvent(Launch); + g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized"))); + // Reenable async events and start the event thread to catch async events. + // g_vsc.debugger.SetAsync(true); +} + +// "NextRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Next request; value of command field is 'next'. The +// request starts the debuggee to run again for one step. +// The debug adapter first sends the NextResponse and then +// a StoppedEvent (event type 'step') after the step has +// completed.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "next" ] +// }, +// "arguments": { +// "$ref": "#/definitions/NextArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "NextArguments": { +// "type": "object", +// "description": "Arguments for 'next' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'next' for this thread." +// } +// }, +// "required": [ "threadId" ] +// }, +// "NextResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'next' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void request_next(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = thread.GetThreadID(); + thread.StepOver(); + } else { + response["success"] = llvm::json::Value(false); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "PauseRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Pause request; value of command field is 'pause'. The +// request suspenses the debuggee. The debug adapter first sends the +// PauseResponse and then a StoppedEvent (event type 'pause') after the +// thread has been paused successfully.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "pause" ] +// }, +// "arguments": { +// "$ref": "#/definitions/PauseArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "PauseArguments": { +// "type": "object", +// "description": "Arguments for 'pause' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Pause execution for this thread." +// } +// }, +// "required": [ "threadId" ] +// }, +// "PauseResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'pause' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void request_pause(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + lldb::SBProcess process = g_vsc.target.GetProcess(); + lldb::SBError error = process.Stop(); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "ScopesRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Scopes request; value of command field is 'scopes'. The +// request returns the variable scopes for a given stackframe ID.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "scopes" ] +// }, +// "arguments": { +// "$ref": "#/definitions/ScopesArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "ScopesArguments": { +// "type": "object", +// "description": "Arguments for 'scopes' request.", +// "properties": { +// "frameId": { +// "type": "integer", +// "description": "Retrieve the scopes for this stackframe." +// } +// }, +// "required": [ "frameId" ] +// }, +// "ScopesResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'scopes' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "scopes": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Scope" +// }, +// "description": "The scopes of the stackframe. If the array has +// length zero, there are no scopes available." +// } +// }, +// "required": [ "scopes" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_scopes(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + auto arguments = request.getObject("arguments"); + lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments); + g_vsc.variables.Clear(); + g_vsc.variables.Append(frame.GetVariables(true, // arguments + true, // locals + false, // statics + true)); // in_scope_only + g_vsc.num_locals = g_vsc.variables.GetSize(); + g_vsc.variables.Append(frame.GetVariables(false, // arguments + false, // locals + true, // statics + true)); // in_scope_only + g_vsc.num_globals = g_vsc.variables.GetSize() - (g_vsc.num_locals); + g_vsc.variables.Append(frame.GetRegisters()); + g_vsc.num_regs = + g_vsc.variables.GetSize() - (g_vsc.num_locals + g_vsc.num_globals); + body.try_emplace("scopes", g_vsc.CreateTopLevelScopes()); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "SetBreakpointsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "SetBreakpoints request; value of command field is +// 'setBreakpoints'. Sets multiple breakpoints for a single source and +// clears all previous breakpoints in that source. To clear all breakpoint +// for a source, specify an empty array. When a breakpoint is hit, a +// StoppedEvent (event type 'breakpoint') is generated.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setBreakpoints" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetBreakpointsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetBreakpointsArguments": { +// "type": "object", +// "description": "Arguments for 'setBreakpoints' request.", +// "properties": { +// "source": { +// "$ref": "#/definitions/Source", +// "description": "The source location of the breakpoints; either +// source.path or source.reference must be specified." +// }, +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/SourceBreakpoint" +// }, +// "description": "The code locations of the breakpoints." +// }, +// "lines": { +// "type": "array", +// "items": { +// "type": "integer" +// }, +// "description": "Deprecated: The code locations of the breakpoints." +// }, +// "sourceModified": { +// "type": "boolean", +// "description": "A value of true indicates that the underlying source +// has been modified which results in new breakpoint locations." +// } +// }, +// "required": [ "source" ] +// }, +// "SetBreakpointsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setBreakpoints' request. Returned is +// information about each breakpoint created by this request. This includes +// the actual code location and whether the breakpoint could be verified. +// The breakpoints returned are in the same order as the elements of the +// 'breakpoints' (or the deprecated 'lines') in the +// SetBreakpointsArguments.", "properties": { +// "body": { +// "type": "object", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Breakpoint" +// }, +// "description": "Information about the breakpoints. The array +// elements are in the same order as the elements of the +// 'breakpoints' (or the deprecated 'lines') in the +// SetBreakpointsArguments." +// } +// }, +// "required": [ "breakpoints" ] +// } +// }, +// "required": [ "body" ] +// }] +// }, +// "SourceBreakpoint": { +// "type": "object", +// "description": "Properties of a breakpoint or logpoint passed to the +// setBreakpoints request.", "properties": { +// "line": { +// "type": "integer", +// "description": "The source line of the breakpoint or logpoint." +// }, +// "column": { +// "type": "integer", +// "description": "An optional source column of the breakpoint." +// }, +// "condition": { +// "type": "string", +// "description": "An optional expression for conditional breakpoints." +// }, +// "hitCondition": { +// "type": "string", +// "description": "An optional expression that controls how many hits of +// the breakpoint are ignored. The backend is expected to interpret the +// expression as needed." +// }, +// "logMessage": { +// "type": "string", +// "description": "If this attribute exists and is non-empty, the backend +// must not 'break' (stop) but log the message instead. Expressions within +// {} are interpolated." +// } +// }, +// "required": [ "line" ] +// } +void request_setBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + auto source = arguments->getObject("source"); + const auto path = GetString(source, "path"); + auto breakpoints = arguments->getArray("breakpoints"); + llvm::json::Array response_breakpoints; + // Decode the source breakpoint infos for this "setBreakpoints" request + SourceBreakpointMap request_bps; + for (const auto &bp : *breakpoints) { + auto bp_obj = bp.getAsObject(); + if (bp_obj) { + SourceBreakpoint src_bp(*bp_obj); + request_bps[src_bp.line] = std::move(src_bp); + } + } + + // See if we already have breakpoints set for this source file from a + // previous "setBreakpoints" request + auto old_src_bp_pos = g_vsc.source_breakpoints.find(path); + if (old_src_bp_pos != g_vsc.source_breakpoints.end()) { + + // We have already set breakpoints in this source file and they are giving + // use a new list of lines to set breakpoints on. Some breakpoints might + // already be set, and some might not. We need to remove any breakpoints + // whose lines are not contained in the any breakpoints lines in in the + // "breakpoints" array. + + // Delete any breakpoints in this source file that aren't in the + // request_bps set. There is no call to remove breakpoints other than + // calling this function with a smaller or empty "breakpoints" list. + std::vector<uint32_t> remove_lines; + for (auto &pair: old_src_bp_pos->second) { + auto request_pos = request_bps.find(pair.first); + if (request_pos == request_bps.end()) { + // This breakpoint no longer exists in this source file, delete it + g_vsc.target.BreakpointDelete(pair.second.bp.GetID()); + remove_lines.push_back(pair.first); + } else { + pair.second.UpdateBreakpoint(request_pos->second); + // Remove this breakpoint from the request breakpoints since we have + // handled it here and we don't need to set a new breakpoint below. + request_bps.erase(request_pos); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + } + } + // Remove any lines from this existing source breakpoint map + for (auto line: remove_lines) + old_src_bp_pos->second.erase(line); + + // Now add any breakpoint infos left over in request_bps are the + // breakpoints that weren't set in this source file yet. We need to update + // thread source breakpoint info for the source file in the variable + // "old_src_bp_pos->second" so the info for this source file is up to date. + for (auto &pair : request_bps) { + pair.second.SetBreakpoint(path.data()); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + old_src_bp_pos->second[pair.first] = std::move(pair.second); + } + } else { + // No breakpoints were set for this source file yet. Set all breakpoints + // for each line and add them to the response and create an entry in + // g_vsc.source_breakpoints for this source file. + for (auto &pair : request_bps) { + pair.second.SetBreakpoint(path.data()); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + } + g_vsc.source_breakpoints[path] = std::move(request_bps); + } + + llvm::json::Object body; + body.try_emplace("breakpoints", std::move(response_breakpoints)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "SetExceptionBreakpointsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "SetExceptionBreakpoints request; value of command field +// is 'setExceptionBreakpoints'. The request configures the debuggers +// response to thrown exceptions. If an exception is configured to break, a +// StoppedEvent is fired (event type 'exception').", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setExceptionBreakpoints" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetExceptionBreakpointsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetExceptionBreakpointsArguments": { +// "type": "object", +// "description": "Arguments for 'setExceptionBreakpoints' request.", +// "properties": { +// "filters": { +// "type": "array", +// "items": { +// "type": "string" +// }, +// "description": "IDs of checked exception options. The set of IDs is +// returned via the 'exceptionBreakpointFilters' capability." +// }, +// "exceptionOptions": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/ExceptionOptions" +// }, +// "description": "Configuration options for selected exceptions." +// } +// }, +// "required": [ "filters" ] +// }, +// "SetExceptionBreakpointsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setExceptionBreakpoints' request. This is +// just an acknowledgement, so no body field is required." +// }] +// } +void request_setExceptionBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + auto filters = arguments->getArray("filters"); + // Keep a list of any exception breakpoint filter names that weren't set + // so we can clear any exception breakpoints if needed. + std::set<std::string> unset_filters; + for (const auto &bp : g_vsc.exception_breakpoints) + unset_filters.insert(bp.filter); + + for (const auto &value : *filters) { + const auto filter = GetAsString(value); + auto exc_bp = g_vsc.GetExceptionBreakpoint(filter); + if (exc_bp) { + exc_bp->SetBreakpoint(); + unset_filters.erase(filter); + } + } + for (const auto &filter : unset_filters) { + auto exc_bp = g_vsc.GetExceptionBreakpoint(filter); + if (exc_bp) + exc_bp->ClearBreakpoint(); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "SetFunctionBreakpointsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "SetFunctionBreakpoints request; value of command field is +// 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears +// all previous function breakpoints. To clear all function breakpoint, +// specify an empty array. When a function breakpoint is hit, a StoppedEvent +// (event type 'function breakpoint') is generated.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setFunctionBreakpoints" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetFunctionBreakpointsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetFunctionBreakpointsArguments": { +// "type": "object", +// "description": "Arguments for 'setFunctionBreakpoints' request.", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/FunctionBreakpoint" +// }, +// "description": "The function names of the breakpoints." +// } +// }, +// "required": [ "breakpoints" ] +// }, +// "FunctionBreakpoint": { +// "type": "object", +// "description": "Properties of a breakpoint passed to the +// setFunctionBreakpoints request.", "properties": { +// "name": { +// "type": "string", +// "description": "The name of the function." +// }, +// "condition": { +// "type": "string", +// "description": "An optional expression for conditional breakpoints." +// }, +// "hitCondition": { +// "type": "string", +// "description": "An optional expression that controls how many hits of +// the breakpoint are ignored. The backend is expected to interpret the +// expression as needed." +// } +// }, +// "required": [ "name" ] +// }, +// "SetFunctionBreakpointsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setFunctionBreakpoints' request. Returned is +// information about each breakpoint created by this request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Breakpoint" +// }, +// "description": "Information about the breakpoints. The array +// elements correspond to the elements of the 'breakpoints' array." +// } +// }, +// "required": [ "breakpoints" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_setFunctionBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + auto breakpoints = arguments->getArray("breakpoints"); + FunctionBreakpointMap request_bps; + llvm::json::Array response_breakpoints; + for (const auto &value : *breakpoints) { + auto bp_obj = value.getAsObject(); + if (bp_obj == nullptr) + continue; + FunctionBreakpoint func_bp(*bp_obj); + request_bps[func_bp.functionName] = std::move(func_bp); + } + + std::vector<llvm::StringRef> remove_names; + // Disable any function breakpoints that aren't in the request_bps. + // There is no call to remove function breakpoints other than calling this + // function with a smaller or empty "breakpoints" list. + for (auto &pair: g_vsc.function_breakpoints) { + auto request_pos = request_bps.find(pair.first()); + if (request_pos == request_bps.end()) { + // This function breakpoint no longer exists delete it from LLDB + g_vsc.target.BreakpointDelete(pair.second.bp.GetID()); + remove_names.push_back(pair.first()); + } else { + // Update the existing breakpoint as any setting withing the function + // breakpoint might have changed. + pair.second.UpdateBreakpoint(request_pos->second); + // Remove this breakpoint from the request breakpoints since we have + // handled it here and we don't need to set a new breakpoint below. + request_bps.erase(request_pos); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + } + } + // Remove any breakpoints that are no longer in our list + for (const auto &name: remove_names) + g_vsc.function_breakpoints.erase(name); + + // Any breakpoints that are left in "request_bps" are breakpoints that + // need to be set. + for (auto &pair : request_bps) { + pair.second.SetBreakpoint(); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + g_vsc.function_breakpoints[pair.first()] = std::move(pair.second); + } + + llvm::json::Object body; + body.try_emplace("breakpoints", std::move(response_breakpoints)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "SourceRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Source request; value of command field is 'source'. The +// request retrieves the source code for a given source reference.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "source" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SourceArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SourceArguments": { +// "type": "object", +// "description": "Arguments for 'source' request.", +// "properties": { +// "source": { +// "$ref": "#/definitions/Source", +// "description": "Specifies the source content to load. Either +// source.path or source.sourceReference must be specified." +// }, +// "sourceReference": { +// "type": "integer", +// "description": "The reference to the source. This is the same as +// source.sourceReference. This is provided for backward compatibility +// since old backends do not understand the 'source' attribute." +// } +// }, +// "required": [ "sourceReference" ] +// }, +// "SourceResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'source' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "content": { +// "type": "string", +// "description": "Content of the source reference." +// }, +// "mimeType": { +// "type": "string", +// "description": "Optional content type (mime type) of the source." +// } +// }, +// "required": [ "content" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_source(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + + auto arguments = request.getObject("arguments"); + auto source = arguments->getObject("source"); + auto sourceReference = GetSigned(source, "sourceReference", -1); + auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference); + if (pos != g_vsc.source_map.end()) { + EmplaceSafeString(body, "content", pos->second.content); + } else { + response["success"] = llvm::json::Value(false); + } + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "StackTraceRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StackTrace request; value of command field is +// 'stackTrace'. The request returns a stacktrace from the current execution +// state.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stackTrace" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StackTraceArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StackTraceArguments": { +// "type": "object", +// "description": "Arguments for 'stackTrace' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Retrieve the stacktrace for this thread." +// }, +// "startFrame": { +// "type": "integer", +// "description": "The index of the first frame to return; if omitted +// frames start at 0." +// }, +// "levels": { +// "type": "integer", +// "description": "The maximum number of frames to return. If levels is +// not specified or 0, all frames are returned." +// }, +// "format": { +// "$ref": "#/definitions/StackFrameFormat", +// "description": "Specifies details on how to format the stack frames." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StackTraceResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stackTrace' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "stackFrames": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/StackFrame" +// }, +// "description": "The frames of the stackframe. If the array has +// length zero, there are no stackframes available. This means that +// there is no location information available." +// }, +// "totalFrames": { +// "type": "integer", +// "description": "The total number of frames available." +// } +// }, +// "required": [ "stackFrames" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_stackTrace(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + lldb::SBError error; + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + llvm::json::Array stackFrames; + llvm::json::Object body; + + if (thread.IsValid()) { + const auto startFrame = GetUnsigned(arguments, "startFrame", 0); + const auto levels = GetUnsigned(arguments, "levels", 0); + const auto endFrame = (levels == 0) ? INT64_MAX : (startFrame + levels); + for (uint32_t i = startFrame; i < endFrame; ++i) { + auto frame = thread.GetFrameAtIndex(i); + if (!frame.IsValid()) + break; + stackFrames.emplace_back(CreateStackFrame(frame)); + } + const auto totalFrames = thread.GetNumFrames(); + body.try_emplace("totalFrames", totalFrames); + } + body.try_emplace("stackFrames", std::move(stackFrames)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "StepInRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StepIn request; value of command field is 'stepIn'. The +// request starts the debuggee to step into a function/method if possible. +// If it cannot step into a target, 'stepIn' behaves like 'next'. The debug +// adapter first sends the StepInResponse and then a StoppedEvent (event +// type 'step') after the step has completed. If there are multiple +// function/method calls (or other targets) on the source line, the optional +// argument 'targetId' can be used to control into which target the 'stepIn' +// should occur. The list of possible targets for a given source line can be +// retrieved via the 'stepInTargets' request.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stepIn" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StepInArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StepInArguments": { +// "type": "object", +// "description": "Arguments for 'stepIn' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'stepIn' for this thread." +// }, +// "targetId": { +// "type": "integer", +// "description": "Optional id of the target to step into." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StepInResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stepIn' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void request_stepIn(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = thread.GetThreadID(); + thread.StepInto(); + } else { + response["success"] = llvm::json::Value(false); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "StepOutRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StepOut request; value of command field is 'stepOut'. The +// request starts the debuggee to run again for one step. The debug adapter +// first sends the StepOutResponse and then a StoppedEvent (event type +// 'step') after the step has completed.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stepOut" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StepOutArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StepOutArguments": { +// "type": "object", +// "description": "Arguments for 'stepOut' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'stepOut' for this thread." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StepOutResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stepOut' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void request_stepOut(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = thread.GetThreadID(); + thread.StepOut(); + } else { + response["success"] = llvm::json::Value(false); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "ThreadsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Thread request; value of command field is 'threads'. The +// request retrieves a list of all threads.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "threads" ] +// } +// }, +// "required": [ "command" ] +// }] +// }, +// "ThreadsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'threads' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "threads": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Thread" +// }, +// "description": "All threads." +// } +// }, +// "required": [ "threads" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_threads(const llvm::json::Object &request) { + + lldb::SBProcess process = g_vsc.target.GetProcess(); + llvm::json::Object response; + FillResponse(request, response); + + const uint32_t num_threads = process.GetNumThreads(); + llvm::json::Array threads; + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); + threads.emplace_back(CreateThread(thread)); + } + if (threads.size() == 0) { + response["success"] = llvm::json::Value(false); + } + llvm::json::Object body; + body.try_emplace("threads", std::move(threads)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "SetVariableRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "setVariable request; value of command field is +// 'setVariable'. Set the variable with the given name in the variable +// container to a new value.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setVariable" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetVariableArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetVariableArguments": { +// "type": "object", +// "description": "Arguments for 'setVariable' request.", +// "properties": { +// "variablesReference": { +// "type": "integer", +// "description": "The reference of the variable container." +// }, +// "name": { +// "type": "string", +// "description": "The name of the variable." +// }, +// "value": { +// "type": "string", +// "description": "The value of the variable." +// }, +// "format": { +// "$ref": "#/definitions/ValueFormat", +// "description": "Specifies details on how to format the response value." +// } +// }, +// "required": [ "variablesReference", "name", "value" ] +// }, +// "SetVariableResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setVariable' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "value": { +// "type": "string", +// "description": "The new value of the variable." +// }, +// "type": { +// "type": "string", +// "description": "The type of the new value. Typically shown in the +// UI when hovering over the value." +// }, +// "variablesReference": { +// "type": "number", +// "description": "If variablesReference is > 0, the new value is +// structured and its children can be retrieved by passing +// variablesReference to the VariablesRequest." +// }, +// "namedVariables": { +// "type": "number", +// "description": "The number of named child variables. The client +// can use this optional information to present the variables in a +// paged UI and fetch them in chunks." +// }, +// "indexedVariables": { +// "type": "number", +// "description": "The number of indexed child variables. The client +// can use this optional information to present the variables in a +// paged UI and fetch them in chunks." +// } +// }, +// "required": [ "value" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_setVariable(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Array variables; + llvm::json::Object body; + auto arguments = request.getObject("arguments"); + // This is a reference to the containing variable/scope + const auto variablesReference = + GetUnsigned(arguments, "variablesReference", 0); + const auto name = GetString(arguments, "name"); + const auto value = GetString(arguments, "value"); + // Set success to false just in case we don't find the variable by name + response.try_emplace("success", false); + + lldb::SBValue variable; + int64_t newVariablesReference = 0; + + // The "id" is the unique integer ID that is unique within the enclosing + // variablesReference. It is optionally added to any "interface Variable" + // objects to uniquely identify a variable within an enclosing + // variablesReference. It helps to disambiguate between two variables that + // have the same name within the same scope since the "setVariables" request + // only specifies the variable reference of the enclosing scope/variable, and + // the name of the variable. We could have two shadowed variables with the + // same name in "Locals" or "Globals". In our case the "id" absolute index + // of the variable within the g_vsc.variables list. + const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX); + if (id_value != UINT64_MAX) { + variable = g_vsc.variables.GetValueAtIndex(id_value); + } else if (VARREF_IS_SCOPE(variablesReference)) { + // variablesReference is one of our scopes, not an actual variable it is + // asking for a variable in locals or globals or registers + int64_t start_idx = 0; + int64_t end_idx = 0; + switch (variablesReference) { + case VARREF_LOCALS: + start_idx = 0; + end_idx = start_idx + g_vsc.num_locals; + break; + case VARREF_GLOBALS: + start_idx = g_vsc.num_locals; + end_idx = start_idx + g_vsc.num_globals; + break; + case VARREF_REGS: + start_idx = g_vsc.num_locals + g_vsc.num_globals; + end_idx = start_idx + g_vsc.num_regs; + break; + default: + break; + } + + // Find the variable by name in the correct scope and hope we don't have + // multiple variables with the same name. We search backwards because + // the list of variables has the top most variables first and variables + // in deeper scopes are last. This means we will catch the deepest + // variable whose name matches which is probably what the user wants. + for (int64_t i = end_idx - 1; i >= start_idx; --i) { + auto curr_variable = g_vsc.variables.GetValueAtIndex(i); + llvm::StringRef variable_name(curr_variable.GetName()); + if (variable_name == name) { + variable = curr_variable; + if (curr_variable.MightHaveChildren()) + newVariablesReference = i; + break; + } + } + } else { + // We have a named item within an actual variable so we need to find it + // withing the container variable by name. + const int64_t var_idx = VARREF_TO_VARIDX(variablesReference); + lldb::SBValue container = g_vsc.variables.GetValueAtIndex(var_idx); + variable = container.GetChildMemberWithName(name.data()); + if (!variable.IsValid()) { + if (name.startswith("[")) { + llvm::StringRef index_str(name.drop_front(1)); + uint64_t index = 0; + if (!index_str.consumeInteger(0, index)) { + if (index_str == "]") + variable = container.GetChildAtIndex(index); + } + } + } + + // We don't know the index of the variable in our g_vsc.variables + if (variable.IsValid()) { + if (variable.MightHaveChildren()) { + newVariablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize()); + g_vsc.variables.Append(variable); + } + } + } + + if (variable.IsValid()) { + lldb::SBError error; + bool success = variable.SetValueFromCString(value.data(), error); + if (success) { + SetValueForKey(variable, body, "value"); + EmplaceSafeString(body, "type", variable.GetType().GetDisplayTypeName()); + body.try_emplace("variablesReference", newVariablesReference); + } else { + EmplaceSafeString(body, "message", std::string(error.GetCString())); + } + response["success"] = llvm::json::Value(success); + } + + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// "VariablesRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Variables request; value of command field is 'variables'. +// Retrieves all child variables for the given variable reference. An +// optional filter can be used to limit the fetched children to either named +// or indexed children.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "variables" ] +// }, +// "arguments": { +// "$ref": "#/definitions/VariablesArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "VariablesArguments": { +// "type": "object", +// "description": "Arguments for 'variables' request.", +// "properties": { +// "variablesReference": { +// "type": "integer", +// "description": "The Variable reference." +// }, +// "filter": { +// "type": "string", +// "enum": [ "indexed", "named" ], +// "description": "Optional filter to limit the child variables to either +// named or indexed. If ommited, both types are fetched." +// }, +// "start": { +// "type": "integer", +// "description": "The index of the first variable to return; if omitted +// children start at 0." +// }, +// "count": { +// "type": "integer", +// "description": "The number of variables to return. If count is missing +// or 0, all variables are returned." +// }, +// "format": { +// "$ref": "#/definitions/ValueFormat", +// "description": "Specifies details on how to format the Variable +// values." +// } +// }, +// "required": [ "variablesReference" ] +// }, +// "VariablesResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'variables' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "variables": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Variable" +// }, +// "description": "All (or a range) of variables for the given +// variable reference." +// } +// }, +// "required": [ "variables" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_variables(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Array variables; + auto arguments = request.getObject("arguments"); + const auto variablesReference = + GetUnsigned(arguments, "variablesReference", 0); + const int64_t start = GetSigned(arguments, "start", 0); + const int64_t count = GetSigned(arguments, "count", 0); + bool hex = false; + auto format = arguments->getObject("format"); + if (format) + hex = GetBoolean(format, "hex", false); + + if (VARREF_IS_SCOPE(variablesReference)) { + // variablesReference is one of our scopes, not an actual variable it is + // asking for the list of args, locals or globals. + int64_t start_idx = 0; + int64_t num_children = 0; + switch (variablesReference) { + case VARREF_LOCALS: + start_idx = start; + num_children = g_vsc.num_locals; + break; + case VARREF_GLOBALS: + start_idx = start + g_vsc.num_locals + start; + num_children = g_vsc.num_globals; + break; + case VARREF_REGS: + start_idx = start + g_vsc.num_locals + g_vsc.num_globals; + num_children = g_vsc.num_regs; + break; + default: + break; + } + const int64_t end_idx = start_idx + ((count == 0) ? num_children : count); + for (auto i = start_idx; i < end_idx; ++i) { + lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(i); + if (!variable.IsValid()) + break; + variables.emplace_back( + CreateVariable(variable, VARIDX_TO_VARREF(i), i, hex)); + } + } else { + // We are expanding a variable that has children, so we will return its + // children. + const int64_t var_idx = VARREF_TO_VARIDX(variablesReference); + lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(var_idx); + if (variable.IsValid()) { + const auto num_children = variable.GetNumChildren(); + const int64_t end_idx = start + ((count == 0) ? num_children : count); + for (auto i = start; i < end_idx; ++i) { + lldb::SBValue child = variable.GetChildAtIndex(i); + if (!child.IsValid()) + break; + if (child.MightHaveChildren()) { + const int64_t var_idx = g_vsc.variables.GetSize(); + auto childVariablesReferences = VARIDX_TO_VARREF(var_idx); + variables.emplace_back( + CreateVariable(child, childVariablesReferences, var_idx, hex)); + g_vsc.variables.Append(child); + } else { + variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex)); + } + } + } + } + llvm::json::Object body; + body.try_emplace("variables", std::move(variables)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// A request used in testing to get the details on all breakpoints that are +// currently set in the target. This helps us to test "setBreakpoints" and +// "setFunctionBreakpoints" requests to verify we have the correct set of +// breakpoints currently set in LLDB. +void request__testGetTargetBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Array response_breakpoints; + for (uint32_t i = 0; g_vsc.target.GetBreakpointAtIndex(i).IsValid(); ++i) { + auto bp = g_vsc.target.GetBreakpointAtIndex(i); + AppendBreakpoint(bp, response_breakpoints); + } + llvm::json::Object body; + body.try_emplace("breakpoints", std::move(response_breakpoints)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +const std::map<std::string, RequestCallback> &GetRequestHandlers() { +#define REQUEST_CALLBACK(name) \ + { #name, request_##name } + static std::map<std::string, RequestCallback> g_request_handlers = { + // VSCode Debug Adaptor requests + REQUEST_CALLBACK(attach), + REQUEST_CALLBACK(completions), + REQUEST_CALLBACK(continue), + REQUEST_CALLBACK(configurationDone), + REQUEST_CALLBACK(disconnect), + REQUEST_CALLBACK(evaluate), + REQUEST_CALLBACK(exceptionInfo), + REQUEST_CALLBACK(initialize), + REQUEST_CALLBACK(launch), + REQUEST_CALLBACK(next), + REQUEST_CALLBACK(pause), + REQUEST_CALLBACK(scopes), + REQUEST_CALLBACK(setBreakpoints), + REQUEST_CALLBACK(setExceptionBreakpoints), + REQUEST_CALLBACK(setFunctionBreakpoints), + REQUEST_CALLBACK(setVariable), + REQUEST_CALLBACK(source), + REQUEST_CALLBACK(stackTrace), + REQUEST_CALLBACK(stepIn), + REQUEST_CALLBACK(stepOut), + REQUEST_CALLBACK(threads), + REQUEST_CALLBACK(variables), + // Testing requests + REQUEST_CALLBACK(_testGetTargetBreakpoints), + }; +#undef REQUEST_CALLBACK + return g_request_handlers; +} + +} // anonymous namespace + +int main(int argc, char *argv[]) { + + // Initialize LLDB first before we do anything. + lldb::SBDebugger::Initialize(); + + if (argc == 2) { + const char *arg = argv[1]; +#if !defined(_WIN32) + if (strcmp(arg, "-g") == 0) { + printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid()); + pause(); + } else { +#else + { +#endif + int portno = atoi(arg); + printf("Listening on port %i...\n", portno); + SOCKET socket_fd = AcceptConnection(portno); + if (socket_fd >= 0) { + g_vsc.input.descriptor = StreamDescriptor::from_socket(socket_fd, true); + g_vsc.output.descriptor = + StreamDescriptor::from_socket(socket_fd, false); + } else { + exit(1); + } + } + } else { + g_vsc.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false); + g_vsc.output.descriptor = + StreamDescriptor::from_file(fileno(stdout), false); + } + auto request_handlers = GetRequestHandlers(); + uint32_t packet_idx = 0; + while (true) { + std::string json = g_vsc.ReadJSON(); + if (json.empty()) + break; + + llvm::StringRef json_sref(json); + llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref); + if (!json_value) { + auto error = json_value.takeError(); + if (g_vsc.log) { + std::string error_str; + llvm::raw_string_ostream strm(error_str); + strm << error; + strm.flush(); + + *g_vsc.log << "error: failed to parse JSON: " << error_str << std::endl + << json << std::endl; + } + return 1; + } + + auto object = json_value->getAsObject(); + if (!object) { + if (g_vsc.log) + *g_vsc.log << "error: json packet isn't a object" << std::endl; + return 1; + } + + const auto packet_type = GetString(object, "type"); + if (packet_type == "request") { + const auto command = GetString(object, "command"); + auto handler_pos = request_handlers.find(command); + if (handler_pos != request_handlers.end()) { + handler_pos->second(*object); + } else { + if (g_vsc.log) + *g_vsc.log << "error: unhandled command \"" << command.data() << std::endl; + return 1; + } + } + ++packet_idx; + } + + // We must terminate the debugger in a thread before the C++ destructor + // chain messes everything up. + lldb::SBDebugger::Terminate(); + return 0; +} diff --git a/gnu/llvm/lldb/tools/lldb-vscode/package.json b/gnu/llvm/lldb/tools/lldb-vscode/package.json new file mode 100644 index 00000000000..847a219fbb5 --- /dev/null +++ b/gnu/llvm/lldb/tools/lldb-vscode/package.json @@ -0,0 +1,245 @@ +{ + "name": "lldb-vscode", + "displayName": "LLDB native Debug stub", + "version": "0.1.0", + "publisher": "llvm.org", + "description": "Debug adapter for LLDB which uses a C++ tool to interface directly with LLDB.", + "author": { + "name": "Greg Clayton", + "email": "clayborg@gmail.com" + }, + "license": "LLVM", + "keywords": [ + "multi-root ready" + ], + "engines": { + "vscode": "^1.18.0", + "node": "^7.9.0" + }, + "icon": "images/lldb.png", + "categories": [ + "Debuggers" + ], + "private": true, + "devDependencies": { + "@types/node": "7.0.43", + "@types/mocha": "2.2.45", + "typescript": "2.6.2", + "mocha": "4.0.1", + "vscode": "1.1.10", + "vscode-debugadapter-testsupport": "1.25.0", + "tslint": "5.8.0", + "vsce": "1.35.0" + }, + "contributes": { + "debuggers": [ + { + "type": "lldb-vscode", + "label": "Native LLDB Debugger", + "enableBreakpointsFor": { + "languageIds": [ + "ada", + "arm", + "asm", + "c", + "cpp", + "crystal", + "d", + "fortan", + "fortran-modern", + "nim", + "objective-c", + "objectpascal", + "pascal", + "rust", + "swift" + ] + }, + "program": "./bin/lldb-vscode", + "windows": { + "program": "./bin/lldb-vscode.exe" + }, + "configurationAttributes": { + "launch": { + "required": [ + "program" + ], + "properties": { + "program": { + "type": "string", + "description": "Path to the program to debug." + }, + "args": { + "type": [ "array", "string" ], + "description": "Program arguments.", + "default": [] + }, + "cwd": { + "type": "string", + "description": "Program working directory.", + "default": "${workspaceRoot}" + }, + "env": { + "type": "array", + "description": "Additional environment variables.", + "default": [] + }, + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": false + }, + "disableASLR": { + "type": "boolean", + "description": "Enable or disable Address space layout randomization if the debugger supports it.", + "default": true + }, + "disableSTDIO": { + "type": "boolean", + "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", + "default": false + }, + "shellExpandArguments": { + "type": "boolean", + "description": "Expand program arguments as a shell would without actually launching the program in a shell.", + "default": false + }, + "detachOnError": { + "type": "boolean", + "description": "Detach from the program.", + "default": false + }, + "trace": { + "type": "boolean", + "description": "Enable logging of the Debug Adapter Protocol.", + "default": true + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "type": "array", + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and desination pathname. Overrides sourcePath.", + "default": [] + }, + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adaptor to so relative object files can be located." + }, + "initCommands": { + "type": "array", + "description": "Initialization commands executed upon debugger startup.", + "default": [] + }, + "preRunCommands": { + "type": "array", + "description": "Commands executed just before the program is launched.", + "default": [] + }, + "stopCommands": { + "type": "array", + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "description": "Commands executed at the end of debugging session.", + "default": [] + } + } + }, + "attach": { + "properties": { + "program": { + "type": "string", + "description": "Path to the program to attach to." + }, + "pid": { + "type": [ + "number", + "string" + ], + "description": "System process ID to attach to." + }, + "waitFor": { + "type": "boolean", + "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.", + "default": true + }, + "trace": { + "type": "boolean", + "description": "Enable logging of the Debug Adapter Protocol.", + "default": true + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "type": "array", + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and desination pathname. Overrides sourcePath.", + "default": [] + }, + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adaptor to so relative object files can be located." + }, + "attachCommands": { + "type": "array", + "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.", + "default": [] + }, + "initCommands": { + "type": "array", + "description": "Initialization commands executed upon debugger startup.", + "default": [] + }, + "preRunCommands": { + "type": "array", + "description": "Commands executed just before the program is attached to.", + "default": [] + }, + "stopCommands": { + "type": "array", + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "description": "Commands executed at the end of debugging session.", + "default": [] + } + } + } + }, + "initialConfigurations": [ + { + "type": "lldb-vscode", + "request": "launch", + "name": "Debug", + "program": "${workspaceRoot}/<your program>", + "args": [], + "env": [], + "cwd": "${workspaceRoot}" + } + ], + "configurationSnippets": [ + { + "label": "LLDB: Launch", + "description": "", + "body": { + "type": "lldb-vscode", + "request": "launch", + "name": "${2:Launch}", + "program": "^\"\\${workspaceRoot}/${1:<your program>}\"", + "args": [], + "env": [], + "cwd": "^\"\\${workspaceRoot}\"" + } + } + ] + } + ] + } +} |