/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.mm.mmtk;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.VM;
import org.jikesrvm.Services;
import org.jikesrvm.classloader.MemberReference;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethods;
import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
import org.jikesrvm.compilers.opt.runtimesupport.OptEncodedCallSiteTree;
import org.jikesrvm.compilers.opt.runtimesupport.OptMachineCodeMap;
import org.jikesrvm.objectmodel.MiscHeader;
import org.jikesrvm.objectmodel.ObjectModel;
import org.jikesrvm.objectmodel.TIB;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.scheduler.RVMThread;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
/**
* Class that supports scanning Objects or Arrays for references
* during tracing, handling those references, and computing death times
*/
@Uninterruptible public final class TraceInterface extends org.mmtk.vm.TraceInterface implements ArchitectureSpecific.ArchConstants {
/***********************************************************************
*
* Class variables
*/
private static byte[][] allocCallMethods;
static {
/* Build the list of "own methods" */
allocCallMethods = new byte[13][];
allocCallMethods[0] = "postAlloc".getBytes();
allocCallMethods[1] = "traceAlloc".getBytes();
allocCallMethods[2] = "allocateScalar".getBytes();
allocCallMethods[3] = "allocateArray".getBytes();
allocCallMethods[4] = "clone".getBytes();
allocCallMethods[5] = "alloc".getBytes();
allocCallMethods[6] = "buildMultiDimensionalArray".getBytes();
allocCallMethods[7] = "resolvedNewScalar".getBytes();
allocCallMethods[8] = "resolvedNewArray".getBytes();
allocCallMethods[9] = "unresolvedNewScalar".getBytes();
allocCallMethods[10] = "unresolvedNewArray".getBytes();
allocCallMethods[11] = "cloneScalar".getBytes();
allocCallMethods[12] = "cloneArray".getBytes();
}
/***********************************************************************
*
* Public Methods
*/
/**
* Returns if the VM is ready for a garbage collection.
*
* @return True if the RVM is ready for GC, false otherwise.
*/
public boolean gcEnabled() {
return RVMThread.gcEnabled();
}
/**
* Given a method name, determine if it is a "real" method or one
* used for allocation/tracing.
*
* @param name The method name to test as an array of bytes
* @return True if the method is a "real" method, false otherwise.
*/
private boolean isAllocCall(byte[] name) {
for (int i = 0; i < allocCallMethods.length; i++) {
byte[] funcName = Services.getArrayNoBarrier(allocCallMethods, i);
if (Magic.getArrayLength(name) == Magic.getArrayLength(funcName)) {
/* Compare the letters in the allocCallMethod */
int j = Magic.getArrayLength(funcName) - 1;
while (j >= 0) {
if (Services.getArrayNoBarrier(name, j) !=
Services.getArrayNoBarrier(funcName, j))
break;
j--;
}
if (j == -1)
return true;
}
}
return false;
}
/**
* This adjusts the offset into an object to reflect what it would look like
* if the fields were laid out in memory space immediately after the object
* pointer.
*
* @param isScalar If this is a pointer store to a scalar object
* @param src The address of the source object
* @param slot The address within <code>src</code> into which
* the update will be stored
* @return The easy to understand offset of the slot
*/
public Offset adjustSlotOffset(boolean isScalar,
ObjectReference src,
Address slot) {
/* Offset scalar objects so that the fields appear to begin at offset 0
of the object. */
Offset offset = slot.diff(src.toAddress());
if (isScalar)
return offset.minus(getHeaderEndOffset());
else
return offset;
}
/**
* This skips over the frames added by the tracing algorithm, outputs
* information identifying the method the containts the "new" call triggering
* the allocation, and returns the address of the first non-trace, non-alloc
* stack frame.
*
* @param typeRef The type reference (tib) of the object just allocated
* @return The frame pointer address for the method that allocated the object
*/
@NoInline
@Interruptible // This can't be uninterruptible --- it is an IO routine
public Address skipOwnFramesAndDump(ObjectReference typeRef) {
TIB tib = Magic.addressAsTIB(typeRef.toAddress());
RVMMethod m = null;
int bci = -1;
int compiledMethodID = 0;
Offset ipOffset = Offset.zero();
Address fp = Magic.getFramePointer();
Address ip = Magic.getReturnAddress(fp);
fp = Magic.getCallerFramePointer(fp);
// This code borrows heavily from RVMThread.dumpStack
while (Magic.getCallerFramePointer(fp).NE(STACKFRAME_SENTINEL_FP)) {
compiledMethodID = Magic.getCompiledMethodID(fp);
if (compiledMethodID != INVISIBLE_METHOD_ID) {
// normal java frame(s)
CompiledMethod compiledMethod =
CompiledMethods.getCompiledMethod(compiledMethodID);
if (compiledMethod.getCompilerType() != CompiledMethod.TRAP) {
ipOffset = compiledMethod.getInstructionOffset(ip);
m = compiledMethod.getMethod();
if (VM.BuildForOptCompiler && compiledMethod.getCompilerType() == CompiledMethod.OPT) {
OptCompiledMethod optInfo = (OptCompiledMethod)compiledMethod;
/* Opt stack frames may contain multiple inlined methods. */
OptMachineCodeMap map = optInfo.getMCMap();
int iei = map.getInlineEncodingForMCOffset(ipOffset);
if (iei >= 0) {
int[] inlineEncoding = map.inlineEncoding;
boolean allocCall = true;
bci = map.getBytecodeIndexForMCOffset(ipOffset);
for (int j = iei; j >= 0 && allocCall;
j = OptEncodedCallSiteTree.getParent(j,inlineEncoding)) {
int mid = OptEncodedCallSiteTree.getMethodID(j, inlineEncoding);
m = MemberReference.getMemberRef(mid).asMethodReference().getResolvedMember();
if (!isAllocCall(m.getName().getBytes()))
allocCall = false;
if (j > 0)
bci = OptEncodedCallSiteTree.getByteCodeOffset(j,
inlineEncoding);
}
if (!allocCall)
break;
}
} else {
if (!isAllocCall(m.getName().getBytes())) {
BaselineCompiledMethod baseInfo =
(BaselineCompiledMethod)compiledMethod;
bci = baseInfo.findBytecodeIndexForInstruction(ipOffset.toWord().lsh(INSTRUCTION_WIDTH).toOffset());
break;
}
}
}
}
ip = Magic.getReturnAddress(fp);
fp = Magic.getCallerFramePointer(fp);
}
if (m != null) {
int allocid = (((compiledMethodID & 0x0000ffff) << 15) ^
((compiledMethodID & 0xffff0000) >> 16) ^
ipOffset.toInt()) & ~0x80000000;
/* Now print the location string. */
VM.sysWrite('\n');
VM.writeHex(allocid);
VM.sysWrite('-');
VM.sysWrite('>');
VM.sysWrite('[');
VM.writeHex(compiledMethodID);
VM.sysWrite(']');
m.getDeclaringClass().getDescriptor().sysWrite();
VM.sysWrite(':');
m.getName().sysWrite();
m.getDescriptor().sysWrite();
VM.sysWrite(':');
VM.writeHex(bci);
VM.sysWrite('\t');
RVMType type = tib.getType();
type.getDescriptor().sysWrite();
VM.sysWrite('\n');
}
return fp;
}
/***********************************************************************
*
* Wrapper methods
*/
@Inline
public void updateDeathTime(ObjectReference obj) {
MiscHeader.updateDeathTime(obj.toObject());
}
@Inline
public void setDeathTime(ObjectReference ref, Word time_) {
MiscHeader.setDeathTime(ref.toObject(), time_);
}
@Inline
public void setLink(ObjectReference ref, ObjectReference link) {
MiscHeader.setLink(ref.toObject(), link);
}
@Inline
public void updateTime(Word time_) {
MiscHeader.updateTime(time_);
}
@Inline
public Word getOID(ObjectReference ref) {
return MiscHeader.getOID(ref.toObject());
}
@Inline
public Word getDeathTime(ObjectReference ref) {
return MiscHeader.getDeathTime(ref.toObject());
}
@Inline
public ObjectReference getLink(ObjectReference ref) {
return MiscHeader.getLink(ref.toObject());
}
@Inline
public Address getBootImageLink() {
return MiscHeader.getBootImageLink();
}
@Inline
public Word getOID() {
return MiscHeader.getOID();
}
@Inline
public void setOID(Word oid) {
MiscHeader.setOID(oid);
}
@Inline
public int getHeaderSize() {
return MiscHeader.getHeaderSize();
}
@Inline
public int getHeaderEndOffset() {
return ObjectModel.getHeaderEndOffset();
}
}