diff --git a/src/projections/Tools/TimeProfile/ThreadedFileReader.java b/src/projections/Tools/TimeProfile/ThreadedFileReader.java index 03fd39a6..efd8655a 100644 --- a/src/projections/Tools/TimeProfile/ThreadedFileReader.java +++ b/src/projections/Tools/TimeProfile/ThreadedFileReader.java @@ -8,7 +8,7 @@ import projections.gui.MainWindow; /** The reader threads for Time Profile tool. This class ought to be generalized for all the other tools needing similar functionality. */ - class ThreadedFileReader implements Runnable { + public class ThreadedFileReader implements Runnable { private int pe; // int p; // Which index am I into the flattened array of potentially sparse pe's @@ -38,7 +38,7 @@ class ThreadedFileReader implements Runnable { * @param intervalSize * @param phaseMarkers * */ - protected ThreadedFileReader(int pe, long intervalSize, int myRun, int startInterval, int endInterval, + public ThreadedFileReader(int pe, long intervalSize, int myRun, int startInterval, int endInterval, TreeMap phaseMarkers, double[][] graphData){ this.phaseMarkers = phaseMarkers; this.pe = pe; diff --git a/src/projections/Tools/TimeProfile/TimeProfileWindow.java b/src/projections/Tools/TimeProfile/TimeProfileWindow.java index 8b7622ea..3677aee4 100644 --- a/src/projections/Tools/TimeProfile/TimeProfileWindow.java +++ b/src/projections/Tools/TimeProfile/TimeProfileWindow.java @@ -23,12 +23,12 @@ import projections.analysis.ProjMain; import projections.analysis.TimedProgressThreadExecutor; import projections.gui.Clickable; -import projections.gui.GenericGraphColorer; import projections.gui.GenericGraphWindow; import projections.gui.IntervalChooserPanel; import projections.gui.JPanelToImage; import projections.gui.MainWindow; import projections.gui.RangeDialog; +import projections.gui.TimeProfileColorer; import projections.gui.U; import projections.gui.Util; @@ -513,45 +513,6 @@ public void done() { } } - - - /** A class that provides the colors for the display */ - public class TimeProfileColorer implements GenericGraphColorer { - int myRun = 0; - int outSize; - int numIntervals; - - TimeProfileColorer(int outSize, int numIntervals){ - this.outSize = outSize; - this.numIntervals = numIntervals; - } - - public Paint[] getColorMap() { - Paint[] outColors = new Paint[outSize]; - for (int i=0; i> phaseSet; + private List phaseName; + private List phaseProcs; + + private List historyStringVector; + + public PhaseHistory(String logDirectory) { + filename = logDirectory + "phases.hst"; + if(!(new File(filename)).exists()) { + phaseSet = new ArrayList>(); + phaseName = new ArrayList(); + phaseProcs = new ArrayList(); + historyStringVector = new ArrayList(); + } else { + try { + loadPhases(); + historyStringVector = new ArrayList(); + for(int i = 0; i < phaseSet.size(); i++) + historyStringVector.add(getPhaseConfigString(i)); + } catch (IOException e) { + System.err.println("Error: " + e.toString()); + } + } + } + + private void loadPhases() throws IOException { + phaseSet = new ArrayList>(); + phaseName = new ArrayList(); + phaseProcs = new ArrayList(); + numEntries = 0; + + String line; + StringTokenizer st; + BufferedReader reader = new BufferedReader(new FileReader(filename)); + + while((line = reader.readLine()) != null) { + st = new StringTokenizer(line); + String s1 = st.nextToken(); + if(s1.equals("ENTRY")) { + if(numEntries >= MAX_ENTRIES) + throw new IOException("Phase history overflow!"); + List currList = new ArrayList(); + while(st.hasMoreTokens()) + currList.add(new Pair(Long.valueOf(st.nextToken()), Long.valueOf(st.nextToken()))); + phaseSet.add(currList); + numEntries++; + } else if(s1.equals("NAMEENTRY")) { + if(st.hasMoreTokens()) + phaseName.add(st.nextToken()); + else + // creates a fake name, cancel, to preserve alignment and prevent issues + phaseName.add("cancel"); + } else if(s1.equals("PROCENTRY")) { + StringBuilder procs = new StringBuilder(); + while(st.hasMoreTokens()) { + procs.append(st.nextToken()); + } + phaseProcs.add(procs.toString()); + } + } + reader.close(); + } + + public void save() throws IOException { + PrintWriter writer = new PrintWriter(new FileWriter(filename), true); + + for(int i = 0; i < numEntries; i++) { + writer.print("ENTRY "); + for(Pair o : phaseSet.get(i)) { + writer.print(o.getStart()); + writer.print(" "); + writer.print(o.getEnd()); + writer.print(" "); + } + writer.println(); + writer.print("NAMEENTRY "); + if(phaseName.size() > i) + writer.println(phaseName.get(i)); + else + writer.println("cancel"); + writer.print("PROCENTRY "); + if(phaseProcs.size() > i) + writer.println(phaseProcs.get(i)); + } + } + + public void add(List list, String name, String procs) { + if(numEntries == MAX_ENTRIES) { + phaseSet.remove(MAX_ENTRIES - 1); + if(phaseName.size() == MAX_ENTRIES) + phaseName.remove(MAX_ENTRIES - 1); + if(phaseProcs.size() == MAX_ENTRIES) + phaseProcs.remove(MAX_ENTRIES - 1); + numEntries--; + } + + if(phaseName == null) + phaseName = new ArrayList(); + phaseName.add(0, name); + phaseSet.add(0, clonePairList(list)); + phaseProcs.add(0, procs); + numEntries++; + } + + public void update(int index, List list, String name, String procs) { + if (index < 0 || index >= numEntries) { + System.err.println("Internal Error: Attempt to update " + + "invalid index " + + index + ". Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + phaseSet.remove(index); + phaseSet.add(index, clonePairList(list)); + + if(phaseProcs != null && phaseProcs.size() > index) { + phaseProcs.remove(index); + phaseProcs.add(index, procs); + } + + if(name != null && phaseName != null && phaseName.size() > index) { + phaseName.remove(index); + phaseName.add(index, name); + } + } + + public void remove(int index) { + if (index < 0 || index >= numEntries) { + System.err.println("Internal Error: Attempt to remove " + + "invalid index " + + index + ". Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + + phaseSet.remove(index); + if(phaseName != null && phaseName.size() > index) + phaseName.remove(index); + if(phaseProcs != null && phaseProcs.size() > index) + phaseProcs.remove(index); + numEntries--; + } + + private List clonePairList(List list) { + List newList = new ArrayList(list.size()); + newList.addAll(list); + return newList; + } + + public List getHistoryStrings() { + return historyStringVector; + } + + public String getProcRange(int index) { + if(phaseProcs == null || phaseProcs.size() <= index || index < 0) + return null; + return phaseProcs.get(index); + } + + public String getPhaseConfigName(int index) { + if(phaseName == null || phaseName.size() <= index || index < 0) + return null; + return phaseName.get(index); + } + + private String getPhaseConfigString(int index) { + if(index < 0 || index >= numEntries) { + System.err.println("Internal Error: Requested phase config string for history index " + + index + " is invalid. Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + + List list = phaseSet.get(index); + StringBuilder historyString = new StringBuilder(); + String minVal = U.humanReadableString(getStartValue(index)), maxVal = U.humanReadableString(getEndValue(index)); + historyString.append(list.size()).append(" Phase(s) "); + if(minVal.indexOf('.') == -1 || minVal.length() - minVal.indexOf('.') <= 2) + historyString.append(minVal); + else if(minVal.length() >= 5) + historyString.append(minVal.substring(0, Math.max(5, minVal.indexOf('.') + 2))); + else + historyString.append(minVal.substring(0, minVal.indexOf('.') + 2)); + historyString.append(" to "); + if(maxVal.indexOf('.') == -1 || maxVal.length() - minVal.indexOf('.') <= 2) + historyString.append(maxVal); + else if(maxVal.length() >= 5) + historyString.append(maxVal.substring(0, Math.max(5, maxVal.indexOf('.') + 2))); + else + historyString.append(maxVal.substring(0, maxVal.indexOf('.') + 2)); + if(phaseProcs != null && phaseProcs.size() != 0 && phaseProcs.size() > index) { + String temp = phaseProcs.get(index); + if(temp.length() > 10) + historyString + .append(" Proc(s): ") + .append(temp.substring(0, 10)) + .append("..."); + else + historyString + .append(" Proc(s): ") + .append(temp); + } + if(phaseName != null && phaseName.size() != 0 && phaseName.get(index) != null && !phaseName.get(index).equals("cancel")) { + String temp = phaseName.get(index); + if(temp.length() > 10) + historyString + .append(" (") + .append(temp.substring(0, 10)) + .append("...)"); + else + historyString + .append(" (") + .append(temp) + .append(")"); + } + return historyString.toString(); + } + + public String getPhaseString(int index, int phaseIndex) { + if(index < 0 || phaseIndex < 0 || index >= numEntries || phaseIndex >= phaseSet.get(index).size()) { + System.err.println("Internal Error: Requested phase string for history index " + + index + " and phase index " + + phaseIndex + " is invalid. Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + + Pair curr = phaseSet.get(index).get(phaseIndex); + StringBuilder phaseString = new StringBuilder(); + phaseString + .append(U.humanReadableString(curr.getStart())) + .append(" to ") + .append(U.humanReadableString(curr.getEnd())); + if(phaseProcs != null && phaseProcs.size() != 0 && phaseProcs.size() > index) { + String temp = phaseProcs.get(index); + if(temp.length() > 10) + phaseString + .append(" Proc(s):") + .append(temp.substring(0, 10)) + .append("..."); + else + phaseString.append(" Proc(s):").append(temp); + } + + return phaseString.toString(); + } + + public long getStartOfPhase(int index, int phaseIndex) { + if(index < 0 || phaseIndex < 0 || index >= numEntries || phaseIndex >= phaseSet.get(index).size()) { + System.err.println("Internal Error: Requested start of phase for history index " + + index + " and phase index " + + phaseIndex + " is invalid. Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + return phaseSet.get(index).get(phaseIndex).getStart(); + } + + public long getEndOfPhase(int index, int phaseIndex) { + if(index < 0 || phaseIndex < 0 || index >= numEntries || phaseIndex >= phaseSet.get(index).size()) { + System.err.println("Internal Error: Requested end of phase for history index " + + index + " and phase index " + + phaseIndex + " is invalid. Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + return phaseSet.get(index).get(phaseIndex).getEnd(); + } + + public int getNumPhases(int index) { + if(index < 0 || index >= numEntries) { + System.err.println("Internal Error: Requested number of phases for history index " + + index + " is invalid. Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + return phaseSet.get(index).size(); + } + + public long getStartValue(int index) { + if(index < 0 || index >= numEntries) { + System.err.println("Internal Error: Requested start value of history index " + + index + " is invalid. Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + List list = phaseSet.get(index); + long minVal = Long.MAX_VALUE; + for(Pair curr : list) { + if(curr.getStart() < minVal) + minVal = curr.getStart(); + } + return minVal; + } + + public long getEndValue(int index) { + if(index < 0 || index >= numEntries) { + System.err.println("Internal Error: Requested end value of history index " + + index + " is invalid. Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + List list = phaseSet.get(index); + long maxVal = Long.MIN_VALUE; + for(Pair curr : list) { + if(curr.getEnd() > maxVal) + maxVal = curr.getEnd(); + } + return maxVal; + } +} diff --git a/src/projections/analysis/RangeHistory.java b/src/projections/analysis/RangeHistory.java index d1b90f76..edfb74ea 100644 --- a/src/projections/analysis/RangeHistory.java +++ b/src/projections/analysis/RangeHistory.java @@ -44,8 +44,8 @@ public RangeHistory(String logDirectory) if (rangeProcs != null && rangeProcs.size() !=0 && rangeProcs.size() > i) { String temp = rangeProcs.get(i); - if (temp.length() > 10) historyString += " Procs:" + temp.substring(0,10)+"..."; - else historyString += " Procs:" + temp; + if (temp.length() > 10) historyString += " Proc(s):" + temp.substring(0,10)+"..."; + else historyString += " Proc(s):" + temp; } if (rangeName != null && rangeName.size() !=0 && !rangeName.get(i).equals(null) && !rangeName.get(i).equals("cancel")) { @@ -147,6 +147,35 @@ public void add(long start, long end, String name, String procs) { numEntries++; } + public void update(int index, long start, long end, String name, String procs) { + if ((index < 0) || + (index >= numEntries)) { + System.err.println("Internal Error: Attempt to update " + + "invalid index " + + index + ". Max number of " + + "histories is " + numEntries + + ". Please report to developers!"); + System.exit(-1); + } + + // remove the "same" index twice because the first remove + // has the side effect of changing the index. + rangeSet.remove(index * 2); + rangeSet.remove(index * 2); + rangeSet.add(index * 2, end); + rangeSet.add(index * 2, start); + + if(name != null && rangeName != null && rangeName.size() > index) { + rangeName.remove(index); + rangeName.add(index, name); + } + + if(procs != null && rangeProcs != null && rangeProcs.size() > index) { + rangeProcs.remove(index); + rangeProcs.add(index, procs); + } + } + public void remove(int index) { if ((index < 0) || (index >= numEntries)) { @@ -184,6 +213,12 @@ public String getProcRange(int index) { return rangeProcs.get(index); } + public String getName(int index) { + if(rangeName == null || rangeName.size() <= index || index < 0) + return null; + return rangeName.get(index); + } + public long getStartValue(int index) { if ((index < 0) || (index >= numEntries)) { diff --git a/src/projections/gui/MainMenuManager.java b/src/projections/gui/MainMenuManager.java index 78b10f00..55c7932f 100644 --- a/src/projections/gui/MainMenuManager.java +++ b/src/projections/gui/MainMenuManager.java @@ -58,6 +58,7 @@ class MainMenuManager private JMenu fileMenu; private JMenu toolMenu; private JMenu debugMenu; + private JMenu phaseRangeMenu; // private static final int NUM_STATES = 4; @@ -103,6 +104,11 @@ class MainMenuManager private JCheckBoxMenuItem perfLogMenuItem; + //The menu items for the phase/range menu + private JMenuItem addNewPhase; + private JMenuItem listPhases; + private JMenuItem listRanges; + protected MainMenuManager(JFrame parent) { this.parent = (MainWindow)parent; createMenus(); @@ -139,6 +145,10 @@ private void stateChanged(int state, int sumDetail) { methodProfileMenuItem.setEnabled(false); messageSizeEvolutionMenuItem.setEnabled(false); + addNewPhase.setEnabled(false); + listPhases.setEnabled(false); + listRanges.setEnabled(false); + break; case OPENED_SUMMARY: @@ -172,6 +182,13 @@ private void stateChanged(int state, int sumDetail) { methodProfileMenuItem.setEnabled(false); messageSizeEvolutionMenuItem.setEnabled(false); + if(sumDetail == 1) + addNewPhase.setEnabled(true); + else + addNewPhase.setEnabled(false); + listPhases.setEnabled(true); + listRanges.setEnabled(true); + break; case OPENED_FILES : @@ -213,6 +230,10 @@ private void stateChanged(int state, int sumDetail) { methodProfileMenuItem.setEnabled(true); messageSizeEvolutionMenuItem.setEnabled(true); + addNewPhase.setEnabled(true); + listPhases.setEnabled(true); + listRanges.setEnabled(true); + break; } } @@ -335,7 +356,21 @@ private void createMenus() { debugMenu.add(perfLogMenuItem); menubar.add(debugMenu); - + // Phase MENU + phaseRangeMenu = new JMenu("Phase & Range Info"); + phaseRangeMenu.setToolTipText("To add new phase configs"); + + addNewPhase = new JMenuItem("Add new Phase Config"); + addNewPhase.addActionListener(this); + listPhases = new JMenuItem("List all Phase Configs"); + listPhases.addActionListener(this); + listRanges = new JMenuItem("List all Range Entries"); + listRanges.addActionListener(this); + + phaseRangeMenu.add(addNewPhase); + phaseRangeMenu.add(listPhases); + phaseRangeMenu.add(listRanges); + menubar.add(phaseRangeMenu); parent.setJMenuBar(menubar); @@ -438,6 +473,15 @@ else if (mi == methodProfileMenuItem) else if(mi == messageSizeEvolutionMenuItem) parent.openTool(new MessageSizeEvolutionWindow(parent)); + else if(mi == addNewPhase) + parent.openTool(new PhaseWindow(parent, -1)); + + else if(mi == listPhases) + parent.openTool(new PhaseListWindow(parent)); + + else if(mi == listRanges) + parent.openTool(new RangeListWindow(parent)); + else System.out.println("ERROR: unknown menu item was selected" + mi); diff --git a/src/projections/gui/PhaseListWindow.java b/src/projections/gui/PhaseListWindow.java new file mode 100644 index 00000000..451a06dc --- /dev/null +++ b/src/projections/gui/PhaseListWindow.java @@ -0,0 +1,167 @@ +package projections.gui; + +import projections.analysis.PhaseHistory; + +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.JTextArea; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; +import javax.swing.JOptionPane; +import javax.swing.border.LineBorder; +import javax.swing.border.TitledBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.io.File; +import java.io.IOException; + +public class PhaseListWindow + extends ProjectionsWindow + implements ListSelectionListener, ActionListener { + + private static int myRun = 0; + private MainWindow mainWindow; + + private PhaseHistory history; + private DefaultListModel listModel; + + private JList phaseHistoryList; + private JTextArea phaseList; + private JButton addButton, removeButton, editButton, saveButton; + + PhaseListWindow(MainWindow mainWindow) { + super("Projections Phase List Window - " + MainWindow.runObject[myRun].getFilename() + ".sts", mainWindow); + this.mainWindow = mainWindow; + history = new PhaseHistory(MainWindow.runObject[myRun].getLogDirectory() + File.separator); + + createLayout(); + pack(); + setLocationRelativeTo(mainWindow); + setVisible(true); + } + + @SuppressWarnings("unchecked") + private void createLayout() { + GridBagConstraints gbc = new GridBagConstraints(); + GridBagLayout gbl = new GridBagLayout(); + + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(2, 2, 2, 2); + + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(gbl); + + listModel = new DefaultListModel(); + for(String o : history.getHistoryStrings()) + listModel.addElement(o); + phaseHistoryList = new JList(listModel); + phaseHistoryList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + phaseHistoryList.addListSelectionListener(this); + phaseHistoryList.setBorder(new TitledBorder(new LineBorder(Color.black), "Phase Config List")); + + phaseList = new JTextArea(5, 20); + phaseList.setBorder(new TitledBorder(new LineBorder(Color.black), "Phase List")); + phaseList.setEditable(false); + + removeButton = new JButton("Remove selected Phase Config"); + editButton = new JButton("Edit selected Phase Config"); + addButton = new JButton("Add new Phase Config"); + saveButton = new JButton("Save changes to disk"); + removeButton.addActionListener(this); + editButton.addActionListener(this); + addButton.addActionListener(this); + saveButton.addActionListener(this); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(gbl); + Util.gblAdd(buttonPanel, removeButton, + gbc, 0, 0, 1, 1, 1, 1); + Util.gblAdd(buttonPanel, editButton, + gbc, 1, 0, 1, 1, 1, 1); + Util.gblAdd(buttonPanel, addButton, + gbc, 2, 0, 1, 1, 1, 1); + Util.gblAdd(buttonPanel, saveButton, + gbc, 3, 0, 1, 1, 1, 1); + + Util.gblAdd(mainPanel, phaseHistoryList, + gbc, 0, 0, 1, 1, 1, 1); + Util.gblAdd(mainPanel, phaseList, + gbc, 1, 0, 1, 1, 1, 1); + Util.gblAdd(mainPanel, buttonPanel, + gbc, 0, 1, 2, 1, 0, 0); + + getContentPane().add(mainPanel); + } + + @Override + public void valueChanged(ListSelectionEvent listSelectionEvent) { + int selection = phaseHistoryList.getSelectedIndex(); + if(selection == -1) + return; + StringBuilder areaText = new StringBuilder(); + for(int i = 0; i < history.getNumPhases(selection); i++) + areaText + .append(history.getPhaseString(selection, i)) + .append("\n"); + phaseList.setText(areaText.toString()); + pack(); + } + + @Override + public void actionPerformed(ActionEvent ae) { + if(ae.getSource() == addButton) { + if(MainWindow.runObject[myRun].hasLogData() || MainWindow.runObject[myRun].hasSumDetailData()) { + super.close(); + new PhaseWindow(mainWindow, -1); + } else + JOptionPane.showMessageDialog(this, + "Need open files open to add new phase.", + "No open files", + JOptionPane.ERROR_MESSAGE); + } else if(ae.getSource() == removeButton) { + if(phaseHistoryList.getModel().getSize() == 0) { + JOptionPane.showMessageDialog(this, + "Need at least one phase config to be removed.", + "Empty Phase Config List", + JOptionPane.ERROR_MESSAGE); + } + else if(phaseHistoryList.isSelectionEmpty()) + JOptionPane.showMessageDialog(this, "Select a Phase Config from the list to be removed."); + else { + history.remove(phaseHistoryList.getSelectedIndex()); + listModel.removeElementAt(phaseHistoryList.getSelectedIndex()); + } + } else if(ae.getSource() == editButton) { + if(phaseHistoryList.getModel().getSize() == 0) { + JOptionPane.showMessageDialog(this, + "Need at least one phase config to be edited.", + "Empty Phase List", + JOptionPane.ERROR_MESSAGE); + } else if(phaseHistoryList.isSelectionEmpty()) + JOptionPane.showMessageDialog(this, "Select a Phase Config from the list to be edited."); + else { + super.close(); + new PhaseWindow(mainWindow, phaseHistoryList.getSelectedIndex()); + } + } else if(ae.getSource() == saveButton) { + try { + history.save(); + } catch (IOException e) { + System.out.println("Error saving history to disk: " + e.toString()); + } + } + } + + protected void showDialog() { + + } +} diff --git a/src/projections/gui/PhaseWindow.java b/src/projections/gui/PhaseWindow.java new file mode 100644 index 00000000..22b55252 --- /dev/null +++ b/src/projections/gui/PhaseWindow.java @@ -0,0 +1,621 @@ +package projections.gui; + +import projections.Tools.TimeProfile.ThreadedFileReader; +import projections.analysis.PhaseHistory; +import projections.analysis.Pair; +import projections.analysis.TimedProgressThreadExecutor; +import projections.analysis.ProjMain; +import projections.analysis.LogReader; + +import javax.swing.JPanel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JTextField; +import javax.swing.JList; +import javax.swing.DefaultListModel; +import javax.swing.JTabbedPane; +import javax.swing.ListSelectionModel; +import javax.swing.JLabel; +import javax.swing.SwingWorker; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.border.LineBorder; +import javax.swing.border.TitledBorder; + +import java.awt.Color; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.GridLayout; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; + +import java.io.File; +import java.io.IOException; + +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class PhaseWindow + extends GenericGraphWindow + implements ActionListener, Clickable { + + private final static int special = 2; + private static int myRun = 0; + + private MainWindow mainWindow; + private PhaseWindow thisWindow; + private PhaseHistory history; + private List historyList; + private String processorListString; + + private JPanel mainPanel; + private JButton removeButton, addButton, saveButton; + private JCheckBox showMarkersCheckBox; + private JCheckBox analyzeSlopesCheckBox; + private JCheckBox hideMouseoversCheckBox; + private IntervalChooserPanel intervalPanel; + private TimeTextField startTimeField; + private TimeTextField endTimeField; + private JTextField nameField; + + private JList phaseList; + private DefaultListModel listModel; + private boolean phaseListEmpty; + private int phaseListIndex; + + private int numEPs; + private boolean[] stateArray; + + private long intervalSize; + private int startInterval; + private int endInterval; + private long startTime; + private double[][] graphData; + private double[][] outputData; + private SortedSet processorList; + private TreeMap phaseMarkers = new TreeMap(); + + private boolean startFlag; + private boolean displaySlopes; + + PhaseWindow(MainWindow mainWindow, int index) { + super("Projections Phase Window - " + MainWindow.runObject[myRun].getFilename() + ".sts", mainWindow); + this.mainWindow = mainWindow; + this.phaseListIndex = index; + + numEPs = MainWindow.runObject[myRun].getNumUserEntries(); + stateArray = new boolean[numEPs + special]; + historyList = new ArrayList(); + history = new PhaseHistory(MainWindow.runObject[myRun].getLogDirectory() + File.separator); + if (phaseListIndex != -1) { + for (int i = 0; i < history.getNumPhases(phaseListIndex); i++) { + Pair curr = new Pair(history.getStartOfPhase(phaseListIndex, i), history.getEndOfPhase(phaseListIndex, i)); + historyList.add(curr); + } + } + + mainPanel = new JPanel(); + getContentPane().add(mainPanel); + + createLayout(); + pack(); + thisWindow = this; + startFlag = true; + displaySlopes = false; + thisWindow.setLocationRelativeTo(null); + showDialog(); + } + + @SuppressWarnings("unchecked") + private void createLayout() { + GridBagConstraints gbc = new GridBagConstraints(); + GridBagLayout gbl = new GridBagLayout(); + + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(2, 2, 2, 2); + mainPanel.setLayout(gbl); + + // control panel items + showMarkersCheckBox = new JCheckBox("Show Iteration/Phase Markers"); + showMarkersCheckBox.setSelected(false); + showMarkersCheckBox.setToolTipText("Draw vertical lines at time associated with any user supplied notes containing\"***\"?"); + showMarkersCheckBox.addActionListener(this); + + analyzeSlopesCheckBox = new JCheckBox("Analyze slope"); + analyzeSlopesCheckBox.setToolTipText("Select a point on the graph to measure the slope"); + analyzeSlopesCheckBox.addActionListener(this); + + hideMouseoversCheckBox = new JCheckBox("Hide Mouseovers"); + hideMouseoversCheckBox.setSelected(false); + hideMouseoversCheckBox.setToolTipText("Disable the displaying of information associated with the data under the mouse pointer."); + hideMouseoversCheckBox.addActionListener(this); + + JPanel controlPanel = new JPanel(); + controlPanel.setLayout(gbl); + controlPanel.setBorder(new LineBorder(Color.black)); + Util.gblAdd(controlPanel, showMarkersCheckBox, gbc, 0, 0, 1, 1, 0, 0); + Util.gblAdd(controlPanel, analyzeSlopesCheckBox, gbc, 1, 0, 1, 1, 0, 0); + Util.gblAdd(controlPanel, hideMouseoversCheckBox, gbc, 2, 0, 1, 1, 0, 0); + + listModel = new DefaultListModel(); + if (phaseListIndex == -1) { + phaseListEmpty = true; + listModel.addElement(""); + } else { + phaseListEmpty = false; + for (int i = 0; i < history.getNumPhases(phaseListIndex); i++) + listModel.addElement(history.getPhaseString(phaseListIndex, i)); + } + + phaseList = new JList(listModel); + phaseList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + phaseList.setBorder(new TitledBorder(new LineBorder(Color.black), "Phase List")); + removeButton = new JButton("Remove Selected Phase"); + removeButton.addActionListener(this); + removeButton.setHorizontalAlignment(JButton.CENTER); + + JPanel removePhasePanel = new JPanel(); + removePhasePanel.setLayout(gbl); + Util.gblAdd(removePhasePanel, removeButton, gbc, 0, 0, 1, 1, 0, 0); + + JLabel startTimeLabel = new JLabel("Start Time of New Phase:"); + startTimeField = new TimeTextField(" ", 12); + JLabel endTimeLabel = new JLabel("End Time of New Phase:"); + endTimeField = new TimeTextField(" ", 12); + addButton = new JButton("Add New Phase"); + addButton.addActionListener(this); + + JPanel addPhasePanel = new JPanel(); + addPhasePanel.setLayout(gbl); + addPhasePanel.setBorder(new TitledBorder(new LineBorder(Color.black), "New Phase Info")); + Util.gblAdd(addPhasePanel, startTimeLabel, gbc, 0, 0, 1, 1, 1, 1); + Util.gblAdd(addPhasePanel, startTimeField, gbc, 1, 0, 1, 1, 1, 1); + Util.gblAdd(addPhasePanel, endTimeLabel, gbc, 2, 0, 1, 1, 1, 1); + Util.gblAdd(addPhasePanel, endTimeField, gbc, 3, 0, 1, 1, 1, 1); + Util.gblAdd(addPhasePanel, addButton, gbc, 4, 0, 1, 1, 0, 0); + + JLabel nameLabel = new JLabel("Name of Phase Config:"); + nameField = new JTextField("", 12); + if (phaseListIndex != -1) { + nameLabel.setText("Rename Phase Config"); + nameField.setText(history.getPhaseConfigName(phaseListIndex)); + } + saveButton = new JButton("Save Phase Config To Disk"); + saveButton.addActionListener(this); + + JPanel savePhasePanel = new JPanel(); + savePhasePanel.setLayout(gbl); + savePhasePanel.setBorder(new LineBorder(Color.black)); + Util.gblAdd(savePhasePanel, nameLabel, gbc, 0, 1, 1, 1, 1, 1); + Util.gblAdd(savePhasePanel, nameField, gbc, 1, 1, 1, 1, 1, 1); + Util.gblAdd(savePhasePanel, saveButton, gbc, 2, 1, 1, 1, 0, 0); + + JPanel graphPanel = getMainPanel(); + Util.gblAdd(mainPanel, graphPanel, gbc, 0, 0, 1, 1, 1, 1); + Util.gblAdd(mainPanel, controlPanel, gbc, 0, 1, 1, 1, 0, 0); + Util.gblAdd(mainPanel, phaseList, gbc, 0, 2, 1, 1, 1, 0); + Util.gblAdd(mainPanel, removePhasePanel, gbc, 0, 3, 1, 1, 1, 0); + Util.gblAdd(mainPanel, addPhasePanel, gbc, 0, 4, 1, 1, 1, 0); + Util.gblAdd(mainPanel, savePhasePanel, gbc, 0, 5, 1, 1, 1, 0); + } + + public void setGraphSpecificData() { + + } + + public void showDialog() { + if (dialog == null) { + intervalPanel = new IntervalChooserPanel(); + dialog = new RangeDialog(this, "Select Range for Phase Chooser", intervalPanel, true); + } + dialog.displayDialog(); + if (!dialog.isCancelled()) { + startInterval = (int) intervalPanel.getStartInterval(); + endInterval = (int) intervalPanel.getEndInterval(); + processorList = dialog.getSelectedProcessors(); + processorListString = Util.listToString(processorList); + startTime = dialog.getStartTime(); + if (MainWindow.runObject[myRun].hasLogFiles()) { + intervalSize = intervalPanel.getIntervalSize(); + } else { + startInterval = 0; + endInterval = (int) MainWindow.runObject[myRun].getSumDetailNumIntervals() - 1; + intervalSize = (long) MainWindow.runObject[myRun].getSumDetailIntervalSize(); + } + + final SwingWorker worker = new SwingWorker() { + public Object doInBackground() { + phaseMarkers.clear(); + + int numIntervals = endInterval - startInterval + 1; + graphData = new double[numIntervals][numEPs + special]; //entry number + idle + + int numProcessors = processorList.size(); + int numUserEntries = MainWindow.runObject[myRun].getNumUserEntries(); + + if (MainWindow.runObject[myRun].hasLogFiles()) { + // Do parallel loading because we have full logs + + // Create a list of worker threads + LinkedList readyReaders = new LinkedList(); + + // Create multiple result arrays to reduce contention for accumulating + int numResultAccumulators = 8; + double[][][] graphDataAccumulators = new double[numResultAccumulators][numIntervals][numEPs + special]; + + int pIdx = 0; + for (Integer pe : processorList) { + readyReaders.add(new ThreadedFileReader(pe, intervalSize, myRun, + startInterval, endInterval, phaseMarkers, + graphDataAccumulators[pIdx % numResultAccumulators])); + pIdx++; + } + + // Determine a component to show the progress bar with + Component guiRootForProgressBar = null; + if (thisWindow != null && thisWindow.isVisible()) { + guiRootForProgressBar = thisWindow; + } else if (mainWindow != null && mainWindow.isVisible()) { + guiRootForProgressBar = mainWindow; + } else if (MainWindow.runObject[myRun].guiRoot != null && MainWindow.runObject[myRun].guiRoot.isVisible()) { + guiRootForProgressBar = MainWindow.runObject[myRun].guiRoot; + } + + // Pass this list of threads to a class that manages/runs the threads nicely + TimedProgressThreadExecutor threadManager = new TimedProgressThreadExecutor("Loading Time Profile in Parallel", readyReaders, guiRootForProgressBar, true); + threadManager.runAll(); + + // Merge resulting graphData structures together. + for (int a = 0; a < numResultAccumulators; a++) { + for (int i = 0; i < numIntervals; i++) { + for (int j = 0; j < numEPs + special; j++) { + graphData[i][j] += graphDataAccumulators[a][i][j]; + } + } + } + } else if (MainWindow.runObject[myRun].hasSumDetailFiles()) { + // Do serial file reading because all we have is the sum files + SortedSet availablePEs = + MainWindow.runObject[myRun].getValidProcessorList(ProjMain.SUMDETAIL); + MainWindow.runObject[myRun].LoadGraphData(intervalSize, 0, numIntervals - 1, false, availablePEs); + int[][] sumDetailData = MainWindow.runObject[myRun].getSumDetailData(); + + for (int i = 0; i < numIntervals; i++) { + for (int j = 0; j < numEPs; j++) { + graphData[i][j] += sumDetailData[i][j]; + } + } + } else if (MainWindow.runObject[myRun].hasSumFiles()) { + // Do serial file reading because all we have is the sum files + + // The data we use is + // systemUsageData[2][*][*] + // userEntryData[*][LogReader.TIME][*][*] + + int[][][] systemUsageData = new int[3][][]; + systemUsageData[2] = new int[numProcessors][]; + + int[][][][] userEntryData = new int[numUserEntries][][][]; + for (int n = 0; n < numUserEntries; n++) { + userEntryData[n] = new int[3][][]; + userEntryData[n][LogReader.TIME] = new int[numProcessors][]; + } + + int[][] temp = MainWindow.runObject[myRun].sumAnalyzer.getSystemUsageData(startInterval, endInterval, intervalSize); + systemUsageData[1] = new int[processorList.size()][endInterval - startInterval + 1]; + + int pIdx = 0; + for (Integer pe : processorList) { + systemUsageData[1][pIdx] = temp[pe]; + pIdx++; + } + + // Extract data and put it into the graph + for (int peIdx = 0; peIdx < numProcessors; peIdx++) { + for (int ep = 0; ep < numEPs; ep++) { + int[][] entryData = userEntryData[ep][LogReader.TIME]; + for (int interval = 0; interval < numIntervals; interval++) { + graphData[interval][ep] += entryData[peIdx][interval]; + graphData[interval][numEPs] -= entryData[peIdx][interval]; // overhead = -work time + } + } + + //YS add for idle time SYS_IDLE=2 + int[][] idleData = systemUsageData[2]; //percent + for (int interval = 0; interval < numIntervals; interval++) { + if (idleData[peIdx] != null && idleData[peIdx].length > interval) { + graphData[interval][numEPs + 1] += idleData[peIdx][interval] * 0.01 * intervalSize; + graphData[interval][numEPs] -= idleData[peIdx][interval] * 0.01 * intervalSize; //overhead = - idle time + graphData[interval][numEPs] += intervalSize; + } + } + } + }//end of summary + + // Scale raw data into percents + for (int interval = 0; interval < graphData.length; interval++) { + for (int e = 0; e < graphData[interval].length; e++) { + graphData[interval][e] = graphData[interval][e] * 100.0 / ((double) intervalSize * (double) numProcessors); + } + } + //Bilge + if (MainWindow.runObject[myRun].hasSumDetailFiles()) { + //idle time calculation for sum detail + int[] idlePercentage = MainWindow.runObject[myRun].sumAnalyzer.getTotalIdlePercentage(); + for (int i = 0; i < numIntervals; i++) { + graphData[i][numEPs + 1] = idlePercentage[i]; + } + //overhead time calculation for sum detail + for (int i = 0; i < numIntervals; i++) { + graphData[i][numEPs] = 100; + for (int j = 0; j < numEPs; j++) { + graphData[i][numEPs] -= graphData[i][j]; + } + graphData[i][numEPs] -= graphData[i][numEPs + 1]; + } + + } + + // Filter Out any bad data + for (int interval = 0; interval < graphData.length; interval++) { + boolean valid = true; + double sumForInterval = 0.0; + for (int e = 0; e < graphData[interval].length; e++) { + sumForInterval += graphData[interval][e]; + if (graphData[interval][e] < 0.0) { + valid = false; + } + } + if (sumForInterval > 105.0) { + valid = false; + } + + if (!valid) { + System.err.println("Time Profile found bad data for interval " + interval + ". The data for bad intervals will be zero-ed out. This problem is either a log file corruption issue, or a bug in Projections."); + for (int e = 0; e < graphData[interval].length; e++) { + graphData[interval][e] = 0.0; + } + } + + } + // set the exists array to accept non-zero + // entries only have initial state also + // display all existing data. Only do this + // once in the beginning + if (startFlag) { + for (int ep = 0; ep < numEPs + special; ep++) { + for (int interval = 0; interval < endInterval - startInterval + 1; interval++) { + if (graphData[interval][ep] > 0) { + stateArray[ep] = true; + break; + } + } + } + } + if (startFlag) + startFlag = false; + + return null; + } + + public void done() { + setOutputGraphData(); + thisWindow.setVisible(true); + } + }; + worker.execute(); + } + } + + private void setOutputGraphData() { + // need first pass to decide the size of the outputdata + int outSize = 0; + for (int ep = 0; ep < numEPs + special; ep++) { + if (stateArray[ep]) { + outSize++; + } + } + if (outSize > 0) { + // actually create and fill the data and color array + int numIntervals = endInterval - startInterval + 1; + outputData = new double[numIntervals][outSize]; + for (int i = 0; i < numIntervals; i++) { + int count = 0; + for (int ep = 0; ep < numEPs + special; ep++) { + if (stateArray[ep]) { + outputData[i][count] = graphData[i][ep]; + } + } + + } + + setYAxis("Percentage Utilization", "%"); + String xAxisLabel = "Time (" + U.humanReadableString(intervalSize) + " resolution)"; + setXAxis(xAxisLabel, "Time", startTime, intervalSize); + setDataSource("Time Profile", outputData, new TimeProfileColorer(outSize, numIntervals, numEPs, outputData, graphData, stateArray), thisWindow); + graphCanvas.setMarkers(phaseMarkers); + refreshGraph(); + } + } + + public String[] getPopup(int xVal, int yVal) { + if ((xVal < 0) || (yVal < 0)) { + return null; + } + + // find the ep corresponding to the yVal + int count = 0; + String epName = ""; + String epClassName = ""; + for (int ep = 0; ep < numEPs; ep++) { + if (stateArray[ep]) { + if (count++ == yVal) { + epName = MainWindow.runObject[myRun].getEntryNameByIndex(ep); + epClassName = MainWindow.runObject[myRun].getEntryChareNameByIndex(ep); + break; + } + } + } + String[] rString = new String[4]; + + rString[0] = "Time Interval: " + + U.humanReadableString((xVal + startInterval) * intervalSize) + " to " + + U.humanReadableString((xVal + startInterval + 1) * intervalSize); + rString[1] = "Chare Name: " + epClassName; + rString[2] = "Entry Method: " + epName; + rString[3] = "Execution Time = " + U.humanReadableString((long) (outputData[xVal][yVal])); + //deal with idle and overhead time + if (yVal == outputData[xVal].length - 2) { + rString[1] = ""; + rString[2] = "Overhead"; + rString[3] = "Time = " + U.humanReadableString((long) (outputData[xVal][yVal])); + } else if (yVal == outputData[xVal].length - 1) { + rString[1] = ""; + rString[2] = "Idle time"; + rString[3] = "Time = " + U.humanReadableString((long) (outputData[xVal][yVal])); + } + return rString; + } + + @SuppressWarnings("unchecked") + public void actionPerformed(ActionEvent e) { + if (e.getSource() == analyzeSlopesCheckBox) { + if (analyzeSlopesCheckBox.isSelected()) { + displaySlopes = true; + graphCanvas.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + } else { + displaySlopes = false; + graphCanvas.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + graphCanvas.clearPolynomial(); + } + } else if (e.getSource() == showMarkersCheckBox) { + graphCanvas.showMarkers(showMarkersCheckBox.isSelected()); + } else if (e.getSource() == hideMouseoversCheckBox) { + graphCanvas.showBubble(!hideMouseoversCheckBox.isSelected()); + } else if (e.getSource() instanceof JMenuItem) { + String arg = ((JMenuItem) e.getSource()).getText(); + if (arg.equals("Close")) { + close(); + } else if (arg.equals("Set Range")) { + showDialog(); + } + } else if (e.getSource() == removeButton) { + if (phaseListEmpty) { + JOptionPane.showMessageDialog(this, + "Need at least one phase to be removed.", + "Empty Phase List", + JOptionPane.ERROR_MESSAGE); + } else if (phaseList.isSelectionEmpty()) { + JOptionPane.showMessageDialog(this, "Select a Phase from the list to be removed."); + } else { + historyList.remove(phaseList.getSelectedIndex()); + listModel.removeElementAt(phaseList.getSelectedIndex()); + if (phaseList.getModel().getSize() == 0) { + listModel.addElement(""); + phaseListEmpty = true; + } + } + } else if (e.getSource() == addButton) { + long startTime = startTimeField.getValue(), endTime = endTimeField.getValue(); + if (startTime >= endTime) { + JOptionPane.showMessageDialog(this, "Start Time must be less than End Time."); + } else { + Pair curr = new Pair(startTime, endTime); + historyList.add(curr); + if (phaseListEmpty) { + listModel.removeAllElements(); + phaseListEmpty = false; + } + StringBuilder phaseString = new StringBuilder(); + phaseString + .append(U.humanReadableString(curr.getStart())) + .append(" to ") + .append(U.humanReadableString(curr.getEnd())); + if (processorListString.length() > 10) + phaseString + .append(" Procs:") + .append(processorListString.substring(0, 10)) + .append("..."); + else + phaseString.append(" Procs:").append(processorListString); + + listModel.addElement(phaseString.toString()); + } + } else if (e.getSource() == saveButton) { + if (phaseListIndex == -1 && nameField.getText().equals("")) { + JOptionPane.showMessageDialog(this, "Must specify the name of the Phase Config."); + } else { + if (phaseListIndex == -1) + history.add(historyList, nameField.getText(), processorListString); + else { + if (nameField.getText().equals("")) + history.update(phaseListIndex, historyList, null, processorListString); + else + history.update(phaseListIndex, historyList, nameField.getText(), processorListString); + } + try { + history.save(); + if (phaseListIndex == -1) { + historyList.clear(); + listModel.removeAllElements(); + listModel.addElement(""); + phaseListEmpty = true; + } else + super.close(); + } catch (IOException o) { + System.out.println("Error saving history to disk: " + o.toString()); + } + } + } + } + + private void createPolynomial(int xVal, int yVal) { + int numIntervals = endInterval - startInterval + 1; + + // We approximate the derivatives by using values from xVal - 2 to xVal + 2 + if (xVal < 2 || yVal < 0 || xVal >= numIntervals - 2) { + return; + } + + // extract the curve that sits: + // above the EP utilization + overhead + // but below the idle time + double[] nonIdle = new double[numIntervals]; + for (int i = 0; i < numIntervals; i++) { + nonIdle[i] = 0.0; + for (int ep = 0; ep < numEPs + 1; ep++) { + nonIdle[i] += graphData[i][ep]; + } + } + + // Lookup the y value on this curve for where the user clicked + double y = nonIdle[xVal]; + double slopeA = (nonIdle[xVal + 1] - nonIdle[xVal - 1]) / 2.0; + double slopeB = (nonIdle[xVal + 2] - nonIdle[xVal - 2]) / 4.0; + double slopeC = (slopeA + slopeB) / 2.0; + + // And to get some y=mx+b style coefficients, we need to do a little math: + double[] coefficients = new double[2]; + coefficients[0] = -1.0 * slopeC * xVal + y; // y intercept of line + coefficients[1] = slopeC; + + graphCanvas.addPolynomial(coefficients); + } + + public void toolMouseMovedResponse(MouseEvent e, int xVal, int yVal) { + if (displaySlopes) + createPolynomial(xVal, yVal); + } + + public void toolClickResponse(MouseEvent e, int xVal, int yVal) { + if (displaySlopes) + JPanelToImage.saveToFileChooserSelection(graphCanvas, "Save Screenshot Image", "./TimeProfileScreenshot.png"); + } +} diff --git a/src/projections/gui/RangeDialog.java b/src/projections/gui/RangeDialog.java index c58beef6..47b67a8f 100644 --- a/src/projections/gui/RangeDialog.java +++ b/src/projections/gui/RangeDialog.java @@ -21,7 +21,6 @@ import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; -import java.util.List; import java.util.SortedSet; import java.util.Vector; @@ -36,12 +35,14 @@ import javax.swing.JProgressBar; import javax.swing.SwingWorker; import javax.swing.JOptionPane; +import javax.swing.JRadioButton; +import javax.swing.ButtonGroup; import projections.analysis.EndOfLogSuccess; import projections.analysis.GenericLogReader; import projections.analysis.ProjDefs; import projections.analysis.RangeHistory; -import projections.misc.LogEntry; +import projections.analysis.PhaseHistory; /** * RangeDialogNew @@ -85,7 +86,7 @@ public final class RangeDialog extends JDialog private ProjectionsWindow parentWindow; // inheritable GUI objects - private JPanel mainPanel, historyPanel, buttonPanel, stepsPanel; + private JPanel stepsPanel; /** A JPanel containing any other input components required by the tool using this dialog box */ private RangeDialogExtensionPanel toolSpecificPanel; @@ -97,8 +98,10 @@ public final class RangeDialog extends JDialog private JPanel timePanel, processorsPanel; private JButton bOK, bCancel; - private JComboBox historyList; + private JPanel phaseChoicePanel; + private JComboBox historyList, phaseList; private JButton bAddToHistory, bRemoveFromHistory, bSaveHistory; + private JRadioButton brangeButton, bphaseButton; private JButton loadUserNotesButton; @@ -108,7 +111,8 @@ public final class RangeDialog extends JDialog // history variables - private RangeHistory history; + private RangeHistory rangeHistory; + private PhaseHistory phaseHistory; // flags private boolean layoutComplete = false; @@ -136,11 +140,13 @@ public RangeDialog(ProjectionsWindow parentWindow, String titleString, RangeDial toolSpecificPanel.setParentDialogBox(this); } - history = new RangeHistory(MainWindow.runObject[myRun].getLogDirectory() + + rangeHistory = new RangeHistory(MainWindow.runObject[myRun].getLogDirectory() + + File.separator); + phaseHistory = new PhaseHistory(MainWindow.runObject[myRun].getLogDirectory() + File.separator); this.setModal(true); dialogState = DIALOG_CANCELLED; // default state - } + } /** Called whenever any input item changes, either in this dialog box, or its possibly extended tool specific JPanel */ @@ -153,16 +159,18 @@ public void someInputChanged() { if(toolSpecificPanel != null){ toolSpecificPanel.updateFields(); } - bOK.setEnabled(true); - bAddToHistory.setEnabled(true); - bRemoveFromHistory.setEnabled(true); + if(!disableTimeRange) { + bOK.setEnabled(true); + bAddToHistory.setEnabled(true); + } } else { // System.out.println("Input is NOT valid"); bOK.setEnabled(false); bAddToHistory.setEnabled(false); - bRemoveFromHistory.setEnabled(false); } + pack(); + } public void displayDialog() { @@ -177,13 +185,13 @@ public void windowClosing(WindowEvent e) } }); - mainPanel = createMainLayout(); + JPanel mainPanel = createMainLayout(); mainPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); - historyPanel = createHistoryLayout(); + JPanel historyPanel = createHistoryLayout(); historyPanel.setBorder(BorderFactory.createEmptyBorder(15,5,5,5)); - buttonPanel = createButtonLayout(); + JPanel buttonPanel = createButtonLayout(); buttonPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); getRootPane().setDefaultButton(bOK); @@ -255,9 +263,11 @@ private void storeRangeToPersistantStorage(){ /** Load the previously used time/PE range */ private void initializeData(){ - startTimeField.setValue(MainWindow.runObject[myRun].persistantRangeData.begintime); - endTimeField.setValue(MainWindow.runObject[myRun].persistantRangeData.endtime); - processorsField.setText(Util.listToString(MainWindow.runObject[myRun].persistantRangeData.plist)); + if(!disableTimeRange) { + startTimeField.setValue(MainWindow.runObject[myRun].persistantRangeData.begintime); + endTimeField.setValue(MainWindow.runObject[myRun].persistantRangeData.endtime); + processorsField.setText(Util.listToString(MainWindow.runObject[myRun].persistantRangeData.plist)); + } } private void initializeToolSpecificData() { @@ -316,8 +326,10 @@ private JPanel createMainLayout() { totalTimeLabel = new JLabel(U.humanReadableString(MainWindow.runObject[myRun].getTotalTime()), JLabel.LEFT); if (disableTimeRange) { - startTimeField.setEnabled(false); + startTimeField.setEnabled(false); endTimeField.setEnabled(false); + startTimeField.setValue(0); + endTimeField.setValue(MainWindow.runObject[myRun].getTotalTime()); } else { // set listeners startTimeField.addActionListener(this); @@ -377,6 +389,7 @@ private JPanel createButtonLayout() { return buttonPanel; } + @SuppressWarnings("unchecked") private JPanel createHistoryLayout() { // Standard Layout behavior for all subcomponents GridBagLayout gbl = new GridBagLayout(); @@ -387,38 +400,65 @@ private JPanel createHistoryLayout() { // Default history layout JPanel historyPanel = new JPanel(); historyPanel.setLayout(gbl); - historyList = new JComboBox(history.getHistoryStrings().toArray()); + historyList = new JComboBox(rangeHistory.getHistoryStrings().toArray()); historyList.setEditable(false); historyList.setMaximumRowCount(RangeHistory.MAX_ENTRIES); historyList.setSelectedIndex(-1); // nothing selected at first - bAddToHistory = new JButton("Add to History List"); - bRemoveFromHistory = new JButton("Remove Selected History"); - bSaveHistory = new JButton("Save History to Disk"); + bAddToHistory = new JButton("Add to Range History List"); + bRemoveFromHistory = new JButton("Remove Selected Range History"); + bSaveHistory = new JButton("Save Range History to Disk"); + + ButtonGroup group = new ButtonGroup(); + brangeButton = new JRadioButton("Range History"); + bphaseButton = new JRadioButton("Phase History"); + group.add(brangeButton); + group.add(bphaseButton); + brangeButton.setSelected(true); + + JLabel phaseListLabel = new JLabel("Choose a Phase:", JLabel.LEFT); + phaseList = new JComboBox(); + phaseList.setEditable(false); + phaseChoicePanel = new JPanel(); + phaseChoicePanel.setVisible(false); + phaseChoicePanel.add(phaseListLabel); + phaseChoicePanel.add(phaseList); if (disableTimeRange) { historyList.setEnabled(false); bAddToHistory.setEnabled(false); bRemoveFromHistory.setEnabled(false); bSaveHistory.setEnabled(false); + brangeButton.setEnabled(false); + bphaseButton.setEnabled(false); + phaseList.setEnabled(false); } else { // set listeners historyList.addActionListener(this); bAddToHistory.addActionListener(this); bRemoveFromHistory.addActionListener(this); bSaveHistory.addActionListener(this); + brangeButton.addActionListener(this); + bphaseButton.addActionListener(this); + phaseList.addActionListener(this); } // layout - Util.gblAdd(historyPanel, historyList, + Util.gblAdd(historyPanel, brangeButton, gbc, 0,0, 1,1, 1,1); - Util.gblAdd(historyPanel, bSaveHistory, + Util.gblAdd(historyPanel, bphaseButton, gbc, 1,0, 1,1, 1,1); - Util.gblAdd(historyPanel, bAddToHistory, + Util.gblAdd(historyPanel, historyList, gbc, 0,1, 1,1, 1,1); - Util.gblAdd(historyPanel, bRemoveFromHistory, + Util.gblAdd(historyPanel, bSaveHistory, gbc, 1,1, 1,1, 1,1); + Util.gblAdd(historyPanel, bAddToHistory, + gbc, 0,2, 1,1, 1,1); + Util.gblAdd(historyPanel, bRemoveFromHistory, + gbc, 1,2, 1,1, 1,1); + Util.gblAdd(historyPanel, phaseChoicePanel, + gbc, 0,3, 2,1, 1,1); return historyPanel; } @@ -538,13 +578,14 @@ public int getNumSelectedProcessors(){ } + @SuppressWarnings("unchecked") public void actionPerformed(ActionEvent evt) { if (evt.getSource() == bOK) { dialogState = DIALOG_OK; setVisible(false); return; - } + } else if(evt.getSource() == bCancel){ dialogState = DIALOG_CANCELLED; @@ -560,57 +601,75 @@ else if (evt.getSource() == loadUserNotesButton){ stepsPanel.add(new JLabel("Now Loading User Notes..."), BorderLayout.CENTER); stepsPanel.add(progressBar, BorderLayout.EAST); stepsPanel.invalidate(); - pack(); determineStepsFromPEZero(); } else if (evt.getSource() == bAddToHistory) { - long start = getStartTime(); - long end = getEndTime(); - String procRange = Util.listToString(processorsField.getValue()); - boolean invalidName = true; - String s = ""; - while (invalidName) - { - s = JOptionPane.showInputDialog(null, "Enter a name for this time range," - + " or leave blank. \nDo not use spaces," + - " \"cancel\", \"ENTRY\", or \"NAMEENTRY\"."); - if (s == null) - { - s = "cancel"; - break; + if(brangeButton.isSelected()) { + long start = getStartTime(); + long end = getEndTime(); + String procRange = Util.listToString(processorsField.getValue()); + boolean invalidName = true; + String s = ""; + while (invalidName) { + s = JOptionPane.showInputDialog(null, "Enter a name for this time range," + + " or leave blank. \nDo not use spaces," + + " \"cancel\", \"ENTRY\", or \"NAMEENTRY\"."); + if (s == null) { + s = "cancel"; + break; + } + if (!s.contains(" ")) invalidName = false; + if (s.equals("ENTRY") || s.equals("NAMEENTRY") || s.equals("cancel")) invalidName = true; } - if (!s.contains(" ")) invalidName = false; - if (s.equals("ENTRY") || s.equals("NAMEENTRY") || s.equals("cancel")) invalidName=true; - } - if (!s.equals("cancel")) - { - String historyString = U.humanReadableString(start) + " to " + U.humanReadableString(end); - if (procRange.length() > 10) historyString += " Procs: " + procRange.substring(0,10)+"..."; - else historyString += " Procs: " + procRange; - if (!s.equals("")) - { - if (s.length() > 10) historyString += " (" + s.substring(0,10)+"...)"; - else historyString += " (" + s + ")"; + if (!s.equals("cancel")) { + String historyString = U.humanReadableString(start) + " to " + U.humanReadableString(end); + if (procRange.length() > 10) historyString += " Proc(s): " + procRange.substring(0, 10) + "..."; + else historyString += " Proc(s): " + procRange; + if (!s.equals("")) { + if (s.length() > 10) historyString += " (" + s.substring(0, 10) + "...)"; + else historyString += " (" + s + ")"; + } + rangeHistory.add(start, end, s, procRange); + historyList.insertItemAt(historyString, 0); + historyList.setSelectedIndex(0); + if(bphaseButton.isSelected()) + bAddToHistory.setText("Edit Phase Config Entry"); + else if(brangeButton.isSelected()) + bAddToHistory.setText("Add to Range History List"); } - history.add(start, end, s, procRange); - historyList.insertItemAt(historyString,0); - historyList.setSelectedIndex(0); + } else if(bphaseButton.isSelected()) { + dialogState = DIALOG_CANCELLED; + setVisible(false); + parentWindow.parentWindow.openTool(new PhaseWindow(parentWindow.parentWindow, historyList.getSelectedIndex())); } } else if (evt.getSource() == bRemoveFromHistory) { int selected = historyList.getSelectedIndex(); if (selected != -1) { - history.remove(selected); + if(brangeButton.isSelected()) { + rangeHistory.remove(selected); + } else if(bphaseButton.isSelected()) { + phaseHistory.remove(selected); + } historyList.setSelectedIndex(-1); historyList.removeItemAt(selected); + if(bphaseButton.isSelected()) { + phaseChoicePanel.setVisible(false); + bAddToHistory.setText("Add to Phase History List"); + } + else if(brangeButton.isSelected()) + bAddToHistory.setText("Add to Range History List"); } } else if (evt.getSource() == bSaveHistory) { try { - history.save(); + if(brangeButton.isSelected()) + rangeHistory.save(); + else if(bphaseButton.isSelected()) + phaseHistory.save(); } catch (IOException e) { System.err.println("Error saving history to disk: " + e.toString()); } @@ -619,12 +678,83 @@ else if (evt.getSource() == bSaveHistory) { else if (evt.getSource() == historyList) { int selection = historyList.getSelectedIndex(); if (selection == -1) { + if(bphaseButton.isSelected()) + bAddToHistory.setText("Add to Phase History List"); + else if(brangeButton.isSelected()) + bAddToHistory.setText("Add to Range History List"); + return; + } + if(brangeButton.isSelected()) { + startTimeField.setValue(rangeHistory.getStartValue(selection)); + endTimeField.setValue(rangeHistory.getEndValue(selection)); + String procRange = rangeHistory.getProcRange(selection); + if (procRange != null) processorsField.setText(rangeHistory.getProcRange(selection)); + } else if(bphaseButton.isSelected()) { + bAddToHistory.setText("Edit Phase Config Entry"); + + startTimeField.setValue(phaseHistory.getStartValue(selection)); + endTimeField.setValue(phaseHistory.getEndValue(selection)); + String procRange = phaseHistory.getProcRange(selection); + if (procRange != null) + processorsField.setText(phaseHistory.getProcRange(selection)); + + phaseList.removeActionListener(this); + phaseList.removeAllItems(); + int size = phaseHistory.getNumPhases(selection); + for(int i = 0; i < size; i++) { + phaseList.addItem(phaseHistory.getPhaseString(selection, i)); + } + phaseList.setMaximumRowCount(size); + phaseList.setSelectedIndex(-1); + phaseList.addActionListener(this); + phaseChoicePanel.setVisible(true); + } + } + + else if (evt.getSource() == brangeButton) { + bAddToHistory.setText("Add to Range History List"); + bRemoveFromHistory.setText("Remove Selected Range History"); + bSaveHistory.setText("Save Range History to Disk"); + + historyList.removeActionListener(this); + historyList.removeAllItems(); + for(Object o : rangeHistory.getHistoryStrings().toArray()) + historyList.addItem(o); + historyList.setMaximumRowCount(RangeHistory.MAX_ENTRIES); + historyList.setSelectedIndex(-1); // nothing selected at first + historyList.addActionListener(this); + initializeData(); + + phaseChoicePanel.setVisible(false); + } + + else if (evt.getSource() == bphaseButton) { + bAddToHistory.setText("Add to Phase History List"); + bRemoveFromHistory.setText("Remove Selected Phase History"); + bSaveHistory.setText("Save Phase History to Disk"); + + historyList.removeActionListener(this); + historyList.removeAllItems(); + for(Object o : phaseHistory.getHistoryStrings().toArray()) + historyList.addItem(o); + historyList.setMaximumRowCount(PhaseHistory.MAX_ENTRIES); + historyList.setSelectedIndex(-1); + historyList.addActionListener(this); + + phaseChoicePanel.setVisible(false); + } + + else if (evt.getSource() == phaseList) { + int selection = historyList.getSelectedIndex(); + if (selection == -1) { + return; + } + int phaseSelection = phaseList.getSelectedIndex(); + if (phaseSelection == -1) { return; } - startTimeField.setValue(history.getStartValue(selection)); - endTimeField.setValue(history.getEndValue(selection)); - String procRange = history.getProcRange(selection); - if (procRange != null) processorsField.setText(history.getProcRange(selection)); + startTimeField.setValue(phaseHistory.getStartOfPhase(selection, phaseSelection)); + endTimeField.setValue(phaseHistory.getEndOfPhase(selection, phaseSelection)); } someInputChanged(); diff --git a/src/projections/gui/RangeListWindow.java b/src/projections/gui/RangeListWindow.java new file mode 100644 index 00000000..5c67597c --- /dev/null +++ b/src/projections/gui/RangeListWindow.java @@ -0,0 +1,225 @@ +package projections.gui; + +import projections.analysis.RangeHistory; + +import javax.swing.DefaultListModel; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JButton; +import javax.swing.JTextField; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; +import javax.swing.JOptionPane; +import javax.swing.border.LineBorder; +import javax.swing.border.TitledBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.io.File; +import java.io.IOException; + +public class RangeListWindow + extends ProjectionsWindow + implements ActionListener, ListSelectionListener{ + + private static int myRun = 0; + + private RangeHistory history; + private DefaultListModel listModel; + + private JList rangeList; + private JButton addButton, removeButton, editButton, saveButton; + private TimeTextField startTimeField, endTimeField; + private JTextField nameField; + private JSelectField processorField; + + RangeListWindow(MainWindow mainWindow) { + super("Projections Range List Window - " + MainWindow.runObject[myRun].getFilename() + ".sts", mainWindow); + history = new RangeHistory(MainWindow.runObject[myRun].getLogDirectory() + File.separator); + + createLayout(); + pack(); + setLocationRelativeTo(mainWindow); + setVisible(true); + } + + @SuppressWarnings("unchecked") + private void createLayout() { + GridBagConstraints gbc = new GridBagConstraints(); + GridBagLayout gbl = new GridBagLayout(); + + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(2, 2, 2, 2); + + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(gbl); + + listModel = new DefaultListModel(); + for(String o : history.getHistoryStrings()) + listModel.addElement(o); + rangeList = new JList(listModel); + rangeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + rangeList.addListSelectionListener(this); + rangeList.setBorder(new TitledBorder(new LineBorder(Color.black), "Range List")); + + JLabel startTimeLabel = new JLabel("Start Time:", JLabel.LEFT); + startTimeField = new TimeTextField(" ", 12); + JLabel endTimeLabel = new JLabel("End Time:", JLabel.LEFT); + endTimeField = new TimeTextField(" ", 12); + JLabel nameLabel = new JLabel("Name of Entry:", JLabel.LEFT); + nameField = new JTextField("", 12); + JLabel processorLabel = new JLabel("Processor Range:", JLabel.LEFT); + if(MainWindow.runObject[myRun].hasLogData() || MainWindow.runObject[myRun].hasSumDetailData()) + processorField = new JSelectField(MainWindow.runObject[myRun].getValidProcessorString(), 12); + + JPanel infoPanel = new JPanel(); + infoPanel.setLayout(gbl); + Util.gblAdd(infoPanel, startTimeLabel, + gbc, 0, 0, 1, 1, 1, 1); + Util.gblAdd(infoPanel, startTimeField, + gbc, 1, 0, 1, 1, 1, 1); + Util.gblAdd(infoPanel, endTimeLabel, + gbc, 2, 0, 1, 1, 1, 1); + Util.gblAdd(infoPanel, endTimeField, + gbc, 3, 0, 1, 1, 1, 1); + Util.gblAdd(infoPanel, nameLabel, + gbc, 0, 1, 1, 1, 1, 1); + Util.gblAdd(infoPanel, nameField, + gbc, 1, 1, 1, 1, 1, 1); + Util.gblAdd(infoPanel, processorLabel, + gbc, 2, 1, 1, 1, 1, 1); + Util.gblAdd(infoPanel, processorField, + gbc, 3, 1, 1, 1, 1, 1); + addButton = new JButton("Add new Range Entry"); + removeButton = new JButton("Remove selected Range Entry"); + editButton = new JButton("Edit selected Range Entry"); + saveButton = new JButton("Save changes to disk"); + addButton.addActionListener(this); + removeButton.addActionListener(this); + editButton.addActionListener(this); + saveButton.addActionListener(this); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(gbl); + Util.gblAdd(buttonPanel, addButton, + gbc, 0, 0, 1, 1, 1, 1); + Util.gblAdd(buttonPanel, removeButton, + gbc, 1, 0, 1, 1, 1, 1); + Util.gblAdd(buttonPanel, editButton, + gbc, 2, 0, 1, 1, 1, 1); + Util.gblAdd(buttonPanel, saveButton, + gbc, 3, 0, 1, 1, 1, 1); + + Util.gblAdd(mainPanel, rangeList, + gbc, 0, 0, 1, 1, 1, 1); + Util.gblAdd(mainPanel, infoPanel, + gbc, 0, 1, 1, 1, 1, 1); + Util.gblAdd(mainPanel, buttonPanel, + gbc, 0, 2, 1, 1, 0, 0); + + getContentPane().add(mainPanel); + } + + @Override + public void valueChanged(ListSelectionEvent listSelectionEvent) { + int selection = rangeList.getSelectedIndex(); + if(selection == -1) + return; + startTimeField.setValue(history.getStartValue(selection)); + endTimeField.setValue(history.getEndValue(selection)); + nameField.setText(history.getName(selection)); + processorField.setText(history.getProcRange(selection)); + pack(); + } + + @SuppressWarnings("unchecked") + @Override + public void actionPerformed(ActionEvent ae) { + if(ae.getSource() == addButton) { + long startTime = startTimeField.getValue(), endTime = endTimeField.getValue(); + if(startTime >= endTime) + JOptionPane.showMessageDialog(this, "Start Time must be less than End Time."); + else if(processorField.getText().equals("")) + JOptionPane.showMessageDialog(this, "Must specify a processor range."); + else if(nameField.getText().equals("")) + JOptionPane.showMessageDialog(this, "Must specify a name for the entry."); + else { + history.add(startTime, endTime, nameField.getText(), processorField.getText()); + String currString = getRangeString(startTime, endTime, nameField.getText(), processorField.getText()); + listModel.addElement(currString); + pack(); + } + } else if(ae.getSource() == removeButton) { + if(rangeList.getModel().getSize() == 0) + JOptionPane.showMessageDialog(this, + "Need at least one range entry to be removed.", + "Empty Range Entry List", + JOptionPane.ERROR_MESSAGE); + else if(rangeList.isSelectionEmpty()) + JOptionPane.showMessageDialog(this, "Select a Range Entry from the list to be removed."); + else { + history.remove(rangeList.getSelectedIndex()); + listModel.removeElementAt(rangeList.getSelectedIndex()); + pack(); + } + } else if(ae.getSource() == editButton) { + long startTime = startTimeField.getValue(), endTime = endTimeField.getValue(); + if(rangeList.getModel().getSize() == 0) + JOptionPane.showMessageDialog(this, + "Need at least one range entry to be edited.", + "Empty Phase List", + JOptionPane.ERROR_MESSAGE); + else if(rangeList.isSelectionEmpty()) + JOptionPane.showMessageDialog(this, "Select a Range Entry from the list to be edited."); + else if(startTime >= endTime) + JOptionPane.showMessageDialog(this, "Start Time must be less than End Time."); + else { + history.update(rangeList.getSelectedIndex(), startTime, endTime, nameField.getText(), processorField.getText()); + int selection = rangeList.getSelectedIndex(); + listModel.removeElementAt(selection); + listModel.add(selection, getRangeString(startTime, endTime, nameField.getText(), processorField.getText())); + } + } else if(ae.getSource() == saveButton) { + try { + history.save(); + } catch (IOException e) { + System.out.println("Error saving history to disk: " + e.toString()); + } + } + } + + private String getRangeString(long startTime, long endTime, String name, String procs) { + StringBuilder currString = new StringBuilder(); + currString + .append(U.humanReadableString(startTime)) + .append(" to ") + .append(U.humanReadableString(endTime)); + if(procs.length() > 10) + currString + .append(" Proc(s): ") + .append(procs.substring(0, 10)) + .append("..."); + else + currString.append(" Proc(s): ").append(procs); + if(name.length() > 10) + currString + .append(" (") + .append(name.substring(0, 10)) + .append("...)"); + else + currString.append(" (").append(name).append(")"); + + return currString.toString(); + } + + protected void showDialog() { + + } +} diff --git a/src/projections/gui/TimeProfileColorer.java b/src/projections/gui/TimeProfileColorer.java new file mode 100644 index 00000000..eb292ca8 --- /dev/null +++ b/src/projections/gui/TimeProfileColorer.java @@ -0,0 +1,43 @@ +package projections.gui; + +import java.awt.*; + +public class TimeProfileColorer implements GenericGraphColorer{ + /** A class that provides the colors for the display */ + int myRun = 0; + private int outSize, numIntervals, numEPs; + private double[][] outputData, graphData; + private boolean[] stateArray; + + public TimeProfileColorer(int outSize, int numIntervals, int numEPs, double[][] outputData, double[][] graphData, boolean[] stateArray){ + this.outSize = outSize; + this.numIntervals = numIntervals; + this.numEPs = numEPs; + this.outputData = outputData; + this.graphData = graphData; + this.stateArray = stateArray; + } + + public Paint[] getColorMap() { + final int special = 2; + Paint[] outColors = new Paint[outSize]; + for (int i = 0; i < numIntervals; i++) { + int count = 0; + for (int ep = 0; ep < numEPs + special; ep++) { + if (stateArray[ep]) { + outputData[i][count] = graphData[i][ep]; + if(ep == numEPs){ + outColors[count++] = MainWindow.runObject[myRun].getOverheadColor(); + } + else if (ep == numEPs+1){ + outColors[count++] = MainWindow.runObject[myRun].getIdleColor(); + } + else + outColors[count++] = MainWindow.runObject[myRun].getEPColorMap()[ep]; + } + } + } + + return outColors; + } +} \ No newline at end of file