0
|
1 /*
|
|
2 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
|
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 *
|
|
5 * This code is free software; you can redistribute it and/or modify it
|
|
6 * under the terms of the GNU General Public License version 2 only, as
|
|
7 * published by the Free Software Foundation.
|
|
8 *
|
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
12 * version 2 for more details (a copy is included in the LICENSE file that
|
|
13 * accompanied this code).
|
|
14 *
|
|
15 * You should have received a copy of the GNU General Public License version
|
|
16 * 2 along with this work; if not, write to the Free Software Foundation,
|
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18 *
|
|
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or
|
|
21 * have any questions.
|
|
22 *
|
|
23 */
|
|
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 }
|