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