diff --git a/src/projections/Tools/Overview/OverviewPanel.java b/src/projections/Tools/Overview/OverviewPanel.java index a9606c71..db84755c 100644 --- a/src/projections/Tools/Overview/OverviewPanel.java +++ b/src/projections/Tools/Overview/OverviewPanel.java @@ -38,6 +38,7 @@ class OverviewPanel extends ScalePanel.Child private int myRun = 0; private int[][] entryData; // [pe][interval] + private int[] entriesPresent; // [ep] 1 if the EP appears in entryData // idleData & mergedData (for supporting - utilization for now - // the other two data formats) @@ -377,29 +378,60 @@ protected void loadData(boolean saveImage) { idleDataNormalized[i][j] = (byte) (100.0f * idleData[i][j]); } } + computeEntriesPresent(); // We default to coloring by entry method for log files colorByEntry(); } else if (MainWindow.runObject[myRun].hasSumDetailData()) { int intervalSize = (int) MainWindow.runObject[myRun].getSumDetailIntervalSize(); - startInterval = (int) startTime / intervalSize; + startInterval = (int) (startTime / intervalSize); endInterval = (int) Math.ceil(((double) endTime) / intervalSize) - 1; int numIntervals = endInterval - startInterval + 1; - utilizationDataNormalized = new int[selectedPEs.size()][numIntervals]; - idleDataNormalized = new byte[selectedPEs.size()][numIntervals]; - MainWindow.runObject[myRun].LoadGraphData(intervalSize, startInterval, endInterval, false, selectedPEs); - utilizationDataNormalized = MainWindow.runObject[myRun].getSumDetailData_PE_interval(); - idleDataNormalized = MainWindow.runObject[myRun].sumAnalyzer.getIdlePercentage(); - double scale = 100.0 / intervalSize; + // Both of these are indexed by position in selectedPEs and are + // relative to startInterval, which is what this panel displays. + // systemUsageData[SYS_CPU] is already a 0-100 percentage. + utilizationDataNormalized = MainWindow.runObject[myRun].getSystemUsageData(1); + entryData = MainWindow.runObject[myRun].getSumDetailData_PE_interval_maxEP(); - // idleDataNormalized is already in terms of percentage, so don't convert it - for (int i = 0; i < utilizationDataNormalized.length; i++) { - for (int j = 0; j < utilizationDataNormalized[i].length - 1; j++) { - utilizationDataNormalized[i][j] = (int) (scale * utilizationDataNormalized[i][j]); + // .sumd files carry no idle data, so idle has to come from the + // .sum files, which are indexed by absolute PE number and binned + // at their own interval size. + idleDataNormalized = new byte[selectedPEs.size()][numIntervals]; + byte[][] sumIdle = (MainWindow.runObject[myRun].sumAnalyzer == null) ? + null : MainWindow.runObject[myRun].sumAnalyzer.getIdlePercentage(); + long sumIntervalSize = MainWindow.runObject[myRun].getSummaryIntervalSize(); + int pIdx = 0; + for (Integer pe : selectedPEs) { + for (int j = 0; j < numIntervals; j++) { + int idle = 0; + if (sumIdle != null && pe < sumIdle.length && sumIdle[pe] != null + && sumIntervalSize > 0) { + int sumInterval = (int) + ((long)(startInterval + j) * intervalSize / sumIntervalSize); + if (sumInterval < sumIdle[pe].length) { + idle = sumIdle[pe][sumInterval]; + } + } + // The .sum idle and the .sumd per-EP times are accumulated by + // independent paths in charm's trace-summary and can overlap, + // so cap idle at whatever the entry methods leave free - the + // same reconciliation Time Profile and Usage Profile use. + int util = utilizationDataNormalized[pIdx][j]; + idleDataNormalized[pIdx][j] = + (byte) Math.max(0, Math.min(idle, 100 - util)); + + // An interval with no recorded entry method time, or one the + // .sum file says was mostly idle, is drawn as idle rather than + // getting an entry method's color. + if (entryData[pIdx][j] < 0 || idleDataNormalized[pIdx][j] > util) { + entryData[pIdx][j] = numEPs + 1; + } } + pIdx++; } + computeEntriesPresent(); // Color by utilization for sumdetail colorByUtil(); } else { @@ -409,7 +441,38 @@ protected void loadData(boolean saveImage) { } } + /** Which entry methods actually appear in the image. The color chooser + * uses this so it offers only the colors on screen instead of every + * entry method in the run. Null if no entry method data was loaded. */ + protected int[] getEntriesPresent() { + return entriesPresent; + } + + /** Scanned once per load; ChooseEntriesWindow asks for the array + * repeatedly, and on a large trace entryData is millions of cells. */ + private void computeEntriesPresent() { + if (entryData == null) { + entriesPresent = null; + return; + } + entriesPresent = new int[numEPs]; + for (int p = 0; p < entryData.length; p++) { + if (entryData[p] == null) + continue; + for (int i = 0; i < entryData[p].length; i++) { + int ep = entryData[p][i]; + if (ep >= 0 && ep < numEPs) + entriesPresent[ep] = 1; + } + } + } + protected void colorByEntry() { + if (entryData == null) { + // No per-entry-method data was loaded; stay in utilization mode + colorByUtil(); + return; + } mode = OverviewWindow.MODE_EP; applyColorMap(entryData, true); } diff --git a/src/projections/Tools/Overview/OverviewWindow.java b/src/projections/Tools/Overview/OverviewWindow.java index 403c1e13..293e4444 100644 --- a/src/projections/Tools/Overview/OverviewWindow.java +++ b/src/projections/Tools/Overview/OverviewWindow.java @@ -34,15 +34,18 @@ import projections.gui.ChooseEntriesWindow; import projections.gui.ColorMap; import projections.gui.ColorUpdateNotifier; +import projections.gui.EntryMethodVisibility; import projections.gui.MainWindow; import projections.gui.ProjectionsWindow; import projections.gui.RangeDialog; import projections.gui.ScalePanel; import projections.gui.ScaleSlider; +import projections.gui.U; import projections.gui.Util; public class OverviewWindow extends ProjectionsWindow -implements MouseListener, ActionListener, ScalePanel.StatusDisplay, ColorUpdateNotifier +implements MouseListener, ActionListener, ScalePanel.StatusDisplay, ColorUpdateNotifier, +EntryMethodVisibility { // Temporary hardcode. This variable will be assigned appropriate @@ -65,6 +68,10 @@ public class OverviewWindow extends ProjectionsWindow private ColorMap utilColorMap; + /** Time at the left edge of the display; the panel's horizontal + * coordinates are microseconds relative to it */ + private long displayStartTime; + private OverviewWindow thisWindow; @@ -117,7 +124,16 @@ private void createLayout() gbc.fill = GridBagConstraints.BOTH; Util.gblAdd(windowPane, displayPanel, gbc, 0,0, 2,1, 2,1, 1,1,1,1); scalePanel.setStatusDisplay(this); - + + // Timestamps under the horizontal tick ruler. The panel's horizontal + // coordinate is microseconds from the start of the displayed range, + // and getTicks() already follows pan and zoom. + scalePanel.setHorizontalAxisLabeler(new ScalePanel.AxisLabeler() { + public String label(double offset) { + return U.humanReadableString(displayStartTime + (long)offset, 3); + } + }); + mChooseColors = new JButton("Choose Entry Method Colors"); mChooseColors.addActionListener(this); @@ -126,7 +142,9 @@ private void createLayout() radioPanel.setLayout(new GridBagLayout()); Util.gblAdd(radioPanel, new JLabel("Color By:"), gbc, 0,0, 1,1, 1,1); - if(MainWindow.runObject[myRun].hasLogFiles()){ + // Both .log files and .sumd files carry per-entry-method data + if(MainWindow.runObject[myRun].hasLogFiles() || + MainWindow.runObject[myRun].hasSumDetailData()){ colorByEntryMethod = new JRadioButton("Entry Method"); colorByEntryMethod.setSelected(true); group.add(colorByEntryMethod); @@ -172,6 +190,7 @@ private void createLayout() private void setStlPanelData(long startTime, long endTime, SortedSet pes){ double horSize, verSize; + displayStartTime = startTime; if (pes == null) { horSize=MainWindow.runObject[myRun].getTotalTime(); verSize=MainWindow.runObject[myRun].getNumProcessors(); @@ -256,10 +275,42 @@ public void actionPerformed(ActionEvent evt) showDialog(); } } else if (evt.getSource() == mChooseColors) { - new ChooseEntriesWindow(this); + new ChooseEntriesWindow(this, false, this); } } + + /** Restrict "Choose Entry Method Colors" to the entry methods actually + * drawn (EntryMethodVisibility, consumed by ChooseEntriesWindow), and + * keep Idle and Overhead out of the list: this tool always draws them + * white and black, so picking colors for them would do nothing. */ + public int[] getEntriesArray() { + return stl.getEntriesPresent(); + } + + public boolean hasEntryList() { + return stl.getEntriesPresent() != null; + } + + public boolean handleIdleOverhead() { + return false; + } + + public boolean entryIsVisibleID(Integer id) { + return true; + } + + public void makeEntryVisibleID(Integer id) { + // visibility checkboxes are not shown for this tool's chooser + } + + public void makeEntryInvisibleID(Integer id) { + } + + public void displayMustBeRedrawn() { + repaint(); + } + public void mouseClicked(MouseEvent evt) { } @@ -286,7 +337,8 @@ public void setStatus(String msg) { /** Recieve notification that colors have been changed */ public void colorsHaveChanged(){ - if (colorByEntryMethod.isSelected()){ + // colorByEntryMethod does not exist for traces without entry method data + if (colorByEntryMethod != null && colorByEntryMethod.isSelected()){ stl.colorByEntry(); } else{ stl.colorByUtil(); diff --git a/src/projections/analysis/Analysis.java b/src/projections/analysis/Analysis.java index ee50721a..40d807a5 100644 --- a/src/projections/analysis/Analysis.java +++ b/src/projections/analysis/Analysis.java @@ -367,6 +367,9 @@ public int[][] getSumDetailData_PE_EP() { public int[][] getSumDetailData_PE_interval() { return intervalData.getSumDetailData_PE_interval(); } + public int[][] getSumDetailData_PE_interval_maxEP() { + return intervalData.getSumDetailData_PE_interval_maxEP(); + } public Color getEntryColor(int entryIdx) { if (entryIdx == IDLE_ENTRY_POINT) { diff --git a/src/projections/analysis/IntervalData.java b/src/projections/analysis/IntervalData.java index 9d2ebb28..f0d0af5e 100644 --- a/src/projections/analysis/IntervalData.java +++ b/src/projections/analysis/IntervalData.java @@ -62,6 +62,9 @@ public class IntervalData private int sumDetailData_interval_EP[][] = null; private int sumDetailData_PE_EP[][] = null; private int sumDetailData_PE_interval[][] = null; + // [pe list index][interval] - which EP used the most time, or -1 for none. + // Indexed by position in the requested processor list, like systemUsageData. + private int sumDetailData_PE_interval_maxEP[][] = null; /** * The constructor @@ -117,6 +120,7 @@ public void loadSumDetailIntervalData(long intervalSize, int intervalStart, int sumDetailData_interval_EP = new int[numIntervals][numEPs]; sumDetailData_PE_EP = new int[numPEs][numEPs]; sumDetailData_PE_interval = new int[numPEs][numIntervals]; + sumDetailData_PE_interval_maxEP = new int[processorList.size()][numIntervals]; systemUsageData = new int[3][processorList.size()][numIntervals]; int processorCount = 0; @@ -139,12 +143,20 @@ public void loadSumDetailIntervalData(long intervalSize, int intervalStart, int double[][] tempData = getData(curPe, TYPE_TIME, intervalSize, intervalStart, numIntervals); for (int i = 0; i < numIntervals; i++) { + int maxEP = -1; + double maxEPTime = 0.0; for (int ep = 0; ep < numEPs; ep++) { sumDetailData_interval_EP[i][ep] += (int) tempData[ep][i]; sumDetailData_PE_EP[curPe][ep] += (int) tempData[ep][i]; sumDetailData_PE_interval[curPe][i] += (int) tempData[ep][i]; systemUsageData[1][processorCount][i] += (int) tempData[ep][i]; + if (tempData[ep][i] > maxEPTime) { + maxEPTime = tempData[ep][i]; + maxEP = ep; + } } + // Overview colors each PE/interval cell by its dominant EP + sumDetailData_PE_interval_maxEP[processorCount][i] = maxEP; // after accumulation for systemUsageData, convert to utilization percentage (0-100) systemUsageData[1][processorCount][i] = (int) IntervalUtils.timeToUtil(systemUsageData[1][processorCount][i], intervalSize); @@ -166,6 +178,10 @@ public int[][] getSumDetailData_PE_interval() { return sumDetailData_PE_interval; } + public int[][] getSumDetailData_PE_interval_maxEP() { + return sumDetailData_PE_interval_maxEP; + } + public int[][][] getSystemUsageData() { return systemUsageData; } diff --git a/src/projections/gui/ChooseEntriesWindow.java b/src/projections/gui/ChooseEntriesWindow.java index 3713cca3..c1e1319f 100644 --- a/src/projections/gui/ChooseEntriesWindow.java +++ b/src/projections/gui/ChooseEntriesWindow.java @@ -59,8 +59,10 @@ public ChooseEntriesWindow(EntryMethodVisibility _data, boolean checkboxesVisibl private void onlyEntryMethodsInRange() { entryNames = new TreeMap(); - for (int i = 0; i < data.getEntriesArray().length; i++) { - if (MainWindow.runObject[myRun].getSts().getEntryNames().containsKey(i) && data.getEntriesArray()[i]!=0) + // asked for once: some tools scan their whole display to answer this + int[] entriesInRange = data.getEntriesArray(); + for (int i = 0; i < entriesInRange.length; i++) { + if (MainWindow.runObject[myRun].getSts().getEntryNames().containsKey(i) && entriesInRange[i]!=0) entryNames.put(i, MainWindow.runObject[myRun].getSts().getEntryNames().get(i) + "::" + MainWindow.runObject[myRun].getSts().entryChares.get(i)); @@ -207,6 +209,19 @@ else if (data!=null && data.hasEntryList()) p.add(topPanel, BorderLayout.NORTH); p.add(scroller, BorderLayout.CENTER); + // Color choices take effect as they are made; this just dismisses + // the window, which otherwise has no obvious way to be closed. + JButton done = new JButton("Done"); + done.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + JPanel donePanel = new JPanel(); + donePanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + donePanel.add(done); + p.add(donePanel, BorderLayout.SOUTH); + this.setContentPane(p); // Display it all diff --git a/src/projections/gui/ScalePanel.java b/src/projections/gui/ScalePanel.java index 41518e52..f060b61f 100644 --- a/src/projections/gui/ScalePanel.java +++ b/src/projections/gui/ScalePanel.java @@ -11,12 +11,15 @@ import java.awt.Color; import java.awt.Component; import java.awt.Dimension; +import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Panel; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; public class ScalePanel extends Panel @@ -196,8 +199,15 @@ else if (i%5==0) { //Multiples of 5 grow faster } } + /** Turns a horizontal panel coordinate into text for the tick ruler. + * Only the panel knows what its coordinates mean, so it supplies this. */ + public static interface AxisLabeler { + public String label(double panelCoordinate); + } + private Axis hor,ver; private Child child; + private AxisLabeler horLabeler; //null: draw tick marks without labels private int lC,rC,tC,bC; //The current child clip region //************ Mouse actions ******** @@ -281,9 +291,17 @@ public void paint(Graphics g) int tickSize=8;//Max. tick size; inset along bot & right edges int upSet=2;//Inset along top & left edges - + + //Tick labels, if any, take a text line below the horizontal ruler + FontMetrics fm=null; + int labelSize=0; + if (horLabeler!=null) { + fm=g.getFontMetrics(); + labelSize=fm.getHeight(); + } + //This is the clip region - lC=upSet; rC=w-tickSize; tC=upSet; bC=h-tickSize; + lC=upSet; rC=w-tickSize; tC=upSet; bC=h-tickSize-labelSize; hor.setScreenSize(rC-lC); ver.setScreenSize(bC-tC); g.setColor(Color.black); //Erase old image @@ -296,13 +314,15 @@ public void paint(Graphics g) g.drawRect(lC-1,tC-1,rC-lC+1,bC-tC+1); float []ticks; //Array of (pixel pos, length fraction, label) pairs - ticks=hor.getTicks(0,w-tickSize-upSet); - for (int t=0;t() { + public int compare(Integer a,Integer b) { + return Float.compare(ticks[b*3+1],ticks[a*3+1]); + } + }); + + boolean taken[]=new boolean[w+1]; //pixel columns already spoken for + g.setColor(Color.lightGray); + for (int t=0;tw) left=w-width; + if (left<0) continue; //label wider than the window + int from=Math.max(0,left-4), to=Math.min(w,left+width+4); + boolean room=true; + for (int x=from;x<=to && room;x++) + room=!taken[x]; + if (!room) continue; + g.drawString(label,left,baseline); + for (int x=from;x<=to;x++) + taken[x]=true; + } + g.setColor(Color.gray); + } + + /** Supply a labeler to get timestamps (or whatever the panel coordinates + * mean) written under the horizontal tick marks. */ + public void setHorizontalAxisLabeler(AxisLabeler labeler) { + horLabeler=labeler; + repaint(); + } + //Set the initial sizes public void setScales(double hVal,double vVal) {