agent/src/share/classes/sun/jvm/hotspot/bugspot/BugSpot.java

Tue, 20 Apr 2010 13:26:33 -0700

author
never
date
Tue, 20 Apr 2010 13:26:33 -0700
changeset 1820
bc32f286fae0
parent 435
a61af66fc99e
child 1907
c18cbe5936b8
permissions
-rw-r--r--

6945219: minor SA fixes
Reviewed-by: twisti

     1 /*
     2  * Copyright 2001-2010 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    20  * CA 95054 USA or visit www.sun.com if you need additional information or
    21  * have any questions.
    22  *
    23  */
    25 package sun.jvm.hotspot.bugspot;
    27 import java.awt.*;
    28 import java.awt.event.*;
    29 import java.io.*;
    30 import java.net.*;
    31 import java.util.*;
    32 import javax.swing.*;
    33 import javax.swing.filechooser.*;
    34 import sun.jvm.hotspot.debugger.*;
    35 import sun.jvm.hotspot.debugger.cdbg.*;
    36 import sun.jvm.hotspot.debugger.posix.*;
    37 import sun.jvm.hotspot.debugger.win32.*;
    38 import sun.jvm.hotspot.livejvm.*;
    39 import sun.jvm.hotspot.memory.*;
    40 import sun.jvm.hotspot.oops.*;
    41 import sun.jvm.hotspot.runtime.*;
    42 import sun.jvm.hotspot.ui.*;
    43 import sun.jvm.hotspot.utilities.*;
    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. */
    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   }
    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   }
    68   /** Indicates whether MDI mode is enabled. */
    69   public boolean getMDIMode() {
    70     return mdiMode;
    71   }
    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());
    78     menuBar = new JMenuBar();
    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();
    86     //
    87     // File menu
    88     //
    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);
   103     menu.addSeparator();
   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);
   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);
   125     // Disable detach menu items at first
   126     setMenuItemsEnabled(detachMenuItems, false);
   128     menu.addSeparator();
   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));
   139     menuBar.add(menu);
   141     //
   142     // Debug menu
   143     //
   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);
   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);
   177     debugMenu.addSeparator();
   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);
   199     debugMenu.setEnabled(false);
   200     menuBar.add(debugMenu);
   202     if (mdiMode) {
   203       desktop = new JDesktopPane();
   204       add(desktop, BorderLayout.CENTER);
   205     }
   207     fixedWidthFont = GraphicsUtilities.lookupFont("Courier");
   209     debugEventTimer = new javax.swing.Timer(100, new ActionListener() {
   210         public void actionPerformed(ActionEvent e) {
   211           pollForDebugEvent();
   212         }
   213       });
   214   }
   216   public JMenuBar getMenuBar() {
   217     return menuBar;
   218   }
   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);
   227     JPanel panel = new JPanel();
   228     panel.setLayout(new BorderLayout());
   229     panel.setBorder(GraphicsUtilities.newBorder(5));
   230     attachDialog.setBackground(panel.getBackground());
   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       });
   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       };
   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);
   290     attachDialog.getContentPane().add(panel, BorderLayout.SOUTH);
   292     addFrame(attachDialog);
   293     attachDialog.pack();
   294     attachDialog.setSize(400, 300);
   295     GraphicsUtilities.centerInContainer(attachDialog.getComponent(),
   296                                         getParentDimension(attachDialog.getComponent()));
   297     attachDialog.setVisible(true);
   298   }
   300   public void showThreadsDialog() {
   301     final FrameWrapper threadsDialog = newFrame("Threads");
   302     threadsDialog.getContentPane().setLayout(new BorderLayout());
   303     threadsDialog.setClosable(true);
   304     threadsDialog.setResizable(true);
   306     ThreadListPanel threads = new ThreadListPanel(getCDebugger(), getAgent().isJavaMode());
   307     threads.addListener(new ThreadListPanel.Listener() {
   308         public void setFocus(ThreadProxy thread, JavaThread jthread) {
   309           setCurrentThread(thread);
   310           // FIXME: print this to GUI, bring some windows to foreground
   311           System.err.println("Focus changed to thread " + thread);
   312         }
   313       });
   314     threads.setBorder(GraphicsUtilities.newBorder(5));
   315     threadsDialog.getContentPane().add(threads);
   316     addFrame(threadsDialog);
   317     threadsDialog.pack();
   318     GraphicsUtilities.reshapeToAspectRatio(threadsDialog.getComponent(),
   319                                            3.0f,
   320                                            0.9f,
   321                                            getParentDimension(threadsDialog.getComponent()));
   322     GraphicsUtilities.centerInContainer(threadsDialog.getComponent(),
   323                                         getParentDimension(threadsDialog.getComponent()));
   324     threadsDialog.setVisible(true);
   325   }
   327   public void showMemoryDialog() {
   328     final FrameWrapper memoryDialog = newFrame("Memory");
   329     memoryDialog.getContentPane().setLayout(new BorderLayout());
   330     memoryDialog.setClosable(true);
   331     memoryDialog.setResizable(true);
   333     memoryDialog.getContentPane().add(new MemoryViewer(getDebugger(),
   334                                                        (getDebugger().getMachineDescription().getAddressSize() == 8)),
   335                                       BorderLayout.CENTER);
   336     addFrame(memoryDialog);
   337     memoryDialog.pack();
   338     GraphicsUtilities.reshapeToAspectRatio(memoryDialog.getComponent(),
   339                                            1.0f,
   340                                            0.7f,
   341                                            getParentDimension(memoryDialog.getComponent()));
   342     GraphicsUtilities.centerInContainer(memoryDialog.getComponent(),
   343                                         getParentDimension(memoryDialog.getComponent()));
   344     memoryDialog.setVisible(true);
   345   }
   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   }
   358   //----------------------------------------------------------------------
   359   // Internals only below this point
   360   //
   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;
   381   private JMenu debugMenu;
   383   // MDI mode only: desktop pane
   384   private JDesktopPane desktop;
   386   // Attach/detach state
   387   private boolean attached;
   389   // Suspension (combined Java/C++) state
   390   private boolean suspended;
   392   // Fixed-width font
   393   private Font fixedWidthFont;
   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;
   401   // Debug events
   402   private javax.swing.Timer debugEventTimer;
   404   // Java debug events
   405   private boolean javaEventPending;
   407   static class BreakpointResult {
   408     private boolean success;
   409     private boolean set;
   410     private int lineNo;
   411     private String why;
   413     /** For positive results */
   414     BreakpointResult(boolean success, boolean set, int lineNo) {
   415       this(success, set, lineNo, null);
   416     }
   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     }
   426     public boolean succeeded() {
   427       return success;
   428     }
   430     public boolean set() {
   431       return set;
   432     }
   434     /** Line at which the breakpoint was actually set; only valid if
   435         succeeded() returns true */
   436     public int getLine() {
   437       return lineNo;
   438     }
   440     public String getWhy() {
   441       return why;
   442     }
   443   }
   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       }
   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
   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     };
   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;
   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       }
   498       // Set up editor map
   499       editors = new HashMap();
   501       // Initialize breakpoints
   502       fileToBreakpointMap = new HashMap();
   504       // Create combined stack trace and local variable panel
   505       JPanel framePanel = new JPanel();
   506       framePanel.setLayout(new BorderLayout());
   507       framePanel.setBorder(GraphicsUtilities.newBorder(5));
   508       localsPanel = new VariablePanel();
   509       JTabbedPane tab = new JTabbedPane();
   510       tab.addTab("Locals", localsPanel);
   511       tab.setTabPlacement(JTabbedPane.BOTTOM);
   512       framePanel.add(tab, BorderLayout.CENTER);
   513       JPanel stackPanel = new JPanel();
   514       stackPanel.setLayout(new BoxLayout(stackPanel, BoxLayout.X_AXIS));
   515       stackPanel.add(new JLabel("Context:"));
   516       stackPanel.add(Box.createHorizontalStrut(5));
   517       stackTracePanel = new StackTracePanel();
   518       stackTracePanel.addListener(new StackTracePanel.Listener() {
   519           public void frameChanged(CFrame fr, JavaVFrame jfr) {
   520             setCurrentFrame(fr, jfr);
   521           }
   522         });
   523       stackPanel.add(stackTracePanel);
   524       framePanel.add(stackPanel, BorderLayout.NORTH);
   525       stackFrame = newFrame("Stack");
   526       stackFrame.getContentPane().setLayout(new BorderLayout());
   527       stackFrame.getContentPane().add(framePanel, BorderLayout.CENTER);
   528       stackFrame.setResizable(true);
   529       stackFrame.setClosable(false);
   530       addFrame(stackFrame);
   531       stackFrame.setSize(400, 200);
   532       GraphicsUtilities.moveToInContainer(stackFrame.getComponent(), 0.0f, 1.0f, 0, 20);
   533       stackFrame.setVisible(true);
   535       // Create register panel
   536       registerPanel = new RegisterPanel();
   537       registerPanel.setFont(fixedWidthFont);
   538       registerFrame = newFrame("Registers");
   539       registerFrame.getContentPane().setLayout(new BorderLayout());
   540       registerFrame.getContentPane().add(registerPanel, BorderLayout.CENTER);
   541       addFrame(registerFrame);
   542       registerFrame.setResizable(true);
   543       registerFrame.setClosable(false);
   544       registerFrame.setSize(225, 200);
   545       GraphicsUtilities.moveToInContainer(registerFrame.getComponent(),
   546                                           1.0f, 0.0f, 0, 0);
   547       registerFrame.setVisible(true);
   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   }
   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   }
   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   }
   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();
   602       if (os.equals("win32")) {
   603         if (!cpu.equals("x86")) {
   604           throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Windows");
   605         }
   607         localDebugger = new Win32DebuggerLocal(new MachineDescriptionIntelX86(), true);
   608       } else if (os.equals("linux")) {
   609         if (!cpu.equals("x86")) {
   610           throw new DebuggerException("Unsupported CPU \"" + cpu + "\" for Linux");
   611         }
   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       }
   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     }
   633     return localDebugger;
   634   }
   636   private BugSpotAgent getAgent() {
   637     return agent;
   638   }
   640   private Debugger getDebugger() {
   641     return getAgent().getDebugger();
   642   }
   644   private CDebugger getCDebugger() {
   645     return getAgent().getCDebugger();
   646   }
   648   private void resetCurrentThread() {
   649     setCurrentThread((ThreadProxy) getCDebugger().getThreadList().get(0));
   650   }
   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);
   728     registerPanel.update(t);
   729   }
   731   private void setCurrentFrame(CFrame fr, JavaVFrame jfr) {
   732     localsPanel.clear();
   734     if (fr != null) {
   735       localsPanel.update(fr);
   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   }
   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   }
   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   }
   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   }
   827   private static JMenuItem createMenuItem(String name, ActionListener l) {
   828     JMenuItem item = new JMenuItem(name);
   829     item.addActionListener(l);
   830     return item;
   831   }
   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   }
   839   private static JMenuItem createMenuItem(String name, ActionListener l, int accelerator) {
   840     return createMenuItemInternal(name, l, accelerator, 0);
   841   }
   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   }
   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   }
   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   }
   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   }
   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   }
   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   }
   912   private void addFrame(FrameWrapper frame) {
   913     if (mdiMode) {
   914       desktop.add(frame.getComponent());
   915     }
   916   }
   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   }
   928   private Dimension getParentDimension(Component c) {
   929     if (mdiMode) {
   930       return desktop.getSize();
   931     } else {
   932       return Toolkit.getDefaultToolkit().getScreenSize();
   933     }
   934   }
   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;
   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     }
   972     public boolean openFile()                        { return code.openFile(filename);     }
   973     public String  getSourceFileName()               { return filename;                    }
   974     public int     getCurrentLineNumber()            { return code.getCurrentLineNumber(); }
   975     public void showLineNumber(int lineNo) {
   976       if (!shown) {
   977         addFrame(editorFrame);
   978         GraphicsUtilities.reshapeToAspectRatio(editorFrame.getComponent(),
   979                                                1.0f,
   980                                                0.85f,
   981                                                getParentDimension(editorFrame.getComponent()));
   982         editorFrame.setVisible(true);
   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   }
   999   class DefaultEditorFactory implements EditorFactory {
  1000     private LinkedList/*<Editor>*/ editors = new LinkedList();
  1002     public Editor openFile(String filename, EditorCommands commands) {
  1003       DefaultEditor editor = new DefaultEditor(this, filename, editorComm);
  1004       if (!editor.openFile()) {
  1005         return null;
  1007       return editor;
  1010     public Editor getCurrentEditor() {
  1011       if (editors.isEmpty()) {
  1012         return null;
  1014       return (Editor) editors.getFirst();
  1017     void editorClosed(Editor editor) {
  1018       editors.remove(editor);
  1021     void makeEditorCurrent(Editor editor) {
  1022       editors.remove(editor);
  1023       editors.addFirst(editor);
  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;
  1033     JavaFileFilter(String packageName, String fileName) {
  1034       this.packageName = packageName;
  1035       this.fileName = fileName;
  1038     public boolean accept(File f) {
  1039       if (f.isDirectory()) {
  1040         return true;
  1042       // This rejects most files
  1043       if (!f.getName().equals(fileName)) {
  1044         return false;
  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;
  1052       return true;
  1055     public String getDescription() { return "Java source files"; }
  1058   // Auxiliary information used only for Java source files
  1059   static class JavaUserData {
  1060     private String packageName; // External format
  1061     private String sourceFileName;
  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;
  1070     String packageName()    { return packageName; }
  1071     String sourceFileName() { return sourceFileName; }
  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;
  1083     File chosen = chooser.getSelectedFile();
  1084     if (chosen == null) {
  1085       return;
  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;
  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;
  1115       editors.put(name, editor);
  1116       if (data != null) {
  1117         editor.setUserData(data);
  1119     } else {
  1120       editor.toFront();
  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());
  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;
  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));
  1155         int res = chooser.showOpenDialog(null);
  1156         if (res != JFileChooser.APPROVE_OPTION) {
  1157           // FIXME: show disassembly instead
  1158           return;
  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;
  1166         realFileName = chosen.getPath();
  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;
  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));
  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());
  1195   //
  1196   // Suspend/resume
  1197   //
  1199   private boolean isSuspended() {
  1200     return suspended;
  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();
  1210     agent.suspend();
  1211     // FIXME: call VM.getVM().fireVMSuspended()
  1212     resetCurrentThread();
  1213     debugEventTimer.stop();
  1214     suspended = true;
  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();
  1233       if (javaEventPending) {
  1234         javaEventPending = false;
  1235         // Clear it out before resuming polling for events
  1236         agent.javaEventContinue();
  1239     agent.enableJavaInteraction();
  1240     suspended = false;
  1241     debugEventTimer.start();
  1244   //
  1245   // Breakpoints
  1246   //
  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");
  1264       boolean mustSuspendAndResume = (!prctl.isSuspended());
  1265       try {
  1266         if (mustSuspendAndResume) {
  1267           prctl.suspend();
  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);
  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());
  1290         } else {
  1291           return new BreakpointResult(false, false, 0, "No debug information for this source file and line");
  1293       } finally {
  1294         if (mustSuspendAndResume) {
  1295           prctl.resume();
  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)";
  1307         return new BreakpointResult(false, false, 0, why);
  1309       Set bpset = (Set) fileToBreakpointMap.get(filename);
  1310       if (bpset == null) {
  1311         bpset = new HashSet();
  1312         fileToBreakpointMap.put(filename, bpset);
  1314       boolean mustResumeAndSuspend = isSuspended();
  1315       try {
  1316         if (mustResumeAndSuspend) {
  1317           agent.resume();
  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());
  1335           if (Assert.ASSERTS_ENABLED) {
  1336             Assert.that(addRemRes, "Inconsistent Java breakpoint state with respect to target process");
  1338           return new BreakpointResult(true, res.getWasSet(), res.getLineNumber());
  1339         } else {
  1340           return new BreakpointResult(false, false, 0, res.getErrMsg());
  1342       } finally {
  1343         if (mustResumeAndSuspend) {
  1344           agent.suspend();
  1345           resetCurrentThread();
  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;
  1358     // Binary search for line number
  1359     return searchLineNumbers(infos, lineNumber, 0, infos.size());
  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);
  1381                   val.add(info);
  1384             });
  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;
  1400           });
  1402       sourceFileToLineNumberInfoMap = map;
  1404     return sourceFileToLineNumberInfoMap;
  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;
  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;
  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);
  1438   private boolean checkLineNumber(java.util.List infoList, int lineNo, int idx) {
  1439     LineNumberInfo info = (LineNumberInfo) infoList.get(idx);
  1440     return (info.getLineNumber() >= lineNo);
  1443   //
  1444   // Debug events
  1445   //
  1447   private synchronized void pollForDebugEvent() {
  1448     ProcessControl prctl = getCDebugger().getProcessControl();
  1449     if (prctl == null) {
  1450       return;
  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
  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();
  1496         showMessageDialog(info, "Unknown debug event", JOptionPane.INFORMATION_MESSAGE);
  1497         suspend();
  1498         prctl.debugEventContinue();
  1500       return;
  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");

mercurial