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