/* * Copyright (c) 2009, 2012, 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 com.sun.max.ins.memory; import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; import com.sun.max.ins.*; import com.sun.max.ins.debug.*; import com.sun.max.ins.gui.*; import com.sun.max.ins.value.*; import com.sun.max.ins.value.WordValueLabel.ValueMode; import com.sun.max.tele.*; import com.sun.max.unsafe.*; import com.sun.max.vm.value.*; /** * A table specialized for displaying memory regions that the VM has allocated from the OS. */ public final class AllocationsTable extends InspectorTable { private final InspectorView view; private final AllocationsTableModel tableModel; AllocationsTable(Inspection inspection, InspectorView view, AllocationsViewPreferences viewPreferences) { super(inspection); this.view = view; this.tableModel = new AllocationsTableModel(inspection); AllocationsColumnModel columnModel = new AllocationsColumnModel(this, this.tableModel, viewPreferences); configureDefaultTable(tableModel, columnModel); } @Override protected InspectorPopupMenu getPopupMenu(int row, int col, MouseEvent mouseEvent) { final InspectorPopupMenu menu = new InspectorPopupMenu(); final MaxMemoryRegion memoryRegion = tableModel.getMemoryRegion(row); final String regionName = memoryRegion.regionName(); final JMenu viewMenu = new JMenu("View for \"" + regionName + "\""); InspectorAction ownerAction = null; if (memoryRegion instanceof MaxEntityMemoryRegion) { MaxEntityMemoryRegion entityMemoryRegion = (MaxEntityMemoryRegion) memoryRegion; if (entityMemoryRegion.owner() != null) { MaxObject representation = entityMemoryRegion.owner().representation(); if (representation != null) { final String actionTitle = "Owner: " + inspection().nameDisplay().longName(representation); ownerAction = views().objects().makeViewAction(representation, actionTitle); } } } if (ownerAction == null) { ownerAction = actions().inertAction("Owner"); } viewMenu.add(ownerAction); viewMenu.add(views().memory().makeViewAction(memoryRegion, regionName, "Memory")); menu.add(viewMenu); // menu.add(actions().setRegionWatchpoint(memoryRegionDisplay, "Watch region memory")); menu.add(Watchpoints.createEditMenu(inspection(), tableModel.getWatchpoints(row))); menu.add(Watchpoints.createRemoveActionOrMenu(inspection(), tableModel.getWatchpoints(row))); return menu; } /** * Sets table selection to the memory region, if any, that is the current user focus. */ @Override public void updateFocusSelection() { final MaxMemoryRegion memoryRegion = focus().memoryRegion(); final int row = tableModel.findRow(memoryRegion); updateSelection(row); } @Override public void valueChanged(ListSelectionEvent listSelectionEvent) { // Row selection changed, perhaps by user mouse click or navigation; // update user focus to follow the selection. super.valueChanged(listSelectionEvent); if (!listSelectionEvent.getValueIsAdjusting()) { final int row = getSelectedRow(); if (row >= 0) { focus().setMemoryRegion(tableModel.getMemoryRegion(row)); } } } /** * {@inheritDoc}. * <p> * Color the text specially in the row where a watchpoint is triggered */ @Override public Color cellForegroundColor(int row, int col) { final MaxWatchpointEvent watchpointEvent = vm().state().watchpointEvent(); if (watchpointEvent != null && tableModel.getMemoryRegion(row).contains(watchpointEvent.address())) { return preference().style().debugIPTagColor(); } return null; } public InspectorView getView() { return view; } /** * Sets a display filter that will cause only the specified rows * to be displayed. * * @param displayedRows the rows to be displayed, sorted in ascending order, null if all should be displayed. */ public void setDisplayedRows(int[] displayedRows) { tableModel.setDisplayedRows(displayedRows); } private final class AllocationsColumnModel extends InspectorTableColumnModel<AllocationsColumnKind> { private AllocationsColumnModel(InspectorTable table, InspectorMemoryTableModel tableModel, AllocationsViewPreferences viewPreferences) { super(inspection(), AllocationsColumnKind.values().length, viewPreferences); addColumnIfSupported(AllocationsColumnKind.TAG, new MemoryTagTableCellRenderer(inspection(), table, tableModel), null); addColumnIfSupported(AllocationsColumnKind.NAME, new NameCellRenderer(), null); addColumnIfSupported(AllocationsColumnKind.START, new StartAddressCellRenderer(), null); addColumnIfSupported(AllocationsColumnKind.MARK, new MarkAddressCellRenderer(), null); addColumnIfSupported(AllocationsColumnKind.END, new EndAddressCellRenderer(), null); addColumnIfSupported(AllocationsColumnKind.SIZE, new SizeCellRenderer(), null); addColumnIfSupported(AllocationsColumnKind.ALLOC, new AllocCellRenderer(), null); } } /** * A table data model built around the list of currently allocated memory regions in the VM. * */ private final class AllocationsTableModel extends InspectorMemoryTableModel { private MaxMemoryRegion[] sortedRegions = null; private int[] displayedRows = null; public AllocationsTableModel(Inspection inspection) { super(inspection, Address.zero()); refresh(); } @Override public void refresh() { final List<MaxEntityMemoryRegion<? extends MaxEntity> > memoryAllocations = vm().state().memoryAllocations(); sortedRegions = memoryAllocations.toArray(new MaxMemoryRegion[memoryAllocations.size()]); Arrays.sort(sortedRegions, MaxMemoryRegion.Util.startComparator()); // Flush any filtering displayedRows = null; super.refresh(); } /** * {@inheritDoc} * <p> * The number of rows actually being displayed, possibly filtered. */ public int getRowCount() { return displayedRows == null ? sortedRegions.length : displayedRows.length; } public int getColumnCount() { return AllocationsColumnKind.values().length; } @Override public Class<?> getColumnClass(int row) { return MaxMemoryRegion.class; } public Object getValueAt(int row, int col) { return getMemoryRegion(row); } @Override public MaxMemoryRegion getMemoryRegion(int row) { return sortedRegions[displayed2ModelRow(row)]; } @Override public int getOffset(int row) { return getAddress(row).toInt(); } /** * {@inheritDoc} * <p> * Only find rows that are being displayed. */ @Override public int findRow(Address address) { final int displayedRowCount = getRowCount(); for (int row = 0; row < displayedRowCount; row++) { if (getMemoryRegion(row).contains(address)) { return row; } } return -1; } @Override public String getRowDescription(int row) { return "Memory region \"" + getMemoryRegion(row).regionName() + "\""; } /** * Find the row, if any, whose memory region specifies the same region * of VM memory as the one specified. * * @param memoryRegion description of a region of VM memory */ int findRow(MaxMemoryRegion memoryRegion) { final int displayedRowCount = getRowCount(); for (int row = 0; row < displayedRowCount; row++) { if (getMemoryRegion(row).sameAs(memoryRegion)) { return row; } } return -1; } public void setDisplayedRows(int[] displayedRows) { this.displayedRows = displayedRows; this.fireTableDataChanged(); } private int displayed2ModelRow(int displayedRow) { return displayedRows == null ? displayedRow : displayedRows[displayedRow]; } } private final class NameCellRenderer implements TableCellRenderer, Prober { // The labels have important user interaction state, so create one per memory region and keep them around, // even though they may not always appear in the same row. private final Map<MaxMemoryRegion, InspectorLabel> regionToLabel = new HashMap<MaxMemoryRegion, InspectorLabel>(); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxMemoryRegion memoryRegion = (MaxMemoryRegion) value; if (memoryRegion == null) { return gui().getUnavailableDataTableCellRenderer(); } InspectorLabel label = regionToLabel.get(memoryRegion); if (label == null) { label = new MemoryRegionNameLabel(inspection(), memoryRegion); regionToLabel.put(memoryRegion, label); } label.setForeground(cellForegroundColor(row, column)); label.setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (Prober prober : regionToLabel.values()) { prober.redisplay(); } } public void refresh(boolean force) { for (Prober prober : regionToLabel.values()) { prober.refresh(force); } } } private final class StartAddressCellRenderer implements TableCellRenderer, Prober { // ValueLabels have important user interaction state, so create one per memory region and keep them around, // even though they may not always appear in the same row. private final Map<MaxMemoryRegion, InspectorLabel> regionToLabel = new HashMap<MaxMemoryRegion, InspectorLabel>(); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxMemoryRegion memoryRegion = (MaxMemoryRegion) value; if (memoryRegion == null) { return gui().getUnavailableDataTableCellRenderer(); } InspectorLabel label = regionToLabel.get(memoryRegion); if (label == null) { label = new WordValueLabel(inspection(), ValueMode.WORD, AllocationsTable.this) { @Override public Value fetchValue() { return WordValue.from(memoryRegion.start()); } }; label.setToolTipPrefix(tableModel.getRowDescription(row) + "<br>Starts @"); label.setOpaque(true); regionToLabel.put(memoryRegion, label); } label.setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (Prober prober : regionToLabel.values()) { prober.redisplay(); } } public void refresh(boolean force) { for (Prober prober : regionToLabel.values()) { prober.refresh(force); } } } private final class MarkAddressCellRenderer implements TableCellRenderer, Prober { // ValueLabels have important user interaction state, so create one per memory region and keep them around, // even though they may not always appear in the same row. private final Map<MaxMemoryRegion, DataLabel.AddressAsHex> regionToLabel = new HashMap<MaxMemoryRegion, DataLabel.AddressAsHex>(); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxMemoryRegion memoryRegion = (MaxMemoryRegion) value; if (memoryRegion == null || !memoryRegion.isAllocated()) { return gui().getUnavailableDataTableCellRenderer(); } final Address mark = memoryRegion.mark(); if (mark == null) { return gui().getEmptyDataTableCellRenderer(); } DataLabel.AddressAsHex label = regionToLabel.get(memoryRegion); if (label == null) { label = new DataLabel.AddressAsHex(inspection(), Address.zero()); label.setToolTipPrefix(tableModel.getRowDescription(row) + "<br>Next allocation @"); label.setOpaque(true); regionToLabel.put(memoryRegion, label); } label.setValue(mark); label.setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (Prober prober : regionToLabel.values()) { prober.redisplay(); } } public void refresh(boolean force) { for (Prober prober : regionToLabel.values()) { prober.refresh(force); } } } private final class EndAddressCellRenderer implements TableCellRenderer, Prober { // Labels have important user interaction state, so create one per memory region and keep them around, // even though they may not always appear in the same row. private final Map<MaxMemoryRegion, DataLabel.AddressAsHex> regionToLabel = new HashMap<MaxMemoryRegion, DataLabel.AddressAsHex>(); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxMemoryRegion memoryRegion = (MaxMemoryRegion) value; if (memoryRegion == null || !memoryRegion.isAllocated()) { return gui().getUnavailableDataTableCellRenderer(); } DataLabel.AddressAsHex label = regionToLabel.get(memoryRegion); if (label == null) { label = new DataLabel.AddressAsHex(inspection(), Address.zero()); label.setOpaque(true); regionToLabel.put(memoryRegion, label); } label.setValue(memoryRegion.end()); label.setToolTipPrefix(tableModel.getRowDescription(row) + "<br>Ends @"); label.setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (Prober prober : regionToLabel.values()) { prober.redisplay(); } } public void refresh(boolean force) { for (Prober prober : regionToLabel.values()) { prober.refresh(force); } } } private final class SizeCellRenderer implements TableCellRenderer, Prober { private final Map<MaxMemoryRegion, InspectorLabel> regionToLabel = new HashMap<MaxMemoryRegion, InspectorLabel>(); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxMemoryRegion memoryRegion = (MaxMemoryRegion) value; if (memoryRegion == null || !memoryRegion.isAllocated()) { return gui().getUnavailableDataTableCellRenderer(); } InspectorLabel label = regionToLabel.get(memoryRegion); if (label == null) { label = new MemoryAllocationsSizeLabel(inspection(), memoryRegion); regionToLabel.put(memoryRegion, label); } // Can't set the prefix (row description) permanently on the label, as they // are cached by location and may not always be displayed on the same row. label.setToolTipPrefix(tableModel.getRowDescription(row) + "<br>Size = "); label.setForeground(cellForegroundColor(row, column)); label.setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (Prober prober : regionToLabel.values()) { prober.redisplay(); } } public void refresh(boolean force) { for (Prober prober : regionToLabel.values()) { prober.refresh(force); } } } private final class AllocCellRenderer implements TableCellRenderer, Prober { private final Map<MaxMemoryRegion, InspectorLabel> regionToLabel = new HashMap<MaxMemoryRegion, InspectorLabel>(); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxMemoryRegion memoryRegion = (MaxMemoryRegion) value; if (memoryRegion == null || !memoryRegion.isAllocated()) { return gui().getUnavailableDataTableCellRenderer(); } InspectorLabel label = regionToLabel.get(memoryRegion); if (label == null) { label = new MemoryRegionAllocationLabel(inspection(), memoryRegion, AllocationsTable.this); regionToLabel.put(memoryRegion, label); } label.setToolTipPrefix(tableModel.getRowDescription(row) + "<br>Alloc = "); label.setForeground(cellForegroundColor(row, column)); label.setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (Prober prober : regionToLabel.values()) { prober.redisplay(); } } public void refresh(boolean force) { for (Prober prober : regionToLabel.values()) { prober.refresh(force); } } } }