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