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 VM.getVM().getCPU().equals("aarch64")) { 990 // For C2, which has null frame pointers on x86/amd64 991 CodeBlob cb = VM.getVM().getCodeCache().findBlob(curFrame.getPC()); 992 Address sp = curFrame.getSP(); 993 if (Assert.ASSERTS_ENABLED) { 994 Assert.that(cb.getFrameSize() > 0, "CodeBlob must have non-zero frame size"); 995 } 996 annoPanel.addAnnotation(new Annotation(sp, 997 sp.addOffsetTo(cb.getFrameSize()), 998 anno)); 999 } else { 1000 Assert.that(VM.getVM().getCPU().equals("ia64"), "only ia64 should reach here"); 1001 } 1002 } 1003 1004 // Add interpreter frame annotations 1005 if (curFrame.isInterpretedFrame()) { 1006 annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameExpressionStack(), 1007 curFrame.addressOfInterpreterFrameTOS(), 1008 "Interpreter expression stack")); 1009 Address monBegin = curFrame.interpreterFrameMonitorBegin().address(); 1010 Address monEnd = curFrame.interpreterFrameMonitorEnd().address(); 1011 if (!monBegin.equals(monEnd)) { 1012 annoPanel.addAnnotation(new Annotation(monBegin, monEnd, 1013 "BasicObjectLocks")); 1014 } 1015 if (interpreterFrameMethod != null) { 1016 // The offset is just to get the right stack slots highlighted in the output 1017 int offset = 1; 1018 annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameLocal(offset), 1019 curFrame.addressOfInterpreterFrameLocal((int) interpreterFrameMethod.getMaxLocals() + offset), 1020 "Interpreter locals area for frame with SP = " + curFrame.getSP())); 1021 } 1022 String methodAnno = "Interpreter frame Method*"; 1023 if (interpreterFrameMethod == null) { 1024 methodAnno += " (BAD OOP)"; 1025 } 1026 Address a = curFrame.addressOfInterpreterFrameMethod(); 1027 annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), methodAnno)); 1028 a = curFrame.addressOfInterpreterFrameCPCache(); 1029 annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache")); 1030 } 1031 1032 RegisterMap rm = (RegisterMap) vf.getRegisterMap().clone(); 1033 if (!shouldSkipOopMaps) { 1034 try { 1035 curFrame.oopsDo(new AddressVisitor() { 1036 public void visitAddress(Address addr) { 1037 if (Assert.ASSERTS_ENABLED) { 1038 Assert.that(addr.andWithMask(VM.getVM().getAddressSize() - 1) == null, 1039 "Address " + addr + "should have been aligned"); 1040 } 1041 OopHandle handle = addr.getOopHandleAt(0); 1042 addAnnotation(addr, handle); 1043 } 1044 1045 public void visitCompOopAddress(Address addr) { 1046 if (Assert.ASSERTS_ENABLED) { 1047 Assert.that(addr.andWithMask(VM.getVM().getAddressSize() - 1) == null, 1048 "Address " + addr + "should have been aligned"); 1049 } 1050 OopHandle handle = addr.getCompOopHandleAt(0); 1051 addAnnotation(addr, handle); 1052 } 1053 1054 public void addAnnotation(Address addr, OopHandle handle) { 1055 // Check contents 1056 String anno = "null oop"; 1057 if (handle != null) { 1058 // Find location 1059 CollectedHeap collHeap = VM.getVM().getUniverse().heap(); 1060 boolean bad = true; 1061 anno = "BAD OOP"; 1062 if (collHeap instanceof GenCollectedHeap) { 1063 GenCollectedHeap heap = (GenCollectedHeap) collHeap; 1064 for (int i = 0; i < heap.nGens(); i++) { 1065 if (heap.getGen(i).isIn(handle)) { 1066 if (i == 0) { 1067 anno = "NewGen "; 1068 } else if (i == 1) { 1069 anno = "OldGen "; 1070 } else { 1071 anno = "Gen " + i + " "; 1072 } 1073 bad = false; 1074 break; 1075 } 1076 } 1077 1078 } else if (collHeap instanceof ParallelScavengeHeap) { 1079 ParallelScavengeHeap heap = (ParallelScavengeHeap) collHeap; 1080 if (heap.youngGen().isIn(handle)) { 1081 anno = "PSYoungGen "; 1082 bad = false; 1083 } else if (heap.oldGen().isIn(handle)) { 1084 anno = "PSOldGen "; 1085 bad = false; 1086 } 1087 } else { 1088 // Optimistically assume the oop isn't bad 1089 anno = "[Unknown generation] "; 1090 bad = false; 1091 } 1092 1093 if (!bad) { 1094 try { 1095 Oop oop = VM.getVM().getObjectHeap().newOop(handle); 1096 if (oop instanceof Instance) { 1097 // Java-level objects always have workable names 1098 anno = anno + oop.getKlass().getName().asString(); 1099 } else { 1100 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 1101 Oop.printOopValueOn(oop, new PrintStream(bos)); 1102 anno = anno + bos.toString(); 1103 } 1104 } 1105 catch (AddressException e) { 1106 anno += "CORRUPT OOP"; 1107 } 1108 catch (NullPointerException e) { 1109 anno += "CORRUPT OOP (null pointer)"; 1110 } 1111 } 1112 } 1113 1114 annoPanel.addAnnotation(new Annotation(addr, addr.addOffsetTo(addressSize), anno)); 1115 } 1116 }, rm); 1117 } catch (Exception e) { 1118 System.err.println("Error while performing oopsDo for frame " + curFrame); 1119 e.printStackTrace(); 1120 } 1121 } 1122 1123 vf = nextVFrame; 1124 } 1125 1126 // This used to paint as we walked the frames. This caused the display to be refreshed 1127 // enough to be annoying on remote displays. It also would cause the annotations to 1128 // be displayed in varying order which caused some annotations to overwrite others 1129 // depending on the races between painting and adding annotations. This latter problem 1130 // still exists to some degree but moving this code here definitely seems to reduce it 1131 annoPanel.makeVisible(startAddr); 1132 annoPanel.repaint(); 1133 } 1134 }); 1135 } 1136 1137 // Attach to existing JVMDebugger, which should be already attached to a core/process. 1138 private void attach(JVMDebugger d) { 1139 attached = true; 1140 showThreadsDialog(); 1141 } 1142 1143 /** NOTE we are in a different thread here than either the main 1144 thread or the Swing/AWT event handler thread, so we must be very 1145 careful when creating or removing widgets */ 1146 private void attach(String pidText) { 1147 try { 1148 this.pidText = pidText; 1149 pid = Integer.parseInt(pidText); 1150 } 1151 catch (NumberFormatException e) { 1152 SwingUtilities.invokeLater(new Runnable() { 1153 public void run() { 1154 setMenuItemsEnabled(attachMenuItems, true); 1155 JOptionPane.showInternalMessageDialog(desktop, 1156 "Unable to parse process ID \"" + HSDB.this.pidText + "\".\nPlease enter a number.", 1157 "Parse error", 1158 JOptionPane.WARNING_MESSAGE); 1159 } 1160 }); 1161 return; 1162 } 1163 1164 // Try to attach to this process 1165 Runnable remover = new Runnable() { 1166 public void run() { 1167 attachWaitDialog.setVisible(false); 1168 desktop.remove(attachWaitDialog); 1169 attachWaitDialog = null; 1170 } 1171 }; 1172 1173 try { 1174 SwingUtilities.invokeLater(new Runnable() { 1175 public void run() { 1176 JOptionPane pane = new JOptionPane("Attaching to process " + pid + ", please wait...", JOptionPane.INFORMATION_MESSAGE); 1177 pane.setOptions(new Object[] {}); 1178 attachWaitDialog = pane.createInternalFrame(desktop, "Attaching to Process"); 1179 attachWaitDialog.show(); 1180 } 1181 }); 1182 1183 // FIXME: display exec'd debugger's output messages during this 1184 // lengthy call 1185 agent.attach(pid); 1186 if (agent.getDebugger().hasConsole()) { 1187 showDbgConsoleMenuItem.setEnabled(true); 1188 } 1189 attached = true; 1190 SwingUtilities.invokeLater(remover); 1191 } 1192 catch (DebuggerException e) { 1193 SwingUtilities.invokeLater(remover); 1194 final String errMsg = formatMessage(e.getMessage(), 80); 1195 SwingUtilities.invokeLater(new Runnable() { 1196 public void run() { 1197 setMenuItemsEnabled(attachMenuItems, true); 1198 JOptionPane.showInternalMessageDialog(desktop, 1199 "Unable to connect to process ID " + pid + ":\n\n" + errMsg, 1200 "Unable to Connect", 1201 JOptionPane.WARNING_MESSAGE); 1202 } 1203 }); 1204 agent.detach(); 1205 return; 1206 } 1207 1208 // OK, the VM should be available. Create the Threads dialog. 1209 showThreadsDialog(); 1210 } 1211 1212 /** NOTE we are in a different thread here than either the main 1213 thread or the Swing/AWT event handler thread, so we must be very 1214 careful when creating or removing widgets */ 1215 private void attach(final String executablePath, final String corePath) { 1216 // Try to open this core file 1217 Runnable remover = new Runnable() { 1218 public void run() { 1219 attachWaitDialog.setVisible(false); 1220 desktop.remove(attachWaitDialog); 1221 attachWaitDialog = null; 1222 } 1223 }; 1224 1225 try { 1226 SwingUtilities.invokeLater(new Runnable() { 1227 public void run() { 1228 JOptionPane pane = new JOptionPane("Opening core file, please wait...", JOptionPane.INFORMATION_MESSAGE); 1229 pane.setOptions(new Object[] {}); 1230 attachWaitDialog = pane.createInternalFrame(desktop, "Opening Core File"); 1231 attachWaitDialog.show(); 1232 } 1233 }); 1234 1235 // FIXME: display exec'd debugger's output messages during this 1236 // lengthy call 1237 agent.attach(executablePath, corePath); 1238 if (agent.getDebugger().hasConsole()) { 1239 showDbgConsoleMenuItem.setEnabled(true); 1240 } 1241 attached = true; 1242 SwingUtilities.invokeLater(remover); 1243 } 1244 catch (DebuggerException e) { 1245 SwingUtilities.invokeLater(remover); 1246 final String errMsg = formatMessage(e.getMessage(), 80); 1247 SwingUtilities.invokeLater(new Runnable() { 1248 public void run() { 1249 setMenuItemsEnabled(attachMenuItems, true); 1250 JOptionPane.showInternalMessageDialog(desktop, 1251 "Unable to open core file\n" + corePath + ":\n\n" + errMsg, 1252 "Unable to Open Core File", 1253 JOptionPane.WARNING_MESSAGE); 1254 } 1255 }); 1256 agent.detach(); 1257 return; 1258 } 1259 1260 // OK, the VM should be available. Create the Threads dialog. 1261 showThreadsDialog(); 1262 } 1263 1264 /** NOTE we are in a different thread here than either the main 1265 thread or the Swing/AWT event handler thread, so we must be very 1266 careful when creating or removing widgets */ 1267 private void connect(final String remoteMachineName) { 1268 // Try to open this core file 1269 Runnable remover = new Runnable() { 1270 public void run() { 1271 attachWaitDialog.setVisible(false); 1272 desktop.remove(attachWaitDialog); 1273 attachWaitDialog = null; 1274 } 1275 }; 1276 1277 try { 1278 SwingUtilities.invokeLater(new Runnable() { 1279 public void run() { 1280 JOptionPane pane = new JOptionPane("Connecting to debug server, please wait...", JOptionPane.INFORMATION_MESSAGE); 1281 pane.setOptions(new Object[] {}); 1282 attachWaitDialog = pane.createInternalFrame(desktop, "Connecting to Debug Server"); 1283 attachWaitDialog.show(); 1284 } 1285 }); 1286 1287 agent.attach(remoteMachineName); 1288 if (agent.getDebugger().hasConsole()) { 1289 showDbgConsoleMenuItem.setEnabled(true); 1290 } 1291 attached = true; 1292 SwingUtilities.invokeLater(remover); 1293 } 1294 catch (DebuggerException e) { 1295 SwingUtilities.invokeLater(remover); 1296 final String errMsg = formatMessage(e.getMessage(), 80); 1297 SwingUtilities.invokeLater(new Runnable() { 1298 public void run() { 1299 setMenuItemsEnabled(attachMenuItems, true); 1300 JOptionPane.showInternalMessageDialog(desktop, 1301 "Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg, 1302 "Unable to Connect", 1303 JOptionPane.WARNING_MESSAGE); 1304 } 1305 }); 1306 agent.detach(); 1307 return; 1308 } 1309 1310 // OK, the VM should be available. Create the Threads dialog. 1311 showThreadsDialog(); 1312 } 1313 1314 private void detachDebugger() { 1315 if (!attached) { 1316 return; 1317 } 1318 agent.detach(); 1319 attached = false; 1320 } 1321 1322 private void detach() { 1323 detachDebugger(); 1324 attachWaitDialog = null; 1325 threadsFrame = null; 1326 consoleFrame = null; 1327 setMenuItemsEnabled(attachMenuItems, true); 1328 setMenuItemsEnabled(detachMenuItems, false); 1329 toolsMenu.setEnabled(false); 1330 showDbgConsoleMenuItem.setEnabled(false); 1331 // FIXME: is this sufficient, or will I have to do anything else 1332 // to the components to kill them off? What about WorkerThreads? 1333 desktop.removeAll(); 1334 desktop.invalidate(); 1335 desktop.validate(); 1336 desktop.repaint(); 1337 } 1338 1339 /** NOTE that this is called from another thread than the main or 1340 Swing thread and we have to be careful about synchronization */ 1341 private void showThreadsDialog() { 1342 SwingUtilities.invokeLater(new Runnable() { 1343 public void run() { 1344 threadsFrame = new JInternalFrame("Java Threads"); 1345 threadsFrame.setResizable(true); 1346 threadsFrame.setIconifiable(true); 1347 JavaThreadsPanel threadsPanel = new JavaThreadsPanel(); 1348 threadsPanel.addPanelListener(HSDB.this); 1349 threadsFrame.getContentPane().add(threadsPanel); 1350 threadsFrame.setSize(500, 300); 1351 threadsFrame.pack(); 1352 desktop.add(threadsFrame); 1353 GraphicsUtilities.moveToInContainer(threadsFrame, 0.75f, 0.25f, 0, 20); 1354 threadsFrame.show(); 1355 setMenuItemsEnabled(attachMenuItems, false); 1356 setMenuItemsEnabled(detachMenuItems, true); 1357 toolsMenu.setEnabled(true); 1358 VM.registerVMInitializedObserver(new Observer() { 1359 public void update(Observable o, Object data) { 1360 computeRevPtrsMenuItem.setEnabled(true); 1361 } 1362 }); 1363 } 1364 }); 1365 } 1366 1367 private void showObjectHistogram() { 1368 sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram(); 1369 ObjectHistogramCleanupThunk cleanup = 1370 new ObjectHistogramCleanupThunk(histo); 1371 doHeapIteration("Object Histogram", 1372 "Generating histogram...", 1373 histo, 1374 cleanup); 1375 } 1376 1377 class ObjectHistogramCleanupThunk implements CleanupThunk { 1378 sun.jvm.hotspot.oops.ObjectHistogram histo; 1379 1380 ObjectHistogramCleanupThunk(sun.jvm.hotspot.oops.ObjectHistogram histo) { 1381 this.histo = histo; 1382 } 1383 1384 public void heapIterationComplete() { 1385 SwingUtilities.invokeLater(new Runnable() { 1386 public void run() { 1387 JInternalFrame histoFrame = new JInternalFrame("Object Histogram"); 1388 histoFrame.setResizable(true); 1389 histoFrame.setClosable(true); 1390 histoFrame.setIconifiable(true); 1391 histoFrame.getContentPane().setLayout(new BorderLayout()); 1392 ObjectHistogramPanel panel = new ObjectHistogramPanel(histo); 1393 panel.addPanelListener(HSDB.this); 1394 histoFrame.getContentPane().add(panel); 1395 desktop.add(histoFrame); 1396 GraphicsUtilities.reshapeToAspectRatio(histoFrame, 4.0f / 3.0f, 0.6f, 1397 histoFrame.getParent().getSize()); 1398 GraphicsUtilities.centerInContainer(histoFrame); 1399 histoFrame.show(); 1400 } 1401 }); 1402 } 1403 } 1404 1405 public void showObjectsOfType(Klass type) { 1406 FindObjectByType finder = new FindObjectByType(type); 1407 FindObjectByTypeCleanupThunk cleanup = 1408 new FindObjectByTypeCleanupThunk(finder); 1409 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 1410 type.printValueOn(new PrintStream(bos)); 1411 String typeName = bos.toString(); 1412 doHeapIteration("Show Objects Of Type", 1413 "Finding instances of \"" + typeName + "\"", 1414 finder, 1415 cleanup); 1416 } 1417 1418 class FindObjectByTypeCleanupThunk implements CleanupThunk { 1419 FindObjectByType finder; 1420 1421 FindObjectByTypeCleanupThunk(FindObjectByType finder) { 1422 this.finder = finder; 1423 } 1424 1425 public void heapIterationComplete() { 1426 SwingUtilities.invokeLater(new Runnable() { 1427 public void run() { 1428 JInternalFrame finderFrame = new JInternalFrame("Show Objects of Type"); 1429 finderFrame.getContentPane().setLayout(new BorderLayout()); 1430 finderFrame.setResizable(true); 1431 finderFrame.setClosable(true); 1432 finderFrame.setIconifiable(true); 1433 ObjectListPanel panel = new ObjectListPanel(finder.getResults(), 1434 new HeapProgress("Reverse Pointers Analysis")); 1435 panel.addPanelListener(HSDB.this); 1436 finderFrame.getContentPane().add(panel); 1437 desktop.add(finderFrame); 1438 GraphicsUtilities.reshapeToAspectRatio(finderFrame, 4.0f / 3.0f, 0.6f, 1439 finderFrame.getParent().getSize()); 1440 GraphicsUtilities.centerInContainer(finderFrame); 1441 finderFrame.show(); 1442 } 1443 }); 1444 } 1445 } 1446 1447 private void showDebuggerConsole() { 1448 if (consoleFrame == null) { 1449 consoleFrame = new JInternalFrame("Debugger Console"); 1450 consoleFrame.setResizable(true); 1451 consoleFrame.setClosable(true); 1452 consoleFrame.setIconifiable(true); 1453 consoleFrame.getContentPane().setLayout(new BorderLayout()); 1454 consoleFrame.getContentPane().add(new DebuggerConsolePanel(agent.getDebugger()), BorderLayout.CENTER); 1455 GraphicsUtilities.reshapeToAspectRatio(consoleFrame, 5.0f, 0.9f, desktop.getSize()); 1456 } 1457 if (consoleFrame.getParent() == null) { 1458 desktop.add(consoleFrame); 1459 } 1460 consoleFrame.setVisible(true); 1461 consoleFrame.show(); 1462 consoleFrame.getContentPane().getComponent(0).requestFocus(); 1463 } 1464 1465 private void showConsole() { 1466 CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() { 1467 public HotSpotAgent getAgent() { 1468 return agent; 1469 } 1470 public boolean isAttached() { 1471 return attached; 1472 } 1473 public void attach(String pid) { 1474 attach(pid); 1475 } 1476 public void attach(String java, String core) { 1477 } 1478 public void detach() { 1479 detachDebugger(); 1480 } 1481 public void reattach() { 1482 if (attached) { 1483 detachDebugger(); 1484 } 1485 if (pidText != null) { 1486 attach(pidText); 1487 } else { 1488 attach(execPath, coreFilename); 1489 } 1490 } 1491 }; 1492 1493 showPanel("Command Line", new CommandProcessorPanel(new CommandProcessor(di, null, null, null))); 1494 } 1495 1496 private void showFindByQueryPanel() { 1497 showPanel("Find Object by Query", new FindByQueryPanel()); 1498 } 1499 1500 private void showFindPanel() { 1501 showPanel("Find Pointer", new FindPanel()); 1502 } 1503 1504 private void showFindInHeapPanel() { 1505 showPanel("Find Address In Heap", new FindInHeapPanel()); 1506 } 1507 1508 private void showFindInCodeCachePanel() { 1509 showPanel("Find Address In Code Cache", new FindInCodeCachePanel()); 1510 } 1511 1512 private void showHeapParametersPanel() { 1513 showPanel("Heap Parameters", new HeapParametersPanel()); 1514 } 1515 1516 public void showThreadInfo(final JavaThread thread) { 1517 showPanel("Info for " + thread.getThreadName(), new ThreadInfoPanel(thread)); 1518 } 1519 1520 public void showJavaStackTrace(final JavaThread thread) { 1521 JavaStackTracePanel jstp = new JavaStackTracePanel(); 1522 showPanel("Java stack trace for " + thread.getThreadName(), jstp); 1523 jstp.setJavaThread(thread); 1524 } 1525 1526 private void showDeadlockDetectionPanel() { 1527 showPanel("Deadlock Detection", new DeadlockDetectionPanel()); 1528 } 1529 1530 private void showMonitorCacheDumpPanel() { 1531 showPanel("Monitor Cache Dump", new MonitorCacheDumpPanel()); 1532 } 1533 1534 public void showClassBrowser() { 1535 final JInternalFrame progressFrame = new JInternalFrame("Class Browser"); 1536 progressFrame.setResizable(true); 1537 progressFrame.setClosable(true); 1538 progressFrame.setIconifiable(true); 1539 progressFrame.getContentPane().setLayout(new BorderLayout()); 1540 final ProgressBarPanel bar = new ProgressBarPanel("Generating class list .."); 1541 bar.setIndeterminate(true); 1542 progressFrame.getContentPane().add(bar, BorderLayout.CENTER); 1543 desktop.add(progressFrame); 1544 progressFrame.pack(); 1545 GraphicsUtilities.centerInContainer(progressFrame); 1546 progressFrame.show(); 1547 1548 workerThread.invokeLater(new Runnable() { 1549 public void run() { 1550 HTMLGenerator htmlGen = new HTMLGenerator(); 1551 InstanceKlass[] klasses = SystemDictionaryHelper.getAllInstanceKlasses(); 1552 final String htmlText = htmlGen.genHTMLForKlassNames(klasses); 1553 SwingUtilities.invokeLater(new Runnable() { 1554 public void run() { 1555 JInternalFrame cbFrame = new JInternalFrame("Class Browser"); 1556 cbFrame.getContentPane().setLayout(new BorderLayout()); 1557 cbFrame.setResizable(true); 1558 cbFrame.setClosable(true); 1559 cbFrame.setIconifiable(true); 1560 ClassBrowserPanel cbPanel = new ClassBrowserPanel(); 1561 cbFrame.getContentPane().add(cbPanel, BorderLayout.CENTER); 1562 desktop.remove(progressFrame); 1563 desktop.repaint(); 1564 desktop.add(cbFrame); 1565 GraphicsUtilities.reshapeToAspectRatio(cbFrame, 1.25f, 0.85f, 1566 cbFrame.getParent().getSize()); 1567 cbFrame.show(); 1568 cbPanel.setClassesText(htmlText); 1569 } 1570 }); 1571 } 1572 }); 1573 } 1574 1575 public void showCodeViewer() { 1576 showPanel("Code Viewer", new CodeViewerPanel(), 1.25f, 0.85f); 1577 } 1578 1579 public void showCodeViewer(final Address address) { 1580 final CodeViewerPanel panel = new CodeViewerPanel(); 1581 showPanel("Code Viewer", panel, 1.25f, 0.85f); 1582 SwingUtilities.invokeLater(new Runnable() { 1583 public void run() { 1584 panel.viewAddress(address); 1585 } 1586 }); 1587 1588 } 1589 1590 public void showMemoryViewer() { 1591 showPanel("Memory Viewer", new MemoryViewer(agent.getDebugger(), agent.getTypeDataBase().getAddressSize() == 8)); 1592 } 1593 1594 public void showCommandLineFlags() { 1595 showPanel("Command Line Flags", new VMFlagsPanel()); 1596 } 1597 1598 public void showVMVersion() { 1599 showPanel("VM Version Info", new VMVersionInfoPanel()); 1600 } 1601 1602 public void showSystemProperties() { 1603 showPanel("System Properties", new SysPropsPanel()); 1604 } 1605 1606 private void showPanel(String name, JPanel panel) { 1607 showPanel(name, panel, 5.0f / 3.0f, 0.4f); 1608 } 1609 1610 private void showPanel(String name, JPanel panel, float aspectRatio, float fillRatio) { 1611 JInternalFrame frame = new JInternalFrame(name); 1612 frame.getContentPane().setLayout(new BorderLayout()); 1613 frame.setResizable(true); 1614 frame.setClosable(true); 1615 frame.setIconifiable(true); 1616 frame.setMaximizable(true); 1617 frame.getContentPane().add(panel, BorderLayout.CENTER); 1618 desktop.add(frame); 1619 GraphicsUtilities.reshapeToAspectRatio(frame, aspectRatio, fillRatio, frame.getParent().getSize()); 1620 GraphicsUtilities.randomLocation(frame); 1621 frame.show(); 1622 if (panel instanceof SAPanel) { 1623 ((SAPanel)panel).addPanelListener(this); 1624 } 1625 } 1626 1627 //-------------------------------------------------------------------------------- 1628 // Framework for heap iteration with progress bar 1629 // 1630 1631 interface CleanupThunk { 1632 public void heapIterationComplete(); 1633 } 1634 1635 class HeapProgress implements HeapProgressThunk { 1636 private JInternalFrame frame; 1637 private ProgressBarPanel bar; 1638 private String windowTitle; 1639 private String progressBarTitle; 1640 private CleanupThunk cleanup; 1641 1642 HeapProgress(String windowTitle) { 1643 this(windowTitle, "Percentage of heap visited", null); 1644 } 1645 1646 HeapProgress(String windowTitle, String progressBarTitle) { 1647 this(windowTitle, progressBarTitle, null); 1648 } 1649 1650 HeapProgress(String windowTitle, String progressBarTitle, CleanupThunk cleanup) { 1651 this.windowTitle = windowTitle; 1652 this.progressBarTitle = progressBarTitle; 1653 this.cleanup = cleanup; 1654 } 1655 1656 public void heapIterationFractionUpdate(final double fractionOfHeapVisited) { 1657 if (frame == null) { 1658 SwingUtilities.invokeLater(new Runnable() { 1659 public void run() { 1660 frame = new JInternalFrame(windowTitle); 1661 frame.setResizable(true); 1662 frame.setIconifiable(true); 1663 frame.getContentPane().setLayout(new BorderLayout()); 1664 bar = new ProgressBarPanel(progressBarTitle); 1665 frame.getContentPane().add(bar, BorderLayout.CENTER); 1666 desktop.add(frame); 1667 frame.pack(); 1668 GraphicsUtilities.constrainToSize(frame, frame.getParent().getSize()); 1669 GraphicsUtilities.centerInContainer(frame); 1670 frame.show(); 1671 } 1672 }); 1673 } 1674 1675 SwingUtilities.invokeLater(new Runnable() { 1676 public void run() { 1677 bar.setValue(fractionOfHeapVisited); 1678 } 1679 }); 1680 } 1681 1682 public void heapIterationComplete() { 1683 SwingUtilities.invokeLater(new Runnable() { 1684 public void run() { 1685 desktop.remove(frame); 1686 desktop.repaint(); 1687 if (VM.getVM().getRevPtrs() != null) { 1688 // Ended up computing reverse pointers as a side-effect 1689 computeRevPtrsMenuItem.setEnabled(false); 1690 } 1691 } 1692 }); 1693 1694 if (cleanup != null) { 1695 cleanup.heapIterationComplete(); 1696 } 1697 } 1698 } 1699 1700 class VisitHeap implements Runnable { 1701 HeapVisitor visitor; 1702 1703 VisitHeap(HeapVisitor visitor) { 1704 this.visitor = visitor; 1705 } 1706 1707 public void run() { 1708 VM.getVM().getObjectHeap().iterate(visitor); 1709 } 1710 } 1711 1712 private void doHeapIteration(String frameTitle, 1713 String progressBarText, 1714 HeapVisitor visitor, 1715 CleanupThunk cleanup) { 1716 sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram(); 1717 HeapProgress progress = new HeapProgress(frameTitle, 1718 progressBarText, 1719 cleanup); 1720 HeapVisitor progVisitor = new ProgressiveHeapVisitor(visitor, progress); 1721 workerThread.invokeLater(new VisitHeap(progVisitor)); 1722 } 1723 1724 //-------------------------------------------------------------------------------- 1725 // Stack trace helper 1726 // 1727 1728 private static JavaVFrame getLastJavaVFrame(JavaThread cur) { 1729 RegisterMap regMap = cur.newRegisterMap(true); 1730 sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess(); 1731 if (f == null) return null; 1732 boolean imprecise = true; 1733 if (f.isInterpretedFrame() && !f.isInterpretedFrameValid()) { 1734 System.err.println("Correcting for invalid interpreter frame"); 1735 f = f.sender(regMap); 1736 imprecise = false; 1737 } 1738 VFrame vf = VFrame.newVFrame(f, regMap, cur, true, imprecise); 1739 if (vf == null) { 1740 System.err.println(" (Unable to create vframe for topmost frame guess)"); 1741 return null; 1742 } 1743 if (vf.isJavaFrame()) { 1744 return (JavaVFrame) vf; 1745 } 1746 return (JavaVFrame) vf.javaSender(); 1747 } 1748 1749 // Internal routine for debugging 1750 private static void dumpStack(JavaThread cur) { 1751 RegisterMap regMap = cur.newRegisterMap(true); 1752 sun.jvm.hotspot.runtime.Frame f = cur.getCurrentFrameGuess(); 1753 PrintStream tty = System.err; 1754 while (f != null) { 1755 tty.print("Found "); 1756 if (f.isInterpretedFrame()) { tty.print("interpreted"); } 1757 else if (f.isCompiledFrame()) { tty.print("compiled"); } 1758 else if (f.isEntryFrame()) { tty.print("entry"); } 1759 else if (f.isNativeFrame()) { tty.print("native"); } 1760 else if (f.isRuntimeFrame()) { tty.print("runtime"); } 1761 else { tty.print("external"); } 1762 tty.print(" frame with PC = " + f.getPC() + ", SP = " + f.getSP() + ", FP = " + f.getFP()); 1763 if (f.isSignalHandlerFrameDbg()) { 1764 tty.print(" (SIGNAL HANDLER)"); 1765 } 1766 tty.println(); 1767 1768 if (!f.isFirstFrame()) { 1769 f = f.sender(regMap); 1770 } else { 1771 f = null; 1772 } 1773 } 1774 } 1775 1776 //-------------------------------------------------------------------------------- 1777 // Component utilities 1778 // 1779 1780 private static JMenuItem createMenuItem(String name, ActionListener l) { 1781 JMenuItem item = new JMenuItem(name); 1782 item.addActionListener(l); 1783 return item; 1784 } 1785 1786 /** Punctuates the given string with \n's where necessary to not 1787 exceed the given number of characters per line. Strips 1788 extraneous whitespace. */ 1789 private String formatMessage(String message, int charsPerLine) { 1790 StringBuffer buf = new StringBuffer(message.length()); 1791 StringTokenizer tokenizer = new StringTokenizer(message); 1792 int curLineLength = 0; 1793 while (tokenizer.hasMoreTokens()) { 1794 String tok = tokenizer.nextToken(); 1795 if (curLineLength + tok.length() > charsPerLine) { 1796 buf.append('\n'); 1797 curLineLength = 0; 1798 } else { 1799 if (curLineLength != 0) { 1800 buf.append(' '); 1801 ++curLineLength; 1802 } 1803 } 1804 buf.append(tok); 1805 curLineLength += tok.length(); 1806 } 1807 return buf.toString(); 1808 } 1809 1810 private void setMenuItemsEnabled(java.util.List items, boolean enabled) { 1811 for (Iterator iter = items.iterator(); iter.hasNext(); ) { 1812 ((JMenuItem) iter.next()).setEnabled(enabled); 1813 } 1814 } 1815 }