/* * Copyright (c) 2000, 2017, 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.parallel.*; import sun.jvm.hotspot.gc.shared.*; import sun.jvm.hotspot.gc.g1.*; 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 */ private java.util.List attachMenuItems; /** List */ 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(); } } 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 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 | 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//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()); ImmutableOopMapSet maps = cb.getOopMaps(); if ((maps == null) || (maps.getCount() == 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 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 x86/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 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 G1CollectedHeap) { G1CollectedHeap heap = (G1CollectedHeap)collHeap; HeapRegion region = heap.hrm().addrToRegion(handle); 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.isPinned()) { anno = "Pinned "; bad = false; } else if (region.isOld()) { anno = "Old "; bad = false; } } 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) { HSDB.this.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); } } }