8315604: IGV: dump and visualize node bottom and phase types
Co-authored-by: Tobias Holenstein <tholenstein@openjdk.org> Reviewed-by: thartmann, chagedorn, tholenstein
This commit is contained in:
parent
8fcf70e931
commit
207819a05e
@ -374,9 +374,28 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) {
|
||||
#ifndef PRODUCT
|
||||
Compile::current()->_in_dump_cnt++;
|
||||
print_prop(NODE_NAME_PROPERTY, (const char *)node->Name());
|
||||
print_prop("idx", node->_idx);
|
||||
const Type *t = node->bottom_type();
|
||||
print_prop("type", t->msg());
|
||||
print_prop("idx", node->_idx);
|
||||
if (t->category() != Type::Category::Control &&
|
||||
t->category() != Type::Category::Memory) {
|
||||
// Print detailed type information for nodes whose type is not trivial.
|
||||
buffer[0] = 0;
|
||||
stringStream bottom_type_stream(buffer, sizeof(buffer) - 1);
|
||||
t->dump_on(&bottom_type_stream);
|
||||
print_prop("bottom_type", buffer);
|
||||
if (C->types() != nullptr && C->matcher() == nullptr) {
|
||||
// Phase types maintained during optimization (GVN, IGVN, CCP) are
|
||||
// available and valid (not in code generation phase).
|
||||
const Type* pt = (*C->types())[node->_idx];
|
||||
if (pt != nullptr) {
|
||||
buffer[0] = 0;
|
||||
stringStream phase_type_stream(buffer, sizeof(buffer) - 1);
|
||||
pt->dump_on(&phase_type_stream);
|
||||
print_prop("phase_type", buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (C->cfg() != nullptr) {
|
||||
Block* block = C->cfg()->get_block_for_node(node);
|
||||
|
@ -89,7 +89,7 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
|
||||
outputStream *_output;
|
||||
ciMethod *_current_method;
|
||||
int _depth;
|
||||
char buffer[128];
|
||||
char buffer[512];
|
||||
bool _should_send_method;
|
||||
PhaseChaitin* _chaitin;
|
||||
bool _traverse_outs;
|
||||
|
@ -124,11 +124,10 @@ class Type_Array : public AnyObj {
|
||||
uint _max;
|
||||
const Type **_types;
|
||||
void grow( uint i ); // Grow array node to fit
|
||||
const Type *operator[] ( uint i ) const // Lookup, or null for not mapped
|
||||
{ return (i<_max) ? _types[i] : (Type*)nullptr; }
|
||||
friend class PhaseValues;
|
||||
public:
|
||||
Type_Array(Arena *a) : _a(a), _max(0), _types(0) {}
|
||||
const Type *operator[] ( uint i ) const // Lookup, or null for not mapped
|
||||
{ return (i<_max) ? _types[i] : (Type*)nullptr; }
|
||||
const Type *fast_lookup(uint i) const{assert(i<_max,"oob");return _types[i];}
|
||||
// Extend the mapping: index i maps to Type *n.
|
||||
void map( uint i, const Type *n ) { if( i>=_max ) grow(i); _types[i] = n; }
|
||||
|
@ -26,23 +26,23 @@ package com.sun.hotspot.igv.filter;
|
||||
import com.sun.hotspot.igv.graph.Diagram;
|
||||
import com.sun.hotspot.igv.graph.Figure;
|
||||
import com.sun.hotspot.igv.graph.Selector;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.List;
|
||||
|
||||
public class EditPropertyFilter extends AbstractFilter {
|
||||
|
||||
private String name;
|
||||
private Selector selector;
|
||||
private final String inputPropertyName;
|
||||
private final String[] inputPropertyNames;
|
||||
private final String outputPropertyName;
|
||||
private final UnaryOperator<String> editFunction;
|
||||
private final Function<String[], String> editFunction;
|
||||
|
||||
public EditPropertyFilter(String name, Selector selector,
|
||||
String inputPropertyName, String outputPropertyName,
|
||||
UnaryOperator<String> editFunction) {
|
||||
String[] inputPropertyNames, String outputPropertyName,
|
||||
Function<String[], String> editFunction) {
|
||||
this.name = name;
|
||||
this.selector = selector;
|
||||
this.inputPropertyName = inputPropertyName;
|
||||
this.inputPropertyNames = inputPropertyNames;
|
||||
this.outputPropertyName = outputPropertyName;
|
||||
this.editFunction = editFunction;
|
||||
}
|
||||
@ -55,9 +55,12 @@ public class EditPropertyFilter extends AbstractFilter {
|
||||
@Override
|
||||
public void apply(Diagram diagram) {
|
||||
List<Figure> list = selector.selected(diagram);
|
||||
String[] inputVals = new String[inputPropertyNames.length];
|
||||
for (Figure f : list) {
|
||||
String inputVal = f.getProperties().get(inputPropertyName);
|
||||
String outputVal = editFunction.apply(inputVal);
|
||||
for (int i = 0; i < inputPropertyNames.length; i++) {
|
||||
inputVals[i] = f.getProperties().get(inputPropertyNames[i]);
|
||||
}
|
||||
String outputVal = editFunction.apply(inputVals);
|
||||
f.getProperties().setProperty(outputPropertyName, outputVal);
|
||||
}
|
||||
}
|
||||
|
@ -190,15 +190,15 @@ var white = Color.white;
|
||||
// function that takes as input the old property value and returns the new
|
||||
// property value.
|
||||
function editSameProperty(selector, propertyName, editFunction) {
|
||||
var f = new EditPropertyFilter("", selector, propertyName, propertyName, editFunction);
|
||||
var f = new EditPropertyFilter("", selector, [propertyName], propertyName, editFunction);
|
||||
f.apply(graph);
|
||||
}
|
||||
|
||||
// Update the value of the given property ('outputPropertyName') in the selected
|
||||
// nodes according to a function that takes as input the value of a possibly
|
||||
// different property ('inputPropertyName') and returns the new property value.
|
||||
function editProperty(selector, inputPropertyName, outputPropertyName, editFunction) {
|
||||
var f = new EditPropertyFilter("", selector, inputPropertyName, outputPropertyName, editFunction);
|
||||
// nodes according to a function that takes as input the values of multiple
|
||||
// properties ('inputPropertyNames') and returns the new property value.
|
||||
function editProperty(selector, inputPropertyNames, outputPropertyName, editFunction) {
|
||||
var f = new EditPropertyFilter("", selector, inputPropertyNames, outputPropertyName, editFunction);
|
||||
f.apply(graph);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
// combination with "Simplify graph".
|
||||
|
||||
// Pretty-print Bool nodes to be shown as output slots.
|
||||
function replaceComparisonWithSign(dump_spec) {
|
||||
function replaceComparisonWithSign(propertyValues) {
|
||||
dump_spec = propertyValues[0]
|
||||
var comparison = dump_spec.replace('[','').replace(']','')
|
||||
switch (comparison) {
|
||||
case "eq": return "=";
|
||||
@ -23,21 +24,22 @@ editSameProperty(and([matches("name", "ConP|ConN"), matches("dump_spec", "#.*NUL
|
||||
function(t) {return "null";});
|
||||
|
||||
// Pretty-print CatchProj nodes.
|
||||
function catchProjShortText(con) {
|
||||
function catchProjShortText(propertyValues) {
|
||||
con = propertyValues[0]
|
||||
switch (con) {
|
||||
case "0": return "F"; // fall-through
|
||||
case "1": return "T"; // throw
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
editProperty(matches("name", "CatchProj"), "con", "short_name", catchProjShortText);
|
||||
editProperty(matches("name", "CatchProj"), ["con"], "short_name", catchProjShortText);
|
||||
|
||||
// Add short text to inlined Mach data parameters.
|
||||
editProperty(and([matches("name", "MachProj"),
|
||||
matches("category", "data"),
|
||||
successorOf(matches("name", "Start"))]),
|
||||
"dump_spec", "short_name",
|
||||
function(dump_spec) {return dump_spec;});
|
||||
["dump_spec"], "short_name",
|
||||
function(dump_spec) {return dump_spec[0];});
|
||||
|
||||
// Condense inputs in all nodes.
|
||||
var anyNode = matches("name", ".*");
|
||||
|
@ -14,10 +14,10 @@ function callJavaInfo(dump_spec, regularPos, trapPos) {
|
||||
}
|
||||
return "trap: " + tm[2];
|
||||
}
|
||||
editProperty(matches("name", "CallStaticJava|CallDynamicJava|CallJava"), "dump_spec", "extra_label",
|
||||
function(dump_spec) {return callJavaInfo(dump_spec, 2, 2);});
|
||||
editProperty(matches("name", "CallStaticJavaDirect|CallDynamicJavaDirect"), "dump_spec", "extra_label",
|
||||
function(dump_spec) {return callJavaInfo(dump_spec, 1, 3);});
|
||||
editProperty(matches("name", "CallStaticJava|CallDynamicJava|CallJava"), ["dump_spec"], "extra_label",
|
||||
function(dump_spec) {return callJavaInfo(dump_spec[0], 2, 2);});
|
||||
editProperty(matches("name", "CallStaticJavaDirect|CallDynamicJavaDirect"), ["dump_spec"], "extra_label",
|
||||
function(dump_spec) {return callJavaInfo(dump_spec[0], 1, 3);});
|
||||
|
||||
function callLeafInfo(dump_spec, pos) {
|
||||
dump_components = split_string(dump_spec);
|
||||
@ -26,21 +26,7 @@ function callLeafInfo(dump_spec, pos) {
|
||||
}
|
||||
return dump_components[pos];
|
||||
}
|
||||
editProperty(matches("name", "CallLeaf|CallLeafNoFP"), "dump_spec", "extra_label",
|
||||
function(dump_spec) {return callLeafInfo(dump_spec, 1);});
|
||||
editProperty(matches("name", "CallLeafDirect|CallLeafDirectVector|CallLeafNoFPDirect"), "dump_spec", "extra_label",
|
||||
function(dump_spec) {return callLeafInfo(dump_spec, 0);});
|
||||
|
||||
// Add extra line to exception creation nodes with the name of the exception.
|
||||
function exceptionInfo(dump_spec) {
|
||||
dump_spec2 = dump_spec.replace('#','')
|
||||
dump_components = split_string(dump_spec2);
|
||||
if (dump_components.length < 1) {
|
||||
return null;
|
||||
}
|
||||
// dump_components[0] has a form like e.g. java/lang/NumberFormatException:NotNull,
|
||||
// we want to return only the simple class name ("NumberFormatException").
|
||||
simple_classname = dump_components[0].split("/").pop();
|
||||
return simple_classname.split(":")[0];
|
||||
}
|
||||
editProperty(matches("name", "CreateEx|CreateException"), "dump_spec", "extra_label", exceptionInfo);
|
||||
editProperty(matches("name", "CallLeaf|CallLeafNoFP"), ["dump_spec"], "extra_label",
|
||||
function(dump_spec) {return callLeafInfo(dump_spec[0], 1);});
|
||||
editProperty(matches("name", "CallLeafDirect|CallLeafDirectVector|CallLeafNoFPDirect"), ["dump_spec"], "extra_label",
|
||||
function(dump_spec) {return callLeafInfo(dump_spec[0], 0);});
|
||||
|
@ -0,0 +1,72 @@
|
||||
// This filter appends simplified type information to the (possibly already
|
||||
// existing) extra-label line.
|
||||
// If the phase type is available, show it. If the bottom type is available and
|
||||
// differs from the bottom type, show it too (prefixed with 'B:').
|
||||
|
||||
// Simplify a reference type of the form
|
||||
// "[5:]my/package/Class (package1/Class1,package2/Class2,..)"
|
||||
// into
|
||||
// "[5:]Class"
|
||||
function simplify_reference_type(type) {
|
||||
// Clean up interface lists in reference types.
|
||||
var m = /(.*)\(.*\)(.*)/.exec(type);
|
||||
if (m != null && typeof m[1] != 'undefined' && typeof m[2] != 'undefined') {
|
||||
type = m[1] + m[2];
|
||||
}
|
||||
// Remove package name in reference types.
|
||||
var m2 = /(\d+:)?.*\/(.*)/.exec(type);
|
||||
if (m2 != null && typeof m2[2] != 'undefined') {
|
||||
type = (typeof m2[1] != 'undefined' ? m2[1] : "") + m2[2];
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
// Remove fixed input types for calls and simplify references.
|
||||
function simplifyType(type) {
|
||||
var callTypeStart = "{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address";
|
||||
if (type.startsWith(callTypeStart)) {
|
||||
// Exclude types of the first five outputs of call-like nodes.
|
||||
type = type.replace(callTypeStart, "").replace("}", "");
|
||||
prefix = ", ";
|
||||
if (type.startsWith(prefix)) {
|
||||
type = type.slice(prefix.length);
|
||||
}
|
||||
components = type.split(", ");
|
||||
for (i = 0; i < components.length; i++) {
|
||||
components[i] = simplify_reference_type(components[i]);
|
||||
}
|
||||
type = "{" + components.join(", ") + "}";
|
||||
} else {
|
||||
type = simplify_reference_type(type);
|
||||
}
|
||||
type = type.replace(">=", "≥").replace("<=", "≤");
|
||||
return type;
|
||||
}
|
||||
|
||||
// Merge a possibly existing extra label, bottom type, and phase type into a
|
||||
// new, single extra label.
|
||||
function mergeAndAppendTypeInfo(extra_label, bottom_type, phase_type) {
|
||||
if (phase_type == null && bottom_type == null) {
|
||||
return extra_label;
|
||||
}
|
||||
type = "";
|
||||
// Always show phase type, if available.
|
||||
if (phase_type != null) {
|
||||
type += simplifyType(phase_type);
|
||||
}
|
||||
// Show bottom type, if available and different from phase type.
|
||||
if (bottom_type != null && bottom_type != phase_type) {
|
||||
if (phase_type != null) {
|
||||
type += " | ";
|
||||
}
|
||||
type += "B: ";
|
||||
type += simplifyType(bottom_type);
|
||||
}
|
||||
new_extra_label = extra_label == null ? "" : (extra_label + " ");
|
||||
return new_extra_label + type;
|
||||
}
|
||||
|
||||
editProperty(not(or([matches("bottom_type", "bottom"),
|
||||
matches("bottom_type", "abIO")])),
|
||||
["extra_label", "bottom_type", "phase_type"], "extra_label",
|
||||
function(propertyValues) {return mergeAndAppendTypeInfo(propertyValues[0], propertyValues[1], propertyValues[2]);});
|
@ -15,11 +15,15 @@
|
||||
</file>
|
||||
<file name="Show custom node info.js" url="filters/customNodeInfo.filter">
|
||||
<attr name="enabled" boolvalue="true"/>
|
||||
<attr name="after" stringvalue="Color by execution frequency"/>
|
||||
<attr name="after" stringvalue="Show node warnings"/>
|
||||
</file>
|
||||
<file name="Show types.js" url="filters/showTypes.filter">
|
||||
<attr name="enabled" boolvalue="false"/>
|
||||
<attr name="after" stringvalue="Show custom node info"/>
|
||||
</file>
|
||||
<file name="Simplify graph.js" url="filters/simplifyGraph.filter">
|
||||
<attr name="enabled" boolvalue="false"/>
|
||||
<attr name="after" stringvalue="Show custom node info"/>
|
||||
<attr name="after" stringvalue="Show types"/>
|
||||
</file>
|
||||
<file name="Condense graph.js" url="filters/condenseGraph.filter">
|
||||
<attr name="enabled" boolvalue="false"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -84,6 +84,16 @@ public class FigureWidget extends Widget implements Properties.Provider, PopupMe
|
||||
return middleWidget.isHitAt(localLocation);
|
||||
}
|
||||
|
||||
private void formatExtraLabel(boolean selected) {
|
||||
// If the figure contains an extra label, use a light italic font to
|
||||
// differentiate it from the regular label.
|
||||
if (getFigure().getProperties().get("extra_label") != null) {
|
||||
LabelWidget extraLabelWidget = labelWidgets.get(labelWidgets.size() - 1);
|
||||
extraLabelWidget.setFont(Diagram.FONT.deriveFont(Font.ITALIC));
|
||||
extraLabelWidget.setForeground(selected ? getTextColor() : Color.DARK_GRAY);
|
||||
}
|
||||
}
|
||||
|
||||
public FigureWidget(final Figure f, DiagramScene scene) {
|
||||
super(scene);
|
||||
|
||||
@ -139,6 +149,7 @@ public class FigureWidget extends Widget implements Properties.Provider, PopupMe
|
||||
lw.setBorder(BorderFactory.createEmptyBorder());
|
||||
lw.setCheckClipping(false);
|
||||
}
|
||||
formatExtraLabel(false);
|
||||
|
||||
if (getFigure().getWarning() != null) {
|
||||
ImageWidget warningWidget = new ImageWidget(scene, warningSign);
|
||||
@ -194,6 +205,7 @@ public class FigureWidget extends Widget implements Properties.Provider, PopupMe
|
||||
for (LabelWidget labelWidget : labelWidgets) {
|
||||
labelWidget.setFont(font);
|
||||
}
|
||||
formatExtraLabel(state.isSelected());
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user