Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
* @ritvikrao
* @lvkale
* @matthiasdiener
* @ritvikrao @lvkale
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
.gradle/
bin/projections.jar
build/

# Written by Projections next to any opened trace when a window is closed
*.projrc

# macOS
.DS_Store
181 changes: 172 additions & 9 deletions src/projections/Tools/TimeProfile/TimeProfileWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import projections.gui.GenericGraphWindow;
import projections.gui.IntervalChooserPanel;
import projections.gui.JPanelToImage;
import projections.gui.Legend;
import projections.gui.graph.Graph;
import projections.gui.MainWindow;
import projections.gui.RangeDialog;
import projections.gui.U;
Expand Down Expand Up @@ -65,6 +67,19 @@ public class TimeProfileWindow extends GenericGraphWindow
private JCheckBox showMarkersCheckBox;
private JCheckBox analyzeSlopesCheckBox;
private JCheckBox hideMouseoversCheckBox;
private JCheckBox showLegendCheckBox;
private JCheckBox labelRegionsCheckBox;

private static final int LEGEND_TOP_N = 10;
private Legend legendWindow;

// Regions must sustain at least this utilization share (percentage
// points) for at least 1/20 of the displayed intervals to get an
// on-chart label.
private static final double REGION_LABEL_MIN_PERCENT = 25.0;
private static final int REGION_LABEL_MIN_FRACTION = 20;
// EPs currently labeled on the chart, omitted from the compact legend
private Set<Integer> overlaidEPs = new HashSet<Integer>();

private long intervalSize;
private int startInterval;
Expand Down Expand Up @@ -176,13 +191,25 @@ private void createLayout() {
hideMouseoversCheckBox.setToolTipText("Disable the displaying of information associated with the data under the mouse pointer.");
hideMouseoversCheckBox.addActionListener(this);

showLegendCheckBox = new JCheckBox("Show Legend (top " + LEGEND_TOP_N + ")");
showLegendCheckBox.setSelected(false);
showLegendCheckBox.setToolTipText("Movable window listing the " + LEGEND_TOP_N + " largest activities in the displayed range; drag it over an empty part of the chart. The Legend menu shows the full list.");
showLegendCheckBox.addActionListener(this);

labelRegionsCheckBox = new JCheckBox("Label Regions");
labelRegionsCheckBox.setSelected(false);
labelRegionsCheckBox.setToolTipText("Draw entry method names directly on large single-color regions of the chart; labeled entries are then omitted from the compact legend.");
labelRegionsCheckBox.addActionListener(this);

controlPanel = new JPanel();
controlPanel.setLayout(gbl);
// Util.gblAdd(controlPanel, epSelection, gbc, 0,0, 1,1, 0,0);
Util.gblAdd(controlPanel, setRanges, gbc, 0,0, 1,1, 0,0);
Util.gblAdd(controlPanel, showMarkersCheckBox, gbc, 3,0, 1,1, 0,0);
Util.gblAdd(controlPanel, analyzeSlopesCheckBox, gbc, 4,0, 1,1, 0,0);
Util.gblAdd(controlPanel, hideMouseoversCheckBox, gbc, 5,0, 1,1, 0,0);
Util.gblAdd(controlPanel, showLegendCheckBox, gbc, 6,0, 1,1, 0,0);
Util.gblAdd(controlPanel, labelRegionsCheckBox, gbc, 7,0, 1,1, 0,0);

JPanel graphPanel = getMainPanel();
Util.gblAdd(mainPanel, graphPanel, gbc, 0,0, 1,1, 1,1);
Expand All @@ -198,11 +225,14 @@ private static class SortableEPs implements Comparable{
private double value;
private String name;
private Paint paint;
// utilization-weighted mean interval; orders phase-like EPs left-to-right
private double centroid;

private SortableEPs(double value, String name, Paint paint){
private SortableEPs(double value, String name, Paint paint, double centroid){
this.value = value;
this.name = name;
this.paint = paint;
this.centroid = centroid;
}

public int compareTo(Object o) {
Expand All @@ -218,31 +248,129 @@ else if(other.value > value)
}

private void generateLegend(boolean useShortenedNames){
makeLegend("Legend", useShortenedNames, Integer.MAX_VALUE, false, true, false, false);
}

/** Recompute and apply (or clear) the on-chart region labels. */
private void updateRegionLabels() {
overlaidEPs.clear();
if (!labelRegionsCheckBox.isSelected() || graphData == null) {
graphCanvas.setRegionLabels(null);
return;
}
List<Graph.RegionLabel> labels = new ArrayList<Graph.RegionLabel>();
int numIntervals = graphData.length;
int minRun = Math.max(2, numIntervals / REGION_LABEL_MIN_FRACTION);
// Only real entry methods get labels; Idle and Overhead colors are
// fixed and familiar to viewers.
for (int ep = 0; ep < numEPs; ep++) {
if (!stateArray[ep]) {
continue;
}
int runStart = -1;
for (int i = 0; i <= numIntervals; i++) {
boolean inRegion = i < numIntervals && graphData[i][ep] >= REGION_LABEL_MIN_PERCENT;
if (inRegion && runStart < 0) {
runStart = i;
} else if (!inRegion && runStart >= 0) {
int runEnd = i - 1;
if (runEnd - runStart + 1 >= minRun) {
int mid = (runStart + runEnd) / 2;
// Vertical center of this EP's band in the stacked bar:
// EPs below it in the stack are those with smaller index
double yBottom = 0;
for (int under = 0; under < ep; under++) {
if (stateArray[under]) {
yBottom += graphData[mid][under];
}
}
labels.add(new Graph.RegionLabel(runStart, runEnd,
yBottom + graphData[mid][ep] / 2.0,
MainWindow.runObject[myRun].getPrettyEntryNameByIndex(ep),
MainWindow.runObject[myRun].getEPColorMap()[ep]));
overlaidEPs.add(ep);
}
runStart = -1;
}
}
}
graphCanvas.setRegionLabels(labels);
}

/** Open (or refresh) the movable top-N legend controlled by the checkbox. */
private void showLegendWindow() {
if (graphData == null) {
return;
}
java.awt.Point oldLocation = null;
if (legendWindow != null) {
oldLocation = legendWindow.getFrame().getLocation();
legendWindow.dispose();
}
legendWindow = makeLegend("Legend (top " + LEGEND_TOP_N + ")", true, LEGEND_TOP_N, true, false, true, true);
if (legendWindow == null) {
return;
}
if (oldLocation != null) {
legendWindow.getFrame().setLocation(oldLocation);
} else {
legendWindow.getFrame().setLocationRelativeTo(thisWindow);
}
// Keep the checkbox in sync if the user closes the legend window directly
legendWindow.getFrame().addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
legendWindow = null;
showLegendCheckBox.setSelected(false);
}
});
}

