blob: 9ae362baa48ee7546b0e05896cdc211aab1301d2 [file] [log] [blame]
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package sun.jvm.hotspot;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.math.*;
import javax.swing.*;
import javax.swing.tree.*;
import java.util.*;
import sun.jvm.hotspot.code.*;
import sun.jvm.hotspot.compiler.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.gc_implementation.parallelScavenge.*;
import sun.jvm.hotspot.gc_interface.*;
import sun.jvm.hotspot.interpreter.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.oops.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.ui.*;
import sun.jvm.hotspot.ui.tree.*;
import sun.jvm.hotspot.ui.classbrowser.*;
import sun.jvm.hotspot.utilities.*;
/** The top-level HotSpot Debugger. FIXME: make this an embeddable
component! (Among other things, figure out what to do with the
menu bar...) */
public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
public static void main(String[] args) {
new HSDB(args).run();
}
//--------------------------------------------------------------------------------
// Internals only below this point
//
private HotSpotAgent agent;
private JVMDebugger jvmDebugger;
private JDesktopPane desktop;
private boolean attached;
private boolean argError;
private JFrame frame;
/** List <JMenuItem> */
private java.util.List attachMenuItems;
/** List <JMenuItem> */
private java.util.List detachMenuItems;
private JMenu toolsMenu;
private JMenuItem showDbgConsoleMenuItem;
private JMenuItem computeRevPtrsMenuItem;
private JInternalFrame attachWaitDialog;
private JInternalFrame threadsFrame;
private JInternalFrame consoleFrame;
private WorkerThread workerThread;
// These had to be made data members because they are referenced in inner classes.
private String pidText;
private int pid;
private String execPath;
private String coreFilename;
private void doUsage() {
System.out.println("Usage: java HSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]");
System.out.println(" pid: attach to the process whose id is 'pid'");
System.out.println(" path-to-java-executable: Debug a core file produced by this program");
System.out.println(" path-to-corefile: Debug this corefile. The default is 'core'");
System.out.println(" If no arguments are specified, you can select what to do from the GUI.\n");
HotSpotAgent.showUsage();
argError = true;
}
public HSDB(JVMDebugger d) {
jvmDebugger = d;
}
private HSDB(String[] args) {
switch (args.length) {
case (0):
break;
case (1):
if (args[0].equals("help") || args[0].equals("-help")) {
doUsage();
}
// If all numbers, it is a PID to attach to
// Else, it is a pathname to a .../bin/java for a core file.
try {
int unused = Integer.parseInt(args[0]);
// If we get here, we have a PID and not a core file name
pidText = args[0];
} catch (NumberFormatException e) {
execPath = args[0];
coreFilename = "core";
}
break;
case (2):
execPath = args[0];
coreFilename = args[1];
break;
default:
System.out.println("HSDB Error: Too many options specified");
doUsage();
}
}
// close this tool without calling System.exit
protected void closeUI() {
workerThread.shutdown();
frame.dispose();
}
public void run() {
// Don't start the UI if there were bad arguments.
if (argError) {
return;
}
agent = new HotSpotAgent();
workerThread = new WorkerThread();
attachMenuItems = new java.util.ArrayList();
detachMenuItems = new java.util.ArrayList();
frame = new JFrame("HSDB - HotSpot Debugger");
frame.setSize(800, 600);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JMenuBar menuBar = new JMenuBar();
//
// File menu
//
JMenu menu = new JMenu("File");
menu.setMnemonic(KeyEvent.VK_F);
JMenuItem item;
item = createMenuItem("Attach to HotSpot process...",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showAttachDialog();
}
});
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.ALT_MASK));
item.setMnemonic(KeyEvent.VK_A);
menu.add(item);
attachMenuItems.add(item);
item = createMenuItem("Open HotSpot core file...",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showOpenCoreFileDialog();
}
});
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK));
item.setMnemonic(KeyEvent.VK_O);
menu.add(item);
attachMenuItems.add(item);
item = createMenuItem("Connect to debug server...",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showConnectDialog();
}
});
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.ALT_MASK));
item.setMnemonic(KeyEvent.VK_S);
menu.add(item);
attachMenuItems.add(item);
item = createMenuItem("Detach",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
detach();
}
});
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.ALT_MASK));
item.setMnemonic(KeyEvent.VK_S);
menu.add(item);
detachMenuItems.add(item);
// Disable detach menu items at first
setMenuItemsEnabled(detachMenuItems, false);
menu.addSeparator();
item = createMenuItem("Exit",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
closeUI();
}
});
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.ALT_MASK));
item.setMnemonic(KeyEvent.VK_X);
menu.add(item);
menuBar.add(menu);
//
// Tools menu
//
toolsMenu = new JMenu("Tools");
toolsMenu.setMnemonic(KeyEvent.VK_T);
item = createMenuItem("Class Browser",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showClassBrowser();
}
});
item.setMnemonic(KeyEvent.VK_B);
toolsMenu.add(item);
item = createMenuItem("Code Viewer",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showCodeViewer();
}
});
item.setMnemonic(KeyEvent.VK_C);
toolsMenu.add(item);
item = createMenuItem("Compute Reverse Ptrs",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
fireComputeReversePtrs();
}
});
computeRevPtrsMenuItem = item;
item.setMnemonic(KeyEvent.VK_M);
toolsMenu.add(item);
item = createMenuItem("Deadlock Detection",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showDeadlockDetectionPanel();
}
});
item.setMnemonic(KeyEvent.VK_D);
toolsMenu.add(item);
item = createMenuItem("Find Object by Query",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showFindByQueryPanel();
}
});
item.setMnemonic(KeyEvent.VK_Q);
toolsMenu.add(item);
item = createMenuItem("Find Pointer",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showFindPanel();
}
});
item.setMnemonic(KeyEvent.VK_P);
toolsMenu.add(item);
item = createMenuItem("Find Value In Heap",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showFindInHeapPanel();
}
});
item.setMnemonic(KeyEvent.VK_V);
toolsMenu.add(item);
item = createMenuItem("Find Value In Code Cache",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showFindInCodeCachePanel();
}
});
item.setMnemonic(KeyEvent.VK_A);
toolsMenu.add(item);
item = createMenuItem("Heap Parameters",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showHeapParametersPanel();
}
});
item.setMnemonic(KeyEvent.VK_H);
toolsMenu.add(item);
item = createMenuItem("Inspector",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showInspector(null);
}
});
item.setMnemonic(KeyEvent.VK_R);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK));
toolsMenu.add(item);
item = createMenuItem("Memory Viewer",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showMemoryViewer();
}
});
item.setMnemonic(KeyEvent.VK_M);
toolsMenu.add(item);
item = createMenuItem("Monitor Cache Dump",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showMonitorCacheDumpPanel();
}
});
item.setMnemonic(KeyEvent.VK_D);
toolsMenu.add(item);
item = createMenuItem("Object Histogram",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showObjectHistogram();
}
});
item.setMnemonic(KeyEvent.VK_O);
toolsMenu.add(item);
item = createMenuItem("Show System Properties",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showSystemProperties();
}
});
item.setMnemonic(KeyEvent.VK_S);
toolsMenu.add(item);
item = createMenuItem("Show VM Version",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showVMVersion();
}
});
item.setMnemonic(KeyEvent.VK_M);
toolsMenu.add(item);
item = createMenuItem("Show -XX flags",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showCommandLineFlags();
}
});
item.setMnemonic(KeyEvent.VK_X);
toolsMenu.add(item);
toolsMenu.setEnabled(false);
menuBar.add(toolsMenu);
//
// Windows menu
//
JMenu windowsMenu = new JMenu("Windows");
windowsMenu.setMnemonic(KeyEvent.VK_W);
item = createMenuItem("Console",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showConsole();
}
});
item.setMnemonic(KeyEvent.VK_C);
windowsMenu.add(item);
showDbgConsoleMenuItem = createMenuItem("Debugger Console",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
showDebuggerConsole();
}
});
showDbgConsoleMenuItem.setMnemonic(KeyEvent.VK_D);
windowsMenu.add(showDbgConsoleMenuItem);
showDbgConsoleMenuItem.setEnabled(false);
menuBar.add(windowsMenu);
frame.setJMenuBar(menuBar);
desktop = new JDesktopPane();
frame.getContentPane().add(desktop);
GraphicsUtilities.reshapeToAspectRatio(frame, 4.0f/3.0f, 0.75f, Toolkit.getDefaultToolkit().getScreenSize());
GraphicsUtilities.centerInContainer(frame, Toolkit.getDefaultToolkit().getScreenSize());
frame.setVisible(true);
Runtime.getRuntime().addShutdownHook(new java.lang.Thread() {
public void run() {
detachDebugger();
}
});
// If jvmDebugger is already set, we have been given a JVMDebugger.
// Otherwise, if pidText != null we are supposed to attach to it.
// Finally, if execPath != null, it is the path of a jdk/bin/java
// and coreFilename is the pathname of a core file we are
// supposed to attach to.
if (jvmDebugger != null) {
attach(jvmDebugger);
} else if (pidText != null) {
attach(pidText);
} else if (execPath != null) {
attach(execPath, coreFilename);
}
}
// FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
private void showAttachDialog() {
// FIXME: create filtered text field which only accepts numbers
setMenuItemsEnabled(attachMenuItems, false);
final JInternalFrame attachDialog = new JInternalFrame("Attach to HotSpot process");
attachDialog.getContentPane().setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
attachDialog.setBackground(panel.getBackground());
panel.add(new JLabel("Enter process ID:"));
final JTextField pidTextField = new JTextField(10);
ActionListener attacher = new ActionListener() {
public void actionPerformed(ActionEvent e) {
attachDialog.setVisible(false);
desktop.remove(attachDialog);
workerThread.invokeLater(new Runnable() {
public void run() {
attach(pidTextField.getText());
}
});
}
};
pidTextField.addActionListener(attacher);
panel.add(pidTextField);
attachDialog.getContentPane().add(panel, BorderLayout.NORTH);
Box vbox = Box.createVerticalBox();
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
JTextArea ta = new JTextArea(
"Enter the process ID of a currently-running HotSpot process. On " +
"Solaris and most Unix operating systems, this can be determined by " +
"typing \"ps -u <your username> | grep java\"; the process ID is the " +
"first number which appears on the resulting line. On Windows, the " +
"process ID is present in the Task Manager, which can be brought up " +
"while logged on to the desktop by pressing Ctrl-Alt-Delete.");
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
ta.setEditable(false);
ta.setBackground(panel.getBackground());
panel.add(ta);
vbox.add(panel);
Box hbox = Box.createHorizontalBox();
hbox.add(Box.createGlue());
JButton button = new JButton("OK");
button.addActionListener(attacher);
hbox.add(button);
hbox.add(Box.createHorizontalStrut(20));
button = new JButton("Cancel");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
attachDialog.setVisible(false);
desktop.remove(attachDialog);
setMenuItemsEnabled(attachMenuItems, true);
}
});
hbox.add(button);
hbox.add(Box.createGlue());
panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.add(hbox);
vbox.add(panel);
attachDialog.getContentPane().add(vbox, BorderLayout.SOUTH);
desktop.add(attachDialog);
attachDialog.setSize(400, 300);
GraphicsUtilities.centerInContainer(attachDialog);
attachDialog.show();
pidTextField.requestFocus();
}
// FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
private void showOpenCoreFileDialog() {
setMenuItemsEnabled(attachMenuItems, false);
final JInternalFrame dialog = new JInternalFrame("Open Core File");
dialog.getContentPane().setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
dialog.setBackground(panel.getBackground());
Box hbox = Box.createHorizontalBox();
Box vbox = Box.createVerticalBox();
vbox.add(new JLabel("Path to core file:"));
vbox.add(new JLabel("Path to Java executable:"));
hbox.add(vbox);
vbox = Box.createVerticalBox();
final JTextField corePathField = new JTextField(40);
final JTextField execPathField = new JTextField(40);
vbox.add(corePathField);
vbox.add(execPathField);
hbox.add(vbox);
final JButton browseCorePath = new JButton("Browse ..");
final JButton browseExecPath = new JButton("Browse ..");
browseCorePath.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser(new File("."));
int retVal = fileChooser.showOpenDialog(dialog);
if (retVal == JFileChooser.APPROVE_OPTION) {
corePathField.setText(fileChooser.getSelectedFile().getPath());
}
}
});
browseExecPath.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser(new File("."));
int retVal = fileChooser.showOpenDialog(dialog);
if (retVal == JFileChooser.APPROVE_OPTION) {
execPathField.setText(fileChooser.getSelectedFile().getPath());
}
}
});
vbox = Box.createVerticalBox();
vbox.add(browseCorePath);
vbox.add(browseExecPath);
hbox.add(vbox);
panel.add(hbox);
dialog.getContentPane().add(panel, BorderLayout.NORTH);
ActionListener attacher = new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
desktop.remove(dialog);
workerThread.invokeLater(new Runnable() {
public void run() {
attach(execPathField.getText(), corePathField.getText());
}
});
}
};
corePathField.addActionListener(attacher);
execPathField.addActionListener(attacher);
vbox = Box.createVerticalBox();
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
JTextArea ta = new JTextArea(
"Enter the full path names to the core file from a HotSpot process " +
"and the Java executable from which it came. The latter is typically " +
"located in the JDK/JRE directory under the directory " +
"jre/bin/<arch>/native_threads.");
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
ta.setEditable(false);
ta.setBackground(panel.getBackground());
panel.add(ta);
vbox.add(panel);
hbox = Box.createHorizontalBox();
hbox.add(Box.createGlue());
JButton button = new JButton("OK");
button.addActionListener(attacher);
hbox.add(button);
hbox.add(Box.createHorizontalStrut(20));
button = new JButton("Cancel");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
desktop.remove(dialog);
setMenuItemsEnabled(attachMenuItems, true);
}
});
hbox.add(button);
hbox.add(Box.createGlue());
panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.add(hbox);
vbox.add(panel);
dialog.getContentPane().add(vbox, BorderLayout.SOUTH);
desktop.add(dialog);
dialog.setSize(500, 300);
GraphicsUtilities.centerInContainer(dialog);
dialog.show();
corePathField.requestFocus();
}
// FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
private void showConnectDialog() {
// FIXME: create filtered text field which only accepts numbers
setMenuItemsEnabled(attachMenuItems, false);
final JInternalFrame dialog = new JInternalFrame("Connect to HotSpot Debug Server");
dialog.getContentPane().setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
dialog.setBackground(panel.getBackground());
panel.add(new JLabel("Enter machine name:"));
final JTextField pidTextField = new JTextField(40);
ActionListener attacher = new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
desktop.remove(dialog);
workerThread.invokeLater(new Runnable() {
public void run() {
connect(pidTextField.getText());
}
});
}
};
pidTextField.addActionListener(attacher);
panel.add(pidTextField);
dialog.getContentPane().add(panel, BorderLayout.NORTH);
Box vbox = Box.createVerticalBox();
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
JTextArea ta = new JTextArea(
"Enter the name of a machine on which the HotSpot \"Debug Server\" is " +
"running and is attached to a process or core file.");
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
ta.setEditable(false);
ta.setBackground(panel.getBackground());
panel.add(ta);
vbox.add(panel);
Box hbox = Box.createHorizontalBox();
hbox.add(Box.createGlue());
JButton button = new JButton("OK");
button.addActionListener(attacher);
hbox.add(button);
hbox.add(Box.createHorizontalStrut(20));
button = new JButton("Cancel");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
desktop.remove(dialog);
setMenuItemsEnabled(attachMenuItems, true);
}
});
hbox.add(button);
hbox.add(Box.createGlue());
panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.add(hbox);
vbox.add(panel);
dialog.getContentPane().add(vbox, BorderLayout.SOUTH);
desktop.add(dialog);
dialog.setSize(400, 300);
GraphicsUtilities.centerInContainer(dialog);
dialog.show();
pidTextField.requestFocus();
}
public void showThreadOopInspector(JavaThread thread) {
showInspector(new OopTreeNodeAdapter(thread.getThreadObj(), null));
}
public void showInspector(SimpleTreeNode adapter) {
showPanel("Inspector", new Inspector(adapter), 1.0f, 0.65f);
}
public void showLiveness(Oop oop, LivenessPathList liveness) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream tty = new PrintStream(bos);
int numPaths = liveness.size();
for (int i = 0; i < numPaths; i++) {
tty.println("Path " + (i + 1) + " of " + numPaths + ":");
liveness.get(i).printOn(tty);
}
JTextArea ta = new JTextArea(bos.toString());
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
ta.setEditable(false);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JScrollPane scroller = new JScrollPane();
scroller.getViewport().add(ta);
panel.add(scroller, BorderLayout.CENTER);
bos = new ByteArrayOutputStream();
tty = new PrintStream(bos);
tty.print("Liveness result for ");
Oop.printOopValueOn(oop, tty);
JInternalFrame frame = new JInternalFrame(bos.toString());
frame.setResizable(true);
frame.setClosable(true);
frame.setIconifiable(true);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.pack();
desktop.add(frame);
GraphicsUtilities.reshapeToAspectRatio(frame, 0.5f / 0.2f, 0.5f, frame.getParent().getSize());
frame.show();
}
private void fireComputeReversePtrs() {
// Possible this might have been computed elsewhere
if (VM.getVM().getRevPtrs() != null) {
computeRevPtrsMenuItem.setEnabled(false);
return;
}
workerThread.invokeLater(new Runnable() {
public void run() {
HeapProgress progress = new HeapProgress("Reverse Pointers Analysis");
try {
ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
analysis.setHeapProgressThunk(progress);
analysis.run();
computeRevPtrsMenuItem.setEnabled(false);
} catch (OutOfMemoryError e) {
final String errMsg = formatMessage(e.toString(), 80);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showInternalMessageDialog(desktop,
"Error computing reverse pointers:" + errMsg,
"Error",
JOptionPane.WARNING_MESSAGE);
}
});
} finally {
// make sure the progress bar goes away
progress.heapIterationComplete();
}
}
});
}
// Simple struct containing signal information
class SignalInfo {
public int sigNum;
public String sigName;
}
// Need to have mutable vframe as well as visible memory panel
abstract class StackWalker implements Runnable {
protected JavaVFrame vf;
protected AnnotatedMemoryPanel annoPanel;
StackWalker(JavaVFrame vf, AnnotatedMemoryPanel annoPanel) {
this.vf = vf;
this.annoPanel = annoPanel;
}
}
public void showThreadStackMemory(final JavaThread thread) {
// dumpStack(thread);
JavaVFrame vframe = getLastJavaVFrame(thread);
if (vframe == null) {
JOptionPane.showInternalMessageDialog(desktop,
"Thread \"" + thread.getThreadName() +
"\" has no Java frames on its stack",
"Show Stack Memory",
JOptionPane.INFORMATION_MESSAGE);
return;
}
JInternalFrame stackFrame = new JInternalFrame("Stack Memory for " + thread.getThreadName());
stackFrame.getContentPane().setLayout(new BorderLayout());
stackFrame.setResizable(true);
stackFrame.setClosable(true);
stackFrame.setIconifiable(true);
final long addressSize = agent.getTypeDataBase().getAddressSize();
boolean is64Bit = (addressSize == 8);
// This is somewhat of a hack to guess a thread's stack limits since the
// JavaThread doesn't support this functionality. However it is nice in that
// it locks us into the active region of the thread's stack and not its
// theoretical limits.
//
sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess();
Address sp = tmpFrame.getSP();
Address starting = sp;
Address maxSP = starting;
Address minSP = starting;
RegisterMap tmpMap = thread.newRegisterMap(false);
while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) {
tmpFrame = tmpFrame.sender(tmpMap);
if (tmpFrame != null) {
sp = tmpFrame.getSP();
if (sp != null) {
maxSP = AddressOps.max(maxSP, sp);
minSP = AddressOps.min(minSP, sp);
}
}
}
// It is useful to be able to see say +/- 8K on the current stack range
AnnotatedMemoryPanel annoMemPanel = new AnnotatedMemoryPanel(agent.getDebugger(), is64Bit, starting,
minSP.addOffsetTo(-8192),
maxSP.addOffsetTo( 8192));
stackFrame.getContentPane().add(annoMemPanel, BorderLayout.CENTER);
desktop.add(stackFrame);
GraphicsUtilities.reshapeToAspectRatio(stackFrame, 4.0f / 3.0f, 0.85f, stackFrame.getParent().getSize());
stackFrame.show();
// Stackmap computation for interpreted frames is expensive; do
// all stackwalking work in another thread for better GUI
// responsiveness
workerThread.invokeLater(new StackWalker(vframe, annoMemPanel) {
public void run() {
Address startAddr = null;
// As this is a debugger, we want to provide potential crash
// information to the user, i.e., by marking signal handler frames
// on the stack. Since this system is currently targeted at
// annotating the Java frames (interpreted or compiled) on the
// stack and not, for example, "external" frames (note the current
// absence of a PC-to-symbol lookup mechanism at the Debugger
// level), we want to mark any Java frames which were interrupted
// by a signal. We do this by making two passes over the stack,
// one which finds signal handler frames and puts the parent
// frames in a table and one which finds Java frames and if they
// are in the table indicates that they were interrupted by a signal.
Map interruptedFrameMap = new HashMap();
{
sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess();
RegisterMap tmpMap = thread.newRegisterMap(false);
while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) {
if (tmpFrame.isSignalHandlerFrameDbg()) {
// Add some information to the map that we can extract later
sun.jvm.hotspot.runtime.Frame interruptedFrame = tmpFrame.sender(tmpMap);
SignalInfo info = new SignalInfo();
info.sigNum = tmpFrame.getSignalNumberDbg();
info.sigName = tmpFrame.getSignalNameDbg();
interruptedFrameMap.put(interruptedFrame, info);
}
tmpFrame = tmpFrame.sender(tmpMap);
}
}
while (vf != null) {
String anno = null;
JavaVFrame curVFrame = vf;
sun.jvm.hotspot.runtime.Frame curFrame = curVFrame.getFrame();
Method interpreterFrameMethod = null;
if (curVFrame.isInterpretedFrame()) {
anno = "Interpreted frame";
} else {
anno = "Compiled frame";
if (curVFrame.isDeoptimized()) {
anno += " (deoptimized)";
}
}
if (curVFrame.mayBeImpreciseDbg()) {
anno += "; information may be imprecise";
}
if (curVFrame.isInterpretedFrame()) {
// Find the codelet
InterpreterCodelet codelet = VM.getVM().getInterpreter().getCodeletContaining(curFrame.getPC());
String description = null;
if (codelet != null) {
description = codelet.getDescription();
}
if (description == null) {
anno += "\n(Unknown interpreter codelet)";
} else {
anno += "\nExecuting in codelet \"" + description + "\" at PC = " + curFrame.getPC();
}
} else if (curVFrame.isCompiledFrame()) {
anno += "\nExecuting at PC = " + curFrame.getPC();
}
if (startAddr == null) {
startAddr = curFrame.getSP();
}
// FIXME: some compiled frames with empty oop map sets have been
// found (for example, Vector's inner Enumeration class, method
// "hasMoreElements"). Not sure yet why these cases are showing
// up -- should be possible (though unlikely) for safepoint code
// to patch the return instruction of these methods and then
// later attempt to get an oop map for that instruction. For
// now, we warn if we find such a method.
boolean shouldSkipOopMaps = false;
if (curVFrame.isCompiledFrame()) {
CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC());
OopMapSet maps = cb.getOopMaps();
if ((maps == null) || (maps.getSize() == 0)) {
shouldSkipOopMaps = true;
}
}
// Add signal information to annotation if necessary
SignalInfo sigInfo = (SignalInfo) interruptedFrameMap.get(curFrame);
if (sigInfo != null) {
// This frame took a signal and we need to report it.
anno = (anno + "\n*** INTERRUPTED BY SIGNAL " + Integer.toString(sigInfo.sigNum) +
" (" + sigInfo.sigName + ")");
}
JavaVFrame nextVFrame = curVFrame;
sun.jvm.hotspot.runtime.Frame nextFrame = curFrame;
do {
curVFrame = nextVFrame;
curFrame = nextFrame;
try {
Method method = curVFrame.getMethod();
if (interpreterFrameMethod == null && curVFrame.isInterpretedFrame()) {
interpreterFrameMethod = method;
}
int bci = curVFrame.getBCI();
String lineNumberAnno = "";
if (method.hasLineNumberTable()) {
if ((bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) ||
(bci >= 0 && bci < method.getCodeSize())) {
lineNumberAnno = ", line " + method.getLineNumberFromBCI(bci);
} else {
lineNumberAnno = " (INVALID BCI)";
}
}
anno += "\n" + method.getMethodHolder().getName().asString() + "." +
method.getName().asString() + method.getSignature().asString() +
"\n@bci " + bci + lineNumberAnno;
} catch (Exception e) {
anno += "\n(ERROR while iterating vframes for frame " + curFrame + ")";
}
nextVFrame = curVFrame.javaSender();
if (nextVFrame != null) {
nextFrame = nextVFrame.getFrame();
}
} while (nextVFrame != null && nextFrame.equals(curFrame));
if (shouldSkipOopMaps) {
anno = anno + "\nNOTE: null or empty OopMapSet found for this CodeBlob";
}
if (curFrame.getFP() != null) {
annoPanel.addAnnotation(new Annotation(curFrame.getSP(),
curFrame.getFP(),
anno));
} else {
if (VM.getVM().getCPU().equals("x86") || VM.getVM().getCPU().equals("amd64")) {
// For C2, which has null frame pointers on x86/amd64
CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC());
Address sp = curFrame.getSP();
if (Assert.ASSERTS_ENABLED) {
Assert.that(cb.getFrameSize() > 0, "CodeBlob must have non-zero frame size");
}
annoPanel.addAnnotation(new Annotation(sp,
sp.addOffsetTo(cb.getFrameSize()),
anno));
} else {
Assert.that(VM.getVM().getCPU().equals("ia64"), "only ia64 should reach here");
}
}
// Add interpreter frame annotations
if (curFrame.isInterpretedFrame()) {
annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameExpressionStack(),
curFrame.addressOfInterpreterFrameTOS(),
"Interpreter expression stack"));
Address monBegin = curFrame.interpreterFrameMonitorBegin().address();
Address monEnd = curFrame.interpreterFrameMonitorEnd().address();
if (!monBegin.equals(monEnd)) {
annoPanel.addAnnotation(new Annotation(monBegin, monEnd,
"BasicObjectLocks"));
}
if (interpreterFrameMethod != null) {
// The offset is just to get the right stack slots highlighted in the output
int offset = 1;
annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameLocal(offset),
curFrame.addressOfInterpreterFrameLocal((int) interpreterFrameMethod.getMaxLocals() + offset),
"Interpreter locals area for frame with SP = " + curFrame.getSP()));
}
String methodAnno = "Interpreter frame Method*";
if (interpreterFrameMethod == null) {
methodAnno += " (BAD OOP)";
}
Address a = curFrame.addressOfInterpreterFrameMethod();
annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), methodAnno));
a = curFrame.addressOfInterpreterFrameCPCache();
annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache"));
}
RegisterMap rm = (RegisterMap) vf.getRegisterMap().clone();
if (!shouldSkipOopMaps) {
try {
curFrame.oopsDo(new AddressVisitor() {
public void visitAddress(Address addr) {
if (Assert.ASSERTS_ENABLED) {
Assert.that(addr.andWithMask(VM.getVM().getAddressSize() - 1) == null,
"Address " + addr + "should have been aligned");
}
OopHandle handle = addr.getOopHandleAt(0);
addAnnotation(addr, handle);
}
public void visitCompOopAddress(Address addr) {
if (Assert.ASSERTS_ENABLED) {
Assert.that(addr.andWithMask(VM.getVM().getAddressSize() - 1) == null,
"Address " + addr + "should have been aligned");
}
OopHandle handle = addr.getCompOopHandleAt(0);
addAnnotation(addr, handle);
}
public void addAnnotation(Address addr, OopHandle handle) {
// Check contents
String anno = "null oop";
if (handle != null) {
// Find location
CollectedHeap collHeap = VM.getVM().getUniverse().heap();
boolean bad = true;
anno = "BAD OOP";
if (collHeap instanceof GenCollectedHeap) {
GenCollectedHeap heap = (GenCollectedHeap) collHeap;
for (int i = 0; i < heap.nGens(); i++) {
if (heap.getGen(i).isIn(handle)) {
if (i == 0) {
anno = "NewGen ";
} else if (i == 1) {
anno = "OldGen ";
} else {
anno = "Gen " + i + " ";
}
bad = false;
break;
}
}
} else if (collHeap instanceof ParallelScavengeHeap) {
ParallelScavengeHeap heap = (ParallelScavengeHeap) collHeap;
if (heap.youngGen().isIn(handle)) {
anno = "PSYoungGen ";
bad = false;
} else if (heap.oldGen().isIn(handle)) {
anno = "PSOldGen ";
bad = false;
}
} else {
// Optimistically assume the oop isn't bad
anno = "[Unknown generation] ";
bad = false;
}
if (!bad) {
try {
Oop oop = VM.getVM().getObjectHeap().newOop(handle);
if (oop instanceof Instance) {
// Java-level objects always have workable names
anno = anno + oop.getKlass().getName().asString();
} else {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Oop.printOopValueOn(oop, new PrintStream(bos));
anno = anno + bos.toString();
}
}
catch (AddressException e) {
anno += "CORRUPT OOP";
}
catch (NullPointerException e) {
anno += "CORRUPT OOP (null pointer)";
}
}
}
annoPanel.addAnnotation(new Annotation(addr, addr.addOffsetTo(addressSize), anno));
}
}, rm);
} catch (Exception e) {
System.err.println("Error while performing oopsDo for frame " + curFrame);
e.printStackTrace();
}
}
vf = nextVFrame;
}
// This used to paint as we walked the frames. This caused the display to be refreshed
// enough to be annoying on remote displays. It also would cause the annotations to
// be displayed in varying order which caused some annotations to overwrite others
// depending on the races between painting and adding annotations. This latter problem
// still exists to some degree but moving this code here definitely seems to reduce it
annoPanel.makeVisible(startAddr);
annoPanel.repaint();
}
});
}
// Attach to existing JVMDebugger, which should be already attached to a core/process.
private void attach(JVMDebugger d) {
attached = true;
showThreadsDialog();
}
/** NOTE we are in a different thread here than either the main
thread or the Swing/AWT event handler thread, so we must be very
careful when creating or removing widgets */
private void attach(String pidText) {
try {
this.pidText = pidText;
pid = Integer.parseInt(pidText);
}
catch (NumberFormatException e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setMenuItemsEnabled(attachMenuItems, true);
JOptionPane.showInternalMessageDialog(desktop,
"Unable to parse process ID \"" + HSDB.this.pidText + "\".\nPlease enter a number.",
"Parse error",
JOptionPane.WARNING_MESSAGE);
}
});
return;
}
// Try to attach to this process
Runnable remover = new Runnable() {
public void run() {
attachWaitDialog.setVisible(false);
desktop.remove(attachWaitDialog);
attachWaitDialog = null;
}
};
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane pane = new JOptionPane("Attaching to process " + pid + ", please wait...", JOptionPane.INFORMATION_MESSAGE);
pane.setOptions(new Object[] {});
attachWaitDialog = pane.createInternalFrame(desktop, "Attaching to Process");
attachWaitDialog.show();
}
});
// FIXME: display exec'd debugger's output messages during this
// lengthy call
agent.attach(pid);
if (agent.getDebugger().hasConsole()) {
showDbgConsoleMenuItem.setEnabled(true);
}
attached = true;
SwingUtilities.invokeLater(remover);
}
catch (DebuggerException e) {
SwingUtilities.invokeLater(remover);
final String errMsg = formatMessage(e.getMessage(), 80);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setMenuItemsEnabled(attachMenuItems, true);
JOptionPane.showInternalMessageDialog(desktop,
"Unable to connect to process ID " + pid + ":\n\n" + errMsg,
"Unable to Connect",
JOptionPane.WARNING_MESSAGE);
}
});
agent.detach();
return;
}
// OK, the VM should be available. Create the Threads dialog.
showThreadsDialog();
}
/** NOTE we are in a different thread here than either the main
thread or the Swing/AWT event handler thread, so we must be very
careful when creating or removing widgets */
private void attach(final String executablePath, final String corePath) {
// Try to open this core file
Runnable remover = new Runnable() {
public void run() {
attachWaitDialog.setVisible(false);
desktop.remove(attachWaitDialog);
attachWaitDialog = null;
}
};
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane pane = new JOptionPane("Opening core file, please wait...", JOptionPane.INFORMATION_MESSAGE);
pane.setOptions(new Object[] {});
attachWaitDialog = pane.createInternalFrame(desktop, "Opening Core File");
attachWaitDialog.show();
}
});
// FIXME: display exec'd debugger's output messages during this
// lengthy call
agent.attach(executablePath, corePath);
if (agent.getDebugger().hasConsole()) {
showDbgConsoleMenuItem.setEnabled(true);
}
attached = true;
SwingUtilities.invokeLater(remover);
}
catch (DebuggerException e) {
SwingUtilities.invokeLater(remover);
final String errMsg = formatMessage(e.getMessage(), 80);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setMenuItemsEnabled(attachMenuItems, true);
JOptionPane.showInternalMessageDialog(desktop,
"Unable to open core file\n" + corePath + ":\n\n" + errMsg,
"Unable to Open Core File",
JOptionPane.WARNING_MESSAGE);
}
});
agent.detach();
return;
}
// OK, the VM should be available. Create the Threads dialog.
showThreadsDialog();
}
/** NOTE we are in a different thread here than either the main
thread or the Swing/AWT event handler thread, so we must be very
careful when creating or removing widgets */
private void connect(final String remoteMachineName) {
// Try to open this core file
Runnable remover = new Runnable() {
public void run() {
attachWaitDialog.setVisible(false);
desktop.remove(attachWaitDialog);
attachWaitDialog = null;
}
};
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane pane = new JOptionPane("Connecting to debug server, please wait...", JOptionPane.INFORMATION_MESSAGE);
pane.setOptions(new Object[] {});
attachWaitDialog = pane.createInternalFrame(desktop, "Connecting to Debug Server");
attachWaitDialog.show();
}
});
agent.attach(remoteMachineName);
if (agent.getDebugger().hasConsole()) {
showDbgConsoleMenuItem.setEnabled(true);
}
attached = true;
SwingUtilities.invokeLater(remover);
}
catch (DebuggerException e) {
SwingUtilities.invokeLater(remover);
final String errMsg = formatMessage(e.getMessage(), 80);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setMenuItemsEnabled(attachMenuItems, true);
JOptionPane.showInternalMessageDialog(desktop,
"Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg,
"Unable to Connect",
JOptionPane.WARNING_MESSAGE);
}
});
agent.detach();
return;
}
// OK, the VM should be available. Create the Threads dialog.
showThreadsDialog();
}
private void detachDebugger() {
if (!attached) {
return;
}
agent.detach();
attached = false;
}
private void detach() {
detachDebugger();
attachWaitDialog = null;
threadsFrame = null;
consoleFrame = null;
setMenuItemsEnabled(attachMenuItems, true);
setMenuItemsEnabled(detachMenuItems, false);
toolsMenu.setEnabled(false);
showDbgConsoleMenuItem.setEnabled(false);
// FIXME: is this sufficient, or will I have to do anything else
// to the components to kill them off? What about WorkerThreads?
desktop.removeAll();
desktop.invalidate();
desktop.validate();
desktop.repaint();
}
/** NOTE that this is called from another thread than the main or
Swing thread and we have to be careful about synchronization */
private void showThreadsDialog() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
threadsFrame = new JInternalFrame("Java Threads");
threadsFrame.setResizable(true);
threadsFrame.setIconifiable(true);
JavaThreadsPanel threadsPanel = new JavaThreadsPanel();
threadsPanel.addPanelListener(HSDB.this);
threadsFrame.getContentPane().add(threadsPanel);
threadsFrame.setSize(500, 300);
threadsFrame.pack();
desktop.add(threadsFrame);
GraphicsUtilities.moveToInContainer(threadsFrame, 0.75f, 0.25f, 0, 20);
threadsFrame.show();
setMenuItemsEnabled(attachMenuItems, false);
setMenuItemsEnabled(detachMenuItems, true);
toolsMenu.setEnabled(true);
VM.registerVMInitializedObserver(new Observer() {
public void update(Observable o, Object data) {
computeRevPtrsMenuItem.setEnabled(true);
}
});
}
});
}
private void showObjectHistogram() {
sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram();
ObjectHistogramCleanupThunk cleanup =
new ObjectHistogramCleanupThunk(histo);
doHeapIteration("Object Histogram",
"Generating histogram...",
histo,
cleanup);
}
class ObjectHistogramCleanupThunk implements CleanupThunk {
sun.jvm.hotspot.oops.ObjectHistogram histo;
ObjectHistogramCleanupThunk(sun.jvm.hotspot.oops.ObjectHistogram histo) {
this.histo = histo;
}
public void heapIterationComplete() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JInternalFrame histoFrame = new JInternalFrame("Object Histogram");
histoFrame.setResizable(true);
histoFrame.setClosable(true);
histoFrame.setIconifiable(true);
histoFrame.getContentPane().setLayout(new BorderLayout());
ObjectHistogramPanel panel = new ObjectHistogramPanel(histo);
panel.addPanelListener(HSDB.this);
histoFrame.getContentPane().add(panel);
desktop.add(histoFrame);
GraphicsUtilities.reshapeToAspectRatio(histoFrame, 4.0f / 3.0f, 0.6f,
histoFrame.getParent().getSize());
GraphicsUtilities.centerInContainer(histoFrame);
histoFrame.show();
}
});
}
}
public void showObjectsOfType(Klass type) {
FindObjectByType finder = new FindObjectByType(type);
FindObjectByTypeCleanupThunk cleanup =
new FindObjectByTypeCleanupThunk(finder);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
type.printValueOn(new PrintStream(bos));
String typeName = bos.toString();
doHeapIteration("Show Objects Of Type",
"Finding instances of \"" + typeName + "\"",
finder,
cleanup);
}
class FindObjectByTypeCleanupThunk implements CleanupThunk {
FindObjectByType finder;
FindObjectByTypeCleanupThunk(FindObjectByType finder) {
this.finder = finder;
}
public void heapIterationComplete() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JInternalFrame finderFrame = new JInternalFrame("Show Objects of Type");
finderFrame.getContentPane().setLayout(new BorderLayout());
finderFrame.setResizable(true);
finderFrame.setClosable(true);
finderFrame.setIconifiable(true);
ObjectListPanel panel = new ObjectListPanel(finder.getResults(),
new HeapProgress("Reverse Pointers Analysis"));
panel.addPanelListener(HSDB.this);
finderFrame.getContentPane().add(panel);
desktop.add(finderFrame);
GraphicsUtilities.reshapeToAspectRatio(finderFrame, 4.0f / 3.0f, 0.6f,
finderFrame.getParent().getSize());
GraphicsUtilities.centerInContainer(finderFrame);
finderFrame.show();
}
});
}
}
private void showDebuggerConsole() {
if (consoleFrame == null) {
consoleFrame = new JInternalFrame("Debugger Console");
consoleFrame.setResizable(true);
consoleFrame.setClosable(true);
consoleFrame.setIconifiable(true);
consoleFrame.getContentPane().setLayout(new BorderLayout());
consoleFrame.getContentPane().add(new DebuggerConsolePanel(agent.getDebugger()), BorderLayout.CENTER);
GraphicsUtilities.reshapeToAspectRatio(consoleFrame, 5.0f, 0.9f, desktop.getSize());
}
if (consoleFrame.getParent() == null) {
desktop.add(consoleFrame);
}
consoleFrame.setVisible(true);
consoleFrame.show();
consoleFrame.getContentPane().getComponent(0).requestFocus();
}
private void showConsole() {
CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() {
public HotSpotAgent getAgent() {
return agent;
}
public boolean isAttached() {
return attached;
}
public void attach(String pid) {
attach(pid);
}
public void attach(String java, String core) {
}
public void detach() {
detachDebugger();
}
public void reattach() {
if (attached) {
detachDebugger();
}
if (pidText != null) {
attach(pidText);
} else {
attach(execPath, coreFilename);
}
}
};
showPanel("Command Line", new CommandProcessorPanel(new CommandProcessor(di, null, null, null)));
}
private void showFindByQueryPanel() {
showPanel("Find Object by Query", new FindByQueryPanel());
}
private void showFindPanel() {
showPanel("Find Pointer", new FindPanel());
}
private void showFindInHeapPanel() {
showPanel("Find Address In Heap", new FindInHeapPanel());
}
private void showFindInCodeCachePanel() {
showPanel("Find Address In Code Cache", new FindInCodeCachePanel());
}
private void showHeapParametersPanel() {
showPanel("Heap Parameters", new HeapParametersPanel());
}
public void showThreadInfo(final JavaThread thread) {
showPanel("Info for " + thread.getThreadName(), new ThreadInfoPanel(thread));
}
public void showJavaStackTrace(final JavaThread thread) {
JavaStackTracePanel jstp = new JavaStackTracePanel();
showPanel("Java stack trace for " + thread.getThreadName(), jstp);
jstp.setJavaThread(thread);
}
private void showDeadlockDetectionPanel() {
showPanel("Deadlock Detection", new DeadlockDetectionPanel());
}
private void showMonitorCacheDumpPanel() {
showPanel("Monitor Cache Dump", new MonitorCacheDumpPanel());
}
public void showClassBrowser() {
final JInternalFrame progressFrame = new JInternalFrame("Class Browser");
progressFrame.setResizable(true);
progressFrame.setClosable(true);
progressFrame.setIconifiable(true);
progressFrame.getContentPane().setLayout(new BorderLayout());
final ProgressBarPanel bar = new ProgressBarPanel("Generating class list ..");
bar.setIndeterminate(true);
progressFrame.getContentPane().add(bar, BorderLayout.CENTER);
desktop.add(progressFrame);
progressFrame.pack();
GraphicsUtilities.centerInContainer(progressFrame);
progressFrame.show();
workerThread.invokeLater(new Runnable() {
public void run() {
HTMLGenerator htmlGen = new HTMLGenerator();
InstanceKlass[] klasses = SystemDictionaryHelper.getAllInstanceKlasses();
final String htmlText = htmlGen.genHTMLForKlassNames(klasses);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JInternalFrame cbFrame = new JInternalFrame("Class Browser");
cbFrame.getContentPane().setLayout(new BorderLayout());
cbFrame.setResizable(true);
cbFrame.setClosable(true);
cbFrame.setIconifiable(true);
ClassBrowserPanel cbPanel = new ClassBrowserPanel();
cbFrame.getContentPane().add(cbPanel, BorderLayout.CENTER);
desktop.remove(progressFrame);
desktop.repaint();
desktop.add(cbFrame);
GraphicsUtilities.reshapeToAspectRatio(cbFrame, 1.25f, 0.85f,
cbFrame.getParent().getSize());
cbFrame.show();
cbPanel.setClassesText(htmlText);
}
});
}
});
}
public void showCodeViewer() {
showPanel("Code Viewer", new CodeViewerPanel(), 1.25f, 0.85f);
}
public void showCodeViewer(final Address address) {
final CodeViewerPanel panel = new CodeViewerPanel();
showPanel("Code Viewer", panel, 1.25f, 0.85f);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.viewAddress(address);
}
});
}
public void showMemoryViewer() {
showPanel("Memory Viewer", new MemoryViewer(agent.getDebugger(), agent.getTypeDataBase().getAddressSize() == 8));
}
public void showCommandLineFlags() {
showPanel("Command Line Flags", new VMFlagsPanel());
}
public void showVMVersion() {
showPanel("VM Version Info", new VMVersionInfoPanel());
}
public void showSystemProperties() {
showPanel("System Properties", new SysPropsPanel());
}
private void showPanel(String name, JPanel panel) {
showPanel(name, panel, 5.0f / 3.0f, 0.4f);
}
private void showPanel(String name, JPanel panel, float aspectRatio, float fillRatio) {
JInternalFrame frame = new JInternalFrame(name);
frame.getContentPane().setLayout(new BorderLayout());
frame.setResizable(true);
frame.setClosable(true);
frame.setIconifiable(true);
frame.setMaximizable(true);
frame.getContentPane().add(panel, BorderLayout.CENTER);
desktop.add(frame);
GraphicsUtilities.reshapeToAspectRatio(frame, aspectRatio, fillRatio, frame.getParent().getSize());
GraphicsUtilities.randomLocation(frame);
frame.show();
if (panel instanceof SAPanel) {
((SAPanel)panel).addPanelListener(this);
}
}
//--------------------------------------------------------------------------------
// Framework for heap iteration with progress bar
//
interface CleanupThunk {
public void heapIterationComplete();
}
class HeapProgress implements HeapProgressThunk {
private JInternalFrame frame;
private ProgressBarPanel bar;
private String windowTitle;
private String progressBarTitle;
private CleanupThunk cleanup;
HeapProgress(String windowTitle) {
this(windowTitle, "Percentage of heap visited", null);
}
HeapProgress(String windowTitle, String progressBarTitle) {
this(windowTitle, progressBarTitle, null);
}
HeapProgress(String windowTitle, String progressBarTitle, CleanupThunk cleanup) {
this.windowTitle = windowTitle;
this.progressBarTitle = progressBarTitle;
this.cleanup = cleanup;
}
public void heapIterationFractionUpdate(final double fractionOfHeapVisited) {
if (frame == null) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame = new JInternalFrame(windowTitle);
frame.setResizable(true);
frame.setIconifiable(true);
frame.getContentPane().setLayout(new BorderLayout());
bar = new ProgressBarPanel(progressBarTitle);
frame.getContentPane().add(bar, BorderLayout.CENTER);
desktop.add(frame);
frame.pack();
GraphicsUtilities.constrainToSize(frame, frame.getParent().getSize());
GraphicsUtilities.centerInContainer(frame);
frame.show();
}
});
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
bar.setValue(fractionOfHeapVisited);
}
});
}
public void heapIterationComplete() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
desktop.remove(frame);
desktop.repaint();
if (VM.getVM().getRevPtrs() != null) {
// Ended up computing reverse pointers as a side-effect
computeRevPtrsMenuItem.setEnabled(false);
}
}
});
if (cleanup != null) {
cleanup.heapIterationComplete();
}
}
}
class VisitHeap implements Runnable {
HeapVisitor visitor;
VisitHeap(HeapVisitor visitor) {
this.visitor = visitor;
}
public void run() {
VM.getVM().getObjectHeap().iterate(visitor);
}
}
private void doHeapIteration(String frameTitle,
String progressBarText,
HeapVisitor visitor,
CleanupThunk cleanup) {
sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram();
HeapProgress progress = new HeapProgress(frameTitle,
progressBarText,
cleanup);
HeapVisitor progVisitor = new ProgressiveHeapVisitor(visitor, progress);
workerThread.invokeLater(new VisitHeap(progVisitor));
}
//--------------------------------------------------------------------------------
// Stack trace helper
//
private static JavaVFrame getLastJavaVFrame(JavaThread cur) {
RegisterMap regMap = cur.newRegisterMap(true);
sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess();
if (f == null) return null;
boolean imprecise = true;
if (f.isInterpretedFrame() && !f.isInterpretedFrameValid()) {
System.err.println("Correcting for invalid interpreter frame");
f = f.sender(regMap);
imprecise = false;
}
VFrame vf = VFrame.newVFrame(f, regMap, cur, true, imprecise);
if (vf == null) {
System.err.println(" (Unable to create vframe for topmost frame guess)");
return null;
}
if (vf.isJavaFrame()) {
return (JavaVFrame) vf;
}
return (JavaVFrame) vf.javaSender();
}
// Internal routine for debugging
private static void dumpStack(JavaThread cur) {
RegisterMap regMap = cur.newRegisterMap(true);
sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess();
PrintStream tty = System.err;
while (f != null) {
tty.print("Found ");
if (f.isInterpretedFrame()) { tty.print("interpreted"); }
else if (f.isCompiledFrame()) { tty.print("compiled"); }
else if (f.isEntryFrame()) { tty.print("entry"); }
else if (f.isNativeFrame()) { tty.print("native"); }
else if (f.isRuntimeFrame()) { tty.print("runtime"); }
else { tty.print("external"); }
tty.print(" frame with PC = " + f.getPC() + ", SP = " + f.getSP() + ", FP = " + f.getFP());
if (f.isSignalHandlerFrameDbg()) {
tty.print(" (SIGNAL HANDLER)");
}
tty.println();
if (!f.isFirstFrame()) {
f = f.sender(regMap);
} else {
f = null;
}
}
}
//--------------------------------------------------------------------------------
// Component utilities
//
private static JMenuItem createMenuItem(String name, ActionListener l) {
JMenuItem item = new JMenuItem(name);
item.addActionListener(l);
return item;
}
/** Punctuates the given string with \n's where necessary to not
exceed the given number of characters per line. Strips
extraneous whitespace. */
private String formatMessage(String message, int charsPerLine) {
StringBuffer buf = new StringBuffer(message.length());
StringTokenizer tokenizer = new StringTokenizer(message);
int curLineLength = 0;
while (tokenizer.hasMoreTokens()) {
String tok = tokenizer.nextToken();
if (curLineLength + tok.length() > charsPerLine) {
buf.append('\n');
curLineLength = 0;
} else {
if (curLineLength != 0) {
buf.append(' ');
++curLineLength;
}
}
buf.append(tok);
curLineLength += tok.length();
}
return buf.toString();
}
private void setMenuItemsEnabled(java.util.List items, boolean enabled) {
for (Iterator iter = items.iterator(); iter.hasNext(); ) {
((JMenuItem) iter.next()).setEnabled(enabled);
}
}
}