1 /*
   2  * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores,
  20  * CA 94065 USA or visit www.oracle.com if you need additional information or
  21  * have any questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot;
  26 
  27 import java.io.*;
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.math.*;
  31 import javax.swing.*;
  32 import javax.swing.tree.*;
  33 import java.util.*;
  34 
  35 import sun.jvm.hotspot.code.*;
  36 import sun.jvm.hotspot.compiler.*;
  37 import sun.jvm.hotspot.debugger.*;
  38 import sun.jvm.hotspot.gc_implementation.parallelScavenge.*;
  39 import sun.jvm.hotspot.gc_interface.*;
  40 import sun.jvm.hotspot.interpreter.*;
  41 import sun.jvm.hotspot.memory.*;
  42 import sun.jvm.hotspot.oops.*;
  43 import sun.jvm.hotspot.runtime.*;
  44 import sun.jvm.hotspot.ui.*;
  45 import sun.jvm.hotspot.ui.tree.*;
  46 import sun.jvm.hotspot.ui.classbrowser.*;
  47 import sun.jvm.hotspot.utilities.*;
  48 
  49 /** The top-level HotSpot Debugger. FIXME: make this an embeddable
  50     component! (Among other things, figure out what to do with the
  51     menu bar...) */
  52 
  53 public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
  54   public static void main(String[] args) {
  55     new HSDB(args).run();
  56   }
  57 
  58   //--------------------------------------------------------------------------------
  59   // Internals only below this point
  60   //
  61   private HotSpotAgent agent;
  62   private JDesktopPane desktop;
  63   private boolean      attached;
  64   /** List <JMenuItem> */
  65   private java.util.List attachMenuItems;
  66   /** List <JMenuItem> */
  67   private java.util.List detachMenuItems;
  68   private JMenu toolsMenu;
  69   private JMenuItem showDbgConsoleMenuItem;
  70   private JMenuItem computeRevPtrsMenuItem;
  71   private JInternalFrame attachWaitDialog;
  72   private JInternalFrame threadsFrame;
  73   private JInternalFrame consoleFrame;
  74   private WorkerThread workerThread;
  75   // These had to be made data members because they are referenced in inner classes.
  76   private String pidText;
  77   private int pid;
  78   private String execPath;
  79   private String coreFilename;
  80 
  81   private void doUsage() {
  82     System.out.println("Usage:  java HSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]");
  83     System.out.println("           pid:                     attach to the process whose id is 'pid'");
  84     System.out.println("           path-to-java-executable: Debug a core file produced by this program");
  85     System.out.println("           path-to-corefile:        Debug this corefile.  The default is 'core'");
  86     System.out.println("        If no arguments are specified, you can select what to do from the GUI.\n");
  87     HotSpotAgent.showUsage();
  88   }
  89 
  90   private HSDB(String[] args) {
  91     switch (args.length) {
  92     case (0):
  93       break;
  94 
  95     case (1):
  96       if (args[0].equals("help") || args[0].equals("-help")) {
  97         doUsage();
  98         System.exit(0);
  99       }
 100       // If all numbers, it is a PID to attach to
 101       // Else, it is a pathname to a .../bin/java for a core file.
 102       try {
 103         int unused = Integer.parseInt(args[0]);
 104         // If we get here, we have a PID and not a core file name
 105         pidText = args[0];
 106       } catch (NumberFormatException e) {
 107         execPath = args[0];
 108         coreFilename = "core";
 109       }
 110       break;
 111 
 112     case (2):
 113       execPath = args[0];
 114       coreFilename = args[1];
 115       break;
 116 
 117     default:
 118       System.out.println("HSDB Error: Too many options specified");
 119       doUsage();
 120       System.exit(1);
 121     }
 122   }
 123 
 124   private void run() {
 125     // At this point, if pidText != null we are supposed to attach to it.
 126     // Else, if execPath != null, it is the path of a jdk/bin/java
 127     // and coreFilename is the pathname of a core file we are
 128     // supposed to attach to.
 129 
 130     agent = new HotSpotAgent();
 131     workerThread = new WorkerThread();
 132     attachMenuItems = new java.util.ArrayList();
 133     detachMenuItems = new java.util.ArrayList();
 134 
 135     JFrame frame = new JFrame("HSDB - HotSpot Debugger");
 136     frame.setSize(800, 600);
 137     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 138 
 139     JMenuBar menuBar = new JMenuBar();
 140 
 141     //
 142     // File menu
 143     //
 144 
 145     JMenu menu = new JMenu("File");
 146     menu.setMnemonic(KeyEvent.VK_F);
 147     JMenuItem item;
 148     item = createMenuItem("Attach to HotSpot process...",
 149                           new ActionListener() {
 150                               public void actionPerformed(ActionEvent e) {
 151                                 showAttachDialog();
 152                               }
 153                             });
 154     item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.ALT_MASK));
 155     item.setMnemonic(KeyEvent.VK_A);
 156     menu.add(item);
 157     attachMenuItems.add(item);
 158 
 159     item = createMenuItem("Open HotSpot core file...",
 160                           new ActionListener() {
 161                               public void actionPerformed(ActionEvent e) {
 162                                 showOpenCoreFileDialog();
 163                               }
 164                             });
 165     item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK));
 166     item.setMnemonic(KeyEvent.VK_O);
 167     menu.add(item);
 168     attachMenuItems.add(item);
 169 
 170     item = createMenuItem("Connect to debug server...",
 171                           new ActionListener() {
 172                               public void actionPerformed(ActionEvent e) {
 173                                 showConnectDialog();
 174                               }
 175                             });
 176     item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.ALT_MASK));
 177     item.setMnemonic(KeyEvent.VK_S);
 178     menu.add(item);
 179     attachMenuItems.add(item);
 180 
 181     item = createMenuItem("Detach",
 182                           new ActionListener() {
 183                               public void actionPerformed(ActionEvent e) {
 184                                 detach();
 185                               }
 186                             });
 187     item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.ALT_MASK));
 188     item.setMnemonic(KeyEvent.VK_S);
 189     menu.add(item);
 190     detachMenuItems.add(item);
 191 
 192     // Disable detach menu items at first
 193     setMenuItemsEnabled(detachMenuItems, false);
 194 
 195     menu.addSeparator();
 196 
 197     item = createMenuItem("Exit",
 198                             new ActionListener() {
 199                                 public void actionPerformed(ActionEvent e) {
 200                                   System.exit(0);
 201                                 }
 202                               });
 203     item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.ALT_MASK));
 204     item.setMnemonic(KeyEvent.VK_X);
 205     menu.add(item);
 206     menuBar.add(menu);
 207 
 208     //
 209     // Tools menu
 210     //
 211 
 212     toolsMenu = new JMenu("Tools");
 213     toolsMenu.setMnemonic(KeyEvent.VK_T);
 214 
 215     item = createMenuItem("Class Browser",
 216                           new ActionListener() {
 217                              public void actionPerformed(ActionEvent e) {
 218                                 showClassBrowser();
 219                              }
 220                           });
 221     item.setMnemonic(KeyEvent.VK_B);
 222 
 223     toolsMenu.add(item);
 224 
 225     item = createMenuItem("Code Viewer",
 226                           new ActionListener() {
 227                              public void actionPerformed(ActionEvent e) {
 228                                 showCodeViewer();
 229                              }
 230                           });
 231     item.setMnemonic(KeyEvent.VK_C);
 232 
 233     toolsMenu.add(item);
 234 
 235 
 236     item = createMenuItem("Compute Reverse Ptrs",
 237                           new ActionListener() {
 238                               public void actionPerformed(ActionEvent e) {
 239                                 fireComputeReversePtrs();
 240                               }
 241                             });
 242     computeRevPtrsMenuItem = item;
 243     item.setMnemonic(KeyEvent.VK_M);
 244     toolsMenu.add(item);
 245 
 246     item = createMenuItem("Deadlock Detection",
 247                           new ActionListener() {
 248                               public void actionPerformed(ActionEvent e) {
 249                                 showDeadlockDetectionPanel();
 250                               }
 251                             });
 252     item.setMnemonic(KeyEvent.VK_D);
 253     toolsMenu.add(item);
 254 
 255     item = createMenuItem("Find Object by Query",
 256                           new ActionListener() {
 257                               public void actionPerformed(ActionEvent e) {
 258                                 showFindByQueryPanel();
 259                               }
 260                             });
 261     item.setMnemonic(KeyEvent.VK_Q);
 262     toolsMenu.add(item);
 263 
 264 
 265     item = createMenuItem("Find Pointer",
 266                           new ActionListener() {
 267                               public void actionPerformed(ActionEvent e) {
 268                                 showFindPanel();
 269                               }
 270                             });
 271     item.setMnemonic(KeyEvent.VK_P);
 272     toolsMenu.add(item);
 273 
 274     item = createMenuItem("Find Value In Heap",
 275                           new ActionListener() {
 276                               public void actionPerformed(ActionEvent e) {
 277                                 showFindInHeapPanel();
 278                               }
 279                             });
 280     item.setMnemonic(KeyEvent.VK_V);
 281     toolsMenu.add(item);
 282 
 283     item = createMenuItem("Find Value In Code Cache",
 284                           new ActionListener() {
 285                               public void actionPerformed(ActionEvent e) {
 286                                 showFindInCodeCachePanel();
 287                               }
 288                             });
 289     item.setMnemonic(KeyEvent.VK_A);
 290     toolsMenu.add(item);
 291 
 292     item = createMenuItem("Heap Parameters",
 293                           new ActionListener() {
 294                               public void actionPerformed(ActionEvent e) {
 295                                 showHeapParametersPanel();
 296                               }
 297                             });
 298     item.setMnemonic(KeyEvent.VK_H);
 299     toolsMenu.add(item);
 300 
 301     item = createMenuItem("Inspector",
 302                           new ActionListener() {
 303                               public void actionPerformed(ActionEvent e) {
 304                                 showInspector(null);
 305                               }
 306                             });
 307     item.setMnemonic(KeyEvent.VK_R);
 308     item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK));
 309     toolsMenu.add(item);
 310 
 311     item = createMenuItem("Memory Viewer",
 312                           new ActionListener() {
 313                              public void actionPerformed(ActionEvent e) {
 314                                 showMemoryViewer();
 315                              }
 316                           });
 317     item.setMnemonic(KeyEvent.VK_M);
 318     toolsMenu.add(item);
 319 
 320     item = createMenuItem("Monitor Cache Dump",
 321                           new ActionListener() {
 322                               public void actionPerformed(ActionEvent e) {
 323                                 showMonitorCacheDumpPanel();
 324                               }
 325                             });
 326     item.setMnemonic(KeyEvent.VK_D);
 327     toolsMenu.add(item);
 328 
 329     item = createMenuItem("Object Histogram",
 330                           new ActionListener() {
 331                               public void actionPerformed(ActionEvent e) {
 332                                 showObjectHistogram();
 333                               }
 334                             });
 335     item.setMnemonic(KeyEvent.VK_O);
 336     toolsMenu.add(item);
 337 
 338     item = createMenuItem("Show System Properties",
 339                           new ActionListener() {
 340                              public void actionPerformed(ActionEvent e) {
 341                                 showSystemProperties();
 342                              }
 343                           });
 344     item.setMnemonic(KeyEvent.VK_S);
 345     toolsMenu.add(item);
 346 
 347     item = createMenuItem("Show VM Version",
 348                           new ActionListener() {
 349                              public void actionPerformed(ActionEvent e) {
 350                                 showVMVersion();
 351                              }
 352                           });
 353     item.setMnemonic(KeyEvent.VK_M);
 354     toolsMenu.add(item);
 355 
 356     item = createMenuItem("Show -XX flags",
 357                           new ActionListener() {
 358                              public void actionPerformed(ActionEvent e) {
 359                                 showCommandLineFlags();
 360                              }
 361                           });
 362     item.setMnemonic(KeyEvent.VK_X);
 363     toolsMenu.add(item);
 364 
 365     toolsMenu.setEnabled(false);
 366     menuBar.add(toolsMenu);
 367 
 368     //
 369     // Windows menu
 370     //
 371 
 372     JMenu windowsMenu = new JMenu("Windows");
 373     windowsMenu.setMnemonic(KeyEvent.VK_W);
 374     item = createMenuItem("Console",
 375                           new ActionListener() {
 376                              public void actionPerformed(ActionEvent e) {
 377                                  showConsole();
 378                              }
 379                           });
 380     item.setMnemonic(KeyEvent.VK_C);
 381     windowsMenu.add(item);
 382     showDbgConsoleMenuItem = createMenuItem("Debugger Console",
 383                                          new ActionListener() {
 384                                              public void actionPerformed(ActionEvent e) {
 385                                                showDebuggerConsole();
 386                                              }
 387                                            });
 388     showDbgConsoleMenuItem.setMnemonic(KeyEvent.VK_D);
 389     windowsMenu.add(showDbgConsoleMenuItem);
 390     showDbgConsoleMenuItem.setEnabled(false);
 391 
 392     menuBar.add(windowsMenu);
 393 
 394 
 395     frame.setJMenuBar(menuBar);
 396 
 397     desktop = new JDesktopPane();
 398     frame.getContentPane().add(desktop);
 399     GraphicsUtilities.reshapeToAspectRatio(frame, 4.0f/3.0f, 0.75f, Toolkit.getDefaultToolkit().getScreenSize());
 400     GraphicsUtilities.centerInContainer(frame, Toolkit.getDefaultToolkit().getScreenSize());
 401     frame.setVisible(true);
 402 
 403     Runtime.getRuntime().addShutdownHook(new java.lang.Thread() {
 404         public void run() {
 405           detachDebugger();
 406         }
 407       });
 408 
 409     if (pidText != null) {
 410       attach(pidText);
 411     } else if (execPath != null) {
 412       attach(execPath, coreFilename);
 413     }
 414   }
 415 
 416   // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
 417   private void showAttachDialog() {
 418     // FIXME: create filtered text field which only accepts numbers
 419     setMenuItemsEnabled(attachMenuItems, false);
 420     final JInternalFrame attachDialog = new JInternalFrame("Attach to HotSpot process");
 421     attachDialog.getContentPane().setLayout(new BorderLayout());
 422 
 423     JPanel panel = new JPanel();
 424     panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
 425     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 426     attachDialog.setBackground(panel.getBackground());
 427 
 428     panel.add(new JLabel("Enter process ID:"));
 429     final JTextField pidTextField = new JTextField(10);
 430     ActionListener attacher = new ActionListener() {
 431         public void actionPerformed(ActionEvent e) {
 432           attachDialog.setVisible(false);
 433           desktop.remove(attachDialog);
 434           workerThread.invokeLater(new Runnable() {
 435               public void run() {
 436                 attach(pidTextField.getText());
 437               }
 438             });
 439         }
 440       };
 441 
 442     pidTextField.addActionListener(attacher);
 443     panel.add(pidTextField);
 444     attachDialog.getContentPane().add(panel, BorderLayout.NORTH);
 445 
 446     Box vbox = Box.createVerticalBox();
 447     panel = new JPanel();
 448     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
 449     panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
 450     JTextArea ta = new JTextArea(
 451                                  "Enter the process ID of a currently-running HotSpot process. On " +
 452                                  "Solaris and most Unix operating systems, this can be determined by " +
 453                                  "typing \"ps -u <your username> | grep java\"; the process ID is the " +
 454                                  "first number which appears on the resulting line. On Windows, the " +
 455                                  "process ID is present in the Task Manager, which can be brought up " +
 456                                  "while logged on to the desktop by pressing Ctrl-Alt-Delete.");
 457     ta.setLineWrap(true);
 458     ta.setWrapStyleWord(true);
 459     ta.setEditable(false);
 460     ta.setBackground(panel.getBackground());
 461     panel.add(ta);
 462     vbox.add(panel);
 463 
 464     Box hbox = Box.createHorizontalBox();
 465     hbox.add(Box.createGlue());
 466     JButton button = new JButton("OK");
 467     button.addActionListener(attacher);
 468     hbox.add(button);
 469     hbox.add(Box.createHorizontalStrut(20));
 470     button = new JButton("Cancel");
 471     button.addActionListener(new ActionListener() {
 472         public void actionPerformed(ActionEvent e) {
 473           attachDialog.setVisible(false);
 474           desktop.remove(attachDialog);
 475           setMenuItemsEnabled(attachMenuItems, true);
 476         }
 477       });
 478     hbox.add(button);
 479     hbox.add(Box.createGlue());
 480     panel = new JPanel();
 481     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 482     panel.add(hbox);
 483     vbox.add(panel);
 484 
 485     attachDialog.getContentPane().add(vbox, BorderLayout.SOUTH);
 486 
 487     desktop.add(attachDialog);
 488     attachDialog.setSize(400, 300);
 489     GraphicsUtilities.centerInContainer(attachDialog);
 490     attachDialog.show();
 491     pidTextField.requestFocus();
 492   }
 493 
 494   // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
 495   private void showOpenCoreFileDialog() {
 496     setMenuItemsEnabled(attachMenuItems, false);
 497     final JInternalFrame dialog = new JInternalFrame("Open Core File");
 498     dialog.getContentPane().setLayout(new BorderLayout());
 499 
 500     JPanel panel = new JPanel();
 501     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
 502     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 503     dialog.setBackground(panel.getBackground());
 504 
 505     Box hbox = Box.createHorizontalBox();
 506     Box vbox = Box.createVerticalBox();
 507     vbox.add(new JLabel("Path to core file:"));
 508     vbox.add(new JLabel("Path to Java executable:"));
 509     hbox.add(vbox);
 510 
 511     vbox = Box.createVerticalBox();
 512     final JTextField corePathField = new JTextField(40);
 513     final JTextField execPathField = new JTextField(40);
 514     vbox.add(corePathField);
 515     vbox.add(execPathField);
 516     hbox.add(vbox);
 517 
 518     final JButton browseCorePath = new JButton("Browse ..");
 519     final JButton browseExecPath = new JButton("Browse ..");
 520     browseCorePath.addActionListener(new ActionListener() {
 521                                         public void actionPerformed(ActionEvent e) {
 522                                            JFileChooser fileChooser = new JFileChooser(new File("."));
 523                                            int retVal = fileChooser.showOpenDialog(dialog);
 524                                            if (retVal == JFileChooser.APPROVE_OPTION) {
 525                                               corePathField.setText(fileChooser.getSelectedFile().getPath());
 526                                            }
 527                                         }
 528                                      });
 529     browseExecPath.addActionListener(new ActionListener() {
 530                                         public void actionPerformed(ActionEvent e) {
 531                                            JFileChooser fileChooser = new JFileChooser(new File("."));
 532                                            int retVal = fileChooser.showOpenDialog(dialog);
 533                                            if (retVal == JFileChooser.APPROVE_OPTION) {
 534                                               execPathField.setText(fileChooser.getSelectedFile().getPath());
 535                                            }
 536                                         }
 537                                      });
 538     vbox = Box.createVerticalBox();
 539     vbox.add(browseCorePath);
 540     vbox.add(browseExecPath);
 541     hbox.add(vbox);
 542 
 543     panel.add(hbox);
 544     dialog.getContentPane().add(panel, BorderLayout.NORTH);
 545 
 546     ActionListener attacher = new ActionListener() {
 547         public void actionPerformed(ActionEvent e) {
 548           dialog.setVisible(false);
 549           desktop.remove(dialog);
 550           workerThread.invokeLater(new Runnable() {
 551               public void run() {
 552                 attach(execPathField.getText(), corePathField.getText());
 553               }
 554             });
 555         }
 556       };
 557     corePathField.addActionListener(attacher);
 558     execPathField.addActionListener(attacher);
 559 
 560     vbox = Box.createVerticalBox();
 561     panel = new JPanel();
 562     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
 563     panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
 564     JTextArea ta = new JTextArea(
 565                                  "Enter the full path names to the core file from a HotSpot process " +
 566                                  "and the Java executable from which it came. The latter is typically " +
 567                                  "located in the JDK/JRE directory under the directory " +
 568                                  "jre/bin/<arch>/native_threads.");
 569     ta.setLineWrap(true);
 570     ta.setWrapStyleWord(true);
 571     ta.setEditable(false);
 572     ta.setBackground(panel.getBackground());
 573     panel.add(ta);
 574     vbox.add(panel);
 575 
 576     hbox = Box.createHorizontalBox();
 577     hbox.add(Box.createGlue());
 578     JButton button = new JButton("OK");
 579     button.addActionListener(attacher);
 580     hbox.add(button);
 581     hbox.add(Box.createHorizontalStrut(20));
 582     button = new JButton("Cancel");
 583     button.addActionListener(new ActionListener() {
 584         public void actionPerformed(ActionEvent e) {
 585           dialog.setVisible(false);
 586           desktop.remove(dialog);
 587           setMenuItemsEnabled(attachMenuItems, true);
 588         }
 589       });
 590     hbox.add(button);
 591     hbox.add(Box.createGlue());
 592     panel = new JPanel();
 593     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 594     panel.add(hbox);
 595     vbox.add(panel);
 596 
 597     dialog.getContentPane().add(vbox, BorderLayout.SOUTH);
 598 
 599     desktop.add(dialog);
 600     dialog.setSize(500, 300);
 601     GraphicsUtilities.centerInContainer(dialog);
 602     dialog.show();
 603     corePathField.requestFocus();
 604   }
 605 
 606   // FIXME: merge showAttachDialog, showOpenCoreFileDialog, showConnectDialog
 607   private void showConnectDialog() {
 608     // FIXME: create filtered text field which only accepts numbers
 609     setMenuItemsEnabled(attachMenuItems, false);
 610     final JInternalFrame dialog = new JInternalFrame("Connect to HotSpot Debug Server");
 611     dialog.getContentPane().setLayout(new BorderLayout());
 612 
 613     JPanel panel = new JPanel();
 614     panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
 615     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 616     dialog.setBackground(panel.getBackground());
 617 
 618     panel.add(new JLabel("Enter machine name:"));
 619     final JTextField pidTextField = new JTextField(40);
 620     ActionListener attacher = new ActionListener() {
 621         public void actionPerformed(ActionEvent e) {
 622           dialog.setVisible(false);
 623           desktop.remove(dialog);
 624           workerThread.invokeLater(new Runnable() {
 625               public void run() {
 626                 connect(pidTextField.getText());
 627               }
 628             });
 629         }
 630       };
 631 
 632     pidTextField.addActionListener(attacher);
 633     panel.add(pidTextField);
 634     dialog.getContentPane().add(panel, BorderLayout.NORTH);
 635 
 636     Box vbox = Box.createVerticalBox();
 637     panel = new JPanel();
 638     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
 639     panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
 640     JTextArea ta = new JTextArea(
 641                                  "Enter the name of a machine on which the HotSpot \"Debug Server\" is " +
 642                                  "running and is attached to a process or core file.");
 643     ta.setLineWrap(true);
 644     ta.setWrapStyleWord(true);
 645     ta.setEditable(false);
 646     ta.setBackground(panel.getBackground());
 647     panel.add(ta);
 648     vbox.add(panel);
 649 
 650     Box hbox = Box.createHorizontalBox();
 651     hbox.add(Box.createGlue());
 652     JButton button = new JButton("OK");
 653     button.addActionListener(attacher);
 654     hbox.add(button);
 655     hbox.add(Box.createHorizontalStrut(20));
 656     button = new JButton("Cancel");
 657     button.addActionListener(new ActionListener() {
 658         public void actionPerformed(ActionEvent e) {
 659           dialog.setVisible(false);
 660           desktop.remove(dialog);
 661           setMenuItemsEnabled(attachMenuItems, true);
 662         }
 663       });
 664     hbox.add(button);
 665     hbox.add(Box.createGlue());
 666     panel = new JPanel();
 667     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 668     panel.add(hbox);
 669     vbox.add(panel);
 670 
 671     dialog.getContentPane().add(vbox, BorderLayout.SOUTH);
 672 
 673     desktop.add(dialog);
 674     dialog.setSize(400, 300);
 675     GraphicsUtilities.centerInContainer(dialog);
 676     dialog.show();
 677     pidTextField.requestFocus();
 678   }
 679 
 680   public void showThreadOopInspector(JavaThread thread) {
 681     showInspector(new OopTreeNodeAdapter(thread.getThreadObj(), null));
 682   }
 683 
 684   public void showInspector(SimpleTreeNode adapter) {
 685     showPanel("Inspector", new Inspector(adapter), 1.0f, 0.65f);
 686   }
 687 
 688   public void showLiveness(Oop oop, LivenessPathList liveness) {
 689     ByteArrayOutputStream bos = new ByteArrayOutputStream();
 690     PrintStream tty = new PrintStream(bos);
 691     int numPaths = liveness.size();
 692     for (int i = 0; i < numPaths; i++) {
 693       tty.println("Path " + (i + 1) + " of " + numPaths + ":");
 694       liveness.get(i).printOn(tty);
 695     }
 696     JTextArea ta = new JTextArea(bos.toString());
 697     ta.setLineWrap(true);
 698     ta.setWrapStyleWord(true);
 699     ta.setEditable(false);
 700 
 701     JPanel panel = new JPanel();
 702     panel.setLayout(new BorderLayout());
 703 
 704     JScrollPane scroller = new JScrollPane();
 705     scroller.getViewport().add(ta);
 706 
 707     panel.add(scroller, BorderLayout.CENTER);
 708 
 709     bos = new ByteArrayOutputStream();
 710     tty = new PrintStream(bos);
 711     tty.print("Liveness result for ");
 712     Oop.printOopValueOn(oop, tty);
 713 
 714     JInternalFrame frame = new JInternalFrame(bos.toString());
 715     frame.setResizable(true);
 716     frame.setClosable(true);
 717     frame.setIconifiable(true);
 718     frame.getContentPane().setLayout(new BorderLayout());
 719     frame.getContentPane().add(panel, BorderLayout.CENTER);
 720     frame.pack();
 721     desktop.add(frame);
 722     GraphicsUtilities.reshapeToAspectRatio(frame, 0.5f / 0.2f, 0.5f, frame.getParent().getSize());
 723     frame.show();
 724   }
 725 
 726   private void fireComputeReversePtrs() {
 727     // Possible this might have been computed elsewhere
 728     if (VM.getVM().getRevPtrs() != null) {
 729       computeRevPtrsMenuItem.setEnabled(false);
 730       return;
 731     }
 732 
 733     workerThread.invokeLater(new Runnable() {
 734         public void run() {
 735           HeapProgress progress = new HeapProgress("Reverse Pointers Analysis");
 736           try {
 737             ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
 738             analysis.setHeapProgressThunk(progress);
 739             analysis.run();
 740             computeRevPtrsMenuItem.setEnabled(false);
 741           } catch (OutOfMemoryError e) {
 742             final String errMsg = formatMessage(e.toString(), 80);
 743             SwingUtilities.invokeLater(new Runnable() {
 744                 public void run() {
 745                   JOptionPane.showInternalMessageDialog(desktop,
 746                                                         "Error computing reverse pointers:" + errMsg,
 747                                                         "Error",
 748                                                         JOptionPane.WARNING_MESSAGE);
 749                 }
 750               });
 751           } finally {
 752             // make sure the progress bar goes away
 753             progress.heapIterationComplete();
 754           }
 755         }
 756       });
 757   }
 758 
 759   // Simple struct containing signal information
 760   class SignalInfo {
 761     public int sigNum;
 762     public String sigName;
 763   }
 764 
 765   // Need to have mutable vframe as well as visible memory panel
 766   abstract class StackWalker implements Runnable {
 767     protected JavaVFrame vf;
 768     protected AnnotatedMemoryPanel annoPanel;
 769 
 770     StackWalker(JavaVFrame vf, AnnotatedMemoryPanel annoPanel) {
 771       this.vf = vf;
 772       this.annoPanel = annoPanel;
 773     }
 774   }
 775 
 776   public void showThreadStackMemory(final JavaThread thread) {
 777     // dumpStack(thread);
 778     JavaVFrame vframe = getLastJavaVFrame(thread);
 779     if (vframe == null) {
 780       JOptionPane.showInternalMessageDialog(desktop,
 781                                             "Thread \"" + thread.getThreadName() +
 782                                             "\" has no Java frames on its stack",
 783                                             "Show Stack Memory",
 784                                             JOptionPane.INFORMATION_MESSAGE);
 785       return;
 786     }
 787 
 788     JInternalFrame stackFrame = new JInternalFrame("Stack Memory for " + thread.getThreadName());
 789     stackFrame.getContentPane().setLayout(new BorderLayout());
 790     stackFrame.setResizable(true);
 791     stackFrame.setClosable(true);
 792     stackFrame.setIconifiable(true);
 793     final long addressSize = agent.getTypeDataBase().getAddressSize();
 794     boolean is64Bit = (addressSize == 8);
 795     // This is somewhat of a  hack to guess a thread's stack limits since the
 796     // JavaThread doesn't support this functionality. However it is nice in that
 797     // it locks us into the active region of the thread's stack and not its
 798     // theoretical limits.
 799     //
 800     sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess();
 801     Address sp = tmpFrame.getSP();
 802     Address starting = sp;
 803     Address maxSP = starting;
 804     Address minSP = starting;
 805     RegisterMap tmpMap = thread.newRegisterMap(false);
 806     while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) {
 807         tmpFrame = tmpFrame.sender(tmpMap);
 808         if (tmpFrame != null) {
 809           sp = tmpFrame.getSP();
 810           if (sp != null) {
 811             maxSP = AddressOps.max(maxSP, sp);
 812             minSP = AddressOps.min(minSP, sp);
 813           }
 814         }
 815 
 816     }
 817     // It is useful to be able to see say +/- 8K on the current stack range
 818     AnnotatedMemoryPanel annoMemPanel = new AnnotatedMemoryPanel(agent.getDebugger(), is64Bit, starting,
 819                                                                  minSP.addOffsetTo(-8192),
 820                                                                  maxSP.addOffsetTo( 8192));
 821 
 822     stackFrame.getContentPane().add(annoMemPanel, BorderLayout.CENTER);
 823     desktop.add(stackFrame);
 824     GraphicsUtilities.reshapeToAspectRatio(stackFrame, 4.0f / 3.0f, 0.85f, stackFrame.getParent().getSize());
 825     stackFrame.show();
 826 
 827     // Stackmap computation for interpreted frames is expensive; do
 828     // all stackwalking work in another thread for better GUI
 829     // responsiveness
 830     workerThread.invokeLater(new StackWalker(vframe, annoMemPanel) {
 831         public void run() {
 832           Address startAddr = null;
 833 
 834           // As this is a debugger, we want to provide potential crash
 835           // information to the user, i.e., by marking signal handler frames
 836           // on the stack. Since this system is currently targeted at
 837           // annotating the Java frames (interpreted or compiled) on the
 838           // stack and not, for example, "external" frames (note the current
 839           // absence of a PC-to-symbol lookup mechanism at the Debugger
 840           // level), we want to mark any Java frames which were interrupted
 841           // by a signal. We do this by making two passes over the stack,
 842           // one which finds signal handler frames and puts the parent
 843           // frames in a table and one which finds Java frames and if they
 844           // are in the table indicates that they were interrupted by a signal.
 845 
 846           Map interruptedFrameMap = new HashMap();
 847           {
 848             sun.jvm.hotspot.runtime.Frame tmpFrame = thread.getCurrentFrameGuess();
 849             RegisterMap tmpMap = thread.newRegisterMap(false);
 850             while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) {
 851               if (tmpFrame.isSignalHandlerFrameDbg()) {
 852                 // Add some information to the map that we can extract later
 853                 sun.jvm.hotspot.runtime.Frame interruptedFrame = tmpFrame.sender(tmpMap);
 854                 SignalInfo info = new SignalInfo();
 855                 info.sigNum  = tmpFrame.getSignalNumberDbg();
 856                 info.sigName = tmpFrame.getSignalNameDbg();
 857                 interruptedFrameMap.put(interruptedFrame, info);
 858               }
 859               tmpFrame = tmpFrame.sender(tmpMap);
 860             }
 861           }
 862 
 863           while (vf != null) {
 864             String anno = null;
 865             JavaVFrame curVFrame = vf;
 866             sun.jvm.hotspot.runtime.Frame curFrame = curVFrame.getFrame();
 867             Method interpreterFrameMethod = null;
 868 
 869             if (curVFrame.isInterpretedFrame()) {
 870               anno = "Interpreted frame";
 871             } else {
 872               anno = "Compiled frame";
 873               if (curVFrame.isDeoptimized()) {
 874                 anno += " (deoptimized)";
 875               }
 876             }
 877             if (curVFrame.mayBeImpreciseDbg()) {
 878               anno += "; information may be imprecise";
 879             }
 880 
 881             if (curVFrame.isInterpretedFrame()) {
 882               // Find the codelet
 883               InterpreterCodelet codelet = VM.getVM().getInterpreter().getCodeletContaining(curFrame.getPC());
 884               String description = null;
 885               if (codelet != null) {
 886                 description = codelet.getDescription();
 887               }
 888               if (description == null) {
 889                 anno += "\n(Unknown interpreter codelet)";
 890               } else {
 891                 anno += "\nExecuting in codelet \"" + description + "\" at PC = " + curFrame.getPC();
 892               }
 893             } else if (curVFrame.isCompiledFrame()) {
 894               anno += "\nExecuting at PC = " + curFrame.getPC();
 895             }
 896 
 897             if (startAddr == null) {
 898               startAddr = curFrame.getSP();
 899             }
 900 
 901             // FIXME: some compiled frames with empty oop map sets have been
 902             // found (for example, Vector's inner Enumeration class, method
 903             // "hasMoreElements"). Not sure yet why these cases are showing
 904             // up -- should be possible (though unlikely) for safepoint code
 905             // to patch the return instruction of these methods and then
 906             // later attempt to get an oop map for that instruction. For
 907             // now, we warn if we find such a method.
 908             boolean shouldSkipOopMaps = false;
 909             if (curVFrame.isCompiledFrame()) {
 910               CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC());
 911               OopMapSet maps = cb.getOopMaps();
 912               if ((maps == null) || (maps.getSize() == 0)) {
 913                 shouldSkipOopMaps = true;
 914               }
 915             }
 916 
 917             // Add signal information to annotation if necessary
 918             SignalInfo sigInfo = (SignalInfo) interruptedFrameMap.get(curFrame);
 919             if (sigInfo != null) {
 920               // This frame took a signal and we need to report it.
 921               anno = (anno + "\n*** INTERRUPTED BY SIGNAL " + Integer.toString(sigInfo.sigNum) +
 922                       " (" + sigInfo.sigName + ")");
 923             }
 924 
 925             JavaVFrame nextVFrame = curVFrame;
 926             sun.jvm.hotspot.runtime.Frame nextFrame = curFrame;
 927             do {
 928               curVFrame = nextVFrame;
 929               curFrame = nextFrame;
 930 
 931               try {
 932                 Method method = curVFrame.getMethod();
 933                 if (interpreterFrameMethod == null && curVFrame.isInterpretedFrame()) {
 934                   interpreterFrameMethod = method;
 935                 }
 936                 int bci = curVFrame.getBCI();
 937                 String lineNumberAnno = "";
 938                 if (method.hasLineNumberTable()) {
 939                   if ((bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) ||
 940                       (bci >= 0 && bci < method.getCodeSize())) {
 941                     lineNumberAnno = ", line " + method.getLineNumberFromBCI(bci);
 942                   } else {
 943                     lineNumberAnno = " (INVALID BCI)";
 944                   }
 945                 }
 946                 anno += "\n" + method.getMethodHolder().getName().asString() + "." +
 947                                method.getName().asString() + method.getSignature().asString() +
 948                                "\n@bci " + bci + lineNumberAnno;
 949               } catch (Exception e) {
 950                 anno += "\n(ERROR while iterating vframes for frame " + curFrame + ")";
 951               }
 952 
 953               nextVFrame = curVFrame.javaSender();
 954               if (nextVFrame != null) {
 955                 nextFrame = nextVFrame.getFrame();
 956               }
 957             } while (nextVFrame != null && nextFrame.equals(curFrame));
 958 
 959             if (shouldSkipOopMaps) {
 960               anno = anno + "\nNOTE: null or empty OopMapSet found for this CodeBlob";
 961             }
 962 
 963             if (curFrame.getFP() != null) {
 964               annoPanel.addAnnotation(new Annotation(curFrame.getSP(),
 965                                                      curFrame.getFP(),
 966                                                      anno));
 967             } else {
 968               if (VM.getVM().getCPU().equals("x86") || VM.getVM().getCPU().equals("amd64")) {
 969                 // For C2, which has null frame pointers on x86/amd64
 970                 CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC());
 971                 Address sp = curFrame.getSP();
 972                 if (Assert.ASSERTS_ENABLED) {
 973                   Assert.that(cb.getFrameSize() > 0, "CodeBlob must have non-zero frame size");
 974                 }
 975                 annoPanel.addAnnotation(new Annotation(sp,
 976                                                        sp.addOffsetTo(cb.getFrameSize()),
 977                                                        anno));
 978               } else {
 979                 Assert.that(VM.getVM().getCPU().equals("ia64"), "only ia64 should reach here");
 980               }
 981             }
 982 
 983             // Add interpreter frame annotations
 984             if (curFrame.isInterpretedFrame()) {
 985               annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameExpressionStack(),
 986                                                      curFrame.addressOfInterpreterFrameTOS(),
 987                                                      "Interpreter expression stack"));
 988               if (interpreterFrameMethod != null) {
 989                 // The offset is just to get the right stack slots highlighted in the output
 990                 int offset = 1;
 991                 annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameLocal(offset),
 992                                                        curFrame.addressOfInterpreterFrameLocal((int) interpreterFrameMethod.getMaxLocals() + offset),
 993                                                        "Interpreter locals area for frame with SP = " + curFrame.getSP()));
 994               }
 995               String methodAnno = "Interpreter frame methodOop";
 996               if (interpreterFrameMethod == null) {
 997                 methodAnno += " (BAD OOP)";
 998               }
 999               Address a = curFrame.addressOfInterpreterFrameMethod();
