/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.classmgr;
import java.io.PrintStream;
import java.util.ArrayList;
import org.jnode.util.NumberUtils;
import org.jnode.vm.objects.VmSystemObject;
/**
* This table is a mapping between a program counter and an address of that
* program counter in compiled code.
*
* @author epr
*/
public final class VmAddressMap extends VmSystemObject {
private AddressPcEntry list;
private VmMethod[] methodTable;
private int[] offsetTable;
/**
* Program counter at a given index
*/
private char[] pcTable;
/**
* Index in the methodTable at a index (null is methodTable.length == 1)
*/
private byte[] methodIndexTable;
/**
* Inline depth at a given index (0..) (null is methodTable.length == 1)
*/
private byte[] inlineDepthTable;
/**
* Create a new instance
*/
public VmAddressMap() {
}
/**
* Add an address-pc mapping. This method cannot be used after {@link #lock()}
* has been called.
*
* @param offset Offset from the start of the method
* @param pc
* @param method
* @param inlineDepth
*/
public void add(VmMethod method, int pc, int offset, int inlineDepth) {
if (offsetTable != null) {
throw new RuntimeException("Address table is locked");
}
final AddressPcEntry entry = new AddressPcEntry(method, pc, offset, inlineDepth);
if (list == null) {
list = entry;
} else {
// Sort on offset (from low to high)
if (offset < list.offset) {
entry.next = list;
list = entry;
} else {
AddressPcEntry p = list;
while ((p.next != null) && (offset > p.next.offset)) {
p = p.next;
}
entry.next = p.next;
p.next = entry;
}
}
}
/**
* Gets the last known address index that corresponds to the given code offset.
*
* @param offset
* @return the index, or {@code -1} if the offset is not found in the offset table.
*/
public final int getIndexForOffset(int offset) {
final int[] offsetTable = this.offsetTable;
if (offsetTable != null) {
final int length = offsetTable.length;
for (int i = 1; i < length; i++) {
if (offsetTable[i] > offset) {
return i - 1;
}
}
}
return -1;
}
/**
* Gets the method at the given index.
*
* @param index
* @return the method
*/
public final VmMethod getMethodAtIndex(int index) {
if (index < 0) {
return null;
} else if (methodIndexTable == null) {
return methodTable[0];
} else {
return methodTable[methodIndexTable[index] & 0xFF];
}
}
/**
* Gets the program counter at the given index.
*
* @param index
* @return the program counter
*/
public final int getProgramCounterAtIndex(int index) {
if (index < 0) {
return 0;
} else {
return pcTable[index];
}
}
/**
* Gets the index that contains the call to the (inlined) method that is
* identified by the given index.
*
* @param index
* @return The call-site index, or {@code -1} if there is no call-site within this
* address map.
*/
public final int getCallSiteIndex(int index) {
final byte[] inlineDepthTable = this.inlineDepthTable;
if ((index >= 0) && (inlineDepthTable != null)) {
final int tableLength = inlineDepthTable.length;
index = Math.min(index, tableLength - 1);
final int depth = inlineDepthTable[index--];
while (index >= 0) {
if (inlineDepthTable[index] == depth - 1) {
return index;
} else {
index--;
}
}
}
return -1;
}
/**
* Convert the address map to its final form. After a call to this method,
* the {@link #add(VmMethod, int, int, int)} method cannot be used.
*/
final void lock() {
AddressPcEntry p = list;
int count = 0;
int maxInlineDepth = 0;
final ArrayList<VmMethod> methods = new ArrayList<VmMethod>();
while (p != null) {
count++;
maxInlineDepth = Math.max(maxInlineDepth, p.inlineDepth & 0xFF);
final VmMethod m = p.method;
if (!methods.contains(m)) {
methods.add(m);
}
p = p.next;
}
final int methodCount = methods.size();
final int[] offsetTable = new int[count];
final char[] pcTable = new char[count];
final byte[] methodIndexTable = (methodCount > 1) ? new byte[count] : null;
final byte[] inlineDepthTable = (maxInlineDepth == 0) ? null : new byte[count];
this.methodTable = (VmMethod[]) methods.toArray(new VmMethod[methodCount]);
p = list;
int i = 0;
int lastOffset = -1;
while (p != null) {
if (methodIndexTable != null) {
methodIndexTable[i] = (byte) methods.indexOf(p.method);
}
pcTable[i] = p.pc;
offsetTable[i] = p.offset;
if (inlineDepthTable != null) {
inlineDepthTable[i] = p.inlineDepth;
}
if (p.offset < lastOffset) {
throw new InternalError("unordered offset found");
}
lastOffset = p.offset;
i++;
p = p.next;
}
this.offsetTable = offsetTable;
this.pcTable = pcTable;
this.methodIndexTable = methodIndexTable;
this.inlineDepthTable = inlineDepthTable;
this.list = null;
}
public void writeTo(PrintStream out) {
for (int i = 0; i < offsetTable.length; i++) {
final int methodIdx = (methodIndexTable != null) ? methodIndexTable[i] : 0;
final int pc = pcTable[i];
final int offset = offsetTable[i];
out.println(methodTable[methodIdx].getName() + ", pc[" + pc
+ "]\t0x" + NumberUtils.hex(offset));
}
}
static class AddressPcEntry extends VmSystemObject {
final VmMethod method;
final char pc;
final int offset;
final byte inlineDepth;
AddressPcEntry next;
public AddressPcEntry(VmMethod method, int pc, int offset, int inlineDepth) {
this.method = method;
this.pc = (char) pc;
this.offset = offset;
this.inlineDepth = (byte) inlineDepth;
}
}
}