/*
* Copyright (c) 2013-2016 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.graphing;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_COMPILER;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_COMPILE_KIND;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_DECOMPILES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_LEVEL;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.TAG_NMETHOD;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.TAG_TASK_QUEUED;
import static org.adoptopenjdk.jitwatch.util.UserInterfaceUtil.fix;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.adoptopenjdk.jitwatch.model.Compilation;
import org.adoptopenjdk.jitwatch.model.IMetaMember;
import org.adoptopenjdk.jitwatch.model.JITEvent;
import org.adoptopenjdk.jitwatch.model.JITStats;
import org.adoptopenjdk.jitwatch.model.Tag;
import org.adoptopenjdk.jitwatch.ui.main.JITWatchUI;
import org.adoptopenjdk.jitwatch.util.ParseUtil;
import org.adoptopenjdk.jitwatch.util.UserInterfaceUtil;
import javafx.scene.Scene;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
public class TimeLineStage extends AbstractGraphStage
{
private IMetaMember selectedMember = null;
private int compilationIndex = 0;
private static final double MARKET_DIAMETER = 10;
private boolean labelLeft = true;
private boolean drawnQueueEvent = false;
public TimeLineStage(final JITWatchUI parent)
{
super(parent, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT, true);
StackPane root = new StackPane();
Scene scene = UserInterfaceUtil.getScene(root, width, height);
canvas.widthProperty().bind(root.widthProperty());
canvas.heightProperty().bind(root.heightProperty());
root.getChildren().add(canvas);
setTitle("Compilations Timeline");
setScene(scene);
show();
redraw();
}
@Override
public final void redraw()
{
labelLeft = true;
super.baseRedraw();
gc.setFont(STANDARD_FONT);
if (selectedMember != mainUI.getSelectedMember())
{
selectedMember = mainUI.getSelectedMember();
}
List<JITEvent> events = mainUI.getJITDataModel().getEventListCopy();
compilationIndex = 0;
if (events.size() > 0)
{
Collections.sort(events, new Comparator<JITEvent>()
{
@Override
public int compare(JITEvent e1, JITEvent e2)
{
return Long.compare(e1.getStamp(), e2.getStamp());
}
});
JITEvent firstEvent = events.get(0);
minX = firstEvent.getStamp();
Tag endOfLogTag = mainUI.getJITDataModel().getEndOfLogTag();
JITEvent lastEvent = events.get(events.size() - 1);
if (endOfLogTag != null)
{
maxX = getStampFromTag(endOfLogTag);
long lastEventPlusPadding = (long)(lastEvent.getStamp() * 1.1);
maxX = Math.min(maxX, lastEventPlusPadding);
}
else
{
maxX = lastEvent.getStamp();
}
minY = 0;
calculateMaxCompiles(events);
drawAxes();
drawEvents(events);
showSelectedMemberLabel();
}
else
{
gc.fillText("No compilation information processed", fix(10), fix(10));
}
}
private void calculateMaxCompiles(List<JITEvent> events)
{
maxY = events.size();
}
private void drawMemberEvents(List<Compilation> compilations, long stamp, double yPos)
{
if (compilationIndex >= compilations.size())
{
return;
}
Compilation compilation = compilations.get(compilationIndex);
if (!compilation.isC2N())
{
if (!drawnQueueEvent)
{
Tag tagTaskQueued = compilation.getTagTaskQueued();
if (tagTaskQueued != null)
{
long tagTime = compilation.getQueuedStamp();
if (tagTime == stamp)
{
drawMemberEvent(compilation, tagTaskQueued, stamp, yPos);
drawnQueueEvent = true;
}
}
}
Tag tagNMethod = compilation.getTagNMethod();
if (tagNMethod != null)
{
long tagTime = compilation.getCompiledStamp();
if (tagTime == stamp)
{
drawMemberEvent(compilation, tagNMethod, stamp, yPos);
compilationIndex++;
drawnQueueEvent = false;
}
}
}
}
private void drawMemberEvent(Compilation compilation, Tag tag, long stamp, double yPos)
{
long journalEventTime = ParseUtil.getStamp(tag.getAttributes());
gc.setFill(Color.BLUE);
double smX = graphGapLeft + normaliseX(journalEventTime);
double blobX = fix(smX - MARKET_DIAMETER / 2);
double blobY = fix(yPos - MARKET_DIAMETER / 2);
gc.fillOval(blobX, blobY, fix(MARKET_DIAMETER), fix(MARKET_DIAMETER));
String label = buildLabel(tag, journalEventTime, compilation);
double labelX;
double labelY;
if (labelLeft)
{
labelX = blobX - getApproximateStringWidth(label) - 16;
labelY = Math.min(blobY - getStringHeight(), graphGapTop + chartHeight - 32);
}
else
{
labelX = blobX + 16;
labelY = Math.min(blobY, graphGapTop + chartHeight - 32);
}
labelLeft = !labelLeft;
drawLabel(label, labelX, labelY, getLabelColour(tag));
}
private Color getLabelColour(Tag tag)
{
Color result = Color.WHITE;
String tagName = tag.getName();
if (TAG_NMETHOD.equals(tagName))
{
if (tag.getAttributes().containsKey(ATTR_DECOMPILES))
{
result = Color.ORANGERED;
}
else
{
result = Color.LIMEGREEN;
}
}
else if (TAG_TASK_QUEUED.equals(tagName))
{
result = Color.YELLOW;
}
return result;
}
private void showSelectedMemberLabel()
{
if (selectedMember != null)
{
gc.setFont(MEMBER_FONT);
drawLabel(selectedMember.toString(), 56, 40);
}
}
private void drawLabel(String text, double xPos, double yPos)
{
drawLabel(text, xPos, yPos, Color.WHITE);
}
private String buildLabel(Tag nextJournalEvent, long journalEventTime, Compilation compilation)
{
StringBuilder selectedItemBuilder = new StringBuilder();
String tagName = nextJournalEvent.getName();
if (TAG_TASK_QUEUED.equals(tagName))
{
selectedItemBuilder.append("Queued");
}
else
{
Map<String, String> eventAttributes = nextJournalEvent.getAttributes();
// if (eventAttributes.containsKey(ATTR_DECOMPILES))
// {
// selectedItemBuilder.append("Recompiled");
// }
// else
// {
// selectedItemBuilder.append("Compiled");
// }
String compiler = eventAttributes.get(ATTR_COMPILER);
if (compiler == null)
{
compiler = "Unknown";
}
selectedItemBuilder.append(compiler);
String compileKind = eventAttributes.get(ATTR_COMPILE_KIND);
if (compileKind != null)
{
selectedItemBuilder.append(C_SPACE).append(C_OPEN_PARENTHESES).append(compileKind.toUpperCase())
.append(C_CLOSE_PARENTHESES);
}
String level = eventAttributes.get(ATTR_LEVEL);
if (level != null)
{
selectedItemBuilder.append(" (Level ").append(level).append(C_CLOSE_PARENTHESES);
}
if (!compilation.isC2N())
{
selectedItemBuilder.append(" in ").append(compilation.getCompileTime()).append("ms");
}
}
return selectedItemBuilder.toString();
}
private void drawEvents(List<JITEvent> events)
{
Color colourMarker = Color.BLUE;
double lineWidth = 2.0;
int cumC = 0;
double lastCX = graphGapLeft + normaliseX(minX);
double lastCY = graphGapTop + normaliseY(0);
for (JITEvent event : events)
{
long stamp = event.getStamp();
cumC++;
double x = graphGapLeft + normaliseX(stamp);
double y = graphGapTop + normaliseY(cumC);
if (selectedMember != null)
{
List<Compilation> compilations = selectedMember.getCompilations();
if (!compilations.isEmpty())
{
drawMemberEvents(compilations, stamp, y);
}
}
gc.setStroke(colourMarker);
gc.setLineWidth(lineWidth);
gc.strokeLine(fix(lastCX), fix(lastCY), fix(x), fix(y));
lastCX = x;
lastCY = y;
}
continueLineToEndOfXAxis(lastCX, lastCY, colourMarker, lineWidth);
showStatsLegend(gc);
}
private void showStatsLegend(GraphicsContext gc)
{
JITStats stats = mainUI.getJITDataModel().getJITStats();
StringBuilder compiledStatsBuilder = new StringBuilder();
compiledStatsBuilder.append("Total Compilations: ").append(stats.getTotalCompiledMethods());
compiledStatsBuilder.append(" (C1: ").append(stats.getCountC1()).append(S_CLOSE_PARENTHESES);
compiledStatsBuilder.append(" (C2: ").append(stats.getCountC2()).append(S_CLOSE_PARENTHESES);
compiledStatsBuilder.append(" (C2N: ").append(stats.getCountC2N()).append(S_CLOSE_PARENTHESES);
compiledStatsBuilder.append(" (OSR: ").append(stats.getCountOSR()).append(S_CLOSE_PARENTHESES);
setStrokeForText();
gc.fillText(compiledStatsBuilder.toString(), fix(graphGapLeft), fix(2));
}
}