001: package tide.profiler;
002:
003: import snow.utils.gui.Icons;
004: import java.awt.EventQueue;
005: import tide.execute.ProcessItem;
006: import java.awt.Insets;
007: import java.awt.event.*;
008: import java.awt.FlowLayout;
009: import snow.utils.gui.ProgressModalDialog;
010: import snow.files.FileChooserFilter;
011: import javax.swing.*;
012: import snow.texteditor.SimpleDocument;
013: import java.util.*;
014: import tide.editor.MainEditorFrame;
015: import tide.project.ProjectSettings;
016: import java.io.File;
017: import snow.utils.*;
018:
019: public final class ProfilerUtils {
020: private ProfilerUtils() {
021: }
022:
023: private static String readyLine = "Server is ready.";
024: public static final String hatServerProcessStartWithName = "jhat server";
025:
026: /** only since 1.6. Dumps to file with jmap.
027: * A file is asked to the user and returned on success.
028: * This is a long process (if the running app uses a lot memory) and produces *big* fat ugly files.
029: * Please call this from a non EDT thread.
030: *
031: * @param forbidden if non null, avoids choose that file again. Pass the choosen jmap dump baseline if any.
032: */
033: public static File dumpToFileWithJMap(ProcessItem pi, File forbidden) {
034: final ProjectSettings proj = MainEditorFrame.instance
035: .getActualProject();
036: SimpleDocument doc = MainEditorFrame.instance.outputPanels
037: .getExecutionOutputDoc();
038: final File jmap = proj.getJDK_Tool("jmap");
039:
040: JFileChooser fs = new JFileChooser(proj
041: .getProfilerResultsFolder());
042: // TODO: only *.heap_dump files
043: FileChooserFilter fsf = new FileChooserFilter(fs);
044: fs.setAccessory(fsf);
045:
046: fs.setDialogTitle("Choose the file to store the heap dump");
047: fs.setMultiSelectionEnabled(false);
048: fs.setFileSelectionMode(JFileChooser.FILES_ONLY);
049: int rep = fs.showSaveDialog(MainEditorFrame.instance);
050: if (rep != JFileChooser.APPROVE_OPTION)
051: return null;
052:
053: // the new dump file.
054: File sel = fs.getSelectedFile();
055:
056: if (!sel.getName().endsWith(".heap_dump.hprof")) {
057: sel = new File(sel.getAbsoluteFile() + ".heap_dump.hprof");
058: }
059:
060: if (forbidden != null && sel.equals(forbidden)) {
061: JOptionPane
062: .showMessageDialog(
063: null,
064: "You must choose another file to store the dump than the baseline for diff analysis.",
065: "Bad dump file", JOptionPane.ERROR_MESSAGE);
066: return null;
067: }
068:
069: if (sel.exists()) {
070: if (!sel.delete()) {
071: doc.appendError("\r\nCannot delete the prof file "
072: + sel);
073: doc
074: .appendError("\r\n(Please verify that the file is not locked by a running jhat server).");
075: }
076: }
077:
078: MainEditorFrame.instance.outputPanels.selectOutputTab(true);
079: doc.append("\r\nDumping the heap of process " + pi.pid + " ("
080: + pi.name + ") to " + sel + "\r\n");
081: try {
082: // TODO: THIS IS LONG !!!
083: String stack = ProcessUtils.readWholeProcessStack(Arrays
084: .asList(jmap.getAbsolutePath(),
085: "-dump:format=b,file=" + sel, pi.pid))
086: + "\r\n";
087: int pos = doc.getLength(); // so we can place the caret later where the stacktrace begins
088:
089: // stores to allow later dump diffs
090: pi.addDumpFile(sel);
091:
092: doc.appendNamed(stack, pi.pid + "> ");
093:
094: MainEditorFrame.instance.outputPanels.executionOutputPanel
095: .setCaretPosition(pos);
096: return sel;
097: } catch (Exception e) {
098: e.printStackTrace();
099: doc.appendError("\r\nERROR: " + e.getMessage());
100: }
101: return null;
102:
103: }
104:
105: /** To detect memory leaks, look at the histogram at "http://localhost:7000/histo" and look at the reference to the objects.
106: * @param baseLine pass null if no baseline (reference).
107: */
108: public static void analyseProfilerResultWithJHatTool(File f,
109: File baseLine) throws Exception {
110: if (baseLine != null && baseLine.equals(f)) {
111: throw new Exception("Cannot compare two same dumps\n " + f
112: + "\nplease save each dump in a separate file.");
113: }
114:
115: // todo: before calling this method ?
116: if (MainEditorFrame.instance.outputPanels.processesManager
117: .getRunningJHatServers().size() > 0) {
118: JOptionPane.showMessageDialog(MainEditorFrame.instance,
119: "A jhat server is already running. Please kill it from the processes tab"
120: + "\nand restart this analysis.",
121: "Can't start jhat analysis",
122: JOptionPane.ERROR_MESSAGE);
123: return;
124: }
125:
126: ProjectSettings proj = MainEditorFrame.instance
127: .getActualProject();
128: File jh = proj.getJHAT_TOOL();
129: if (!jh.exists())
130: throw new Exception(
131: "jHat tool not found (only since Java 1.6).");
132:
133: List<String> args = new ArrayList<String>();
134: args.add(jh.getAbsolutePath());
135: if (proj.getExtJavaToolMemoryOption().length() > 0) {
136: args.add(proj.getExtJavaToolMemoryOption());
137: }
138: if (baseLine != null) {
139: args.add("-baseline");
140: args.add(baseLine.getAbsolutePath());
141: }
142: args.add(f.getAbsolutePath());
143:
144: ProcessBuilder pb = new ProcessBuilder(args);
145: final Process p = pb.start();
146:
147: MainEditorFrame.instance.outputPanels.processesManager
148: .addProcess(hatServerProcessStartWithName + " for "
149: + f.getName() + " @ http://localhost:7000", p,
150: true);
151:
152: final ProgressModalDialog pmd = new ProgressModalDialog(
153: MainEditorFrame.instance,
154: "Starting jhat analysis server", false);
155: pmd
156: .addSomeShortDescriptionLines("The jhat server should be stopped after usage."
157: + "\nThis can also be done from the processes tab.");
158:
159: // leave EDT
160: Thread t = new Thread() {
161: public void run() {
162: gobbleAndWaitUntilServerReady(p, pmd);
163: try {
164: SysUtils
165: .openBrowser("http://localhost:7000/histo/"); // / is mandatory.
166: } catch (final Exception e) {
167: EventQueue.invokeLater(new Runnable() {
168: public void run() {
169: JOptionPane.showMessageDialog(
170: MainEditorFrame.instance, ""
171: + e.getMessage(),
172: "Cannot launch browser...",
173: JOptionPane.ERROR_MESSAGE);
174: }
175: });
176: }
177: }
178: };
179: t.start();
180: }
181:
182: private static void gobbleAndWaitUntilServerReady(final Process p,
183: final ProgressModalDialog pmd) {
184:
185: pmd.setProcessToOptionalKill(p);
186: pmd.start();
187:
188: Waiter waiter = new Waiter(pmd);
189: waiter.setPriority(Thread.MIN_PRIORITY); // Feb2007: was missing
190: waiter.start();
191:
192: StreamGobbler sg = new StreamGobbler(p.getInputStream(),
193: System.out);
194: sg.setLineListener(waiter);
195: sg.start();
196:
197: StreamGobbler sg2 = new StreamGobbler(p.getErrorStream(),
198: System.err);
199: sg2.setLineListener(waiter);
200: sg2.start();
201:
202: try {
203: waiter.join();
204: pmd.setSeeRunningSince(false);
205: } catch (Exception e) {
206: e.printStackTrace();
207: }
208:
209: EventQueue.invokeLater(new Runnable() {
210: public void run() {
211:
212: pmd.setCommentLabel("");
213: pmd.setProgressValue(100,
214: "Server is running at http://localhost:7000");
215: pmd.setCancelText("stop jhat server", Icons.sharedStop);
216:
217: JPanel optsPanel = new JPanel(new FlowLayout(
218: FlowLayout.LEFT, 3, 0));
219: optsPanel.add(new JLabel("Jump to "));
220: JButton home = new JButton("All classes");
221: home.setMargin(new Insets(0, 1, 0, 1));
222: optsPanel.add(home);
223: home.setFocusPainted(false);
224: home.addActionListener(new ActionListener() {
225: public void actionPerformed(ActionEvent ae) {
226: try {
227: SysUtils
228: .openBrowser("http://localhost:7000");
229: } catch (Exception e) {
230: e.printStackTrace();
231: }
232:
233: }
234: });
235:
236: JButton histo = new JButton("Histogram");
237: histo.setMargin(new Insets(0, 1, 0, 1));
238: optsPanel.add(histo);
239: histo.setFocusPainted(false);
240: histo.addActionListener(new ActionListener() {
241: public void actionPerformed(ActionEvent ae) {
242: try {
243: SysUtils
244: .openBrowser("http://localhost:7000/histo/"); // / at end is mandatory ! showInstanceCounts
245: } catch (Exception e) {
246: e.printStackTrace();
247: }
248: }
249: });
250:
251: JButton inst = new JButton("Instances count");
252: inst.setMargin(new Insets(0, 1, 0, 1));
253: optsPanel.add(inst);
254: inst.setFocusPainted(false);
255: inst.addActionListener(new ActionListener() {
256: public void actionPerformed(ActionEvent ae) {
257: try {
258: SysUtils
259: .openBrowser("http://localhost:7000/showInstanceCounts/"); // / at end is mandatory !
260: } catch (Exception e) {
261: e.printStackTrace();
262: }
263: }
264: });
265:
266: pmd.addComponentToUI(optsPanel);
267:
268: }
269: });
270: }
271:
272: static class Waiter extends Thread implements
273: StringGobblerLineListener {
274: final ProgressModalDialog pmd;
275:
276: public Waiter(ProgressModalDialog pmd) {
277: this .pmd = pmd;
278: }
279:
280: volatile boolean terminated = false;
281:
282: public void lineMatch(String matchedLine) {
283: terminated = true;
284: }
285:
286: public void lineRead(String line) {
287: pmd.setCommentLabel(line); // without "..."
288: }
289:
290: @Override
291: public void run() {
292: while (!terminated) {
293: yield();
294: }
295: }
296:
297: public String lineToMatch() {
298: return readyLine;
299: }
300: }
301: }
|