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