private void closeLegendWindow() {
if (legendWindow != null) {
Legend l = legendWindow;
legendWindow = null;
l.dispose();
}
}

private Legend makeLegend(String title, boolean useShortenedNames, int maxEntries, boolean showPercent, boolean includeIdleOverhead, boolean omitOverlaid, boolean orderByAppearance){

List<SortableEPs> l = new ArrayList<SortableEPs>();

// Accumulate data shown in graph
double[] sums = new double[numEPs+2];
double[] firstMoment = new double[numEPs+2];
double grandTotal = 0.0;
for(int i=0; i<graphData.length; i++){
for(int ep=0; ep<graphData[i].length; ep++){
sums[ep] += graphData[i][ep];
firstMoment[ep] += i * graphData[i][ep];
grandTotal += graphData[i][ep];
}
}
double[] centroid = new double[numEPs+2];
for(int ep=0; ep<numEPs+2; ep++){
centroid[ep] = sums[ep] > 0 ? firstMoment[ep] / sums[ep] : 0;
}


// Put data into list
for (int ep=0; ep<numEPs; ep++) {
if(omitOverlaid && overlaidEPs.contains(ep))
continue;
if(useShortenedNames)
l.add(new SortableEPs(sums[ep], MainWindow.runObject[myRun].getPrettyEntryNameByIndex(ep), MainWindow.runObject[myRun].getEPColorMap()[ep]));
l.add(new SortableEPs(sums[ep], MainWindow.runObject[myRun].getPrettyEntryNameByIndex(ep), MainWindow.runObject[myRun].getEPColorMap()[ep], centroid[ep]));
else
l.add(new SortableEPs(sums[ep], MainWindow.runObject[myRun].getEntryNameByIndex(ep), MainWindow.runObject[myRun].getEPColorMap()[ep]));
l.add(new SortableEPs(sums[ep], MainWindow.runObject[myRun].getEntryNameByIndex(ep), MainWindow.runObject[myRun].getEPColorMap()[ep], centroid[ep]));

}

l.add(new SortableEPs(sums[numEPs], "Overhead", MainWindow.runObject[myRun].getOverheadColor()));
l.add(new SortableEPs(sums[numEPs+1], "Idle", MainWindow.runObject[myRun].getIdleColor()));
// Idle (white) and Overhead (black) use fixed colors familiar to all
// viewers, so the compact legend omits them to save slots.
if (includeIdleOverhead) {
l.add(new SortableEPs(sums[numEPs], "Overhead", MainWindow.runObject[myRun].getOverheadColor(), centroid[numEPs]));
l.add(new SortableEPs(sums[numEPs+1], "Idle", MainWindow.runObject[myRun].getIdleColor(), centroid[numEPs+1]));
}


// sort list
Expand All @@ -253,18 +381,38 @@ private void generateLegend(boolean useShortenedNames){
List<String> names = new ArrayList<String>();
List<Paint> paints = new ArrayList<Paint>();

List<SortableEPs> selected = new ArrayList<SortableEPs>();
Iterator<SortableEPs> iter = l.iterator();
while(iter.hasNext()){
while(iter.hasNext() && selected.size() < maxEntries){
SortableEPs s = iter.next();
if(s.value > grandTotal * 0.005){
names.add(s.name);
paints.add(s.paint);
selected.add(s);
}
}

if(orderByAppearance){
// Left-to-right chart order for phase-like entry methods
Collections.sort(selected, new Comparator<SortableEPs>() {
public int compare(SortableEPs a, SortableEPs b) {
return Double.compare(a.centroid, b.centroid);
}
});
}

for(SortableEPs s : selected){
if(showPercent)
names.add(String.format("%.1f%% %s", s.value * 100.0 / grandTotal, s.name));
else
names.add(s.name);
paints.add(s.paint);
}

if (names.isEmpty()) {
return null;
}

// Display the legend
new Legend("Legend", names, paints);
return new Legend(title, names, paints);

}

Expand Down Expand Up @@ -506,6 +654,10 @@ else if( MainWindow.runObject[myRun].hasSumFiles()){
}
public void done() {
setOutputGraphData();
updateRegionLabels();
if (showLegendCheckBox.isSelected()) {
showLegendWindow();
}
thisWindow.setVisible(true);
}
};
Expand Down Expand Up @@ -651,6 +803,17 @@ public void actionPerformed(ActionEvent e) {
graphCanvas.showMarkers(showMarkersCheckBox.isSelected());
} else if (e.getSource() == hideMouseoversCheckBox) {
graphCanvas.showBubble(! hideMouseoversCheckBox.isSelected());
} else if (e.getSource() == showLegendCheckBox) {
if (showLegendCheckBox.isSelected()) {
showLegendWindow();
} else {
closeLegendWindow();
}
} else if (e.getSource() == labelRegionsCheckBox) {
updateRegionLabels();
if (showLegendCheckBox.isSelected()) {
showLegendWindow(); // re-filter against the labeled EPs
}
} else if (e.getSource() == setRanges) {
showDialog();
} else if(e.getSource() == mDisplayLegend){
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package projections.Tools.TimeProfile;
package projections.gui;



Expand All @@ -15,12 +15,10 @@
import javax.swing.JFrame;
import javax.swing.JLabel;

import projections.gui.JPanelToImage;
import projections.gui.MainWindow;

/** Display a legend in a new window (clickable to save image to file) */
class Legend implements MouseListener {
public class Legend implements MouseListener {
private BufferedImage image;
private JFrame frame;
private Paint fgColor;
private Paint bgColor;

Expand All @@ -34,7 +32,7 @@ class Legend implements MouseListener {
private Font namesFont;
private Font legendFont;

Legend(String title, List<String> names, List<Paint> paints){
public Legend(String title, List<String> names, List<Paint> paints){
this.names = names;

namesFont = new Font("SansSerif", Font.PLAIN, fontSizeNames() );
Expand Down Expand Up @@ -87,19 +85,28 @@ class Legend implements MouseListener {

// Display the thing
ImageIcon imageIcon = new ImageIcon(image);
JFrame f = new JFrame();
JFrame f = new JFrame(title);
JLabel l = new JLabel(imageIcon);


l.addMouseListener(this);
f.getContentPane().add(l);
f.pack();
f.setVisible(true);
frame = f;

g.dispose();

}

public JFrame getFrame() {
return frame;
}

public void dispose() {
frame.dispose();
}

private int fontSizeLegend(){
return 30;
}
Expand Down
Loading
Loading