1000               annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), methodAnno));
1001               a = curFrame.addressOfInterpreterFrameCPCache();
1002               annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache"));
1003             }
1004 
1005             RegisterMap rm = (RegisterMap) vf.getRegisterMap().clone();
1006             if (!shouldSkipOopMaps) {
1007               try {
1008                 curFrame.oopsDo(new AddressVisitor() {
1009                     public void visitAddress(Address addr) {
1010                       if (Assert.ASSERTS_ENABLED) {
1011                         Assert.that(addr.andWithMask(VM.getVM().getAddressSize() - 1) == null,
1012                                     "Address " + addr + "should have been aligned");
1013                       }
1014                       OopHandle handle = addr.getOopHandleAt(0);
1015                       addAnnotation(addr, handle);
1016                     }
1017 
1018                     public void visitCompOopAddress(Address addr) {
1019                       if (Assert.ASSERTS_ENABLED) {
1020                         Assert.that(addr.andWithMask(VM.getVM().getAddressSize() - 1) == null,
1021                                     "Address " + addr + "should have been aligned");
1022                       }
1023                       OopHandle handle = addr.getCompOopHandleAt(0);
1024                       addAnnotation(addr, handle);
1025                     }
1026 
1027                     public void addAnnotation(Address addr, OopHandle handle) {
1028                       // Check contents
1029                       String anno = "null oop";
1030                       if (handle != null) {
1031                         // Find location
1032                         CollectedHeap collHeap = VM.getVM().getUniverse().heap();
1033                         boolean bad = true;
1034                         anno = "BAD OOP";
1035                         if (collHeap instanceof GenCollectedHeap) {
1036                           GenCollectedHeap heap = (GenCollectedHeap) collHeap;
1037                           for (int i = 0; i < heap.nGens(); i++) {
1038                             if (heap.getGen(i).isIn(handle)) {
1039                               if (i == 0) {
1040                                 anno = "NewGen ";
1041                               } else if (i == 1) {
1042                                 anno = "OldGen ";
1043                               } else {
1044                                 anno = "Gen " + i + " ";
1045                               }
1046                               bad = false;
1047                               break;
1048                             }
1049                           }
1050 
1051                           if (bad) {
1052                             // Try perm gen
1053                             if (heap.permGen().isIn(handle)) {
1054                               anno = "PermGen ";
1055                               bad = false;
1056                             }
1057                           }
1058                         } else if (collHeap instanceof ParallelScavengeHeap) {
1059                           ParallelScavengeHeap heap = (ParallelScavengeHeap) collHeap;
1060                           if (heap.youngGen().isIn(handle)) {
1061                             anno = "PSYoungGen ";
1062                             bad = false;
1063                           } else if (heap.oldGen().isIn(handle)) {
1064                             anno = "PSOldGen ";
1065                             bad = false;
1066                           } else if (heap.permGen().isIn(handle)) {
1067                             anno = "PSPermGen ";
1068                             bad = false;
1069                           }
1070                         } else {
1071                           // Optimistically assume the oop isn't bad
1072                           anno = "[Unknown generation] ";
1073                           bad = false;
1074                         }
1075 
1076                         if (!bad) {
1077                           try {
1078                             Oop oop = VM.getVM().getObjectHeap().newOop(handle);
1079                             if (oop instanceof Instance) {
1080                                 // Java-level objects always have workable names
1081                               anno = anno + oop.getKlass().getName().asString();
1082                             } else {
1083                               ByteArrayOutputStream bos = new ByteArrayOutputStream();
1084                               Oop.printOopValueOn(oop, new PrintStream(bos));
1085                               anno = anno + bos.toString();
1086                             }
1087                           }
1088                           catch (AddressException e) {
1089                             anno += "CORRUPT OOP";
1090                           }
1091                           catch (NullPointerException e) {
1092                             anno += "CORRUPT OOP (null pointer)";
1093                           }
1094                         }
1095                       }
1096 
1097                       annoPanel.addAnnotation(new Annotation(addr, addr.addOffsetTo(addressSize), anno));
1098                     }
1099                   }, rm);
1100               } catch (Exception e) {
1101                 System.err.println("Error while performing oopsDo for frame " + curFrame);
1102                 e.printStackTrace();
1103               }
1104             }
1105 
1106             vf = nextVFrame;
1107           }
1108 
1109           // This used to paint as we walked the frames. This caused the display to be refreshed
1110           // enough to be annoying on remote displays. It also would cause the annotations to
1111           // be displayed in varying order which caused some annotations to overwrite others
1112           // depending on the races between painting and adding annotations. This latter problem
1113           // still exists to some degree but moving this code here definitely seems to reduce it
1114           annoPanel.makeVisible(startAddr);
1115           annoPanel.repaint();
1116         }
1117       });
1118   }
1119 
1120   /** NOTE we are in a different thread here than either the main
1121       thread or the Swing/AWT event handler thread, so we must be very
1122       careful when creating or removing widgets */
1123   private void attach(String pidText) {
1124       try {
1125       this.pidText = pidText;
1126       pid = Integer.parseInt(pidText);
1127     }
1128     catch (NumberFormatException e) {
1129       SwingUtilities.invokeLater(new Runnable() {
1130           public void run() {
1131             setMenuItemsEnabled(attachMenuItems, true);
1132             JOptionPane.showInternalMessageDialog(desktop,
1133                                                   "Unable to parse process ID \"" + HSDB.this.pidText + "\".\nPlease enter a number.",
1134                                                   "Parse error",
1135                                                   JOptionPane.WARNING_MESSAGE);
1136           }
1137         });
1138       return;
1139     }
1140 
1141     // Try to attach to this process
1142     Runnable remover = new Runnable() {
1143           public void run() {
1144             attachWaitDialog.setVisible(false);
1145             desktop.remove(attachWaitDialog);
1146             attachWaitDialog = null;
1147           }
1148       };
1149 
1150     try {
1151       SwingUtilities.invokeLater(new Runnable() {
1152           public void run() {
1153             JOptionPane pane = new JOptionPane("Attaching to process " + pid + ", please wait...", JOptionPane.INFORMATION_MESSAGE);
1154             pane.setOptions(new Object[] {});
1155             attachWaitDialog = pane.createInternalFrame(desktop, "Attaching to Process");
1156             attachWaitDialog.show();
1157           }
1158         });
1159 
1160       // FIXME: display exec'd debugger's output messages during this
1161       // lengthy call
1162       agent.attach(pid);
1163       if (agent.getDebugger().hasConsole()) {
1164         showDbgConsoleMenuItem.setEnabled(true);
1165       }
1166       attached = true;
1167       SwingUtilities.invokeLater(remover);
1168     }
1169     catch (DebuggerException e) {
1170       SwingUtilities.invokeLater(remover);
1171       final String errMsg = formatMessage(e.getMessage(), 80);
1172       SwingUtilities.invokeLater(new Runnable() {
1173           public void run() {
1174             setMenuItemsEnabled(attachMenuItems, true);
1175             JOptionPane.showInternalMessageDialog(desktop,
1176                                                   "Unable to connect to process ID " + pid + ":\n\n" + errMsg,
1177                                                   "Unable to Connect",
1178                                                   JOptionPane.WARNING_MESSAGE);
1179           }
1180         });
1181       agent.detach();
1182       return;
1183     }
1184 
1185     // OK, the VM should be available. Create the Threads dialog.
1186     showThreadsDialog();
1187   }
1188 
1189   /** NOTE we are in a different thread here than either the main
1190       thread or the Swing/AWT event handler thread, so we must be very
1191       careful when creating or removing widgets */
1192   private void attach(final String executablePath, final String corePath) {
1193     // Try to open this core file
1194     Runnable remover = new Runnable() {
1195           public void run() {
1196             attachWaitDialog.setVisible(false);
1197             desktop.remove(attachWaitDialog);
1198             attachWaitDialog = null;
1199           }
1200       };
1201 
1202     try {
1203       SwingUtilities.invokeLater(new Runnable() {
1204           public void run() {
1205             JOptionPane pane = new JOptionPane("Opening core file, please wait...", JOptionPane.INFORMATION_MESSAGE);
1206             pane.setOptions(new Object[] {});
1207             attachWaitDialog = pane.createInternalFrame(desktop, "Opening Core File");
1208             attachWaitDialog.show();
1209           }
1210         });
1211 
1212       // FIXME: display exec'd debugger's output messages during this
1213       // lengthy call
1214       agent.attach(executablePath, corePath);
1215       if (agent.getDebugger().hasConsole()) {
1216         showDbgConsoleMenuItem.setEnabled(true);
1217       }
1218       attached = true;
1219       SwingUtilities.invokeLater(remover);
1220     }
1221     catch (DebuggerException e) {
1222       SwingUtilities.invokeLater(remover);
1223       final String errMsg = formatMessage(e.getMessage(), 80);
1224       SwingUtilities.invokeLater(new Runnable() {
1225           public void run() {
1226             setMenuItemsEnabled(attachMenuItems, true);
1227             JOptionPane.showInternalMessageDialog(desktop,
1228                                                   "Unable to open core file\n" + corePath + ":\n\n" + errMsg,
1229                                                   "Unable to Open Core File",
1230                                                   JOptionPane.WARNING_MESSAGE);
1231           }
1232         });
1233       agent.detach();
1234       return;
1235     }
1236 
1237     // OK, the VM should be available. Create the Threads dialog.
1238     showThreadsDialog();
1239   }
1240 
1241   /** NOTE we are in a different thread here than either the main
1242       thread or the Swing/AWT event handler thread, so we must be very
1243       careful when creating or removing widgets */
1244   private void connect(final String remoteMachineName) {
1245     // Try to open this core file
1246     Runnable remover = new Runnable() {
1247           public void run() {
1248             attachWaitDialog.setVisible(false);
1249             desktop.remove(attachWaitDialog);
1250             attachWaitDialog = null;
1251           }
1252       };
1253 
1254     try {
1255       SwingUtilities.invokeLater(new Runnable() {
1256           public void run() {
1257             JOptionPane pane = new JOptionPane("Connecting to debug server, please wait...", JOptionPane.INFORMATION_MESSAGE);
1258             pane.setOptions(new Object[] {});
1259             attachWaitDialog = pane.createInternalFrame(desktop, "Connecting to Debug Server");
1260             attachWaitDialog.show();
1261           }
1262         });
1263 
1264       agent.attach(remoteMachineName);
1265       if (agent.getDebugger().hasConsole()) {
1266         showDbgConsoleMenuItem.setEnabled(true);
1267       }
1268       attached = true;
1269       SwingUtilities.invokeLater(remover);
1270     }
1271     catch (DebuggerException e) {
1272       SwingUtilities.invokeLater(remover);
1273       final String errMsg = formatMessage(e.getMessage(), 80);
1274       SwingUtilities.invokeLater(new Runnable() {
1275           public void run() {
1276             setMenuItemsEnabled(attachMenuItems, true);
1277             JOptionPane.showInternalMessageDialog(desktop,
1278                                                   "Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg,
1279                                                   "Unable to Connect",
1280                                                   JOptionPane.WARNING_MESSAGE);
1281           }
1282         });
1283       agent.detach();
1284       return;
1285     }
1286 
1287     // OK, the VM should be available. Create the Threads dialog.
1288     showThreadsDialog();
1289   }
1290 
1291   private void detachDebugger() {
1292     if (!attached) {
1293       return;
1294     }
1295     agent.detach();
1296     attached = false;
1297   }
1298 
1299   private void detach() {
1300     detachDebugger();
1301     attachWaitDialog = null;
1302     threadsFrame = null;
1303     consoleFrame = null;
1304     setMenuItemsEnabled(attachMenuItems, true);
1305     setMenuItemsEnabled(detachMenuItems, false);
1306     toolsMenu.setEnabled(false);
1307     showDbgConsoleMenuItem.setEnabled(false);
1308     // FIXME: is this sufficient, or will I have to do anything else
1309     // to the components to kill them off? What about WorkerThreads?
1310     desktop.removeAll();
1311     desktop.invalidate();
1312     desktop.validate();
1313     desktop.repaint();
1314   }
1315 
1316   /** NOTE that this is called from another thread than the main or
1317       Swing thread and we have to be careful about synchronization */
1318   private void showThreadsDialog() {
1319     SwingUtilities.invokeLater(new Runnable() {
1320         public void run() {
1321           threadsFrame = new JInternalFrame("Java Threads");
1322           threadsFrame.setResizable(true);
1323           threadsFrame.setIconifiable(true);
1324           JavaThreadsPanel threadsPanel = new JavaThreadsPanel();
1325           threadsPanel.addPanelListener(HSDB.this);
1326           threadsFrame.getContentPane().add(threadsPanel);
1327           threadsFrame.setSize(500, 300);
1328           threadsFrame.pack();
1329           desktop.add(threadsFrame);
1330           GraphicsUtilities.moveToInContainer(threadsFrame, 0.75f, 0.25f, 0, 20);
1331           threadsFrame.show();
1332           setMenuItemsEnabled(attachMenuItems, false);
1333           setMenuItemsEnabled(detachMenuItems, true);
1334           toolsMenu.setEnabled(true);
1335           VM.registerVMInitializedObserver(new Observer() {
1336               public void update(Observable o, Object data) {
1337                 computeRevPtrsMenuItem.setEnabled(true);
1338               }
1339             });
1340         }
1341       });
1342   }
1343 
1344   private void showObjectHistogram() {
1345     sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram();
1346     ObjectHistogramCleanupThunk cleanup =
1347       new ObjectHistogramCleanupThunk(histo);
1348     doHeapIteration("Object Histogram",
1349                     "Generating histogram...",
1350                     histo,
1351                     cleanup);
1352   }
1353 
1354   class ObjectHistogramCleanupThunk implements CleanupThunk {
1355     sun.jvm.hotspot.oops.ObjectHistogram histo;
1356 
1357     ObjectHistogramCleanupThunk(sun.jvm.hotspot.oops.ObjectHistogram histo) {
1358       this.histo = histo;
1359     }
1360 
1361     public void heapIterationComplete() {
1362       SwingUtilities.invokeLater(new Runnable() {
1363           public void run() {
1364             JInternalFrame histoFrame = new JInternalFrame("Object Histogram");
1365             histoFrame.setResizable(true);
1366             histoFrame.setClosable(true);
1367             histoFrame.setIconifiable(true);
1368             histoFrame.getContentPane().setLayout(new BorderLayout());
1369             ObjectHistogramPanel panel = new ObjectHistogramPanel(histo);
1370             panel.addPanelListener(HSDB.this);
1371             histoFrame.getContentPane().add(panel);
1372             desktop.add(histoFrame);
1373             GraphicsUtilities.reshapeToAspectRatio(histoFrame, 4.0f / 3.0f, 0.6f,
1374                                        histoFrame.getParent().getSize());
1375             GraphicsUtilities.centerInContainer(histoFrame);
1376             histoFrame.show();
1377           }
1378         });
1379     }
1380   }
1381 
1382   public void showObjectsOfType(Klass type) {
1383     FindObjectByType finder = new FindObjectByType(type);
1384     FindObjectByTypeCleanupThunk cleanup =
1385       new FindObjectByTypeCleanupThunk(finder);
1386     ByteArrayOutputStream bos = new ByteArrayOutputStream();
1387     type.printValueOn(new PrintStream(bos));
1388     String typeName = bos.toString();
1389     doHeapIteration("Show Objects Of Type",
1390                     "Finding instances of \"" + typeName + "\"",
1391                     finder,
1392                     cleanup);
1393   }
1394 
1395   class FindObjectByTypeCleanupThunk implements CleanupThunk {
1396     FindObjectByType finder;
1397 
1398     FindObjectByTypeCleanupThunk(FindObjectByType finder) {
1399       this.finder = finder;
1400     }
1401 
1402     public void heapIterationComplete() {
1403       SwingUtilities.invokeLater(new Runnable() {
1404           public void run() {
1405             JInternalFrame finderFrame = new JInternalFrame("Show Objects of Type");
1406             finderFrame.getContentPane().setLayout(new BorderLayout());
1407             finderFrame.setResizable(true);
1408             finderFrame.setClosable(true);
1409             finderFrame.setIconifiable(true);
1410             ObjectListPanel panel = new ObjectListPanel(finder.getResults(),
1411                                                         new HeapProgress("Reverse Pointers Analysis"));
1412             panel.addPanelListener(HSDB.this);
1413             finderFrame.getContentPane().add(panel);
1414             desktop.add(finderFrame);
1415             GraphicsUtilities.reshapeToAspectRatio(finderFrame, 4.0f / 3.0f, 0.6f,
1416                                        finderFrame.getParent().getSize());
1417             GraphicsUtilities.centerInContainer(finderFrame);
1418             finderFrame.show();
1419           }
1420         });
1421     }
1422   }
1423 
1424   private void showDebuggerConsole() {
1425     if (consoleFrame == null) {
1426       consoleFrame = new JInternalFrame("Debugger Console");
1427       consoleFrame.setResizable(true);
1428       consoleFrame.setClosable(true);
1429       consoleFrame.setIconifiable(true);
1430       consoleFrame.getContentPane().setLayout(new BorderLayout());
1431       consoleFrame.getContentPane().add(new DebuggerConsolePanel(agent.getDebugger()), BorderLayout.CENTER);
1432       GraphicsUtilities.reshapeToAspectRatio(consoleFrame, 5.0f, 0.9f, desktop.getSize());
1433     }
1434     if (consoleFrame.getParent() == null) {
1435       desktop.add(consoleFrame);
1436     }
1437     consoleFrame.setVisible(true);
1438     consoleFrame.show();
1439     consoleFrame.getContentPane().getComponent(0).requestFocus();
1440   }
1441 
1442   private void showConsole() {
1443       CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() {
1444               public HotSpotAgent getAgent() {
1445                   return agent;
1446               }
1447               public boolean isAttached() {
1448                   return attached;
1449               }
1450               public void attach(String pid) {
1451                   attach(pid);
1452               }
1453               public void attach(String java, String core) {
1454               }
1455               public void detach() {
1456                   detachDebugger();
1457               }
1458               public void reattach() {
1459                   if (attached) {
1460                       detachDebugger();
1461                   }
1462                   if (pidText != null) {
1463                       attach(pidText);
1464                   } else {
1465                       attach(execPath, coreFilename);
1466                   }
1467               }
1468           };
1469 
1470       showPanel("Command Line", new CommandProcessorPanel(new CommandProcessor(di, null, null, null)));
1471   }
1472 
1473   private void showFindByQueryPanel() {
1474     showPanel("Find Object by Query", new FindByQueryPanel());
1475   }
1476 
1477   private void showFindPanel() {
1478     showPanel("Find Pointer", new FindPanel());
1479   }
1480 
1481   private void showFindInHeapPanel() {
1482     showPanel("Find Address In Heap", new FindInHeapPanel());
1483   }
1484 
1485   private void showFindInCodeCachePanel() {
1486     showPanel("Find Address In Code Cache", new FindInCodeCachePanel());
1487   }
1488 
1489   private void showHeapParametersPanel() {
1490     showPanel("Heap Parameters", new HeapParametersPanel());
1491   }
1492 
1493   public void showThreadInfo(final JavaThread thread) {
1494     showPanel("Info for " + thread.getThreadName(), new ThreadInfoPanel(thread));
1495   }
1496 
1497   public void showJavaStackTrace(final JavaThread thread) {
1498     JavaStackTracePanel jstp = new JavaStackTracePanel();
1499     showPanel("Java stack trace for " + thread.getThreadName(), jstp);
1500     jstp.setJavaThread(thread);
1501   }
1502 
1503   private void showDeadlockDetectionPanel() {
1504     showPanel("Deadlock Detection", new DeadlockDetectionPanel());
1505   }
1506 
1507   private void showMonitorCacheDumpPanel() {
1508     showPanel("Monitor Cache Dump", new MonitorCacheDumpPanel());
1509   }
1510 
1511   public void showClassBrowser() {
1512     final JInternalFrame progressFrame = new JInternalFrame("Class Browser");
1513     progressFrame.setResizable(true);
1514     progressFrame.setClosable(true);
1515     progressFrame.setIconifiable(true);
1516     progressFrame.getContentPane().setLayout(new BorderLayout());
1517     final ProgressBarPanel bar = new ProgressBarPanel("Generating class list ..");
1518     bar.setIndeterminate(true);
1519     progressFrame.getContentPane().add(bar, BorderLayout.CENTER);
1520     desktop.add(progressFrame);
1521     progressFrame.pack();
1522     GraphicsUtilities.centerInContainer(progressFrame);
1523     progressFrame.show();
1524 
1525     workerThread.invokeLater(new Runnable() {
1526                                 public void run() {
1527                                    HTMLGenerator htmlGen = new HTMLGenerator();
1528                                    InstanceKlass[] klasses = SystemDictionaryHelper.getAllInstanceKlasses();
1529                                    final String htmlText = htmlGen.genHTMLForKlassNames(klasses);
1530                                    SwingUtilities.invokeLater(new Runnable() {
1531                                       public void run() {
1532                                          JInternalFrame cbFrame = new JInternalFrame("Class Browser");
1533                                          cbFrame.getContentPane().setLayout(new BorderLayout());
1534                                          cbFrame.setResizable(true);
1535                                          cbFrame.setClosable(true);
1536                                          cbFrame.setIconifiable(true);
1537                                          ClassBrowserPanel cbPanel = new ClassBrowserPanel();
1538                                          cbFrame.getContentPane().add(cbPanel, BorderLayout.CENTER);
1539                                          desktop.remove(progressFrame);
1540                                          desktop.repaint();
1541                                          desktop.add(cbFrame);
1542                                          GraphicsUtilities.reshapeToAspectRatio(cbFrame, 1.25f, 0.85f,
1543                                                                       cbFrame.getParent().getSize());
1544                                          cbFrame.show();
1545                                          cbPanel.setClassesText(htmlText);
1546                                       }
1547                                    });
1548                                 }
1549                              });
1550   }
1551 
1552   public void showCodeViewer() {
1553     showPanel("Code Viewer", new CodeViewerPanel(), 1.25f, 0.85f);
1554   }
1555 
1556   public void showCodeViewer(final Address address) {
1557     final CodeViewerPanel panel = new CodeViewerPanel();
1558     showPanel("Code Viewer", panel, 1.25f, 0.85f);
1559     SwingUtilities.invokeLater(new Runnable() {
1560         public void run() {
1561           panel.viewAddress(address);
1562         }
1563       });
1564 
1565   }
1566 
1567   public void showMemoryViewer() {
1568     showPanel("Memory Viewer", new MemoryViewer(agent.getDebugger(), agent.getTypeDataBase().getAddressSize() == 8));
1569   }
1570 
1571   public void showCommandLineFlags() {
1572     showPanel("Command Line Flags", new VMFlagsPanel());
1573   }
1574 
1575   public void showVMVersion() {
1576     showPanel("VM Version Info", new VMVersionInfoPanel());
1577   }
1578 
1579   public void showSystemProperties() {
1580     showPanel("System Properties", new SysPropsPanel());
1581   }
1582 
1583   private void showPanel(String name, JPanel panel) {
1584     showPanel(name, panel, 5.0f / 3.0f, 0.4f);
1585   }
1586 
1587   private void showPanel(String name, JPanel panel, float aspectRatio, float fillRatio) {
1588     JInternalFrame frame = new JInternalFrame(name);
1589     frame.getContentPane().setLayout(new BorderLayout());
1590     frame.setResizable(true);
1591     frame.setClosable(true);
1592     frame.setIconifiable(true);
1593     frame.setMaximizable(true);
1594     frame.getContentPane().add(panel, BorderLayout.CENTER);
1595     desktop.add(frame);
1596     GraphicsUtilities.reshapeToAspectRatio(frame, aspectRatio, fillRatio, frame.getParent().getSize());
1597     GraphicsUtilities.randomLocation(frame);
1598     frame.show();
1599     if (panel instanceof SAPanel) {
1600       ((SAPanel)panel).addPanelListener(this);
1601     }
1602   }
1603 
1604   //--------------------------------------------------------------------------------
1605   // Framework for heap iteration with progress bar
1606   //
1607 
1608   interface CleanupThunk {
1609     public void heapIterationComplete();
1610   }
1611 
1612   class HeapProgress implements HeapProgressThunk {
1613     private JInternalFrame frame;
1614     private ProgressBarPanel bar;
1615     private String windowTitle;
1616     private String progressBarTitle;
1617     private CleanupThunk cleanup;
1618 
1619     HeapProgress(String windowTitle) {
1620       this(windowTitle, "Percentage of heap visited", null);
1621     }
1622 
1623     HeapProgress(String windowTitle, String progressBarTitle) {
1624       this(windowTitle, progressBarTitle, null);
1625     }
1626 
1627     HeapProgress(String windowTitle, String progressBarTitle, CleanupThunk cleanup) {
1628       this.windowTitle = windowTitle;
1629       this.progressBarTitle = progressBarTitle;
1630       this.cleanup = cleanup;
1631     }
1632 
1633     public void heapIterationFractionUpdate(final double fractionOfHeapVisited) {
1634       if (frame == null) {
1635         SwingUtilities.invokeLater(new Runnable() {
1636             public void run() {
1637               frame = new JInternalFrame(windowTitle);
1638               frame.setResizable(true);
1639               frame.setIconifiable(true);
1640               frame.getContentPane().setLayout(new BorderLayout());
1641               bar = new ProgressBarPanel(progressBarTitle);
1642               frame.getContentPane().add(bar, BorderLayout.CENTER);
1643               desktop.add(frame);
1644               frame.pack();
1645               GraphicsUtilities.constrainToSize(frame, frame.getParent().getSize());
1646               GraphicsUtilities.centerInContainer(frame);
1647               frame.show();
1648             }
1649           });
1650       }
1651 
1652       SwingUtilities.invokeLater(new Runnable() {
1653           public void run() {
1654             bar.setValue(fractionOfHeapVisited);
1655           }
1656         });
1657     }
1658 
1659     public void heapIterationComplete() {
1660       SwingUtilities.invokeLater(new Runnable() {
1661           public void run() {
1662             desktop.remove(frame);
1663             desktop.repaint();
1664             if (VM.getVM().getRevPtrs() != null) {
1665               // Ended up computing reverse pointers as a side-effect
1666               computeRevPtrsMenuItem.setEnabled(false);
1667             }
1668           }
1669         });
1670 
1671       if (cleanup != null) {
1672         cleanup.heapIterationComplete();
1673       }
1674     }
1675   }
1676 
1677   class VisitHeap implements Runnable {
1678     HeapVisitor visitor;
1679 
1680     VisitHeap(HeapVisitor visitor) {
1681       this.visitor = visitor;
1682     }
1683 
1684     public void run() {
1685       VM.getVM().getObjectHeap().iterate(visitor);
1686     }
1687   }
1688 
1689   private void doHeapIteration(String frameTitle,
1690                                String progressBarText,
1691                                HeapVisitor visitor,
1692                                CleanupThunk cleanup) {
1693     sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram();
1694     HeapProgress progress = new HeapProgress(frameTitle,
1695                                              progressBarText,
1696                                              cleanup);
1697     HeapVisitor progVisitor = new ProgressiveHeapVisitor(visitor, progress);
1698     workerThread.invokeLater(new VisitHeap(progVisitor));
1699   }
1700 
1701   //--------------------------------------------------------------------------------
1702   // Stack trace helper
1703   //
1704 
1705   private static JavaVFrame getLastJavaVFrame(JavaThread cur) {
1706     RegisterMap regMap = cur.newRegisterMap(true);
1707     sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess();
1708     if (f == null) return null;
1709     boolean imprecise = true;
1710     if (f.isInterpretedFrame() && !f.isInterpretedFrameValid()) {
1711       System.err.println("Correcting for invalid interpreter frame");
1712       f = f.sender(regMap);
1713       imprecise = false;
1714     }
1715     VFrame vf = VFrame.newVFrame(f, regMap, cur, true, imprecise);
1716     if (vf == null) {
1717       System.err.println(" (Unable to create vframe for topmost frame guess)");
1718       return null;
1719     }
1720     if (vf.isJavaFrame()) {
1721       return (JavaVFrame) vf;
1722     }
1723     return (JavaVFrame) vf.javaSender();
1724   }
1725 
1726   // Internal routine for debugging
1727   private static void dumpStack(JavaThread cur) {
1728     RegisterMap regMap = cur.newRegisterMap(true);
1729     sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess();
1730     PrintStream tty = System.err;
1731     while (f != null) {
1732       tty.print("Found ");
1733            if (f.isInterpretedFrame()) { tty.print("interpreted"); }
1734       else if (f.isCompiledFrame())    { tty.print("compiled"); }
1735       else if (f.isEntryFrame())       { tty.print("entry"); }
1736       else if (f.isNativeFrame())      { tty.print("native"); }
1737       else if (f.isGlueFrame())        { tty.print("glue"); }
1738       else { tty.print("external"); }
1739       tty.print(" frame with PC = " + f.getPC() + ", SP = " + f.getSP() + ", FP = " + f.getFP());
1740       if (f.isSignalHandlerFrameDbg()) {
1741         tty.print(" (SIGNAL HANDLER)");
1742       }
1743       tty.println();
1744 
1745       if (!f.isFirstFrame()) {
1746         f = f.sender(regMap);
1747       } else {
1748         f = null;
1749       }
1750     }
1751   }
1752 
1753   //--------------------------------------------------------------------------------
1754   // Component utilities
1755   //
1756 
1757   private static JMenuItem createMenuItem(String name, ActionListener l) {
1758     JMenuItem item = new JMenuItem(name);
1759     item.addActionListener(l);
1760     return item;
1761   }
1762 
1763   /** Punctuates the given string with \n's where necessary to not
1764       exceed the given number of characters per line. Strips
1765       extraneous whitespace. */
1766   private String formatMessage(String message, int charsPerLine) {
1767     StringBuffer buf = new StringBuffer(message.length());
1768     StringTokenizer tokenizer = new StringTokenizer(message);
1769     int curLineLength = 0;
1770     while (tokenizer.hasMoreTokens()) {
1771       String tok = tokenizer.nextToken();
1772       if (curLineLength + tok.length() > charsPerLine) {
1773         buf.append('\n');
1774         curLineLength = 0;
1775       } else {
1776         if (curLineLength != 0) {
1777           buf.append(' ');
1778           ++curLineLength;
1779         }
1780       }
1781       buf.append(tok);
1782       curLineLength += tok.length();
1783     }
1784     return buf.toString();
1785   }
1786 
1787   private void setMenuItemsEnabled(java.util.List items, boolean enabled) {
1788     for (Iterator iter = items.iterator(); iter.hasNext(); ) {
1789       ((JMenuItem) iter.next()).setEnabled(enabled);
1790     }
1791   }
1792 }