001: package tide.profiler;
002:
003: import snow.utils.DateUtils;
004: import tide.execute.ProcessItem;
005: import tide.execute.JProcess;
006: import javax.swing.border.LineBorder;
007: import java.awt.Color;
008: import java.awt.FlowLayout;
009: import tide.editor.MainEditorFrame;
010: import snow.utils.gui.Icons;
011: import snow.utils.gui.GUIUtils;
012: import tide.execute.ExecutionOutputPanel;
013: import snow.sortabletable.*;
014: import snow.utils.storage.FileUtils;
015: import snow.utils.StringUtils;
016: import java.io.*;
017: import javax.swing.*;
018: import javax.swing.event.*;
019: import java.awt.BorderLayout;
020: import java.awt.Insets;
021: import java.awt.EventQueue;
022: import java.awt.event.*;
023: import java.util.*;
024:
025: /** Intended to be inserted in a tab of the output panels.
026: * Analyses (parse) a hprof file and shows it (sortabletable).
027: * Reads cpu and memory profiles, currently not the full dumps.
028: * only stores trace locations in a map => few memory requirements.
029: */
030: public final class ProfilerResultViewer extends JPanel {
031: final JTable table = new JTable();
032: ProfilerTableModel profilerTableModel;
033: SortableTableModel sortableTableModel;
034:
035: final List<PItem> items = new ArrayList<PItem>();
036: final Map<Long, Long> posForTrace = new HashMap<Long, Long>();
037:
038: final ExecutionOutputPanel tracePanel = new ExecutionOutputPanel(
039: false);
040:
041: enum ProfType {
042: CPU, DUMP, SITES, UNKNOWN
043: }
044:
045: private String[] COLUMN_NAMES = new String[] { "Rank", "Self [%]",
046: "Count", "Source" };
047: private int[] COLUMN_PREFERED_SIZES = new int[] { 4, 4, 6, 45 };
048:
049: private ProfType profType = ProfType.UNKNOWN;
050:
051: private final JPanel progressContentPanel = new JPanel(
052: new BorderLayout(0, 0));
053: private final JProgressBar progress = new JProgressBar();
054:
055: private final JSplitPane splitPane;
056: private final JPanel resultsContentPanel = new JPanel(
057: new BorderLayout(0, 0));
058: final long fileDate;
059:
060: private boolean fileReadDone = false;
061: final JProcess proc;
062:
063: public ProfilerResultViewer(final File file, final JProcess proc) {
064: super (new BorderLayout());
065:
066: this .proc = proc;
067: fileDate = file.lastModified();
068:
069: profilerTableModel = new ProfilerTableModel();
070: sortableTableModel = new SortableTableModel(profilerTableModel);
071: table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
072:
073: splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
074: new JScrollPane(table), tracePanel);
075: splitPane.setOneTouchExpandable(true);
076: splitPane.setDividerLocation(200);
077:
078: // center, the splitpane will come later, when loaded
079:
080: add(progressContentPanel, BorderLayout.CENTER);
081: JPanel progressPan = new JPanel();
082: progressPan.add(progress);
083: progressContentPanel.add(progressPan, BorderLayout.CENTER);
084: progress.setIndeterminate(true);
085: progress.setStringPainted(true);
086: progress.setString("Loading profiler results...");
087:
088: resultsContentPanel.add(splitPane, BorderLayout.CENTER);
089:
090: MultiSearchPanel asp = new MultiSearchPanel("Filter: ", null,
091: sortableTableModel);
092: JPanel pt = new JPanel();
093: pt.setLayout(new BoxLayout(pt, BoxLayout.X_AXIS));
094: pt.add(GUIUtils.wrapLeft(asp, 0));
095: resultsContentPanel.add(pt, BorderLayout.NORTH);
096:
097: JButton close = new JButton(Icons.sharedCross);
098: close.setMargin(new Insets(0, 0, 0, 0));
099: pt.add(Box.createHorizontalStrut(50));
100: pt.add(close);
101: close.addActionListener(new ActionListener() {
102: public void actionPerformed(ActionEvent ae) {
103: closeAndRemoveTab();
104: }
105: });
106:
107: table.getSelectionModel().addListSelectionListener(
108: new ListSelectionListener() {
109: public void valueChanged(ListSelectionEvent lse) {
110: if (lse.getValueIsAdjusting())
111: return;
112:
113: int posView = table.getSelectedRow();
114: if (posView == -1) {
115: tracePanel.doc.setText("no selection");
116: return;
117: }
118:
119: int posModel = sortableTableModel
120: .getIndexInUnsortedFromTablePos(posView);
121: if (posModel == -1) {
122: if (!fileReadDone) {
123: tracePanel.doc
124: .setText("The file reading is not terminated... please retry later...");
125: } else {
126: tracePanel.doc
127: .setText("no model pos for "
128: + posView);
129: }
130: return;
131: }
132:
133: PItem pi = items.get(posModel);
134: if (pi.trace == -1) {
135: tracePanel.doc.setText("no trace");
136: return;
137: }
138:
139: tracePanel.doc.setText(getStackTrace(file,
140: pi.trace));
141: tracePanel.setCaret0();
142:
143: }
144: });
145:
146: // Analyse the file !
147: //
148: final Thread af = new Thread() {
149: @Override
150: public void run() {
151: try {
152: analyse(file);
153: } catch (Exception e) {
154: e.printStackTrace();
155: // TODO: tell user...
156: }
157:
158: }
159: };
160: af.setName("Profiler result analysis");
161: af.start();
162:
163: // todo: offer killing analysis...
164: }
165:
166: public void installAsTab() {
167: final JTabbedPane tp = MainEditorFrame.instance.outputPanels;
168: String title = "Profiler result ("
169: + DateUtils.formatDateAndTimeHuman(fileDate) + ")";
170: tp.addTab(title, this );
171:
172: JPanel rp = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
173: rp.setOpaque(false);
174: rp.add(new JLabel(title));
175: JButton cb = new JButton(new Icons.CrossIcon(8, 8, true));
176: rp.add(cb);
177: cb.setFocusPainted(false);
178: cb.setMargin(new Insets(0, 0, 0, 0));
179: cb.setBorder(new LineBorder(Color.lightGray, 1, true)); //null);
180: cb.addActionListener(new ActionListener() {
181: public void actionPerformed(ActionEvent ae) {
182: tp.remove(ProfilerResultViewer.this );
183: }
184: });
185: tp.setTabComponentAt(tp.getTabCount() - 1, rp);
186: tp.setSelectedIndex(tp.getTabCount() - 1);
187: }
188:
189: private void closeAndRemoveTab() {
190: if (getParent() instanceof JTabbedPane) {
191: JTabbedPane tp = (JTabbedPane) getParent();
192: tp.remove(ProfilerResultViewer.this );
193: }
194:
195: sortableTableModel.removeOldListeners();
196: items.clear();
197: posForTrace.clear();
198: }
199:
200: private String getStackTrace(File file, long str) {
201: if (!posForTrace.containsKey(str))
202: return "TRACE " + str + " not found";
203: long pos = this .posForTrace.get(str);
204: StringBuilder sb = new StringBuilder();
205: RandomAccessFile raf = null;
206: try {
207: raf = new RandomAccessFile(file, "r");
208: raf.seek(pos);
209: String line;
210: wl: while ((line = raf.readLine()) != null) {
211: if (line.startsWith("\t"))
212: sb.append(line + "\n");
213: else {
214: break wl;
215: }
216: }
217: } catch (Exception e) {
218: e.printStackTrace();
219: } finally {
220: FileUtils.closeIgnoringExceptions(raf);
221: }
222:
223: return "\t" + (sb.toString().trim());
224: }
225:
226: /** Called in a worker thread.
227: */
228: private void analyse(final File file) {
229: // RandomAccessFile is 10 times slower than a buffered file reader !
230: RandomAccessFile raf = null;
231: try {
232: raf = new RandomAccessFile(file, "r");
233:
234: // search the last line !
235: //
236: raf.seek(raf.length() - 100);
237: String line = "";
238: String type = null;
239: EventQueue.invokeLater(new Runnable() {
240: public void run() {
241: progress.setString("reading end tag...");
242: }
243: });
244: wl: while ((line = raf.readLine()) != null) {
245: //System.out.println(""+line);
246: if (line.endsWith("END")) // ex: CPU SAMPLES END
247: {
248: //System.out.println("Found: "+line);
249: type = line.substring(0, line.length() - 4);
250: break wl;
251: }
252: }
253:
254: if (type.startsWith("CPU")) {
255: profType = ProfType.CPU;
256: } else if (type.startsWith("SITES")) {
257: profType = ProfType.SITES;
258: } else if (type.startsWith("HEAP")) {
259: profType = ProfType.DUMP;
260: EventQueue.invokeLater(new Runnable() {
261: public void run() {
262: progress.setIndeterminate(false);
263: progress
264: .setString("HEAP not supported => not analysed");
265: }
266: });
267: return;
268: } else {
269: //profType = ProfType.UNKNOWN;
270: /*final String ftype = type;
271: EventQueue.invokeLater(new Runnable() { public void run() {
272: progress.setIndeterminate(false);
273: progress.setString("Unknow type "+ftype+" => not analysed");
274: }});*/
275: throw new Exception("unknown hprof type " + type
276: + " found");
277: }
278:
279: if (type == null) {
280: throw new Exception("analysis type not found (at end)");
281: }
282:
283: // and now, from the beginning, look for the begin "tag".
284: EventQueue.invokeLater(new Runnable() {
285: public void run() {
286: progress.setString("seeking beginning tag...");
287: }
288: });
289: raf.seek(0);
290: String startToSearch = type + " BEGIN";
291: long posStart = -1;
292: wl: while ((line = raf.readLine()) != null) {
293: if (line.startsWith(startToSearch)) {
294: //System.out.println("FOUND: "+line);
295: posStart = raf.getFilePointer();
296: break wl;
297: }
298: }
299:
300: if (posStart < 0)
301: throw new Exception("Start position not found");
302:
303: // we are at start => parse
304: EventQueue.invokeLater(new Runnable() {
305: public void run() {
306: progress.setString("parse results...");
307: }
308: });
309: String endToReach = type + " END";
310: // skip he haders
311: raf.readLine();
312: wl: while ((line = raf.readLine()) != null) {
313: if (line.startsWith(endToReach))
314: break wl;
315: try {
316: items.add(new PItem(line));
317: } catch (Exception e) {
318: System.out.println("Error parsing prof line: "
319: + line);
320: }
321: }
322:
323: installResultsGUIWhenRead();
324:
325: // store the traces positions
326: EventQueue.invokeLater(new Runnable() {
327: public void run() {
328: progress.setString("parse stacktraces...");
329: }
330: });
331:
332: raf.seek(0);
333: wl: while ((line = raf.readLine()) != null) {
334: if (line.startsWith("TRACE ")) {
335: String trl = StringUtils
336: .extractFromFirstToNext_Excluded(line,
337: "TRACE ", ":");
338: if (trl != null) {
339: long tr = Long.parseLong(trl.trim());
340: posForTrace.put(tr, raf.getFilePointer());
341: } else {
342: System.out.println("No trace in:" + line); // occurs in the help
343: }
344: }
345: }
346:
347: EventQueue.invokeLater(new Runnable() {
348: public void run() {
349: progress.setString("terminated...");
350: }
351: });
352: MainEditorFrame.debugOut("" + posForTrace.size()
353: + " traces found in " + file);
354:
355: } catch (final Exception e) {
356: if (proc != null) {
357: ProcessItem pi = MainEditorFrame.instance.outputPanels.processesManager
358: .getProcess(proc);
359: if (pi != null) {
360: if (pi.hasBeenKilled) {
361: JOptionPane
362: .showMessageDialog(
363: null,
364: "Error: the process has been killed."
365: + "\nOnly normally terminated programs gives HPROF data.",
366: "Profiler analysis error",
367: JOptionPane.ERROR_MESSAGE);
368: closeAndRemoveTab();
369: return;
370: }
371: }
372: }
373:
374: e.printStackTrace();
375: JOptionPane.showMessageDialog(null, "Error:"
376: + e.getMessage(), "Profiler analysis error",
377: JOptionPane.ERROR_MESSAGE);
378: closeAndRemoveTab();
379: } finally {
380: FileUtils.closeIgnoringExceptions(raf);
381: fileReadDone = true;
382: }
383:
384: if (profType == ProfType.SITES) {
385: COLUMN_NAMES = new String[] { "Rank", "Self [%]",
386: "bytes(live)", "count(live)", "bytes(alloc)",
387: "count(alloc)", "Source" };
388: COLUMN_PREFERED_SIZES = new int[] { 4, 4, 6, 6, 6, 6, 45 };
389: }
390: }
391:
392: private void installResultsGUIWhenRead() {
393: EventQueue.invokeLater(new Runnable() {
394: public void run() {
395: remove(progressContentPanel);
396: invalidate();
397: add(resultsContentPanel, BorderLayout.CENTER);
398: updateUI();
399: repaint();
400:
401: table.setModel(sortableTableModel);
402: sortableTableModel.installGUI(table);
403: sortableTableModel.setAllColumnsVisible();
404: sortableTableModel.setPreferredColumnSizesFromModel();
405:
406: profilerTableModel.update();
407: }
408: });
409: }
410:
411: class ProfilerTableModel extends FineGrainTableModel {
412: public int getRowCount() {
413: return items.size();
414: }
415:
416: public int getColumnCount() {
417: return COLUMN_NAMES.length;
418: }
419:
420: public Object getValueAt(int row, int col) {
421: if (row < 0)
422: return "bad row:" + row;
423: if (row >= items.size())
424: return "bad row:" + row;
425:
426: PItem pi = items.get(row);
427: if (profType == ProfType.CPU) {
428: if (col == 0)
429: return pi.rank;
430: if (col == 1)
431: return pi.self;
432: if (col == 2)
433: return pi.count;
434: if (col == 3)
435: return pi.name;
436: } else {
437: if (col == 0)
438: return pi.rank;
439: if (col == 1)
440: return pi.self;
441: if (col == 2)
442: return pi.bytes1;
443: if (col == 3)
444: return pi.count1;
445: if (col == 4)
446: return pi.bytes2;
447: if (col == 5)
448: return pi.count2;
449: if (col == 6)
450: return pi.name;
451: }
452: return "?";
453: }
454:
455: public void update() {
456: fireTableDataChanged();
457: fireTableModelHasChanged();
458: }
459:
460: @Override
461: public String getColumnName(int column) {
462: if (column >= 0 && column < COLUMN_NAMES.length)
463: return COLUMN_NAMES[column];
464: return "??";
465: }
466:
467: @Override
468: public int getPreferredColumnWidth(int column) {
469: if (column >= 0 && column < COLUMN_PREFERED_SIZES.length)
470: return COLUMN_PREFERED_SIZES[column];
471: return -1;
472: }
473: }
474:
475: class PItem {
476: int rank, count;
477: long trace = -1;
478: double self;
479: //double cummulated;
480: String name;
481: long bytes1, bytes2;
482: int count1, count2;
483:
484: public PItem(String line) {
485: Scanner sc = new Scanner(line);
486:
487: rank = sc.nextInt();
488: String n = sc.next();
489: self = Double.parseDouble(n.substring(0, n.length() - 1));
490: n = sc.next();
491: //cummulated = Double.parseDouble(n.substring(0,n.length()-1));
492: if (profType == ProfType.CPU) {
493: count = sc.nextInt();
494:
495: trace = sc.nextLong();
496: name = sc.next();
497: } else if (profType == ProfType.SITES) {
498: bytes1 = sc.nextLong();
499: count1 = sc.nextInt();
500: bytes2 = sc.nextLong();
501: count2 = sc.nextInt();
502: trace = sc.nextLong();
503: name = sc.next();
504: }
505: }
506: }
507: }
|