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