Package jdk.graal.compiler.graphio
package jdk.graal.compiler.graphio
Send your graphs to IGV via a socket or a file. This package allows one to easily encode
any graph-like data structure and send it for visualization to OracleLab's Ideal Graph
Visualizer tool. Assuming you already have your own data structure that contains
nodes and edges among them, creating a
The
Now you are ready to dump your graph into IGV. Where to obtain the right channel? One
option is to create a
Call the
All these interfaces follow the
singletonizer API pattern again - e.g.
no need to change your existing data structures, just implement the operations provided by the
interfaces you pass into the builder. By combining these interfaces together you can get as rich,
colorful, source linked graphs as the compiler produces to describe its optimizations.
GraphOutput
specialized for your data is a matter of
implementing a single interface:
class AcmeGraphStructure implements
GraphStructure<AcmeGraph, AcmeNode, AcmeNodeType, AcmePorts> {
@Override
public AcmeGraph graph(AcmeGraph currentGraph, Object obj) {
return obj instanceof AcmeGraph ? (AcmeGraph) obj : null;
}
@Override
public Iterable<? extends AcmeNode> nodes(AcmeGraph graph) {
return graph.allNodes();
}
@Override
public int nodesCount(AcmeGraph graph) {
return graph.allNodes().size();
}
@Override
public int nodeId(AcmeNode node) {
return node.id;
}
@Override
public boolean nodeHasPredecessor(AcmeNode node) {
return node.id > 0;
}
@Override
public void nodeProperties(
AcmeGraph graph, AcmeNode node, Map<String, ? super Object> properties
) {
properties.put("id", node.id);
}
@Override
public AcmeNodeType nodeClass(Object obj) {
return obj instanceof AcmeNodeType ? (AcmeNodeType) obj : null;
}
@Override
public AcmeNode node(Object obj) {
return obj instanceof AcmeNode ? (AcmeNode) obj : null;
}
@Override
public AcmeNodeType classForNode(AcmeNode node) {
// we have only one type of nodes
return AcmeNodeType.STANDARD;
}
@Override
public String nameTemplate(AcmeNodeType nodeClass) {
return "Acme ({p#id})";
}
@Override
public Object nodeClassType(AcmeNodeType nodeClass) {
return nodeClass.getClass();
}
@Override
public AcmePorts portInputs(AcmeNodeType nodeClass) {
return AcmePorts.INPUT;
}
@Override
public AcmePorts portOutputs(AcmeNodeType nodeClass) {
return AcmePorts.OUTPUT;
}
@Override
public int portSize(AcmePorts port) {
return port == AcmePorts.OUTPUT ? 1 : 0;
}
@Override
public boolean edgeDirect(AcmePorts port, int index) {
return false;
}
@Override
public String edgeName(AcmePorts port, int index) {
return port.name();
}
@Override
public Object edgeType(AcmePorts port, int index) {
return port;
}
@Override
public Collection<? extends AcmeNode> edgeNodes(
AcmeGraph graph, AcmeNode node, AcmePorts port, int index
) {
if (port == AcmePorts.OUTPUT) {
return node.outgoing.targets;
}
return null;
}
}
GraphStructure
interface defines the set of operations
that are needed by the graph protocol to encode a graph into the IGV expected
format. The graph structure is implemented as a so called
singletonizer API pattern: there is no
need to change your data structures or implement some special interfaces - everything needed is
provided by implementing the GraphStructure
operations.
The next step is to turn this graph structure into an instance of
GraphOutput
. To do so use the associated
builder
just like shown in the following
method:
static GraphOutput<AcmeGraph, ?> buildOutput(WritableByteChannel channel)
throws IOException {
return GraphOutput.newBuilder(acmeGraphStructure()).
// use the latest version; currently 6.0
protocolVersion(6, 0).
build(channel);
}
FileChannel
and dump the data into a file
(preferrably with .bgv
extension). The other is to open a socket to port
4445
(the default port IGV listens to) and dump the data there. Here is an
example:
static void dump(File toFile) throws IOException {
try (
FileChannel ch = new FileOutputStream(toFile).getChannel();
GraphOutput<AcmeGraph, ?> output = buildOutput(ch);
) {
AcmeNode root = new AcmeNode(0);
AcmeNode n1 = new AcmeNode(1);
AcmeNode n2 = new AcmeNode(2);
AcmeNode n3 = new AcmeNode(3);
root.linkTo(n1);
root.linkTo(n2);
n1.linkTo(n3);
n2.linkTo(n3);
AcmeGraph diamondGraph = new AcmeGraph(root);
output.beginGroup(diamondGraph, "Diamond", "dia", null, 0, null);
output.print(diamondGraph, null, 0, "Diamond graph #%d", 1);
output.endGroup();
}
}
dump
method with pointer to file diamond.bgv
and then you can open the
file in IGV. The result will look like this:
You can verify the behavior directly in the IGV by downloading diamond.bgv file generated from the above diamond structure graph.
The primary IGV focus is on graphs used by the compiler. As such they aren't plain graphs, but contain various compiler oriented attributes:
- code blocks information
- method and fields information
- Advanced support for recognizing types
GraphBlocks
,
GraphElements
and
GraphTypes
) are optional - they don't have to be provided. As
such they can be specified via GraphOutput.Builder
instance
methods, which may, but need not be called at all. Here is an example:
static GraphOutput<AcmeGraph, ?> buildAll(WritableByteChannel channel)
throws IOException {
GraphBlocks<AcmeGraph, AcmeBlocks, AcmeNode> graphBlocks = acmeBlocks();
GraphElements<AcmeMethod, AcmeField,
AcmeSignature, AcmeCodePosition> graphElements = acmeElements();
GraphTypes graphTypes = acmeTypes();
return GraphOutput.newBuilder(acmeGraphStructure()).
protocolVersion(6, 0).
blocks(graphBlocks).
elements(graphElements).
types(graphTypes).
build(channel);
}
-
ClassDescriptionGraphBlocks<G,
B, N> Special support for dealing with blocks.GraphElements<M,F, S, P> Representation of methods, fields, their signatures and code locations.GraphLocations<M,P, L> Provides source location information about compiled code.GraphOutput<G,M> Instance of output to dump informations about a compiler compilations.GraphOutput.Builder<G,N, M> Builder to configure and create an instance ofGraphOutput
.GraphStructure<G,N, C, P> Interface that defines structure of a compiler graph.Special support for dealing with enums.