blob: 993c44529083e9e69d44d9f0346203270cb64d36 [file] [log] [blame]
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package sun.jvm.hotspot.ui;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.TreePath;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.oops.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.ui.tree.*;
import sun.jvm.hotspot.utilities.*;
/** This class implements tree-browsing functionality of a particular
SimpleTreeNode, and is only designed to be used in a debugging
system. It uses a SimpleTreeModel internally. It can inspect both
oops as well as C++ objects described by the VMStructs database in
the target VM. */
public class Inspector extends SAPanel {
private JTree tree;
private SimpleTreeModel model;
// UI widgets we need permanent handles to
private HistoryComboBox addressField;
private JLabel statusLabel;
private JButton livenessButton;
private ActionListener livenessButtonListener;
private ActionListener showLivenessListener;
private static final String computeLivenessText = "Compute Liveness";
private static final String showLivenessText = "Show Liveness";
private JLabel liveStatus;
private LivenessPathList list = null;
private Oop currentOop = null;
public Inspector() {
model = new SimpleTreeModel();
tree = new JTree(model);
setLayout(new BorderLayout());
Box hbox = Box.createHorizontalBox();
JButton button = new JButton("Previous Oop");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String text = addressField.getText();
try {
VM vm = VM.getVM();
Address a = vm.getDebugger().parseAddress(text);
OopHandle handle = a.addOffsetToAsOopHandle(-vm.getAddressSize());
addressField.setText(handle.toString());
} catch (Exception ex) {
}
}
});
hbox.add(button);
hbox.add(new JLabel("Address / C++ Expression: "));
addressField = new HistoryComboBox();
hbox.add(addressField);
statusLabel = new JLabel();
hbox.add(statusLabel);
Box hboxDown = Box.createHorizontalBox();
hboxDown.add(Box.createGlue());
livenessButton = new JButton(computeLivenessText);
livenessButtonListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (currentOop != null) {
fireComputeLiveness();
}
return;
}
};
showLivenessListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
fireShowLiveness();
}
};
livenessButton.addActionListener(livenessButtonListener);
hboxDown.add(livenessButton);
hboxDown.add(Box.createGlue());
liveStatus = new JLabel();
hboxDown.add(liveStatus);
hboxDown.add(Box.createGlue());
add(hbox, BorderLayout.NORTH);
add(hboxDown, BorderLayout.SOUTH);
addressField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String text = addressField.getText();
try {
Address a = VM.getVM().getDebugger().parseAddress(text);
int max_searches = 1000;
int searches = 0;
int offset = 0;
Oop oop = null;
if (a != null) {
OopHandle handle = a.addOffsetToAsOopHandle(0);
while (searches < max_searches) {
searches++;
if (RobustOopDeterminator.oopLooksValid(handle)) {
try {
oop = VM.getVM().getObjectHeap().newOop(handle);
addressField.setText(handle.toString());
break;
} catch (UnknownOopException ex) {
// ok
} catch (RuntimeException ex) {
ex.printStackTrace();
}
}
offset -= 4;
handle = a.addOffsetToAsOopHandle(offset);
}
}
if (oop != currentOop) {
currentOop = oop;
liveStatus.setText("");
list = null;
if (livenessButton.getText().equals(showLivenessText)) {
livenessButton.setText(computeLivenessText);
livenessButton.removeActionListener(showLivenessListener);
livenessButton.addActionListener(livenessButtonListener);
}
}
if (oop != null) {
statusLabel.setText("");
setRoot(new OopTreeNodeAdapter(oop, null));
return;
}
// Try to treat this address as a C++ object and deduce its type
Type t = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
if (t != null) {
statusLabel.setText("");
setRoot(new CTypeTreeNodeAdapter(a, t, null));
return;
}
statusLabel.setText("<bad oop or unknown C++ object " + text + ">");
}
catch (NumberFormatException ex) {
currentOop = null;
liveStatus.setText("");
list = null;
if (livenessButton.getText().equals(showLivenessText)) {
livenessButton.setText(computeLivenessText);
livenessButton.removeActionListener(showLivenessListener);
livenessButton.addActionListener(livenessButtonListener);
}
// Try to treat this as a C++ expression
CPPExpressions.CastExpr cast = CPPExpressions.parseCast(text);
if (cast != null) {
TypeDataBase db = VM.getVM().getTypeDataBase();
Type t = db.lookupType(cast.getType());
if (t == null) {
statusLabel.setText("<unknown C++ type \"" + cast.getType() + "\">");
} else {
try {
Address a = VM.getVM().getDebugger().parseAddress(cast.getAddress());
statusLabel.setText("");
setRoot(new CTypeTreeNodeAdapter(a, t, null));
} catch (NumberFormatException ex2) {
statusLabel.setText("<bad address " + cast.getAddress() + ">");
}
}
return;
}
CPPExpressions.StaticFieldExpr stat = CPPExpressions.parseStaticField(text);
if (stat != null) {
TypeDataBase db = VM.getVM().getTypeDataBase();
Type t = db.lookupType(stat.getContainingType());
if (t == null) {
statusLabel.setText("<unknown C++ type \"" + stat.getContainingType() + "\">");
} else {
sun.jvm.hotspot.types.Field f = t.getField(stat.getFieldName(), true, false);
if (f == null) {
statusLabel.setText("<unknown field \"" + stat.getFieldName() + "\" in type \"" +
stat.getContainingType() + "\">");
} else if (!f.isStatic()) {
statusLabel.setText("<field \"" + stat.getContainingType() + "::" +
stat.getFieldName() + "\" was not static>");
} else {
Type fieldType = f.getType();
if (fieldType.isPointerType()) {
fieldType = ((PointerType) fieldType).getTargetType();
// Try to get a more derived type
Type typeGuess = db.guessTypeForAddress(f.getAddress());
if (typeGuess != null) {
fieldType = typeGuess;
}
statusLabel.setText("");
setRoot(new CTypeTreeNodeAdapter(f.getAddress(),
fieldType,
new NamedFieldIdentifier(text)));
} else {
statusLabel.setText("");
setRoot(new CTypeTreeNodeAdapter(f.getStaticFieldAddress(),
f.getType(),
new NamedFieldIdentifier(text)));
}
}
}
return;
}
statusLabel.setText("<parse error>");
}
catch (AddressException ex) {
ex.printStackTrace();
currentOop = null;
liveStatus.setText("");
list = null;
if (livenessButton.getText().equals(showLivenessText)) {
livenessButton.setText(computeLivenessText);
livenessButton.removeActionListener(showLivenessListener);
livenessButton.addActionListener(livenessButtonListener);
}
statusLabel.setText("<bad address>");
}
catch (Exception ex) {
ex.printStackTrace();
currentOop = null;
liveStatus.setText("");
list = null;
if (livenessButton.getText().equals(showLivenessText)) {
livenessButton.setText(computeLivenessText);
livenessButton.removeActionListener(showLivenessListener);
livenessButton.addActionListener(livenessButtonListener);
}
statusLabel.setText("<error constructing oop>");
}
}
});
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if(selRow != -1) {
if (e.getClickCount() == 1 && (e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) {
Object node = tree.getLastSelectedPathComponent();
if (node != null && node instanceof SimpleTreeNode) {
showInspector((SimpleTreeNode)node);
}
}
}
}
};
tree.addMouseListener(ml);
JScrollPane scrollPane = new JScrollPane(tree);
// Let's see what happens if we let the parent deal with resizing the panel
add(scrollPane, BorderLayout.CENTER);
}
public Inspector(final SimpleTreeNode root) {
this();
SwingUtilities.invokeLater( new Runnable() {
public void run() {
if (root instanceof OopTreeNodeAdapter) {
final Oop oop = ((OopTreeNodeAdapter)root).getOop();
addressField.setText(oop.getHandle().toString());
}
setRoot(root);
}
});
}
private void setRoot(SimpleTreeNode root) {
model.setRoot(root);
// tree.invalidate();
// tree.validate();
// repaint();
// FIXME: invalidate? How to get to redraw? Will I have to make
// tree listeners work?
}
private void fireComputeLiveness() {
final Runnable cutoverButtonRunnable = new Runnable() {
public void run() {
list = LivenessAnalysis.computeAllLivenessPaths(currentOop);
if (list == null) {
liveStatus.setText("Oop is Dead");
} else {
liveStatus.setText("Oop is Alive");
livenessButton.removeActionListener(livenessButtonListener);
livenessButton.addActionListener(showLivenessListener);
livenessButton.setEnabled(true);
livenessButton.setText(showLivenessText);
}
}
};
if (VM.getVM().getRevPtrs() != null) {
cutoverButtonRunnable.run();
} else {
final WorkerThread worker = new WorkerThread();
worker.invokeLater(new Runnable() {
public void run() {
try {
ReversePtrsAnalysis rev = new ReversePtrsAnalysis();
rev.run();
cutoverButtonRunnable.run();
} finally {
worker.shutdown();
}
}
});
}
}
private void fireShowLiveness() {
if (list == null) {
return; // dead object
}
for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
SAListener listener = (SAListener) iter.next();
listener.showLiveness(currentOop, list);
}
}
}