changeset 59233:a662625813af

8244207: Simplify usage of Compile::print_method() when debugging with gdb and enable its use with rr Summary: Improve debugging with usage of Compile::print_method() for IGV at breakpoints from gdb and rr. Reviewed-by: kvn, thartmann
author chagedorn
date Mon, 11 May 2020 12:57:39 +0200
parents 5bd7c6e73e2d
children 4aedf0253cda
files src/hotspot/share/opto/compile.cpp src/hotspot/share/opto/compile.hpp src/hotspot/share/opto/idealGraphPrinter.cpp src/hotspot/share/opto/idealGraphPrinter.hpp src/hotspot/share/opto/phasetype.hpp
diffstat 5 files changed, 195 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/opto/compile.cpp	Mon May 11 10:37:54 2020 +0200
+++ b/src/hotspot/share/opto/compile.cpp	Mon May 11 12:57:39 2020 +0200
@@ -4545,7 +4545,6 @@
   }
 }
 
-
 // Move Allocate nodes to the start of the list
 void Compile::sort_macro_nodes() {
   int count = macro_count();
@@ -4562,3 +4561,79 @@
     }
   }
 }
+
+
+#ifndef PRODUCT
+IdealGraphPrinter* Compile::_debug_file_printer = NULL;
+IdealGraphPrinter* Compile::_debug_network_printer = NULL;
+
+// Called from debugger. Prints method to the default file with the default phase name.
+// This works regardless of any Ideal Graph Visualizer flags set or not.
+void igv_print() {
+  Compile::current()->igv_print_method_to_file();
+}
+
+// Same as igv_print() above but with a specified phase name.
+void igv_print(const char* phase_name) {
+  Compile::current()->igv_print_method_to_file(phase_name);
+}
+
+// Called from debugger. Prints method with the default phase name to the default network or the one specified with
+// the network flags for the Ideal Graph Visualizer, or to the default file depending on the 'network' argument.
+// This works regardless of any Ideal Graph Visualizer flags set or not.
+void igv_print(bool network) {
+  if (network) {
+    Compile::current()->igv_print_method_to_network();
+  } else {
+    Compile::current()->igv_print_method_to_file();
+  }
+}
+
+// Same as igv_print(bool network) above but with a specified phase name.
+void igv_print(bool network, const char* phase_name) {
+  if (network) {
+    Compile::current()->igv_print_method_to_network(phase_name);
+  } else {
+    Compile::current()->igv_print_method_to_file(phase_name);
+  }
+}
+
+// Called from debugger. Normal write to the default _printer. Only works if Ideal Graph Visualizer printing flags are set.
+void igv_print_default() {
+  Compile::current()->print_method(PHASE_DEBUG, 0, 0);
+}
+
+// Called from debugger, especially when replaying a trace in which the program state cannot be altered like with rr replay.
+// A method is appended to an existing default file with the default phase name. This means that igv_append() must follow
+// an earlier igv_print(*) call which sets up the file. This works regardless of any Ideal Graph Visualizer flags set or not.
+void igv_append() {
+  Compile::current()->igv_print_method_to_file("Debug", true);
+}
+
+// Same as igv_append() above but with a specified phase name.
+void igv_append(const char* phase_name) {
+  Compile::current()->igv_print_method_to_file(phase_name, true);
+}
+
+void Compile::igv_print_method_to_file(const char* phase_name, bool append) {
+  const char* file_name = "custom_debug.xml";
+  if (_debug_file_printer == NULL) {
+    _debug_file_printer = new IdealGraphPrinter(C, file_name, append);
+  } else {
+    _debug_file_printer->update_compiled_method(C->method());
+  }
+  tty->print_cr("Method %s to %s", append ? "appended" : "printed", file_name);
+  _debug_file_printer->print_method(phase_name, 0);
+}
+
+void Compile::igv_print_method_to_network(const char* phase_name) {
+  if (_debug_network_printer == NULL) {
+    _debug_network_printer = new IdealGraphPrinter(C);
+  } else {
+    _debug_network_printer->update_compiled_method(C->method());
+  }
+  tty->print_cr("Method printed over network stream to IGV");
+  _debug_network_printer->print_method(phase_name, 0);
+}
+#endif
+
--- a/src/hotspot/share/opto/compile.hpp	Mon May 11 10:37:54 2020 +0200
+++ b/src/hotspot/share/opto/compile.hpp	Mon May 11 12:57:39 2020 +0200
@@ -317,6 +317,8 @@
   ConnectionGraph*      _congraph;
 #ifndef PRODUCT
   IdealGraphPrinter*    _printer;
