/*
* Copyright (c) 2009, 2011, 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.vm.hosted;
import java.io.*;
import java.lang.reflect.*;
import java.nio.*;
import com.sun.max.program.option.*;
import com.sun.max.vm.collect.*;
import com.sun.max.vm.hosted.BootImage.FieldSection;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.layout.Layout.HeaderField;
/**
* A utility for printing the contents of a {@link BootImage}.
*/
public class BootImagePrinter {
public static class Range {
final int start;
final int size;
public Range(int start, int size) {
this.start = start;
this.size = size;
}
public boolean contains(Range other) {
return other.start >= start && other.end() <= end();
}
public int end() {
return start + size;
}
@Override
public String toString() {
return "[" + start + '-' + (end() - 1) + ']';
}
}
private final OptionSet options = new OptionSet();
private final Option<Boolean> help = options.newBooleanOption("help", false,
"Show help message and exit.");
private final Option<File> outputFileOption = options.newFileOption("o", (File) null,
"The file to which output is written instead of standard out.");
private final Option<String> sectionsOption = options.newStringOption("s", "HST",
"The sections to be printed: (H)eader, (S)tringInfo, (T)railer, (h)eap, (c)ode, (r)elocation data, (p)adding. " +
"A sub-range of the data sections ('h', 'c', 'r', 'p') can be specified with a range suffix of the form '{<start>:<size>}' where " +
"<start> and <size> can be hexadecimal (with a '0x' prefix) or plain decimal. For example, " +
"'-s=h{0x100:300}c{+100:20}' will dump 300 bytes of the heap starting at heap address 0x100 and 20 bytes " +
"of the code starting at code address 100 (the '+' prefix specifies that <start> is relative to the first " +
"address in the code section). Note that the heap and code are contiguous and share the same address space. " +
"All other data sections have their own address space.");
private final Option<String> filterOption = options.newStringOption("filter", null,
"Only heap and code values matching containing the specified hex value substring are printed.");
private Range heapRegion;
private Range codeRegion;
private int wordSize;
private BootImage bootImage;
private String filter;
public static void main(String[] args) throws IOException, BootImageException {
new BootImagePrinter().run(args);
}
public int run(String[] args) throws BootImageException, IOException {
options.parseArguments(args);
if (help.getValue()) {
options.printHelp(System.out, 80);
return 0;
}
String[] arguments = options.getArguments();
if (arguments.length != 1) {
System.out.println("Expected exactly 1 non-option command line argument, got " + arguments.length);
options.printHelp(System.out, 80);
return 1;
}
String bootImageFilePath = arguments[0];
File bootImageFile = new File(bootImageFilePath);
bootImage = new BootImage(bootImageFile);
heapRegion = new Range(0, bootImage.header.heapSize);
codeRegion = new Range(heapRegion.end(), bootImage.header.codeSize);
wordSize = bootImage.header.wordSize;
PrintStream out = System.out;
if (outputFileOption.getValue() != null) {
out = new PrintStream(new FileOutputStream(outputFileOption.getValue()));
}
filter = filterOption.getValue();
int cursor = 0;
String sections = sectionsOption.getValue();
while (cursor < sections.length()) {
char section = sections.charAt(cursor++);
switch (section) {
case 'H': {
printHeader(out);
break;
}
case 'S': {
printStringInfo(out);
break;
}
case 'T': {
printTrailer(out);
break;
}
case 'h': {
Range subregion = parseNarrowingRange(sections, cursor, heapRegion);
if (subregion != null) {
cursor = sections.indexOf('}', cursor) + 1;
}
printHeap(out, subregion);
break;
}
case 'c': {
Range subregion = parseNarrowingRange(sections, cursor, codeRegion);
if (subregion != null) {
cursor = sections.indexOf('}', cursor) + 1;
}
printCode(out, subregion);
break;
}
case 'r': {
Range subregion = parseNarrowingRange(sections, cursor, new Range(0, bootImage.header.relocationDataSize));
if (subregion != null) {
cursor = sections.indexOf('}', cursor) + 1;
}
printRelocationData(out, subregion);
break;
}
case 'p': {
Range subregion = parseNarrowingRange(sections, cursor, new Range(0, bootImage.paddingSize()));
if (subregion != null) {
cursor = sections.indexOf('}', cursor) + 1;
}
printPadding(out, subregion);
break;
}
}
}
if (outputFileOption.getValue() != null) {
out.close();
}
return 0;
}
private static int parseInt(String s) {
if (s.startsWith("0x")) {
return Integer.parseInt(s.substring(2), 16);
}
return Integer.parseInt(s);
}
private static Range parseNarrowingRange(String s, int cursor, Range range) {
if (cursor < s.length() && s.charAt(cursor) == '{') {
int offset = 0;
if (cursor + 1 < s.length() && s.charAt(cursor + 1) == '+') {
offset = range.start;
cursor++;
}
int colon = s.indexOf(':', cursor + 1);
if (colon == -1) {
throw new IllegalArgumentException("Range at index " + cursor + " in sections string is missing ':'");
}
int closingBrace = s.indexOf('}', colon + 1);
if (closingBrace == -1) {
throw new IllegalArgumentException("Range at index " + cursor + " in sections string is missing '}'");
}
Range result = new Range(offset + parseInt(s.substring(cursor + 1, colon)), parseInt(s.substring(colon + 1, closingBrace)));
if (!range.contains(result)) {
throw new IllegalArgumentException("Narrowing range " + result + " is not completely contained by " + range);
}
return result;
}
return null;
}
private void printPadding(PrintStream out, Range subregion) {
byte[] padding = bootImage.padding;
printData(out, "PADDING", bootImage.paddingOffset(), padding, subregion);
}
private void printRelocationData(PrintStream out, Range subregion) {
byte[] relocationData = bootImage.relocationData;
printData(out, "RELOCATION DATA", bootImage.relocationDataOffset(), relocationData, subregion);
}
private void printHeader(PrintStream out) {
printSection(out, bootImage.header, "HEADER");
}
private void printTrailer(PrintStream out) {
printSection(out, bootImage.trailer, "TRAILER");
}
private void printHeap(PrintStream out, Range subregion) {
printHeapOrCodeData(out, "HEAP", heapRegion, bootImage.heapAndCode(), subregion, bootImage.heapOffset());
}
private void printCode(PrintStream out, Range subregion) {
printHeapOrCodeData(out, "CODE", codeRegion, bootImage.heapAndCode(), subregion, bootImage.codeOffset());
}
private void printStringInfo(PrintStream out) {
printSection(out, bootImage.stringInfo, "STRING INFO");
}
private int wordAlign(int value) {
return (value + (wordSize - 1)) & ~(wordSize - 1);
}
private void printData(PrintStream out, String name, int offsetInImage, byte[] buffer, Range subregion) {
int startAddress = 0;
int size = buffer.length;
out.println(sectionHeader(name, size, startAddress, offsetInImage));
int end = startAddress + size;
int address;
if (subregion != null) {
address = subregion.start;
end = address + subregion.size;
} else {
address = startAddress;
end = startAddress + size;
}
assert end <= buffer.length;
while (address < end) {
out.printf("0x%08x:", address);
for (int i = 0; i < 32 && address < end; ++i) {
out.printf(" %02x", buffer[address]);
address++;
}
out.println();
}
}
private long readWord(int pointer, ByteBuffer memory) {
if (wordSize == 8) {
return memory.getLong(pointer);
}
return memory.getInt(pointer);
}
private static boolean contains(int pointer, int size, ByteBuffer memory) {
return pointer >= 0 && pointer + size <= memory.limit();
}
private boolean isValidOrigin(int origin, ByteBuffer memory) {
GeneralLayout layout = bootImage.vmConfiguration.layoutScheme().generalLayout;
int hubOffset = layout.getOffsetFromOrigin(HeaderField.HUB).toInt();
if (contains(origin + hubOffset, wordSize, memory)) {
int hub = (int) readWord(origin + hubOffset, memory);
if (contains(hub + hubOffset, wordSize, memory)) {
int hubHub = (int) readWord(hub + hubOffset, memory);
if (contains(hubHub + hubOffset, wordSize, memory)) {
int hubHubHub = (int) readWord(hubHub + hubOffset, memory);
return hubHub == hubHubHub;
}
}
}
return false;
}
private void printHeapOrCodeData(PrintStream out, String name, Range region, ByteBuffer heapAndCode, Range subregion, int offsetInImage) {
int startAddress = region.start;
int size = region.size;
out.println(sectionHeader(name, size, startAddress, offsetInImage));
int end;
int address;
ByteArrayBitMap relocMap = new ByteArrayBitMap(bootImage.relocationData);
if (subregion != null) {
address = wordAlign(subregion.start);
end = address + subregion.size;
} else {
address = startAddress;
end = startAddress + size;
}
while (address < end) {
int addressIndex = address / wordSize;
char pointerMark = relocMap.isSet(addressIndex) ? '*' : ' ';
String originLabel = isValidOrigin(address, heapAndCode) ? " <-- origin" : "";
if (wordSize == 8) {
String value = String.format("%016x", heapAndCode.getLong(address));
if (filter == null || value.contains(filter)) {
out.printf("%c0x%08x: 0x%s%s%n", pointerMark, address, value, originLabel);
}
} else {
String value = String.format("%08x", heapAndCode.getInt(address));
if (filter == null || value.contains(filter)) {
out.printf("%c0x%08x: 0x%s%s%n", pointerMark, address, value, originLabel);
}
}
address += wordSize;
}
}
private String sectionHeader(String name, int size, int startAddress, int offsetInImage) {
int end = startAddress + size;
int wordSizeWidth = wordSize * 2;
return String.format("--- %s: start=0x%0" + wordSizeWidth + "x, end=0x%0" + wordSizeWidth +
"x, size=%d[0x%08x], image-offset=%d[0x%08x] ---", name, startAddress, end, size, size, offsetInImage, offsetInImage);
}
private void printSection(PrintStream out, FieldSection section, String name) {
int nameWidth = 0;
int valueWidth = 0;
for (Field field : section.fields()) {
nameWidth = Math.max(nameWidth, field.getName().length());
try {
valueWidth = Math.max(valueWidth, String.valueOf(field.get(section)).length());
} catch (Exception e) {
}
}
out.println(sectionHeader(name, section.size(), 0, section.offset()));
for (Field field : section.fields()) {
Object value;
try {
value = field.get(section);
} catch (Exception e) {
value = "error: " + e;
}
if (section.fieldType() == int.class) {
out.printf("%" + nameWidth + "s: %-" + valueWidth + "s 0x%08x%n", field.getName(), value, value);
} else {
assert section.fieldType() == String.class;
out.printf("%" + nameWidth + "s: %-" + valueWidth + "s%n", field.getName(), value);
}
}
}
}