diff options
author | Vladimir Mezentsev <vladimir.mezentsev@oracle.com> | 2022-03-11 08:58:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2022-03-11 08:58:31 +0000 |
commit | bb368aad297fe3ad40cf397e6fc85aa471429a28 (patch) | |
tree | 0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/src/Disasm.cc | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2 |
gprofng: a new GNU profiler
top-level
* Makefile.def: Add gprofng module.
* configure.ac: Add --enable-gprofng option.
* src-release.sh: Add gprofng.
* Makefile.in: Regenerate.
* configure: Regenerate.
* gprofng: New directory.
binutils
* MAINTAINERS: Add gprofng maintainer.
* README-how-to-make-a-release: Add gprofng.
include.
* collectorAPI.h: New file.
* libcollector.h: New file.
* libfcollector.h: New file.
Diffstat (limited to 'gprofng/src/Disasm.cc')
-rw-r--r-- | gprofng/src/Disasm.cc | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/gprofng/src/Disasm.cc b/gprofng/src/Disasm.cc new file mode 100644 index 0000000..0fec9c3 --- /dev/null +++ b/gprofng/src/Disasm.cc @@ -0,0 +1,403 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <sys/param.h> + +#include "disassemble.h" +#include "dis-asm.h" +#include "demangle.h" +#include "dbe_types.h" +#include "DbeSession.h" +#include "Elf.h" +#include "Disasm.h" +#include "Stabs.h" +#include "i18n.h" +#include "util.h" +#include "StringBuilder.h" + +struct DisContext +{ + bool is_Intel; + Stabs *stabs; + uint64_t pc; // first_pc <= pc < last_pc + uint64_t first_pc; + uint64_t last_pc; + uint64_t f_offset; // file offset for first_pc + int codeptr[4]; // longest instruction length may not be > 16 + Data_window *elf; +}; + +static const int MAX_DISASM_STR = 2048; +static const int MAX_INSTR_SIZE = 8; + +Disasm::Disasm (char *fname) +{ + dwin = NULL; + dis_str = NULL; + need_swap_endian = false; + my_stabs = Stabs::NewStabs (fname, fname); + if (my_stabs == NULL) + return; + stabs = my_stabs; + platform = stabs->get_platform (); + disasm_open (); +} + +Disasm::Disasm (Platform_t _platform, Stabs *_stabs) +{ + dwin = NULL; + dis_str = NULL; + need_swap_endian = false; + stabs = _stabs; + platform = _platform; + my_stabs = NULL; + disasm_open (); +} + +static int +fprintf_func (void *arg, const char *fmt, ...) +{ + char buf[512]; + va_list vp; + va_start (vp, fmt); + int cnt = vsnprintf (buf, sizeof (buf), fmt, vp); + va_end (vp); + + Disasm *dis = (Disasm *) arg; + dis->dis_str->append (buf); + return cnt; +} + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +static int +read_memory_func (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + disassemble_info *info) +{ + unsigned int opb = info->octets_per_byte; + size_t end_addr_offset = length / opb; + size_t max_addr_offset = info->buffer_length / opb; + size_t octets = (memaddr - info->buffer_vma) * opb; + if (memaddr < info->buffer_vma + || memaddr - info->buffer_vma > max_addr_offset + || memaddr - info->buffer_vma + end_addr_offset > max_addr_offset + || (info->stop_vma && (memaddr >= info->stop_vma + || memaddr + end_addr_offset > info->stop_vma))) + return -1; + memcpy (myaddr, info->buffer + octets, length); + return 0; +} + +static void +print_address_func (bfd_vma addr, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "0x%llx", (unsigned long long) addr); +} + +static asymbol * +symbol_at_address_func (bfd_vma addr ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return NULL; +} + +static bfd_boolean +symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return TRUE; +} + +static void +memory_error_func (int status, bfd_vma addr, disassemble_info *info) +{ + info->fprintf_func (info->stream, "Address 0x%llx is out of bounds.\n", + (unsigned long long) addr); +} + +void +Disasm::disasm_open () +{ + hex_visible = 1; + snprintf (addr_fmt, sizeof (addr_fmt), NTXT ("%s"), NTXT ("%8llx: ")); + if (dis_str == NULL) + dis_str = new StringBuilder; + + switch (platform) + { + case Aarch64: + case Intel: + case Amd64: + need_swap_endian = (DbeSession::platform == Sparc); + break; + case Sparcv8plus: + case Sparcv9: + case Sparc: + default: + need_swap_endian = (DbeSession::platform != Sparc); + break; + } + + memset (&dis_info, 0, sizeof (dis_info)); + dis_info.flavour = bfd_target_unknown_flavour; + dis_info.endian = BFD_ENDIAN_UNKNOWN; + dis_info.endian_code = dis_info.endian; + dis_info.octets_per_byte = 1; + dis_info.disassembler_needs_relocs = FALSE; + dis_info.fprintf_func = fprintf_func; + dis_info.stream = this; + dis_info.disassembler_options = NULL; + dis_info.read_memory_func = read_memory_func; + dis_info.memory_error_func = memory_error_func; + dis_info.print_address_func = print_address_func; + dis_info.symbol_at_address_func = symbol_at_address_func; + dis_info.symbol_is_valid = symbol_is_valid; + dis_info.display_endian = BFD_ENDIAN_UNKNOWN; + dis_info.symtab = NULL; + dis_info.symtab_size = 0; + dis_info.buffer_vma = 0; + switch (platform) + { + case Aarch64: + dis_info.arch = bfd_arch_aarch64; + dis_info.mach = bfd_mach_aarch64; + break; + case Intel: + case Amd64: + dis_info.arch = bfd_arch_i386; + dis_info.mach = bfd_mach_x86_64; + break; + case Sparcv8plus: + case Sparcv9: + case Sparc: + default: + dis_info.arch = bfd_arch_unknown; + dis_info.endian = BFD_ENDIAN_UNKNOWN; + break; + } + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_BIG; + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_LITTLE; + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_UNKNOWN; + disassemble_init_for_target (&dis_info); +} + +Disasm::~Disasm () +{ + delete my_stabs; + delete dwin; + delete dis_str; +} + +void +Disasm::set_img_name (char *img_fname) +{ + if (stabs == NULL && img_fname && dwin == NULL) + { + dwin = new Data_window (img_fname); + if (dwin->not_opened ()) + { + delete dwin; + dwin = NULL; + return; + } + dwin->need_swap_endian = need_swap_endian; + } +} + +void +Disasm::remove_disasm_hndl (void *hndl) +{ + DisContext *ctx = (DisContext *) hndl; + delete ctx; +} + +#if 0 +int +Disasm::get_instr_size (uint64_t vaddr, void *hndl) +{ + DisContext *ctx = (DisContext *) hndl; + if (ctx == NULL || vaddr < ctx->first_pc || vaddr >= ctx->last_pc) + return -1; + ctx->pc = vaddr; + size_t sz = ctx->is_Intel ? sizeof (ctx->codeptr) : 4; + if (sz > ctx->last_pc - vaddr) + sz = (size_t) (ctx->last_pc - vaddr); + if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), + sz, ctx->codeptr) == NULL) + return -1; + + char buf[MAX_DISASM_STR]; + *buf = 0; + uint64_t inst_vaddr = vaddr; +#if MEZ_NEED_TO_FIX + size_t instrs_cnt = 0; + disasm_err_code_t status = disasm (handle, &inst_vaddr, ctx->last_pc, 1, + ctx, buf, sizeof (buf), &instrs_cnt); + if (instrs_cnt != 1 || status != disasm_err_ok) + return -1; +#endif + return (int) (inst_vaddr - vaddr); +} +#endif + +void +Disasm::set_addr_end (uint64_t end_address) +{ + char buf[32]; + int len = snprintf (buf, sizeof (buf), "%llx", (long long) end_address); + snprintf (addr_fmt, sizeof (addr_fmt), "%%%dllx: ", len < 8 ? 8 : len); +} + +char * +Disasm::get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, int64_t &inst_size) +{ + inst_size = 0; + if (inst_address >= end_address) + return NULL; + Data_window *dw = stabs ? stabs->openElf (false) : dwin; + if (dw == NULL) + return NULL; + + unsigned char buffer[MAX_DISASM_STR]; + dis_info.buffer = buffer; + dis_info.buffer_length = end_address - inst_address; + if (dis_info.buffer_length > sizeof (buffer)) + dis_info.buffer_length = sizeof (buffer); + dw->get_data (f_offset + (inst_address - start_address), + dis_info.buffer_length, dis_info.buffer); + + dis_str->setLength (0); + bfd abfd; + disassembler_ftype disassemble = disassembler (dis_info.arch, dis_info.endian, + dis_info.mach, &abfd); + if (disassemble == NULL) + { + printf ("ERROR: unsupported disassemble\n"); + return NULL; + } + inst_size = disassemble (0, &dis_info); + if (inst_size <= 0) + { + inst_size = 0; + return NULL; + } + StringBuilder sb; + sb.appendf (addr_fmt, inst_address); // Write address + + // Write hex bytes of instruction + if (hex_visible) + { + char bytes[64]; + *bytes = '\0'; + for (int i = 0; i < inst_size; i++) + { + unsigned int hex_value = buffer[i] & 0xff; + snprintf (bytes + 3 * i, sizeof (bytes) - 3 * i, "%02x ", hex_value); + } + const char *fmt = "%s "; + if (platform == Intel) + fmt = "%-21s "; // 21 = 3 * 7 - maximum instruction length on Intel + sb.appendf (fmt, bytes); + } + sb.append (dis_str); +#if MEZ_NEED_TO_FIX + // Write instruction + if (ctx.is_Intel) // longest instruction length for Intel is 7 + sb.appendf (NTXT ("%-7s %s"), parts_array[1], parts_array[2]); + else // longest instruction length for SPARC is 11 + sb.appendf (NTXT ("%-11s %s"), parts_array[1], parts_array[2]); + if (strcmp (parts_array[1], NTXT ("call")) == 0) + { + if (strncmp (parts_array[2], NTXT ("0x"), 2) == 0) + sb.append (GTXT ("\t! (Unable to determine target symbol)")); + } +#endif + return sb.toString (); +} + +#if MEZ_NEED_TO_FIX +void * +Disasm::get_inst_ptr (disasm_handle_t, uint64_t vaddr, void *pass_through) +{ + // Actually it fetches only one instruction at a time for sparc, + // and one byte at a time for intel. + DisContext *ctx = (DisContext*) pass_through; + size_t sz = ctx->is_Intel ? 1 : 4; + if (vaddr + sz > ctx->last_pc) + { + ctx->codeptr[0] = -1; + return ctx->codeptr; + } + if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), sz, ctx->codeptr) == NULL) + { + ctx->codeptr[0] = -1; + return ctx->codeptr; + } + if (ctx->elf->need_swap_endian && !ctx->is_Intel) + ctx->codeptr[0] = ctx->elf->decode (ctx->codeptr[0]); + return ctx->codeptr; +} + +// get a symbol name for an address +disasm_err_code_t +Disasm::get_sym_name (disasm_handle_t, // an open disassembler handle + uint64_t target_address, // the target virtual address + uint64_t inst_address, // virtual address of instruction + // being disassembled + int use_relocation, // flag to use relocation information + char *buffer, // places the symbol here + size_t buffer_size, // limit on symbol length + int *, // sys/elf_{SPARC.386}.h + uint64_t *offset, // from the symbol to the address + void *pass_through) // disassembler context +{ + char buf[MAXPATHLEN]; + if (!use_relocation) + return disasm_err_symbol; + + DisContext *ctxp = (DisContext*) pass_through; + char *name = NULL; + if (ctxp->stabs) + { + uint64_t addr = ctxp->f_offset + (inst_address - ctxp->first_pc); + name = ctxp->stabs->sym_name (target_address, addr, use_relocation); + } + if (name == NULL) + return disasm_err_symbol; + + char *s = NULL; + if (*name == '_') + s = cplus_demangle (name, DMGL_PARAMS); + if (s) + { + snprintf (buffer, buffer_size, NTXT ("%s"), s); + free (s); + } + else + snprintf (buffer, buffer_size, NTXT ("%s"), name); + + *offset = 0; + return disasm_err_ok; +} +#endif |