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