1 | /* |
2 | * Copyright 2008 Eric Caspole |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this |
5 | * file except in compliance with the License. You may obtain a copy of the License at |
6 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | * |
9 | * Unless required by applicable law or agreed to in writing, software distributed under |
10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
11 | * KIND, either express or implied. See the License for the specific language governing |
12 | * permissions and limitations under the License. |
13 | */ |
14 | package net.sf.madmap; |
15 | |
16 | import java.io.*; |
17 | import java.util.*; |
18 | import java.awt.*; |
19 | import java.awt.event.*; |
20 | import javax.swing.*; |
21 | import javax.swing.event.*; |
22 | import javax.swing.filechooser.*; |
23 | |
24 | public class MainWindow extends JPanel implements ActionListener, ItemListener { |
25 | static private final String newline = "\n"; |
26 | public JTextArea log; |
27 | JTabbedPane tabbedPane; |
28 | JFileChooser fc; |
29 | JFileChooser saveChooser; |
30 | JProgressBar pb; |
31 | JLabel pbl; |
32 | JProgressBar mpb; |
33 | JLabel mpbl; |
34 | JMenuItem newWindowItem; |
35 | JMenuItem openItem; |
36 | JMenuItem saveItem; |
37 | JMenuItem quitItem; |
38 | JMenuItem classSizeItem; |
39 | JMenuItem memberNamesItem; |
40 | JMenuItem verboseItem; |
41 | JMenuItem helpItem; |
42 | JFrame frame; |
43 | Madmap document = null; |
44 | |
45 | ILogger _logger = (ILogger)(MadmapMain.appContext()).getBean("logger"); |
46 | IResources _resources = (IResources)(MadmapMain.appContext()).getBean("resources"); |
47 | |
48 | public static File getCWD(){ |
49 | return new File(System.getProperty("user.dir")); |
50 | } |
51 | |
52 | public JMenuBar createMenuBar() { |
53 | JMenuBar menuBar; |
54 | JMenu menu; |
55 | |
56 | menuBar = new JMenuBar(); |
57 | |
58 | menu = new JMenu(_resources.getString("fileMenu")); |
59 | menuBar.add(menu); |
60 | |
61 | newWindowItem = new JMenuItem(_resources.getString("newItem")); |
62 | newWindowItem.getAccessibleContext().setAccessibleDescription(_resources.getString("newDescription")); |
63 | menu.add(newWindowItem); |
64 | newWindowItem.addActionListener(this); |
65 | |
66 | openItem = new JMenuItem(_resources.getString("openItem")); |
67 | openItem.getAccessibleContext().setAccessibleDescription(_resources.getString("openDescription")); |
68 | menu.add(openItem); |
69 | openItem.addActionListener(this); |
70 | |
71 | saveItem = new JMenuItem(_resources.getString("saveItem")); |
72 | saveItem.getAccessibleContext().setAccessibleDescription(_resources.getString("saveDescription")); |
73 | menu.add(saveItem); |
74 | saveItem.addActionListener(this); |
75 | |
76 | quitItem = new JMenuItem(_resources.getString("quitItem")); |
77 | quitItem.getAccessibleContext().setAccessibleDescription(_resources.getString("quitDescription")); |
78 | menu.add(quitItem); |
79 | quitItem.addActionListener(this); |
80 | |
81 | menu = new JMenu(_resources.getString("helpMenu")); |
82 | menuBar.add(menu); |
83 | |
84 | //classSizeItem = new JMenuItem(_resources.getString("classSizeItem")); |
85 | //classSizeItem.getAccessibleContext().setAccessibleDescription(_resources.getString("helpDescription")); |
86 | //menu.add(classSizeItem); |
87 | //classSizeItem.addActionListener(this); |
88 | |
89 | memberNamesItem = new JCheckBoxMenuItem(_resources.getString("memberNamesItem")); |
90 | //classSizeItem.getAccessibleContext().setAccessibleDescription(_resources.getString("helpDescription")); |
91 | menu.add(memberNamesItem); |
92 | memberNamesItem.addItemListener(this); |
93 | memberNamesItem.setSelected(MadmapMain.getSaveMemberNames()); |
94 | |
95 | verboseItem = new JCheckBoxMenuItem(_resources.getString("verboseItem")); |
96 | //verboseItem.getAccessibleContext().setAccessibleDescription(_resources.getString("helpDescription")); |
97 | menu.add(verboseItem); |
98 | //verboseItem.addActionListener(this); |
99 | verboseItem.addItemListener(this); |
100 | verboseItem.setSelected(MadmapMain.verbose()); |
101 | |
102 | helpItem = new JMenuItem(_resources.getString("helpItem")); |
103 | helpItem.getAccessibleContext().setAccessibleDescription(_resources.getString("helpDescription")); |
104 | menu.add(helpItem); |
105 | helpItem.addActionListener(this); |
106 | |
107 | return menuBar; |
108 | } |
109 | |
110 | |
111 | public MainWindow(JFrame theFrame) { |
112 | super(new BorderLayout()); |
113 | frame = theFrame; |
114 | |
115 | //Create the log first, because the action listeners |
116 | //need to refer to it. |
117 | log = new JTextArea(); |
118 | log.setMargin(new Insets(5,5,5,5)); |
119 | log.setEditable(false); |
120 | |
121 | //Create the progress bar |
122 | pb = new JProgressBar(0,100); |
123 | pb.setStringPainted(true); |
124 | pbl = new JLabel( "Ready. " ); |
125 | |
126 | //Create the progress bar for the memory |
127 | mpb = new JProgressBar(0,100); |
128 | mpb.setStringPainted(true); |
129 | mpbl = new JLabel( "Heap used: " ); |
130 | |
131 | mpb.setMinimum(0); |
132 | mpb.setMaximum(100); |
133 | mpb.setValue(0); |
134 | |
135 | fc = new JFileChooser(getCWD()); |
136 | saveChooser = new JFileChooser(getCWD()); |
137 | |
138 | JPanel buttonPanel = new JPanel(); |
139 | buttonPanel.add(pbl); |
140 | buttonPanel.add(pb); |
141 | buttonPanel.add(mpbl); |
142 | buttonPanel.add(mpb); |
143 | |
144 | tabbedPane = new JTabbedPane(); |
145 | |
146 | JComponent panel1 = makeLogPanel(); |
147 | panel1.setPreferredSize(new Dimension(600, 200)); |
148 | tabbedPane.addTab("Report", null, panel1, |
149 | "Progress and diagnostic messages. You can save the report by the Save Button"); |
150 | |
151 | add(buttonPanel, BorderLayout.PAGE_END); |
152 | add(tabbedPane, BorderLayout.CENTER); |
153 | } |
154 | |
155 | protected JComponent makeTextPanel(String text) { |
156 | JPanel panel = new JPanel(false); |
157 | JLabel filler = new JLabel(text); |
158 | filler.setHorizontalAlignment(JLabel.CENTER); |
159 | panel.setLayout(new GridLayout(1, 1)); |
160 | panel.add(filler); |
161 | return panel; |
162 | } |
163 | |
164 | protected JComponent makeLogPanel() { |
165 | JPanel panel = new JPanel(false); |
166 | JScrollPane logScrollPane = new JScrollPane(log); |
167 | panel.setLayout(new GridLayout(1, 1)); |
168 | panel.add(logScrollPane); |
169 | return panel; |
170 | } |
171 | |
172 | public void addLiveHeapTab( java.util.List<HprofClassElement> liveHeap, long liveSize ) { |
173 | JPanel panel2 = new JPanel(false); |
174 | JTable table = new JTable(new LiveHeapTableModel(liveHeap, liveSize)); |
175 | JScrollPane scrollpane = new JScrollPane(table); |
176 | panel2.setLayout(new GridLayout(1, 1)); |
177 | panel2.add(scrollpane); |
178 | tabbedPane.addTab("Live Heap", null, panel2, |
179 | "Display live heap table with size and count"); |
180 | } |
181 | |
182 | public void addRetainedByClassTab( Madmap m, ArrayList liveHeap, long liveSize, String title ) { |
183 | JPanel panel = new JPanel(false); |
184 | JTable table = new JTable(new CumulativeRetainedHeapTableModel(m, liveHeap, liveSize)); |
185 | JScrollPane scrollpane = new JScrollPane(table); |
186 | panel.setLayout(new GridLayout(1, 1)); |
187 | panel.add(scrollpane); |
188 | tabbedPane.addTab(new String("Ret by Class: " + title), null, panel, |
189 | "Display classes retaining the most cumulative size when roots sorted by " + title); |
190 | } |
191 | |
192 | public void addRetainedHeapTab( Madmap m, ArrayList liveHeap, long liveSize, String title ) { |
193 | JPanel panel2 = new JPanel(false); |
194 | JTextArea retLog = new JTextArea(); |
195 | JTable table = new JTable(new RetainedHeapTableModel(m, liveHeap, liveSize, retLog)); |
196 | |
197 | table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
198 | table.getSelectionModel().addListSelectionListener(new RowListener(table)); |
199 | |
200 | JScrollPane scrollpane = new JScrollPane(table); |
201 | panel2.setLayout(new GridLayout(2, 0)); |
202 | panel2.add(scrollpane, BorderLayout.PAGE_START); |
203 | |
204 | retLog.setMargin(new Insets(5,5,5,5)); |
205 | retLog.setEditable(false); |
206 | JScrollPane retScrollPane = new JScrollPane(retLog); |
207 | panel2.add(retScrollPane, BorderLayout.PAGE_END); |
208 | |
209 | retLog.setText("Click on a row to see the allocation site."); |
210 | tabbedPane.addTab(new String( "Ret: " + title ), null, panel2, |
211 | "Display objects retaining the most size when roots sorted by " + title); |
212 | } |
213 | |
214 | private void outputSelection(JTable table) { |
215 | int selectedRow[] = table.getSelectedRows(); |
216 | |
217 | assert selectedRow.length == 1 : "Should set selection model to single"; |
218 | |
219 | JTextArea retLog = ((RetainedHeapTableModel)table.getModel()).getLog(); |
220 | java.util.List stk = ((RetainedHeapTableModel)table.getModel()).getStack( selectedRow[0] ); |
221 | |
222 | retLog.setText( "" ); |
223 | retLog.setText( "Allocation site:\n" ); |
224 | if ( stk != null ) { |
225 | for (int i = 0; i < stk.size(); i++ ) { |
226 | String output = new String( " " + ((String) stk.get(i)) + "\n" ); |
227 | retLog.append(output); |
228 | } |
229 | } else { |
230 | String output = new String( " <no stack trace available>" + "\n" ); |
231 | retLog.append(output); |
232 | } |
233 | |
234 | retLog.append("\n"); |
235 | |
236 | String summary = ((RetainedHeapTableModel)table.getModel()).childrenSummary( selectedRow[0] ); |
237 | if ( summary != null ) { |
238 | retLog.append(summary); |
239 | } else { |
240 | String output = new String( " <no member data available>" + "\n" ); |
241 | retLog.append(output); |
242 | } |
243 | |
244 | retLog.updateUI(); |
245 | updateMemoryProgressBar(); |
246 | } |
247 | |
248 | private class RowListener implements ListSelectionListener { |
249 | JTable _parent; |
250 | public RowListener( JTable t ) { |
251 | _parent = t; |
252 | } |
253 | public void valueChanged(ListSelectionEvent event) { |
254 | if (event.getValueIsAdjusting()) { |
255 | return; |
256 | } |
257 | outputSelection(_parent); |
258 | } |
259 | } |
260 | |
261 | |
262 | public void addFinalizerHeapTab( java.util.List<HprofClassElement> finalizableHeap ) { |
263 | JPanel panel2 = new JPanel(false); |
264 | JTable table = new JTable(new FinalizerTableModel(finalizableHeap)); |
265 | JScrollPane scrollpane = new JScrollPane(table); |
266 | panel2.setLayout(new GridLayout(1, 1)); |
267 | panel2.add(scrollpane); |
268 | tabbedPane.addTab("Finalizable Objects", null, panel2, |
269 | "Display finalizable objects table with size and count"); |
270 | } |
271 | |
272 | public void addThreadsTab( TreeSet threads ) { |
273 | JPanel panel2 = new JPanel(false); |
274 | JTable table = new JTable(new ThreadTableModel(threads)); |
275 | JScrollPane scrollpane = new JScrollPane(table); |
276 | panel2.setLayout(new GridLayout(1, 1)); |
277 | panel2.add(scrollpane); |
278 | tabbedPane.addTab("Threads", null, panel2, |
279 | "Display threads appearing in the profile. Not all the threads may be live at the time the heap dump was written."); |
280 | } |
281 | |
282 | public void itemStateChanged(ItemEvent e) { |
283 | if (e.getSource() == verboseItem) { |
284 | boolean curr = verboseItem.isSelected(); |
285 | //log.append("Verbose: was " + !curr + "." + newline); |
286 | MadmapMain.setVerbose(curr); |
287 | //log.append("Verbose: now " + MadmapMain.verbose() + "." + newline); |
288 | } else if (e.getSource() == memberNamesItem) { |
289 | boolean curr = memberNamesItem.isSelected(); |
290 | MadmapMain.setSaveMemberNames(curr); |
291 | } |
292 | } |
293 | |
294 | public void actionPerformed(ActionEvent e) { |
295 | if (e.getSource() == openItem) { |
296 | if (document == null) { |
297 | int returnVal = fc.showOpenDialog(MainWindow.this); |
298 | if (returnVal == JFileChooser.APPROVE_OPTION) { |
299 | File file = fc.getSelectedFile(); |
300 | log.append("Opening: " + file.getName() + "." + newline); |
301 | frame.setTitle( "Madmap: " + file.getName() ); |
302 | (new Thread( document = new Madmap(file, this))).start(); |
303 | } |
304 | log.setCaretPosition(log.getDocument().getLength()); |
305 | } else { |
306 | // If there is already a file open in the current window, |
307 | // open the chosen document in a new window |
308 | int returnVal = fc.showOpenDialog(MainWindow.this); |
309 | if (returnVal == JFileChooser.APPROVE_OPTION) { |
310 | File file = fc.getSelectedFile(); |
311 | JFrame frame = new JFrame("Madmap"); |
312 | frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); |
313 | newContentPane = new MainWindow(frame); |
314 | newContentPane.setOpaque(true); //content panes must be opaque |
315 | frame.setContentPane(newContentPane); |
316 | frame.setJMenuBar(newContentPane.createMenuBar()); |
317 | frame.pack(); |
318 | frame.setVisible(true); |
319 | frame.setTitle( "Madmap: " + file.getName() ); |
320 | (new Thread( document = new Madmap(file, newContentPane))).start(); |
321 | } |
322 | } |
323 | } else if (e.getSource() == newWindowItem) { |
324 | init(); |
325 | } else if (e.getSource() == saveItem) { |
326 | int returnVal = saveChooser.showSaveDialog(MainWindow.this); |
327 | if (returnVal == JFileChooser.APPROVE_OPTION) { |
328 | File file = saveChooser.getSelectedFile(); |
329 | log.append("Saving: " + file.getName() + "." + newline); |
330 | _logger.saveConsoleLog( this, file ); |
331 | } else { |
332 | log.append("Save command cancelled by user." + newline); |
333 | } |
334 | log.setCaretPosition(log.getDocument().getLength()); |
335 | } else if (e.getSource() == helpItem) { |
336 | // get help text from resource bundle |
337 | log.append( _resources.getString("helpText") ); |
338 | log.append( _resources.getString("usageStrs") ); |
339 | } else if (e.getSource() == saveItem) { |
340 | } else if (e.getSource() == quitItem) { |
341 | System.err.println("goodbye!"); |
342 | System.exit(0); |
343 | } |
344 | } |
345 | |
346 | static MainWindow newContentPane = null; |
347 | |
348 | public static MainWindow getToolUI() { return newContentPane; } |
349 | |
350 | |
351 | private static void createAndShowGUI() { |
352 | JFrame frame = new JFrame("Madmap"); |
353 | frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); |
354 | |
355 | newContentPane = new MainWindow(frame); |
356 | newContentPane.setOpaque(true); //content panes must be opaque |
357 | frame.setContentPane(newContentPane); |
358 | |
359 | frame.setJMenuBar(newContentPane.createMenuBar()); |
360 | |
361 | //Display the window. |
362 | frame.pack(); |
363 | frame.setVisible(true); |
364 | } |
365 | |
366 | public static void init() { |
367 | javax.swing.SwingUtilities.invokeLater(new Runnable() { |
368 | public void run() { |
369 | createAndShowGUI(); |
370 | MadmapMain.guiNotify(); |
371 | } |
372 | }); |
373 | } |
374 | |
375 | public JProgressBar getProgressBar() { |
376 | return pb; |
377 | } |
378 | |
379 | public JLabel getLabelForProgressBar() { |
380 | return pbl; |
381 | } |
382 | |
383 | public void updateMemoryProgressBar() { |
384 | long total = Runtime.getRuntime().totalMemory(); |
385 | long free = Runtime.getRuntime().freeMemory(); |
386 | int usedPct = (int)( (double)(total - free)/((double)total) * (double)100.0 ); |
387 | mpb.setValue(usedPct); |
388 | } |
389 | } |
390 | |