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
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@
import org.jfree.data.category.DefaultCategoryDataset;

import projections.analysis.TimedProgressThreadExecutor;
import projections.gui.ChooseEntriesWindow;
import projections.gui.ColorUpdateNotifier;
import projections.gui.EntryMethodVisibility;
import projections.gui.ProjectionsWindow;
import projections.gui.MainWindow;
import projections.gui.RangeDialog;
import projections.gui.U;
import projections.gui.JPanelToImage;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.JOptionPane;
import javax.swing.JMenuItem;
Expand All @@ -24,6 +30,7 @@
import java.awt.Color;
import java.awt.Container;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

Expand All @@ -35,7 +42,7 @@

public class MessageSizeEvolutionWindow
extends ProjectionsWindow
implements ActionListener {
implements ActionListener, EntryMethodVisibility, ColorUpdateNotifier {

private int myRun = 0;

Expand All @@ -48,10 +55,20 @@ public class MessageSizeEvolutionWindow
private JFreeChart chart;
private ChartPanel chartPanel;

// counts is indexed by msg bin index, then time bin index followed by ep id.
// NOTE: bin indices need not be of the same size
// epCounts is indexed by time bin, then msg bin, then entry method id.
// The extra ep slot at the end holds events whose entry id is outside
// the sts table; it is always displayed.
private int[][][] epCounts;
private int numEPs;
private boolean[] epVisible;

// What the chart shows: epCounts summed over the visible entry methods,
// indexed by time bin then msg bin.
private int[][] counts;

private JButton selectEntriesButton;
private JLabel entriesShownLabel;

private int timeNumBins;
private long timeBinSize;
private long startTime;
Expand Down Expand Up @@ -108,12 +125,16 @@ protected Object doInBackground() throws Exception {
System.out.println("You cannot enter a starting bin size less than 1.");
return null;
}
counts = new int[timeNumBins + 1][msgNumBins + 1];
numEPs = MainWindow.runObject[myRun].getNumUserEntries();
epVisible = new boolean[numEPs];
for (int ep = 0; ep < numEPs; ep++)
epVisible[ep] = true;
epCounts = new int[timeNumBins + 1][msgNumBins + 1][numEPs + 1];

// Create a list of worker threads
List<Runnable> readyReaders = new ArrayList<Runnable>(dialog.getSelectedProcessors().size());
for (Integer nextPe : dialog.getSelectedProcessors()) {
readyReaders.add(new ThreadedFileReader(counts, nextPe, startTime, dialog.getEndTime(), timeNumBins, timeBinSize, msgNumBins, msgBinSize, msgMinBinSize, msgLogScale, msgCreationEvent));
readyReaders.add(new ThreadedFileReader(epCounts, nextPe, startTime, dialog.getEndTime(), timeNumBins, timeBinSize, msgNumBins, msgBinSize, msgMinBinSize, msgLogScale, msgCreationEvent));
}

// Determine a component to show the progress bar with
Expand All @@ -133,16 +154,110 @@ else if (MainWindow.runObject[myRun].guiRoot != null && MainWindow.runObject[myR
}

protected void done() {
createPlot();
// epCounts stays null when the bin sizes failed validation
if (epCounts != null)
createPlot();
}
};
worker.execute();
}
}

/** Sum the per entry method counts over the entry methods currently
* switched on, into the 2D array the chart is built from. The catch-all
* slot for events outside the sts table is always included. */
private void aggregateVisibleCounts() {
counts = new int[timeNumBins + 1][msgNumBins + 1];
for (int i = 0; i < epCounts.length; i++) {
for (int j = 0; j < epCounts[i].length; j++) {
for (int ep = 0; ep <= numEPs; ep++) {
if (ep == numEPs || epVisible[ep])
counts[i][j] += epCounts[i][j][ep];
}
}
}
}