+  static IdealGraphPrinter* _debug_file_printer;
+  static IdealGraphPrinter* _debug_network_printer;
 #endif
 
 
@@ -639,9 +641,9 @@
     if (should_print(level)) {
       char output[1024];
       if (idx != 0) {
-        sprintf(output, "%s:%d", CompilerPhaseTypeHelper::to_string(cpt), idx);
+        jio_snprintf(output, sizeof(output), "%s:%d", CompilerPhaseTypeHelper::to_string(cpt), idx);
       } else {
-        sprintf(output, "%s", CompilerPhaseTypeHelper::to_string(cpt));
+        jio_snprintf(output, sizeof(output), "%s", CompilerPhaseTypeHelper::to_string(cpt));
       }
       _printer->print_method(output, level);
     }
@@ -649,6 +651,13 @@
     C->_latest_stage_start_counter.stamp();
   }
 
+#ifndef PRODUCT
+  void igv_print_method_to_file(const char* phase_name = "Debug", bool append = false);
+  void igv_print_method_to_network(const char* phase_name = "Debug");
+  static IdealGraphPrinter* debug_file_printer() { return _debug_file_printer; }
+  static IdealGraphPrinter* debug_network_printer() { return _debug_network_printer; }
+#endif
+
   void end_method(int level = 1) {
     EventCompilerPhase event;
     if (event.should_commit()) {
--- a/src/hotspot/share/opto/idealGraphPrinter.cpp	Mon May 11 10:37:54 2020 +0200
+++ b/src/hotspot/share/opto/idealGraphPrinter.cpp	Mon May 11 12:57:39 2020 +0200
@@ -92,21 +92,47 @@
 }
 
 void IdealGraphPrinter::clean_up() {
-  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *p = jtiwh.next(); ) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread* p = jtiwh.next(); ) {
     if (p->is_Compiler_thread()) {
-      CompilerThread *c = (CompilerThread *)p;
-      IdealGraphPrinter *printer = c->ideal_graph_printer();
+      CompilerThread* c = (CompilerThread*)p;
+      IdealGraphPrinter* printer = c->ideal_graph_printer();
       if (printer) {
         delete printer;
       }
       c->set_ideal_graph_printer(NULL);
     }
   }
+  IdealGraphPrinter* debug_file_printer = Compile::debug_file_printer();
+  if (debug_file_printer != NULL) {
+    delete debug_file_printer;
+  }
+  IdealGraphPrinter* debug_network_printer = Compile::debug_network_printer();
+  if (debug_network_printer != NULL) {
+    delete debug_network_printer;
+  }
 }
 
-// Constructor, either file or network output
+// Either print methods to file specified with PrintIdealGraphFile or otherwise over the network to the IGV
 IdealGraphPrinter::IdealGraphPrinter() {
+  init(PrintIdealGraphFile, true, false);
+}
 
