1 /* 2 * Copyright 2001-2003 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.bugspot; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.io.*; 30 import java.net.*; 31 import java.util.*; 32 import javax.swing.*; 33 import javax.swing.filechooser.*; 34 import sun.jvm.hotspot.debugger.*; 35 import sun.jvm.hotspot.debugger.cdbg.*; 36 import sun.jvm.hotspot.debugger.posix.*; 37 import sun.jvm.hotspot.debugger.win32.*; 38 import sun.jvm.hotspot.livejvm.*; 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.utilities.*; 44 45 /** The BugSpot component. This is embeddable in an application by 46 virtue of its being a JComponent. It (currently) requires the use 47 of a menu bar which can be fetched via getMenuBar(). This is 48 intended ultimately to replace HSDB. */ 49 50 public class BugSpot extends JPanel { 51 public BugSpot() { 52 super(); 53 Runtime.getRuntime().addShutdownHook(new java.lang.Thread() { 54 public void run() { 55 detachDebugger(); 56 } 57 }); 58 } 59 60 /** Turn on or off MDI (Multiple Document Interface) mode. When MDI 61 is enabled, the BugSpot component contains a JDesktopPane and all 62 windows are JInternalFrames. When disabled, only the menu bar is 63 relevant. */ 64 public void setMDIMode(boolean onOrOff) { 65 mdiMode = onOrOff; 66 } 67 68 /** Indicates whether MDI mode is enabled. */ 69 public boolean getMDIMode() { 70 return mdiMode; 71 } 72 73 /** Build user interface widgets. This must be called before adding 74 the BugSpot component to its parent. */ 75 public void build() { 76 setLayout(new BorderLayout()); 77 78 menuBar = new JMenuBar(); 79 80 attachMenuItems = new java.util.ArrayList(); 81 detachMenuItems = new java.util.ArrayList(); 82 debugMenuItems = new java.util.ArrayList(); 83 suspendDebugMenuItems = new java.util.ArrayList(); 84 resumeDebugMenuItems = new java.util.ArrayList(); 85 86 // 87 // File menu 88 // 89 90 JMenu menu = createMenu("File", 'F', 0); 91 JMenuItem item; 92 item = createMenuItem("Open source file...", 93 new ActionListener() { 94 public void actionPerformed(ActionEvent e) { 95 openSourceFile(); 96 } 97 }, 98 KeyEvent.VK_O, InputEvent.CTRL_MASK, 99 'O', 0); 100 menu.add(item); 101 detachMenuItems.add(item); 102 103 menu.addSeparator(); 104 105 item = createMenuItem("Attach to process...", 106 new ActionListener() { 107 public void actionPerformed(ActionEvent e) { 108 showAttachDialog(); 109 } 110 }, 111 'A', 0); 112 menu.add(item); 113 attachMenuItems.add(item); 114 115 item = createMenuItem("Detach", 116 new ActionListener() { 117 public void actionPerformed(ActionEvent e) { 118 detach(); 119 } 120 }, 121 'D', 0); 122 menu.add(item); 123 detachMenuItems.add(item); 124 125 // Disable detach menu items at first 126 setMenuItemsEnabled(detachMenuItems, false); 127 128 menu.addSeparator(); 129 130 menu.add(createMenuItem("Exit", 131 new ActionListener() { 132 public void actionPerformed(ActionEvent e) { 133 detach(); 134 System.exit(0); 135 } 136 }, 137 'x', 1)); 138 139 menuBar.add(menu); 140 141 // 142 // Debug menu 143 // 144 145 debugMenu = createMenu("Debug", 'D', 0); 146 item = createMenuItem("Go", 147 new ActionListener() { 148 public void actionPerformed(ActionEvent e) { 149 if (!attached) return; 150 if (!isSuspended()) return; 151 resume(); 152 } 153 }, 154 KeyEvent.VK_F5, 0, 155 'G', 0); 156 debugMenu.add(item); 157 resumeDebugMenuItems.add(item); 158 159 item = createMenuItem("Break", 160 new ActionListener() { 161 public void actionPerformed(ActionEvent e) { 162 if (!attached) { 163 System.err.println("Not attached"); 164 return; 165 } 166 if (isSuspended()) { 167 System.err.println("Already suspended"); 168 return; 169 } 170 suspend(); 171 } 172 }, 173 'B', 0); 174 debugMenu.add(item); 175 suspendDebugMenuItems.add(item); 176 177 debugMenu.addSeparator(); 178 179 item = createMenuItem("Threads...", 180 new ActionListener() { 181 public void actionPerformed(ActionEvent e) { 182 showThreadsDialog(); 183 } 184 }, 185 'T', 0); 186 debugMenu.add(item); 187 debugMenuItems.add(item); 188 // FIXME: belongs under "View -> Debug Windows" 189 item = createMenuItem("Memory", 190 new ActionListener() { 191 public void actionPerformed(ActionEvent e) { 192 showMemoryDialog(); 193 } 194 }, 195 'M', 0); 196 debugMenu.add(item); 197 debugMenuItems.add(item); 198 199 debugMenu.setEnabled(false); 200 menuBar.add(debugMenu); 201 202 if (mdiMode) { 203 desktop = new JDesktopPane(); 204 add(desktop, BorderLayout.CENTER); 205 } 206 207 fixedWidthFont = GraphicsUtilities.lookupFont("Courier"); 208 209 debugEventTimer = new javax.swing.Timer(100, new ActionListener() { 210 public void actionPerformed(ActionEvent e) { 211 pollForDebugEvent(); 212 } 213 }); 214 } 215 216 public JMenuBar getMenuBar() { 217 return menuBar; 218 } 219 220 public void showAttachDialog() { 221 setMenuItemsEnabled(attachMenuItems, false); 222 final FrameWrapper attachDialog = newFrame("Attach to process"); 223 attachDialog.getContentPane().setLayout(new BorderLayout()); 224 attachDialog.setClosable(true); 225 attachDialog.setResizable(true); 226 227 JPanel panel = new JPanel(); 228 panel.setLayout(new BorderLayout()); 229 panel.setBorder(GraphicsUtilities.newBorder(5)); 230 attachDialog.setBackground(panel.getBackground()); 231 232 JPanel listPanel = new JPanel(); 233 listPanel.setLayout(new BorderLayout()); 234 final ProcessListPanel plist = new ProcessListPanel(getLocalDebugger()); 235 panel.add(plist, BorderLayout.CENTER); 236 JCheckBox check = new JCheckBox("Update list continuously"); 237 check.addItemListener(new ItemListener() { 238 public void itemStateChanged(ItemEvent e) { 239 if (e.getStateChange() == ItemEvent.SELECTED) { 240 plist.start(); 241 } else { 242 plist.stop(); 243 } 244 } 245 }); 246 listPanel.add(plist, BorderLayout.CENTER); 247 listPanel.add(check, BorderLayout.SOUTH); 248 panel.add(listPanel, BorderLayout.CENTER); 249 attachDialog.getContentPane().add(panel, BorderLayout.CENTER); 250 attachDialog.setClosingActionListener(new ActionListener() { 251 public void actionPerformed(ActionEvent e) { 252 plist.stop(); 253 setMenuItemsEnabled(attachMenuItems, true); 254 } 255 }); 256 257 ActionListener attacher = new ActionListener() { 258 public void actionPerformed(ActionEvent e) { 259 plist.stop(); 260 attachDialog.setVisible(false); 261 removeFrame(attachDialog); 262 ProcessInfo info = plist.getSelectedProcess(); 263 if (info != null) { 264 attach(info.getPid()); 265 } 266 } 267 }; 268 269 Box hbox = Box.createHorizontalBox(); 270 hbox.add(Box.createGlue()); 271 JButton button = new JButton("OK"); 272 button.addActionListener(attacher); 273 hbox.add(button); 274 hbox.add(Box.createHorizontalStrut(20)); 275 button = new JButton("Cancel"); 276 button.addActionListener(new ActionListener() { 277 public void actionPerformed(ActionEvent e) { 278 plist.stop(); 279 attachDialog.setVisible(false); 280 removeFrame(attachDialog); 281 setMenuItemsEnabled(attachMenuItems, true); 282 } 283 }); 284 hbox.add(button); 285 hbox.add(Box.createGlue()); 286 panel = new JPanel(); 287 panel.setBorder(GraphicsUtilities.newBorder(5)); 288 panel.add(hbox); 289 290 attachDialog.getContentPane().add(panel, BorderLayout.SOUTH); 291 292 addFrame(attachDialog); 293 attachDialog.pack(); 294 attachDialog.setSize(400, 300); 295 GraphicsUtilities.centerInContainer(attachDialog.getComponent(), 296 getParentDimension(attachDialog.getComponent())); 297 attachDialog.show(); 298 } 299 300 public void showThreadsDialog() { 301 final FrameWrapper threadsDialog = newFrame("Threads"); 302 threadsDialog.getContentPane().setLayout(new BorderLayout()); 303 threadsDialog.setClosable(true); 304 threadsDialog.setResizable(true); 305 306 ThreadListPanel threads = new ThreadListPanel(getCDebugger(), getAgent().isJavaMode()); 307 threads.addListener(new ThreadListPanel.Listener() { 308 public void setFocus(ThreadProxy thread, JavaThread jthread) { 309 setCurrentThread(thread); 310 // FIXME: print this to GUI, bring some windows to foreground 311 System.err.println("Focus changed to thread " + thread); 312 } 313 }); 314 threads.setBorder(GraphicsUtilities.newBorder(5)); 315 threadsDialog.getContentPane().add(threads); 316 addFrame(threadsDialog); 317 threadsDialog.pack(); 318 GraphicsUtilities.reshapeToAspectRatio(threadsDialog.getComponent(), 319 3.0f, 320 0.9f, 321 getParentDimension(threadsDialog.getComponent())); 322 GraphicsUtilities.centerInContainer(threadsDialog.getComponent(), 323 getParentDimension(threadsDialog.getComponent())); 324 threadsDialog.show(); 325 } 326 327 public void showMemoryDialog() { 328 final FrameWrapper memoryDialog = newFrame("Memory"); 329 memoryDialog.getContentPane().setLayout(new BorderLayout()); 330 memoryDialog.setClosable(true); 331 memoryDialog.setResizable(true); 332 333 memoryDialog.getContentPane().add(new MemoryViewer(getDebugger(), 334 (getDebugger().getMachineDescription().getAddressSize() == 8)), 335 BorderLayout.CENTER); 336 addFrame(memoryDialog); 337 memoryDialog.pack(); 338 GraphicsUtilities.reshapeToAspectRatio(memoryDialog.getComponent(), 339 1.0f, 340 0.7f, 341 getParentDimension(memoryDialog.getComponent())); 342 GraphicsUtilities.centerInContainer(memoryDialog.getComponent(), 343 getParentDimension(memoryDialog.getComponent())); 344 memoryDialog.show(); 345 } 346 347 /** Changes the editor factory this debugger uses to display source 348 code. Specified factory may be null, in which case the default 349 factory is used. */ 350 public void setEditorFactory(EditorFactory fact) { 351 if (fact != null) { 352 editorFact = fact; 353 } else { 354 editorFact = new DefaultEditorFactory(); 355 } 356 } 357 358 //---------------------------------------------------------------------- 359 // Internals only below this point 360 // 361 362 private WorkerThread workerThread; 363 private boolean mdiMode; 364 private JVMDebugger localDebugger; 365 private BugSpotAgent agent = new BugSpotAgent(); 366 private JMenuBar menuBar; 367 /** List <JMenuItem> */ 368 private java.util.List attachMenuItems; 369 private java.util.List detachMenuItems; 370 private java.util.List debugMenuItems; 371 private java.util.List suspendDebugMenuItems; 372 private java.util.List resumeDebugMenuItems; 373 private FrameWrapper stackFrame; 374 private VariablePanel localsPanel; 375 private StackTracePanel stackTracePanel; 376 private FrameWrapper registerFrame; 377 private RegisterPanel registerPanel; 378 // Used for mixed-language stack traces 379 private Map threadToJavaThreadMap; 380 381 private JMenu debugMenu; 382 383 // MDI mode only: desktop pane 384 private JDesktopPane desktop; 385 386 // Attach/detach state 387 private boolean attached; 388 389 // Suspension (combined Java/C++) state 390 private boolean suspended; 391 392 // Fixed-width font 393 private Font fixedWidthFont; 394 395 // Breakpoint setting 396 // Maps Strings to List/*<LineNumberInfo>*/ 397 private Map sourceFileToLineNumberInfoMap; 398 // Maps Strings (file names) to Sets of Integers (line numbers) 399 private Map fileToBreakpointMap; 400 401 // Debug events 402 private javax.swing.Timer debugEventTimer; 403 404 // Java debug events 405 private boolean javaEventPending; 406 407 static class BreakpointResult { 408 private boolean success; 409 private boolean set; 410 private int lineNo; 411 private String why; 412 413 /** For positive results */ 414 BreakpointResult(boolean success, boolean set, int lineNo) { 415 this(success, set, lineNo, null); 416 } 417 418 /** For negative results */ 419 BreakpointResult(boolean success, boolean set, int lineNo, String why) { 420 this.success = success; 421 this.set = set; 422 this.lineNo = lineNo; 423 this.why = why; 424 } 425 426 public boolean succeeded() { 427 return success; 428 } 429 430 public boolean set() { 431 return set; 432 } 433 434 /** Line at which the breakpoint was actually set; only valid if 435 succeeded() returns true */ 436 public int getLine() { 437 return lineNo; 438 } 439 440 public String getWhy() { 441 return why; 442 } 443 } 444 445 446 // Editors for source code. File name-to-Editor mapping. 447 private Map editors; 448 private EditorFactory editorFact = new DefaultEditorFactory(); 449 private EditorCommands editorComm = new EditorCommands() { 450 public void windowClosed(Editor editor) { 451 editors.remove(editor.getSourceFileName()); 452 } 453 454 public void toggleBreakpointAtLine(Editor editor, int lineNumber) { 455 // FIXME: handle "lazy" breakpoints where the source file has 456 // been opened with some other mechanism (File -> Open) and we 457 // don't have debug information pointing to that file yet 458 // FIXME: NOT FINISHED 459 460 BreakpointResult res = 461 handleBreakpointToggle(editor, lineNumber); 462 if (res.succeeded()) { 463 if (res.set()) { 464 editor.showBreakpointAtLine(res.getLine()); 465 } else { 466 editor.clearBreakpointAtLine(res.getLine()); 467 } 468 } else { 469 String why = res.getWhy(); 470 if (why == null) { 471 why = ""; 472 } else { 473 why = ": " + why; 474 } 475 showMessageDialog("Unable to toggle breakpoint" + why, 476 "Unable to toggle breakpoint", 477 JOptionPane.WARNING_MESSAGE); 478 } 479 } 480 }; 481 482 private void attach(final int pid) { 483 try { 484 getAgent().attach(pid); 485 setMenuItemsEnabled(detachMenuItems, true); 486 setMenuItemsEnabled(suspendDebugMenuItems, false); 487 setMenuItemsEnabled(resumeDebugMenuItems, true); 488 debugMenu.setEnabled(true); 489 attached = true; 490 suspended = true; 491 492 if (getAgent().isJavaMode()) { 493 System.err.println("Java HotSpot(TM) virtual machine detected."); 494 } else { 495 System.err.println("(No Java(TM) virtual machine detected)"); 496 } 497 498 // Set up editor map 499 editors = new HashMap(); 500 501 // Initialize breakpoints 502 fileToBreakpointMap = new HashMap(); 503 504 // Create combined stack trace and local variable panel 505 JPanel framePanel = new JPanel(); 506 framePanel.setLayout(new BorderLayout()); 507 framePanel.setBorder(GraphicsUtilities.newBorder(5)); 508 localsPanel = new VariablePanel(); 509 JTabbedPane tab = new JTabbedPane(); 510 tab.addTab("Locals", localsPanel); 511 tab.setTabPlacement(JTabbedPane.BOTTOM); 512 framePanel.add(tab, BorderLayout.CENTER); 513 JPanel stackPanel = new JPanel(); 514 stackPanel.setLayout(new BoxLayout(stackPanel, BoxLayout.X_AXIS)); 515 stackPanel.add(new JLabel("Context:")); 516 stackPanel.add(Box.createHorizontalStrut(5)); 517 stackTracePanel = new StackTracePanel(); 518 stackTracePanel.addListener(new StackTracePanel.Listener() { 519 public void frameChanged(CFrame fr, JavaVFrame jfr) { 520 setCurrentFrame(fr, jfr); 521 } 522 }); 523 stackPanel.add(stackTracePanel); 524 framePanel.add(stackPanel, BorderLayout.NORTH); 525 stackFrame = newFrame("Stack"); 526 stackFrame.getContentPane().setLayout(new BorderLayout()); 527 stackFrame.getContentPane().add(framePanel, BorderLayout.CENTER); 528 stackFrame.setResizable(true); 529 stackFrame.setClosable(false); 530 addFrame(stackFrame); 531 stackFrame.setSize(400, 200); 532 GraphicsUtilities.moveToInContainer(stackFrame.getComponent(), 0.0f, 1.0f, 0, 20); 533 stackFrame.show(); 534 535 // Create register panel 536 registerPanel = new RegisterPanel(); 537 registerPanel.setFont(fixedWidthFont); 538 registerFrame = newFrame("Registers"); 539 registerFrame.getContentPane().setLayout(new BorderLayout()); 540 registerFrame.getContentPane().add(registerPanel, BorderLayout.CENTER); 541 addFrame(registerFrame); 542 registerFrame.setResizable(true); 543 registerFrame.setClosable(false); 544 registerFrame.setSize(225, 200); 545 GraphicsUtilities.moveToInContainer(registerFrame.getComponent(), 546 1.0f, 0.0f, 0, 0); 547 registerFrame.show(); 548 549 resetCurrentThread(); 550 } catch (DebuggerException e) { 551 final String errMsg = formatMessage(e.getMessage(), 80); 552 setMenuItemsEnabled(attachMenuItems, true); 553 showMessageDialog("Unable to connect to process ID " + pid + ":\n\n" + errMsg, 554 "Unable to Connect", 555 JOptionPane.WARNING_MESSAGE); 556 getAgent().detach(); 557 } 558 } 559 560 private synchronized void detachDebugger() { 561 if (!attached) { 562 return; 563 } 564 if (isSuspended()) { 565 resume(); // Necessary for JVMDI resumption 566 } 567 getAgent().detach(); 568 // FIXME: clear out breakpoints (both Java and C/C++) from target 569 // process 570 sourceFileToLineNumberInfoMap = null; 571 fileToBreakpointMap = null; 572 threadToJavaThreadMap = null; 573 editors = null; 574 attached = false; 575 } 576 577 private synchronized void detach() { 578 detachDebugger(); 579 setMenuItemsEnabled(attachMenuItems, true); 580 setMenuItemsEnabled(detachMenuItems, false); 581 debugMenu.setEnabled(false); 582 if (mdiMode) { 583 // FIXME: is this sufficient, or will I have to do anything else 584 // to the components to kill them off? What about WorkerThreads? 585 desktop.removeAll(); 586 desktop.invalidate(); 587 desktop.validate(); 588 desktop.repaint(); 589 } 590 // FIXME: keep track of all windows and close them even in non-MDI 591 // mode 592 debugEventTimer.stop(); 593 } 594 595 // Returns a Debugger for processes on the local machine. This is 596 // only used to fetch the process list. 597 private Debugger getLocalDebugger() { 598 if (localDebugger == null) { 599 String os = PlatformInfo.getOS(); 600 String cpu = PlatformInfo.getCPU(); 601 602 if (os.equals("win32")) { 603 if (!cpu.equals("x86")) { 604 throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Windows"); 605 } 606 607 localDebugger = new Win32DebuggerLocal(new MachineDescriptionIntelX86(), true); 608 } else if (os.equals("linux")) { 609 if (!cpu.equals("x86")) { 610 throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Linux"); 611 } 612 613 // FIXME: figure out how to specify path to debugger module 614 throw new RuntimeException("FIXME: figure out how to specify path to debugger module"); 615 // localDebugger = new PosixDebuggerLocal(new MachineDescriptionIntelX86(), true); 616 } else { 617 // FIXME: port to Solaris 618 throw new DebuggerException("Unsupported OS \"" + os + "\""); 619 } 620 621 // FIXME: we require that the primitive type sizes be configured 622 // in order to use basic functionality in class Address such as 623 // the fetching of floating-point values. There are a lot of 624 // assumptions in the current code that Java floats and doubles 625 // are of equivalent size to C values. The configurability of the 626 // primitive type sizes hasn't seemed necessary and in this kind 627 // of debugging scenario (namely, debugging arbitrary C++ 628 // processes) it appears difficult to support that kind of 629 // flexibility. 630 localDebugger.configureJavaPrimitiveTypeSizes(1, 1, 2, 8, 4, 4, 8, 2); 631 } 632 633 return localDebugger; 634 } 635 636 private BugSpotAgent getAgent() { 637 return agent; 638 } 639 640 private Debugger getDebugger() { 641 return getAgent().getDebugger(); 642 } 643 644 private CDebugger getCDebugger() { 645 return getAgent().getCDebugger(); 646 } 647 648 private void resetCurrentThread() { 649 setCurrentThread((ThreadProxy) getCDebugger().getThreadList().get(0)); 650 } 651 652 private void setCurrentThread(ThreadProxy t) { 653 // Create stack trace 654 // FIXME: add ability to intermix C/Java frames 655 java.util.List trace = new ArrayList(); 656 CFrame fr = getCDebugger().topFrameForThread(t); 657 while (fr != null) { 658 trace.add(new StackTraceEntry(fr, getCDebugger())); 659 try { 660 fr = fr.sender(); 661 } catch (AddressException e) { 662 e.printStackTrace(); 663 showMessageDialog("Error while walking stack; stack trace will be truncated\n(see console for details)", 664 "Error walking stack", 665 JOptionPane.WARNING_MESSAGE); 666 fr = null; 667 } 668 } 669 JavaThread jthread = javaThreadForProxy(t); 670 if (jthread != null) { 671 // Java mode, and we have a Java thread. 672 // Find all Java frames on the stack. We currently do this in a 673 // manner which involves minimal interaction between the Java 674 // and C/C++ debugging systems: any C frame which has a PC in an 675 // unknown location (i.e., not in any DSO) is assumed to be a 676 // Java frame. We merge stack segments of unknown frames with 677 // segments of Java frames beginning with native methods. 678 java.util.List javaTrace = new ArrayList(); 679 VFrame vf = jthread.getLastJavaVFrameDbg(); 680 while (vf != null) { 681 if (vf.isJavaFrame()) { 682 javaTrace.add(new StackTraceEntry((JavaVFrame) vf)); 683 vf = vf.sender(); 684 } 685 } 686 // Merge stack traces 687 java.util.List mergedTrace = new ArrayList(); 688 int c = 0; 689 int j = 0; 690 while (c < trace.size()) { 691 StackTraceEntry entry = (StackTraceEntry) trace.get(c); 692 if (entry.isUnknownCFrame()) { 693 boolean gotJavaFrame = false; 694 while (j < javaTrace.size()) { 695 StackTraceEntry javaEntry = (StackTraceEntry) javaTrace.get(j); 696 JavaVFrame jvf = javaEntry.getJavaFrame(); 697 Method m = jvf.getMethod(); 698 if (!m.isNative() || !gotJavaFrame) { 699 gotJavaFrame = true; 700 mergedTrace.add(javaEntry); 701 ++j; 702 } else { 703 break; // Reached native method; have intervening C frames 704 } 705 } 706 if (gotJavaFrame) { 707 // Skip this sequence of unknown frames, as we've 708 // successfully identified it as Java frames 709 while (c < trace.size() && entry.isUnknownCFrame()) { 710 ++c; 711 if (c < trace.size()) { 712 entry = (StackTraceEntry) trace.get(c); 713 } 714 } 715 continue; 716 } 717 } 718 // If we get here, we either have an unknown frame we didn't 719 // know how to categorize or we have a known C frame. Add it 720 // to the trace. 721 mergedTrace.add(entry); 722 ++c; 723 } 724 trace = mergedTrace; 725 } 726 stackTracePanel.setTrace(trace); 727 728 registerPanel.update(t); 729 } 730 731 private void setCurrentFrame(CFrame fr, JavaVFrame jfr) { 732 localsPanel.clear(); 733 734 if (fr != null) { 735 localsPanel.update(fr); 736 737 // FIXME: load source file if we can find it, otherwise display disassembly 738 LoadObject lo = getCDebugger().loadObjectContainingPC(fr.pc()); 739 if (lo != null) { 740 CDebugInfoDataBase db = lo.getDebugInfoDataBase(); 741 if (db != null) { 742 LineNumberInfo info = db.lineNumberForPC(fr.pc()); 743 if (info != null) { 744 System.err.println("PC " + fr.pc() + ": Source file \"" + 745 info.getSourceFileName() + 746 "\", line number " + 747 info.getLineNumber() + 748 ", PC range [" + 749 info.getStartPC() + 750 ", " + 751 info.getEndPC() + 752 ")"); 753 // OK, here we go... 754 showLineNumber(null, info.getSourceFileName(), info.getLineNumber()); 755 } else { 756 System.err.println("(No line number information for PC " + fr.pc() + ")"); 757 // Dump line number information for database 758 db.iterate(new LineNumberVisitor() { 759 public void doLineNumber(LineNumberInfo info) { 760 System.err.println(" Source file \"" + 761 info.getSourceFileName() + 762 "\", line number " + 763 info.getLineNumber() + 764 ", PC range [" + 765 info.getStartPC() + 766 ", " + 767 info.getEndPC() + 768 ")"); 769 } 770 }); 771 } 772 } 773 } 774 } else { 775 if (Assert.ASSERTS_ENABLED) { 776 Assert.that(jfr != null, "Must have either C or Java frame"); 777 } 778 localsPanel.update(jfr); 779 // See whether we can locate source file and line number 780 // FIXME: infer pathmap entries from user's locating of this 781 // source file 782 // FIXME: figure out what to do for native methods. Possible to 783 // go to line number for the native method declaration? 784 Method m = jfr.getMethod(); 785 Symbol sfn = ((InstanceKlass) m.getMethodHolder()).getSourceFileName(); 786 if (sfn != null) { 787 int bci = jfr.getBCI(); 788 int lineNo = m.getLineNumberFromBCI(bci); 789 if (lineNo >= 0) { 790 // FIXME: show disassembly otherwise 791 showLineNumber(packageName(m.getMethodHolder().getName().asString()), 792 sfn.asString(), lineNo); 793 } 794 } 795 } 796 } 797 798 private String packageName(String str) { 799 int idx = str.lastIndexOf('/'); 800 if (idx < 0) { 801 return ""; 802 } 803 return str.substring(0, idx).replace('/', '.'); 804 } 805 806 private JavaThread javaThreadForProxy(ThreadProxy t) { 807 if (!getAgent().isJavaMode()) { 808 return null; 809 } 810 if (threadToJavaThreadMap == null) { 811 threadToJavaThreadMap = new HashMap(); 812 Threads threads = VM.getVM().getThreads(); 813 for (JavaThread thr = threads.first(); thr != null; thr = thr.next()) { 814 threadToJavaThreadMap.put(thr.getThreadProxy(), thr); 815 } 816 } 817 return (JavaThread) threadToJavaThreadMap.get(t); 818 } 819 820 private static JMenu createMenu(String name, char mnemonic, int mnemonicPos) { 821 JMenu menu = new JMenu(name); 822 menu.setMnemonic(mnemonic); 823 menu.setDisplayedMnemonicIndex(mnemonicPos); 824 return menu; 825 } 826 827 private static JMenuItem createMenuItem(String name, ActionListener l) { 828 JMenuItem item = new JMenuItem(name); 829 item.addActionListener(l); 830 return item; 831 } 832 833 private static JMenuItem createMenuItemInternal(String name, ActionListener l, int accelerator, int modifiers) { 834 JMenuItem item = createMenuItem(name, l); 835 item.setAccelerator(KeyStroke.getKeyStroke(accelerator, modifiers)); 836 return item; 837 } 838 839 private static JMenuItem createMenuItem(String name, ActionListener l, int accelerator) { 840 return createMenuItemInternal(name, l, accelerator, 0); 841 } 842 843 private static JMenuItem createMenuItem(String name, ActionListener l, char mnemonic, int mnemonicPos) { 844 JMenuItem item = createMenuItem(name, l); 845 item.setMnemonic(mnemonic); 846 item.setDisplayedMnemonicIndex(mnemonicPos); 847 return item; 848 } 849 850 private static JMenuItem createMenuItem(String name, 851 ActionListener l, 852 int accelerator, 853 int acceleratorMods, 854 char mnemonic, 855 int mnemonicPos) { 856 JMenuItem item = createMenuItemInternal(name, l, accelerator, acceleratorMods); 857 item.setMnemonic(mnemonic); 858 item.setDisplayedMnemonicIndex(mnemonicPos); 859 return item; 860 } 861 862 /** Punctuates the given string with \n's where necessary to not 863 exceed the given number of characters per line. Strips 864 extraneous whitespace. */ 865 private static String formatMessage(String message, int charsPerLine) { 866 StringBuffer buf = new StringBuffer(message.length()); 867 StringTokenizer tokenizer = new StringTokenizer(message); 868 int curLineLength = 0; 869 while (tokenizer.hasMoreTokens()) { 870 String tok = tokenizer.nextToken(); 871 if (curLineLength + tok.length() > charsPerLine) { 872 buf.append('\n'); 873 curLineLength = 0; 874 } else { 875 if (curLineLength != 0) { 876 buf.append(' '); 877 ++curLineLength; 878 } 879 } 880 buf.append(tok); 881 curLineLength += tok.length(); 882 } 883 return buf.toString(); 884 } 885 886 private void setMenuItemsEnabled(java.util.List items, boolean enabled) { 887 for (Iterator iter = items.iterator(); iter.hasNext(); ) { 888 ((JMenuItem) iter.next()).setEnabled(enabled); 889 } 890 } 891 892 private void showMessageDialog(final String message, final String title, final int jOptionPaneKind) { 893 SwingUtilities.invokeLater(new Runnable() { 894 public void run() { 895 if (mdiMode) { 896 JOptionPane.showInternalMessageDialog(desktop, message, title, jOptionPaneKind); 897 } else { 898 JOptionPane.showMessageDialog(null, message, title, jOptionPaneKind); 899 } 900 } 901 }); 902 } 903 904 private FrameWrapper newFrame(String title) { 905 if (mdiMode) { 906 return new JInternalFrameWrapper(new JInternalFrame(title)); 907 } else { 908 return new JFrameWrapper(new JFrame(title)); 909 } 910 } 911 912 private void addFrame(FrameWrapper frame) { 913 if (mdiMode) { 914 desktop.add(frame.getComponent()); 915 } 916 } 917 918 private void removeFrame(FrameWrapper frame) { 919 if (mdiMode) { 920 desktop.remove(frame.getComponent()); 921 desktop.invalidate(); 922 desktop.validate(); 923 desktop.repaint(); 924 } 925 // FIXME: do something when not in MDI mode 926 } 927 928 private Dimension getParentDimension(Component c) { 929 if (mdiMode) { 930 return desktop.getSize(); 931 } else { 932 return Toolkit.getDefaultToolkit().getScreenSize(); 933 } 934 } 935 936 // Default editor implementation 937 class DefaultEditor implements Editor { 938 private DefaultEditorFactory factory; 939 private FrameWrapper editorFrame; 940 private String filename; 941 private SourceCodePanel code; 942 private boolean shown; 943 private Object userData; 944 945 public DefaultEditor(DefaultEditorFactory fact, String filename, final EditorCommands comm) { 946 this.filename = filename; 947 this.factory = fact; 948 editorFrame = newFrame(filename); 949 code = new SourceCodePanel(); 950 // FIXME: when font changes, change font in editors as well 951 code.setFont(fixedWidthFont); 952 editorFrame.getContentPane().add(code); 953 editorFrame.setClosable(true); 954 editorFrame.setResizable(true); 955 editorFrame.setClosingActionListener(new ActionListener() { 956 public void actionPerformed(ActionEvent e) { 957 comm.windowClosed(DefaultEditor.this); 958 removeFrame(editorFrame); 959 editorFrame.dispose(); 960 factory.editorClosed(DefaultEditor.this); 961 } 962 }); 963 editorFrame.setActivatedActionListener(new ActionListener() { 964 public void actionPerformed(ActionEvent e) { 965 factory.makeEditorCurrent(DefaultEditor.this); 966 code.requestFocus(); 967 } 968 }); 969 code.setEditorCommands(comm, this); 970 } 971 972 public boolean openFile() { return code.openFile(filename); } 973 public String getSourceFileName() { return filename; } 974 public int getCurrentLineNumber() { return code.getCurrentLineNumber(); } 975 public void showLineNumber(int lineNo) { 976 if (!shown) { 977 addFrame(editorFrame); 978 GraphicsUtilities.reshapeToAspectRatio(editorFrame.getComponent(), 979 1.0f, 980 0.85f, 981 getParentDimension(editorFrame.getComponent())); 982 editorFrame.show(); 983 shown = true; 984 } 985 code.showLineNumber(lineNo); 986 editorFrame.toFront(); 987 } 988 public void highlightLineNumber(int lineNo) { code.highlightLineNumber(lineNo); } 989 public void showBreakpointAtLine(int lineNo) { code.showBreakpointAtLine(lineNo); } 990 public boolean hasBreakpointAtLine(int lineNo) { return code.hasBreakpointAtLine(lineNo); } 991 public void clearBreakpointAtLine(int lineNo) { code.clearBreakpointAtLine(lineNo); } 992 public void clearBreakpoints() { code.clearBreakpoints(); } 993 public void setUserData(Object o) { userData = o; } 994 public Object getUserData() { return userData; } 995 public void toFront() { editorFrame.toFront(); 996 factory.makeEditorCurrent(this); } 997 } 998 999 class DefaultEditorFactory implements EditorFactory { 1000 private LinkedList/*<Editor>*/ editors = new LinkedList(); 1001 1002 public Editor openFile(String filename, EditorCommands commands) { 1003 DefaultEditor editor = new DefaultEditor(this, filename, editorComm); 1004 if (!editor.openFile()) { 1005 return null; 1006 } 1007 return editor; 1008 } 1009 1010 public Editor getCurrentEditor() { 1011 if (editors.isEmpty()) { 1012 return null; 1013 } 1014 return (Editor) editors.getFirst(); 1015 } 1016 1017 void editorClosed(Editor editor) { 1018 editors.remove(editor); 1019 } 1020 1021 void makeEditorCurrent(Editor editor) { 1022 editors.remove(editor); 1023 editors.addFirst(editor); 1024 } 1025 } 1026 1027 // Helper class for loading .java files; show only those with 1028 // correct file name which are also in the correct package 1029 static class JavaFileFilter extends javax.swing.filechooser.FileFilter { 1030 private String packageName; 1031 private String fileName; 1032 1033 JavaFileFilter(String packageName, String fileName) { 1034 this.packageName = packageName; 1035 this.fileName = fileName; 1036 } 1037 1038 public boolean accept(File f) { 1039 if (f.isDirectory()) { 1040 return true; 1041 } 1042 // This rejects most files 1043 if (!f.getName().equals(fileName)) { 1044 return false; 1045 } 1046 // Ensure selected file is in the correct package 1047 PackageScanner scanner = new PackageScanner(); 1048 String pkg = scanner.scan(f); 1049 if (!pkg.equals(packageName)) { 1050 return false; 1051 } 1052 return true; 1053 } 1054 1055 public String getDescription() { return "Java source files"; } 1056 } 1057 1058 // Auxiliary information used only for Java source files 1059 static class JavaUserData { 1060 private String packageName; // External format 1061 private String sourceFileName; 1062 1063 /** Source file name is equivalent to that found in the .java 1064 file; i.e., not a full path */ 1065 JavaUserData(String packageName, String sourceFileName) { 1066 this.packageName = packageName; 1067 this.sourceFileName = sourceFileName; 1068 } 1069 1070 String packageName() { return packageName; } 1071 String sourceFileName() { return sourceFileName; } 1072 } 1073 1074 // Opens a source file. This makes it available for the setting of 1075 // lazy breakpoints. 1076 private void openSourceFile() { 1077 JFileChooser chooser = new JFileChooser(); 1078 chooser.setDialogTitle("Open source code file"); 1079 chooser.setMultiSelectionEnabled(false); 1080 if (chooser.showOpenDialog(null) != JFileChooser.APPROVE_OPTION) { 1081 return; 1082 } 1083 File chosen = chooser.getSelectedFile(); 1084 if (chosen == null) { 1085 return; 1086 } 1087 1088 // See whether we have a Java source file. If so, derive a package 1089 // name for it. 1090 String path = chosen.getPath(); 1091 String name = null; 1092 JavaUserData data = null; 1093 if (path.endsWith(".java")) { 1094 PackageScanner scanner = new PackageScanner(); 1095 String pkg = scanner.scan(chosen); 1096 // Now knowing both the package name and file name, we can put 1097 // this in the editor map and use it for setting breakpoints 1098 // later 1099 String fileName = chosen.getName(); 1100 name = pkg + "." + fileName; 1101 data = new JavaUserData(pkg, fileName); 1102 } else { 1103 // FIXME: need pathmap mechanism 1104 name = path; 1105 } 1106 Editor editor = (Editor) editors.get(name); 1107 if (editor == null) { 1108 editor = editorFact.openFile(path, editorComm); 1109 if (editor == null) { 1110 showMessageDialog("Unable to open file \"" + path + "\" -- unexpected error.", 1111 "Unable to open file", 1112 JOptionPane.WARNING_MESSAGE); 1113 return; 1114 } 1115 editors.put(name, editor); 1116 if (data != null) { 1117 editor.setUserData(data); 1118 } 1119 } else { 1120 editor.toFront(); 1121 } 1122 editor.showLineNumber(1); 1123 // Show breakpoints as well if we have any for this file 1124 Set set = (Set) fileToBreakpointMap.get(editor.getSourceFileName()); 1125 if (set != null) { 1126 for (Iterator iter = set.iterator(); iter.hasNext(); ) { 1127 editor.showBreakpointAtLine(((Integer) iter.next()).intValue()); 1128 } 1129 } 1130 } 1131 1132 // Package name may be null, in which case the file is assumed to be 1133 // a C source file. Otherwise it is assumed to be a Java source file 1134 // and certain filtering rules will be applied. 1135 private void showLineNumber(String packageName, String fileName, int lineNumber) { 1136 String name; 1137 if (packageName == null) { 1138 name = fileName; 1139 } else { 1140 name = packageName + "." + fileName; 1141 } 1142 Editor editor = (Editor) editors.get(name); 1143 if (editor == null) { 1144 // See whether file exists 1145 File file = new File(fileName); 1146 String realFileName = fileName; 1147 if (!file.exists()) { 1148 // User must specify path to file 1149 JFileChooser chooser = new JFileChooser(); 1150 chooser.setDialogTitle("Please locate " + fileName); 1151 chooser.setMultiSelectionEnabled(false); 1152 if (packageName != null) { 1153 chooser.setFileFilter(new JavaFileFilter(packageName, fileName)); 1154 } 1155 int res = chooser.showOpenDialog(null); 1156 if (res != JFileChooser.APPROVE_OPTION) { 1157 // FIXME: show disassembly instead 1158 return; 1159 } 1160 // FIXME: would like to infer more from the selection; i.e., 1161 // a pathmap leading up to this file 1162 File chosen = chooser.getSelectedFile(); 1163 if (chosen == null) { 1164 return; 1165 } 1166 realFileName = chosen.getPath(); 1167 } 1168 // Now instruct editor factory to open file 1169 editor = editorFact.openFile(realFileName, editorComm); 1170 if (editor == null) { 1171 showMessageDialog("Unable to open file \"" + realFileName + "\" -- unexpected error.", 1172 "Unable to open file", 1173 JOptionPane.WARNING_MESSAGE); 1174 return; 1175 } 1176 // Got an editor; put it in map 1177 editors.put(name, editor); 1178 // If Java source file, add additional information for later 1179 if (packageName != null) { 1180 editor.setUserData(new JavaUserData(packageName, fileName)); 1181 } 1182 } 1183 // Got editor; show line 1184 editor.showLineNumber(lineNumber); 1185 editor.highlightLineNumber(lineNumber); 1186 // Show breakpoints as well if we have any for this file 1187 Set set = (Set) fileToBreakpointMap.get(editor.getSourceFileName()); 1188 if (set != null) { 1189 for (Iterator iter = set.iterator(); iter.hasNext(); ) { 1190 editor.showBreakpointAtLine(((Integer) iter.next()).intValue()); 1191 } 1192 } 1193 } 1194 1195 // 1196 // Suspend/resume 1197 // 1198 1199 private boolean isSuspended() { 1200 return suspended; 1201 } 1202 1203 private synchronized void suspend() { 1204 setMenuItemsEnabled(resumeDebugMenuItems, true); 1205 setMenuItemsEnabled(suspendDebugMenuItems, false); 1206 BugSpotAgent agent = getAgent(); 1207 if (agent.canInteractWithJava() && !agent.isJavaSuspended()) { 1208 agent.suspendJava(); 1209 } 1210 agent.suspend(); 1211 // FIXME: call VM.getVM().fireVMSuspended() 1212 resetCurrentThread(); 1213 debugEventTimer.stop(); 1214 suspended = true; 1215 } 1216 1217 private synchronized void resume() { 1218 // Note: we don't wipe out the cached state like the 1219 // sourceFileToLineNumberInfoMap since it is too expensive to 1220 // recompute. Instead we recompute it if any DLLs are loaded or 1221 // unloaded. 1222 threadToJavaThreadMap = null; 1223 setMenuItemsEnabled(resumeDebugMenuItems, false); 1224 setMenuItemsEnabled(suspendDebugMenuItems, true); 1225 registerPanel.clear(); 1226 // FIXME: call VM.getVM().fireVMResumed() 1227 BugSpotAgent agent = getAgent(); 1228 agent.resume(); 1229 if (agent.canInteractWithJava()) { 1230 if (agent.isJavaSuspended()) { 1231 agent.resumeJava(); 1232 } 1233 if (javaEventPending) { 1234 javaEventPending = false; 1235 // Clear it out before resuming polling for events 1236 agent.javaEventContinue(); 1237 } 1238 } 1239 agent.enableJavaInteraction(); 1240 suspended = false; 1241 debugEventTimer.start(); 1242 } 1243 1244 // 1245 // Breakpoints 1246 // 1247 1248 private synchronized BreakpointResult handleBreakpointToggle(Editor editor, int lineNumber) { 1249 // Currently we only use user data in editors to indicate Java 1250 // source files. If this changes then this code will need to 1251 // change. 1252 JavaUserData data = (JavaUserData) editor.getUserData(); 1253 String filename = editor.getSourceFileName(); 1254 if (data == null) { 1255 // C/C++ code 1256 // FIXME: as noted above in EditorCommands.toggleBreakpointAtLine, 1257 // this needs more work to handle "lazy" breakpoints in files 1258 // which we don't know about in the debug information yet 1259 CDebugger dbg = getCDebugger(); 1260 ProcessControl prctl = dbg.getProcessControl(); 1261 if (prctl == null) { 1262 return new BreakpointResult(false, false, 0, "Process control not enabled"); 1263 } 1264 boolean mustSuspendAndResume = (!prctl.isSuspended()); 1265 try { 1266 if (mustSuspendAndResume) { 1267 prctl.suspend(); 1268 } 1269 // Search debug info for all DSOs 1270 LineNumberInfo info = getLineNumberInfo(filename, lineNumber); 1271 if (info != null) { 1272 Set bpset = (Set) fileToBreakpointMap.get(filename); 1273 if (bpset == null) { 1274 bpset = new HashSet(); 1275 fileToBreakpointMap.put(filename, bpset); 1276 } 1277 Integer key = new Integer(info.getLineNumber()); 1278 if (bpset.contains(key)) { 1279 // Clear breakpoint at this line's PC 1280 prctl.clearBreakpoint(info.getStartPC()); 1281 bpset.remove(key); 1282 return new BreakpointResult(true, false, info.getLineNumber()); 1283 } else { 1284 // Set breakpoint at this line's PC 1285 System.err.println("Setting breakpoint at PC " + info.getStartPC()); 1286 prctl.setBreakpoint(info.getStartPC()); 1287 bpset.add(key); 1288 return new BreakpointResult(true, true, info.getLineNumber()); 1289 } 1290 } else { 1291 return new BreakpointResult(false, false, 0, "No debug information for this source file and line"); 1292 } 1293 } finally { 1294 if (mustSuspendAndResume) { 1295 prctl.resume(); 1296 } 1297 } 1298 } else { 1299 BugSpotAgent agent = getAgent(); 1300 if (!agent.canInteractWithJava()) { 1301 String why; 1302 if (agent.isJavaInteractionDisabled()) { 1303 why = "Can not toggle Java breakpoints while stopped because\nof C/C++ debug events (breakpoints, single-stepping)"; 1304 } else { 1305 why = "Could not talk to SA's JVMDI module to enable Java\nprogramming language breakpoints (run with -Xdebug -Xrunsa)"; 1306 } 1307 return new BreakpointResult(false, false, 0, why); 1308 } 1309 Set bpset = (Set) fileToBreakpointMap.get(filename); 1310 if (bpset == null) { 1311 bpset = new HashSet(); 1312 fileToBreakpointMap.put(filename, bpset); 1313 } 1314 boolean mustResumeAndSuspend = isSuspended(); 1315 try { 1316 if (mustResumeAndSuspend) { 1317 agent.resume(); 1318 } 1319 ServiceabilityAgentJVMDIModule.BreakpointToggleResult res = 1320 getAgent().toggleJavaBreakpoint(data.sourceFileName(), 1321 data.packageName(), 1322 lineNumber); 1323 if (res.getSuccess()) { 1324 Integer key = new Integer(res.getLineNumber()); 1325 boolean addRemRes = false; 1326 if (res.getWasSet()) { 1327 addRemRes = bpset.add(key); 1328 System.err.println("Setting breakpoint at " + res.getMethodName() + res.getMethodSignature() + 1329 ", bci " + res.getBCI() + ", line " + res.getLineNumber()); 1330 } else { 1331 addRemRes = bpset.remove(key); 1332 System.err.println("Clearing breakpoint at " + res.getMethodName() + res.getMethodSignature() + 1333 ", bci " + res.getBCI() + ", line " + res.getLineNumber()); 1334 } 1335 if (Assert.ASSERTS_ENABLED) { 1336 Assert.that(addRemRes, "Inconsistent Java breakpoint state with respect to target process"); 1337 } 1338 return new BreakpointResult(true, res.getWasSet(), res.getLineNumber()); 1339 } else { 1340 return new BreakpointResult(false, false, 0, res.getErrMsg()); 1341 } 1342 } finally { 1343 if (mustResumeAndSuspend) { 1344 agent.suspend(); 1345 resetCurrentThread(); 1346 } 1347 } 1348 } 1349 } 1350 1351 // Must call only when suspended 1352 private LineNumberInfo getLineNumberInfo(String filename, int lineNumber) { 1353 Map map = getSourceFileToLineNumberInfoMap(); 1354 java.util.List infos = (java.util.List) map.get(filename); 1355 if (infos == null) { 1356 return null; 1357 } 1358 // Binary search for line number 1359 return searchLineNumbers(infos, lineNumber, 0, infos.size()); 1360 } 1361 1362 // Must call only when suspended 1363 private Map getSourceFileToLineNumberInfoMap() { 1364 if (sourceFileToLineNumberInfoMap == null) { 1365 // Build from debug info 1366 java.util.List loadObjects = getCDebugger().getLoadObjectList(); 1367 final Map map = new HashMap(); 1368 for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { 1369 LoadObject lo = (LoadObject) iter.next(); 1370 CDebugInfoDataBase db = lo.getDebugInfoDataBase(); 1371 if (db != null) { 1372 db.iterate(new LineNumberVisitor() { 1373 public void doLineNumber(LineNumberInfo info) { 1374 String name = info.getSourceFileName(); 1375 if (name != null) { 1376 java.util.List val = (java.util.List) map.get(name); 1377 if (val == null) { 1378 val = new ArrayList(); 1379 map.put(name, val); 1380 } 1381 val.add(info); 1382 } 1383 } 1384 }); 1385 } 1386 } 1387 // Sort all lists 1388 for (Iterator iter = map.values().iterator(); iter.hasNext(); ) { 1389 java.util.List list = (java.util.List) iter.next(); 1390 Collections.sort(list, new Comparator() { 1391 public int compare(Object o1, Object o2) { 1392 LineNumberInfo l1 = (LineNumberInfo) o1; 1393 LineNumberInfo l2 = (LineNumberInfo) o2; 1394 int n1 = l1.getLineNumber(); 1395 int n2 = l2.getLineNumber(); 1396 if (n1 < n2) return -1; 1397 if (n1 == n2) return 0; 1398 return 1; 1399 } 1400 }); 1401 } 1402 sourceFileToLineNumberInfoMap = map; 1403 } 1404 return sourceFileToLineNumberInfoMap; 1405 } 1406 1407 private LineNumberInfo searchLineNumbers(java.util.List infoList, int lineNo, int lowIdx, int highIdx) { 1408 if (highIdx < lowIdx) return null; 1409 if (lowIdx == highIdx) { 1410 // Base case: see whether start PC is less than or equal to addr 1411 if (checkLineNumber(infoList, lineNo, lowIdx)) { 1412 return (LineNumberInfo) infoList.get(lowIdx); 1413 } else { 1414 return null; 1415 } 1416 } else if (lowIdx == highIdx - 1) { 1417 if (checkLineNumber(infoList, lineNo, lowIdx)) { 1418 return (LineNumberInfo) infoList.get(lowIdx); 1419 } else if (checkLineNumber(infoList, lineNo, highIdx)) { 1420 return (LineNumberInfo) infoList.get(highIdx); 1421 } else { 1422 return null; 1423 } 1424 } 1425 int midIdx = (lowIdx + highIdx) >> 1; 1426 LineNumberInfo info = (LineNumberInfo) infoList.get(midIdx); 1427 if (lineNo < info.getLineNumber()) { 1428 // Always move search down 1429 return searchLineNumbers(infoList, lineNo, lowIdx, midIdx); 1430 } else if (lineNo == info.getLineNumber()) { 1431 return info; 1432 } else { 1433 // Move search up 1434 return searchLineNumbers(infoList, lineNo, midIdx, highIdx); 1435 } 1436 } 1437 1438 private boolean checkLineNumber(java.util.List infoList, int lineNo, int idx) { 1439 LineNumberInfo info = (LineNumberInfo) infoList.get(idx); 1440 return (info.getLineNumber() >= lineNo); 1441 } 1442 1443 // 1444 // Debug events 1445 // 1446 1447 private synchronized void pollForDebugEvent() { 1448 ProcessControl prctl = getCDebugger().getProcessControl(); 1449 if (prctl == null) { 1450 return; 1451 } 1452 DebugEvent ev = prctl.debugEventPoll(); 1453 if (ev != null) { 1454 DebugEvent.Type t = ev.getType(); 1455 if (t == DebugEvent.Type.LOADOBJECT_LOAD || 1456 t == DebugEvent.Type.LOADOBJECT_UNLOAD) { 1457 // Conservatively clear cached debug info state 1458 sourceFileToLineNumberInfoMap = null; 1459 // FIXME: would be very useful to have "stop on load/unload" 1460 // events 1461 // FIXME: must do work at these events to implement lazy 1462 // breakpoints 1463 prctl.debugEventContinue(); 1464 } else if (t == DebugEvent.Type.BREAKPOINT) { 1465 // Note: Visual C++ only notifies on breakpoints it doesn't 1466 // know about 1467 1468 // FIXME: put back test 1469 // if (!prctl.isBreakpointSet(ev.getPC())) { 1470 showMessageDialog("Breakpoint reached at PC " + ev.getPC(), 1471 "Breakpoint reached", 1472 JOptionPane.INFORMATION_MESSAGE); 1473 // } 1474 agent.disableJavaInteraction(); 1475 suspend(); 1476 prctl.debugEventContinue(); 1477 } else if (t == DebugEvent.Type.SINGLE_STEP) { 1478 agent.disableJavaInteraction(); 1479 suspend(); 1480 prctl.debugEventContinue(); 1481 } else if (t == DebugEvent.Type.ACCESS_VIOLATION) { 1482 showMessageDialog("Access violation attempting to " + 1483 (ev.getWasWrite() ? "write" : "read") + 1484 " address " + ev.getAddress() + 1485 " at PC " + ev.getPC(), 1486 "Access Violation", 1487 JOptionPane.WARNING_MESSAGE); 1488 agent.disableJavaInteraction(); 1489 suspend(); 1490 prctl.debugEventContinue(); 1491 } else { 1492 String info = "Unknown debug event encountered"; 1493 if (ev.getUnknownEventDetail() != null) { 1494 info = info + ": " + ev.getUnknownEventDetail(); 1495 } 1496 showMessageDialog(info, "Unknown debug event", JOptionPane.INFORMATION_MESSAGE); 1497 suspend(); 1498 prctl.debugEventContinue(); 1499 } 1500 return; 1501 } 1502 1503 // No C++ debug event; poll for Java debug event 1504 if (getAgent().canInteractWithJava()) { 1505 if (!javaEventPending) { 1506 if (getAgent().javaEventPending()) { 1507 suspend(); 1508 // This does a lot of work and we want to have the page 1509 // cache available to us as it runs 1510 sun.jvm.hotspot.livejvm.Event jev = getAgent().javaEventPoll(); 1511 if (jev != null) { 1512 javaEventPending = true; 1513 if (jev.getType() == sun.jvm.hotspot.livejvm.Event.Type.BREAKPOINT) { 1514 BreakpointEvent bpev = (BreakpointEvent) jev; 1515 showMessageDialog("Breakpoint reached in method\n" + 1516 bpev.methodID().method().externalNameAndSignature() + 1517 ",\nbci " + bpev.location(), 1518 "Breakpoint reached", 1519 JOptionPane.INFORMATION_MESSAGE); 1520 } else if (jev.getType() == sun.jvm.hotspot.livejvm.Event.Type.EXCEPTION) { 1521 ExceptionEvent exev = (ExceptionEvent) jev; 1522 showMessageDialog(exev.exception().getKlass().getName().asString() + 1523 "\nthrown in method\n" + 1524 exev.methodID().method().externalNameAndSignature() + 1525 "\nat BCI " + exev.location(), 1526 "Exception thrown", 1527 JOptionPane.INFORMATION_MESSAGE); 1528 } else { 1529 Assert.that(false, "Should not reach here"); 1530 } 1531 } 1532 } 1533 } 1534 } 1535 } 1536 }