View Javadoc
1   /*
2    * Copyright (c) 2000, 2006, 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.ui;
26  
27  import java.awt.*;
28  import java.awt.event.*;
29  import java.util.*;
30  import javax.swing.*;
31  import javax.swing.tree.TreePath;
32  import sun.jvm.hotspot.debugger.*;
33  import sun.jvm.hotspot.oops.*;
34  import sun.jvm.hotspot.runtime.*;
35  import sun.jvm.hotspot.types.*;
36  import sun.jvm.hotspot.ui.tree.*;
37  import sun.jvm.hotspot.utilities.*;
38  
39  /** This class implements tree-browsing functionality of a particular
40      SimpleTreeNode, and is only designed to be used in a debugging
41      system. It uses a SimpleTreeModel internally. It can inspect both
42      oops as well as C++ objects described by the VMStructs database in
43      the target VM. */
44  
45  public class Inspector extends SAPanel {
46    private JTree tree;
47    private SimpleTreeModel model;
48  
49    // UI widgets we need permanent handles to
50    private HistoryComboBox addressField;
51    private JLabel statusLabel;
52  
53    private JButton            livenessButton;
54    private ActionListener     livenessButtonListener;
55    private ActionListener     showLivenessListener;
56    private static final String computeLivenessText = "Compute Liveness";
57    private static final String showLivenessText = "Show Liveness";
58    private JLabel liveStatus;
59    private LivenessPathList list = null;
60    private Oop currentOop = null;
61  
62    public Inspector() {
63      model = new SimpleTreeModel();
64      tree = new JTree(model);
65  
66      setLayout(new BorderLayout());
67      Box hbox = Box.createHorizontalBox();
68      JButton button = new JButton("Previous Oop");
69      button.addActionListener(new ActionListener() {
70          public void actionPerformed(ActionEvent e) {
71            String text = addressField.getText();
72            try {
73              VM vm = VM.getVM();
74              Address a = vm.getDebugger().parseAddress(text);
75              OopHandle handle = a.addOffsetToAsOopHandle(-vm.getAddressSize());
76              addressField.setText(handle.toString());
77            } catch (Exception ex) {
78            }
79          }
80        });
81      hbox.add(button);
82      hbox.add(new JLabel("Address / C++ Expression: "));
83      addressField = new HistoryComboBox();
84      hbox.add(addressField);
85      statusLabel = new JLabel();
86      hbox.add(statusLabel);
87  
88      Box hboxDown = Box.createHorizontalBox();
89      hboxDown.add(Box.createGlue());
90  
91      livenessButton = new JButton(computeLivenessText);
92      livenessButtonListener = new ActionListener() {
93            public void actionPerformed(ActionEvent e) {
94                 if (currentOop != null) {
95                    fireComputeLiveness();
96                 }
97                 return;
98           }
99      };
100     showLivenessListener = new ActionListener() {
101       public void actionPerformed(ActionEvent e) {
102       fireShowLiveness();
103       }
104     };
105     livenessButton.addActionListener(livenessButtonListener);
106     hboxDown.add(livenessButton);
107     hboxDown.add(Box.createGlue());
108 
109     liveStatus = new JLabel();
110     hboxDown.add(liveStatus);
111     hboxDown.add(Box.createGlue());
112 
113     add(hbox, BorderLayout.NORTH);
114     add(hboxDown, BorderLayout.SOUTH);
115 
116     addressField.addActionListener(new ActionListener() {
117         public void actionPerformed(ActionEvent e) {
118           String text = addressField.getText();
119           try {
120             Address a = VM.getVM().getDebugger().parseAddress(text);
121             int max_searches = 1000;
122             int searches = 0;
123             int offset = 0;
124             Oop oop = null;
125             if (a != null) {
126               OopHandle handle = a.addOffsetToAsOopHandle(0);
127               while (searches < max_searches) {
128                 searches++;
129                 if (RobustOopDeterminator.oopLooksValid(handle)) {
130                   try {
131                     oop = VM.getVM().getObjectHeap().newOop(handle);
132                     addressField.setText(handle.toString());
133                     break;
134                   } catch (UnknownOopException ex) {
135                     // ok
136                   } catch (RuntimeException ex) {
137                     ex.printStackTrace();
138                   }
139                 }
140                 offset -= 4;
141                 handle = a.addOffsetToAsOopHandle(offset);
142               }
143             }
144             if (oop != currentOop) {
145               currentOop = oop;
146               liveStatus.setText("");
147               list = null;
148               if (livenessButton.getText().equals(showLivenessText)) {
149                 livenessButton.setText(computeLivenessText);
150                 livenessButton.removeActionListener(showLivenessListener);
151                 livenessButton.addActionListener(livenessButtonListener);
152               }
153             }
154 
155             if (oop != null) {
156               statusLabel.setText("");
157               setRoot(new OopTreeNodeAdapter(oop, null));
158               return;
159             }
160 
161             // Try to treat this address as a C++ object and deduce its type
162             Type t = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
163             if (t != null) {
164               statusLabel.setText("");
165               setRoot(new CTypeTreeNodeAdapter(a, t, null));
166               return;
167             }
168 
169             statusLabel.setText("<bad oop or unknown C++ object " + text + ">");
170           }
171           catch (NumberFormatException ex) {
172               currentOop = null;
173               liveStatus.setText("");
174               list = null;
175               if (livenessButton.getText().equals(showLivenessText)) {
176                 livenessButton.setText(computeLivenessText);
177                 livenessButton.removeActionListener(showLivenessListener);
178                 livenessButton.addActionListener(livenessButtonListener);
179               }
180             // Try to treat this as a C++ expression
181             CPPExpressions.CastExpr cast = CPPExpressions.parseCast(text);
182             if (cast != null) {
183               TypeDataBase db = VM.getVM().getTypeDataBase();
184               Type t = db.lookupType(cast.getType());
185               if (t == null) {
186                 statusLabel.setText("<unknown C++ type \"" + cast.getType() + "\">");
187               } else {
188                 try {
189                   Address a = VM.getVM().getDebugger().parseAddress(cast.getAddress());
190                   statusLabel.setText("");
191                   setRoot(new CTypeTreeNodeAdapter(a, t, null));
192                 } catch (NumberFormatException ex2) {
193                   statusLabel.setText("<bad address " + cast.getAddress() + ">");
194                 }
195               }
196               return;
197             }
198             CPPExpressions.StaticFieldExpr stat = CPPExpressions.parseStaticField(text);
199             if (stat != null) {
200               TypeDataBase db = VM.getVM().getTypeDataBase();
201               Type t = db.lookupType(stat.getContainingType());
202               if (t == null) {
203                 statusLabel.setText("<unknown C++ type \"" + stat.getContainingType() + "\">");
204               } else {
205                 sun.jvm.hotspot.types.Field f = t.getField(stat.getFieldName(), true, false);
206                 if (f == null) {
207                   statusLabel.setText("<unknown field \"" + stat.getFieldName() + "\" in type \"" +
208                                       stat.getContainingType() + "\">");
209                 } else if (!f.isStatic()) {
210                   statusLabel.setText("<field \"" + stat.getContainingType() + "::" +
211                                       stat.getFieldName() + "\" was not static>");
212                 } else {
213                   Type fieldType = f.getType();
214                   if (fieldType.isPointerType()) {
215                     fieldType = ((PointerType) fieldType).getTargetType();
216 
217                     // Try to get a more derived type
218                     Type typeGuess = db.guessTypeForAddress(f.getAddress());
219                     if (typeGuess != null) {
220                       fieldType = typeGuess;
221                     }
222 
223                     statusLabel.setText("");
224                     setRoot(new CTypeTreeNodeAdapter(f.getAddress(),
225                                                      fieldType,
226                                                      new NamedFieldIdentifier(text)));
227                   } else {
228                     statusLabel.setText("");
229                     setRoot(new CTypeTreeNodeAdapter(f.getStaticFieldAddress(),
230                                                      f.getType(),
231                                                      new NamedFieldIdentifier(text)));
232                   }
233                 }
234               }
235               return;
236             }
237 
238             statusLabel.setText("<parse error>");
239           }
240           catch (AddressException ex) {
241             ex.printStackTrace();
242             currentOop = null;
243             liveStatus.setText("");
244             list = null;
245             if (livenessButton.getText().equals(showLivenessText)) {
246               livenessButton.setText(computeLivenessText);
247               livenessButton.removeActionListener(showLivenessListener);
248               livenessButton.addActionListener(livenessButtonListener);
249             }
250             statusLabel.setText("<bad address>");
251           }
252           catch (Exception ex) {
253             ex.printStackTrace();
254             currentOop = null;
255             liveStatus.setText("");
256             list = null;
257             if (livenessButton.getText().equals(showLivenessText)) {
258               livenessButton.setText(computeLivenessText);
259               livenessButton.removeActionListener(showLivenessListener);
260               livenessButton.addActionListener(livenessButtonListener);
261             }
262             statusLabel.setText("<error constructing oop>");
263           }
264         }
265       });
266 
267     MouseListener ml = new MouseAdapter() {
268         public void mousePressed(MouseEvent e) {
269           int selRow = tree.getRowForLocation(e.getX(), e.getY());
270           TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
271           if(selRow != -1) {
272             if (e.getClickCount() == 1 && (e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) {
273               Object node = tree.getLastSelectedPathComponent();
274               if (node != null && node instanceof SimpleTreeNode) {
275                 showInspector((SimpleTreeNode)node);
276               }
277             }
278           }
279         }
280       };
281     tree.addMouseListener(ml);
282 
283     JScrollPane scrollPane = new JScrollPane(tree);
284 
285     // Let's see what happens if we let the parent deal with resizing the panel
286     add(scrollPane, BorderLayout.CENTER);
287   }
288 
289   public Inspector(final SimpleTreeNode root) {
290     this();
291     SwingUtilities.invokeLater( new Runnable() {
292         public void run() {
293           if (root instanceof OopTreeNodeAdapter) {
294             final Oop oop = ((OopTreeNodeAdapter)root).getOop();
295             addressField.setText(oop.getHandle().toString());
296           }
297           setRoot(root);
298         }
299       });
300   }
301 
302   private void setRoot(SimpleTreeNode root) {
303     model.setRoot(root);
304 
305     //    tree.invalidate();
306     //    tree.validate();
307     //    repaint();
308     // FIXME: invalidate? How to get to redraw? Will I have to make
309     // tree listeners work?
310   }
311 
312   private void fireComputeLiveness() {
313     final Runnable cutoverButtonRunnable = new Runnable() {
314         public void run() {
315           list = LivenessAnalysis.computeAllLivenessPaths(currentOop);
316           if (list == null) {
317             liveStatus.setText("Oop is Dead");
318           } else {
319             liveStatus.setText("Oop is Alive");
320             livenessButton.removeActionListener(livenessButtonListener);
321             livenessButton.addActionListener(showLivenessListener);
322 
323             livenessButton.setEnabled(true);
324             livenessButton.setText(showLivenessText);
325           }
326         }
327       };
328 
329 
330     if (VM.getVM().getRevPtrs() != null) {
331       cutoverButtonRunnable.run();
332     } else {
333       final WorkerThread worker = new WorkerThread();
334       worker.invokeLater(new Runnable() {
335           public void run() {
336             try {
337               ReversePtrsAnalysis rev = new ReversePtrsAnalysis();
338               rev.run();
339               cutoverButtonRunnable.run();
340             } finally {
341               worker.shutdown();
342             }
343           }
344         });
345     }
346   }
347 
348   private void fireShowLiveness() {
349     if (list == null) {
350       return; // dead object
351     }
352 
353     for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
354       SAListener listener = (SAListener) iter.next();
355       listener.showLiveness(currentOop, list);
356     }
357   }
358 }