private void createPlot() {
thisWindow.setVisible(false);

aggregateVisibleCounts();
buildChart();

Container windowPane = thisWindow.getContentPane();
windowPane.removeAll();
windowPane.setLayout(new BorderLayout());
windowPane.add(chartPanel, BorderLayout.CENTER);

selectEntriesButton = new JButton("Select Entry Methods");
selectEntriesButton.addActionListener(this);
entriesShownLabel = new JLabel();
updateEntriesShownLabel();
JPanel controlPanel = new JPanel(new FlowLayout());
controlPanel.add(selectEntriesButton);
controlPanel.add(entriesShownLabel);
windowPane.add(controlPanel, BorderLayout.SOUTH);

thisWindow.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
fitChartToWindow();
}
});

thisWindow.pack();
thisWindow.setVisible(true);
}

/** Rebuild the chart in place after an entry method was switched on or
* off, leaving the window itself (and its size) alone. */
private void redrawPlot() {
aggregateVisibleCounts();
Container windowPane = thisWindow.getContentPane();
windowPane.remove(chartPanel);
buildChart();
windowPane.add(chartPanel, BorderLayout.CENTER);
fitChartToWindow();
updateEntriesShownLabel();
windowPane.revalidate();
windowPane.repaint();
}

/** The chart draws at the window's size rather than scaling a
* fixed-size rendering. */
private void fitChartToWindow() {
chartPanel.setMaximumDrawHeight(thisWindow.getHeight());
chartPanel.setMaximumDrawWidth(thisWindow.getWidth());
chartPanel.setMinimumDrawWidth(thisWindow.getWidth());
chartPanel.setMinimumDrawHeight(thisWindow.getHeight());
}

private void updateEntriesShownLabel() {
int present = 0, shown = 0;
long[] totals = epMessageTotals();
for (int ep = 0; ep < numEPs; ep++) {
if (totals[ep] > 0) {
present++;
if (epVisible[ep])
shown++;
}
}
entriesShownLabel.setText(shown == present ?
"(all " + present + " entry methods with messages shown)" :
"(" + shown + " of " + present + " entry methods with messages shown)");
}

/** Messages counted for each entry method over the whole loaded range. */
private long[] epMessageTotals() {
long[] totals = new long[numEPs];
for (int i = 0; i < epCounts.length; i++)
for (int j = 0; j < epCounts[i].length; j++)
for (int ep = 0; ep < numEPs; ep++)
totals[ep] += epCounts[i][j][ep];
return totals;
}

private void buildChart() {
int[][] heatMap = new int[counts.length][];
double maxVal = Double.MIN_VALUE, minVal = Double.MAX_VALUE;
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
Expand Down Expand Up @@ -175,6 +290,13 @@ private void createPlot() {
}
}

// Every visible cell can be zero once entry methods are switched off
// (Hide All, or hiding the one entry method that carried the traffic);
// the renderer's paint scale needs a positive maximum, so give it one
// and the chart comes out uniformly white instead of throwing.
if (maxVal <= 0)
maxVal = 1;

chart = ChartFactory.createStackedBarChart(
"Message Size Evolution Chart",
"Message Size",
Expand All @@ -197,24 +319,6 @@ private void createPlot() {

chartPanel = new ChartPanel(chart);
chart.setBackgroundPaint(Color.LIGHT_GRAY);

Container windowPane = thisWindow.getContentPane();
windowPane.removeAll();
windowPane.setLayout(new BorderLayout());
windowPane.add(chartPanel, BorderLayout.CENTER);

thisWindow.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
chartPanel.setMaximumDrawHeight(e.getComponent().getHeight());
chartPanel.setMaximumDrawWidth(e.getComponent().getWidth());
chartPanel.setMinimumDrawWidth(e.getComponent().getWidth());
chartPanel.setMinimumDrawHeight(e.getComponent().getHeight());
}
});

thisWindow.pack();
thisWindow.setVisible(true);
}