+// Either print methods to the specified file 'file_name' or if NULL over the network to the IGV. If 'append'
+// is set, the next phase is directly appended to the specified file 'file_name'. This is useful when doing
+// replay compilation with a tool like rr that cannot alter the current program state but only the file.
+IdealGraphPrinter::IdealGraphPrinter(Compile* compile, const char* file_name, bool append) {
+  assert(!append || (append && file_name != NULL), "can only use append flag when printing to file");
+  init(file_name, false, append);
+  C = compile;
+  if (append) {
+    // When directly appending the next graph, we only need to set _current_method and not set up a new method
+    _current_method = C->method();
+  } else {
+    begin_method();
+  }
+}
+
+void IdealGraphPrinter::init(const char* file_name, bool use_multiple_files, bool append) {
   // By default dump both ins and outs since dead or unreachable code
   // needs to appear in the graph.  There are also some special cases
   // in the mach where kill projections have no users but should
@@ -117,60 +143,21 @@
   buffer[0] = 0;
   _depth = 0;
   _current_method = NULL;
-  assert(!_current_method, "current method must be initialized to NULL");
-  _stream = NULL;
+  _network_stream = NULL;
 
-  if (PrintIdealGraphFile != NULL) {
-    ThreadCritical tc;
-    // User wants all output to go to files
-    if (_file_count != 0) {
-      ResourceMark rm;
-      stringStream st;
-      const char* dot = strrchr(PrintIdealGraphFile, '.');
-      if (dot) {
-        st.write(PrintIdealGraphFile, dot - PrintIdealGraphFile);
-        st.print("%d%s", _file_count, dot);
-      } else {
-        st.print("%s%d", PrintIdealGraphFile, _file_count);
-      }
-      fileStream *stream = new (ResourceObj::C_HEAP, mtCompiler) fileStream(st.as_string());
-      _output = stream;
-    } else {
-      fileStream *stream = new (ResourceObj::C_HEAP, mtCompiler) fileStream(PrintIdealGraphFile);
-      _output = stream;
-    }
-    _file_count++;
+  if (file_name != NULL) {
+    init_file_stream(file_name, use_multiple_files, append);
   } else {
-    _stream = new (ResourceObj::C_HEAP, mtCompiler) networkStream();
-
-    // Try to connect to visualizer
-    if (_stream->connect(PrintIdealGraphAddress, PrintIdealGraphPort)) {
-      char c = 0;
-      _stream->read(&c, 1);
-      if (c != 'y') {
-        tty->print_cr("Client available, but does not want to receive data!");
-        _stream->close();
-        delete _stream;
-        _stream = NULL;
-        return;
-      }
-      _output = _stream;
-    } else {
-      // It would be nice if we could shut down cleanly but it should
-      // be an error if we can't connect to the visualizer.
-      fatal("Couldn't connect to visualizer at %s:" INTX_FORMAT,
-            PrintIdealGraphAddress, PrintIdealGraphPort);
-    }
+    init_network_stream();
   }
-
   _xml = new (ResourceObj::C_HEAP, mtCompiler) xmlStream(_output);
-
-  head(TOP_ELEMENT);
+  if (!append) {
+    head(TOP_ELEMENT);
+  }
 }
 
 // Destructor, close file or network stream
 IdealGraphPrinter::~IdealGraphPrinter() {
-
   tail(TOP_ELEMENT);
 
   // tty->print_cr("Walk time: %d", (int)_walk_time.milliseconds());
@@ -182,12 +169,12 @@
     _xml = NULL;
   }
 
-  if (_stream) {
-    delete _stream;
-    if (_stream == _output) {
+  if (_network_stream) {
+    delete _network_stream;
+    if (_network_stream == _output) {
       _output = NULL;
     }
-    _stream = NULL;
+    _network_stream = NULL;
   }
 
   if (_output) {
@@ -285,12 +272,9 @@
 }
 
 void IdealGraphPrinter::print_inline_tree(InlineTree *tree) {
-
-  if (tree == NULL) return;
-
-  ciMethod *method = tree->method();
-  print_method(tree->method(), tree->caller_bci(), tree);
-
+  if (tree != NULL) {
+    print_method(tree->method(), tree->caller_bci(), tree);
+  }
 }
 
 void IdealGraphPrinter::print_inlining() {
@@ -342,9 +326,6 @@
 
 // Has to be called whenever a method has finished compilation
 void IdealGraphPrinter::end_method() {
-
-  nmethod* method = (nmethod*)this->_current_method->code();
-
   tail(GROUP_ELEMENT);
   _current_method = NULL;
   _xml->flush();
@@ -715,6 +696,61 @@
   return C->directive()->IGVPrintLevelOption >= level;
 }
 
+void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multiple_files, bool append) {
+  ThreadCritical tc;
+  if (use_multiple_files && _file_count != 0) {
+    assert(!append, "append should only be used for debugging with a single file");
+    ResourceMark rm;
+    stringStream st;
+    const char* dot = strrchr(file_name, '.');
+    if (dot) {
+      st.write(file_name, dot - file_name);
+      st.print("%d%s", _file_count, dot);
+    } else {
+      st.print("%s%d", file_name, _file_count);
+    }
+    _output = new (ResourceObj::C_HEAP, mtCompiler) fileStream(st.as_string(), "w");
+  } else {
+    _output = new (ResourceObj::C_HEAP, mtCompiler) fileStream(file_name, append ? "a" : "w");
+  }
+  if (use_multiple_files) {
+    assert(!append, "append should only be used for debugging with a single file");
+    _file_count++;
+  }
+}
+
+void IdealGraphPrinter::init_network_stream() {
+  _network_stream = new (ResourceObj::C_HEAP, mtCompiler) networkStream();
+  // Try to connect to visualizer
+  if (_network_stream->connect(PrintIdealGraphAddress, PrintIdealGraphPort)) {
+    char c = 0;
+    _network_stream->read(&c, 1);
+    if (c != 'y') {
+      tty->print_cr("Client available, but does not want to receive data!");
+      _network_stream->close();
+      delete _network_stream;
+      _network_stream = NULL;
+      return;
+    }
+    _output = _network_stream;
+  } else {
+    // It would be nice if we could shut down cleanly but it should
+    // be an error if we can't connect to the visualizer.
+    fatal("Couldn't connect to visualizer at %s:" INTX_FORMAT,
+          PrintIdealGraphAddress, PrintIdealGraphPort);
+  }
+}
+
+void IdealGraphPrinter::update_compiled_method(ciMethod* current_method) {
+  assert(C != NULL, "must already be set");
+  if (current_method != _current_method) {
+    // If a different method, end the old and begin with the new one.
+    end_method();
+    _current_method = NULL;
+    begin_method();
+  }
+}
+
 extern const char *NodeClassNames[];
 
 #endif
--- a/src/hotspot/share/opto/idealGraphPrinter.hpp	Mon May 11 10:37:54 2020 +0200
+++ b/src/hotspot/share/opto/idealGraphPrinter.hpp	Mon May 11 12:57:39 2020 +0200
@@ -81,8 +81,8 @@
   static const char *METHOD_SHORT_NAME_PROPERTY;
   static const char *ASSEMBLY_ELEMENT;
 
-    static int _file_count;
-  networkStream *_stream;
+  static int _file_count;
+  networkStream *_network_stream;
   xmlStream *_xml;
   outputStream *_output;
   ciMethod *_current_method;
@@ -108,11 +108,14 @@
   void tail(const char *name);
   void head(const char *name);
   void text(const char *s);
+  void init(const char* file_name, bool use_multiple_files, bool append);
+  void init_file_stream(const char* file_name, bool use_multiple_files, bool append);
+  void init_network_stream();
   IdealGraphPrinter();
   ~IdealGraphPrinter();
 
  public:
-
+  IdealGraphPrinter(Compile* compile, const char* file_name = NULL, bool append = false);
   static void clean_up();
   static IdealGraphPrinter *printer();
 
@@ -125,6 +128,7 @@
   void print(const char *name, Node *root);
   bool should_print(int level);
   void set_compile(Compile* compile) {C = compile; }
+  void update_compiled_method(ciMethod* current_method);
 };
 
 #endif
--- a/src/hotspot/share/opto/phasetype.hpp	Mon May 11 10:37:54 2020 +0200
+++ b/src/hotspot/share/opto/phasetype.hpp	Mon May 11 12:57:39 2020 +0200
@@ -59,6 +59,7 @@
   PHASE_ADD_UNSAFE_BARRIER,
   PHASE_END,
   PHASE_FAILURE,
+  PHASE_DEBUG,
 
   PHASE_NUM_TYPES
 };
@@ -100,6 +101,7 @@
       case PHASE_ADD_UNSAFE_BARRIER:         return "Add barrier to unsafe op";
       case PHASE_END:                        return "End";
       case PHASE_FAILURE:                    return "Failure";
+      case PHASE_DEBUG:                      return "Debug";
       default:
         ShouldNotReachHere();
         return NULL;