Mercurial > hg > truffle
annotate agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java @ 3945:8ed53447f690
Merge
author | iveresov |
---|---|
date | Thu, 15 Sep 2011 12:44:09 -0700 |
parents | f6f3bb0ee072 |
children | b87e5a681416 |
rev | line source |
---|---|
0 | 1 /* |
3939 | 2 * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. |
0 | 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 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1385
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1385
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1385
diff
changeset
|
21 * questions. |
0 | 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.*; | |
3939 | 37 import sun.jvm.hotspot.debugger.windbg.*; |
0 | 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())); | |
1385 | 297 attachDialog.setVisible(true); |
0 | 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())); | |
1385 | 324 threadsDialog.setVisible(true); |
0 | 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())); | |
1385 | 344 memoryDialog.setVisible(true); |
0 | 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); | |
1385 | 533 stackFrame.setVisible(true); |
0 | 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); | |
1385 | 547 registerFrame.setVisible(true); |
0 | 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 | |
3939 | 607 localDebugger = new WindbgDebuggerLocal(new MachineDescriptionIntelX86(), true); |
0 | 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())); | |
1385 | 982 editorFrame.setVisible(true); |
0 | 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 } |