diff options
Diffstat (limited to 'agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java')
-rw-r--r-- | agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java | 1536 |
1 files changed, 0 insertions, 1536 deletions
diff --git a/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java b/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java deleted file mode 100644 index 2ff5308b1..000000000 --- a/agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java +++ /dev/null @@ -1,1536 +0,0 @@ -/* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -package sun.jvm.hotspot.bugspot; - -import java.awt.*; -import java.awt.event.*; -import java.io.*; -import java.net.*; -import java.util.*; -import javax.swing.*; -import javax.swing.filechooser.*; -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.debugger.cdbg.*; -import sun.jvm.hotspot.debugger.posix.*; -import sun.jvm.hotspot.debugger.windbg.*; -import sun.jvm.hotspot.livejvm.*; -import sun.jvm.hotspot.memory.*; -import sun.jvm.hotspot.oops.*; -import sun.jvm.hotspot.runtime.*; -import sun.jvm.hotspot.ui.*; -import sun.jvm.hotspot.utilities.*; - -/** The BugSpot component. This is embeddable in an application by - virtue of its being a JComponent. It (currently) requires the use - of a menu bar which can be fetched via getMenuBar(). This is - intended ultimately to replace HSDB. */ - -public class BugSpot extends JPanel { - public BugSpot() { - super(); - Runtime.getRuntime().addShutdownHook(new java.lang.Thread() { - public void run() { - detachDebugger(); - } - }); - } - - /** Turn on or off MDI (Multiple Document Interface) mode. When MDI - is enabled, the BugSpot component contains a JDesktopPane and all - windows are JInternalFrames. When disabled, only the menu bar is - relevant. */ - public void setMDIMode(boolean onOrOff) { - mdiMode = onOrOff; - } - - /** Indicates whether MDI mode is enabled. */ - public boolean getMDIMode() { - return mdiMode; - } - - /** Build user interface widgets. This must be called before adding - the BugSpot component to its parent. */ - public void build() { - setLayout(new BorderLayout()); - - menuBar = new JMenuBar(); - - attachMenuItems = new java.util.ArrayList(); - detachMenuItems = new java.util.ArrayList(); - debugMenuItems = new java.util.ArrayList(); - suspendDebugMenuItems = new java.util.ArrayList(); - resumeDebugMenuItems = new java.util.ArrayList(); - - // - // File menu - // - - JMenu menu = createMenu("File", 'F', 0); - JMenuItem item; - item = createMenuItem("Open source file...", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - openSourceFile(); - } - }, - KeyEvent.VK_O, InputEvent.CTRL_MASK, - 'O', 0); - menu.add(item); - detachMenuItems.add(item); - - menu.addSeparator(); - - item = createMenuItem("Attach to process...", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - showAttachDialog(); - } - }, - 'A', 0); - menu.add(item); - attachMenuItems.add(item); - - item = createMenuItem("Detach", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - detach(); - } - }, - 'D', 0); - menu.add(item); - detachMenuItems.add(item); - - // Disable detach menu items at first - setMenuItemsEnabled(detachMenuItems, false); - - menu.addSeparator(); - - menu.add(createMenuItem("Exit", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - detach(); - System.exit(0); - } - }, - 'x', 1)); - - menuBar.add(menu); - - // - // Debug menu - // - - debugMenu = createMenu("Debug", 'D', 0); - item = createMenuItem("Go", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (!attached) return; - if (!isSuspended()) return; - resume(); - } - }, - KeyEvent.VK_F5, 0, - 'G', 0); - debugMenu.add(item); - resumeDebugMenuItems.add(item); - - item = createMenuItem("Break", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (!attached) { - System.err.println("Not attached"); - return; - } - if (isSuspended()) { - System.err.println("Already suspended"); - return; - } - suspend(); - } - }, - 'B', 0); - debugMenu.add(item); - suspendDebugMenuItems.add(item); - - debugMenu.addSeparator(); - - item = createMenuItem("Threads...", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - showThreadsDialog(); - } - }, - 'T', 0); - debugMenu.add(item); - debugMenuItems.add(item); - // FIXME: belongs under "View -> Debug Windows" - item = createMenuItem("Memory", - new ActionListener() { - public void actionPerformed(ActionEvent e) { - showMemoryDialog(); - } - }, - 'M', 0); - debugMenu.add(item); - debugMenuItems.add(item); - - debugMenu.setEnabled(false); - menuBar.add(debugMenu); - - if (mdiMode) { - desktop = new JDesktopPane(); - add(desktop, BorderLayout.CENTER); - } - - fixedWidthFont = GraphicsUtilities.lookupFont("Courier"); - - debugEventTimer = new javax.swing.Timer(100, new ActionListener() { - public void actionPerformed(ActionEvent e) { - pollForDebugEvent(); - } - }); - } - - public JMenuBar getMenuBar() { - return menuBar; - } - - public void showAttachDialog() { - setMenuItemsEnabled(attachMenuItems, false); - final FrameWrapper attachDialog = newFrame("Attach to process"); - attachDialog.getContentPane().setLayout(new BorderLayout()); - attachDialog.setClosable(true); - attachDialog.setResizable(true); - - JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - panel.setBorder(GraphicsUtilities.newBorder(5)); - attachDialog.setBackground(panel.getBackground()); - - JPanel listPanel = new JPanel(); - listPanel.setLayout(new BorderLayout()); - final ProcessListPanel plist = new ProcessListPanel(getLocalDebugger()); - panel.add(plist, BorderLayout.CENTER); - JCheckBox check = new JCheckBox("Update list continuously"); - check.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - plist.start(); - } else { - plist.stop(); - } - } - }); - listPanel.add(plist, BorderLayout.CENTER); - listPanel.add(check, BorderLayout.SOUTH); - panel.add(listPanel, BorderLayout.CENTER); - attachDialog.getContentPane().add(panel, BorderLayout.CENTER); - attachDialog.setClosingActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - plist.stop(); - setMenuItemsEnabled(attachMenuItems, true); - } - }); - - ActionListener attacher = new ActionListener() { - public void actionPerformed(ActionEvent e) { - plist.stop(); - attachDialog.setVisible(false); - removeFrame(attachDialog); - ProcessInfo info = plist.getSelectedProcess(); - if (info != null) { - attach(info.getPid()); - } - } - }; - - Box hbox = Box.createHorizontalBox(); - hbox.add(Box.createGlue()); - JButton button = new JButton("OK"); - button.addActionListener(attacher); - hbox.add(button); - hbox.add(Box.createHorizontalStrut(20)); - button = new JButton("Cancel"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - plist.stop(); - attachDialog.setVisible(false); - removeFrame(attachDialog); - setMenuItemsEnabled(attachMenuItems, true); - } - }); - hbox.add(button); - hbox.add(Box.createGlue()); - panel = new JPanel(); - panel.setBorder(GraphicsUtilities.newBorder(5)); - panel.add(hbox); - - attachDialog.getContentPane().add(panel, BorderLayout.SOUTH); - - addFrame(attachDialog); - attachDialog.pack(); - attachDialog.setSize(400, 300); - GraphicsUtilities.centerInContainer(attachDialog.getComponent(), - getParentDimension(attachDialog.getComponent())); - attachDialog.setVisible(true); - } - - public void showThreadsDialog() { - final FrameWrapper threadsDialog = newFrame("Threads"); - threadsDialog.getContentPane().setLayout(new BorderLayout()); - threadsDialog.setClosable(true); - threadsDialog.setResizable(true); - - ThreadListPanel threads = new ThreadListPanel(getCDebugger(), getAgent().isJavaMode()); - threads.addListener(new ThreadListPanel.Listener() { - public void setFocus(ThreadProxy thread, JavaThread jthread) { - setCurrentThread(thread); - // FIXME: print this to GUI, bring some windows to foreground - System.err.println("Focus changed to thread " + thread); - } - }); - threads.setBorder(GraphicsUtilities.newBorder(5)); - threadsDialog.getContentPane().add(threads); - addFrame(threadsDialog); - threadsDialog.pack(); - GraphicsUtilities.reshapeToAspectRatio(threadsDialog.getComponent(), - 3.0f, - 0.9f, - getParentDimension(threadsDialog.getComponent())); - GraphicsUtilities.centerInContainer(threadsDialog.getComponent(), - getParentDimension(threadsDialog.getComponent())); - threadsDialog.setVisible(true); - } - - public void showMemoryDialog() { - final FrameWrapper memoryDialog = newFrame("Memory"); - memoryDialog.getContentPane().setLayout(new BorderLayout()); - memoryDialog.setClosable(true); - memoryDialog.setResizable(true); - - memoryDialog.getContentPane().add(new MemoryViewer(getDebugger(), - (getDebugger().getMachineDescription().getAddressSize() == 8)), - BorderLayout.CENTER); - addFrame(memoryDialog); - memoryDialog.pack(); - GraphicsUtilities.reshapeToAspectRatio(memoryDialog.getComponent(), - 1.0f, - 0.7f, - getParentDimension(memoryDialog.getComponent())); - GraphicsUtilities.centerInContainer(memoryDialog.getComponent(), - getParentDimension(memoryDialog.getComponent())); - memoryDialog.setVisible(true); - } - - /** Changes the editor factory this debugger uses to display source - code. Specified factory may be null, in which case the default - factory is used. */ - public void setEditorFactory(EditorFactory fact) { - if (fact != null) { - editorFact = fact; - } else { - editorFact = new DefaultEditorFactory(); - } - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private WorkerThread workerThread; - private boolean mdiMode; - private JVMDebugger localDebugger; - private BugSpotAgent agent = new BugSpotAgent(); - private JMenuBar menuBar; - /** List <JMenuItem> */ - private java.util.List attachMenuItems; - private java.util.List detachMenuItems; - private java.util.List debugMenuItems; - private java.util.List suspendDebugMenuItems; - private java.util.List resumeDebugMenuItems; - private FrameWrapper stackFrame; - private VariablePanel localsPanel; - private StackTracePanel stackTracePanel; - private FrameWrapper registerFrame; - private RegisterPanel registerPanel; - // Used for mixed-language stack traces - private Map threadToJavaThreadMap; - - private JMenu debugMenu; - - // MDI mode only: desktop pane - private JDesktopPane desktop; - - // Attach/detach state - private boolean attached; - - // Suspension (combined Java/C++) state - private boolean suspended; - - // Fixed-width font - private Font fixedWidthFont; - - // Breakpoint setting - // Maps Strings to List/*<LineNumberInfo>*/ - private Map sourceFileToLineNumberInfoMap; - // Maps Strings (file names) to Sets of Integers (line numbers) - private Map fileToBreakpointMap; - - // Debug events - private javax.swing.Timer debugEventTimer; - - // Java debug events - private boolean javaEventPending; - - static class BreakpointResult { - private boolean success; - private boolean set; - private int lineNo; - private String why; - - /** For positive results */ - BreakpointResult(boolean success, boolean set, int lineNo) { - this(success, set, lineNo, null); - } - - /** For negative results */ - BreakpointResult(boolean success, boolean set, int lineNo, String why) { - this.success = success; - this.set = set; - this.lineNo = lineNo; - this.why = why; - } - - public boolean succeeded() { - return success; - } - - public boolean set() { - return set; - } - - /** Line at which the breakpoint was actually set; only valid if - succeeded() returns true */ - public int getLine() { - return lineNo; - } - - public String getWhy() { - return why; - } - } - - - // Editors for source code. File name-to-Editor mapping. - private Map editors; - private EditorFactory editorFact = new DefaultEditorFactory(); - private EditorCommands editorComm = new EditorCommands() { - public void windowClosed(Editor editor) { - editors.remove(editor.getSourceFileName()); - } - - public void toggleBreakpointAtLine(Editor editor, int lineNumber) { - // FIXME: handle "lazy" breakpoints where the source file has - // been opened with some other mechanism (File -> Open) and we - // don't have debug information pointing to that file yet - // FIXME: NOT FINISHED - - BreakpointResult res = - handleBreakpointToggle(editor, lineNumber); - if (res.succeeded()) { - if (res.set()) { - editor.showBreakpointAtLine(res.getLine()); - } else { - editor.clearBreakpointAtLine(res.getLine()); - } - } else { - String why = res.getWhy(); - if (why == null) { - why = ""; - } else { - why = ": " + why; - } - showMessageDialog("Unable to toggle breakpoint" + why, - "Unable to toggle breakpoint", - JOptionPane.WARNING_MESSAGE); - } - } - }; - - private void attach(final int pid) { - try { - getAgent().attach(pid); - setMenuItemsEnabled(detachMenuItems, true); - setMenuItemsEnabled(suspendDebugMenuItems, false); - setMenuItemsEnabled(resumeDebugMenuItems, true); - debugMenu.setEnabled(true); - attached = true; - suspended = true; - - if (getAgent().isJavaMode()) { - System.err.println("Java HotSpot(TM) virtual machine detected."); - } else { - System.err.println("(No Java(TM) virtual machine detected)"); - } - - // Set up editor map - editors = new HashMap(); - - // Initialize breakpoints - fileToBreakpointMap = new HashMap(); - - // Create combined stack trace and local variable panel - JPanel framePanel = new JPanel(); - framePanel.setLayout(new BorderLayout()); - framePanel.setBorder(GraphicsUtilities.newBorder(5)); - localsPanel = new VariablePanel(); - JTabbedPane tab = new JTabbedPane(); - tab.addTab("Locals", localsPanel); - tab.setTabPlacement(JTabbedPane.BOTTOM); - framePanel.add(tab, BorderLayout.CENTER); - JPanel stackPanel = new JPanel(); - stackPanel.setLayout(new BoxLayout(stackPanel, BoxLayout.X_AXIS)); - stackPanel.add(new JLabel("Context:")); - stackPanel.add(Box.createHorizontalStrut(5)); - stackTracePanel = new StackTracePanel(); - stackTracePanel.addListener(new StackTracePanel.Listener() { - public void frameChanged(CFrame fr, JavaVFrame jfr) { - setCurrentFrame(fr, jfr); - } - }); - stackPanel.add(stackTracePanel); - framePanel.add(stackPanel, BorderLayout.NORTH); - stackFrame = newFrame("Stack"); - stackFrame.getContentPane().setLayout(new BorderLayout()); - stackFrame.getContentPane().add(framePanel, BorderLayout.CENTER); - stackFrame.setResizable(true); - stackFrame.setClosable(false); - addFrame(stackFrame); - stackFrame.setSize(400, 200); - GraphicsUtilities.moveToInContainer(stackFrame.getComponent(), 0.0f, 1.0f, 0, 20); - stackFrame.setVisible(true); - - // Create register panel - registerPanel = new RegisterPanel(); - registerPanel.setFont(fixedWidthFont); - registerFrame = newFrame("Registers"); - registerFrame.getContentPane().setLayout(new BorderLayout()); - registerFrame.getContentPane().add(registerPanel, BorderLayout.CENTER); - addFrame(registerFrame); - registerFrame.setResizable(true); - registerFrame.setClosable(false); - registerFrame.setSize(225, 200); - GraphicsUtilities.moveToInContainer(registerFrame.getComponent(), - 1.0f, 0.0f, 0, 0); - registerFrame.setVisible(true); - - resetCurrentThread(); - } catch (DebuggerException e) { - final String errMsg = formatMessage(e.getMessage(), 80); - setMenuItemsEnabled(attachMenuItems, true); - showMessageDialog("Unable to connect to process ID " + pid + ":\n\n" + errMsg, - "Unable to Connect", - JOptionPane.WARNING_MESSAGE); - getAgent().detach(); - } - } - - private synchronized void detachDebugger() { - if (!attached) { - return; - } - if (isSuspended()) { - resume(); // Necessary for JVMDI resumption - } - getAgent().detach(); - // FIXME: clear out breakpoints (both Java and C/C++) from target - // process - sourceFileToLineNumberInfoMap = null; - fileToBreakpointMap = null; - threadToJavaThreadMap = null; - editors = null; - attached = false; - } - - private synchronized void detach() { - detachDebugger(); - setMenuItemsEnabled(attachMenuItems, true); - setMenuItemsEnabled(detachMenuItems, false); - debugMenu.setEnabled(false); - if (mdiMode) { - // FIXME: is this sufficient, or will I have to do anything else - // to the components to kill them off? What about WorkerThreads? - desktop.removeAll(); - desktop.invalidate(); - desktop.validate(); - desktop.repaint(); - } - // FIXME: keep track of all windows and close them even in non-MDI - // mode - debugEventTimer.stop(); - } - - // Returns a Debugger for processes on the local machine. This is - // only used to fetch the process list. - private Debugger getLocalDebugger() { - if (localDebugger == null) { - String os = PlatformInfo.getOS(); - String cpu = PlatformInfo.getCPU(); - - if (os.equals("win32")) { - if (!cpu.equals("x86")) { - throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Windows"); - } - - localDebugger = new WindbgDebuggerLocal(new MachineDescriptionIntelX86(), true); - } else if (os.equals("linux")) { - if (!cpu.equals("x86")) { - throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Linux"); - } - - // FIXME: figure out how to specify path to debugger module - throw new RuntimeException("FIXME: figure out how to specify path to debugger module"); - // localDebugger = new PosixDebuggerLocal(new MachineDescriptionIntelX86(), true); - } else { - // FIXME: port to Solaris - throw new DebuggerException("Unsupported OS \"" + os + "\""); - } - - // FIXME: we require that the primitive type sizes be configured - // in order to use basic functionality in class Address such as - // the fetching of floating-point values. There are a lot of - // assumptions in the current code that Java floats and doubles - // are of equivalent size to C values. The configurability of the - // primitive type sizes hasn't seemed necessary and in this kind - // of debugging scenario (namely, debugging arbitrary C++ - // processes) it appears difficult to support that kind of - // flexibility. - localDebugger.configureJavaPrimitiveTypeSizes(1, 1, 2, 8, 4, 4, 8, 2); - } - - return localDebugger; - } - - private BugSpotAgent getAgent() { - return agent; - } - - private Debugger getDebugger() { - return getAgent().getDebugger(); - } - - private CDebugger getCDebugger() { - return getAgent().getCDebugger(); - } - - private void resetCurrentThread() { - setCurrentThread((ThreadProxy) getCDebugger().getThreadList().get(0)); - } - - private void setCurrentThread(ThreadProxy t) { - // Create stack trace - // FIXME: add ability to intermix C/Java frames - java.util.List trace = new ArrayList(); - CFrame fr = getCDebugger().topFrameForThread(t); - while (fr != null) { - trace.add(new StackTraceEntry(fr, getCDebugger())); - try { - fr = fr.sender(t); - } catch (AddressException e) { - e.printStackTrace(); - showMessageDialog("Error while walking stack; stack trace will be truncated\n(see console for details)", - "Error walking stack", - JOptionPane.WARNING_MESSAGE); - fr = null; - } - } - JavaThread jthread = javaThreadForProxy(t); - if (jthread != null) { - // Java mode, and we have a Java thread. - // Find all Java frames on the stack. We currently do this in a - // manner which involves minimal interaction between the Java - // and C/C++ debugging systems: any C frame which has a PC in an - // unknown location (i.e., not in any DSO) is assumed to be a - // Java frame. We merge stack segments of unknown frames with - // segments of Java frames beginning with native methods. - java.util.List javaTrace = new ArrayList(); - VFrame vf = jthread.getLastJavaVFrameDbg(); - while (vf != null) { - if (vf.isJavaFrame()) { - javaTrace.add(new StackTraceEntry((JavaVFrame) vf)); - vf = vf.sender(); - } - } - // Merge stack traces - java.util.List mergedTrace = new ArrayList(); - int c = 0; - int j = 0; - while (c < trace.size()) { - StackTraceEntry entry = (StackTraceEntry) trace.get(c); - if (entry.isUnknownCFrame()) { - boolean gotJavaFrame = false; - while (j < javaTrace.size()) { - StackTraceEntry javaEntry = (StackTraceEntry) javaTrace.get(j); - JavaVFrame jvf = javaEntry.getJavaFrame(); - Method m = jvf.getMethod(); - if (!m.isNative() || !gotJavaFrame) { - gotJavaFrame = true; - mergedTrace.add(javaEntry); - ++j; - } else { - break; // Reached native method; have intervening C frames - } - } - if (gotJavaFrame) { - // Skip this sequence of unknown frames, as we've - // successfully identified it as Java frames - while (c < trace.size() && entry.isUnknownCFrame()) { - ++c; - if (c < trace.size()) { - entry = (StackTraceEntry) trace.get(c); - } - } - continue; - } - } - // If we get here, we either have an unknown frame we didn't - // know how to categorize or we have a known C frame. Add it - // to the trace. - mergedTrace.add(entry); - ++c; - } - trace = mergedTrace; - } - stackTracePanel.setTrace(trace); - - registerPanel.update(t); - } - - private void setCurrentFrame(CFrame fr, JavaVFrame jfr) { - localsPanel.clear(); - - if (fr != null) { - localsPanel.update(fr); - - // FIXME: load source file if we can find it, otherwise display disassembly - LoadObject lo = getCDebugger().loadObjectContainingPC(fr.pc()); - if (lo != null) { - CDebugInfoDataBase db = lo.getDebugInfoDataBase(); - if (db != null) { - LineNumberInfo info = db.lineNumberForPC(fr.pc()); - if (info != null) { - System.err.println("PC " + fr.pc() + ": Source file \"" + - info.getSourceFileName() + - "\", line number " + - info.getLineNumber() + - ", PC range [" + - info.getStartPC() + - ", " + - info.getEndPC() + - ")"); - // OK, here we go... - showLineNumber(null, info.getSourceFileName(), info.getLineNumber()); - } else { - System.err.println("(No line number information for PC " + fr.pc() + ")"); - // Dump line number information for database - db.iterate(new LineNumberVisitor() { - public void doLineNumber(LineNumberInfo info) { - System.err.println(" Source file \"" + - info.getSourceFileName() + - "\", line number " + - info.getLineNumber() + - ", PC range [" + - info.getStartPC() + - ", " + - info.getEndPC() + - ")"); - } - }); - } - } - } - } else { - if (Assert.ASSERTS_ENABLED) { - Assert.that(jfr != null, "Must have either C or Java frame"); - } - localsPanel.update(jfr); - // See whether we can locate source file and line number - // FIXME: infer pathmap entries from user's locating of this - // source file - // FIXME: figure out what to do for native methods. Possible to - // go to line number for the native method declaration? - Method m = jfr.getMethod(); - Symbol sfn = ((InstanceKlass) m.getMethodHolder()).getSourceFileName(); - if (sfn != null) { - int bci = jfr.getBCI(); - int lineNo = m.getLineNumberFromBCI(bci); - if (lineNo >= 0) { - // FIXME: show disassembly otherwise - showLineNumber(packageName(m.getMethodHolder().getName().asString()), - sfn.asString(), lineNo); - } - } - } - } - - private String packageName(String str) { - int idx = str.lastIndexOf('/'); - if (idx < 0) { - return ""; - } - return str.substring(0, idx).replace('/', '.'); - } - - private JavaThread javaThreadForProxy(ThreadProxy t) { - if (!getAgent().isJavaMode()) { - return null; - } - if (threadToJavaThreadMap == null) { - threadToJavaThreadMap = new HashMap(); - Threads threads = VM.getVM().getThreads(); - for (JavaThread thr = threads.first(); thr != null; thr = thr.next()) { - threadToJavaThreadMap.put(thr.getThreadProxy(), thr); - } - } - return (JavaThread) threadToJavaThreadMap.get(t); - } - - private static JMenu createMenu(String name, char mnemonic, int mnemonicPos) { - JMenu menu = new JMenu(name); - menu.setMnemonic(mnemonic); - menu.setDisplayedMnemonicIndex(mnemonicPos); - return menu; - } - - private static JMenuItem createMenuItem(String name, ActionListener l) { - JMenuItem item = new JMenuItem(name); - item.addActionListener(l); - return item; - } - - private static JMenuItem createMenuItemInternal(String name, ActionListener l, int accelerator, int modifiers) { - JMenuItem item = createMenuItem(name, l); - item.setAccelerator(KeyStroke.getKeyStroke(accelerator, modifiers)); - return item; - } - - private static JMenuItem createMenuItem(String name, ActionListener l, int accelerator) { - return createMenuItemInternal(name, l, accelerator, 0); - } - - private static JMenuItem createMenuItem(String name, ActionListener l, char mnemonic, int mnemonicPos) { - JMenuItem item = createMenuItem(name, l); - item.setMnemonic(mnemonic); - item.setDisplayedMnemonicIndex(mnemonicPos); - return item; - } - - private static JMenuItem createMenuItem(String name, - ActionListener l, - int accelerator, - int acceleratorMods, - char mnemonic, - int mnemonicPos) { - JMenuItem item = createMenuItemInternal(name, l, accelerator, acceleratorMods); - item.setMnemonic(mnemonic); - item.setDisplayedMnemonicIndex(mnemonicPos); - return item; - } - - /** Punctuates the given string with \n's where necessary to not - exceed the given number of characters per line. Strips - extraneous whitespace. */ - private static String formatMessage(String message, int charsPerLine) { - StringBuffer buf = new StringBuffer(message.length()); - StringTokenizer tokenizer = new StringTokenizer(message); - int curLineLength = 0; - while (tokenizer.hasMoreTokens()) { - String tok = tokenizer.nextToken(); - if (curLineLength + tok.length() > charsPerLine) { - buf.append('\n'); - curLineLength = 0; - } else { - if (curLineLength != 0) { - buf.append(' '); - ++curLineLength; - } - } - buf.append(tok); - curLineLength += tok.length(); - } - return buf.toString(); - } - - private void setMenuItemsEnabled(java.util.List items, boolean enabled) { - for (Iterator iter = items.iterator(); iter.hasNext(); ) { - ((JMenuItem) iter.next()).setEnabled(enabled); - } - } - - private void showMessageDialog(final String message, final String title, final int jOptionPaneKind) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - if (mdiMode) { - JOptionPane.showInternalMessageDialog(desktop, message, title, jOptionPaneKind); - } else { - JOptionPane.showMessageDialog(null, message, title, jOptionPaneKind); - } - } - }); - } - - private FrameWrapper newFrame(String title) { - if (mdiMode) { - return new JInternalFrameWrapper(new JInternalFrame(title)); - } else { - return new JFrameWrapper(new JFrame(title)); - } - } - - private void addFrame(FrameWrapper frame) { - if (mdiMode) { - desktop.add(frame.getComponent()); - } - } - - private void removeFrame(FrameWrapper frame) { - if (mdiMode) { - desktop.remove(frame.getComponent()); - desktop.invalidate(); - desktop.validate(); - desktop.repaint(); - } - // FIXME: do something when not in MDI mode - } - - private Dimension getParentDimension(Component c) { - if (mdiMode) { - return desktop.getSize(); - } else { - return Toolkit.getDefaultToolkit().getScreenSize(); - } - } - - // Default editor implementation - class DefaultEditor implements Editor { - private DefaultEditorFactory factory; - private FrameWrapper editorFrame; - private String filename; - private SourceCodePanel code; - private boolean shown; - private Object userData; - - public DefaultEditor(DefaultEditorFactory fact, String filename, final EditorCommands comm) { - this.filename = filename; - this.factory = fact; - editorFrame = newFrame(filename); - code = new SourceCodePanel(); - // FIXME: when font changes, change font in editors as well - code.setFont(fixedWidthFont); - editorFrame.getContentPane().add(code); - editorFrame.setClosable(true); - editorFrame.setResizable(true); - editorFrame.setClosingActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - comm.windowClosed(DefaultEditor.this); - removeFrame(editorFrame); - editorFrame.dispose(); - factory.editorClosed(DefaultEditor.this); - } - }); - editorFrame.setActivatedActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - factory.makeEditorCurrent(DefaultEditor.this); - code.requestFocus(); - } - }); - code.setEditorCommands(comm, this); - } - - public boolean openFile() { return code.openFile(filename); } - public String getSourceFileName() { return filename; } - public int getCurrentLineNumber() { return code.getCurrentLineNumber(); } - public void showLineNumber(int lineNo) { - if (!shown) { - addFrame(editorFrame); - GraphicsUtilities.reshapeToAspectRatio(editorFrame.getComponent(), - 1.0f, - 0.85f, - getParentDimension(editorFrame.getComponent())); - editorFrame.setVisible(true); - shown = true; - } - code.showLineNumber(lineNo); - editorFrame.toFront(); - } - public void highlightLineNumber(int lineNo) { code.highlightLineNumber(lineNo); } - public void showBreakpointAtLine(int lineNo) { code.showBreakpointAtLine(lineNo); } - public boolean hasBreakpointAtLine(int lineNo) { return code.hasBreakpointAtLine(lineNo); } - public void clearBreakpointAtLine(int lineNo) { code.clearBreakpointAtLine(lineNo); } - public void clearBreakpoints() { code.clearBreakpoints(); } - public void setUserData(Object o) { userData = o; } - public Object getUserData() { return userData; } - public void toFront() { editorFrame.toFront(); - factory.makeEditorCurrent(this); } - } - - class DefaultEditorFactory implements EditorFactory { - private LinkedList/*<Editor>*/ editors = new LinkedList(); - - public Editor openFile(String filename, EditorCommands commands) { - DefaultEditor editor = new DefaultEditor(this, filename, editorComm); - if (!editor.openFile()) { - return null; - } - return editor; - } - - public Editor getCurrentEditor() { - if (editors.isEmpty()) { - return null; - } - return (Editor) editors.getFirst(); - } - - void editorClosed(Editor editor) { - editors.remove(editor); - } - - void makeEditorCurrent(Editor editor) { - editors.remove(editor); - editors.addFirst(editor); - } - } - - // Helper class for loading .java files; show only those with - // correct file name which are also in the correct package - static class JavaFileFilter extends javax.swing.filechooser.FileFilter { - private String packageName; - private String fileName; - - JavaFileFilter(String packageName, String fileName) { - this.packageName = packageName; - this.fileName = fileName; - } - - public boolean accept(File f) { - if (f.isDirectory()) { - return true; - } - // This rejects most files - if (!f.getName().equals(fileName)) { - return false; - } - // Ensure selected file is in the correct package - PackageScanner scanner = new PackageScanner(); - String pkg = scanner.scan(f); - if (!pkg.equals(packageName)) { - return false; - } - return true; - } - - public String getDescription() { return "Java source files"; } - } - - // Auxiliary information used only for Java source files - static class JavaUserData { - private String packageName; // External format - private String sourceFileName; - - /** Source file name is equivalent to that found in the .java - file; i.e., not a full path */ - JavaUserData(String packageName, String sourceFileName) { - this.packageName = packageName; - this.sourceFileName = sourceFileName; - } - - String packageName() { return packageName; } - String sourceFileName() { return sourceFileName; } - } - - // Opens a source file. This makes it available for the setting of - // lazy breakpoints. - private void openSourceFile() { - JFileChooser chooser = new JFileChooser(); - chooser.setDialogTitle("Open source code file"); - chooser.setMultiSelectionEnabled(false); - if (chooser.showOpenDialog(null) != JFileChooser.APPROVE_OPTION) { - return; - } - File chosen = chooser.getSelectedFile(); - if (chosen == null) { - return; - } - - // See whether we have a Java source file. If so, derive a package - // name for it. - String path = chosen.getPath(); - String name = null; - JavaUserData data = null; - if (path.endsWith(".java")) { - PackageScanner scanner = new PackageScanner(); - String pkg = scanner.scan(chosen); - // Now knowing both the package name and file name, we can put - // this in the editor map and use it for setting breakpoints - // later - String fileName = chosen.getName(); - name = pkg + "." + fileName; - data = new JavaUserData(pkg, fileName); - } else { - // FIXME: need pathmap mechanism - name = path; - } - Editor editor = (Editor) editors.get(name); - if (editor == null) { - editor = editorFact.openFile(path, editorComm); - if (editor == null) { - showMessageDialog("Unable to open file \"" + path + "\" -- unexpected error.", - "Unable to open file", - JOptionPane.WARNING_MESSAGE); - return; - } - editors.put(name, editor); - if (data != null) { - editor.setUserData(data); - } - } else { - editor.toFront(); - } - editor.showLineNumber(1); - // Show breakpoints as well if we have any for this file - Set set = (Set) fileToBreakpointMap.get(editor.getSourceFileName()); - if (set != null) { - for (Iterator iter = set.iterator(); iter.hasNext(); ) { - editor.showBreakpointAtLine(((Integer) iter.next()).intValue()); - } - } - } - - // Package name may be null, in which case the file is assumed to be - // a C source file. Otherwise it is assumed to be a Java source file - // and certain filtering rules will be applied. - private void showLineNumber(String packageName, String fileName, int lineNumber) { - String name; - if (packageName == null) { - name = fileName; - } else { - name = packageName + "." + fileName; - } - Editor editor = (Editor) editors.get(name); - if (editor == null) { - // See whether file exists - File file = new File(fileName); - String realFileName = fileName; - if (!file.exists()) { - // User must specify path to file - JFileChooser chooser = new JFileChooser(); - chooser.setDialogTitle("Please locate " + fileName); - chooser.setMultiSelectionEnabled(false); - if (packageName != null) { - chooser.setFileFilter(new JavaFileFilter(packageName, fileName)); - } - int res = chooser.showOpenDialog(null); - if (res != JFileChooser.APPROVE_OPTION) { - // FIXME: show disassembly instead - return; - } - // FIXME: would like to infer more from the selection; i.e., - // a pathmap leading up to this file - File chosen = chooser.getSelectedFile(); - if (chosen == null) { - return; - } - realFileName = chosen.getPath(); - } - // Now instruct editor factory to open file - editor = editorFact.openFile(realFileName, editorComm); - if (editor == null) { - showMessageDialog("Unable to open file \"" + realFileName + "\" -- unexpected error.", - "Unable to open file", - JOptionPane.WARNING_MESSAGE); - return; - } - // Got an editor; put it in map - editors.put(name, editor); - // If Java source file, add additional information for later - if (packageName != null) { - editor.setUserData(new JavaUserData(packageName, fileName)); - } - } - // Got editor; show line - editor.showLineNumber(lineNumber); - editor.highlightLineNumber(lineNumber); - // Show breakpoints as well if we have any for this file - Set set = (Set) fileToBreakpointMap.get(editor.getSourceFileName()); - if (set != null) { - for (Iterator iter = set.iterator(); iter.hasNext(); ) { - editor.showBreakpointAtLine(((Integer) iter.next()).intValue()); - } - } - } - - // - // Suspend/resume - // - - private boolean isSuspended() { - return suspended; - } - - private synchronized void suspend() { - setMenuItemsEnabled(resumeDebugMenuItems, true); - setMenuItemsEnabled(suspendDebugMenuItems, false); - BugSpotAgent agent = getAgent(); - if (agent.canInteractWithJava() && !agent.isJavaSuspended()) { - agent.suspendJava(); - } - agent.suspend(); - // FIXME: call VM.getVM().fireVMSuspended() - resetCurrentThread(); - debugEventTimer.stop(); - suspended = true; - } - - private synchronized void resume() { - // Note: we don't wipe out the cached state like the - // sourceFileToLineNumberInfoMap since it is too expensive to - // recompute. Instead we recompute it if any DLLs are loaded or - // unloaded. - threadToJavaThreadMap = null; - setMenuItemsEnabled(resumeDebugMenuItems, false); - setMenuItemsEnabled(suspendDebugMenuItems, true); - registerPanel.clear(); - // FIXME: call VM.getVM().fireVMResumed() - BugSpotAgent agent = getAgent(); - agent.resume(); - if (agent.canInteractWithJava()) { - if (agent.isJavaSuspended()) { - agent.resumeJava(); - } - if (javaEventPending) { - javaEventPending = false; - // Clear it out before resuming polling for events - agent.javaEventContinue(); - } - } - agent.enableJavaInteraction(); - suspended = false; - debugEventTimer.start(); - } - - // - // Breakpoints - // - - private synchronized BreakpointResult handleBreakpointToggle(Editor editor, int lineNumber) { - // Currently we only use user data in editors to indicate Java - // source files. If this changes then this code will need to - // change. - JavaUserData data = (JavaUserData) editor.getUserData(); - String filename = editor.getSourceFileName(); - if (data == null) { - // C/C++ code - // FIXME: as noted above in EditorCommands.toggleBreakpointAtLine, - // this needs more work to handle "lazy" breakpoints in files - // which we don't know about in the debug information yet - CDebugger dbg = getCDebugger(); - ProcessControl prctl = dbg.getProcessControl(); - if (prctl == null) { - return new BreakpointResult(false, false, 0, "Process control not enabled"); - } - boolean mustSuspendAndResume = (!prctl.isSuspended()); - try { - if (mustSuspendAndResume) { - prctl.suspend(); - } - // Search debug info for all DSOs - LineNumberInfo info = getLineNumberInfo(filename, lineNumber); - if (info != null) { - Set bpset = (Set) fileToBreakpointMap.get(filename); - if (bpset == null) { - bpset = new HashSet(); - fileToBreakpointMap.put(filename, bpset); - } - Integer key = new Integer(info.getLineNumber()); - if (bpset.contains(key)) { - // Clear breakpoint at this line's PC - prctl.clearBreakpoint(info.getStartPC()); - bpset.remove(key); - return new BreakpointResult(true, false, info.getLineNumber()); - } else { - // Set breakpoint at this line's PC - System.err.println("Setting breakpoint at PC " + info.getStartPC()); - prctl.setBreakpoint(info.getStartPC()); - bpset.add(key); - return new BreakpointResult(true, true, info.getLineNumber()); - } - } else { - return new BreakpointResult(false, false, 0, "No debug information for this source file and line"); - } - } finally { - if (mustSuspendAndResume) { - prctl.resume(); - } - } - } else { - BugSpotAgent agent = getAgent(); - if (!agent.canInteractWithJava()) { - String why; - if (agent.isJavaInteractionDisabled()) { - why = "Can not toggle Java breakpoints while stopped because\nof C/C++ debug events (breakpoints, single-stepping)"; - } else { - why = "Could not talk to SA's JVMDI module to enable Java\nprogramming language breakpoints (run with -Xdebug -Xrunsa)"; - } - return new BreakpointResult(false, false, 0, why); - } - Set bpset = (Set) fileToBreakpointMap.get(filename); - if (bpset == null) { - bpset = new HashSet(); - fileToBreakpointMap.put(filename, bpset); - } - boolean mustResumeAndSuspend = isSuspended(); - try { - if (mustResumeAndSuspend) { - agent.resume(); - } - ServiceabilityAgentJVMDIModule.BreakpointToggleResult res = - getAgent().toggleJavaBreakpoint(data.sourceFileName(), - data.packageName(), - lineNumber); - if (res.getSuccess()) { - Integer key = new Integer(res.getLineNumber()); - boolean addRemRes = false; - if (res.getWasSet()) { - addRemRes = bpset.add(key); - System.err.println("Setting breakpoint at " + res.getMethodName() + res.getMethodSignature() + - ", bci " + res.getBCI() + ", line " + res.getLineNumber()); - } else { - addRemRes = bpset.remove(key); - System.err.println("Clearing breakpoint at " + res.getMethodName() + res.getMethodSignature() + - ", bci " + res.getBCI() + ", line " + res.getLineNumber()); - } - if (Assert.ASSERTS_ENABLED) { - Assert.that(addRemRes, "Inconsistent Java breakpoint state with respect to target process"); - } - return new BreakpointResult(true, res.getWasSet(), res.getLineNumber()); - } else { - return new BreakpointResult(false, false, 0, res.getErrMsg()); - } - } finally { - if (mustResumeAndSuspend) { - agent.suspend(); - resetCurrentThread(); - } - } - } - } - - // Must call only when suspended - private LineNumberInfo getLineNumberInfo(String filename, int lineNumber) { - Map map = getSourceFileToLineNumberInfoMap(); - java.util.List infos = (java.util.List) map.get(filename); - if (infos == null) { - return null; - } - // Binary search for line number - return searchLineNumbers(infos, lineNumber, 0, infos.size()); - } - - // Must call only when suspended - private Map getSourceFileToLineNumberInfoMap() { - if (sourceFileToLineNumberInfoMap == null) { - // Build from debug info - java.util.List loadObjects = getCDebugger().getLoadObjectList(); - final Map map = new HashMap(); - for (Iterator iter = loadObjects.iterator(); iter.hasNext(); ) { - LoadObject lo = (LoadObject) iter.next(); - CDebugInfoDataBase db = lo.getDebugInfoDataBase(); - if (db != null) { - db.iterate(new LineNumberVisitor() { - public void doLineNumber(LineNumberInfo info) { - String name = info.getSourceFileName(); - if (name != null) { - java.util.List val = (java.util.List) map.get(name); - if (val == null) { - val = new ArrayList(); - map.put(name, val); - } - val.add(info); - } - } - }); - } - } - // Sort all lists - for (Iterator iter = map.values().iterator(); iter.hasNext(); ) { - java.util.List list = (java.util.List) iter.next(); - Collections.sort(list, new Comparator() { - public int compare(Object o1, Object o2) { - LineNumberInfo l1 = (LineNumberInfo) o1; - LineNumberInfo l2 = (LineNumberInfo) o2; - int n1 = l1.getLineNumber(); - int n2 = l2.getLineNumber(); - if (n1 < n2) return -1; - if (n1 == n2) return 0; - return 1; - } - }); - } - sourceFileToLineNumberInfoMap = map; - } - return sourceFileToLineNumberInfoMap; - } - - private LineNumberInfo searchLineNumbers(java.util.List infoList, int lineNo, int lowIdx, int highIdx) { - if (highIdx < lowIdx) return null; - if (lowIdx == highIdx) { - // Base case: see whether start PC is less than or equal to addr - if (checkLineNumber(infoList, lineNo, lowIdx)) { - return (LineNumberInfo) infoList.get(lowIdx); - } else { - return null; - } - } else if (lowIdx == highIdx - 1) { - if (checkLineNumber(infoList, lineNo, lowIdx)) { - return (LineNumberInfo) infoList.get(lowIdx); - } else if (checkLineNumber(infoList, lineNo, highIdx)) { - return (LineNumberInfo) infoList.get(highIdx); - } else { - return null; - } - } - int midIdx = (lowIdx + highIdx) >> 1; - LineNumberInfo info = (LineNumberInfo) infoList.get(midIdx); - if (lineNo < info.getLineNumber()) { - // Always move search down - return searchLineNumbers(infoList, lineNo, lowIdx, midIdx); - } else if (lineNo == info.getLineNumber()) { - return info; - } else { - // Move search up - return searchLineNumbers(infoList, lineNo, midIdx, highIdx); - } - } - - private boolean checkLineNumber(java.util.List infoList, int lineNo, int idx) { - LineNumberInfo info = (LineNumberInfo) infoList.get(idx); - return (info.getLineNumber() >= lineNo); - } - - // - // Debug events - // - - private synchronized void pollForDebugEvent() { - ProcessControl prctl = getCDebugger().getProcessControl(); - if (prctl == null) { - return; - } - DebugEvent ev = prctl.debugEventPoll(); - if (ev != null) { - DebugEvent.Type t = ev.getType(); - if (t == DebugEvent.Type.LOADOBJECT_LOAD || - t == DebugEvent.Type.LOADOBJECT_UNLOAD) { - // Conservatively clear cached debug info state - sourceFileToLineNumberInfoMap = null; - // FIXME: would be very useful to have "stop on load/unload" - // events - // FIXME: must do work at these events to implement lazy - // breakpoints - prctl.debugEventContinue(); - } else if (t == DebugEvent.Type.BREAKPOINT) { - // Note: Visual C++ only notifies on breakpoints it doesn't - // know about - - // FIXME: put back test - // if (!prctl.isBreakpointSet(ev.getPC())) { - showMessageDialog("Breakpoint reached at PC " + ev.getPC(), - "Breakpoint reached", - JOptionPane.INFORMATION_MESSAGE); - // } - agent.disableJavaInteraction(); - suspend(); - prctl.debugEventContinue(); - } else if (t == DebugEvent.Type.SINGLE_STEP) { - agent.disableJavaInteraction(); - suspend(); - prctl.debugEventContinue(); - } else if (t == DebugEvent.Type.ACCESS_VIOLATION) { - showMessageDialog("Access violation attempting to " + - (ev.getWasWrite() ? "write" : "read") + - " address " + ev.getAddress() + - " at PC " + ev.getPC(), - "Access Violation", - JOptionPane.WARNING_MESSAGE); - agent.disableJavaInteraction(); - suspend(); - prctl.debugEventContinue(); - } else { - String info = "Unknown debug event encountered"; - if (ev.getUnknownEventDetail() != null) { - info = info + ": " + ev.getUnknownEventDetail(); - } - showMessageDialog(info, "Unknown debug event", JOptionPane.INFORMATION_MESSAGE); - suspend(); - prctl.debugEventContinue(); - } - return; - } - - // No C++ debug event; poll for Java debug event - if (getAgent().canInteractWithJava()) { - if (!javaEventPending) { - if (getAgent().javaEventPending()) { - suspend(); - // This does a lot of work and we want to have the page - // cache available to us as it runs - sun.jvm.hotspot.livejvm.Event jev = getAgent().javaEventPoll(); - if (jev != null) { - javaEventPending = true; - if (jev.getType() == sun.jvm.hotspot.livejvm.Event.Type.BREAKPOINT) { - BreakpointEvent bpev = (BreakpointEvent) jev; - showMessageDialog("Breakpoint reached in method\n" + - bpev.methodID().method().externalNameAndSignature() + - ",\nbci " + bpev.location(), - "Breakpoint reached", - JOptionPane.INFORMATION_MESSAGE); - } else if (jev.getType() == sun.jvm.hotspot.livejvm.Event.Type.EXCEPTION) { - ExceptionEvent exev = (ExceptionEvent) jev; - showMessageDialog(exev.exception().getKlass().getName().asString() + - "\nthrown in method\n" + - exev.methodID().method().externalNameAndSignature() + - "\nat BCI " + exev.location(), - "Exception thrown", - JOptionPane.INFORMATION_MESSAGE); - } else { - Assert.that(false, "Should not reach here"); - } - } - } - } - } - } -} |