| /* |
| * 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); |
| } |
| } |
| } |