/*
* Copyright (c) 2007, 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.util.*;
import java.util.List;
import javax.swing.*;
import com.sun.max.gui.*;
import com.sun.max.ins.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.value.*;
import com.sun.max.ins.value.WordValueLabel.ValueMode;
import com.sun.max.ins.view.*;
import com.sun.max.ins.view.InspectionViews.ViewKind;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.unsafe.*;
/**
* A view that renders memory contents, letting you select the start address, etc.
*/
public final class MemoryBytesView extends AbstractView<MemoryBytesView> {
private static final int TRACE_VALUE = 2;
private static final ViewKind VIEW_KIND = ViewKind.MEMORY_BYTES;
private static final String SHORT_NAME = "Memory as bytes";
private static final String LONG_NAME = "Memory Bytes View";
private static MemoryBytesViewManager viewManager;
public static MemoryBytesViewManager makeViewManager(Inspection inspection) {
if (viewManager == null) {
viewManager = new MemoryBytesViewManager(inspection);
}
return viewManager;
}
public static final class MemoryBytesViewManager extends AbstractMultiViewManager<MemoryBytesView> implements MemoryBytesViewFactory {
private final InspectorAction interactiveMakeViewAction;
private final List<InspectorAction> makeViewActions;
protected MemoryBytesViewManager(final Inspection inspection) {
super(inspection, VIEW_KIND, SHORT_NAME, LONG_NAME);
Trace.begin(TRACE_VALUE, tracePrefix() + "creating");
interactiveMakeViewAction = new InspectorAction(inspection(), "View memory bytes at address...") {
@Override
protected void procedure() {
new AddressInputDialog(inspection, inspection.vm().bootImageStart(), "View memory bytes at address...", "View") {
@Override
public void entered(Address address) {
makeView(address).highlight();
}
};
}
};
makeViewActions = new ArrayList<InspectorAction>(1);
makeViewActions.add(interactiveMakeViewAction);
Trace.end(TRACE_VALUE, tracePrefix() + "creating");
}
public MemoryBytesView makeView(Address address) {
final MemoryBytesView memoryBytesView = new MemoryBytesView(inspection(), address, 64, 1, 8);
notifyAddingView(memoryBytesView);
return memoryBytesView;
}
public MemoryBytesView makeView(MaxObject object) {
final MaxMemoryRegion region = object.objectMemoryRegion();
final long nBytes = region.nBytes();
assert nBytes < Integer.MAX_VALUE;
final MemoryBytesView memoryBytesView = new MemoryBytesView(inspection(), region.start(), (int) nBytes, 1, 16);
notifyAddingView(memoryBytesView);
return memoryBytesView;
}
public InspectorAction makeViewAction() {
return interactiveMakeViewAction;
}
public InspectorAction makeViewAction(final Address address, String actionTitle) {
return new InspectorAction(inspection(), actionTitle == null ? "View memory as bytes" : actionTitle) {
@Override
protected void procedure() {
makeView(address);
}
};
}
@Override
protected List<InspectorAction> makeViewActions() {
return makeViewActions;
}
}
// TODO (mlvdv) actions should be enabled by memory being available to read
private final Rectangle originalFrameGeometry;
private Address address;
private int numberOfGroups;
private int numberOfBytesPerGroup;
private int numberOfGroupsPerLine;
/**
* @param inspection
* @param address
* @param numberOfGroups
* @param numberOfBytesPerGroup
* @param numberOfGroupsPerLine
*/
private MemoryBytesView(Inspection inspection, Address address, int numberOfGroups, int numberOfBytesPerGroup, int numberOfGroupsPerLine) {
super(inspection, VIEW_KIND, null);
this.address = address;
this.numberOfGroups = numberOfGroups;
this.numberOfBytesPerGroup = numberOfBytesPerGroup;
this.numberOfGroupsPerLine = numberOfGroupsPerLine;
Trace.begin(TRACE_VALUE, tracePrefix() + " creating for " + getTextForTitle());
createFrame(true);
inspection.gui().setLocationRelativeToMouse(this, inspection.preference().geometry().newFrameDiagonalOffset());
originalFrameGeometry = getGeometry();
Trace.end(TRACE_VALUE, tracePrefix() + " creating for " + getTextForTitle());
}
private JComponent createController() {
final JPanel controller = new InspectorPanel(inspection(), new SpringLayout());
controller.add(new TextLabel(inspection(), "start:"));
final AddressInputField.Hex addressField = new AddressInputField.Hex(inspection(), address) {
@Override
public void update(Address a) {
if (!a.equals(address)) {
address = a;
MemoryBytesView.this.reconstructView();
}
}
};
controller.add(addressField);
controller.add(new TextLabel(inspection(), "bytes/group:"));
final AddressInputField.Decimal numberOfBytesPerGroupField = new AddressInputField.Decimal(inspection(), Address.fromInt(numberOfBytesPerGroup)) {
@Override
public void update(Address value) {
if (!value.equals(numberOfBytesPerGroup)) {
numberOfBytesPerGroup = value.toInt();
MemoryBytesView.this.reconstructView();
}
}
};
numberOfBytesPerGroupField.setRange(1, 16);
controller.add(numberOfBytesPerGroupField);
controller.add(new TextLabel(inspection(), "groups:"));
final AddressInputField.Decimal numberOfGroupsField = new AddressInputField.Decimal(inspection(), Address.fromInt(numberOfGroups)) {
@Override
public void update(Address value) {
if (!value.equals(numberOfGroups)) {
numberOfGroups = value.toInt();
MemoryBytesView.this.reconstructView();
}
}
};
numberOfGroupsField.setRange(1, 1024);
controller.add(numberOfGroupsField);
controller.add(new TextLabel(inspection(), "groups/line:"));
final AddressInputField.Decimal numberOfGroupsPerLineField = new AddressInputField.Decimal(inspection(), Address.fromInt(numberOfGroupsPerLine)) {
@Override
public void update(Address value) {
if (!value.equals(numberOfGroupsPerLine)) {
numberOfGroupsPerLine = value.toInt();
MemoryBytesView.this.reconstructView();
}
}
};
numberOfGroupsPerLineField.setRange(1, 256);
controller.add(numberOfGroupsPerLineField);
SpringUtilities.makeCompactGrid(controller, 4);
return controller;
}
private TextLabel[] memoryLabels;
// Char labels displayed as Word data (with fixed width font) so that horizontal alignment works
private TextLabel[] charLabels;
@Override
protected void refreshState(boolean force) {
final byte[] bytes = new byte[numberOfBytesPerGroup];
for (int i = 0; i < numberOfGroups; i++) {
final Address address = this.address.plus(i * numberOfBytesPerGroup);
vm().memoryIO().readBytes(address, bytes);
memoryLabels[i].setText(byteGroupToString(bytes));
memoryLabels[i].setToolTipText(address.toHexString());
switch (numberOfBytesPerGroup) {
case 1: {
final char ch = (char) bytes[0];
charLabels[i].setText(Character.toString(ch));
break;
}
case 2: {
final char ch = (char) ((bytes[1] * 256) + bytes[0]);
charLabels[i].setText(Character.toString(ch));
break;
}
default: {
break;
}
}
}
}
private JPanel contentPane;
@Override
public Rectangle defaultGeometry() {
return originalFrameGeometry;
}
@Override
public String getTextForTitle() {
return MemoryBytesView.class.getSimpleName() + ": " + address.toHexString();
}
@Override
protected void createViewContent() {
contentPane = new InspectorPanel(inspection());
setContentPane(contentPane);
contentPane.removeAll();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
contentPane.add(createController());
final JPanel view = new InspectorPanel(inspection(), new SpringLayout());
contentPane.add(view);
int numberOfLines = numberOfGroups / numberOfGroupsPerLine;
if (numberOfGroups % numberOfGroupsPerLine != 0) {
numberOfLines++;
}
final int numberOfLabels = numberOfLines * numberOfGroupsPerLine;
memoryLabels = new TextLabel[numberOfLabels];
charLabels = new TextLabel[numberOfLabels];
final String space = Strings.times(' ', 2 * numberOfBytesPerGroup);
Address lineAddress = address;
final int numberOfBytesPerLine = numberOfGroupsPerLine * numberOfBytesPerGroup;
for (int line = 0; line < numberOfLines; line++) {
final ValueLabel lineAddressLabel = new WordValueLabel(inspection(), ValueMode.WORD, lineAddress, null);
view.add(lineAddressLabel);
lineAddress = lineAddress.plus(numberOfBytesPerLine);
for (int group = 0; group < numberOfGroupsPerLine; group++) {
final int index = (line * numberOfGroupsPerLine) + group;
memoryLabels[index] = new TextLabel(inspection(), space);
view.add(memoryLabels[index]);
}
final Space leftSpace = new Space();
view.add(leftSpace);
for (int group = 0; group < numberOfGroupsPerLine; group++) {
final int index = (line * numberOfGroupsPerLine) + group;
charLabels[index] = new TextLabel(inspection(), space);
view.add(charLabels[index]);
}
}
// Populate menu bar
final InspectorMenu defaultMenu = makeMenu(MenuKind.DEFAULT_MENU);
defaultMenu.add(defaultMenuItems(MenuKind.DEFAULT_MENU));
defaultMenu.addSeparator();
defaultMenu.add(views().deactivateOtherViewsAction(ViewKind.MEMORY_BYTES, this));
defaultMenu.add(views().deactivateAllViewsAction(ViewKind.MEMORY_BYTES));
final InspectorMenu memoryMenu = makeMenu(MenuKind.MEMORY_MENU);
memoryMenu.add(defaultMenuItems(MenuKind.MEMORY_MENU));
memoryMenu.add(views().activateSingletonViewAction(ViewKind.ALLOCATIONS));
makeMenu(MenuKind.VIEW_MENU).add(defaultMenuItems(MenuKind.VIEW_MENU));
forceRefresh();
SpringUtilities.makeCompactGrid(view, numberOfLines * 2, 1 + numberOfGroupsPerLine, 0, 0, 5, 5);
}
private String byteGroupToString(byte[] bytes) {
String s = "";
switch (vm().bootImage().header.endianness()) {
case LITTLE:
for (int i = bytes.length - 1; i >= 0; i--) {
s += String.format("%02X", bytes[i]);
}
break;
case BIG:
for (int i = 0; i < bytes.length; i++) {
s += String.format("%02X", bytes[i]);
}
break;
}
return s;
}
@Override
public void viewClosing() {
// Unsubscribe to view preferences, when we get them.
super.viewClosing();
}
@Override
public void vmProcessTerminated() {
dispose();
}
}