mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
1847 lines
71 KiB
Java
1847 lines
71 KiB
Java
/*
|
|
* Copyright (c) 2000, 2024, 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 javax.swing.*;
|
|
import java.util.*;
|
|
|
|
import sun.jvm.hotspot.code.*;
|
|
import sun.jvm.hotspot.compiler.*;
|
|
import sun.jvm.hotspot.debugger.*;
|
|
import sun.jvm.hotspot.gc.epsilon.*;
|
|
import sun.jvm.hotspot.gc.parallel.*;
|
|
import sun.jvm.hotspot.gc.serial.*;
|
|
import sun.jvm.hotspot.gc.shared.*;
|
|
import sun.jvm.hotspot.gc.shenandoah.*;
|
|
import sun.jvm.hotspot.gc.g1.*;
|
|
import sun.jvm.hotspot.gc.z.*;
|
|
import sun.jvm.hotspot.interpreter.*;
|
|
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.*;
|
|
import sun.jvm.hotspot.utilities.Observable;
|
|
import sun.jvm.hotspot.utilities.Observer;
|
|
|
|
/** 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;
|
|
private java.util.List<JMenuItem> attachMenuItems;
|
|
private java.util.List<JMenuItem> 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 int pid;
|
|
private String execPath;
|
|
private String coreFilename;
|
|
private String debugServerName;
|
|
|
|
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) {
|
|
pid = -1;
|
|
execPath = null;
|
|
coreFilename = null;
|
|
debugServerName = null;
|
|
jvmDebugger = d;
|
|
}
|
|
|
|
private HSDB(String[] args) {
|
|
pid = -1;
|
|
execPath = null;
|
|
coreFilename = null;
|
|
debugServerName = null;
|
|
|
|
switch (args.length) {
|
|
case (0):
|
|
break;
|
|
|
|
case (1):
|
|
if (args[0].equals("help") || args[0].equals("-help")) {
|
|
doUsage();
|
|
}
|
|
try {
|
|
// Attempt to attach as a PID
|
|
pid = Integer.parseInt(args[0]);
|
|
} catch (NumberFormatException e) {
|
|
// Attempt to connect to remote debug server
|
|
debugServerName = args[0];
|
|
}
|
|
break;
|
|
|
|
case (2):
|
|
execPath = args[0];
|
|
coreFilename = args[1];
|
|
break;
|
|
|
|
default:
|
|
System.out.println("HSDB Error: Too many options specified");
|
|
doUsage();
|
|
}
|
|
}
|
|
|
|
private class CloseUI extends WindowAdapter {
|
|
|
|
@Override
|
|
public void windowClosing(WindowEvent e) {
|
|
workerThread.shutdown();
|
|
frame.dispose();
|
|
}
|
|
|
|
}
|
|
|
|
public void run() {
|
|
// Don't start the UI if there were bad arguments.
|
|
if (argError) {
|
|
return;
|
|
}
|
|
|
|
// Create frame first, to catch any GUI creation issues
|
|
// before we initialize agent
|
|
|
|
frame = new JFrame("HSDB - HotSpot Debugger");
|
|
frame.setSize(800, 600);
|
|
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
|
frame.addWindowListener(new CloseUI());
|
|
|
|
agent = new HotSpotAgent();
|
|
workerThread = new WorkerThread();
|
|
attachMenuItems = new java.util.ArrayList<>();
|
|
detachMenuItems = new java.util.ArrayList<>();
|
|
|
|
|
|
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) {
|
|
workerThread.shutdown();
|
|
frame.dispose();
|
|
}
|
|
});
|
|
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 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("Annotated Memory Viewer",
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
showAnnotatedMemoryViewer();
|
|
}
|
|
});
|
|
item.setMnemonic(KeyEvent.VK_W);
|
|
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 pid != -1 we are supposed to attach to it.
|
|
// 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.
|
|
// Finally, if debugServerName != null, we are supposed to
|
|
// connect to remote debug server.
|
|
|
|
if (jvmDebugger != null) {
|
|
attach(jvmDebugger);
|
|
} else if (pid != -1) {
|
|
attach(pid);
|
|
} else if (execPath != null) {
|
|
attach(execPath, coreFilename);
|
|
} else if (debugServerName != null) {
|
|
connect(debugServerName);
|
|
}
|
|
}
|
|
|
|
// 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(Integer.parseInt(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 " +
|
|
"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<sun.jvm.hotspot.runtime.Frame, SignalInfo> 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());
|
|
ImmutableOopMapSet maps = cb.getOopMaps();
|
|
if ((maps == null) || (maps.getCount() == 0)) {
|
|
shouldSkipOopMaps = true;
|
|
}
|
|
}
|
|
|
|
// Add signal information to annotation if necessary
|
|
SignalInfo sigInfo = interruptedFrameMap.get(curFrame);
|
|
if (sigInfo != null) {
|
|
// This frame took a signal and we need to report it.
|
|
anno = anno + "\n*** INTERRUPTED BY SIGNAL " + 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 ImmutableOopMapSet found for this CodeBlob";
|
|
}
|
|
|
|
if (curFrame.getFP() != null) {
|
|
annoPanel.addAnnotation(new Annotation(curFrame.getSP(),
|
|
curFrame.getFP(),
|
|
anno));
|
|
} else {
|
|
// For C2, which has null frame pointers on amd64/aarch64
|
|
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));
|
|
}
|
|
|
|
// 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 SerialHeap) {
|
|
SerialHeap heap = (SerialHeap) collHeap;
|
|
if (heap.youngGen().isIn(handle)) {
|
|
anno = "NewGen ";
|
|
bad = false;
|
|
} else if (heap.oldGen().isIn(handle)) {
|
|
anno = "OldGen ";
|
|
bad = false;
|
|
}
|
|
} else if (collHeap instanceof G1CollectedHeap) {
|
|
G1CollectedHeap heap = (G1CollectedHeap)collHeap;
|
|
G1HeapRegion region = heap.hrm().getByAddress(handle);
|
|
|
|
if (region == null) {
|
|
// intentionally skip
|
|
} else if (region.isFree()) {
|
|
anno = "Free ";
|
|
bad = false;
|
|
} else if (region.isYoung()) {
|
|
anno = "Young ";
|
|
bad = false;
|
|
} else if (region.isHumongous()) {
|
|
anno = "Humongous ";
|
|
bad = false;
|
|
} else if (region.isOld()) {
|
|
anno = "Old ";
|
|
bad = false;
|
|
}
|
|
if (!bad && region.isPinned()) {
|
|
anno += "Pinned ";
|
|
}
|
|
} 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 if (collHeap instanceof EpsilonHeap) {
|
|
anno = "Epsilon ";
|
|
bad = false;
|
|
} else if (collHeap instanceof ShenandoahHeap) {
|
|
ShenandoahHeap heap = (ShenandoahHeap) collHeap;
|
|
anno = "ShenandoahHeap ";
|
|
bad = false;
|
|
} else if (collHeap instanceof ZCollectedHeap) {
|
|
ZCollectedHeap heap = (ZCollectedHeap) collHeap;
|
|
anno = "ZHeap ";
|
|
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(int pid) {
|
|
this.pid = pid;
|
|
// 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 debugServerName) {
|
|
// 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(debugServerName);
|
|
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 \"" + debugServerName + "\":\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(int pid) {
|
|
HSDB.this.attach(pid);
|
|
}
|
|
public void attach(String java, String core) {
|
|
}
|
|
public void attach(String debugServerName) {
|
|
HSDB.this.connect(debugServerName);
|
|
}
|
|
public void detach() {
|
|
detachDebugger();
|
|
}
|
|
public void reattach() {
|
|
if (attached) {
|
|
detachDebugger();
|
|
}
|
|
if (pid != -1) {
|
|
attach(pid);
|
|
} else if (debugServerName != null) {
|
|
connect(debugServerName);
|
|
} else {
|
|
attach(execPath, coreFilename);
|
|
}
|
|
}
|
|
};
|
|
|
|
showPanel("Command Line", new CommandProcessorPanel(new CommandProcessor(di, null, null, null)));
|
|
}
|
|
|
|
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(), false, agent.getTypeDataBase().getAddressSize() == 8));
|
|
}
|
|
|
|
public void showAnnotatedMemoryViewer() {
|
|
showPanel("Annotated Memory Viewer", new MemoryViewer(agent.getDebugger(), true, 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) {
|
|
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 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) {
|
|
StringBuilder buf = new StringBuilder(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<JMenuItem> items, boolean enabled) {
|
|
for (Iterator<JMenuItem> iter = items.iterator(); iter.hasNext(); ) {
|
|
iter.next().setEnabled(enabled);
|
|
}
|
|
}
|
|
}
|