001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.ui.memory;
042:
043: import org.netbeans.lib.profiler.TargetAppRunner;
044: import org.netbeans.lib.profiler.client.ClientUtils;
045: import org.netbeans.lib.profiler.global.CommonConstants;
046: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
047: import org.netbeans.lib.profiler.instrumentation.InstrumentationException;
048: import org.netbeans.lib.profiler.results.memory.MemoryCCTProvider;
049: import org.netbeans.lib.profiler.ui.LiveResultsPanel;
050: import org.netbeans.lib.profiler.ui.UIUtils;
051: import java.awt.*;
052: import java.awt.event.ActionEvent;
053: import java.awt.event.ActionListener;
054: import java.awt.image.BufferedImage;
055: import java.text.MessageFormat;
056: import java.util.ResourceBundle;
057: import javax.swing.*;
058: import javax.swing.event.PopupMenuEvent;
059: import javax.swing.event.PopupMenuListener;
060:
061: /**
062: * This class implements presentation frames for Object Liveness Profiling.
063: *
064: * @author Misha Dmitriev
065: * @author Ian Formanek
066: * @author Jiri Sedlacek
067: */
068: public class LiveLivenessResultsPanel extends LivenessResultsPanel
069: implements LiveResultsPanel, ActionListener {
070: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
071:
072: // -----
073: // I18N String constants
074: private static final ResourceBundle messages = ResourceBundle
075: .getBundle("org.netbeans.lib.profiler.ui.memory.Bundle"); // NOI18N
076: private static final String GO_SOURCE_POPUP_ITEM_NAME = messages
077: .getString("LiveLivenessResultsPanel_GoSourcePopupItemName"); // NOI18N
078: private static final String SHOW_STACK_TRACES_POPUP_ITEM_NAME = messages
079: .getString("AllocResultsPanel_LiveShowStackTracesPopupItemName"); // NOI18N
080: private static final String STOP_CLASS_POPUP_ITEM_NAME = messages
081: .getString("LiveLivenessResultsPanel_StopClassPopupItemName"); // NOI18N
082: private static final String STOP_BELOW_LINE_POPUP_ITEM_NAME = messages
083: .getString("LiveLivenessResultsPanel_StopBelowLinePopupItemName"); // NOI18N
084: private static final String STOP_CLASS_SPEC_POPUP_ITEM_NAME = messages
085: .getString("LiveLivenessResultsPanel_StopClassSpecPopupItemName"); // NOI18N
086: private static final String STOP_BELOW_LINE_SPEC_POPUP_ITEM_NAME = messages
087: .getString("LiveLivenessResultsPanel_StopBelowLineSpecPopupItemName"); // NOI18N
088: private static final String LOG_CLASS_HISTORY = messages
089: .getString("LiveResultsPanel_LogClassHistory"); // NOI18N
090: // -----
091:
092: //~ Instance fields ----------------------------------------------------------------------------------------------------------
093:
094: protected TargetAppRunner runner;
095:
096: //common actions handler
097: ActionsHandler handler;
098: private JMenuItem popupRemoveProfForClass;
099: private JMenuItem popupRemoveProfForClassesBelow;
100: private JMenuItem popupShowSource;
101: private JMenuItem popupShowStacks;
102: private JMenuItem startHisto;
103: private JPopupMenu popup;
104: private ProfilingSessionStatus status;
105: private boolean updateResultsInProgress = false;
106: private boolean updateResultsPending = false;
107:
108: //~ Constructors -------------------------------------------------------------------------------------------------------------
109:
110: public LiveLivenessResultsPanel(TargetAppRunner runner,
111: MemoryResUserActionsHandler actionsHandler) {
112: this (runner, actionsHandler, null);
113: }
114:
115: public LiveLivenessResultsPanel(TargetAppRunner runner,
116: MemoryResUserActionsHandler actionsHandler,
117: ActionsHandler handler) {
118: super (actionsHandler);
119: this .runner = runner;
120: this .status = runner.getProfilerClient().getStatus();
121: initColumnsData();
122: this .handler = handler;
123: }
124:
125: //~ Methods ------------------------------------------------------------------------------------------------------------------
126:
127: public BufferedImage getViewImage(boolean onlyVisibleArea) {
128: if (onlyVisibleArea) {
129: return UIUtils.createScreenshot(jScrollPane);
130: } else {
131: return UIUtils.createScreenshot(resTable);
132: }
133: }
134:
135: public String getViewName() {
136: return "memory-liveness-live"; // NOI18N
137: }
138:
139: public void actionPerformed(ActionEvent e) {
140: Object source = e.getSource();
141:
142: if (source == popupRemoveProfForClass) {
143: MemoryCCTProvider olcgb = runner.getProfilerClient()
144: .getMemoryCCTProvider();
145: boolean[] newlyUnprofiledClasses = new boolean[sortedClassIds.length];
146: int line = ((Integer) filteredToFullIndexes
147: .get(clickedLine)).intValue();
148:
149: if (!olcgb.classMarkedUnprofiled(sortedClassIds[line])) {
150: olcgb.markClassUnprofiled(sortedClassIds[line]);
151: newlyUnprofiledClasses[sortedClassIds[line]] = true;
152:
153: if (line < nTrackedAllocObjects.length) { // The following arrays may actually be smaller
154: nTrackedAllocObjects[line] = 0;
155: nTrackedLiveObjects[line] = 0;
156: trackedLiveObjectsSize[line] = 0;
157: avgObjectAge[line] = 0;
158: maxSurvGen[line] = 0;
159: }
160:
161: nTotalAllocObjects[line] = 0;
162:
163: deinstrumentMemoryProfiledClasses(newlyUnprofiledClasses);
164: }
165:
166: prepareResults();
167: } else if (source == popupRemoveProfForClassesBelow) {
168: int line = clickedLine;
169: MemoryCCTProvider olcgb = runner.getProfilerClient()
170: .getMemoryCCTProvider();
171: boolean[] newlyUnprofiledClasses = new boolean[sortedClassIds.length];
172: int nClasses = filteredToFullIndexes.size();
173:
174: for (int i = line + 1; i < nClasses; i++) {
175: int index = ((Integer) filteredToFullIndexes.get(i))
176: .intValue();
177:
178: if (!olcgb.classMarkedUnprofiled(sortedClassIds[index])) {
179: olcgb.markClassUnprofiled(sortedClassIds[index]);
180: newlyUnprofiledClasses[sortedClassIds[index]] = true;
181: nTrackedAllocObjects[index] = 0;
182: nTrackedLiveObjects[index] = 0;
183: trackedLiveObjectsSize[index] = 0;
184: avgObjectAge[index] = 0;
185: maxSurvGen[index] = 0;
186: nTotalAllocObjects[index] = 0;
187: }
188: }
189:
190: deinstrumentMemoryProfiledClasses(newlyUnprofiledClasses);
191: prepareResults();
192: } else if (source == popupShowSource) {
193: showSourceForClass(selectedClassId);
194: } else if (source == popupShowStacks) {
195: actionsHandler.showStacksForClass(selectedClassId,
196: getSortingColumn(), getSortingOrder());
197: } else if ((e.getSource() == startHisto) && (handler != null)) {
198: handler.performAction("history logging", new Object[] {
199: new Integer(selectedClassId),
200: getClassName(selectedClassId), Boolean.TRUE }); // NOI18N
201: }
202: }
203:
204: public void fetchResultsFromTargetApp()
205: throws ClientUtils.TargetAppOrVMTerminated {
206: MemoryCCTProvider olcgb = runner.getProfilerClient()
207: .getMemoryCCTProvider();
208:
209: if (olcgb == null) {
210: throw new ClientUtils.TargetAppOrVMTerminated(
211: ClientUtils.TargetAppOrVMTerminated.VM);
212: } else {
213: MemoryCCTProvider.ObjectNumbersContainer onc = olcgb
214: .getLivenessObjectNumbers();
215: nTrackedAllocObjects = onc.nTrackedAllocObjects;
216: nTrackedLiveObjects = onc.nTrackedLiveObjects;
217: trackedLiveObjectsSize = onc.trackedLiveObjectsSize;
218: avgObjectAge = onc.avgObjectAge;
219: maxSurvGen = onc.maxSurvGen;
220: nInstrClasses = onc.nInstrClasses;
221:
222: if (((nTrackedLiveObjects == null) && (nTrackedAllocObjects == null))
223: || (avgObjectAge == null) || (maxSurvGen == null)) {
224: return;
225: }
226:
227: // This returns the array containing the total number of allocated objects for each class.
228: nTotalAllocObjects = runner.getProfilerClient()
229: .getAllocatedObjectsCountResults();
230:
231: // Below is a bit of "defensive programming". Normally the sizes of arrays here should be same
232: // except for nTotalAllocObjects, that is returned from the server, and may be shorter if some
233: // instrumented classes have not propagated to the server yet.
234: nTrackedItems = Math.min(nTrackedAllocObjects.length,
235: nTrackedLiveObjects.length);
236: nTrackedItems = Math.min(nTrackedItems,
237: trackedLiveObjectsSize.length);
238: nTrackedItems = Math
239: .min(nTrackedItems, avgObjectAge.length);
240: nTrackedItems = Math.min(nTrackedItems, maxSurvGen.length);
241: nTrackedItems = Math.min(nTrackedItems, nInstrClasses);
242: nTrackedItems = Math.min(nTrackedItems,
243: nTotalAllocObjects.length);
244:
245: // Now if some classes are unprofiled, reflect that in nTotalAllocObjects
246: //for (int i = 0; i < nTrackedAllocObjects.length; i++) {
247: for (int i = 0; i < nTrackedItems; i++) {
248: if (nTrackedAllocObjects[i] == -1) {
249: nTotalAllocObjects[i] = 0;
250: }
251: }
252:
253: // Operations necessary for correct bar representation of results
254: maxValue = 0;
255: nTotalTrackedBytes = 0;
256: nTotalTracked = 0;
257:
258: //for (int i = 0; i < trackedLiveObjectsSize.length; i++) {
259: for (int i = 0; i < nTrackedItems; i++) {
260: if (maxValue < trackedLiveObjectsSize[i]) {
261: maxValue = trackedLiveObjectsSize[i];
262: }
263:
264: nTotalTrackedBytes += trackedLiveObjectsSize[i];
265: nTotalTracked += nTrackedLiveObjects[i];
266: }
267:
268: if (handler != null) {
269: handler.performAction("history update", new Object[] {
270: nTrackedLiveObjects, nTotalAllocObjects }); // NOI18N
271: }
272:
273: initDataUponResultsFetch();
274: }
275: }
276:
277: public boolean fitsVisibleArea() {
278: return !jScrollPane.getVerticalScrollBar().isEnabled();
279: }
280:
281: public void handleRemove() {
282: }
283:
284: /**
285: * Called when auto refresh is on and profiling session will finish
286: * to give the panel chance to do some cleanup before asynchrounous
287: * call to updateLiveResults() will happen.
288: *
289: * Currently it closes the context menu if open, which would otherwise
290: * block updating the results.
291: */
292: public void handleShutdown() {
293: // Profiling session will finish and context menu is opened, this would block last live results update -> menu will be closed
294: if ((popup != null) && popup.isVisible()) {
295: updateResultsPending = false; // clear the flag, updateLiveResults() will be called explicitely from outside
296: popup.setVisible(false); // close the context menu
297: }
298: }
299:
300: // --- Save current View action support --------------------------------------
301: public boolean hasView() {
302: return resTable != null;
303: }
304:
305: public boolean supports(int instrumentationType) {
306: return instrumentationType == CommonConstants.INSTR_OBJECT_LIVENESS;
307: }
308:
309: public void updateLiveResults() {
310: if ((popup != null) && popup.isVisible()) {
311: updateResultsPending = true;
312:
313: return;
314: }
315:
316: if (updateResultsInProgress == true) {
317: return;
318: }
319:
320: updateResultsInProgress = true;
321:
322: String selectedRowString = null;
323:
324: if (resTable != null) {
325: int selectedRowIndex = resTable.getSelectedRow();
326:
327: if (selectedRowIndex >= resTable.getRowCount()) {
328: selectedRowIndex = -1;
329: resTable.clearSelection();
330: }
331:
332: if (selectedRowIndex != -1) {
333: selectedRowString = resTable.getValueAt(
334: selectedRowIndex, 0).toString();
335: }
336: }
337:
338: try {
339: if (runner.getProfilingSessionStatus().targetAppRunning) {
340: reset();
341: fetchResultsFromTargetApp();
342: }
343:
344: prepareResults();
345:
346: if (selectedRowString != null) {
347: resTable.selectRowByContents(selectedRowString, 0,
348: false);
349: }
350:
351: if ((resTable != null) && resTable.isFocusOwner()) {
352: resTable.requestFocusInWindow(); // prevents results table from losing focus
353: }
354: } catch (ClientUtils.TargetAppOrVMTerminated targetAppOrVMTerminated) {
355: targetAppOrVMTerminated.printStackTrace(System.err);
356: }
357:
358: updateResultsInProgress = false;
359: }
360:
361: protected String getClassName(int classId) {
362: return status.getClassNames()[classId];
363: }
364:
365: protected String[] getClassNames() {
366: return status.getClassNames();
367: }
368:
369: protected int getPercentsTracked() {
370: return 100 / runner.getProfilerEngineSettings()
371: .getAllocTrackEvery();
372: }
373:
374: protected JPopupMenu getPopupMenu() {
375: if (popup == null) {
376: popup = new JPopupMenu();
377:
378: Font boldfont = popup.getFont().deriveFont(Font.BOLD);
379:
380: popupShowSource = new JMenuItem();
381: popupRemoveProfForClass = new JMenuItem();
382: popupRemoveProfForClassesBelow = new JMenuItem();
383:
384: popupShowSource.setText(GO_SOURCE_POPUP_ITEM_NAME);
385: popupShowSource.setFont(boldfont);
386: popupRemoveProfForClass.setText(STOP_CLASS_POPUP_ITEM_NAME);
387: popupRemoveProfForClassesBelow
388: .setText(STOP_BELOW_LINE_POPUP_ITEM_NAME);
389:
390: popup.add(popupShowSource);
391:
392: if (runner.getProfilerEngineSettings()
393: .getAllocStackTraceLimit() != 0) {
394: popup.addSeparator();
395: popupShowStacks = new JMenuItem();
396: popupShowStacks
397: .setText(SHOW_STACK_TRACES_POPUP_ITEM_NAME);
398: popup.add(popupShowStacks);
399: popupShowStacks.addActionListener(this );
400: }
401:
402: popup.addSeparator();
403: popup.add(popupRemoveProfForClass);
404: popup.add(popupRemoveProfForClassesBelow);
405:
406: popupShowSource.addActionListener(this );
407: popupRemoveProfForClass.addActionListener(this );
408: popupRemoveProfForClassesBelow.addActionListener(this );
409:
410: popup.addSeparator();
411: startHisto = new JMenuItem();
412: startHisto.setText(LOG_CLASS_HISTORY);
413: popup.add(startHisto);
414: startHisto.addActionListener(this );
415:
416: popup.addPopupMenuListener(new PopupMenuListener() {
417: public void popupMenuCanceled(PopupMenuEvent e) {
418: }
419:
420: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
421: }
422:
423: public void popupMenuWillBecomeInvisible(
424: PopupMenuEvent e) {
425: SwingUtilities.invokeLater(new Runnable() {
426: public void run() {
427: if (updateResultsPending) {
428: updateLiveResults();
429: updateResultsPending = false;
430: }
431: }
432: });
433: }
434: });
435: }
436:
437: return popup;
438: }
439:
440: /**
441: * When the user invokes a popup menu, we need to adjust the name for the "Stop profiling classes below this line"
442: * item to include the concrete class name, so that the user knows exactly what they are going to remove.
443: */
444: protected void adjustFramePopupMenuTextIfNecessary() {
445: //String name = StringUtils.userFormClassName(sortedClassNames[clickedLine]);
446: String name = sortedClassNames[clickedLine];
447: popupRemoveProfForClass
448: .setText(MessageFormat.format(
449: STOP_CLASS_SPEC_POPUP_ITEM_NAME,
450: new Object[] { name }));
451: popupRemoveProfForClassesBelow.setText(MessageFormat.format(
452: STOP_BELOW_LINE_SPEC_POPUP_ITEM_NAME,
453: new Object[] { name }));
454: }
455:
456: protected void performDefaultAction(int classId) {
457: showSourceForClass(classId);
458: }
459:
460: /**
461: * Disable memory profiling for classes with ids such that newlyUnprofiledClasses[id] = true
462: */
463: private void deinstrumentMemoryProfiledClasses(
464: boolean[] newlyUnprofiledClasses) {
465: try {
466: runner.getProfilerClient()
467: .deinstrumentMemoryProfiledClasses(
468: newlyUnprofiledClasses);
469: } catch (InstrumentationException ex1) {
470: runner.getAppStatusHandler().displayError(ex1.getMessage());
471: } catch (ClientUtils.TargetAppOrVMTerminated ex2) {
472: runner.getAppStatusHandler().displayError(ex2.getMessage());
473: }
474: }
475: }
|