/*
* Copyright (c) 2013-2017 Chris Newland.
* Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
* Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
*/
package org.adoptopenjdk.jitwatch.ui.compilechain;
import static org.adoptopenjdk.jitwatch.util.UserInterfaceUtil.fix;
import org.adoptopenjdk.jitwatch.chain.CompileNode;
import org.adoptopenjdk.jitwatch.model.IMetaMember;
import org.adoptopenjdk.jitwatch.ui.main.IStageAccessProxy;
import org.adoptopenjdk.jitwatch.ui.main.JITWatchUI;
import org.adoptopenjdk.jitwatch.util.UserInterfaceUtil;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class CompileChainStage extends Stage
{
private ScrollPane scrollPane;
private Pane pane;
private IStageAccessProxy stageAccess;
private CompileNode rootNode;
private static final double X_OFFSET = 16;
private static final double Y_OFFSET = 16;
private double y = Y_OFFSET;
private static final double X_GAP = 25;
private static final int STROKE_WIDTH = 3;
private static final double RECT_HEIGHT = 25;
private static final double RECT_Y_GAP = 16;
class PlotNode
{
public Rectangle rect;
public Text text;
}
public CompileChainStage(final IStageAccessProxy stageAccess, CompileNode root)
{
initStyle(StageStyle.DECORATED);
this.stageAccess = stageAccess;
this.rootNode = root;
scrollPane = new ScrollPane();
pane = new Pane();
scrollPane.setContent(pane);
Scene scene = UserInterfaceUtil.getScene(scrollPane, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT);
setTitle("Compile Chain: " + root.getMemberName() + " " + root.getCompilation().getSignature());
setScene(scene);
redraw();
}
private void redraw()
{
showKey();
show(rootNode, X_OFFSET, Y_OFFSET, 0);
if (rootNode.getChildren().isEmpty())
{
Text text = new Text("No method calls made by " + rootNode.getMemberName() + " were inlined or JIT compiled");
text.setX(fix(X_OFFSET));
text.setY(fix(y));
text.setStrokeWidth(1.0);
pane.getChildren().add(text);
}
}
private void showKey()
{
double keyX = scrollPane.getWidth() - 220;
double keyY = 10;
Rectangle roundedRect = new Rectangle(keyX - 20, keyY, 210, 180);
roundedRect.setArcHeight(30);
roundedRect.setArcWidth(30);
roundedRect.setStroke(Color.BLACK);
roundedRect.setFill(Color.TRANSPARENT);
pane.getChildren().add(roundedRect);
keyY += 20;
Text text = new Text("Key");
text.setX(fix(keyX + 75));
text.setY(fix(keyY));
pane.getChildren().add(text);
keyY += 15;
buildNode("Inlined", keyX, keyY, true, false, false);
keyY += 35;
buildNode("Compiled", keyX, keyY, false, true, false);
keyY += 35;
buildNode("Virtual Call", keyX, keyY, false, false, true);
keyY += 35;
buildNode("Not Compiled", keyX, keyY, false, false, false);
keyY += 35;
}
private void show(CompileNode node, double x, double parentY, int depth)
{
double lastX = x;
lastX = plotNode(node, x, parentY, depth);
y += RECT_HEIGHT + STROKE_WIDTH + RECT_Y_GAP;
parentY = y - RECT_Y_GAP;
for (CompileNode child : node.getChildren())
{
show(child, lastX, parentY, depth + 1);
}
}
private String getLabelText(CompileNode node)
{
IMetaMember member = node.getMember();
String result = null;
if (member == null)
{
result = "Unknown";
}
else if (member.isConstructor())
{
result = member.getMetaClass().getAbbreviatedFullyQualifiedName() + "()";
}
else
{
result = member.getAbbreviatedFullyQualifiedMemberName() + "()";
}
return result;
}
private double plotNode(final CompileNode node, final double x, final double parentY, final int depth)
{
String labelText = getLabelText(node);
PlotNode plotNode = buildNode(labelText, x, y, node.isInlined(), node.isCompiled(), node.isVirtualCall());
if (depth > 0)
{
double connectX = x - X_GAP;
double connectY = y + RECT_HEIGHT / 2;
double upLineY = y + RECT_HEIGHT / 2;
Line lineUp = new Line(connectX, upLineY, connectX, parentY);
lineUp.setStrokeWidth(STROKE_WIDTH);
pane.getChildren().add(lineUp);
Line lineLeft = new Line(connectX, connectY, x, connectY);
lineLeft.setStrokeWidth(STROKE_WIDTH);
pane.getChildren().add(lineLeft);
}
double nextX = x + plotNode.rect.getWidth() / 2;
nextX += X_GAP;
initialiseRectWithOnMouseClickedEventHandler(node, plotNode.rect);
initialiseRectWithOnMouseClickedEventHandler(node, plotNode.text);
Tooltip tip = new Tooltip(node == this.rootNode ? "Root node" : node.getTooltipText());
Tooltip.install(plotNode.rect, tip);
Tooltip.install(plotNode.text, tip);
return nextX;
}
private PlotNode buildNode(String labelText, double x, double y, boolean inlined, boolean compiled, boolean virtualCall)
{
Text text = new Text(labelText);
text.snapshot(null, null);
double textWidth = text.getLayoutBounds().getWidth();
double textHeight = text.getLayoutBounds().getHeight();
double rectWidth = textWidth + 20;
Rectangle rect = new Rectangle(x, y, rectWidth, RECT_HEIGHT);
rect.setArcWidth(16);
rect.setArcHeight(16);
text.setX(fix(x + (rectWidth / 2 - textWidth / 2)));
text.setY(fix(y + RECT_HEIGHT - STROKE_WIDTH - (RECT_HEIGHT - textHeight) / 2));
text.setStrokeWidth(0.5);
text.setFill(Color.WHITE);
text.setStroke(Color.WHITE);
rect.setStroke(Color.BLACK);
rect.setStrokeWidth(STROKE_WIDTH);
rect.setFill(getColourForCompilation(compiled, inlined, virtualCall));
pane.getChildren().add(rect);
pane.getChildren().add(text);
PlotNode result = new PlotNode();
result.rect = rect;
result.text = text;
return result;
}
private Color getColourForCompilation(boolean isCompiled, boolean isInlined, boolean isVirtual)
{
if (isInlined)
{
return Color.GREEN;
}
else if (isVirtual)
{
return Color.PURPLE;
}
else if (isCompiled)
{
return Color.RED;
}
else
{
return Color.GRAY;
}
}
private void initialiseRectWithOnMouseClickedEventHandler(final CompileNode node, Shape shape)
{
shape.setOnMouseClicked(new EventHandler<MouseEvent>()
{
@Override
public void handle(MouseEvent arg0)
{
stageAccess.openTriView(node.getMember(), true);
}
});
}
}