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