Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/ui/MemoryPanel.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | c18cbe5936b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/ui/MemoryPanel.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,678 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.ui; + +import java.awt.*; +import java.awt.datatransfer.*; +import java.awt.event.*; +import java.io.IOException; +import java.math.*; +import java.util.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.ui.*; + +public class MemoryPanel extends JPanel { + private boolean is64Bit; + private Debugger debugger; + private int addressSize; + private String unmappedAddrString; + private HighPrecisionJScrollBar scrollBar; + private AbstractTableModel model; + private JTable table; + private BigInteger startVal; + // Includes any partially-visible row at the bottom + private int numVisibleRows; + // Frequently-used subexpression + private int numUsableRows; + // Multi-row (and multi-column) selection. Have to duplicate state + // from UI so this can work as we scroll off the screen. + private boolean haveAnchor; + private int rowAnchorIndex; + private int colAnchorIndex; + private boolean haveLead; + private int rowLeadIndex; + private int colLeadIndex; + + abstract class ActionWrapper extends AbstractAction { + private Action parent; + ActionWrapper() { + } + + void setParent(Action parent) { + this.parent = parent; + } + + Action getParent() { + return parent; + } + + public void actionPerformed(ActionEvent e) { + if (getParent() != null) { + getParent().actionPerformed(e); + } + } + } + + public MemoryPanel(final Debugger debugger, boolean is64Bit) { + super(); + this.debugger = debugger; + this.is64Bit = is64Bit; + if (is64Bit) { + addressSize = 8; + unmappedAddrString = "??????????????????"; + } else { + addressSize = 4; + unmappedAddrString = "??????????"; + } + setLayout(new BorderLayout()); + setupScrollBar(); + add(scrollBar, BorderLayout.EAST); + + model = new AbstractTableModel() { + public int getRowCount() { + return numVisibleRows; + } + public int getColumnCount() { + return 2; + } + public Object getValueAt(int row, int column) { + switch (column) { + case 0: return bigIntToHexString(startVal.add(new BigInteger(Integer.toString((row * addressSize))))); + case 1: { + try { + Address addr = bigIntToAddress(startVal.add(new BigInteger(Integer.toString((row * addressSize))))); + if (addr != null) { + return addressToString(addr.getAddressAt(0)); + } + return unmappedAddrString; + } catch (UnmappedAddressException e) { + return unmappedAddrString; + } + } + default: throw new RuntimeException("Column " + column + " out of bounds"); + } + } + public boolean isCellEditable(int row, int col) { + return false; + } + }; + + // View with JTable with no header + table = new JTable(model); + table.setTableHeader(null); + table.setShowGrid(false); + table.setIntercellSpacing(new Dimension(0, 0)); + table.setCellSelectionEnabled(true); + table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + table.setDragEnabled(true); + Font font = GraphicsUtilities.lookupFont("Courier"); + if (font == null) { + throw new RuntimeException("Error looking up monospace font Courier"); + } + table.setFont(font); + + // Export proper data. + // We need to keep our own notion of the selection in order to + // properly export data, since the selection can go beyond the + // visible area on the screen (and since the table's model doesn't + // back all of those slots). + // Code thanks to Shannon.Hickey@sfbay + table.setTransferHandler(new TransferHandler() { + protected Transferable createTransferable(JComponent c) { + JTable table = (JTable)c; + if (haveSelection()) { + StringBuffer buf = new StringBuffer(); + int iDir = (getRowAnchor() < getRowLead() ? 1 : -1); + int jDir = (getColAnchor() < getColLead() ? 1 : -1); + + for (int i = getRowAnchor(); i != getRowLead() + iDir; i += iDir) { + for (int j = getColAnchor(); j != getColLead() + jDir; j += jDir) { + Object val = model.getValueAt(i, j); + buf.append(val == null ? "" : val.toString()); + if (j != getColLead()) { + buf.append("\t"); + } + } + if (i != getRowLead()) { + buf.append("\n"); + } + } + + return new StringTransferable(buf.toString()); + } + return null; + } + + public int getSourceActions(JComponent c) { + return COPY; + } + + public boolean importData(JComponent c, Transferable t) { + if (canImport(c, t.getTransferDataFlavors())) { + try { + String str = (String)t.getTransferData(DataFlavor.stringFlavor); + handleImport(c, str); + return true; + } catch (UnsupportedFlavorException ufe) { + } catch (IOException ioe) { + } + } + + return false; + } + + public boolean canImport(JComponent c, DataFlavor[] flavors) { + for (int i = 0; i < flavors.length; i++) { + if (DataFlavor.stringFlavor.equals(flavors[i])) { + return true; + } + } + return false; + } + + private void handleImport(JComponent c, String str) { + // do whatever you want with the string here + try { + makeVisible(debugger.parseAddress(str)); + clearSelection(); + table.clearSelection(); + } catch (NumberFormatException e) { + System.err.println("Unable to parse address \"" + str + "\""); + } + } + }); + + // Supporting keyboard scrolling + // See src/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java, + // search for Table.AncestorInputMap + + // Actions to override: + // selectPreviousRow, selectNextRow, + // scrollUpChangeSelection, scrollDownChangeSelection, + // selectPreviousRowExtendSelection, selectNextRowExtendSelection, + // scrollDownExtendSelection, scrollUpExtendSelection (Shift-PgDn/PgUp) + + ActionMap map = table.getActionMap(); + + // Up arrow + installActionWrapper(map, "selectPreviousRow", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + if (table.getSelectedRow() == 0) { + scrollBar.scrollUpOrLeft(); + table.setRowSelectionInterval(0, 0); + } else { + super.actionPerformed(e); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Down arrow + installActionWrapper(map, "selectNextRow", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + int row = table.getSelectedRow(); + if (row >= numUsableRows) { + scrollBar.scrollDownOrRight(); + table.setRowSelectionInterval(row, row); + } else { + super.actionPerformed(e); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Page up + installActionWrapper(map, "scrollUpChangeSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + int row = table.getSelectedRow(); + scrollBar.pageUpOrLeft(); + if (row >= 0) { + table.setRowSelectionInterval(row, row); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Page down + installActionWrapper(map, "scrollDownChangeSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + clearSelection(); + int row = table.getSelectedRow(); + scrollBar.pageDownOrRight(); + if (row >= 0) { + table.setRowSelectionInterval(row, row); + } + maybeGrabSelection(); + endUpdate(); + } + }); + // Shift + Up arrow + installActionWrapper(map, "selectPreviousRowExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() - 1; + int newAnchor = getRowAnchor(); + if (newLead < 0) { + scrollBar.scrollUpOrLeft(); + ++newLead; + ++newAnchor; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + // Shift + Left arrow + installActionWrapper(map, "selectPreviousColumnExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + } + int newLead = Math.max(0, getColLead() - 1); + setSelection(getRowAnchor(), getRowLead(), getColAnchor(), newLead); + // printSelection(); + endUpdate(); + } + }); + // Shift + Down arrow + installActionWrapper(map, "selectNextRowExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() + 1; + int newAnchor = getRowAnchor(); + if (newLead > numUsableRows) { + scrollBar.scrollDownOrRight(); + --newLead; + --newAnchor; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + // Shift + Right arrow + installActionWrapper(map, "selectNextColumnExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + } + int newLead = Math.min(model.getColumnCount() - 1, getColLead() + 1); + setSelection(getRowAnchor(), getRowLead(), getColAnchor(), newLead); + // printSelection(); + endUpdate(); + } + }); + // Shift + Page up + installActionWrapper(map, "scrollUpExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() - numUsableRows; + int newAnchor = getRowAnchor(); + if (newLead < 0) { + scrollBar.pageUpOrLeft(); + newLead += numUsableRows; + newAnchor += numUsableRows; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + // Shift + Page down + installActionWrapper(map, "scrollDownExtendSelection", new ActionWrapper() { + public void actionPerformed(ActionEvent e) { + beginUpdate(); + if (!haveAnchor()) { + setAnchorFromTable(); + setLeadFromTable(); + // setAnchor(table.getSelectedRow()); + // setLead(table.getSelectedRow()); + } + int newLead = getRowLead() + numUsableRows; + int newAnchor = getRowAnchor(); + if (newLead > numUsableRows) { + scrollBar.pageDownOrRight(); + newLead -= numUsableRows; + newAnchor -= numUsableRows; + } + setSelection(newAnchor, newLead, getColAnchor(), getColLead()); + // printSelection(); + endUpdate(); + } + }); + + // Clear our notion of selection upon mouse press + table.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (shouldIgnore(e)) { + return; + } + // Make shift-clicking work properly + if (e.isShiftDown()) { + maybeGrabSelection(); + return; + } + // System.err.println(" Clearing selection on mouse press"); + clearSelection(); + } + }); + + // Watch for mouse going out of bounds + table.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseDragged(MouseEvent e) { + if (shouldIgnore(e)) { + // System.err.println(" (Ignoring consumed mouse event)"); + return; + } + + // Look for drag events outside table and scroll if necessary + Point p = e.getPoint(); + if (table.rowAtPoint(p) == -1) { + // See whether we are above or below the table + Rectangle rect = new Rectangle(); + getBounds(rect); + beginUpdate(); + if (p.y < rect.y) { + // System.err.println(" Scrolling up due to mouse event"); + // Scroll up + scrollBar.scrollUpOrLeft(); + setSelection(getRowAnchor(), 0, getColAnchor(), getColLead()); + } else { + // System.err.println(" Scrolling down due to mouse event"); + // Scroll down + scrollBar.scrollDownOrRight(); + setSelection(getRowAnchor(), numUsableRows, getColAnchor(), getColLead()); + } + // printSelection(); + endUpdate(); + } else { + maybeGrabSelection(); + } + } + }); + + + add(table, BorderLayout.CENTER); + + // Make sure we recompute number of visible rows + addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + recomputeNumVisibleRows(); + constrain(); + } + }); + addHierarchyListener(new HierarchyListener() { + public void hierarchyChanged(HierarchyEvent e) { + recomputeNumVisibleRows(); + constrain(); + } + }); + updateFromScrollBar(); + } + + /** Makes the given address visible somewhere in the window */ + public void makeVisible(Address addr) { + BigInteger bi = addressToBigInt(addr); + scrollBar.setValueHP(bi); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void setupScrollBar() { + if (is64Bit) { + // 64-bit mode + scrollBar = + new HighPrecisionJScrollBar( + Scrollbar.VERTICAL, + new BigInteger(1, new byte[] { + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC})); + scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08})); + scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40})); + } else { + // 32-bit mode + scrollBar= + new HighPrecisionJScrollBar( + Scrollbar.VERTICAL, + new BigInteger(1, new byte[] { + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + new BigInteger(1, new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC})); + scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04})); + scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x20})); + } + scrollBar.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + updateFromScrollBar(); + } + }); + } + + private void updateFromScrollBar() { + beginUpdate(); + BigInteger oldStartVal = startVal; + startVal = scrollBar.getValueHP(); + constrain(); + model.fireTableDataChanged(); + if (oldStartVal != null) { + modifySelection(oldStartVal.subtract(startVal).intValue() / addressSize); + } + endUpdate(); + } + + private void constrain() { + BigInteger offset = new BigInteger(Integer.toString(addressSize * (numUsableRows))); + BigInteger endVal = startVal.add(offset); + if (endVal.compareTo(scrollBar.getMaximumHP()) > 0) { + startVal = scrollBar.getMaximumHP().subtract(offset); + endVal = scrollBar.getMaximumHP(); + scrollBar.setValueHP(startVal); + model.fireTableDataChanged(); + } + } + + private void recomputeNumVisibleRows() { + Rectangle rect = new Rectangle(); + getBounds(rect); + int h = table.getRowHeight(); + numVisibleRows = (rect.height + (h - 1)) / h; + numUsableRows = numVisibleRows - 2; + scrollBar.setBlockIncrementHP(new BigInteger(Integer.toString(addressSize * (numUsableRows)))); + model.fireTableDataChanged(); + // FIXME: refresh selection + } + + private String bigIntToHexString(BigInteger bi) { + StringBuffer buf = new StringBuffer(); + buf.append("0x"); + String val = bi.toString(16); + for (int i = 0; i < ((2 * addressSize) - val.length()); i++) { + buf.append('0'); + } + buf.append(val); + return buf.toString(); + } + + private Address bigIntToAddress(BigInteger i) { + String s = bigIntToHexString(i); + return debugger.parseAddress(s); + } + + private BigInteger addressToBigInt(Address a) { + String s = addressToString(a); + if (!s.startsWith("0x")) { + throw new NumberFormatException(s); + } + return new BigInteger(s.substring(2), 16); + } + + private String addressToString(Address a) { + if (a == null) { + if (is64Bit) { + return "0x0000000000000000"; + } else { + return "0x00000000"; + } + } + return a.toString(); + } + + private static void installActionWrapper(ActionMap map, + String actionName, + ActionWrapper wrapper) { + wrapper.setParent(map.get(actionName)); + map.put(actionName, wrapper); + } + + private boolean shouldIgnore(MouseEvent e) { + return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && table.isEnabled())); + } + + private void clearSelection() { + haveAnchor = false; + haveLead = false; + } + + private int updateLevel; + private boolean updating() { return updateLevel > 0; } + private void beginUpdate() { ++updateLevel; } + private void endUpdate() { --updateLevel; } + + private boolean haveAnchor() { return haveAnchor; } + private boolean haveLead() { return haveLead; } + private boolean haveSelection() { return haveAnchor() && haveLead(); } + private int getRowAnchor() { return rowAnchorIndex; } + private int getColAnchor() { return colAnchorIndex; } + private int getRowLead() { return rowLeadIndex; } + private int getColLead() { return colLeadIndex; } + + private void setAnchorFromTable() { + setAnchor(table.getSelectionModel().getAnchorSelectionIndex(), + table.getColumnModel().getSelectionModel().getAnchorSelectionIndex()); + } + private void setLeadFromTable() { + setLead(table.getSelectionModel().getAnchorSelectionIndex(), + table.getColumnModel().getSelectionModel().getAnchorSelectionIndex()); + } + private void setAnchor(int row, int col) { + rowAnchorIndex = row; + colAnchorIndex = col; + haveAnchor = true; + } + private void setLead(int row, int col) { + rowLeadIndex = row; + colLeadIndex = col; + haveLead = true; + } + private int clamp(int val, int min, int max) { + return Math.max(Math.min(val, max), min); + } + private void maybeGrabSelection() { + if (table.getSelectedRow() != -1) { + // Grab selection + ListSelectionModel rowSel = table.getSelectionModel(); + ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); + if (!haveAnchor()) { + // System.err.println("Updating from table's selection"); + setSelection(rowSel.getAnchorSelectionIndex(), rowSel.getLeadSelectionIndex(), + colSel.getAnchorSelectionIndex(), colSel.getLeadSelectionIndex()); + } else { + // System.err.println("Updating lead from table's selection"); + setSelection(getRowAnchor(), rowSel.getLeadSelectionIndex(), + getColAnchor(), colSel.getLeadSelectionIndex()); + } + // printSelection(); + } + } + private void setSelection(int rowAnchor, int rowLead, int colAnchor, int colLead) { + setAnchor(rowAnchor, colAnchor); + setLead(rowLead, colLead); + table.setRowSelectionInterval(clamp(rowAnchor, 0, numUsableRows), + clamp(rowLead, 0, numUsableRows)); + table.setColumnSelectionInterval(colAnchor, colLead); + } + private void modifySelection(int amount) { + if (haveSelection()) { + setSelection(getRowAnchor() + amount, getRowLead() + amount, + getColAnchor(), getColLead()); + } + } + private void printSelection() { + System.err.println("Selection updated to (" + + model.getValueAt(getRowAnchor(), getColAnchor()) + + ", " + + model.getValueAt(getRowLead(), getColLead()) + ") [(" + + getRowAnchor() + ", " + getColAnchor() + "), (" + + getRowLead() + ", " + getColLead() + ")]"); + } +}