| /* |
| * 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.io.*; |
| import java.util.*; |
| import java.awt.*; |
| import java.awt.event.*; |
| import javax.swing.*; |
| import javax.swing.table.*; |
| |
| import sun.jvm.hotspot.debugger.*; |
| import sun.jvm.hotspot.oops.*; |
| import sun.jvm.hotspot.runtime.*; |
| import sun.jvm.hotspot.utilities.*; |
| import sun.jvm.hotspot.ui.table.*; |
| import sun.jvm.hotspot.ui.tree.*; |
| |
| /** Lists objects along with their types */ |
| |
| public class ObjectListPanel extends SAPanel { |
| private ObjectListTableModel dataModel; |
| private JTable table; |
| private java.util.List elements; |
| private HeapProgressThunk thunk; |
| private boolean checkedForArrays; |
| private boolean hasArrays; |
| private int numColumns; |
| // For changing the text of the "Compute Liveness" button |
| private JButton livenessButton; |
| private ActionListener livenessButtonListener; |
| private static final String showLivenessText = "Show Liveness"; |
| |
| /** Takes a List<Oop> in constructor, and an optional |
| HeapProgressThunk used if computing liveness */ |
| public ObjectListPanel(java.util.List els, |
| HeapProgressThunk thunk) { |
| super(); |
| |
| elements = els; |
| this.thunk = thunk; |
| computeNumColumns(); |
| |
| setLayout(new BorderLayout()); |
| |
| dataModel = new ObjectListTableModel(); |
| |
| table = new JTable(dataModel); |
| table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| JTableHeader header = table.getTableHeader(); |
| header.setDefaultRenderer(new SortHeaderCellRenderer(header, dataModel)); |
| header.addMouseListener(new SortHeaderMouseAdapter(table, dataModel)); |
| |
| JScrollPane scrollPane = new JScrollPane(table); |
| add(scrollPane, BorderLayout.CENTER); |
| |
| JPanel panel = new JPanel(); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); |
| panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); |
| Box box = Box.createHorizontalBox(); |
| box.add(Box.createGlue()); |
| JButton button = new JButton("Inspect"); |
| button.addActionListener(new ActionListener() { |
| public void actionPerformed(ActionEvent e) { |
| fireShowInspector(); |
| } |
| }); |
| box.add(button); |
| |
| box.add(Box.createHorizontalStrut(20)); |
| |
| // Liveness button |
| button = new JButton(); |
| livenessButton = button; |
| if (VM.getVM().getRevPtrs() == null) { |
| button.setText("Compute Liveness"); |
| livenessButtonListener = new ActionListener() { |
| public void actionPerformed(ActionEvent e) { |
| fireComputeLiveness(); |
| } |
| }; |
| } else { |
| button.setText("Show Liveness Path"); |
| livenessButtonListener = new ActionListener() { |
| public void actionPerformed(ActionEvent e) { |
| fireShowLiveness(); |
| } |
| }; |
| } |
| button.addActionListener(livenessButtonListener); |
| box.add(button); |
| box.add(Box.createGlue()); |
| panel.add(box); |
| add(panel, BorderLayout.SOUTH); |
| } |
| |
| //-------------------------------------------------------------------------------- |
| // Internals only below this point |
| // |
| |
| private static class AddressWrapper implements Comparable { |
| private Address address; |
| |
| private AddressWrapper(Address address) { |
| this.address = address; |
| } |
| |
| public String toString() { |
| return address.toString(); |
| } |
| |
| public int compareTo(Object o) { |
| AddressWrapper wrapper = (AddressWrapper) o; |
| Address addr = wrapper.address; |
| if (AddressOps.lessThan(address, addr)) return -1; |
| if (AddressOps.greaterThan(address, addr)) return 1; |
| return 0; |
| } |
| } |
| |
| private class ObjectListTableModel extends SortableTableModel { |
| public ObjectListTableModel() { |
| // Set the rows |
| this.elements = ObjectListPanel.this.elements; |
| setComparator(new ObjectListComparator(this)); |
| } |
| |
| public int getColumnCount() { return numColumns; } |
| public int getRowCount() { return elements.size(); } |
| public String getColumnName(int col) { |
| switch (col) { |
| case 0: |
| return "Address"; |
| case 1: |
| return "Oop"; |
| case 2: |
| if (hasArrays) { |
| return "Length"; |
| } else { |
| return "Class Description"; |
| } |
| case 3: |
| if (hasArrays) { |
| return "Class Description"; |
| } else if (VM.getVM().getRevPtrs() != null) { |
| return "Liveness"; |
| } |
| case 4: |
| if (hasArrays && (VM.getVM().getRevPtrs() != null)) { |
| return "Liveness"; |
| } |
| } |
| throw new RuntimeException("Index " + col + " out of bounds"); |
| } |
| |
| public Object getValueAt(int row, int col) { |
| Oop oop = (Oop) elements.get(row); |
| return getValueForColumn(oop, col); |
| } |
| |
| public Object getValueForColumn(Oop oop, int col) { |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| |
| switch (col) { |
| case 0: |
| return new AddressWrapper(oop.getHandle()); |
| case 1: |
| oop.printValueOn(new PrintStream(bos)); |
| break; |
| case 2: |
| if (hasArrays) { |
| if (oop instanceof Array) { |
| return new Long(((Array) oop).getLength()); |
| } |
| return null; |
| } else { |
| oop.getKlass().printValueOn(new PrintStream(bos)); |
| break; |
| } |
| case 3: |
| if (hasArrays) { |
| oop.getKlass().printValueOn(new PrintStream(bos)); |
| break; |
| } else { |
| if (VM.getVM().getRevPtrs() != null) { |
| if (VM.getVM().getRevPtrs().get(oop) != null) { |
| return "Alive"; |
| } else { |
| return "Dead"; |
| } |
| } |
| } |
| case 4: |
| if (hasArrays) { |
| if (VM.getVM().getRevPtrs() != null) { |
| if (VM.getVM().getRevPtrs().get(oop) != null) { |
| return "Alive"; |
| } else { |
| return "Dead"; |
| } |
| } |
| } |
| default: |
| throw new RuntimeException("Column " + col + " out of bounds"); |
| } |
| |
| return bos.toString(); |
| } |
| |
| private class ObjectListComparator extends TableModelComparator { |
| public ObjectListComparator(ObjectListTableModel model) { |
| super(model); |
| } |
| |
| /** |
| * Returns the value for the comparing object for the |
| * column. |
| * |
| * @param obj Object that was passed for Comparator |
| * @param column the column to retrieve |
| */ |
| public Object getValueForColumn(Object obj, int column) { |
| ObjectListTableModel omodel = (ObjectListTableModel)model; |
| return omodel.getValueForColumn((Oop) obj, column); |
| } |
| } |
| } |
| |
| private void fireShowInspector() { |
| int i = table.getSelectedRow(); |
| if (i < 0) { |
| return; |
| } |
| |
| Oop oop = (Oop) elements.get(i); |
| |
| for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { |
| SAListener listener = (SAListener) iter.next(); |
| listener.showInspector(new OopTreeNodeAdapter(oop, null)); |
| } |
| } |
| |
| private void fireComputeLiveness() { |
| final Runnable cutoverButtonRunnable = new Runnable() { |
| public void run() { |
| livenessButton.removeActionListener(livenessButtonListener); |
| livenessButtonListener = null; |
| livenessButton.addActionListener(new ActionListener() { |
| public void actionPerformed(ActionEvent e) { |
| fireShowLiveness(); |
| } |
| }); |
| computeNumColumns(); |
| livenessButton.setEnabled(true); |
| livenessButton.setText(showLivenessText); |
| dataModel.fireTableStructureChanged(); |
| } |
| }; |
| |
| |
| 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(); |
| if (thunk != null) { |
| rev.setHeapProgressThunk(thunk); |
| } |
| rev.run(); |
| cutoverButtonRunnable.run(); |
| } finally { |
| worker.shutdown(); |
| } |
| } |
| }); |
| } |
| } |
| |
| private void fireShowLiveness() { |
| if (VM.getVM().getRevPtrs() == null) { |
| return; |
| } |
| |
| int i = table.getSelectedRow(); |
| if (i < 0) { |
| return; |
| } |
| |
| Oop oop = (Oop) elements.get(i); |
| LivenessPathList list = LivenessAnalysis.computeAllLivenessPaths(oop); |
| if (list == null) { |
| return; // dead object |
| } |
| |
| for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { |
| SAListener listener = (SAListener) iter.next(); |
| listener.showLiveness(oop, list); |
| } |
| } |
| |
| private void checkForArrays() { |
| if (checkedForArrays) return; |
| checkedForArrays = true; |
| for (Iterator iter = elements.iterator(); iter.hasNext(); ) { |
| if (iter.next() instanceof Array) { |
| hasArrays = true; |
| return; |
| } |
| } |
| } |
| |
| private void computeNumColumns() { |
| checkForArrays(); |
| numColumns = 3; |
| if (hasArrays) ++numColumns; |
| if (VM.getVM().getRevPtrs() != null) ++numColumns; |
| } |
| } |