mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-28 19:20:33 +00:00
8354520: IGV: dump contextual information
Reviewed-by: epeter, dfenacci
This commit is contained in:
parent
9f8fbf2922
commit
def907ab89
@ -5205,32 +5205,39 @@ IdealGraphPrinter* Compile::_debug_network_printer = nullptr;
|
||||
|
||||
// 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();
|
||||
// Use in debugger (gdb/rr): p igv_print($sp, $fp, $pc).
|
||||
void igv_print(void* sp, void* fp, void* pc) {
|
||||
frame fr(sp, fp, pc);
|
||||
Compile::current()->igv_print_method_to_file(nullptr, false, &fr);
|
||||
}
|
||||
|
||||
// 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);
|
||||
void igv_print(const char* phase_name, void* sp, void* fp, void* pc) {
|
||||
frame fr(sp, fp, pc);
|
||||
Compile::current()->igv_print_method_to_file(phase_name, false, &fr);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Use in debugger (gdb/rr): p igv_print(true, $sp, $fp, $pc).
|
||||
void igv_print(bool network, void* sp, void* fp, void* pc) {
|
||||
frame fr(sp, fp, pc);
|
||||
if (network) {
|
||||
Compile::current()->igv_print_method_to_network();
|
||||
Compile::current()->igv_print_method_to_network(nullptr, &fr);
|
||||
} else {
|
||||
Compile::current()->igv_print_method_to_file();
|
||||
Compile::current()->igv_print_method_to_file(nullptr, false, &fr);
|
||||
}
|
||||
}
|
||||
|
||||
// Same as igv_print(bool network) above but with a specified phase name.
|
||||
void igv_print(bool network, const char* phase_name) {
|
||||
// Same as igv_print(bool network, ...) above but with a specified phase name.
|
||||
// Use in debugger (gdb/rr): p igv_print(true, "MyPhase", $sp, $fp, $pc).
|
||||
void igv_print(bool network, const char* phase_name, void* sp, void* fp, void* pc) {
|
||||
frame fr(sp, fp, pc);
|
||||
if (network) {
|
||||
Compile::current()->igv_print_method_to_network(phase_name);
|
||||
Compile::current()->igv_print_method_to_network(phase_name, &fr);
|
||||
} else {
|
||||
Compile::current()->igv_print_method_to_file(phase_name);
|
||||
Compile::current()->igv_print_method_to_file(phase_name, false, &fr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5242,16 +5249,20 @@ void igv_print_default() {
|
||||
// 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);
|
||||
// Use in debugger (gdb/rr): p igv_append($sp, $fp, $pc).
|
||||
void igv_append(void* sp, void* fp, void* pc) {
|
||||
frame fr(sp, fp, pc);
|
||||
Compile::current()->igv_print_method_to_file(nullptr, true, &fr);
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Same as igv_append(...) above but with a specified phase name.
|
||||
// Use in debugger (gdb/rr): p igv_append("MyPhase", $sp, $fp, $pc).
|
||||
void igv_append(const char* phase_name, void* sp, void* fp, void* pc) {
|
||||
frame fr(sp, fp, pc);
|
||||
Compile::current()->igv_print_method_to_file(phase_name, true, &fr);
|
||||
}
|
||||
|
||||
void Compile::igv_print_method_to_file(const char* phase_name, bool append) {
|
||||
void Compile::igv_print_method_to_file(const char* phase_name, bool append, const frame* fr) {
|
||||
const char* file_name = "custom_debug.xml";
|
||||
if (_debug_file_printer == nullptr) {
|
||||
_debug_file_printer = new IdealGraphPrinter(C, file_name, append);
|
||||
@ -5259,23 +5270,23 @@ void Compile::igv_print_method_to_file(const char* phase_name, bool append) {
|
||||
_debug_file_printer->update_compiled_method(C->method());
|
||||
}
|
||||
tty->print_cr("Method %s to %s", append ? "appended" : "printed", file_name);
|
||||
_debug_file_printer->print_graph(phase_name);
|
||||
_debug_file_printer->print_graph(phase_name, fr);
|
||||
}
|
||||
|
||||
void Compile::igv_print_method_to_network(const char* phase_name) {
|
||||
void Compile::igv_print_method_to_network(const char* phase_name, const frame* fr) {
|
||||
ResourceMark rm;
|
||||
GrowableArray<const Node*> empty_list;
|
||||
igv_print_graph_to_network(phase_name, empty_list);
|
||||
igv_print_graph_to_network(phase_name, empty_list, fr);
|
||||
}
|
||||
|
||||
void Compile::igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes) {
|
||||
void Compile::igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes, const frame* fr) {
|
||||
if (_debug_network_printer == nullptr) {
|
||||
_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(name, C->root(), visible_nodes);
|
||||
_debug_network_printer->print(name, C->root(), visible_nodes, fr);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -673,13 +673,13 @@ public:
|
||||
void init_igv();
|
||||
void dump_igv(const char* graph_name, int level = 3) {
|
||||
if (should_print_igv(level)) {
|
||||
_igv_printer->print_graph(graph_name);
|
||||
_igv_printer->print_graph(graph_name, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
void igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes);
|
||||
void igv_print_method_to_file(const char* phase_name = nullptr, bool append = false, const frame* fr = nullptr);
|
||||
void igv_print_method_to_network(const char* phase_name = nullptr, const frame* fr = nullptr);
|
||||
void igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes, const frame* fr);
|
||||
static IdealGraphPrinter* debug_file_printer() { return _debug_file_printer; }
|
||||
static IdealGraphPrinter* debug_network_printer() { return _debug_network_printer; }
|
||||
#endif
|
||||
|
||||
@ -27,8 +27,10 @@
|
||||
#include "opto/idealGraphPrinter.hpp"
|
||||
#include "opto/machnode.hpp"
|
||||
#include "opto/parse.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/threadCritical.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "utilities/decoder.hpp"
|
||||
#include "utilities/stringUtils.hpp"
|
||||
|
||||
#ifndef PRODUCT
|
||||
@ -49,6 +51,13 @@ const char *IdealGraphPrinter::REMOVE_EDGE_ELEMENT = "removeEdge";
|
||||
const char *IdealGraphPrinter::REMOVE_NODE_ELEMENT = "removeNode";
|
||||
const char *IdealGraphPrinter::COMPILATION_ID_PROPERTY = "compilationId";
|
||||
const char *IdealGraphPrinter::COMPILATION_OSR_PROPERTY = "osr";
|
||||
const char *IdealGraphPrinter::COMPILATION_ARGUMENTS_PROPERTY = "arguments";
|
||||
const char *IdealGraphPrinter::COMPILATION_MACHINE_PROPERTY = "machine";
|
||||
const char *IdealGraphPrinter::COMPILATION_CPU_FEATURES_PROPERTY = "cpuFeatures";
|
||||
const char *IdealGraphPrinter::COMPILATION_VM_VERSION_PROPERTY = "vm";
|
||||
const char *IdealGraphPrinter::COMPILATION_DATE_TIME_PROPERTY = "dateTime";
|
||||
const char *IdealGraphPrinter::COMPILATION_PROCESS_ID_PROPERTY = "processId";
|
||||
const char *IdealGraphPrinter::COMPILATION_THREAD_ID_PROPERTY = "threadId";
|
||||
const char *IdealGraphPrinter::METHOD_NAME_PROPERTY = "name";
|
||||
const char *IdealGraphPrinter::METHOD_IS_PUBLIC_PROPERTY = "public";
|
||||
const char *IdealGraphPrinter::METHOD_IS_STATIC_PROPERTY = "static";
|
||||
@ -336,6 +345,42 @@ void IdealGraphPrinter::begin_method() {
|
||||
|
||||
print_prop(COMPILATION_ID_PROPERTY, C->compile_id());
|
||||
|
||||
stringStream args;
|
||||
Arguments::print_jvm_args_on(&args);
|
||||
print_prop(COMPILATION_ARGUMENTS_PROPERTY, args.freeze());
|
||||
|
||||
stringStream machine;
|
||||
buffer[0] = 0;
|
||||
os::print_summary_info(&machine, buffer, sizeof(buffer) - 1);
|
||||
print_prop(COMPILATION_MACHINE_PROPERTY, machine.freeze());
|
||||
|
||||
print_prop(COMPILATION_CPU_FEATURES_PROPERTY, VM_Version::features_string());
|
||||
|
||||
stringStream version;
|
||||
buffer[0] = 0;
|
||||
JDK_Version::current().to_string(buffer, sizeof(buffer) - 1);
|
||||
const char* runtime_name = JDK_Version::runtime_name() != nullptr ?
|
||||
JDK_Version::runtime_name() : "";
|
||||
const char* runtime_version = JDK_Version::runtime_version() != nullptr ?
|
||||
JDK_Version::runtime_version() : "";
|
||||
const char* vendor_version = JDK_Version::runtime_vendor_version() != nullptr ?
|
||||
JDK_Version::runtime_vendor_version() : "";
|
||||
const char* jdk_debug_level = VM_Version::printable_jdk_debug_level() != nullptr ?
|
||||
VM_Version::printable_jdk_debug_level() : "";
|
||||
|
||||
version.print_cr("%s%s%s (%s) (%sbuild %s)", runtime_name,
|
||||
(*vendor_version != '\0') ? " " : "", vendor_version,
|
||||
buffer, jdk_debug_level, runtime_version);
|
||||
print_prop(COMPILATION_VM_VERSION_PROPERTY, version.freeze());
|
||||
|
||||
stringStream time;
|
||||
buffer[0] = 0;
|
||||
os::print_date_and_time(&time, buffer, sizeof(buffer) - 1);
|
||||
print_prop(COMPILATION_DATE_TIME_PROPERTY, time.freeze());
|
||||
|
||||
print_prop(COMPILATION_PROCESS_ID_PROPERTY, os::current_process_id());
|
||||
print_prop(COMPILATION_THREAD_ID_PROPERTY, os::current_thread_id());
|
||||
|
||||
tail(PROPERTIES_ELEMENT);
|
||||
|
||||
_should_send_method = true;
|
||||
@ -862,24 +907,91 @@ void IdealGraphPrinter::walk_nodes(Node* start, bool edges) {
|
||||
}
|
||||
}
|
||||
|
||||
void IdealGraphPrinter::print_graph(const char* name) {
|
||||
// Whether the stack walk should skip the given frame when producing a C2 stack
|
||||
// trace. We consider IGV- and debugger-specific frames uninteresting.
|
||||
static bool should_skip_frame(const char* name) {
|
||||
return strstr(name, "IdealGraphPrinter") != nullptr ||
|
||||
strstr(name, "Compile::print_method") != nullptr ||
|
||||
strstr(name, "Compile::igv_print_graph") != nullptr ||
|
||||
strstr(name, "PrintBFS") != nullptr ||
|
||||
strstr(name, "Node::dump_bfs") != nullptr;
|
||||
}
|
||||
|
||||
// Whether the stack walk should be considered done when visiting a certain
|
||||
// frame. The purpose of walking the stack is producing a C2 trace, so we
|
||||
// consider all frames below (and including) C2Compiler::compile_method(..)
|
||||
// uninteresting.
|
||||
static bool should_end_stack_walk(const char* name) {
|
||||
return strstr(name, "C2Compiler::compile_method") != nullptr;
|
||||
}
|
||||
|
||||
void IdealGraphPrinter::print_stack(const frame* initial_frame, outputStream* graph_name) {
|
||||
char buf[O_BUFLEN];
|
||||
frame fr = initial_frame == nullptr ? os::current_frame() : *initial_frame;
|
||||
int frame = 0;
|
||||
for (int count = 0; count < StackPrintLimit && fr.pc() != nullptr; count++) {
|
||||
int offset;
|
||||
buf[0] = '\0';
|
||||
bool found = os::dll_address_to_function_name(fr.pc(), buf, sizeof(buf), &offset);
|
||||
if (!found || should_end_stack_walk(buf)) {
|
||||
break;
|
||||
}
|
||||
if (!should_skip_frame(buf)) {
|
||||
stringStream frame_loc;
|
||||
frame_loc.print("%s", buf);
|
||||
buf[0] = '\0';
|
||||
int line_no;
|
||||
if (Decoder::get_source_info(fr.pc(), buf, sizeof(buf), &line_no, count != 1)) {
|
||||
frame_loc.print(" (%s:%d)", buf, line_no);
|
||||
if (graph_name != nullptr) {
|
||||
// Extract a debug graph name and return.
|
||||
graph_name->print("%s:%d", buf, line_no);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (graph_name == nullptr) {
|
||||
// Print frame as IGV property and continue to the next frame.
|
||||
stringStream frame_number_str;
|
||||
frame_number_str.print("frame %d:", frame);
|
||||
print_prop(frame_number_str.freeze(), frame_loc.freeze());
|
||||
frame++;
|
||||
}
|
||||
}
|
||||
fr = frame::next_frame(fr, Thread::current_or_null());
|
||||
}
|
||||
}
|
||||
|
||||
void IdealGraphPrinter::print_graph(const char* name, const frame* fr) {
|
||||
ResourceMark rm;
|
||||
GrowableArray<const Node*> empty_list;
|
||||
print(name, (Node*) C->root(), empty_list);
|
||||
print(name, (Node*) C->root(), empty_list, fr);
|
||||
}
|
||||
|
||||
// Print current ideal graph
|
||||
void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const Node*>& visible_nodes) {
|
||||
void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const Node*>& visible_nodes, const frame* fr) {
|
||||
|
||||
if (!_current_method || !_should_send_method || node == nullptr) return;
|
||||
|
||||
if (name == nullptr) {
|
||||
stringStream graph_name;
|
||||
print_stack(fr, &graph_name);
|
||||
name = graph_name.freeze();
|
||||
if (strlen(name) == 0) {
|
||||
name = "Debug";
|
||||
}
|
||||
}
|
||||
|
||||
// Warning, unsafe cast?
|
||||
_chaitin = (PhaseChaitin *)C->regalloc();
|
||||
|
||||
begin_head(GRAPH_ELEMENT);
|
||||
print_attr(GRAPH_NAME_PROPERTY, (const char *)name);
|
||||
print_attr(GRAPH_NAME_PROPERTY, name);
|
||||
end_head();
|
||||
|
||||
head(PROPERTIES_ELEMENT);
|
||||
print_stack(fr, nullptr);
|
||||
tail(PROPERTIES_ELEMENT);
|
||||
|
||||
head(NODES_ELEMENT);
|
||||
if (C->cfg() != nullptr) {
|
||||
// Compute the maximum estimated frequency in the current graph.
|
||||
|
||||
@ -67,6 +67,13 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
|
||||
static const char *ALL_PROPERTY;
|
||||
static const char *COMPILATION_ID_PROPERTY;
|
||||
static const char *COMPILATION_OSR_PROPERTY;
|
||||
static const char *COMPILATION_ARGUMENTS_PROPERTY;
|
||||
static const char *COMPILATION_MACHINE_PROPERTY;
|
||||
static const char *COMPILATION_CPU_FEATURES_PROPERTY;
|
||||
static const char *COMPILATION_VM_VERSION_PROPERTY;
|
||||
static const char *COMPILATION_DATE_TIME_PROPERTY;
|
||||
static const char *COMPILATION_PROCESS_ID_PROPERTY;
|
||||
static const char *COMPILATION_THREAD_ID_PROPERTY;
|
||||
static const char *METHOD_NAME_PROPERTY;
|
||||
static const char *BLOCK_NAME_PROPERTY;
|
||||
static const char *BLOCK_DOMINATOR_PROPERTY;
|
||||
@ -110,6 +117,10 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
|
||||
double _max_freq;
|
||||
bool _append;
|
||||
|
||||
// Walk the native stack and print relevant C2 frames as IGV properties (if
|
||||
// graph_name == nullptr) or the graph name based on the highest C2 frame (if
|
||||
// graph_name != nullptr).
|
||||
void print_stack(const frame* initial_frame, outputStream* graph_name);
|
||||
void print_method(ciMethod* method, int bci, InlineTree* tree);
|
||||
void print_inline_tree(InlineTree* tree);
|
||||
void visit_node(Node* n, bool edges);
|
||||
@ -149,8 +160,8 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
|
||||
void print_inlining();
|
||||
void begin_method();
|
||||
void end_method();
|
||||
void print_graph(const char* name);
|
||||
void print(const char* name, Node* root, GrowableArray<const Node*>& hidden_nodes);
|
||||
void print_graph(const char* name, const frame* fr = nullptr);
|
||||
void print(const char* name, Node* root, GrowableArray<const Node*>& hidden_nodes, const frame* fr = nullptr);
|
||||
void set_compile(Compile* compile) {C = compile; }
|
||||
void update_compiled_method(ciMethod* current_method);
|
||||
};
|
||||
|
||||
@ -1774,8 +1774,8 @@ Node* Node::find(const int idx, bool only_ctrl) {
|
||||
|
||||
class PrintBFS {
|
||||
public:
|
||||
PrintBFS(const Node* start, const int max_distance, const Node* target, const char* options, outputStream* st)
|
||||
: _start(start), _max_distance(max_distance), _target(target), _options(options), _output(st),
|
||||
PrintBFS(const Node* start, const int max_distance, const Node* target, const char* options, outputStream* st, const frame* fr)
|
||||
: _start(start), _max_distance(max_distance), _target(target), _options(options), _output(st), _frame(fr),
|
||||
_dcc(this), _info_uid(cmpkey, hashkey) {}
|
||||
|
||||
void run();
|
||||
@ -1796,6 +1796,7 @@ private:
|
||||
const Node* _target;
|
||||
const char* _options;
|
||||
outputStream* _output;
|
||||
const frame* _frame;
|
||||
|
||||
// options
|
||||
bool _traverse_inputs = false;
|
||||
@ -2057,7 +2058,7 @@ void PrintBFS::print() {
|
||||
if (_print_igv) {
|
||||
Compile* C = Compile::current();
|
||||
C->init_igv();
|
||||
C->igv_print_graph_to_network("PrintBFS", _print_list);
|
||||
C->igv_print_graph_to_network(nullptr, _print_list, _frame);
|
||||
}
|
||||
} else {
|
||||
_output->print_cr("No nodes to print.");
|
||||
@ -2102,6 +2103,8 @@ void PrintBFS::print_options_help(bool print_examples) {
|
||||
_output->print_cr(" B: print scheduling blocks (if available)");
|
||||
_output->print_cr(" $: dump only, no header, no other columns");
|
||||
_output->print_cr(" !: show nodes on IGV (sent over network stream)");
|
||||
_output->print_cr(" (use preferably with dump_bfs(int, Node*, char*, void*, void*, void*)");
|
||||
_output->print_cr(" to produce a C2 stack trace along with the graph dump, see examples below)");
|
||||
_output->print_cr("");
|
||||
_output->print_cr("recursively follow edges to nodes with permitted visit types,");
|
||||
_output->print_cr("on the boundary additionally display nodes allowed in boundary types");
|
||||
@ -2151,6 +2154,9 @@ void PrintBFS::print_options_help(bool print_examples) {
|
||||
_output->print_cr(" find all paths (A) between two nodes of length at most 8");
|
||||
_output->print_cr(" find_node(741)->dump_bfs(7, find_node(741), \"c+A\")");
|
||||
_output->print_cr(" find all control loops for this node");
|
||||
_output->print_cr(" find_node(741)->dump_bfs(7, find_node(741), \"c+A!\", $sp, $fp, $pc)");
|
||||
_output->print_cr(" same as above, but printing the resulting subgraph");
|
||||
_output->print_cr(" along with a C2 stack trace on IGV");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2409,8 +2415,8 @@ void Node::dump_bfs(const int max_distance, Node* target, const char* options) c
|
||||
}
|
||||
|
||||
// Used to dump to stream.
|
||||
void Node::dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st) const {
|
||||
PrintBFS bfs(this, max_distance, target, options, st);
|
||||
void Node::dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st, const frame* fr) const {
|
||||
PrintBFS bfs(this, max_distance, target, options, st, fr);
|
||||
bfs.run();
|
||||
}
|
||||
|
||||
@ -2419,6 +2425,13 @@ void Node::dump_bfs(const int max_distance) const {
|
||||
dump_bfs(max_distance, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// Call this from debugger, with stack handling register arguments for IGV dumps.
|
||||
// Example: p find_node(741)->dump_bfs(7, find_node(741), "c+A!", $sp, $fp, $pc).
|
||||
void Node::dump_bfs(const int max_distance, Node* target, const char* options, void* sp, void* fp, void* pc) const {
|
||||
frame fr(sp, fp, pc);
|
||||
dump_bfs(max_distance, target, options, tty, &fr);
|
||||
}
|
||||
|
||||
// -----------------------------dump_idx---------------------------------------
|
||||
void Node::dump_idx(bool align, outputStream* st, DumpConfig* dc) const {
|
||||
if (dc != nullptr) {
|
||||
|
||||
@ -1298,9 +1298,10 @@ public:
|
||||
public:
|
||||
Node* find(int idx, bool only_ctrl = false); // Search the graph for the given idx.
|
||||
Node* find_ctrl(int idx); // Search control ancestors for the given idx.
|
||||
void dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st) const;
|
||||
void dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st, const frame* fr = nullptr) const;
|
||||
void dump_bfs(const int max_distance, Node* target, const char* options) const; // directly to tty
|
||||
void dump_bfs(const int max_distance) const; // dump_bfs(max_distance, nullptr, nullptr)
|
||||
void dump_bfs(const int max_distance, Node* target, const char* options, void* sp, void* fp, void* pc) const;
|
||||
class DumpConfig {
|
||||
public:
|
||||
// overridden to implement coloring of node idx
|
||||
|
||||
@ -22,6 +22,8 @@ for building and running IGV.
|
||||
|
||||
# Usage
|
||||
|
||||
## Regular JVM Execution
|
||||
|
||||
The JVM support is controlled by the flag `-XX:PrintIdealGraphLevel=N`, where
|
||||
Ideal graphs are dumped at the following points:
|
||||
|
||||
@ -48,6 +50,59 @@ Alternatively the output can be sent to a file using
|
||||
with unique names being generated by adding a number onto the provided file
|
||||
name.
|
||||
|
||||
## Dumping Graphs From a Debugger
|
||||
|
||||
The JVM provides some entry functions to dump graphs from a debugger such as
|
||||
`gdb` or `rr`, see the different variants of `igv_print` and `igv_append` in
|
||||
[`compile.cpp`](/src/hotspot/share/opto/compile.cpp). In combination with the
|
||||
IGV network interface, these functions enable a powerful interactive workflow
|
||||
where the user can simultaneously step through C2's code and visualize the
|
||||
evolving Ideal graph. Note that, to produce and print meaningful C2 stack
|
||||
traces, these functions take the stack pointer, frame pointer, and program
|
||||
counter registers as arguments. These are usually `$sp`, `$fp`, and `$pc`:
|
||||
|
||||
```
|
||||
(gdb) p igv_print(true, $sp, $fp, $pc)
|
||||
Method printed over network stream to IGV
|
||||
```
|
||||
|
||||
but might be given different names on different platforms, see the output of
|
||||
`p help()` for more information. A tip to further simplify the workflow in
|
||||
`gdb` or `rr` is to create a user-defined command such as e.g.:
|
||||
|
||||
```
|
||||
define igv
|
||||
p igv_print(true, $sp, $fp, $pc)
|
||||
end
|
||||
```
|
||||
|
||||
On `lldb`, it might be necessary to explicitly cast the registers to `void*` to
|
||||
ensure their values are correctly passed as arguments:
|
||||
|
||||
```
|
||||
(lldb) p igv_print(true, (void*)$sp, (void*)$fp, (void*)$pc)
|
||||
```
|
||||
|
||||
Another way to dump graphs interactively is through the `Node::dump_bfs`
|
||||
functionality with the option `!` (run `p find_node(0)->dump_bfs(0,0,"H")` to
|
||||
see the complete list of options). One of the versions of this function also
|
||||
takes the three stack management registers to produce a C2 stack trace:
|
||||
|
||||
```
|
||||
(gdb) p find_node(3)->dump_bfs(0, 0, "!", $sp, $fp, $pc)
|
||||
(...)
|
||||
Method printed over network stream to IGV
|
||||
```
|
||||
|
||||
Again, user-defined debugger commands can be created for simplicity. For
|
||||
example, to dump the current graph with a given node highlighted:
|
||||
|
||||
```
|
||||
define igv_node
|
||||
p find_node($arg0)->dump_bfs(0, 0, "!", $sp, $fp, $pc)
|
||||
end
|
||||
```
|
||||
|
||||
## Defining Custom Filters
|
||||
|
||||
IGV has a powerful filter mechanism with which nodes and blocks can be colored,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user