/*
* Copyright (c) 2011, 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.debug;
import static com.sun.max.tele.MaxProcessState.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.sun.max.ins.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.gui.TableColumnVisibilityPreferences.TableColumnViewPreferenceListener;
import com.sun.max.ins.util.*;
import com.sun.max.ins.view.*;
import com.sun.max.ins.view.InspectionViews.ViewKind;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.MaxMarkBitmap.MarkColor;
import com.sun.max.unsafe.*;
public class MarkBitmapView extends AbstractView<MarkBitmapView> implements TableColumnViewPreferenceListener {
private static final int TRACE_VALUE = 1;
private static final ViewKind VIEW_KIND = ViewKind.MARK_BITMAP;
private static final String SHORT_NAME = "Mark Bitmap";
private static final String LONG_NAME = "Mark Bitmap View";
private static final String GEOMETRY_SETTINGS_KEY = "markBitmapViewGeometry";
public static final class MarkBitmapViewManager extends AbstractSingletonViewManager<MarkBitmapView> {
protected MarkBitmapViewManager(Inspection inspection) {
super(inspection, VIEW_KIND, SHORT_NAME, LONG_NAME);
focus().addListener(new InspectionFocusAdapter() {
@Override
public void markBitIndexFocusChanged(int oldHeapMarkBit, int heapMarkBit) {
if (heapMarkBit >= 0) {
final MarkBitmapView view = MarkBitmapViewManager.this.activateView();
view.scrollToRowCentered(heapMarkBit);
}
}
});
}
@Override
protected MarkBitmapView createView(Inspection inspection) {
return new MarkBitmapView(inspection);
}
@Override
public boolean isSupported() {
return vm().heap().hasMarkBitmap();
}
}
private static MarkBitmapViewManager viewManager = null;
public static MarkBitmapViewManager makeViewManager(Inspection inspection) {
if (viewManager == null) {
viewManager = new MarkBitmapViewManager(inspection);
}
return viewManager;
}
public static enum ViewMode {
SET_BIT("Set Mark Bit", "Scroll to next/previous set mark bit",
"Scroll to previous set mark bit", "Scroll to next set mark bit"),
BLACK("Black Mark", "Scroll to next/previous object marked BLACK",
"Scroll to previous object marked BLACK", "Scroll to next object marked BLACK"),
GRAY("Gray Mark", "Scroll to next/previous object marked GRAY",
"Scroll to previous object marked GRAY", "Scroll to next object marked GRAY"),
WHITE("White Mark", "Scroll to next/previous object marked WHITE",
"Scroll to previous object marked WHITE", "Scroll to next object marked WHITE"),
INVALID("Invalid Mark", "Scroll to next/previous INVALID object mark",
"Scroll to previous INVALID object mark", "Scroll to next INVALID object mark");
private final String label;
private final String description;
private final String previousToolTip;
private final String nextToolTip;
/**
* @param label the label that identifies the mode
* @param description description of the mode
* @param previousToolTip description of the move backwards action in this mode
* @param nextToolTip description of the move forward action in this mode
*/
private ViewMode(String label, String description, String previousToolTip, String nextToolTip) {
this.label = label;
this.description = description;
this.previousToolTip = previousToolTip;
this.nextToolTip = nextToolTip;
}
public String label() {
return label;
}
public String description() {
return description;
}
public String previousToolTip() {
return previousToolTip;
}
public String nextToolTip() {
return nextToolTip;
}
}
private final InspectorAction scrollToFocusAction;
private final InspectorAction viewBitmapMemoryAction;
private final InspectorAction viewBitmapDataAction;
private MaxMarkBitmap markBitmap = null;
private MaxObject markBitmapData = null;
private MemoryColoringTable table;
private InspectorScrollPane scrollPane;
private JToolBar toolBar;
private final InspectorComboBox viewModeComboBox; // TODO (mlvdv) generic in Java 7
private final JLabel viewModeComboBoxRenderer; // Holds current view mode, even across view reconstructions.
private final InspectorButton previousButton;
private final InspectorButton nextButton;
private final InspectorButton prefsButton;
// This is a singleton viewer, so only use a single level of view preferences.
private final MarkBitmapViewPreferences viewPreferences;
// TODO (mlvdv) generify the combo box when abandon Java 6
@SuppressWarnings("unchecked")
protected MarkBitmapView(Inspection inspection) {
super(inspection, VIEW_KIND, GEOMETRY_SETTINGS_KEY);
markBitmap = vm().heap().markBitmap();
viewPreferences = MarkBitmapViewPreferences.globalPreferences(inspection());
viewPreferences.addListener(this);
// The combo box holds the current view mode
viewModeComboBox = new InspectorComboBox(inspection, ViewMode.values());
viewModeComboBox.setSelectedItem(ViewMode.SET_BIT);
// Add the listener after the initial selection is set; we're not ready for an update yet.
viewModeComboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateViewMode();
}
});
viewModeComboBoxRenderer = new JLabel();
// TODO (mlvdv) can't change to the generic form here until we drop support for jdk6 and earlier
viewModeComboBox.setRenderer(new ListCellRenderer() {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
final ViewMode viewMode = (ViewMode) value;
viewModeComboBoxRenderer.setText(viewMode.label());
return viewModeComboBoxRenderer;
}
});
previousButton = new InspectorButton(inspection(), new AbstractAction() {
public void actionPerformed(ActionEvent e) {
moveBack();
}
});
final InspectorStyle style = preference().style();
previousButton.setIcon(style.navigationBackIcon());
nextButton = new InspectorButton(inspection(), new AbstractAction() {
public void actionPerformed(ActionEvent e) {
moveForward();
}
});
nextButton.setIcon(style.navigationForwardIcon());
prefsButton = new InspectorButton(inspection(), new AbstractAction() {
public void actionPerformed(ActionEvent e) {
new SimpleDialog(inspection(), viewPreferences.getPanel(), "View Preferences", true);
}
});
prefsButton.setText(null);
prefsButton.setToolTipText("Column view options");
prefsButton.setIcon(style.generalPreferencesIcon());
scrollToFocusAction = new ScrollToFocusAction(inspection);
viewBitmapMemoryAction = new ViewBitmapMemoryAction(inspection);
viewBitmapDataAction = new ViewBitmapDataAction(inspection);
refreshAllActions(true);
createFrame(true);
Trace.end(TRACE_VALUE, tracePrefix() + " initializing");
}
@Override
public String getTextForTitle() {
return viewManager.shortName();
}
@Override
protected InspectorTable getTable() {
return table;
}
@Override
protected void createViewContent() {
// Populate menu bar
makeMenu(MenuKind.DEFAULT_MENU).add(defaultMenuItems(MenuKind.DEFAULT_MENU));
final InspectorMenu memoryMenu = makeMenu(MenuKind.MEMORY_MENU);
memoryMenu.add(viewBitmapMemoryAction);
memoryMenu.add(actions().viewSelectedMemoryWatchpointAction());
memoryMenu.add(defaultMenuItems(MenuKind.MEMORY_MENU));
final InspectorMenu objectMenu = makeMenu(MenuKind.OBJECT_MENU);
objectMenu.add(viewBitmapDataAction);
objectMenu.add(defaultMenuItems(MenuKind.OBJECT_MENU));
final InspectorMenu viewMenu = makeMenu(MenuKind.VIEW_MENU);
viewMenu.add(scrollToFocusAction);
viewMenu.add(defaultMenuItems(MenuKind.VIEW_MENU));
if (markBitmap == null) {
createNullContent();
} else {
createTableContent();
}
}
@Override
protected void refreshState(boolean force) {
if (markBitmap == null && vm().heap().markBitmap() != null) {
markBitmap = vm().heap().markBitmap();
reconstructView();
}
if (table != null) {
table.refresh(force);
}
refreshAllActions(force);
}
/**
* Creates a placeholder content pane, to be used until we discover that the mark bitmap has been allocated.
*/
private void createNullContent() {
final InspectorPanel nullPanel = new InspectorPanel(inspection(), new BorderLayout());
nullPanel.add(new PlainLabel(inspection(), "<no bitmap>"), BorderLayout.PAGE_START);
setContentPane(nullPanel);
}
/**
* Creates a pane that displays and permits interaction with the mark bitmap, assumes that the bitmap has been allocated in VM memory.
*/
private void createTableContent() {
markBitmap = vm().heap().markBitmap();
assert markBitmap != null;
markBitmapData = markBitmap.representation();
table = new MemoryColoringTable(inspection(), this, markBitmap, viewPreferences);
final InspectorPanel panel = new InspectorPanel(inspection(), new BorderLayout());
toolBar = new InspectorToolBar(inspection());
toolBar.setBorder(preference().style().defaultPaneBorder());
toolBar.setFloatable(false);
toolBar.setRollover(true);
toolBar.add(Box.createHorizontalGlue());
toolBar.add(previousButton);
toolBar.add(viewModeComboBox);
toolBar.add(nextButton);
toolBar.add(prefsButton);
toolBar.add(Box.createHorizontalGlue());
panel.add(toolBar, BorderLayout.NORTH);
scrollPane = new InspectorScrollPane(inspection(), table);
panel.add(scrollPane, BorderLayout.CENTER);
setContentPane(panel);
// Force everything into consistency with the current view mode.
updateViewMode();
}
/**
* Gets current view mode.
*/
private ViewMode viewMode() {
return (ViewMode) viewModeComboBox.getSelectedItem();
}
/**
* Sets current view mode and updates related state.
*/
private void setViewMode(ViewMode viewMode) {
viewModeComboBox.setSelectedItem(viewMode);
updateViewMode();
}
/**
* Sets the current view parameters to the default state.
*/
private void clearViewMode() {
setViewMode(ViewMode.SET_BIT);
}
/**
* Updates state related to current view mode.
*/
private void updateViewMode() {
previousButton.setToolTipText(viewMode().previousToolTip());
nextButton.setToolTipText(viewMode().nextToolTip());
setTitle();
}
private void refreshAllActions(boolean force) {
viewBitmapMemoryAction.refresh(force);
viewBitmapDataAction.refresh(force);
}
private int firstVisibleRow() {
return table.rowAtPoint(new Point(0, scrollPane.getViewport().getViewRect().y));
}
private int lastVisibleRow() {
final Rectangle visible = scrollPane.getViewport().getViewRect();
return table.rowAtPoint(new Point(0, visible.y + visible.height - 10));
}
private boolean rowIsVible(int row) {
return firstVisibleRow() <= row && row <= lastVisibleRow();
}
private void scrollToRowCentered(int row) {
final int nRows = lastVisibleRow() - firstVisibleRow();
table.scrollToRows(row - nRows / 3, row + 2 * nRows / 3);
}
/**
* Modal navigation; the kind of move depends on the currently selected view mode.
*/
private void moveForward() {
int startIndex = table.getSelectedRow();
if (!rowIsVible(startIndex)) {
startIndex = firstVisibleRow();
}
int goalIndex = -1;
switch (viewMode()) {
case SET_BIT:
goalIndex = markBitmap.nextSetBitAfter(startIndex);
break;
case BLACK:
goalIndex = markBitmap.nextMarkAfter(startIndex, MarkColor.MARK_BLACK);
break;
case GRAY:
goalIndex = markBitmap.nextMarkAfter(startIndex, MarkColor.MARK_GRAY);
break;
case WHITE:
goalIndex = markBitmap.nextMarkAfter(startIndex, MarkColor.MARK_WHITE);
break;
case INVALID:
goalIndex = markBitmap.nextMarkAfter(startIndex, MarkColor.MARK_INVALID);
break;
default:
InspectorError.unknownCase();
}
if (goalIndex < 0) {
flash(3);
} else {
focus().setAddress(markBitmap.heapAddress(goalIndex));
scrollToRowCentered(goalIndex);
}
}
/**
* Modal navigation; the kind of move depends on the currently selected view mode.
*/
private void moveBack() {
int startIndex = table.getSelectedRow();
if (!rowIsVible(startIndex)) {
startIndex = lastVisibleRow();
}
int goalIndex = -1;
switch (viewMode()) {
case SET_BIT:
goalIndex = markBitmap.previousSetBitBefore(startIndex);
break;
case BLACK:
goalIndex = markBitmap.previousMarkBefore(startIndex, MarkColor.MARK_BLACK);
break;
case GRAY:
goalIndex = markBitmap.previousMarkBefore(startIndex, MarkColor.MARK_GRAY);
break;
case WHITE:
goalIndex = markBitmap.previousMarkBefore(startIndex, MarkColor.MARK_WHITE);
break;
case INVALID:
goalIndex = markBitmap.previousMarkBefore(startIndex, MarkColor.MARK_INVALID);
break;
default:
InspectorError.unknownCase();
}
if (goalIndex < 0) {
flash(3);
} else {
focus().setAddress(markBitmap.heapAddress(goalIndex));
scrollToRowCentered(goalIndex);
}
}
@Override
public void threadFocusSet(MaxThread oldThread, MaxThread thread) {
// Memory view displays are sensitive to the current thread selection (for register values)
forceRefresh();
}
@Override
public void addressFocusChanged(Address oldAddress, Address newAddress) {
forceRefresh();
}
@Override
public void watchpointSetChanged() {
if (vm().state().processState() != TERMINATED) {
forceRefresh();
}
}
@Override
public InspectorAction getViewOptionsAction() {
return new InspectorAction(inspection(), "View Options") {
@Override
public void procedure() {
new TableColumnVisibilityPreferences.ColumnPreferencesDialog<MarkBitmapColumnKind>(inspection(), viewManager.shortName() + " View Options", viewPreferences);
}
};
}
public void tableColumnViewPreferencesChanged() {
reconstructView();
}
@Override
public void viewClosing() {
viewPreferences.removeListener(this);
super.viewClosing();
}
@Override
public void vmProcessTerminated() {
markBitmap = null;
reconstructView();
}
private final class ScrollToFocusAction extends InspectorAction {
public ScrollToFocusAction(Inspection inspection) {
super(inspection(), "Scroll to selected (covered) memory location");
refresh(true);
}
@Override
protected void procedure() {
scrollToRowCentered(table.findCoveringRow(focus().address()));
MarkBitmapView.this.forceRefresh();
}
@Override
public void refresh(boolean force) {
setEnabled(markBitmap != null && markBitmap.isCovered(focus().address()));
}
}
private final class ViewBitmapDataAction extends InspectorAction {
private static final String TITLE = "View Bitmap Data as Array";
public ViewBitmapDataAction(Inspection inspection) {
super(inspection, TITLE);
refresh(true);
}
@Override
protected void procedure() {
if (markBitmapData != null) {
focus().setHeapObject(markBitmapData);
}
}
@Override
public void refresh(boolean force) {
setEnabled(markBitmapData != null);
}
}
private final class ViewBitmapMemoryAction extends InspectorAction {
private static final String TITLE = "View Bitmap Memory";
public ViewBitmapMemoryAction(Inspection inspection) {
super(inspection, TITLE);
refresh(true);
}
@Override
protected void procedure() {
final MaxEntityMemoryRegion<MaxMarkBitmap> memoryRegion = vm().heap().markBitmap().memoryRegion();
if (memoryRegion.isAllocated()) {
views().memory().makeView(memoryRegion, null);
}
}
@Override
public void refresh(boolean force) {
final MaxMarkBitmap markBitMap = vm().heap().markBitmap();
setEnabled(markBitMap != null && markBitMap.memoryRegion().isAllocated());
}
}
}