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