protected void createMenus() {
Expand Down Expand Up @@ -249,5 +353,64 @@ public void actionPerformed(ActionEvent ae) {
else if (c == mSaveScreenshot && chartPanel != null)
JPanelToImage.saveToFileChooserSelection(chart, chartPanel.getWidth(), chartPanel.getHeight(),
"Save Evolution Chart", "./MessageSizeEvolution.pdf");
else if (c == selectEntriesButton)
new ChooseEntriesWindow(this, true, this);
}

// ---- EntryMethodVisibility: the shared entry method chooser drives
// ---- which entry methods' messages the chart is built from.

/** Messages counted per entry method, so the chooser lists the busiest
* first with the counts in a column of their own. */
public int[] getEntriesArray() {
long[] totals = epMessageTotals();
int[] entries = new int[numEPs];
for (int ep = 0; ep < numEPs; ep++)
entries[ep] = (totals[ep] >= Integer.MAX_VALUE) ?
Integer.MAX_VALUE : (int) totals[ep];
return entries;
}

public boolean sortEntriesByCount() {
return true;
}

public boolean hasEntryList() {
return true;
}

/** Idle and overhead are times, not messages, so they have no place here. */
public boolean handleIdleOverhead() {
return false;
}

public boolean entryIsVisibleID(Integer id) {
return !inRange(id) || epVisible[id];
}

public void makeEntryVisibleID(Integer id) {
if (inRange(id))
epVisible[id] = true;
}

public void makeEntryInvisibleID(Integer id) {
if (inRange(id))
epVisible[id] = false;
}

/** Idle and overhead come through as negative ids from the shared dialog;
* this tool does not show them. */
private boolean inRange(Integer id) {
return id != null && id >= 0 && id < numEPs;
}

public void displayMustBeRedrawn() {
if (epCounts != null && chartPanel != null)
redrawPlot();
}

/** The chart is a heat map, not colored by entry method, so a color
* change from the chooser has nothing to repaint here. */
public void colorsHaveChanged() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class ThreadedFileReader
private boolean msgLogScale;
private boolean msgCreationEvent;

private int[][]outputCounts;
private int[][][] outputCounts;

protected ThreadedFileReader(int[][] outputCounts, int pe, long startTime, long endTime, int timeNumBins, long timeBinSize, int msgNumBins, long msgBinSize, long msgMinBinSize, boolean msgLogScale, boolean msgCreationEvent) {
protected ThreadedFileReader(int[][][] outputCounts, int pe, long startTime, long endTime, int timeNumBins, long timeBinSize, int msgNumBins, long msgBinSize, long msgMinBinSize, boolean msgLogScale, boolean msgCreationEvent) {
this.pe = pe;
this.startTime = startTime;
this.endTime = endTime;
Expand All @@ -40,12 +40,14 @@ protected ThreadedFileReader(int[][] outputCounts, int pe, long startTime, long
}

public void run() {
int[][] myCounts = getCounts();
int[][][] myCounts = getCounts();
// in synchronized manner accumulate into global counts:
synchronized (outputCounts) {
for (int i = 0; i < outputCounts.length; i++) {
for (int j = 0; j < outputCounts[i].length; j++) {
outputCounts[i][j] += myCounts[i][j];
for (int ep = 0; ep < outputCounts[i][j].length; ep++) {
outputCounts[i][j][ep] += myCounts[i][j][ep];
}
}
}
}
Expand All @@ -57,11 +59,16 @@ private static int log2(long value) {
return Long.SIZE - Long.numberOfLeadingZeros(value);
}

private int[][] getCounts() {
private int[][][] getCounts() {
long adjustedTime;
long adjustedSize;

int[][] countData = new int[timeNumBins + 1][msgNumBins + 1];
// Counts are kept per entry method so the window can focus the chart
// on a chosen subset without re-reading the logs. The extra slot at
// the end catches events whose entry id is outside the sts table, so
// filtering can never silently change the total.
final int numEPs = MainWindow.runObject[myRun].getNumUserEntries();
int[][][] countData = new int[timeNumBins + 1][msgNumBins + 1][numEPs + 1];

GenericLogReader reader = new GenericLogReader(pe, MainWindow.runObject[myRun].getVersion());
try {
Expand Down Expand Up @@ -90,7 +97,10 @@ private int[][] getCounts() {
int timeTargetBin = (int) (adjustedTime / timeBinSize);
msgTargetBin = Math.min(msgTargetBin, msgNumBins);
timeTargetBin = Math.min(timeTargetBin, timeNumBins);
countData[timeTargetBin][msgTargetBin] += 1;
int ep = logData.entry;
if (ep < 0 || ep >= numEPs)
ep = numEPs;
countData[timeTargetBin][msgTargetBin][ep] += 1;
}
break;
}
Expand Down
Loading