diff agent/src/share/classes/sun/jvm/hotspot/HSDB.java @ 0:a61af66fc99e jdk7-b24

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children ba764ed4b6f2 c70a245cad3a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/share/classes/sun/jvm/hotspot/HSDB.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1779 @@
+/*
+ * Copyright 2000-2006 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+package sun.jvm.hotspot;
+
+import java.io.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.math.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import java.util.*;
+
+import sun.jvm.hotspot.code.*;
+import sun.jvm.hotspot.compiler.*;
+import sun.jvm.hotspot.debugger.*;
+import sun.jvm.hotspot.gc_implementation.parallelScavenge.*;
+import sun.jvm.hotspot.gc_interface.*;
+import sun.jvm.hotspot.interpreter.*;
+import sun.jvm.hotspot.memory.*;
+import sun.jvm.hotspot.oops.*;
+import sun.jvm.hotspot.runtime.*;
+import sun.jvm.hotspot.ui.*;
+import sun.jvm.hotspot.ui.tree.*;
+import sun.jvm.hotspot.ui.classbrowser.*;
+import sun.jvm.hotspot.utilities.*;
+
+/** The top-level HotSpot Debugger. FIXME: make this an embeddable
+    component! (Among other things, figure out what to do with the
+    menu bar...) */
+
+public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
+  public static void main(String[] args) {
+    new HSDB(args).run();
+  }
+
+  //--------------------------------------------------------------------------------
+  // Internals only below this point
+  //
+  private HotSpotAgent agent;
+  private JDesktopPane desktop;
+  private boolean      attached;
+  /** List <JMenuItem> */
+  private java.util.List attachMenuItems;
+  /** List <JMenuItem> */
+  private java.util.List detachMenuItems;
+  private JMenu toolsMenu;
+  private JMenuItem showDbgConsoleMenuItem;
+  private JMenuItem computeRevPtrsMenuItem;
+  private JInternalFrame attachWaitDialog;
+  private JInternalFrame threadsFrame;
+  private JInternalFrame consoleFrame;
+  private WorkerThread workerThread;
+  // These had to be made data members because they are referenced in inner classes.
+  private String pidText;
+  private int pid;
+  private String execPath;
+  private String coreFilename;
+
+  private void doUsage() {
+    System.out.println("Usage:  java HSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]");
+    System.out.println("           pid:                     attach to the process whose id is 'pid'");
+    System.out.println("           path-to-java-executable: Debug a core file produced by this program");
+    System.out.println("           path-to-corefile:        Debug this corefile.  The default is 'core'");
+    System.out.println("        If no arguments are specified, you can select what to do from the GUI.\n");
+    HotSpotAgent.showUsage();
+  }
+
+  private HSDB(String[] args) {
+    switch (args.length) {
+    case (0):
+      break;
+
+    case (1):
+      if (args[0].equals("help") || args[0].equals("-help")) {
+        doUsage();
+        System.exit(0);
+      }
+      // 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();
+      System.exit(1);
+    }
+  }
+
+  private void run() {
+    // At this point, if pidText != null we are supposed to attach to it.
+    // Else, 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.
+
+    agent = new HotSpotAgent();
+    workerThread = new WorkerThread();
+    attachMenuItems = new java.util.ArrayList();
+    detachMenuItems = new java.util.ArrayList();
+
+    JFrame frame = new JFrame("HSDB - HotSpot Debugger");
+    frame.setSize(800, 600);
+    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+    JMenuBar menuBar = new JMenuBar();
+
+    //
+    // File menu
+    //
+
+    JMenu menu = new JMenu("File");
+    menu.setMnemonic(KeyEvent.VK_F);
+    JMenuItem item;
+    item = createMenuItem("Attach to HotSpot process...",
+                          new ActionListener() {
+                              public void actionPerformed(ActionEvent e) {
+                                showAttachDialog();
+                              }
+                            });
+    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.ALT_MASK));
+    item.setMnemonic(KeyEvent.VK_A);
+    menu.add(item);
+    attachMenuItems.add(item);
+
+    item = createMenuItem("Open HotSpot core file...",
+                          new ActionListener() {
+                              public void actionPerformed(ActionEvent e) {
+                                showOpenCoreFileDialog();
+                              }
+                            });
+    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK));
+    item.setMnemonic(KeyEvent.VK_O);
+    menu.add(item);
+    attachMenuItems.add(item);
+
+    item = createMenuItem("Connect to debug server...",
+                          new ActionListener() {
+                              public void actionPerformed(ActionEvent e) {
+                                showConnectDialog();
+                              }
+                            });
+    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.ALT_MASK));
+    item.setMnemonic(KeyEvent.VK_S);
+    menu.add(item);
+    attachMenuItems.add(item);
+
+    item = createMenuItem("Detach",
+                          new ActionListener() {
+                              public void actionPerformed(ActionEvent e) {
+                                detach();
+                              }
+                            });
+    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.ALT_MASK));
+    item.setMnemonic(KeyEvent.VK_S);
+    menu.add(item);
+    detachMenuItems.add(item);
+
+    // Disable detach menu items at first
+    setMenuItemsEnabled(detachMenuItems, false);
+
+    menu.addSeparator();
+
+    item = createMenuItem("Exit",
+                            new ActionListener() {
+                                public void actionPerformed(ActionEvent e) {
+                                  System.exit(0);
+                                }
+                              });
+    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.show();
+
+    Runtime.getRuntime().addShutdownHook(new java.lang.Thread() {
+        public void run() {
+          detachDebugger();
+        }
+      });
+
+    if (pidText != null) {
+      attach(pidText);
+    } else if (execPath != null) {
+      attach(execPath, coreFilename);
+    }
+  }
+
+  // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
+  private void showAttachDialog() {
+    // FIXME: create filtered text field which only accepts numbers
+    setMenuItemsEnabled(attachMenuItems, false);
+    final JInternalFrame attachDialog = new JInternalFrame("Attach to HotSpot process");
+    attachDialog.getContentPane().setLayout(new BorderLayout());
+
+    JPanel panel = new JPanel();
+    panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
+    panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+    attachDialog.setBackground(panel.getBackground());
+
+    panel.add(new JLabel("Enter process ID:"));
+    final JTextField pidTextField = new JTextField(10);
+    ActionListener attacher = new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          attachDialog.setVisible(false);
+          desktop.remove(attachDialog);
+          workerThread.invokeLater(new Runnable() {
+              public void run() {
+                attach(pidTextField.getText());
+              }
+            });
+        }
+      };
+
+    pidTextField.addActionListener(attacher);
+    panel.add(pidTextField);
+    attachDialog.getContentPane().add(panel, BorderLayout.NORTH);
+
+    Box vbox = Box.createVerticalBox();
+    panel = new JPanel();
+    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+    panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+    JTextArea ta = new JTextArea(
+                                 "Enter the process ID of a currently-running HotSpot process. On " +
+                                 "Solaris and most Unix operating systems, this can be determined by " +
+                                 "typing \"ps -u <your username> | grep java\"; the process ID is the " +
+                                 "first number which appears on the resulting line. On Windows, the " +
+                                 "process ID is present in the Task Manager, which can be brought up " +
+                                 "while logged on to the desktop by pressing Ctrl-Alt-Delete.");
+    ta.setLineWrap(true);
+    ta.setWrapStyleWord(true);
+    ta.setEditable(false);
+    ta.setBackground(panel.getBackground());
+    panel.add(ta);
+    vbox.add(panel);
+
+    Box hbox = Box.createHorizontalBox();
+    hbox.add(Box.createGlue());
+    JButton button = new JButton("OK");
+    button.addActionListener(attacher);
+    hbox.add(button);
+    hbox.add(Box.createHorizontalStrut(20));
+    button = new JButton("Cancel");
+    button.addActionListener(new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          attachDialog.setVisible(false);
+          desktop.remove(attachDialog);
+          setMenuItemsEnabled(attachMenuItems, true);
+        }
+      });
+    hbox.add(button);
+    hbox.add(Box.createGlue());
+    panel = new JPanel();
+    panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+    panel.add(hbox);
+    vbox.add(panel);
+
+    attachDialog.getContentPane().add(vbox, BorderLayout.SOUTH);
+
+    desktop.add(attachDialog);
+    attachDialog.setSize(400, 300);
+    GraphicsUtilities.centerInContainer(attachDialog);
+    attachDialog.show();
+    pidTextField.requestFocus();
+  }
+
+  // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
+  private void showOpenCoreFileDialog() {
+    setMenuItemsEnabled(attachMenuItems, false);
+    final JInternalFrame dialog = new JInternalFrame("Open Core File");
+    dialog.getContentPane().setLayout(new BorderLayout());
+
+    JPanel panel = new JPanel();
+    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+    panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+    dialog.setBackground(panel.getBackground());
+
+    Box hbox = Box.createHorizontalBox();
+    Box vbox = Box.createVerticalBox();
+    vbox.add(new JLabel("Path to core file:"));
+    vbox.add(new JLabel("Path to Java executable:"));
+    hbox.add(vbox);
+
+    vbox = Box.createVerticalBox();
+    final JTextField corePathField = new JTextField(40);
+    final JTextField execPathField = new JTextField(40);
+    vbox.add(corePathField);
+    vbox.add(execPathField);
+    hbox.add(vbox);
+
+    final JButton browseCorePath = new JButton("Browse ..");
+    final JButton browseExecPath = new JButton("Browse ..");
+    browseCorePath.addActionListener(new ActionListener() {
+                                        public void actionPerformed(ActionEvent e) {
+                                           JFileChooser fileChooser = new JFileChooser(new File("."));
+                                           int retVal = fileChooser.showOpenDialog(dialog);
+                                           if (retVal == JFileChooser.APPROVE_OPTION) {
+                                              corePathField.setText(fileChooser.getSelectedFile().getPath());
+                                           }
+                                        }
+                                     });
+    browseExecPath.addActionListener(new ActionListener() {
+                                        public void actionPerformed(ActionEvent e) {
+                                           JFileChooser fileChooser = new JFileChooser(new File("."));
+                                           int retVal = fileChooser.showOpenDialog(dialog);
+                                           if (retVal == JFileChooser.APPROVE_OPTION) {
+                                              execPathField.setText(fileChooser.getSelectedFile().getPath());
+                                           }
+                                        }
+                                     });
+    vbox = Box.createVerticalBox();
+    vbox.add(browseCorePath);
+    vbox.add(browseExecPath);
+    hbox.add(vbox);
+
+    panel.add(hbox);
+    dialog.getContentPane().add(panel, BorderLayout.NORTH);
+
+    ActionListener attacher = new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          dialog.setVisible(false);
+          desktop.remove(dialog);
+          workerThread.invokeLater(new Runnable() {
+              public void run() {
+                attach(execPathField.getText(), corePathField.getText());
+              }
+            });
+        }
+      };
+    corePathField.addActionListener(attacher);
+    execPathField.addActionListener(attacher);
+
+    vbox = Box.createVerticalBox();
+    panel = new JPanel();
+    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+    panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+    JTextArea ta = new JTextArea(
+                                 "Enter the full path names to the core file from a HotSpot process " +
+                                 "and the Java executable from which it came. The latter is typically " +
+                                 "located in the JDK/JRE directory under the directory " +
+                                 "jre/bin/<arch>/native_threads.");
+    ta.setLineWrap(true);
+    ta.setWrapStyleWord(true);
+    ta.setEditable(false);
+    ta.setBackground(panel.getBackground());
+    panel.add(ta);
+    vbox.add(panel);
+
+    hbox = Box.createHorizontalBox();
+    hbox.add(Box.createGlue());
+    JButton button = new JButton("OK");
+    button.addActionListener(attacher);
+    hbox.add(button);
+    hbox.add(Box.createHorizontalStrut(20));
+    button = new JButton("Cancel");
+    button.addActionListener(new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          dialog.setVisible(false);
+          desktop.remove(dialog);
+          setMenuItemsEnabled(attachMenuItems, true);
+        }
+      });
+    hbox.add(button);
+    hbox.add(Box.createGlue());
+    panel = new JPanel();
+    panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+    panel.add(hbox);
+    vbox.add(panel);
+
+    dialog.getContentPane().add(vbox, BorderLayout.SOUTH);
+
+    desktop.add(dialog);
+    dialog.setSize(500, 300);
+    GraphicsUtilities.centerInContainer(dialog);
+    dialog.show();
+    corePathField.requestFocus();
+  }
+
+  // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
+  private void showConnectDialog() {
+    // FIXME: create filtered text field which only accepts numbers
+    setMenuItemsEnabled(attachMenuItems, false);
+    final JInternalFrame dialog = new JInternalFrame("Connect to HotSpot Debug Server");
+    dialog.getContentPane().setLayout(new BorderLayout());
+
+    JPanel panel = new JPanel();
+    panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
+    panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+    dialog.setBackground(panel.getBackground());
+
+    panel.add(new JLabel("Enter machine name:"));
+    final JTextField pidTextField = new JTextField(40);
+    ActionListener attacher = new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          dialog.setVisible(false);
+          desktop.remove(dialog);
+          workerThread.invokeLater(new Runnable() {
+              public void run() {
+                connect(pidTextField.getText());
+              }
+            });
+        }
+      };
+
+    pidTextField.addActionListener(attacher);
+    panel.add(pidTextField);
+    dialog.getContentPane().add(panel, BorderLayout.NORTH);
+
+    Box vbox = Box.createVerticalBox();
+    panel = new JPanel();
+    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+    panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+    JTextArea ta = new JTextArea(
+                                 "Enter the name of a machine on which the HotSpot \"Debug Server\" is " +
+                                 "running and is attached to a process or core file.");
+    ta.setLineWrap(true);
+    ta.setWrapStyleWord(true);
+    ta.setEditable(false);
+    ta.setBackground(panel.getBackground());
+    panel.add(ta);
+    vbox.add(panel);
+
+    Box hbox = Box.createHorizontalBox();
+    hbox.add(Box.createGlue());
+    JButton button = new JButton("OK");
+    button.addActionListener(attacher);
+    hbox.add(button);
+    hbox.add(Box.createHorizontalStrut(20));
+    button = new JButton("Cancel");
+    button.addActionListener(new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          dialog.setVisible(false);
+          desktop.remove(dialog);
+          setMenuItemsEnabled(attachMenuItems, true);
+        }
+      });
+    hbox.add(button);
+    hbox.add(Box.createGlue());
+    panel = new JPanel();
+    panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+    panel.add(hbox);
+    vbox.add(panel);
+
+    dialog.getContentPane().add(vbox, BorderLayout.SOUTH);
+
+    desktop.add(dialog);
+    dialog.setSize(400, 300);
+    GraphicsUtilities.centerInContainer(dialog);
+    dialog.show();
+    pidTextField.requestFocus();
+  }
+
+  public void showThreadOopInspector(JavaThread thread) {
+    showInspector(new OopTreeNodeAdapter(thread.getThreadObj(), null));
+  }
+
+  public void showInspector(SimpleTreeNode adapter) {
+    showPanel("Inspector", new Inspector(adapter), 1.0f, 0.65f);
+  }
+
+  public void showLiveness(Oop oop, LivenessPathList liveness) {
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    PrintStream tty = new PrintStream(bos);
+    int numPaths = liveness.size();
+    for (int i = 0; i < numPaths; i++) {
+      tty.println("Path " + (i + 1) + " of " + numPaths + ":");
+      liveness.get(i).printOn(tty);
+    }
+    JTextArea ta = new JTextArea(bos.toString());
+    ta.setLineWrap(true);
+    ta.setWrapStyleWord(true);
+    ta.setEditable(false);
+
+    JPanel panel = new JPanel();
+    panel.setLayout(new BorderLayout());
+
+    JScrollPane scroller = new JScrollPane();
+    scroller.getViewport().add(ta);
+
+    panel.add(scroller, BorderLayout.CENTER);
+
+    bos = new ByteArrayOutputStream();
+    tty = new PrintStream(bos);
+    tty.print("Liveness result for ");
+    Oop.printOopValueOn(oop, tty);
+
+    JInternalFrame frame = new JInternalFrame(bos.toString());
+    frame.setResizable(true);
+    frame.setClosable(true);
+    frame.setIconifiable(true);
+    frame.getContentPane().setLayout(new BorderLayout());
+    frame.getContentPane().add(panel, BorderLayout.CENTER);
+    frame.pack();
+    desktop.add(frame);
+    GraphicsUtilities.reshapeToAspectRatio(frame, 0.5f / 0.2f, 0.5f, frame.getParent().getSize());
+    frame.show();
+  }
+
+  private void fireComputeReversePtrs() {
+    // Possible this might have been computed elsewhere
+    if (VM.getVM().getRevPtrs() != null) {
+      computeRevPtrsMenuItem.setEnabled(false);
+      return;
+    }
+
+    workerThread.invokeLater(new Runnable() {
+        public void run() {
+          HeapProgress progress = new HeapProgress("Reverse Pointers Analysis");
+          try {
+            ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
+            analysis.setHeapProgressThunk(progress);
+            analysis.run();
+            computeRevPtrsMenuItem.setEnabled(false);
+          } catch (OutOfMemoryError e) {
+            final String errMsg = formatMessage(e.toString(), 80);
+            SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                  JOptionPane.showInternalMessageDialog(desktop,
+                                                        "Error computing reverse pointers:" + errMsg,
+                                                        "Error",
+                                                        JOptionPane.WARNING_MESSAGE);
+                }
+              });
+          } finally {
+            // make sure the progress bar goes away
+            progress.heapIterationComplete();
+          }
+        }
+      });
+  }
+
+  // Simple struct containing signal information
+  class SignalInfo {
+    public int sigNum;
+    public String sigName;
+  }
+
+  // Need to have mutable vframe as well as visible memory panel
+  abstract class StackWalker implements Runnable {
+    protected JavaVFrame vf;
+    protected AnnotatedMemoryPanel annoPanel;
+
+    StackWalker(JavaVFrame vf, AnnotatedMemoryPanel annoPanel) {
+      this.vf = vf;
+      this.annoPanel = annoPanel;
+    }
+  }
+
+  public void showThreadStackMemory(final JavaThread thread) {
+    // dumpStack(thread);
+    JavaVFrame vframe = getLastJavaVFrame(thread);
+    if (vframe == null) {
+      JOptionPane.showInternalMessageDialog(desktop,
+                                            "Thread \"" + thread.getThreadName() +
+                                            "\" has no Java frames on its stack",
+                                            "Show Stack Memory",
+                                            JOptionPane.INFORMATION_MESSAGE);
+      return;
+    }
+
+    JInternalFrame stackFrame = new JInternalFrame("Stack Memory for " + thread.getThreadName());
+    stackFrame.getContentPane().setLayout(new BorderLayout());
+    stackFrame.setResizable(true);
+    stackFrame.setClosable(true);
+    stackFrame.setIconifiable(true);
+    final long addressSize = agent.getTypeDataBase().getAddressSize();
+    boolean is64Bit = (addressSize == 8);
+    // This is somewhat of a  hack to guess a thread's stack limits since the
+    // JavaThread doesn't support this functionality. However it is nice in that
+    // it locks us into the active region of the thread's stack and not its
+    // theoretical limits.
+    //
+    sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess();
+    Address sp = tmpFrame.getSP();
+    Address starting = sp;
+    Address maxSP = starting;
+    Address minSP = starting;
+    RegisterMap tmpMap = thread.newRegisterMap(false);
+    while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) {
+        tmpFrame = tmpFrame.sender(tmpMap);
+        if (tmpFrame != null) {
+          sp = tmpFrame.getSP();
+          if (sp != null) {
+            maxSP = AddressOps.max(maxSP, sp);
+            minSP = AddressOps.min(minSP, sp);
+          }
+        }
+
+    }
+    // It is useful to be able to see say +/- 8K on the current stack range
+    AnnotatedMemoryPanel annoMemPanel = new AnnotatedMemoryPanel(agent.getDebugger(), is64Bit, starting,
+                                                                 minSP.addOffsetTo(-8192),
+                                                                 maxSP.addOffsetTo( 8192));
+
+    stackFrame.getContentPane().add(annoMemPanel, BorderLayout.CENTER);
+    desktop.add(stackFrame);
+    GraphicsUtilities.reshapeToAspectRatio(stackFrame, 4.0f / 3.0f, 0.85f, stackFrame.getParent().getSize());
+    stackFrame.show();
+
+    // Stackmap computation for interpreted frames is expensive; do
+    // all stackwalking work in another thread for better GUI
+    // responsiveness
+    workerThread.invokeLater(new StackWalker(vframe, annoMemPanel) {
+        public void run() {
+          Address startAddr = null;
+
+          // As this is a debugger, we want to provide potential crash
+          // information to the user, i.e., by marking signal handler frames
+          // on the stack. Since this system is currently targeted at
+          // annotating the Java frames (interpreted or compiled) on the
+          // stack and not, for example, "external" frames (note the current
+          // absence of a PC-to-symbol lookup mechanism at the Debugger
+          // level), we want to mark any Java frames which were interrupted
+          // by a signal. We do this by making two passes over the stack,
+          // one which finds signal handler frames and puts the parent
+          // frames in a table and one which finds Java frames and if they
+          // are in the table indicates that they were interrupted by a signal.
+
+          Map interruptedFrameMap = new HashMap();
+          {
+            sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess();
+            RegisterMap tmpMap = thread.newRegisterMap(false);
+            while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) {
+              if (tmpFrame.isSignalHandlerFrameDbg()) {
+                // Add some information to the map that we can extract later
+                sun.jvm.hotspot.runtime.Frame interruptedFrame = tmpFrame.sender(tmpMap);
+                SignalInfo info = new SignalInfo();
+                info.sigNum  = tmpFrame.getSignalNumberDbg();
+                info.sigName = tmpFrame.getSignalNameDbg();
+                interruptedFrameMap.put(interruptedFrame, info);
+              }
+              tmpFrame = tmpFrame.sender(tmpMap);
+            }
+          }
+
+          while (vf != null) {
+            String anno = null;
+            JavaVFrame curVFrame = vf;
+            sun.jvm.hotspot.runtime.Frame curFrame = curVFrame.getFrame();
+            Method interpreterFrameMethod = null;
+
+            if (curVFrame.isInterpretedFrame()) {
+              anno = "Interpreted frame";
+            } else {
+              anno = "Compiled frame";
+              if (curVFrame.isDeoptimized()) {
+                anno += " (deoptimized)";
+              }
+            }
+            if (curVFrame.mayBeImpreciseDbg()) {
+              anno += "; information may be imprecise";
+            }
+
+            if (curVFrame.isInterpretedFrame()) {
+              // Find the codelet
+              InterpreterCodelet codelet = VM.getVM().getInterpreter().getCodeletContaining(curFrame.getPC());
+              String description = null;
+              if (codelet != null) {
+                description = codelet.getDescription();
+              }
+              if (description == null) {
+                anno += "\n(Unknown interpreter codelet)";
+              } else {
+                anno += "\nExecuting in codelet \"" + description + "\" at PC = " + curFrame.getPC();
+              }
+            } else if (curVFrame.isCompiledFrame()) {
+              anno += "\nExecuting at PC = " + curFrame.getPC();
+            }
+
+            if (startAddr == null) {
+              startAddr = curFrame.getSP();
+            }
+
+            // FIXME: some compiled frames with empty oop map sets have been
+            // found (for example, Vector's inner Enumeration class, method
+            // "hasMoreElements"). Not sure yet why these cases are showing
+            // up -- should be possible (though unlikely) for safepoint code
+            // to patch the return instruction of these methods and then
+            // later attempt to get an oop map for that instruction. For
+            // now, we warn if we find such a method.
+            boolean shouldSkipOopMaps = false;
+            if (curVFrame.isCompiledFrame()) {
+              CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC());
+              OopMapSet maps = cb.getOopMaps();
+              if ((maps == null) || (maps.getSize() == 0)) {
+                shouldSkipOopMaps = true;
+              }
+            }
+
+            // Add signal information to annotation if necessary
+            SignalInfo sigInfo = (SignalInfo) interruptedFrameMap.get(curFrame);
+            if (sigInfo != null) {
+              // This frame took a signal and we need to report it.
+              anno = (anno + "\n*** INTERRUPTED BY SIGNAL " + Integer.toString(sigInfo.sigNum) +
+                      " (" + sigInfo.sigName + ")");
+            }
+
+            JavaVFrame nextVFrame = curVFrame;
+            sun.jvm.hotspot.runtime.Frame nextFrame = curFrame;
+            do {
+              curVFrame = nextVFrame;
+              curFrame = nextFrame;
+
+              try {
+                Method method = curVFrame.getMethod();
+                if (interpreterFrameMethod == null && curVFrame.isInterpretedFrame()) {
+                  interpreterFrameMethod = method;
+                }
+                int bci = curVFrame.getBCI();
+                String lineNumberAnno = "";
+                if (method.hasLineNumberTable()) {
+                  if ((bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) ||
+                      (bci >= 0 && bci < method.getCodeSize())) {
+                    lineNumberAnno = ", line " + method.getLineNumberFromBCI(bci);
+                  } else {
+                    lineNumberAnno = " (INVALID BCI)";
+                  }
+                }
+                anno += "\n" + method.getMethodHolder().getName().asString() + "." +
+                               method.getName().asString() + method.getSignature().asString() +
+                               "\n@bci " + bci + lineNumberAnno;
+              } catch (Exception e) {
+                anno += "\n(ERROR while iterating vframes for frame " + curFrame + ")";
+              }
+
+              nextVFrame = curVFrame.javaSender();
+              if (nextVFrame != null) {
+                nextFrame = nextVFrame.getFrame();
+              }
+            } while (nextVFrame != null && nextFrame.equals(curFrame));
+
+            if (shouldSkipOopMaps) {
+              anno = anno + "\nNOTE: null or empty OopMapSet found for this CodeBlob";
+            }
+
+            if (curFrame.getFP() != null) {
+              annoPanel.addAnnotation(new Annotation(curFrame.getSP(),
+                                                     curFrame.getFP(),
+                                                     anno));
+            } else {
+              if (VM.getVM().getCPU().equals("x86") || VM.getVM().getCPU().equals("amd64")) {
+                // For C2, which has null frame pointers on x86/amd64
+                CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC());
+                Address sp = curFrame.getSP();
+                if (Assert.ASSERTS_ENABLED) {
+                  Assert.that(cb.getFrameSize() > 0, "CodeBlob must have non-zero frame size");
+                }
+                annoPanel.addAnnotation(new Annotation(sp,
+                                                       sp.addOffsetTo(cb.getFrameSize()),
+                                                       anno));
+              } else {
+                Assert.that(VM.getVM().getCPU().equals("ia64"), "only ia64 should reach here");
+              }
+            }
+
+            // Add interpreter frame annotations
+            if (curFrame.isInterpretedFrame()) {
+              annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameExpressionStack(),
+                                                     curFrame.addressOfInterpreterFrameTOS(),
+                                                     "Interpreter expression stack"));
+              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 methodOop";
+              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");
+                      }
+                      // Check contents
+                      OopHandle handle = addr.getOopHandleAt(0);
+                      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;
+                            }
+                          }
+
+                          if (bad) {
+                            // Try perm gen
+                            if (heap.permGen().isIn(handle)) {
+                              anno = "PermGen ";
+                              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 if (heap.permGen().isIn(handle)) {
+                            anno = "PSPermGen ";
+                            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();
+        }
+      });
+  }
+
+  /** NOTE we are in a different thread here than either the main
+      thread or the Swing/AWT event handler thread, so we must be very
+      careful when creating or removing widgets */
+  private void attach(String pidText) {
+      try {
+      this.pidText = pidText;
+      pid = Integer.parseInt(pidText);
+    }
+    catch (NumberFormatException e) {
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            setMenuItemsEnabled(attachMenuItems, true);
+            JOptionPane.showInternalMessageDialog(desktop,
+                                                  "Unable to parse process ID \"" + HSDB.this.pidText + "\".\nPlease enter a number.",
+                                                  "Parse error",
+                                                  JOptionPane.WARNING_MESSAGE);
+          }
+        });
+      return;
+    }
+
+    // Try to attach to this process
+    Runnable remover = new Runnable() {
+          public void run() {
+            attachWaitDialog.setVisible(false);
+            desktop.remove(attachWaitDialog);
+            attachWaitDialog = null;
+          }
+      };
+
+    try {
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            JOptionPane pane = new JOptionPane("Attaching to process " + pid + ", please wait...", JOptionPane.INFORMATION_MESSAGE);
+            pane.setOptions(new Object[] {});
+            attachWaitDialog = pane.createInternalFrame(desktop, "Attaching to Process");
+            attachWaitDialog.show();
+          }
+        });
+
+      // FIXME: display exec'd debugger's output messages during this
+      // lengthy call
+      agent.attach(pid);
+      if (agent.getDebugger().hasConsole()) {
+        showDbgConsoleMenuItem.setEnabled(true);
+      }
+      attached = true;
+      SwingUtilities.invokeLater(remover);
+    }
+    catch (DebuggerException e) {
+      SwingUtilities.invokeLater(remover);
+      final String errMsg = formatMessage(e.getMessage(), 80);
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            setMenuItemsEnabled(attachMenuItems, true);
+            JOptionPane.showInternalMessageDialog(desktop,
+                                                  "Unable to connect to process ID " + pid + ":\n\n" + errMsg,
+                                                  "Unable to Connect",
+                                                  JOptionPane.WARNING_MESSAGE);
+          }
+        });
+      agent.detach();
+      return;
+    }
+
+    // OK, the VM should be available. Create the Threads dialog.
+    showThreadsDialog();
+  }
+
+  /** NOTE we are in a different thread here than either the main
+      thread or the Swing/AWT event handler thread, so we must be very
+      careful when creating or removing widgets */
+  private void attach(final String executablePath, final String corePath) {
+    // Try to open this core file
+    Runnable remover = new Runnable() {
+          public void run() {
+            attachWaitDialog.setVisible(false);
+            desktop.remove(attachWaitDialog);
+            attachWaitDialog = null;
+          }
+      };
+
+    try {
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            JOptionPane pane = new JOptionPane("Opening core file, please wait...", JOptionPane.INFORMATION_MESSAGE);
+            pane.setOptions(new Object[] {});
+            attachWaitDialog = pane.createInternalFrame(desktop, "Opening Core File");
+            attachWaitDialog.show();
+          }
+        });
+
+      // FIXME: display exec'd debugger's output messages during this
+      // lengthy call
+      agent.attach(executablePath, corePath);
+      if (agent.getDebugger().hasConsole()) {
+        showDbgConsoleMenuItem.setEnabled(true);
+      }
+      attached = true;
+      SwingUtilities.invokeLater(remover);
+    }
+    catch (DebuggerException e) {
+      SwingUtilities.invokeLater(remover);
+      final String errMsg = formatMessage(e.getMessage(), 80);
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            setMenuItemsEnabled(attachMenuItems, true);
+            JOptionPane.showInternalMessageDialog(desktop,
+                                                  "Unable to open core file\n" + corePath + ":\n\n" + errMsg,
+                                                  "Unable to Open Core File",
+                                                  JOptionPane.WARNING_MESSAGE);
+          }
+        });
+      agent.detach();
+      return;
+    }
+
+    // OK, the VM should be available. Create the Threads dialog.
+    showThreadsDialog();
+  }
+
+  /** NOTE we are in a different thread here than either the main
+      thread or the Swing/AWT event handler thread, so we must be very
+      careful when creating or removing widgets */
+  private void connect(final String remoteMachineName) {
+    // Try to open this core file
+    Runnable remover = new Runnable() {
+          public void run() {
+            attachWaitDialog.setVisible(false);
+            desktop.remove(attachWaitDialog);
+            attachWaitDialog = null;
+          }
+      };
+
+    try {
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            JOptionPane pane = new JOptionPane("Connecting to debug server, please wait...", JOptionPane.INFORMATION_MESSAGE);
+            pane.setOptions(new Object[] {});
+            attachWaitDialog = pane.createInternalFrame(desktop, "Connecting to Debug Server");
+            attachWaitDialog.show();
+          }
+        });
+
+      agent.attach(remoteMachineName);
+      if (agent.getDebugger().hasConsole()) {
+        showDbgConsoleMenuItem.setEnabled(true);
+      }
+      attached = true;
+      SwingUtilities.invokeLater(remover);
+    }
+    catch (DebuggerException e) {
+      SwingUtilities.invokeLater(remover);
+      final String errMsg = formatMessage(e.getMessage(), 80);
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            setMenuItemsEnabled(attachMenuItems, true);
+            JOptionPane.showInternalMessageDialog(desktop,
+                                                  "Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg,
+                                                  "Unable to Connect",
+                                                  JOptionPane.WARNING_MESSAGE);
+          }
+        });
+      agent.detach();
+      return;
+    }
+
+    // OK, the VM should be available. Create the Threads dialog.
+    showThreadsDialog();
+  }
+
+  private void detachDebugger() {
+    if (!attached) {
+      return;
+    }
+    agent.detach();
+    attached = false;
+  }
+
+  private void detach() {
+    detachDebugger();
+    attachWaitDialog = null;
+    threadsFrame = null;
+    consoleFrame = null;
+    setMenuItemsEnabled(attachMenuItems, true);
+    setMenuItemsEnabled(detachMenuItems, false);
+    toolsMenu.setEnabled(false);
+    showDbgConsoleMenuItem.setEnabled(false);
+    // FIXME: is this sufficient, or will I have to do anything else
+    // to the components to kill them off? What about WorkerThreads?
+    desktop.removeAll();
+    desktop.invalidate();
+    desktop.validate();
+    desktop.repaint();
+  }
+
+  /** NOTE that this is called from another thread than the main or
+      Swing thread and we have to be careful about synchronization */
+  private void showThreadsDialog() {
+    SwingUtilities.invokeLater(new Runnable() {
+        public void run() {
+          threadsFrame = new JInternalFrame("Java Threads");
+          threadsFrame.setResizable(true);
+          threadsFrame.setIconifiable(true);
+          JavaThreadsPanel threadsPanel = new JavaThreadsPanel();
+          threadsPanel.addPanelListener(HSDB.this);
+          threadsFrame.getContentPane().add(threadsPanel);
+          threadsFrame.setSize(500, 300);
+          threadsFrame.pack();
+          desktop.add(threadsFrame);
+          GraphicsUtilities.moveToInContainer(threadsFrame, 0.75f, 0.25f, 0, 20);
+          threadsFrame.show();
+          setMenuItemsEnabled(attachMenuItems, false);
+          setMenuItemsEnabled(detachMenuItems, true);
+          toolsMenu.setEnabled(true);
+          VM.registerVMInitializedObserver(new Observer() {
+              public void update(Observable o, Object data) {
+                computeRevPtrsMenuItem.setEnabled(true);
+              }
+            });
+        }
+      });
+  }
+
+  private void showObjectHistogram() {
+    sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram();
+    ObjectHistogramCleanupThunk cleanup =
+      new ObjectHistogramCleanupThunk(histo);
+    doHeapIteration("Object Histogram",
+                    "Generating histogram...",
+                    histo,
+                    cleanup);
+  }
+
+  class ObjectHistogramCleanupThunk implements CleanupThunk {
+    sun.jvm.hotspot.oops.ObjectHistogram histo;
+
+    ObjectHistogramCleanupThunk(sun.jvm.hotspot.oops.ObjectHistogram histo) {
+      this.histo = histo;
+    }
+
+    public void heapIterationComplete() {
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            JInternalFrame histoFrame = new JInternalFrame("Object Histogram");
+            histoFrame.setResizable(true);
+            histoFrame.setClosable(true);
+            histoFrame.setIconifiable(true);
+            histoFrame.getContentPane().setLayout(new BorderLayout());
+            ObjectHistogramPanel panel = new ObjectHistogramPanel(histo);
+            panel.addPanelListener(HSDB.this);
+            histoFrame.getContentPane().add(panel);
+            desktop.add(histoFrame);
+            GraphicsUtilities.reshapeToAspectRatio(histoFrame, 4.0f / 3.0f, 0.6f,
+                                       histoFrame.getParent().getSize());
+            GraphicsUtilities.centerInContainer(histoFrame);
+            histoFrame.show();
+          }
+        });
+    }
+  }
+
+  public void showObjectsOfType(Klass type) {
+    FindObjectByType finder = new FindObjectByType(type);
+    FindObjectByTypeCleanupThunk cleanup =
+      new FindObjectByTypeCleanupThunk(finder);
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    type.printValueOn(new PrintStream(bos));
+    String typeName = bos.toString();
+    doHeapIteration("Show Objects Of Type",
+                    "Finding instances of \"" + typeName + "\"",
+                    finder,
+                    cleanup);
+  }
+
+  class FindObjectByTypeCleanupThunk implements CleanupThunk {
+    FindObjectByType finder;
+
+    FindObjectByTypeCleanupThunk(FindObjectByType finder) {
+      this.finder = finder;
+    }
+
+    public void heapIterationComplete() {
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            JInternalFrame finderFrame = new JInternalFrame("Show Objects of Type");
+            finderFrame.getContentPane().setLayout(new BorderLayout());
+            finderFrame.setResizable(true);
+            finderFrame.setClosable(true);
+            finderFrame.setIconifiable(true);
+            ObjectListPanel panel = new ObjectListPanel(finder.getResults(),
+                                                        new HeapProgress("Reverse Pointers Analysis"));
+            panel.addPanelListener(HSDB.this);
+            finderFrame.getContentPane().add(panel);
+            desktop.add(finderFrame);
+            GraphicsUtilities.reshapeToAspectRatio(finderFrame, 4.0f / 3.0f, 0.6f,
+                                       finderFrame.getParent().getSize());
+            GraphicsUtilities.centerInContainer(finderFrame);
+            finderFrame.show();
+          }
+        });
+    }
+  }
+
+  private void showDebuggerConsole() {
+    if (consoleFrame == null) {
+      consoleFrame = new JInternalFrame("Debugger Console");
+      consoleFrame.setResizable(true);
+      consoleFrame.setClosable(true);
+      consoleFrame.setIconifiable(true);
+      consoleFrame.getContentPane().setLayout(new BorderLayout());
+      consoleFrame.getContentPane().add(new DebuggerConsolePanel(agent.getDebugger()), BorderLayout.CENTER);
+      GraphicsUtilities.reshapeToAspectRatio(consoleFrame, 5.0f, 0.9f, desktop.getSize());
+    }
+    if (consoleFrame.getParent() == null) {
+      desktop.add(consoleFrame);
+    }
+    consoleFrame.setVisible(true);
+    consoleFrame.show();
+    consoleFrame.getContentPane().getComponent(0).requestFocus();
+  }
+
+  private void showConsole() {
+      CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() {
+              public HotSpotAgent getAgent() {
+                  return agent;
+              }
+              public boolean isAttached() {
+                  return attached;
+              }
+              public void attach(String pid) {
+                  attach(pid);
+              }
+              public void attach(String java, String core) {
+              }
+              public void detach() {
+                  detachDebugger();
+              }
+              public void reattach() {
+                  if (attached) {
+                      detachDebugger();
+                  }
+                  if (pidText != null) {
+                      attach(pidText);
+                  } else {
+                      attach(execPath, coreFilename);
+                  }
+              }
+          };
+
+      showPanel("Command Line", new CommandProcessorPanel(new CommandProcessor(di, null, null, null)));
+  }
+
+  private void showFindByQueryPanel() {
+    showPanel("Find Object by Query", new FindByQueryPanel());
+  }
+
+  private void showFindPanel() {
+    showPanel("Find Pointer", new FindPanel());
+  }
+
+  private void showFindInHeapPanel() {
+    showPanel("Find Address In Heap", new FindInHeapPanel());
+  }
+
+  private void showFindInCodeCachePanel() {
+    showPanel("Find Address In Code Cache", new FindInCodeCachePanel());
+  }
+
+  private void showHeapParametersPanel() {
+    showPanel("Heap Parameters", new HeapParametersPanel());
+  }
+
+  public void showThreadInfo(final JavaThread thread) {
+    showPanel("Info for " + thread.getThreadName(), new ThreadInfoPanel(thread));
+  }
+
+  public void showJavaStackTrace(final JavaThread thread) {
+    JavaStackTracePanel jstp = new JavaStackTracePanel();
+    showPanel("Java stack trace for " + thread.getThreadName(), jstp);
+    jstp.setJavaThread(thread);
+  }
+
+  private void showDeadlockDetectionPanel() {
+    showPanel("Deadlock Detection", new DeadlockDetectionPanel());
+  }
+
+  private void showMonitorCacheDumpPanel() {
+    showPanel("Monitor Cache Dump", new MonitorCacheDumpPanel());
+  }
+
+  public void showClassBrowser() {
+    final JInternalFrame progressFrame = new JInternalFrame("Class Browser");
+    progressFrame.setResizable(true);
+    progressFrame.setClosable(true);
+    progressFrame.setIconifiable(true);
+    progressFrame.getContentPane().setLayout(new BorderLayout());
+    final ProgressBarPanel bar = new ProgressBarPanel("Generating class list ..");
+    bar.setIndeterminate(true);
+    progressFrame.getContentPane().add(bar, BorderLayout.CENTER);
+    desktop.add(progressFrame);
+    progressFrame.pack();
+    GraphicsUtilities.centerInContainer(progressFrame);
+    progressFrame.show();
+
+    workerThread.invokeLater(new Runnable() {
+                                public void run() {
+                                   HTMLGenerator htmlGen = new HTMLGenerator();
+                                   InstanceKlass[] klasses = SystemDictionaryHelper.getAllInstanceKlasses();
+                                   final String htmlText = htmlGen.genHTMLForKlassNames(klasses);
+                                   SwingUtilities.invokeLater(new Runnable() {
+                                      public void run() {
+                                         JInternalFrame cbFrame = new JInternalFrame("Class Browser");
+                                         cbFrame.getContentPane().setLayout(new BorderLayout());
+                                         cbFrame.setResizable(true);
+                                         cbFrame.setClosable(true);
+                                         cbFrame.setIconifiable(true);
+                                         ClassBrowserPanel cbPanel = new ClassBrowserPanel();
+                                         cbFrame.getContentPane().add(cbPanel, BorderLayout.CENTER);
+                                         desktop.remove(progressFrame);
+                                         desktop.repaint();
+                                         desktop.add(cbFrame);
+                                         GraphicsUtilities.reshapeToAspectRatio(cbFrame, 1.25f, 0.85f,
+                                                                      cbFrame.getParent().getSize());
+                                         cbFrame.show();
+                                         cbPanel.setClassesText(htmlText);
+                                      }
+                                   });
+                                }
+                             });
+  }
+
+  public void showCodeViewer() {
+    showPanel("Code Viewer", new CodeViewerPanel(), 1.25f, 0.85f);
+  }
+
+  public void showCodeViewer(final Address address) {
+    final CodeViewerPanel panel = new CodeViewerPanel();
+    showPanel("Code Viewer", panel, 1.25f, 0.85f);
+    SwingUtilities.invokeLater(new Runnable() {
+        public void run() {
+          panel.viewAddress(address);
+        }
+      });
+
+  }
+
+  public void showMemoryViewer() {
+    showPanel("Memory Viewer", new MemoryViewer(agent.getDebugger(), agent.getTypeDataBase().getAddressSize() == 8));
+  }
+
+  public void showCommandLineFlags() {
+    showPanel("Command Line Flags", new VMFlagsPanel());
+  }
+
+  public void showVMVersion() {
+    showPanel("VM Version Info", new VMVersionInfoPanel());
+  }
+
+  public void showSystemProperties() {
+    showPanel("System Properties", new SysPropsPanel());
+  }
+
+  private void showPanel(String name, JPanel panel) {
+    showPanel(name, panel, 5.0f / 3.0f, 0.4f);
+  }
+
+  private void showPanel(String name, JPanel panel, float aspectRatio, float fillRatio) {
+    JInternalFrame frame = new JInternalFrame(name);
+    frame.getContentPane().setLayout(new BorderLayout());
+    frame.setResizable(true);
+    frame.setClosable(true);
+    frame.setIconifiable(true);
+    frame.setMaximizable(true);
+    frame.getContentPane().add(panel, BorderLayout.CENTER);
+    desktop.add(frame);
+    GraphicsUtilities.reshapeToAspectRatio(frame, aspectRatio, fillRatio, frame.getParent().getSize());
+    GraphicsUtilities.randomLocation(frame);
+    frame.show();
+    if (panel instanceof SAPanel) {
+      ((SAPanel)panel).addPanelListener(this);
+    }
+  }
+
+  //--------------------------------------------------------------------------------
+  // Framework for heap iteration with progress bar
+  //
+
+  interface CleanupThunk {
+    public void heapIterationComplete();
+  }
+
+  class HeapProgress implements HeapProgressThunk {
+    private JInternalFrame frame;
+    private ProgressBarPanel bar;
+    private String windowTitle;
+    private String progressBarTitle;
+    private CleanupThunk cleanup;
+
+    HeapProgress(String windowTitle) {
+      this(windowTitle, "Percentage of heap visited", null);
+    }
+
+    HeapProgress(String windowTitle, String progressBarTitle) {
+      this(windowTitle, progressBarTitle, null);
+    }
+
+    HeapProgress(String windowTitle, String progressBarTitle, CleanupThunk cleanup) {
+      this.windowTitle = windowTitle;
+      this.progressBarTitle = progressBarTitle;
+      this.cleanup = cleanup;
+    }
+
+    public void heapIterationFractionUpdate(final double fractionOfHeapVisited) {
+      if (frame == null) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+              frame = new JInternalFrame(windowTitle);
+              frame.setResizable(true);
+              frame.setIconifiable(true);
+              frame.getContentPane().setLayout(new BorderLayout());
+              bar = new ProgressBarPanel(progressBarTitle);
+              frame.getContentPane().add(bar, BorderLayout.CENTER);
+              desktop.add(frame);
+              frame.pack();
+              GraphicsUtilities.constrainToSize(frame, frame.getParent().getSize());
+              GraphicsUtilities.centerInContainer(frame);
+              frame.show();
+            }
+          });
+      }
+
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            bar.setValue(fractionOfHeapVisited);
+          }
+        });
+    }
+
+    public void heapIterationComplete() {
+      SwingUtilities.invokeLater(new Runnable() {
+          public void run() {
+            desktop.remove(frame);
+            desktop.repaint();
+            if (VM.getVM().getRevPtrs() != null) {
+              // Ended up computing reverse pointers as a side-effect
+              computeRevPtrsMenuItem.setEnabled(false);
+            }
+          }
+        });
+
+      if (cleanup != null) {
+        cleanup.heapIterationComplete();
+      }
+    }
+  }
+
+  class VisitHeap implements Runnable {
+    HeapVisitor visitor;
+
+    VisitHeap(HeapVisitor visitor) {
+      this.visitor = visitor;
+    }
+
+    public void run() {
+      VM.getVM().getObjectHeap().iterate(visitor);
+    }
+  }
+
+  private void doHeapIteration(String frameTitle,
+                               String progressBarText,
+                               HeapVisitor visitor,
+                               CleanupThunk cleanup) {
+    sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram();
+    HeapProgress progress = new HeapProgress(frameTitle,
+                                             progressBarText,
+                                             cleanup);
+    HeapVisitor progVisitor = new ProgressiveHeapVisitor(visitor, progress);
+    workerThread.invokeLater(new VisitHeap(progVisitor));
+  }
+
+  //--------------------------------------------------------------------------------
+  // Stack trace helper
+  //
+
+  private static JavaVFrame getLastJavaVFrame(JavaThread cur) {
+    RegisterMap regMap = cur.newRegisterMap(true);
+    sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess();
+    if (f == null) return null;
+    boolean imprecise = true;
+    if (f.isInterpretedFrame() && !f.isInterpretedFrameValid()) {
+      System.err.println("Correcting for invalid interpreter frame");
+      f = f.sender(regMap);
+      imprecise = false;
+    }
+    VFrame vf = VFrame.newVFrame(f, regMap, cur, true, imprecise);
+    if (vf == null) {
+      System.err.println(" (Unable to create vframe for topmost frame guess)");
+      return null;
+    }
+    if (vf.isJavaFrame()) {
+      return (JavaVFrame) vf;
+    }
+    return (JavaVFrame) vf.javaSender();
+  }
+
+  // Internal routine for debugging
+  private static void dumpStack(JavaThread cur) {
+    RegisterMap regMap = cur.newRegisterMap(true);
+    sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess();
+    PrintStream tty = System.err;
+    while (f != null) {
+      tty.print("Found ");
+           if (f.isInterpretedFrame()) { tty.print("interpreted"); }
+      else if (f.isCompiledFrame())    { tty.print("compiled"); }
+      else if (f.isEntryFrame())       { tty.print("entry"); }
+      else if (f.isNativeFrame())      { tty.print("native"); }
+      else if (f.isGlueFrame())        { tty.print("glue"); }
+      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);
+    }
+  }
+}