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