< 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,11 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
-#include "asm/macroAssembler.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,10 +41,11 @@
 #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,131 +54,50 @@
 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;
+  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;
-  outputStream* _output;
-  address       _start, _end;
-  ptrdiff_t     _offset;
+  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;
-  bool          _print_pc;
-  bool          _print_bytes;
-  address       _cur_insn;
+  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 (strncmp(event, tag, taglen) != 0)
+    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,18 +105,60 @@
     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 process_options(outputStream* ost);
+
   void print_insn_labels();
-  void print_insn_bytes(address pc0, address pc);
+  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,44 +201,32 @@
   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();
-  }
+  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,82 +309,250 @@
       }
     }
   }
 }
 
-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) {
+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;
-  _code = code;
-  if (code != NULL && code->is_nmethod())
+  _codeBlob    = code;
+  _codeBuffer  = NULL;
+  _helpPrinted = false;
+  if (_codeBlob != NULL && _codeBlob->is_nmethod()) {
     _nm = (nmethod*) code;
+  }
   _strings.copy(c);
-  _offset = offset;
 
+  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_pc       = true;
-  _print_bytes    = false;
+  _print_help      = false;
   _bytes_per_line = Disassembler::pd_instruction_alignment();
-  _print_file_name= true;
+  _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(), "hsdis-")) {
-    if (strstr(options(), "hsdis-print-raw"))
+  if (strstr(options(), "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());
+    _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 (match(event, "insn")) {
+
+#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;
     }
-  } else if (match(event, "mach")) {
-    static char buffer[32] = { 0, };
-    if (strcmp(buffer, (const char*)arg) != 0 ||
-        strlen((const char*)arg) > sizeof(buffer) - 1) {
+    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']", arg);
+      output()->print_cr("[Disassembling for mach='%s']", (const char*)arg);
+    }
+    return NULL;
     }
-  } else if (match(event, "format bytes-per-line")) {
+
+  //---<  Event: format bytes-per-line  >---
+  if (decode_env::match(event, "format bytes-per-line")) {
     _bytes_per_line = (int) (intptr_t) arg;
-  } else {
-    // ignore unrecognized markup
+    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;
+  outputStream* st = output();
 
   if (adr == NULL) {
     st->print("NULL");
     return;
   }

@@ -475,13 +593,15 @@
   }
 
   if (_nm == NULL) {
     // Don't do this for native methods, as the function name will be printed in
     // nmethod::reloc_string_for().
-    ResourceMark rm;
+    // 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 = NEW_RESOURCE_ARRAY(char, buflen);
+    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,58 +613,35 @@
   // 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();
-  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
+    //---<  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 (incr == sizeof(int)) {
-        st->print("%08x", *(int*)pc);
-      } else {
-        st->print("%02x", (*pc)&0xFF);
+    if (_codeBlob != NULL) {
+      _codeBlob->print_block_comment(st, p);
       }
+    if (_codeBuffer != NULL) {
+      _codeBuffer->print_block_comment(st, p);
     }
-    st->cr();
+    _strings.print_block_comment(st, (intptr_t)(p - _start));
   }
 }
 
-
-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);
+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,20 +670,35 @@
   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()) {
+// 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,20 +735,181 @@
                                           &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.
 
-void Disassembler::decode(CodeBlob* cb, outputStream* st) {
-  ttyLocker ttyl;
-  if (!load_library())  return;
+
+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()) {
-    decode((nmethod*)cb, st);
+    // 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("----------------------------------------------------------------------");
+  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,61 +919,82 @@
       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("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())) * sizeof(unsigned char*));
+  }
+  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
 }
 
-void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c,
-                          ptrdiff_t offset) {
+// 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;
-  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);
-    }
+  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
-  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));
+// 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;
   }
 
-  env.decode_instructions(p, end);
+// 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 >