< prev index next >
src/hotspot/share/compiler/disassembler.cpp
Print this page
rev 54960 : 8213084: Rework and enhance Print[Opto]Assembly output
Reviewed-by: kvn, thartmann
*** 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"
*** 41,50 ****
--- 41,51 ----
#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;
*** 53,183 ****
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];
--- 54,103 ----
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];
*** 185,202 ****
strcat(fillp, p);
// replace white space by commas:
char* q = fillp;
while ((q = strpbrk(q, " \t\n")) != NULL)
*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;
--- 105,164 ----
strcat(fillp, p);
// replace white space by commas:
char* q = fillp;
while ((q = strpbrk(q, " \t\n")) != NULL)
*q++ = ',';
}
+ 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;
*** 239,282 ****
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) {
--- 201,232 ----
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) {
*** 359,440 ****
}
}
}
}
! decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c,
! ptrdiff_t offset) : _nm(NULL),
! _start(NULL),
! _end(NULL),
! _option_buf(),
! _print_raw('\0'),
! _cur_insn(NULL) {
_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;
}
--- 309,558 ----
}
}
}
}
! 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);
! if (strstr(options(), "print-raw")) {
_print_raw = (strstr(options(), "xml") ? 2 : 1);
}
+
if (strstr(options(), "help")) {
! _print_help = true;
}
! if (strstr(options(), "align-instr")) {
! AbstractDisassembler::toggle_align_instr();
! }
! if (strstr(options(), "show-pc")) {
! AbstractDisassembler::toggle_show_pc();
! }
! if (strstr(options(), "show-offset")) {
! AbstractDisassembler::toggle_show_offset();
! }
! if (strstr(options(), "show-bytes")) {
! AbstractDisassembler::toggle_show_bytes();
! }
! if (strstr(options(), "show-data-hex")) {
! AbstractDisassembler::toggle_show_data_hex();
! }
! if (strstr(options(), "show-data-int")) {
! AbstractDisassembler::toggle_show_data_int();
! }
! if (strstr(options(), "show-data-float")) {
! AbstractDisassembler::toggle_show_data_float();
! }
! if (strstr(options(), "show-structs")) {
! AbstractDisassembler::toggle_show_structs();
! }
! if (strstr(options(), "show-comment")) {
! AbstractDisassembler::toggle_show_comment();
! }
! if (strstr(options(), "show-block-comment")) {
! AbstractDisassembler::toggle_show_block_comment();
! }
! _optionsParsed = true;
!
! if (_print_help && ! _helpPrinted) {
! _helpPrinted = true;
! ost->print_cr("PrintAssemblyOptions help:");
! ost->print_cr(" print-raw test plugin by requesting raw output");
! ost->print_cr(" print-raw-xml test plugin by requesting raw xml");
! ost->cr();
! ost->print_cr(" show-pc toggle printing current pc, currently %s", AbstractDisassembler::show_pc() ? "ON" : "OFF");
! ost->print_cr(" show-offset toggle printing current offset, currently %s", AbstractDisassembler::show_offset() ? "ON" : "OFF");
! ost->print_cr(" show-bytes toggle printing instruction bytes, currently %s", AbstractDisassembler::show_bytes() ? "ON" : "OFF");
! ost->print_cr(" show-data-hex toggle formatting data as hex, currently %s", AbstractDisassembler::show_data_hex() ? "ON" : "OFF");
! ost->print_cr(" show-data-int toggle formatting data as int, currently %s", AbstractDisassembler::show_data_int() ? "ON" : "OFF");
! ost->print_cr(" show-data-float toggle formatting data as float, currently %s", AbstractDisassembler::show_data_float() ? "ON" : "OFF");
! ost->print_cr(" show-structs toggle compiler data structures, currently %s", AbstractDisassembler::show_structs() ? "ON" : "OFF");
! ost->print_cr(" show-comment toggle instruction comments, currently %s", AbstractDisassembler::show_comment() ? "ON" : "OFF");
! ost->print_cr(" show-block-comment toggle block comments, currently %s", AbstractDisassembler::show_block_comment() ? "ON" : "OFF");
! ost->print_cr(" 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);
} else if (match(event, "/insn")) {
end_insn(arg);
} else if (match(event, "addr")) {
if (arg != NULL) {
print_address(arg);
return 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;
}
*** 475,487 ****
}
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);
--- 593,607 ----
}
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);
*** 493,550 ****
// 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;
--- 613,647 ----
// 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;
*** 573,592 ****
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
--- 670,704 ----
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
*** 623,642 ****
&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());
--- 735,915 ----
&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());
*** 646,706 ****
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) {
--- 919,1000 ----
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 >