< prev index next >

src/hotspot/share/compiler/disassembler.cpp

Print this page
rev 54763 : 8213084: Rework and enhance Print[Opto]Assembly output
Reviewed-by:

*** 21,31 **** * questions. * */ #include "precompiled.hpp" ! #include "asm/macroAssembler.hpp" #include "ci/ciUtilities.hpp" #include "classfile/javaClasses.hpp" #include "code/codeCache.hpp" #include "compiler/disassembler.hpp" #include "gc/shared/cardTable.hpp" --- 21,31 ---- * questions. * */ #include "precompiled.hpp" ! #include "asm/assembler.inline.hpp" #include "ci/ciUtilities.hpp" #include "classfile/javaClasses.hpp" #include "code/codeCache.hpp" #include "compiler/disassembler.hpp" #include "gc/shared/cardTable.hpp"
*** 40,49 **** --- 40,50 ---- #include "utilities/resourceHash.hpp" #include CPU_HEADER(depChecker) void* Disassembler::_library = NULL; bool Disassembler::_tried_to_load_library = false; + bool Disassembler::_library_usable = false; // This routine is in the shared library: Disassembler::decode_func_virtual Disassembler::_decode_instructions_virtual = NULL; Disassembler::decode_func Disassembler::_decode_instructions = NULL;
*** 52,182 **** static const char decode_instructions_name[] = "decode_instructions"; static bool use_new_version = true; #define COMMENT_COLUMN 52 LP64_ONLY(+8) /*could be an option*/ #define BYTES_COMMENT ";..." /* funky byte display comment */ - bool Disassembler::load_library() { - if (_decode_instructions_virtual != NULL || _decode_instructions != NULL) { - // Already succeeded. - return true; - } - if (_tried_to_load_library) { - // Do not try twice. - // To force retry in debugger: assign _tried_to_load_library=0 - return false; - } - // Try to load it. - char ebuf[1024]; - char buf[JVM_MAXPATHLEN]; - os::jvm_path(buf, sizeof(buf)); - int jvm_offset = -1; - int lib_offset = -1; - #ifdef STATIC_BUILD - char* p = strrchr(buf, '/'); - *p = '\0'; - strcat(p, "/lib/"); - lib_offset = jvm_offset = strlen(buf); - #else - { - // Match "jvm[^/]*" in jvm_path. - const char* base = buf; - const char* p = strrchr(buf, *os::file_separator()); - if (p != NULL) lib_offset = p - base + 1; - p = strstr(p ? p : base, "jvm"); - if (p != NULL) jvm_offset = p - base; - } - #endif - // Find the disassembler shared library. - // Search for several paths derived from libjvm, in this order: - // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so (for compatibility) - // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so - // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so - // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH) - if (jvm_offset >= 0) { - // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so - strcpy(&buf[jvm_offset], hsdis_library_name); - strcat(&buf[jvm_offset], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - if (_library == NULL && lib_offset >= 0) { - // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so - strcpy(&buf[lib_offset], hsdis_library_name); - strcat(&buf[lib_offset], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - } - if (_library == NULL && lib_offset > 0) { - // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so - buf[lib_offset - 1] = '\0'; - const char* p = strrchr(buf, *os::file_separator()); - if (p != NULL) { - lib_offset = p - buf + 1; - strcpy(&buf[lib_offset], hsdis_library_name); - strcat(&buf[lib_offset], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - } - } - } - if (_library == NULL) { - // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH) - strcpy(&buf[0], hsdis_library_name); - strcat(&buf[0], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - } - if (_library != NULL) { - _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual, - os::dll_lookup(_library, decode_instructions_virtual_name)); - } - if (_decode_instructions_virtual == NULL && _library != NULL) { - // could not spot in new version, try old version - _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, - os::dll_lookup(_library, decode_instructions_name)); - use_new_version = false; - } else { - use_new_version = true; - } - _tried_to_load_library = true; - if (_decode_instructions_virtual == NULL && _decode_instructions == NULL) { - tty->print_cr("Could not load %s; %s; %s", buf, - ((_library != NULL) - ? "entry point is missing" - : (WizardMode || PrintMiscellaneous) - ? (const char*)ebuf - : "library not loadable"), - "PrintAssembly is disabled"); - return false; - } - - // Success. - tty->print_cr("Loaded disassembler from %s", buf); - return true; - } - - class decode_env { private: ! nmethod* _nm; ! CodeBlob* _code; CodeStrings _strings; ! outputStream* _output; ! address _start, _end; ! ptrdiff_t _offset; char _option_buf[512]; char _print_raw; ! bool _print_pc; ! bool _print_bytes; ! address _cur_insn; int _bytes_per_line; // arch-specific formatting option bool _print_file_name; static bool match(const char* event, const char* tag) { size_t taglen = strlen(tag); ! if (strncmp(event, tag, taglen) != 0) return false; char delim = event[taglen]; return delim == '\0' || delim == ' ' || delim == '/' || delim == '='; } void collect_options(const char* p) { if (p == NULL || p[0] == '\0') return; size_t opt_so_far = strlen(_option_buf); if (opt_so_far + 1 + strlen(p) + 1 > sizeof(_option_buf)) return; char* fillp = &_option_buf[opt_so_far]; --- 53,102 ---- static const char decode_instructions_name[] = "decode_instructions"; static bool use_new_version = true; #define COMMENT_COLUMN 52 LP64_ONLY(+8) /*could be an option*/ #define BYTES_COMMENT ";..." /* funky byte display comment */ class decode_env { private: ! outputStream* _output; // where the disassembly is directed to ! CodeBuffer* _codeBuffer; // != NULL only when decoding a CodeBuffer ! CodeBlob* _codeBlob; // != NULL only when decoding a CodeBlob ! nmethod* _nm; // != NULL only when decoding a nmethod CodeStrings _strings; ! address _start; // != NULL when decoding a range of unknown type ! address _end; // != NULL when decoding a range of unknown type char _option_buf[512]; char _print_raw; ! address _cur_insn; // address of instruction currently being decoded int _bytes_per_line; // arch-specific formatting option + int _pre_decode_alignment; + int _post_decode_alignment; bool _print_file_name; + bool _print_help; + bool _helpPrinted; + static bool _optionsParsed; + + enum { + tabspacing = 8 + }; + // Check if the event matches the expected tag + // The tag must be a substring of the event, and + // the tag must be a token in the event, i.e. separated by delimiters static bool match(const char* event, const char* tag) { + size_t eventlen = strlen(event); size_t taglen = strlen(tag); ! if (eventlen < taglen) // size mismatch ! return false; ! if (strncmp(event, tag, taglen) != 0) // string mismatch return false; char delim = event[taglen]; return delim == '\0' || delim == ' ' || delim == '/' || delim == '='; } + // Merge new option string with previously recorded options void collect_options(const char* p) { if (p == NULL || p[0] == '\0') return; size_t opt_so_far = strlen(_option_buf); if (opt_so_far + 1 + strlen(p) + 1 > sizeof(_option_buf)) return; char* fillp = &_option_buf[opt_so_far];
*** 188,201 **** *q++ = ','; // Note that multiple PrintAssemblyOptions flags accumulate with \n, // which we want to be changed to a comma... } void print_insn_labels(); ! void print_insn_bytes(address pc0, address pc); void print_address(address value); struct SourceFileInfo { struct Link : public CHeapObj<mtCode> { const char* file; int line; Link* next; --- 108,165 ---- *q++ = ','; // Note that multiple PrintAssemblyOptions flags accumulate with \n, // which we want to be changed to a comma... } + void process_options(outputStream* ost); + void print_insn_labels(); ! void print_insn_prefix(); void print_address(address value); + // Properly initializes _start/_end. Overwritten too often if + // printing of instructions is called for each instruction. + void set_start(address s) { _start = s; } + void set_end (address e) { _end = e; } + void set_nm (nmethod* nm) { _nm = nm; } + void set_output(outputStream* st) { _output = st; } + + #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + // The disassembler library (sometimes) uses tabs to nicely align the instruction operands. + // Depending on the mnemonic length and the column position where the + // mnemonic is printed, alignment may turn out to be not so nice. + // To improve, we assume 8-character tab spacing and left-align the mnemonic on a tab position. + // Instruction comments are aligned 4 tab positions to the right of the mnemonic. + void calculate_alignment() { + _pre_decode_alignment = ((output()->position()+tabspacing-1)/tabspacing)*tabspacing; + _post_decode_alignment = _pre_decode_alignment + 4*tabspacing; + } + + void start_insn(address pc) { + _cur_insn = pc; + output()->bol(); + print_insn_labels(); + print_insn_prefix(); + } + + void end_insn(address pc) { + address pc0 = cur_insn(); + outputStream* st = output(); + + if (AbstractDisassembler::show_comment()) { + if ((_nm != NULL) && _nm->has_code_comment(pc0, pc)) { + _nm->print_code_comment_on(st, _post_decode_alignment, pc0, pc); + // this calls reloc_string_for which calls oop::print_value_on + } + print_hook_comments(pc0, _nm != NULL); + } + Disassembler::annotate(pc0, output()); + // follow each complete insn by a nice newline + st->bol(); + } + #endif + struct SourceFileInfo { struct Link : public CHeapObj<mtCode> { const char* file; int line; Link* next;
*** 238,281 **** static SourceFileInfoTable _src_table; static const char* _cached_src; static GrowableArray<const char*>* _cached_src_lines; public: ! decode_env(CodeBlob* code, outputStream* output, ! CodeStrings c = CodeStrings(), ptrdiff_t offset = 0); ! ! address decode_instructions(address start, address end); ! ! void start_insn(address pc) { ! _cur_insn = pc; ! output()->bol(); ! print_insn_labels(); ! } ! ! void end_insn(address pc) { ! address pc0 = cur_insn(); ! outputStream* st = output(); ! if (_print_bytes && pc > pc0) ! print_insn_bytes(pc0, pc); ! if (_nm != NULL) { ! _nm->print_code_comment_on(st, COMMENT_COLUMN, pc0, pc); ! // this calls reloc_string_for which calls oop::print_value_on ! } ! print_hook_comments(pc0, _nm != NULL); ! // follow each complete insn by a nice newline ! st->cr(); ! } address handle_event(const char* event, address arg); outputStream* output() { return _output; } address cur_insn() { return _cur_insn; } const char* options() { return _option_buf; } static void hook(const char* file, int line, address pc); void print_hook_comments(address pc, bool newline); }; decode_env::SourceFileInfoTable decode_env::_src_table; const char* decode_env::_cached_src = NULL; GrowableArray<const char*>* decode_env::_cached_src_lines = NULL; void decode_env::hook(const char* file, int line, address pc) { --- 202,233 ---- static SourceFileInfoTable _src_table; static const char* _cached_src; static GrowableArray<const char*>* _cached_src_lines; public: ! decode_env(CodeBuffer* code, outputStream* output); ! decode_env(CodeBlob* code, outputStream* output, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */); ! decode_env(nmethod* code, outputStream* output, CodeStrings c = CodeStrings()); ! // Constructor for a 'decode_env' to decode an arbitrary ! // piece of memory, hopefully containing code. ! decode_env(address start, address end, outputStream* output); ! ! // Add 'original_start' argument which is the the original address ! // the instructions were located at (if this is not equal to 'start'). ! address decode_instructions(address start, address end, address original_start = NULL); address handle_event(const char* event, address arg); outputStream* output() { return _output; } address cur_insn() { return _cur_insn; } const char* options() { return _option_buf; } static void hook(const char* file, int line, address pc); void print_hook_comments(address pc, bool newline); }; + bool decode_env::_optionsParsed = false; + decode_env::SourceFileInfoTable decode_env::_src_table; const char* decode_env::_cached_src = NULL; GrowableArray<const char*>* decode_env::_cached_src_lines = NULL; void decode_env::hook(const char* file, int line, address pc) {
*** 358,435 **** } } } } ! decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c, ! ptrdiff_t offset) { memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. _output = output ? output : tty; ! _code = code; ! if (code != NULL && code->is_nmethod()) _nm = (nmethod*) code; _strings.copy(c); - _offset = offset; // by default, output pc but not bytes: ! _print_pc = true; ! _print_bytes = false; _bytes_per_line = Disassembler::pd_instruction_alignment(); ! _print_file_name= true; // parse the global option string: collect_options(Disassembler::pd_cpu_opts()); collect_options(PrintAssemblyOptions); if (strstr(options(), "hsdis-")) { ! if (strstr(options(), "hsdis-print-raw")) _print_raw = (strstr(options(), "xml") ? 2 : 1); - if (strstr(options(), "hsdis-print-pc")) - _print_pc = !_print_pc; - if (strstr(options(), "hsdis-print-bytes")) - _print_bytes = !_print_bytes; - } - if (strstr(options(), "help")) { - tty->print_cr("PrintAssemblyOptions help:"); - tty->print_cr(" hsdis-print-raw test plugin by requesting raw output"); - tty->print_cr(" hsdis-print-raw-xml test plugin by requesting raw xml"); - tty->print_cr(" hsdis-print-pc turn off PC printing (on by default)"); - tty->print_cr(" hsdis-print-bytes turn on instruction byte output"); - tty->print_cr("combined options: %s", options()); } ! } address decode_env::handle_event(const char* event, address arg) { ! if (match(event, "insn")) { ! start_insn(arg); ! } else if (match(event, "/insn")) { end_insn(arg); ! } else if (match(event, "addr")) { ! if (arg != NULL) { print_address(arg); return arg; } ! } else if (match(event, "mach")) { ! static char buffer[32] = { 0, }; ! if (strcmp(buffer, (const char*)arg) != 0 || ! strlen((const char*)arg) > sizeof(buffer) - 1) { // Only print this when the mach changes strncpy(buffer, (const char*)arg, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; ! output()->print_cr("[Disassembling for mach='%s']", arg); } ! } else if (match(event, "format bytes-per-line")) { _bytes_per_line = (int) (intptr_t) arg; ! } else { ! // ignore unrecognized markup } return NULL; } // called by the disassembler to print out jump targets and data addresses void decode_env::print_address(address adr) { ! outputStream* st = _output; if (adr == NULL) { st->print("NULL"); return; } --- 310,564 ---- } } } } ! decode_env::decode_env(CodeBuffer* code, outputStream* output) { ! memset(this, 0, sizeof(*this)); ! _output = output ? output : tty; ! _codeBlob = NULL; ! _codeBuffer = code; ! _helpPrinted = false; ! ! process_options(_output); ! } ! ! decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c) { memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. _output = output ? output : tty; ! _codeBlob = code; ! _codeBuffer = NULL; ! _helpPrinted = false; ! if (_codeBlob != NULL && _codeBlob->is_nmethod()) { _nm = (nmethod*) code; + } _strings.copy(c); + process_options(_output); + } + + decode_env::decode_env(nmethod* code, outputStream* output, CodeStrings c) { + memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. + _output = output ? output : tty; + _codeBlob = NULL; + _codeBuffer = NULL; + _nm = code; + _start = _nm->code_begin(); + _end = _nm->code_end(); + _helpPrinted = false; + _strings.copy(c); + + process_options(_output); + } + + // Constructor for a 'decode_env' to decode a memory range [start, end) + // of unknown origin, assuming it contains code. + decode_env::decode_env(address start, address end, outputStream* output) { + assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end)); + memset(this, 0, sizeof(*this)); + _output = output ? output : tty; + _codeBlob = NULL; + _codeBuffer = NULL; + _start = start; + _end = end; + _helpPrinted = false; + + process_options(_output); + } + + void decode_env::process_options(outputStream* ost) { // by default, output pc but not bytes: ! _print_help = false; _bytes_per_line = Disassembler::pd_instruction_alignment(); ! _print_file_name = true; ! ! if (_optionsParsed) return; // parse only once // parse the global option string: collect_options(Disassembler::pd_cpu_opts()); collect_options(PrintAssemblyOptions); + // ost->print_cr("PrintAssemblyOptions='%s'", options()); + if (strstr(options(), "hsdis-")) { ! if (strstr(options(), "hsdis-print-raw")) { _print_raw = (strstr(options(), "xml") ? 2 : 1); } ! if (strstr(options(), "hsdis-print-pc")) { // deprecated ! AbstractDisassembler::toggle_show_pc(); ! } ! if (strstr(options(), "hsdis-print-bytes")) { // deprecated ! AbstractDisassembler::toggle_show_bytes(); ! } + if (strstr(options(), "hsdis-help")) { + _print_help = true; + } + if (strstr(options(), "hsdis-align-instr")) { + AbstractDisassembler::toggle_align_instr(); + } + if (strstr(options(), "hsdis-show-pc")) { + AbstractDisassembler::toggle_show_pc(); + } + if (strstr(options(), "hsdis-show-offset")) { + AbstractDisassembler::toggle_show_offset(); + } + if (strstr(options(), "hsdis-show-bytes")) { + AbstractDisassembler::toggle_show_bytes(); + } + if (strstr(options(), "hsdis-show-data-hex")) { + AbstractDisassembler::toggle_show_data_hex(); + } + if (strstr(options(), "hsdis-show-data-int")) { + AbstractDisassembler::toggle_show_data_int(); + } + if (strstr(options(), "hsdis-show-data-float")) { + AbstractDisassembler::toggle_show_data_float(); + } + if (strstr(options(), "hsdis-show-structs")) { + AbstractDisassembler::toggle_show_structs(); + } + if (strstr(options(), "hsdis-show-comment")) { + AbstractDisassembler::toggle_show_comment(); + } + if (strstr(options(), "hsdis-show-block-comment")) { + AbstractDisassembler::toggle_show_block_comment(); + } + _optionsParsed = true; + } + + if (_print_help && ! _helpPrinted) { + _helpPrinted = true; + ost->print_cr("PrintAssemblyOptions help:"); + ost->print_cr(" hsdis-print-raw test plugin by requesting raw output"); + ost->print_cr(" hsdis-print-raw-xml test plugin by requesting raw xml"); + ost->print_cr(" hsdis-print-pc turn off PC printing (on by default)"); + ost->print_cr(" hsdis-print-bytes turn on instruction byte output"); + ost->cr(); + ost->print_cr(" hsdis-show-pc toggle printing current pc, currently %s", AbstractDisassembler::show_pc() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-offset toggle printing current offset, currently %s", AbstractDisassembler::show_offset() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-bytes toggle printing instruction bytes, currently %s", AbstractDisassembler::show_bytes() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-data-hex toggle formatting data as hex, currently %s", AbstractDisassembler::show_data_hex() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-data-int toggle formatting data as int, currently %s", AbstractDisassembler::show_data_int() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-data-float toggle formatting data as float, currently %s", AbstractDisassembler::show_data_float() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-structs toggle compiler data structures, currently %s", AbstractDisassembler::show_structs() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-comment toggle instruction comments, currently %s", AbstractDisassembler::show_comment() ? "ON" : "OFF"); + ost->print_cr(" hsdis-show-block-comment toggle block comments, currently %s", AbstractDisassembler::show_block_comment() ? "ON" : "OFF"); + ost->print_cr(" hsdis-align-instr toggle instruction alignment, currently %s", AbstractDisassembler::align_instr() ? "ON" : "OFF"); + ost->print_cr("combined options: %s", options()); + } + } + + // Disassembly Event Handler. + // This method receives events from the disassembler library hsdis + // via event_to_env for each decoding step (installed by + // Disassembler::decode_instructions(), replacing the default + // callback method). This enables dumping additional info + // and custom line formatting. + // In a future extension, calling a custom decode method will be + // supported. We can use such a method to decode instructions the + // binutils decoder does not handle to our liking (suboptimal + // formatting, incomplete information, ...). + // Returns: + // - NULL for all standard invocations. The function result is not + // examined (as of now, 20190409) by the hsdis decoder loop. + // - next for 'insn0' invocations. + // next == arg: the custom decoder didn't do anything. + // next > arg: the custom decoder did decode the instruction. + // next points to the next undecoded instruction + // (continuation point for decoder loop). + // + // "Normal" sequence of events: + // insns - start of instruction stream decoding + // mach - display architecture + // format - display bytes-per-line + // for each instruction: + // insn - start of instruction decoding + // insn0 - custom decoder invocation (if any) + // addr - print address value + // /insn - end of instruction decoding + // /insns - premature end of instruction stream due to no progress + // address decode_env::handle_event(const char* event, address arg) { ! ! #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ! ! //---< Event: end decoding loop (error, no progress) >--- ! if (decode_env::match(event, "/insns")) { ! // Nothing to be done here. ! return NULL; ! } ! ! //---< Event: start decoding loop >--- ! if (decode_env::match(event, "insns")) { ! // Nothing to be done here. ! return NULL; ! } ! ! //---< Event: finish decoding an instruction >--- ! if (decode_env::match(event, "/insn")) { ! output()->fill_to(_post_decode_alignment); end_insn(arg); ! return NULL; ! } ! ! //---< Event: start decoding an instruction >--- ! if (decode_env::match(event, "insn")) { ! start_insn(arg); ! calculate_alignment(); ! output()->fill_to(_pre_decode_alignment); ! return NULL; ! } ! ! //---< Event: call custom decoder (platform specific) >--- ! if (decode_env::match(event, "insn0")) { ! return Disassembler::decode_instruction0(arg, output(), arg); ! } ! ! //---< Event: Print address >--- ! if (decode_env::match(event, "addr")) { print_address(arg); return arg; } ! ! //---< Event: mach (inform about machine architecture) >--- ! // This event is problematic because it messes up the output. ! // The event is fired after the instruction address has already ! // been printed. The decoded instruction (event "insn") is ! // printed afterwards. That doesn't look nice. ! if (decode_env::match(event, "mach")) { ! guarantee(arg != NULL, "event_to_env - arg must not be NULL for event 'mach'"); ! static char buffer[64] = { 0, }; ! // Output suppressed because it messes up disassembly. ! // Only print this when the mach changes. ! if (false && (strcmp(buffer, (const char*)arg) != 0 || ! strlen((const char*)arg) > sizeof(buffer) - 1)) { // Only print this when the mach changes strncpy(buffer, (const char*)arg, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; ! output()->print_cr("[Disassembling for mach='%s']", (const char*)arg); } ! return NULL; ! } ! ! //---< Event: format bytes-per-line >--- ! if (decode_env::match(event, "format bytes-per-line")) { _bytes_per_line = (int) (intptr_t) arg; ! return NULL; } + #endif return NULL; } + static void* event_to_env(void* env_pv, const char* event, void* arg) { + decode_env* env = (decode_env*) env_pv; + return env->handle_event(event, (address) arg); + } + // called by the disassembler to print out jump targets and data addresses void decode_env::print_address(address adr) { ! outputStream* st = output(); if (adr == NULL) { st->print("NULL"); return; }
*** 470,482 **** } if (_nm == NULL) { // Don't do this for native methods, as the function name will be printed in // nmethod::reloc_string_for(). ! ResourceMark rm; const int buflen = 1024; ! char* buf = NEW_RESOURCE_ARRAY(char, buflen); int offset; if (os::dll_address_to_function_name(adr, buf, buflen, &offset)) { st->print(PTR_FORMAT " = %s", p2i(adr), buf); if (offset != 0) { st->print("+%d", offset); --- 599,613 ---- } if (_nm == NULL) { // Don't do this for native methods, as the function name will be printed in // nmethod::reloc_string_for(). ! // Allocate the buffer on the stack instead of as RESOURCE array. ! // In case we do DecodeErrorFile, Thread will not be initialized, ! // causing a "assert(current != __null) failed" failure. const int buflen = 1024; ! char buf[buflen]; int offset; if (os::dll_address_to_function_name(adr, buf, buflen, &offset)) { st->print(PTR_FORMAT " = %s", p2i(adr), buf); if (offset != 0) { st->print("+%d", offset);
*** 488,545 **** // Fall through to a simple (hexadecimal) numeral. st->print(PTR_FORMAT, p2i(adr)); } void decode_env::print_insn_labels() { address p = cur_insn(); outputStream* st = output(); - CodeBlob* cb = _code; - if (cb != NULL) { - cb->print_block_comment(st, p); - } - _strings.print_block_comment(st, (intptr_t)(p - _start + _offset)); - if (_print_pc) { - st->print(" " PTR_FORMAT ": ", p2i(p)); - } - } ! void decode_env::print_insn_bytes(address pc, address pc_limit) { ! outputStream* st = output(); ! size_t incr = 1; ! size_t perline = _bytes_per_line; ! if ((size_t) Disassembler::pd_instruction_alignment() >= sizeof(int) ! && !((uintptr_t)pc % sizeof(int)) ! && !((uintptr_t)pc_limit % sizeof(int))) { ! incr = sizeof(int); ! if (perline % incr) perline += incr - (perline % incr); ! } ! while (pc < pc_limit) { ! // tab to the desired column: ! st->move_to(COMMENT_COLUMN); ! address pc0 = pc; ! address pc1 = pc + perline; ! if (pc1 > pc_limit) pc1 = pc_limit; ! for (; pc < pc1; pc += incr) { ! if (pc == pc0) { ! st->print(BYTES_COMMENT); ! } else if ((uint)(pc - pc0) % sizeof(int) == 0) { ! st->print(" "); // put out a space on word boundaries } ! if (incr == sizeof(int)) { ! st->print("%08x", *(int*)pc); ! } else { ! st->print("%02x", (*pc)&0xFF); } } ! st->cr(); } } ! ! static void* event_to_env(void* env_pv, const char* event, void* arg) { ! decode_env* env = (decode_env*) env_pv; ! return env->handle_event(event, (address) arg); } ATTRIBUTE_PRINTF(2, 3) static int printf_to_env(void* env_pv, const char* format, ...) { decode_env* env = (decode_env*) env_pv; --- 619,653 ---- // Fall through to a simple (hexadecimal) numeral. st->print(PTR_FORMAT, p2i(adr)); } void decode_env::print_insn_labels() { + if (AbstractDisassembler::show_block_comment()) { address p = cur_insn(); outputStream* st = output(); ! //---< Block comments for nmethod >--- ! // Outputs a bol() before and a cr() after, but only if a comment is printed. ! // Prints nmethod_section_label as well. ! if (_nm != NULL) { ! _nm->print_block_comment(st, p); } ! if (_codeBlob != NULL) { ! _codeBlob->print_block_comment(st, p); } + if (_codeBuffer != NULL) { + _codeBuffer->print_block_comment(st, p); } ! _strings.print_block_comment(st, (intptr_t)(p - _start)); } } ! void decode_env::print_insn_prefix() { ! address p = cur_insn(); ! outputStream* st = output(); ! AbstractDisassembler::print_location(p, _start, _end, st, false, false); ! AbstractDisassembler::print_instruction(p, Assembler::instr_len(p), Assembler::instr_maxlen(), st, true, false); } ATTRIBUTE_PRINTF(2, 3) static int printf_to_env(void* env_pv, const char* format, ...) { decode_env* env = (decode_env*) env_pv;
*** 568,587 **** julong cnt1 = st->count(); va_end(ap); return (int)(cnt1 - cnt0); } ! address decode_env::decode_instructions(address start, address end) { ! _start = start; _end = end; ! ! assert(((((intptr_t)start | (intptr_t)end) % Disassembler::pd_instruction_alignment()) == 0), "misaligned insn addr"); ! ! const int show_bytes = false; // for disassembler debugging ! ! //_version = Disassembler::pd_cpu_version(); ! ! if (!Disassembler::can_decode()) { return NULL; } // decode a series of instructions and return the end of the last instruction --- 676,710 ---- julong cnt1 = st->count(); va_end(ap); return (int)(cnt1 - cnt0); } ! // The 'original_start' argument holds the the original address where ! // the instructions were located in the originating system. If zero (NULL) ! // is passed in, there is no original address. ! address decode_env::decode_instructions(address start, address end, address original_start /* = 0*/) { ! // CodeComment in Stubs. ! // Properly initialize _start/_end. Overwritten too often if ! // printing of instructions is called for each instruction. ! assert((_start == NULL) || (start == NULL) || (_start == start), "don't overwrite CTOR values"); ! assert((_end == NULL) || (end == NULL) || (_end == end ), "don't overwrite CTOR values"); ! if (start != NULL) set_start(start); ! if (end != NULL) set_end(end); ! if (original_start == NULL) { ! original_start = start; ! } ! ! //---< Check (and correct) alignment >--- ! // Don't check alignment of end, it is not aligned. ! if (((uint64_t)start & ((uint64_t)Disassembler::pd_instruction_alignment() - 1)) != 0) { ! output()->print_cr("Decode range start:" PTR_FORMAT ": ... (unaligned)", p2i(start)); ! start = (address)((uint64_t)start & ~((uint64_t)Disassembler::pd_instruction_alignment() - 1)); ! } ! ! // Trying to decode instructions doesn't make sense if we ! // couldn't load the disassembler library. ! if (Disassembler::is_abstract()) { return NULL; } // decode a series of instructions and return the end of the last instruction
*** 618,637 **** &event_to_env, (void*) this, &printf_to_env, (void*) this, options()); } ! void Disassembler::decode(CodeBlob* cb, outputStream* st) { ! ttyLocker ttyl; ! if (!load_library()) return; if (cb->is_nmethod()) { ! decode((nmethod*)cb, st); return; } decode_env env(cb, st); ! env.output()->print_cr("----------------------------------------------------------------------"); if (cb->is_aot()) { env.output()->print("A "); if (cb->is_compiled()) { CompiledMethod* cm = (CompiledMethod*)cb; env.output()->print("%d ",cm->compile_id()); --- 741,921 ---- &event_to_env, (void*) this, &printf_to_env, (void*) this, options()); } + // ---------------------------------------------------------------------------- + // Disassembler + // Used as a static wrapper for decode_env. + // Each method will create a decode_env before decoding. + // You can call the decode_env methods directly if you already have one. ! ! bool Disassembler::load_library(outputStream* st) { ! // Do not try to load multiple times. Failed once -> fails always. ! // To force retry in debugger: assign _tried_to_load_library=0 ! if (_tried_to_load_library) { ! return _library_usable; ! } ! ! #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ! // Print to given stream, if any. ! // Print to tty if Verbose is on and no stream given. ! st = ((st == NULL) && Verbose) ? tty : st; ! ! // Compute fully qualified library name. ! char ebuf[1024]; ! char buf[JVM_MAXPATHLEN]; ! os::jvm_path(buf, sizeof(buf)); ! int jvm_offset = -1; ! int lib_offset = -1; ! #ifdef STATIC_BUILD ! char* p = strrchr(buf, '/'); ! *p = '\0'; ! strcat(p, "/lib/"); ! lib_offset = jvm_offset = strlen(buf); ! #else ! { ! // Match "libjvm" instead of "jvm" on *nix platforms. Creates better matches. ! // Match "[lib]jvm[^/]*" in jvm_path. ! const char* base = buf; ! const char* p = strrchr(buf, *os::file_separator()); ! #ifdef _WIN32 ! p = strstr(p ? p : base, "jvm"); ! #else ! p = strstr(p ? p : base, "libjvm"); ! #endif ! if (p != NULL) lib_offset = p - base + 1; ! if (p != NULL) jvm_offset = p - base; ! } ! #endif ! ! // Find the disassembler shared library. ! // Search for several paths derived from libjvm, in this order: ! // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so (for compatibility) ! // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so ! // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so ! // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH) ! if (jvm_offset >= 0) { ! // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so ! strcpy(&buf[jvm_offset], hsdis_library_name); ! strcat(&buf[jvm_offset], os::dll_file_extension()); ! _library = os::dll_load(buf, ebuf, sizeof ebuf); ! if (_library == NULL && lib_offset >= 0) { ! // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so ! strcpy(&buf[lib_offset], hsdis_library_name); ! strcat(&buf[lib_offset], os::dll_file_extension()); ! _library = os::dll_load(buf, ebuf, sizeof ebuf); ! } ! if (_library == NULL && lib_offset > 0) { ! // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so ! buf[lib_offset - 1] = '\0'; ! const char* p = strrchr(buf, *os::file_separator()); ! if (p != NULL) { ! lib_offset = p - buf + 1; ! strcpy(&buf[lib_offset], hsdis_library_name); ! strcat(&buf[lib_offset], os::dll_file_extension()); ! _library = os::dll_load(buf, ebuf, sizeof ebuf); ! } ! } ! } ! if (_library == NULL) { ! // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH) ! strcpy(&buf[0], hsdis_library_name); ! strcat(&buf[0], os::dll_file_extension()); ! _library = os::dll_load(buf, ebuf, sizeof ebuf); ! } ! ! // load the decoder function to use (new or old version). ! if (_library != NULL) { ! _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual, ! os::dll_lookup(_library, decode_instructions_virtual_name)); ! } ! if (_decode_instructions_virtual == NULL && _library != NULL) { ! // could not spot in new version, try old version ! _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, ! os::dll_lookup(_library, decode_instructions_name)); ! use_new_version = false; ! } else { ! use_new_version = true; ! } ! _tried_to_load_library = true; ! _library_usable = _decode_instructions_virtual != NULL || _decode_instructions != NULL; ! ! // Create a dummy environment to initialize PrintAssemblyOptions. ! // The PrintAssemblyOptions must be known for abstract disassemblies as well. ! decode_env dummy((unsigned char*)(&buf[0]), (unsigned char*)(&buf[1]), st); ! ! // Report problems during dll_load or dll_lookup, if any. ! if (st != NULL) { ! // Success. ! if (_library_usable) { ! st->print_cr("Loaded disassembler from %s", buf); ! } else { ! st->print_cr("Could not load %s; %s; %s", ! buf, ! ((_library != NULL) ! ? "entry point is missing" ! : ((WizardMode || PrintMiscellaneous) ! ? (const char*)ebuf ! : "library not loadable")), ! "PrintAssembly defaults to abstract disassembly."); ! } ! } ! #endif ! return _library_usable; ! } ! ! ! // Directly disassemble code buffer. ! void Disassembler::decode(CodeBuffer* cb, address start, address end, outputStream* st) { ! #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ! //---< Test memory before decoding >--- ! if (!(cb->contains(start) && cb->contains(end))) { ! //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- ! if (st != NULL) { ! st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not contained in CodeBuffer", p2i(start), p2i(end)); ! } ! return; ! } ! if (!os::is_readable_range(start, end)) { ! //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- ! if (st != NULL) { ! st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end)); ! } ! return; ! } ! ! decode_env env(cb, st); ! env.output()->print_cr("--------------------------------------------------------------------------------"); ! env.output()->print("Decoding CodeBuffer (" PTR_FORMAT ")", p2i(cb)); ! if (cb->name() != NULL) { ! env.output()->print(", name: %s,", cb->name()); ! } ! env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(start), p2i(end), ((jlong)(end - start))); ! ! if (is_abstract()) { ! AbstractDisassembler::decode_abstract(start, end, env.output(), Assembler::instr_maxlen()); ! } else { ! env.decode_instructions(start, end); ! } ! env.output()->print_cr("--------------------------------------------------------------------------------"); ! #endif ! } ! ! // Directly disassemble code blob. ! void Disassembler::decode(CodeBlob* cb, outputStream* st, CodeStrings c) { ! #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) if (cb->is_nmethod()) { ! // If we have an nmethod at hand, ! // call the specialized decoder directly. ! decode((nmethod*)cb, st, c); return; } + decode_env env(cb, st); ! env.output()->print_cr("--------------------------------------------------------------------------------"); if (cb->is_aot()) { env.output()->print("A "); if (cb->is_compiled()) { CompiledMethod* cm = (CompiledMethod*)cb; env.output()->print("%d ",cm->compile_id());
*** 641,701 **** cm->method()->signature()->print_symbol_on(env.output()); } else { env.output()->print_cr("%s", cb->name()); } } else { ! env.output()->print_cr("%s", cb->name()); } ! env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(cb->code_begin()), p2i(cb->code_end()), ((jlong)(cb->code_end() - cb->code_begin())) * sizeof(unsigned char*)); env.decode_instructions(cb->code_begin(), cb->code_end()); } ! void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c, ! ptrdiff_t offset) { ttyLocker ttyl; - if (!load_library()) return; - decode_env env(CodeCache::find_blob_unsafe(start), st, c, offset); - env.decode_instructions(start, end); - } - void Disassembler::decode(nmethod* nm, outputStream* st) { - ttyLocker ttyl; - if (!load_library()) return; decode_env env(nm, st); ! env.output()->print_cr("----------------------------------------------------------------------"); ! ! unsigned char* p = nm->code_begin(); ! unsigned char* end = nm->code_end(); ! ! nm->method()->method_holder()->name()->print_symbol_on(env.output()); ! env.output()->print("."); ! nm->method()->name()->print_symbol_on(env.output()); ! nm->method()->signature()->print_symbol_on(env.output()); ! #if INCLUDE_JVMCI ! { ! const char* jvmciName = nm->jvmci_name(); ! if (jvmciName != NULL) { ! env.output()->print(" (%s)", jvmciName); ! } } #endif ! env.output()->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(p), p2i(end), ((jlong)(end - p))); ! // Print constant table. ! if (nm->consts_size() > 0) { ! nm->print_nmethod_labels(env.output(), nm->consts_begin()); ! int offset = 0; ! for (address p = nm->consts_begin(); p < nm->consts_end(); p += 4, offset += 4) { ! if ((offset % 8) == 0) { ! env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, p2i(p), offset, *((int32_t*) p), *((int64_t*) p)); ! } else { ! env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT, p2i(p), offset, *((int32_t*) p)); } } } ! env.decode_instructions(p, end); } // To prevent excessive code expansion in the interpreter generator, we // do not inline this function into Disassembler::hook(). void Disassembler::_hook(const char* file, int line, MacroAssembler* masm) { --- 925,1006 ---- cm->method()->signature()->print_symbol_on(env.output()); } else { env.output()->print_cr("%s", cb->name()); } } else { ! env.output()->print("Decoding CodeBlob"); ! if (cb->name() != NULL) { ! env.output()->print(", name: %s,", cb->name()); } ! } ! env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(cb->code_begin()), p2i(cb->code_end()), ((jlong)(cb->code_end() - cb->code_begin()))); ! ! if (is_abstract()) { ! AbstractDisassembler::decode_abstract(cb->code_begin(), cb->code_end(), env.output(), Assembler::instr_maxlen()); ! } else { env.decode_instructions(cb->code_begin(), cb->code_end()); + } + env.output()->print_cr("--------------------------------------------------------------------------------"); + #endif } ! // Decode a nmethod. ! // This includes printing the constant pool and all code segments. ! // The nmethod data structures (oop maps, relocations and the like) are not printed. ! void Disassembler::decode(nmethod* nm, outputStream* st, CodeStrings c) { ! #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ttyLocker ttyl; decode_env env(nm, st); ! env.output()->print_cr("--------------------------------------------------------------------------------"); ! nm->print_constant_pool(env.output()); ! env.output()->print_cr("--------------------------------------------------------------------------------"); ! env.output()->cr(); ! if (is_abstract()) { ! AbstractDisassembler::decode_abstract(nm->code_begin(), nm->code_end(), env.output(), Assembler::instr_maxlen()); ! } else { ! env.decode_instructions(nm->code_begin(), nm->code_end()); } + env.output()->print_cr("--------------------------------------------------------------------------------"); #endif ! } ! // Decode a range, given as [start address, end address) ! void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c /*, ptrdiff_t offset */) { ! #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ! //---< Test memory before decoding >--- ! if (!os::is_readable_range(start, end)) { ! //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- ! if (st != NULL) { ! st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end)); } + return; } + + if (is_abstract()) { + AbstractDisassembler::decode_abstract(start, end, st, Assembler::instr_maxlen()); + return; } ! // Don't do that fancy stuff. If we just have two addresses, live with it ! // and treat the memory contents as "amorphic" piece of code. ! #if 0 ! CodeBlob* cb = CodeCache::find_blob_unsafe(start); ! if (cb != NULL) { ! // If we have an CodeBlob at hand, ! // call the specialized decoder directly. ! decode(cb, st, c); ! } else ! #endif ! { ! // This seems to be just a chunk of memory. ! decode_env env(start, end, st); ! env.output()->print_cr("--------------------------------------------------------------------------------"); ! env.decode_instructions(start, end); ! env.output()->print_cr("--------------------------------------------------------------------------------"); ! } ! #endif } // To prevent excessive code expansion in the interpreter generator, we // do not inline this function into Disassembler::hook(). void Disassembler::_hook(const char* file, int line, MacroAssembler* masm) {
< prev index next >