/*
* 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.tele.method;
import static com.sun.max.platform.Platform.*;
import java.io.*;
import java.util.*;
import com.sun.cri.ci.*;
import com.sun.cri.ri.*;
import com.sun.max.tele.*;
import com.sun.max.tele.data.*;
import com.sun.max.tele.memory.*;
import com.sun.max.tele.method.CodeLocation.*;
import com.sun.max.tele.object.*;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
public class TeleNativeFunction extends AbstractVmHolder implements MaxNativeFunction, Comparable<TeleNativeFunction> {
/**
* Description of a region of native function code discovered in the VM's process.
* <p>
* This region has no children.
*/
static final class NativeFunctionMemoryRegion extends TeleFixedMemoryRegion implements MaxEntityMemoryRegion<MaxNativeFunction> {
private static final List<MaxEntityMemoryRegion<? extends MaxEntity>> EMPTY = Collections.emptyList();
private MaxNativeFunction owner;
private NativeFunctionMemoryRegion(MaxVM vm, MaxNativeFunction owner) {
super(vm, owner.name(), owner.getCodeStart(), owner.length());
this.owner = owner;
}
@SuppressWarnings("unchecked")
public MaxEntityMemoryRegion< ? extends MaxEntity> parent() {
if (owner.library() == null) {
return null;
} else {
return owner.library().memoryRegion();
}
}
public List<MaxEntityMemoryRegion< ? extends MaxEntity>> children() {
return EMPTY;
}
public MaxNativeFunction owner() {
return owner;
}
}
/**
* Summary information about a sequence of native disassembled machine code instructions about
* which little is known.
*/
private final class NativeFunctionMachineCodeInfo implements MaxMachineCodeInfo {
private final List<MachineCodeLocation> machineCodeLocations;
/**
* Unmodifiable list of all instruction indexes where a label is present.
*/
private final List<Integer> labelIndexes;
NativeFunctionMachineCodeInfo() throws MaxInvalidAddressException {
instructions = getInstructions();
final int length = instructions.size();
final List<MachineCodeLocation> locations = new ArrayList<MachineCodeLocation>(length);
final List<Integer> labels = new ArrayList<Integer>();
try {
for (int index = 0; index < length; index++) {
final TargetCodeInstruction targetCodeInstruction = instructions.get(index);
locations.add(codeLocations().createMachineCodeLocation(targetCodeInstruction.address, "native function code instruction"));
if (targetCodeInstruction.label != null) {
labels.add(index);
}
}
} catch (InvalidCodeAddressException e) {
TeleError.unexpected("Getting native function code info failed @ " + e.getAddressString() + ": " + e.getMessage());
}
machineCodeLocations = locations;
labelIndexes = Collections.unmodifiableList(labels);
}
public int length() {
return instructions.size();
}
public TargetCodeInstruction instruction(int index) throws IllegalArgumentException {
if (index < 0 || index >= instructions.size()) {
throw new IllegalArgumentException();
}
return instructions.get(index);
}
public int findInstructionIndex(Address address) {
if (address != null) {
final int length = instructions.size();
if (address.greaterEqual(instructions.get(0).address)) {
for (int index = 1; index < length; index++) {
instructions.get(index);
if (address.lessThan(instructions.get(index).address)) {
return index - 1;
}
}
final TargetCodeInstruction lastInstruction = instructions.get(instructions.size() - 1);
if (address.lessThan(lastInstruction.address.plus(lastInstruction.bytes.length))) {
return length - 1;
}
}
}
return -1;
}
public MachineCodeLocation instructionLocation(int index) {
if (index < 0 || index >= instructions.size()) {
throw new IllegalArgumentException();
}
return machineCodeLocations.get(index);
}
public boolean isSafepoint(int index) throws IllegalArgumentException {
if (index < 0 || index >= instructions.size()) {
throw new IllegalArgumentException();
}
return false;
}
public boolean isCall(int index) throws IllegalArgumentException {
// TODO (mlvdv) how to determine this?
return false;
}
public boolean isNativeCall(int index) throws IllegalArgumentException {
return isCall(index);
}
public boolean isBytecodeBoundary(int index) throws IllegalArgumentException {
if (index < 0 || index >= instructions.size()) {
throw new IllegalArgumentException();
}
return false;
}
public CiDebugInfo debugInfoAt(int index) throws IllegalArgumentException {
if (index < 0 || index >= instructions.size()) {
throw new IllegalArgumentException();
}
return null;
}
public int opcode(int index) throws IllegalArgumentException {
if (index < 0 || index >= instructions.size()) {
throw new IllegalArgumentException();
}
return -1;
}
public RiMethod calleeAt(int index) throws IllegalArgumentException {
if (index < 0 || index >= instructions.size()) {
throw new IllegalArgumentException();
}
return null;
}
public List<Integer> labelIndexes() {
return labelIndexes;
}
public int[] bciToMachineCodePositionMap() {
return null;
}
}
private final String name;
Address base;
int length;
private TeleNativeLibrary lib; // null for disconnected function (rare)
private NativeFunctionMemoryRegion nativeFunctionMemoryRegion;
private MaxMachineCodeInfo machineCodeInfo;
private List<TargetCodeInstruction> instructions;
private List<MachineCodeLocation> instructionLocations;
private CodeLocation codeStartLocation = null;
private TeleNativeFunction(TeleVM vm, String name, Address base) {
super(vm);
this.name = name;
this.base = base;
}
/**
* Create a {@link TeleNativeFunction}.
* @param vm
* @param name function name
* @param offset initially the offset from the base of the library.
* @param lib associated native library.
* @throws MaxInvalidAddressException
*/
public TeleNativeFunction(TeleVM vm, String name, Address offset, TeleNativeLibrary lib) throws MaxInvalidAddressException {
this(vm, name, offset);
this.lib = lib;
}
/**
* Create a disconnected native function.
* @param vm
* @param name
* @param base
* @param length
*/
public TeleNativeFunction(TeleVM vm, String name, Address base, long length) throws MaxInvalidAddressException {
this(vm, name, base);
this.length = (int) length;
this.nativeFunctionMemoryRegion = new NativeFunctionMemoryRegion(vm(), this);
}
public void updateAddress() {
assert lib != null && lib.base().isNotZero();
base = base.plus(lib.base());
}
public void updateLength(int length) throws MaxInvalidAddressException {
this.length = length;
this.nativeFunctionMemoryRegion = new NativeFunctionMemoryRegion(vm(), this);
}
@Override
public String toString() {
return name;
}
@Override
public String entityName() {
return qualName();
}
@Override
public String entityDescription() {
return "Native function " + qualName();
}
@Override
public MaxEntityMemoryRegion<MaxNativeFunction> memoryRegion() {
return nativeFunctionMemoryRegion;
}
@Override
public boolean contains(Address address) {
return nativeFunctionMemoryRegion.contains(address);
}
@Override
public TeleObject representation() {
// No distinguished object in VM runtime represents a native function.
return null;
}
@Override
public String name() {
return name;
}
@Override
public int length() {
return length;
}
@Override
public String qualName() {
return lib == null ? name : lib.entityName() + ":" + name;
}
@Override
public MaxNativeLibrary library() {
return lib;
}
public int compareTo(TeleNativeFunction other) {
if (lib.sortByName()) {
return name.compareToIgnoreCase(other.name);
} else {
if (base.lessThan(other.base)) {
return -1;
} else if (base.greaterThan(other.base)) {
return 1;
} else {
return 0;
}
}
}
public MaxMachineCodeInfo getMachineCodeInfo() {
if (machineCodeInfo == null) {
try {
machineCodeInfo = new NativeFunctionMachineCodeInfo();
} catch (MaxInvalidAddressException ex) {
TeleError.unexpected("error accessing native function memory", ex);
}
}
return machineCodeInfo;
}
/** {@inheritDoc}
* <p>
* We don't bother to check if native code has changed once we have read and disassembled it.
*/
@Override
public int codeVersion() {
return 0;
}
public Address getCodeStart() {
return base;
}
public CodeLocation getCodeStartLocation() {
if (codeStartLocation == null) {
try {
codeStartLocation = codeLocations().createMachineCodeLocation(getCodeStart(), "code start location in external native code");
} catch (InvalidCodeAddressException e) {
}
}
return codeStartLocation;
}
public CodeLocation getCallEntryLocation() {
return null;
}
public void writeSummary(PrintStream printStream) {
printStream.println("External native code: " + entityName());
printStream.println(" ***UNIMPLEMENTED*** for native functions");
}
private List<TargetCodeInstruction> getInstructions() throws MaxInvalidAddressException {
if (instructions == null && vm().tryLock()) {
byte[] code = null;
final Address codeStart = getCodeStart();
try {
final long nBytes = nativeFunctionMemoryRegion.nBytes();
assert nBytes < Integer.MAX_VALUE;
code = memory().readBytes(codeStart, (int) nBytes);
} catch (DataIOError dataIOError) {
throw new MaxInvalidAddressException(codeStart, "Can't read data at " + codeStart.to0xHexString());
} finally {
vm().unlock();
}
if (code != null) {
instructions = TeleDisassembler.decode(platform(), codeStart, code, null);
}
}
return instructions;
}
}