Skip to content
Permalink
Browse files
8244207: Simplify usage of Compile::print_method() when debugging wit…
…h gdb and enable its use with rr

Improve debugging with usage of Compile::print_method() for IGV at breakpoints from gdb and rr.

Reviewed-by: kvn, thartmann
  • Loading branch information
chhagedorn committed May 11, 2020
1 parent ceda308 commit 3887904c164976772a9c40162a07dd0ca056bed2
Showing 5 changed files with 196 additions and 70 deletions.
@@ -4545,7 +4545,6 @@ void CloneMap::dump(node_idx_t key) const {
}
}


// Move Allocate nodes to the start of the list
void Compile::sort_macro_nodes() {
int count = macro_count();
@@ -4562,3 +4561,79 @@ void Compile::sort_macro_nodes() {
}
}
}


#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

@@ -317,6 +317,8 @@ class Compile : public Phase {
ConnectionGraph* _congraph;
#ifndef PRODUCT
IdealGraphPrinter* _printer;
static IdealGraphPrinter* _debug_file_printer;
static IdealGraphPrinter* _debug_network_printer;
#endif


@@ -639,16 +641,23 @@ class Compile : public Phase {
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);
}
#endif
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()) {
@@ -92,21 +92,47 @@ IdealGraphPrinter *IdealGraphPrinter::printer() {
}

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 @@ IdealGraphPrinter::IdealGraphPrinter() {
buffer[0] = 0;
_depth = 0;
_current_method = NULL;
assert(!_current_method, "current method must be initialized to NULL");
_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++;
_network_stream = NULL;

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 @@ IdealGraphPrinter::~IdealGraphPrinter() {
_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_method(ciMethod *method, int bci, InlineTree *tree
}

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 @@ void IdealGraphPrinter::begin_method() {

// 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 @@ bool IdealGraphPrinter::should_print(int level) {
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
@@ -81,8 +81,8 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
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 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
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 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
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
@@ -59,6 +59,7 @@ enum CompilerPhaseType {
PHASE_ADD_UNSAFE_BARRIER,
PHASE_END,
PHASE_FAILURE,
PHASE_DEBUG,

PHASE_NUM_TYPES
};
@@ -100,6 +101,7 @@ class CompilerPhaseTypeHelper {
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;

0 comments on commit 3887904

Please sign in to comment.