/*
* 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.vm.code;
import static com.sun.max.vm.compiler.CallEntryPoint.*;
import static com.sun.max.vm.compiler.target.Safepoints.*;
import static com.sun.max.vm.intrinsics.MaxineIntrinsicIDs.*;
import java.util.*;
import com.sun.max.annotate.*;
import com.sun.max.lang.*;
import com.sun.max.memory.*;
import com.sun.max.platform.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.compiler.target.amd64.*;
import com.sun.max.vm.log.VMLog.Record;
import com.sun.max.vm.log.hosted.*;
import com.sun.max.vm.profile.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.stack.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.ti.*;
/**
* Code garbage collection (eviction).
* See <a href="https://wikis.oracle.com/display/MaxineVM/Code+Management">the Wiki page</a> for more details.
*/
public final class CodeEviction extends VmOperation {
/**
* Protect baseline methods until the given callee depth.
*/
private static int CodeEvictionProtectCalleeDepth = 1;
static {
VMOptions.addFieldOption("-XX:", "CodeEvictionProtectCalleeDepth", CodeEviction.class,
"During code eviction, protect callees of on-stack methods up until the given depth (default: 1).",
MaxineVM.Phase.STARTING);
}
/**
* Marks all target methods on the stack as live that are short-lived (baseline),
* and all baseline methods directly invoked from those.
*/
final class LiveMethodsMarker extends RawStackFrameVisitor {
@Override
public boolean visitFrame(StackFrameCursor current, StackFrameCursor callee) {
TargetMethod tm = current.targetMethod();
if (tm != null && CodeManager.isShortlived(tm)) {
logMark("ON STACK", tm);
tm.mark();
markDirectCalleesOf(tm, CodeEvictionProtectCalleeDepth);
}
return true;
}
/**
* Iterate over direct callees in the {@code tm} parameter and mark them.
* Recurse (depth-first) if necessary.
*/
private void markDirectCalleesOf(TargetMethod tm, int depthRemaining) {
if (depthRemaining == 0) {
return;
}
final Safepoints sps = tm.safepoints();
for (int i = sps.nextDirectCall(0); i >= 0; i = sps.nextDirectCall(i + 1)) {
TargetMethod directCallee = AMD64TargetMethodUtil.readCall32Target(tm, sps.causePosAt(i)).toTargetMethod();
if (directCallee != null && CodeManager.isShortlived(directCallee) && !directCallee.isMarked()) {
logMarkLevel("DIRECT CALLEE", directCallee, depthRemaining);
directCallee.mark();
markDirectCalleesOf(directCallee, depthRemaining - 1);
}
}
}
}
/**
* Marks all protected methods.
*
* A method is protected if it must not be evicted in spite of not being present on any call stack.
* Currently, this is true for methods ...<ul>
* <li>having type profiles (as they will soon be recompiled by the optimising compiler),</li>
* <li>that have just been compiled but are not yet fully installed in the system (e.g., by being referenced
* from a stack),</li>
* <li>whose invocation count is within the threshold denoted by {@link MethodInstrumentation#PROTECTION_PERCENTAGE}.</li>
* </ul>
*/
final class ProtectedMethodsMarker implements TargetMethod.Closure {
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
// avoid further tests if already marked
if (!targetMethod.isMarked()) {
if (targetMethod.isProtected()) {
logMark("PROTECTED (protected)", targetMethod);
targetMethod.mark();
} else if (targetMethod.withinInvocationThreshold()) {
logMark("PROTECTED (invocation count)", targetMethod);
targetMethod.mark();
} else if (targetMethod.hasTypeProfile()) {
logMark("PROTECTED (type profile)", targetMethod);
targetMethod.mark();
}
}
return true;
}
}
final class InvalidateDispatchTables implements TargetMethod.Closure {
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
if (!targetMethod.isMarked() && !targetMethod.isWiped()) {
++nStale;
nStaleBytes += targetMethod.codeLength();
logStaleMethod(targetMethod);
patchDispatchTables(targetMethod, true);
assert invalidateCode(targetMethod.code());
targetMethod.wipe();
targetMethod.classMethodActor.compiledState = Compilations.EMPTY;
} else {
++nSurvivors;
nSurvivingBytes += targetMethod.codeLength();
targetMethod.unmark();
}
return true;
}
}
final class InvalidateBaselineDirectCalls implements TargetMethod.Closure {
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
if (targetMethod.isMarked() && !targetMethod.isWiped()) {
++nBaseMeth;
nBaseDirect += targetMethod.safepoints().numberOfDirectCalls();
nCallBaseline += patchDirectCallsIn(targetMethod);
}
return true;
}
}
final class InvalidateOptDirectCalls implements TargetMethod.Closure {
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
++nOptMeth;
nOptDirect += targetMethod.safepoints().numberOfDirectCalls();
nCallOpt += patchDirectCallsIn(targetMethod);
return true;
}
}
final class StackPatcher extends RawStackFrameVisitor {
@Override
public boolean visitFrame(StackFrameCursor current, StackFrameCursor callee) {
final TargetMethod tm = current.targetMethod();
if (tm == null) {
return true;
}
// Patch locals
// (ds) There used to exist a mechanism for querying whether a TargetMethod
// may include CodePointer values in an activation frame. This meant that
// scanning for CodePointers only had to be done for these methods. However,
// the extra complexity introduced into the CRI was not worth the
// performance gain of this optimization, especially given how rare code
// evictions are.
tm.prepareReferenceMap(current, callee, codePointerRelocator);
// If the method executing in the current stack frame (tm) is a baseline method,
// the callee's return address needs to be patched.
final boolean patch = CodeManager.runtimeBaselineCodeRegion.contains(tm.codeStart().toAddress());
logMethodPatch(tm, patch);
if (!patch) {
logCalleeReturnAddress(callee);
return true;
}
final Pointer patchHere = callee.targetMethod().returnAddressPointer(callee);
CodePointer calleeRet = CodePointer.from(patchHere.readWord(0));
final Address offset = calleeRet.minus(tm.oldStart()).toAddress();
CodePointer newCalleeRet = CodePointer.from(tm.start().plus(offset));
logReturnAddressPatch(tm, calleeRet, newCalleeRet);
patchHere.writeWord(0, newCalleeRet.toAddress());
return true;
}
}
/**
* Most of the methods in this subclass of {@link FrameReferenceMapVisitor} are not needed.
* Code eviction logic is piggybacking on the stack root scanning logic to avoid duplicating
* the respective code.
*/
private final class CodePointerRelocator extends FrameReferenceMapVisitor {
@Override
public void visitReferenceMapBits(StackFrameCursor cursor, Pointer slotPointer, int refMap, int numBits) {
logVisitRefMapBits(cursor, slotPointer, refMap, numBits);
if (refMap == 0) {
return; // nothing to do
}
// Iterate over the bits in the ref map. Check references. If they are tagged, they represent
// code pointers and need to be relocated if they point to from-space.
for (int i = 0; i < numBits; i++) {
if (((refMap >> i) & 1) == 1) {
final Address raw = slotPointer.getWord(i).asAddress();
if ((raw.toLong() & 1L) == 1L) { // tagged?
final CodePointer cp = CodePointer.fromTaggedLong(raw.toLong());
if (CodeManager.runtimeBaselineCodeRegion.isInFromSpace(cp.toAddress())) {
final TargetMethod tm = CodeManager.runtimeBaselineCodeRegion.findInFromSpace(cp.toAddress());
final Offset offset = tm.start().minus(tm.oldStart()).asOffset();
final CodePointer newCp = cp.relocate(offset);
assert CodeManager.runtimeBaselineCodeRegion.isInToSpace(newCp.toPointer());
final Address newCpAddress = Address.fromLong(newCp.toTaggedLong());
logRelocateCodePointer(i, cp, newCp, tm);
slotPointer.setWord(i, newCpAddress);
}
}
}
}
}
@Override
public void logPrepareReferenceMap(TargetMethod targetMethod, int safepointIndex, Pointer refmapFramePointer, String label) {
// unimplemented
}
@Override
public int referenceMapBitIndex(Address slotAddress) {
// unimplemented
return -1;
}
@Override
public void setBits(int baseSlotIndex, byte referenceMapByte) {
// unimplemented
}
@Override
public void logReferenceMapByteBefore(int byteIndex, byte referenceMapByte, String referenceMapLabel) {
// unimplemented
}
@Override
public void logReferenceMapByteAfter(Pointer framePointer, int baseSlotIndex, byte referenceMapByte) {
// unimplemented
}
}
/**
* Overwrites a machine code array with an illegal instruction pattern.
*/
static boolean invalidateCode(byte[] code) {
if (Platform.platform().isa == ISA.AMD64) {
byte int3 = (byte) 0xcc;
Arrays.fill(code, int3);
} else {
throw FatalError.unimplemented();
}
return true;
}
final class CopySurvivors implements TargetMethod.Closure {
final SemiSpaceCodeRegion cr = CodeManager.runtimeBaselineCodeRegion;
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
assert cr.isInFromSpace(targetMethod.start()) : "all target methods to be copied should be in from-space";
if (!targetMethod.isWiped()) {
// preparation
final Pointer from = targetMethod.start().asPointer();
final Pointer to = cr.mark();
final Size size = targetMethod.size();
// first, address dispatch table entries
logCodeMotion(targetMethod, from, to, size);
patchDispatchTables(targetMethod, false);
// next, physically move the code
Memory.copyBytes(from, to, size);
assert invalidateCode(targetMethod.code()); // this invalidates the old code as targetMethod's pointers have not been changed yet!
targetMethod.setOldStart(targetMethod.start());
targetMethod.setStart(to);
final byte[] code = (byte[]) relocate(from, to, targetMethod.code());
final Pointer codeStart = to.plus(targetMethod.codeStart().toPointer().minus(from));
final byte[] scalarLiterals = targetMethod.scalarLiterals() == null ?
null : (byte[]) relocate(from, to, targetMethod.scalarLiterals());
final Object[] referenceLiterals = targetMethod.referenceLiterals() == null ?
null : (Object[]) relocate(from, to, targetMethod.referenceLiterals());
targetMethod.setCodeArrays(code, codeStart, scalarLiterals, referenceLiterals);
cr.setMark(cr.mark().plus(size));
CodeManager.runtimeBaselineCodeRegion.add(targetMethod);
targetMethod.survivedEviction();
} else {
// set the oldStart address to mark this method as "old"
targetMethod.setOldStart(targetMethod.start());
logNotCopying(targetMethod);
}
return true;
}
private Object relocate(Pointer fromBase, Pointer toBase, Object o) {
if (o == null) {
return null;
}
final Address offset = Reference.fromJava(o).toOrigin().minus(fromBase);
return Reference.fromOrigin(toBase.plus(offset)).toJava();
}
}
/**
* Fixes all direct calls in the machine code of a moved (baseline) target method.
*
* After a method has been relocated, its direct calls all point to invalid addresses.
*
* The following assumes that the method has been moved by an offset called {@code delta}:
* {@code delta = newStart - oldStart},
* and that the call target extractable from a direct call site is named {@code target}.
* Furthermore, the <i>intended</i> call target {@code itarget} is determined by appropiately applying {@code delta}:
* {@code itarget = target - delta}.
*
* There are two cases for a direct call in moved (baseline) code:<ul>
* <li><i>Direct call to moved code.</i>
* In this case, first the old {@code oldCalleeStart} and new {@code newCalleeStart} start locations
* of the callee method are obtained.
* After that, the entry point offset into the old callee location is obtained:
* {@code epoffset = itarget - oldCalleeStart}.
* Finally, the actual target is computed:
* {@code newTarget = newCalleeStart + epoffset}.</li>
* <li><i>Direct call to non-moved code.</i>
* In this case, the correct target is simply the intended target:
* {@code newTarget = itarget}.</li>
* </ul>
*/
final class BaselineFixCalls implements TargetMethod.Closure {
int fixed;
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
final Offset delta = targetMethod.start().minus(targetMethod.oldStart()).asOffset();
logFixCallForMovedCode(targetMethod, delta);
final Safepoints safepoints = targetMethod.safepoints();
for (int spi = safepoints.nextDirectCall(0); spi >= 0; spi = safepoints.nextDirectCall(spi + 1)) {
final int callPos = safepoints.causePosAt(spi);
final CodePointer target = AMD64TargetMethodUtil.readCall32Target(targetMethod, callPos);
final CodePointer itarget = target.minus(delta);
logDirectCallInfo(spi, callPos, target, itarget);
if (CodeManager.runtimeBaselineCodeRegion.isInFromSpace(itarget.toAddress())) {
// direct call to moved code
final TargetMethod callee = CodeManager.runtimeBaselineCodeRegion.findInFromSpace(itarget.toAddress());
assert callee != null : "callee should not be null, from-space address " + itarget.to0xHexString();
final Address oldCalleeStart = callee.oldStart();
final Address newCalleeStart = callee.start();
final Address epoffset = itarget.minus(oldCalleeStart).toAddress();
final CodePointer newTarget = CodePointer.from(newCalleeStart.plus(epoffset));
logToMoved(callee, oldCalleeStart, newCalleeStart, epoffset, newTarget);
targetMethod.fixupCallSite(callPos, newTarget);
} else {
// direct call to unmoved code
logToUnmoved(itarget);
targetMethod.fixupCallSite(callPos, itarget);
}
++fixed;
}
return true;
}
}
/**
* Fixes all direct calls in the machine code of a moved (baseline) target method.
*
* After a method has been relocated, calls to it from unmoved code must be updated.
*
* The following assumes the call target extractable from a direct call site is named {@code target}.
*
* First, the old {@code oldCalleeStart} and new {@code newCalleeStart} start locations
* of the callee method are obtained.
* Next, the entry point offset into the old callee location is obtained:
* {@code epoffset = target - oldCalleeStart}.
* Finally, the actual target is computed:
* {@code newTarget = newCalleeStart + epoffset}.
*/
final class OptFixCalls implements TargetMethod.Closure {
int fixed;
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
boolean haveLoggedMethod = false;
final Safepoints safepoints = targetMethod.safepoints();
for (int spi = safepoints.nextDirectCall(0); spi >= 0; spi = safepoints.nextDirectCall(spi + 1)) {
final int callPos = safepoints.causePosAt(spi);
final CodePointer target = AMD64TargetMethodUtil.readCall32Target(targetMethod, callPos);
if (CodeManager.runtimeBaselineCodeRegion.isInFromSpace(target.toAddress())) {
if (!haveLoggedMethod) {
logOptMethod(targetMethod);
haveLoggedMethod = true;
}
logDirectCallInfo(spi, callPos, target, CodePointer.zero());
final TargetMethod callee = CodeManager.runtimeBaselineCodeRegion.findInFromSpace(target.toAddress());
assert callee != null : "callee should not be null, from-space address " + target.to0xHexString();
assert !callee.isWiped() : "callee should not have been wiped, from-space address " + target.to0xHexString();
final Address oldCalleeStart = callee.oldStart();
final Address newCalleeStart = callee.start();
final Address epoffset = target.toAddress().minus(oldCalleeStart);
final CodePointer newTarget = CodePointer.from(newCalleeStart.plus(epoffset));
logToMoved(callee, oldCalleeStart, newCalleeStart, epoffset, newTarget);
targetMethod.fixupCallSite(callPos, newTarget);
++fixed;
}
}
return true;
}
}
final class VMTIUnload implements TargetMethod.Closure {
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
VMTI.handler().methodUnloaded(targetMethod.classMethodActor, targetMethod.codeStart().toPointer());
return true;
}
}
final class VMTIMove implements TargetMethod.Closure {
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
VMTI.handler().methodCompiled(targetMethod.classMethodActor);
return true;
}
}
/*
* Pre-allocated closures for VMTI events relating to code unloading/moving.
*/
private final VMTIUnload vmtiUnload = new VMTIUnload();
private final VMTIMove vmtiMove = new VMTIMove();
private static enum Phase {
DUMPING,
PATCHING,
COMPACTING
}
private Phase phase;
public CodeEviction() {
super("code cache cleaner", null, Mode.Safepoint);
}
private static int evictionCount = 0;
public static int evictionCount() {
return evictionCount;
}
private static CodeEviction codeEviction = new CodeEviction();
/**
* Run a code eviction operation.
*/
public static void run() {
codeEviction.submit();
}
@Override
protected void doIt() {
++evictionCount;
if (codeEvictionLogger.enabled()) {
codeEvictionLogger.logRun("starting", evictionCount, callingThread());
}
// phase 0 (optional): dump before
if (logging()) {
phase = Phase.DUMPING;
dumpCodeAddresses("before");
}
// phase 1: identify stale methods and invalidate them, patching all relevant dispatch table entries and direct calls
phase = Phase.PATCHING;
CodeManager.Inspect.notifyEvictionStarted(CodeManager.runtimeBaselineCodeRegion);
timerStart();
doAllThreads();
tMarking = timerEnd();
timerStart();
markProtectedMethods();
tMarkProtected = timerEnd();
invalidateDirectCalls();
timerStart();
invalidateDispatchTableEntries();
tInvalidateTables = timerEnd();
if (CodeManager.CodeCacheContentionFrequency > 0) {
Code.getCodeManager().recordSurvivorSize(nSurvivingBytes);
}
logStatistics();
resetCounters();
// phase 2: compact the baseline code cache and patch all PC values and return addresses
phase = Phase.COMPACTING;
timerStart();
compact();
tCompact = timerEnd();
fixCallSitesForMovedCode();
logFixed();
timerStart();
doAllThreads();
tPatchStacks = timerEnd();
if (/*VMTI.handler().activeAgents() > 0*/true) {
CodeManager.runtimeBaselineCodeRegion.doOldTargetMethods(vmtiUnload);
CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(vmtiMove);
}
CodeManager.runtimeBaselineCodeRegion.resetFromSpace();
if (logging()) {
codeEvictionLogger.logMove_Progress("FINISHED walking threads");
}
CodeManager.Inspect.notifyEvictionCompleted(CodeManager.runtimeBaselineCodeRegion);
// phase 3 (optional): dump after
if (logging()) {
phase = Phase.DUMPING;
dumpCodeAddresses("after");
}
if (codeEvictionLogger.enabled()) {
codeEvictionLogger.logRun("completed", evictionCount, callingThread());
}
logTimingResults();
}
/**
* Perform a specific action for a given thread.
* This method is invoked multiple times during the execution of {@linkplain #doIt()}.
* What action is performed is controlled by the {@linkplain #phase} member,
* which is set accordingly in {@linkplain #doIt()}.
*/
@Override
protected void doThread(VmThread vmThread, Pointer ip, Pointer sp, Pointer fp) {
// bail out if the thread was stopped in native code before invoking any Java method
if (ip.isZero() && sp.isZero() && fp.isZero()) {
return;
}
walker.setTLA(vmThread.tla());
switch(phase) {
case DUMPING:
threadName = vmThread.getName();
walker.inspect(ip, sp, fp, stackDumper);
break;
case PATCHING :
// collect all reachable baseline methods
walker.inspect(ip, sp, fp, liveMethodsMarker);
break;
case COMPACTING:
// walk all stacks, patching PC values, return addresses, and local variables
logThread(vmThread);
CodeManager.runtimeBaselineCodeRegion.allowFromSpaceLookup = true;
walker.inspect(ip, sp, fp, stackPatcher);
CodeManager.runtimeBaselineCodeRegion.allowFromSpaceLookup = false;
if (logging()) {
stackDump(ip, sp, fp);
}
break;
default:
throw FatalError.unexpected("invalid code cache cleaner phase");
}
}
private final VmStackFrameWalker walker = new VmStackFrameWalker(Pointer.zero());
private final LiveMethodsMarker liveMethodsMarker = new LiveMethodsMarker();
private final ProtectedMethodsMarker protectedMethodsMarker = new ProtectedMethodsMarker();
private final StackPatcher stackPatcher = new StackPatcher();
private final CodePointerRelocator codePointerRelocator = new CodePointerRelocator();
private void markProtectedMethods() {
CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(protectedMethodsMarker);
}
/**
* Iterate over the baseline code region and invalidate references to stale methods.
* This includes vtable and itable entries as well as {@linkplain MethodActor} target states.
* These entries are invalidated by letting them reference the respective trampolines again.
* This also wipes out all invalidated methods by overwriting them with illegal instructions,
* and unmarks all marked methods.
*/
private void invalidateDispatchTableEntries() {
// to/from have not yet been flipped
CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(invalidateDispatchTables);
logPatchDetails();
}
private final InvalidateDispatchTables invalidateDispatchTables = new InvalidateDispatchTables();
/**
* Patch the dispatch table entries for a given {@linkplain TargetMethod}.
*/
private void patchDispatchTables(final TargetMethod tm, final boolean count) {
final ClassMethodActor cma = tm.classMethodActor;
if (cma instanceof VirtualMethodActor) {
final ClassActor ca = cma.holder();
final DynamicHub holderHub = ca.dynamicHub();
final StaticHub staticHub = ca.staticHub();
final int vtableIndex = ((VirtualMethodActor) cma).vTableIndex();
final boolean patchStaticHubAndArrayHubs = vtableIndex < StaticHub.vTableStartIndex() + staticHub.vTableLength();
if (vtableIndex >= 0) { // constructors and other nonvirtual methods are dealt with below
if (count) {
++nVT;
}
holderHub.resetVTableEntry(vtableIndex);
logDispatchTableReset('V', holderHub, vtableIndex);
if (patchStaticHubAndArrayHubs) {
staticHub.resetVTableEntry(vtableIndex);
logDispatchTableReset('V', staticHub, vtableIndex);
patchArrayHubs(vtableIndex);
}
// act accordingly if this method is also an interface method
patchItables(holderHub, tm);
// include subclasses (the vtable index stays the same for this method)
subclassPatcher.cma = cma;
subclassPatcher.tm = tm;
subclassPatcher.vtableIndex = vtableIndex;
subclassPatcher.patchStaticHub = patchStaticHubAndArrayHubs;
ca.allSubclassesDo(subclassPatcher);
} else {
assert vtableIndex != VirtualMethodActor.INVALID_VTABLE_INDEX : "must not be an invalid vtable index";
assert cma.isLeafMethod() || cma.isConstructor() : "expected leaf (statically resolvable) method or constructor";
if (vtableIndex != VirtualMethodActor.INVALID_VTABLE_INDEX) {
if (count) {
++nNonvirtual;
}
if (logging()) {
codeEvictionLogger.logDetails_PatchType(" NONVIRTUAL");
}
}
}
} else if (cma instanceof StaticMethodActor) {
if (count) {
++nStatic;
}
if (logging()) {
codeEvictionLogger.logDetails_PatchType(" STATIC");
}
} else {
throw FatalError.unexpected("unexpected target method type: " + cma.getClass().getName() + " for " + cma);
}
}
private final SubclassPatcher subclassPatcher = new SubclassPatcher();
private class SubclassPatcher implements ClassActor.Closure {
int vtableIndex;
ClassMethodActor cma;
TargetMethod tm;
boolean patchStaticHub;
@Override
public boolean doClass(ClassActor ca) {
++nVT;
DynamicHub hub = ca.dynamicHub();
hub.resetVTableEntry(vtableIndex);
logDispatchTableReset('V', hub, vtableIndex);
if (patchStaticHub) {
StaticHub shub = ca.staticHub();
shub.resetVTableEntry(vtableIndex);
logDispatchTableReset('V', shub, vtableIndex);
}
patchItables(hub, tm);
return true;
}
}
/**
* Helper function to collect patch locations for interface methods.
*/
private void patchItables(DynamicHub holderHub, TargetMethod tm) {
final int lastITableIndex = holderHub.iTableStartIndex + holderHub.iTableLength;
for (int i = holderHub.iTableStartIndex; i < lastITableIndex; i++) {
if (holderHub.getWord(i).equals(tm.getEntryPoint(VTABLE_ENTRY_POINT))) {
++nIT;
holderHub.resetITableEntry(i);
logDispatchTableReset('I', holderHub, i);
}
}
}
/**
* Patches all dynamic and static hubs of array classes.
* This separate treatment is required as array classes are not part of the hierarchy represented in {@link ClassActor}.
*/
private void patchArrayHubs(final int vtableIndex) {
arrayHubPatcher.vtableIndex = vtableIndex;
ClassActor.allNonInstanceClassesDo(arrayHubPatcher);
}
private final ArrayHubPatcher arrayHubPatcher = new ArrayHubPatcher();
private class ArrayHubPatcher implements ClassActor.Closure {
int vtableIndex;
@Override
public boolean doClass(ClassActor ca) {
if (ca instanceof ArrayClassActor) {
final DynamicHub dhub = ca.dynamicHub();
final StaticHub shub = ca.staticHub();
dhub.resetVTableEntry(vtableIndex);
logDispatchTableReset('V', dhub, vtableIndex);
shub.resetVTableEntry(vtableIndex);
logDispatchTableReset('V', shub, vtableIndex);
}
return true;
}
}
/**
* Iterate over native code and identify places to patch. Those places are all direct call sites that call
* one of the stale methods.
*/
private void invalidateDirectCalls() {
// to/from have not yet been flipped
timerStart();
CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(invalidateBaselineDirectCalls);
tInvalidateCallsBaseline = timerEnd();
timerStart();
CodeManager.runtimeOptCodeRegion.doAllTargetMethods(invalidateOptDirectCalls);
tInvalidateCallsOpt = timerEnd();
timerStart();
invalidateBootDirectCalls();
tInvalidateCallsBoot = timerEnd();
logDirectCallNumbers();
}
private final InvalidateBaselineDirectCalls invalidateBaselineDirectCalls = new InvalidateBaselineDirectCalls();
private final InvalidateOptDirectCalls invalidateOptDirectCalls = new InvalidateOptDirectCalls();
private void invalidateBootDirectCalls() {
final int btbSize = CodeManager.bootToBaselineSize();
if (btbSize != 0) {
final TargetMethod[] bootDirectCallers = CodeManager.bootToBaselineCallers();
for (int i = 0; i < btbSize; ++i) {
final TargetMethod tm = bootDirectCallers[i];
++nBootMeth;
nBootDirect += tm.safepoints().numberOfDirectCalls();
nCallBoot += patchDirectCallsIn(tm);
}
}
}
private int directCalleePosition(TargetMethod tm, int callPos) {
final Safepoints safepoints = tm.safepoints();
int dcIndex = 0;
for (int i = 0; i < safepoints.size(); i++) {
if (safepoints.isSetAt(DIRECT_CALL, i)) {
if (safepoints.causePosAt(i) == callPos) {
return dcIndex;
}
dcIndex++;
}
}
return -1;
}
private int patchDirectCallsIn(TargetMethod tm) {
int calls = 0;
final Safepoints safepoints = tm.safepoints();
for (int spi = safepoints.nextDirectCall(0); spi >= 0; spi = safepoints.nextDirectCall(spi + 1)) {
final int callPos = safepoints.causePosAt(spi);
final CodePointer target = AMD64TargetMethodUtil.readCall32Target(tm, callPos);
final TargetMethod callee = target.toTargetMethod();
assert callee != null : "callee should not be null in " + tm + "@" + callPos + " " + target.to0xHexString();
final int dcIndex = directCalleePosition(tm, callPos);
assert dcIndex >= 0 : "direct callee index should not be -1 for " + tm + "@" + callPos + " calling " + callee;
if (isStaleCallee(callee)) {
++calls;
logDirectCallReset(tm, spi, callee);
tm.resetDirectCall(spi, dcIndex);
}
}
return calls;
}
private boolean isStaleCallee(TargetMethod tm) {
return tm != null && CodeManager.runtimeBaselineCodeRegion.contains(tm.codeStart().toAddress()) && !tm.isMarked() && !tm.isWiped();
}
/**
* Compact the baseline code cache, i.e., flip spaces and copy all survivors (non-wiped methods) from from-space
* to to-space.
*/
private void compact() {
final SemiSpaceCodeRegion cr = CodeManager.runtimeBaselineCodeRegion;
if (logging()) {
codeEvictionLogger.logMove_Progress("compacting code cache: copying starts ...");
}
cr.flip();
logCodeCacheBoundaries(cr);
cr.doOldTargetMethods(copySurvivors);
if (logging()) {
codeEvictionLogger.logMove_Progress("copying done!");
}
}
private final CopySurvivors copySurvivors = new CopySurvivors();
/**
* Iterate over all methods code caches one last time, fixing direct calls to moved code.
*/
private void fixCallSitesForMovedCode() {
if (logging()) {
codeEvictionLogger.logMove_Progress("fixing call sites ...\nmoved code ...");
}
timerStart();
baselineFixCalls.fixed = 0;
CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(baselineFixCalls);
nCallBaseline = baselineFixCalls.fixed;
tFixCallsBaseline = timerEnd();
if (logging()) {
codeEvictionLogger.logMove_Progress("optimised code ...");
}
timerStart();
optFixCalls.fixed = 0;
CodeManager.runtimeOptCodeRegion.doAllTargetMethods(optFixCalls);
nCallOpt = optFixCalls.fixed;
tFixCallsOpt = timerEnd();
if (logging()) {
codeEvictionLogger.logMove_Progress("boot code ...");
}
timerStart();
optFixCalls.fixed = 0;
if (CodeManager.bootToBaselineSize() > 0) {
CodeManager.bootToBaselineDo(optFixCalls);
}
nCallBoot = optFixCalls.fixed;
tFixCallsBoot = timerEnd();
if (logging()) {
codeEvictionLogger.logMove_Progress("fixing done!");
}
}
/**
* It is crucial that *all* direct calls in moved code are fixed, because they are relative and hence no longer
* valid after code motion.
*/
private final BaselineFixCalls baselineFixCalls = new BaselineFixCalls();
/**
* Optimised and boot image code might contain direct calls to moved baseline code.
* These are fixed here.
*/
private final OptFixCalls optFixCalls = new OptFixCalls();
private boolean wasMoved(TargetMethod tm) {
return CodeManager.runtimeBaselineCodeRegion.isInToSpace(tm.codeStart().toAddress());
}
private void resetCounters() {
nStale = 0;
nStaleBytes = 0;
nBaseDirect = 0;
nBaseMeth = 0;
nOptDirect = 0;
nOptMeth = 0;
nBootDirect = 0;
nBootMeth = 0;
nSurvivors = 0;
nSurvivingBytes = 0;
nVT = 0;
nIT = 0;
nNonvirtual = 0;
nStatic = 0;
nCallBaseline = 0;
nCallOpt = 0;
nCallBoot = 0;
}
int nStale = 0;
int nStaleBytes = 0;
int nBaseDirect = 0;
int nBaseMeth = 0;
int nOptDirect = 0;
int nOptMeth = 0;
int nBootDirect = 0;
int nBootMeth = 0;
int nSurvivors = 0;
int nSurvivingBytes = 0;
int nVT = 0;
int nIT = 0;
int nNonvirtual = 0;
int nStatic = 0;
int nCallBaseline = 0;
int nCallOpt = 0;
int nCallBoot = 0;
private long timer;
private long tMarking;
private long tMarkProtected;
private long tInvalidateCallsBaseline;
private long tInvalidateCallsOpt;
private long tInvalidateCallsBoot;
private long tInvalidateTables;
private long tCompact;
private long tPatchStacks;
private long tFixCallsBaseline;
private long tFixCallsOpt;
private long tFixCallsBoot;
private long tTotal;
private void timerStart() {
timer = System.nanoTime();
}
private long timerEnd() {
return System.nanoTime() - timer;
}
/*
* Everything below here is related to logging the behavior of the algorithm, or dumping the state of
* VM that is pertinent to the algorithm.
*/
private static void stackDump(Pointer ip, Pointer sp, Pointer fp) {
// We have no direct control over this (tracing) output, so we control via the operation enabling.
if (CodeEvictionLogger.stackDumpEnabled()) {
Throw.stackDump("dump after patching", ip, sp, fp);
}
}
private final class StackDumper extends RawStackFrameVisitor {
@Override
public boolean visitFrame(StackFrameCursor current, StackFrameCursor callee) {
final TargetMethod tmCurrent = current.targetMethod();
final TargetMethod tmCallee = callee.targetMethod();
if (tmCurrent == null || tmCallee == null) {
return true;
}
final Pointer rap = tmCallee.returnAddressPointer(callee);
final CodePointer ret = CodePointer.from(rap.readWord(0));
dump("STACK", printThreadName, tmCurrent, ret);
return true;
}
}
final class DumpDirectCalls implements TargetMethod.Closure {
@Override
public boolean doTargetMethod(TargetMethod targetMethod) {
s1 = targetMethod.toString();
final Safepoints safepoints = targetMethod.safepoints();
for (int spi = safepoints.nextDirectCall(0); spi >= 0; spi = safepoints.nextDirectCall(spi + 1)) {
final int callPos = safepoints.causePosAt(spi);
final CodePointer target = AMD64TargetMethodUtil.readCall32Target(targetMethod, callPos);
final TargetMethod callee = target.toTargetMethod();
assert callee != null : "callee should not be null in " + targetMethod + "@" + callPos + "->" + target.to0xHexString();
idx = callPos;
dump("DIRECT", printTriple, callee, target);
}
return true;
}
}
private final class DumpDispatchTables implements ClassActor.Closure {
@Override
public boolean doClass(ClassActor classActor) {
final DynamicHub dhub = classActor.dynamicHub();
if (dhub != null) {
dumpHub(dhub, DynamicHub.vTableStartIndex(), true, "DHUB");
}
final StaticHub shub = classActor.staticHub();
if (shub != null) {
dumpHub(shub, Hub.vTableStartIndex(), !(classActor.isInterface() || classActor.isPrimitiveClassActor()), "SHUB");
}
return true;
}
}
/**
* Dump all code addresses from stacks (return addresses), vtables/itables/targetStates, and direct calls. This
* generates a great deal of output and we currently do not store it in the {@link VMLog}. However, it is enabled
* through the logging interface.
*/
private void dumpCodeAddresses(String when) {
if (CodeEvictionLogger.dumpEnabled()) {
Log.print("++++++++++ start dump ");
Log.print(when);
Log.println(" code eviction ++++++++++");
doAllThreads();
dumpTables();
dumpDirectCalls();
Log.print("++++++++++ end dump ");
Log.print(when);
Log.println(" code eviction ++++++++++");
}
}
private String threadName;
/**
* Dump one piece of information in the form [context] [target method] [target method code start] +[offset],
* with tabs in between the elements.
*/
private void dump(final String context, final DumpElement element, final TargetMethod tm, final CodePointer a) {
Log.print(context);
Log.print('\t');
element.print();
Log.print('\t');
if (tm != null) {
Log.printMethod(tm, false);
Log.print('\t');
CodePointer cs = tm.codeStart();
Log.print(cs);
Log.print('\t');
final Offset offset = a.minus(cs).toOffset();
Log.println(offset.toInt());
} else {
Log.print("<null>\t");
Log.print(a);
Log.println("\t<null>");
}
}
private interface DumpElement {
void print();
}
private final DumpElement printThreadName = new DumpElement() {
@Override
public void print() {
Log.print(threadName);
}
};
private final StackDumper stackDumper = new StackDumper();
private void dumpTables() {
ClassActor.allClassesDo(dumpDispatchTables);
}
private final DumpDispatchTables dumpDispatchTables = new DumpDispatchTables();
private String s1;
private String s2;
private int idx;
private final DumpElement printTriple = new DumpElement() {
@Override
public void print() {
Log.print(s1);
Log.print(s2);
Log.print(idx);
}
};
private void dumpHub(final Hub hub, final int vstart, final boolean dumpVtable, final String context) {
s1 = hub.toString();
if (dumpVtable) {
s2 = "@V";
final int vend = vstart + hub.vTableLength();
for (int i = vstart; i < vend; ++i) {
final CodePointer address = CodePointer.from(hub.getWord(i));
final TargetMethod tm = address.toTargetMethod();
idx = i;
dump(context, printTriple, tm, address);
}
}
s2 = "@I";
final int istart = hub.iTableStartIndex;
final int iend = istart + hub.iTableLength;
for (int i = istart + 1; i < iend; ++i) {
final CodePointer p = CodePointer.from(hub.getWord(i));
if (!(p.isZero() || Hub.validItableEntry(p))) {
final TargetMethod tm = p.toTargetMethod();
idx = i;
dump(context, printTriple, tm, p);
}
}
}
private void dumpDirectCalls() {
s2 = "@";
CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(dumpDirectCalls);
CodeManager.runtimeOptCodeRegion.doAllTargetMethods(dumpDirectCalls);
Code.bootCodeRegion().doAllTargetMethods(dumpDirectCalls);
}
private final DumpDirectCalls dumpDirectCalls = new DumpDirectCalls();
/**
* Start logging upon the n-th eviction cycle.
*/
private static int LogStartEviction;
static {
VMOptions.addFieldOption("-XX:", "LogStartEviction", CodeEviction.class,
"Start logging upon the n-th code eviction cycle; all cycles before that are silent.",
MaxineVM.Phase.STARTING);
}
/**
* Checks whether the logger is enabled and if we have reached the evictionCount to start logging.
* @return
*/
protected static boolean logging() {
return codeEvictionLogger.enabled() && evictionCount >= LogStartEviction;
}
// The following methods are essentially convenience methods that avoid cluttering the algorithm
// with checks that logging is enabled, although some do a small amount of additional setup.
@NEVER_INLINE
private void logTimingResults() {
if (logging()) {
tTotal =
tMarking + tMarkProtected +
tInvalidateCallsBaseline + tInvalidateCallsOpt + tInvalidateCallsBoot + tInvalidateTables +
tCompact + tPatchStacks + tFixCallsBaseline + tFixCallsOpt + tFixCallsBoot;
codeEvictionLogger.logStats_TimingResults(this);
}
}
private void logStaleMethod(TargetMethod tm) {
if (logging()) {
codeEvictionLogger.logDetails_StaleMethod(nStale, tm);
}
}
private void logMethodPatch(final TargetMethod tm, final boolean patch) {
if (logging()) {
codeEvictionLogger.logMove_MethodPatch(tm, patch);
}
}
@NEVER_INLINE
private void logCalleeReturnAddress(StackFrameCursor callee) {
if (logging() && callee != null && callee.targetMethod() != null) {
final Pointer rap = callee.targetMethod().returnAddressPointer(callee);
final Pointer ret = rap.readWord(0).asPointer();
codeEvictionLogger.logMove_CalleeReturnAddress(ret);
}
}
private void logReturnAddressPatch(final TargetMethod tm, final CodePointer calleeRet, final CodePointer newCalleeRet) {
if (logging()) {
codeEvictionLogger.logMove_ReturnAddressPatch(tm, calleeRet, newCalleeRet);
}
}
private void logCodeMotion(TargetMethod tm, final Pointer from, final Pointer to, final Size size) {
if (logging()) {
codeEvictionLogger.logMove_CodeMotion(tm, from, to, size);
}
}
private void logNotCopying(TargetMethod tm) {
if (logging()) {
codeEvictionLogger.logMove_NotCopying(tm);
}
}
private void logFixCall(TargetMethod tm, int i, CodePointer callTarget) {
if (logging()) {
codeEvictionLogger.logMove_FixCall(tm, i, callTarget);
}
}
private void logStatistics() {
if (logging()) {
codeEvictionLogger.logStats_Statistics(this);
}
}
private void logPatchDetails() {
if (logging()) {
codeEvictionLogger.logDetails_PatchDetails(this);
}
}
private void logCodeCacheBoundaries(final SemiSpaceCodeRegion cr) {
if (logging()) {
codeEvictionLogger.logMove_CodeCacheBoundaries(cr);
}
}
private void logDirectCallReset(TargetMethod tm, int i, final TargetMethod callee) {
if (logging()) {
codeEvictionLogger.logDetails_DirectCallReset(tm, i, callee);
}
}
private void logDirectCallNumbers() {
if (logging()) {
codeEvictionLogger.logStats_DirectCallNumbers(this);
}
}
private void logDispatchTableReset(char tableKind, Hub hub, int index) {
if (logging()) {
codeEvictionLogger.logDetails_DispatchTableReset(tableKind, hub, index);
}
}
private void logThread(VmThread vmThread) {
if (logging()) {
codeEvictionLogger.logMove_ProcessThread(vmThread);
}
}
private void logFixCallForMovedCode(TargetMethod tm, Offset d) {
if (logging()) {
codeEvictionLogger.logMove_FixCallForMovedCode(tm, d);
}
}
private void logDirectCallInfo(int i, int callPos, CodePointer target, CodePointer itarget) {
if (logging()) {
codeEvictionLogger.logMove_DirectCallInfo(i, callPos, target, itarget);
}
}
private void logToMoved(TargetMethod callee, Address oldCalleeStart, Address newCalleeStart, Address epoffset, CodePointer newTarget) {
if (logging()) {
codeEvictionLogger.logMove_ToMoved(callee, oldCalleeStart, newCalleeStart, epoffset, newTarget);
}
}
private void logToUnmoved(CodePointer iTarget) {
if (logging()) {
codeEvictionLogger.logMove_ToUnmoved(iTarget);
}
}
private void logOptMethod(TargetMethod tm) {
if (logging()) {
codeEvictionLogger.logMove_OptMethod(tm);
}
}
private void logFixed() {
if (logging()) {
codeEvictionLogger.logStats_Fixed(this);
}
}
private void logMark(String s, TargetMethod tm) {
if (logging()) {
codeEvictionLogger.logDetails_Mark(s, tm);
}
}
private void logMarkLevel(String s, TargetMethod tm, int level) {
if (logging()) {
codeEvictionLogger.logDetails_MarkLevel(s, tm, level);
}
}
private void logVisitRefMapBits(StackFrameCursor cursor, Pointer slotPointer, int refMap, int numBits) {
if (logging()) {
codeEvictionLogger.logDetails_VisitRefMapBits(cursor, slotPointer, refMap, numBits);
}
}
private void logRelocateCodePointer(int bitIndex, CodePointer from, CodePointer to, TargetMethod tm) {
if (logging()) {
codeEvictionLogger.logDetails_RelocateCodePointer(bitIndex, from, to, tm);
}
}
/**
* The interface to the {@link CodeEviction logger}. This evolved from a hand-crafted system using {@link Log}. It
* has several distinct facets that used to be controlled by an integer level, but are now controlled by pattern
* matching on a prefix to the the operation name. To make this clear, the prefix is separated by a '_' character
* from the operation name proper.
*
* An alternate design would be separate loggers for each of the facets.
*
* Dumping of the VM state pre/post a code eviction and generating a thread stack trace after patching
* do not place data in the {@link VMLog}. However, they are defined here as operations so that they
* can be enabled/disabled selectively using {@code -XX:LogCodeEvictionInclude/Exclude}. N.B., when
* enabled they will generate {@link Log} output regardless of whether {@code TraceCodeEviction} is set.
* By default, they are both disabled unless the operations are explicitly enabled on the VM startup command line.
*
*/
@HOSTED_ONLY
@VMLoggerInterface
private interface CodeEvictionLoggerInterface {
// Control
void run(
@VMLogParam(name = "mode") String mode,
@VMLogParam(name = "evictionCount") int evictionCount,
@VMLogParam(name = "callingThread") VmThread callingThread);
// Statistics. The data is all stored in the CodeEviction instance, so for now we just log that.
void stats_DirectCallNumbers(@VMLogParam(name = "codeEviction") CodeEviction codeEviction);
void stats_Fixed(@VMLogParam(name = "codeEviction") CodeEviction codeEviction);
void stats_Statistics(@VMLogParam(name = "codeEviction") CodeEviction codeEviction);
void stats_TimingResults(@VMLogParam(name = "codeEviction") CodeEviction codeEviction);
void stats_Surviving(
@VMLogParam(name = "lastSurvivorSize") int lastSurvivorSize,
@VMLogParam(name = "largestSurvivorSize") int largestSurvivorSize);
// Details
void details_DirectCallReset(
@VMLogParam(name = "tm") TargetMethod tm,
@VMLogParam(name = "i") int i,
@VMLogParam(name = "callee") TargetMethod callee);
void details_DispatchTableReset(
@VMLogParam(name = "tableKind") char tableKind,
@VMLogParam(name = "hub") Hub hub,
@VMLogParam(name = "index") int index);
void details_Mark(@VMLogParam(name = "s") String s, @VMLogParam(name = "tm") TargetMethod tm);
void details_MarkLevel(
@VMLogParam(name = "s") String s,
@VMLogParam(name = "tm") TargetMethod tm,
@VMLogParam(name = "level") int level);
void details_PatchDetails(@VMLogParam(name = "codeEviction") CodeEviction codeEviction);
void details_RelocateCodePointer(
@VMLogParam(name = "bitIndex") int bitIndex,
@VMLogParam(name = "from") CodePointer from,
@VMLogParam(name = "to") CodePointer to,
@VMLogParam(name = "tm") TargetMethod tm);
void details_StaleMethod(@VMLogParam(name = "nStale") int nStale, @VMLogParam(name = "tm") TargetMethod tm);
void details_VisitRefMapBits(
@VMLogParam(name = "cursor") StackFrameCursor cursor,
@VMLogParam(name = "slotPointer") Pointer slotPointer,
@VMLogParam(name = "refMap") int refMap,
@VMLogParam(name = "numBits") int numBits);
void details_PatchType(@VMLogParam(name = "type") String type);
// Threads/Code Motion
void move_Progress(@VMLogParam(name = "s") String s);
void move_CalleeReturnAddress(@VMLogParam(name = "ret") Pointer ret);
void move_ReturnAddressPatch(
@VMLogParam(name = "tm") TargetMethod tm,
@VMLogParam(name = "calleeRet") CodePointer calleeRet,
@VMLogParam(name = "newCalleeRet") CodePointer newCalleeRet);
void move_CodeMotion(
@VMLogParam(name = "tm") TargetMethod tm,
@VMLogParam(name = "from") Pointer from,
@VMLogParam(name = "to") Pointer to,
@VMLogParam(name = "size") Size size);
void move_NotCopying(@VMLogParam(name = "tm") TargetMethod tm);
void move_FixCall(
@VMLogParam(name = "tm") TargetMethod tm,
@VMLogParam(name = "i") int i,
@VMLogParam(name = "callTarget") CodePointer callTarget);
void move_FixCallForMovedCode(
@VMLogParam(name = "tm") TargetMethod tm,
@VMLogParam(name = "d") Offset d);
void move_CodeCacheBoundaries(@VMLogParam(name = "cr") SemiSpaceCodeRegion cr);
void move_DirectCallInfo(
@VMLogParam(name = "i") int i,
@VMLogParam(name = "callPos") int callPos,
@VMLogParam(name = "target") CodePointer target,
@VMLogParam(name = "itarget") CodePointer itarget);
void move_ToMoved(
@VMLogParam(name = "callee") TargetMethod callee,
@VMLogParam(name = "oldCalleeStart") Address oldCalleeStart,
@VMLogParam(name = "newCalleeStart") Address newCalleeStart,
@VMLogParam(name = "epoffset") Address epoffset,
@VMLogParam(name = "newTarget") CodePointer newTarget);
void move_ToUnmoved(@VMLogParam(name = "iTarget") CodePointer itarget);
void move_OptMethod(@VMLogParam(name = "tm") TargetMethod tm);
void move_MethodPatch(
@VMLogParam(name = "tm") TargetMethod tm,
@VMLogParam(name = "patch") boolean patch);
void move_ProcessThread(@VMLogParam(name = "vmThread") VmThread vmThread);
void bootToBaseline(@VMLogParam(name = "tm") TargetMethod tm);
/**
* This is a placeholder to control this operation which does not go via {@link VMLog}.
*/
void stackDump();
// Dumping.
/**
* This is a placeholder that serves simply to enable dumping, as the dump info is not stored in the {@link VMLog}.
*/
void dump();
}
static final CodeEvictionLogger codeEvictionLogger = new CodeEvictionLogger();
static class CodeEvictionLogger extends CodeEvictionLoggerAuto {
protected CodeEvictionLogger() {
super("CodeEviction", "Log code eviction after baseline code cache contention. Operation prefixes control logging:, " +
"Run = log each code eviction run (enabled when any other ops are enabled)" +
"Stat_.* = statistics (count evicted/surviving bytes and methods), " +
"Details_.* = give detailed information about what methods and dispatch entries are treated, " +
"Move_.* = print details about threads and code motion, " +
"Dump = give full dumps of all code addresses before and after eviction");
}
static boolean dumpEnabled() {
return codeEvictionLogger.opEnabled(Operation.Dump.ordinal());
}
static boolean stackDumpEnabled() {
return codeEvictionLogger.opEnabled(Operation.StackDump.ordinal());
}
@Override
public void checkOptions() {
super.checkOptions();
if (enabled()) {
// Always enable the Run operation if we are doing any logging.
setOperationState(Operation.Run.ordinal(), true);
// Unless explicitly set on the command line, disable Dump and StackDump
if (!getOperationStateByCLI(Operation.Dump.ordinal())) {
setOperationState(Operation.Dump.ordinal(), false);
}
if (!getOperationStateByCLI(Operation.StackDump.ordinal())) {
setOperationState(Operation.StackDump.ordinal(), false);
}
}
}
// The implementations of the trace* methods (for non-Inspector) usage.
@Override
protected void traceRun(String mode, int evictionCount, VmThread callingThread) {
Log.print(mode);
Log.print(" code eviction run #");
Log.print(evictionCount);
Log.print(" triggered by ");
Log.printThread(callingThread, true);
}
@Override
protected void traceStats_DirectCallNumbers(CodeEviction codeEviction) {
Log.print("patched direct calls: ");
Log.print(codeEviction.nCallBaseline);
Log.print(" baseline, ");
Log.print(codeEviction.nCallOpt);
Log.print(" opt, ");
Log.print(codeEviction.nCallBoot);
Log.print(" boot; looked at ");
Log.print(codeEviction.nBaseDirect);
Log.print(" sites in ");
Log.print(codeEviction.nBaseMeth);
Log.print(" baseline methods, ");
Log.print(codeEviction.nOptDirect);
Log.print(" sites in ");
Log.print(codeEviction.nOptMeth);
Log.print(" opt methods, and ");
Log.print(codeEviction.nBootDirect);
Log.print(" sites in ");
Log.print(codeEviction.nBootMeth);
Log.println(" boot image methods");
}
@Override
protected void traceStats_Fixed(CodeEviction codeEviction) {
Log.print("fixed direct calls: ");
Log.print(codeEviction.nCallBaseline);
Log.print(" baseline, ");
Log.print(codeEviction.nCallOpt);
Log.println(" opt");
}
@Override
protected void traceStats_Statistics(CodeEviction codeEviction) {
Log.print("code eviction: ");
Log.print(codeEviction.nStale);
Log.print(" stale methods (");
Log.print(codeEviction.nStaleBytes / 1024);
Log.print(" kB); ");
Log.print(codeEviction.nSurvivors);
Log.print(" survivors (");
Log.print(codeEviction.nSurvivingBytes / 1024);
Log.print(" kB) - ");
final int totalMethods = codeEviction.nSurvivors + codeEviction.nStale;
Log.print(codeEviction.nSurvivors * 100 / totalMethods);
Log.println(" % of methods survived");
}
@Override
protected void traceStats_TimingResults(CodeEviction codeEviction) {
Log.println("timing summary");
Log.print("total ");
Log.print(codeEviction.tTotal / 1000000.0);
Log.println(" ms");
long tTotal = codeEviction.tTotal;
printTime(" Phase 1 - mark ", codeEviction.tMarking, tTotal);
printTime(" mark protected methods ", codeEviction.tMarkProtected, tTotal);
printTime(" invalidate baseline calls ", codeEviction.tInvalidateCallsBaseline, tTotal);
printTime(" invalicate opt calls ", codeEviction.tInvalidateCallsOpt, tTotal);
printTime(" invalidate boot calls ", codeEviction.tInvalidateCallsBoot, tTotal);
printTime(" invalidate dispatch tables ", codeEviction.tInvalidateTables, tTotal);
printTime(" Phase 2 - compact ", codeEviction.tCompact, tTotal);
printTime(" patch stacks ", codeEviction.tPatchStacks, tTotal);
printTime(" fix baseline calls ", codeEviction.tFixCallsBaseline, tTotal);
printTime(" fix opt calls ", codeEviction.tFixCallsOpt, tTotal);
printTime(" fix boot calls ", codeEviction.tFixCallsBoot, tTotal);
}
private static void printTime(String s, long t, long tTotal) {
Log.print(s);
Log.print(t);
Log.print(" ns (");
Log.print(t / 1000000.0);
Log.print(" ms, ");
Log.print(t * 100.0 / tTotal);
Log.println(" %)");
}
@Override
protected void traceDetails_DirectCallReset(TargetMethod tm, int i, TargetMethod callee) {
Log.print("DIRECT CALL ");
Log.print(tm);
Log.print('@');
Log.print(i);
Log.print(" -> ");
Log.println(callee);
}
@Override
protected void traceDetails_DispatchTableReset(char tableKind, Hub hub, int index) {
Log.print(" ");
Log.print(tableKind);
Log.print("TABLE ");
Log.print(hub);
Log.print('@');
Log.println(index);
}
@Override
protected void traceDetails_Mark(String s, TargetMethod tm) {
Log.print("MARKING ");
Log.print(s);
Log.print(' ');
Log.println(tm);
}
@Override
protected void traceDetails_PatchDetails(CodeEviction codeEviction) {
Log.print("dispatch table / target state summary: patched ");
Log.print(codeEviction.nVT);
Log.print(" vtable, ");
Log.print(codeEviction.nIT);
Log.print(" itable entries; ");
Log.print(codeEviction.nNonvirtual);
Log.print(" nonvirtual (leaves), ");
Log.print(codeEviction.nStatic);
Log.println(" static methods");
}
@Override
protected void traceDetails_PatchType(String type) {
Log.println(type);
}
@Override
protected void traceDetails_RelocateCodePointer(int bitIndex, CodePointer from, CodePointer to, TargetMethod tm) {
Log.print(" bit ");
Log.print(bitIndex);
Log.print(" from ");
Log.print(from);
Log.print(" to ");
Log.print(to);
Log.print(": ");
Log.println(tm);
}
@Override
protected void traceDetails_StaleMethod(int nStale, TargetMethod tm) {
Log.print(nStale);
Log.print(". ");
Log.print(tm);
Log.print(" - invocations: ");
Log.println(MethodInstrumentation.initialEntryCount - tm.profile().entryCount);
}
@Override
protected void traceDetails_VisitRefMapBits(StackFrameCursor cursor, Pointer slotPointer, int refMap, int numBits) {
Log.print("visit ref map bits for code relocation: ");
Log.print(cursor.targetMethod());
Log.print(" slot pointer ");
Log.print(slotPointer);
Log.print(" ref map ");
Log.print(refMap);
Log.print(" num bits ");
Log.println(numBits);
if (refMap == 0) {
Log.println(" empty ref map");
}
}
@Override
protected void traceDetails_MarkLevel(String s, TargetMethod tm, int level) {
Log.print("MARKING (level ");
Log.print(CodeEvictionProtectCalleeDepth - level + 1);
Log.print(')');
Log.print(s);
Log.print(' ');
Log.println(tm);
}
@Override
protected void traceMove_CalleeReturnAddress(Pointer ret) {
Log.print(" \\---> callee return address ");
Log.println(ret);
}
@Override
protected void traceMove_CodeCacheBoundaries(SemiSpaceCodeRegion cr) {
Log.print("flipped spaces. from: ");
Log.print(cr.fromSpace);
Log.print("..");
Log.print(cr.fromSpace.plus(cr.spaceSize).minus(1));
Log.print(", to: ");
Log.print(cr.toSpace);
Log.print("..");
Log.println(cr.toSpace.plus(cr.spaceSize).minus(1));
}
@Override
protected void traceMove_CodeMotion(TargetMethod tm, Pointer from, Pointer to, Size size) {
Log.print("copying ");
Log.print(tm);
Log.print(" [");
Log.print(from);
Log.print("..");
Log.print(from.plus(size));
Log.print("]->[");
Log.print(to);
Log.print("..");
Log.print(to.plus(size).minus(1));
Log.println(']');
}
@Override
protected void traceMove_DirectCallInfo(int i, int callPos, CodePointer target, CodePointer itarget) {
Log.print(" ");
Log.print(i);
Log.print(". ");
Log.print("call pos ");
Log.print(callPos);
Log.print(" target ");
Log.print(target);
if (!itarget.isZero()) {
Log.print(" itarget ");
Log.print(itarget);
}
Log.println();
}
@Override
protected void traceMove_FixCall(TargetMethod tm, int i, CodePointer callTarget) {
final TargetMethod callee = callTarget.toTargetMethod();
Log.print("direct call ");
Log.print(tm);
Log.print('@');
Log.print(i);
Log.print(" -> ");
Log.print(callTarget);
Log.print("=");
Log.print(callee);
if (callee == null) {
Log.print(" (trampoline)");
}
Log.println();
}
@Override
protected void traceMove_FixCallForMovedCode(TargetMethod tm, Offset d) {
Log.print("fixing calls for moved method ");
Log.println(tm);
Log.print(" moved by ");
Log.println(d.toInt());
}
@Override
protected void traceMove_NotCopying(TargetMethod tm) {
Log.print("NOT copying ");
Log.print(tm);
Log.print(" [");
Log.print(tm.start());
Log.print("..");
Log.print(tm.start().plus(tm.size()).minus(1));
Log.println(']');
}
@Override
protected void traceMove_OptMethod(TargetMethod tm) {
Log.print("fixing calls for opt method ");
Log.println(tm);
}
@Override
protected void traceMove_ReturnAddressPatch(TargetMethod tm, CodePointer calleeRet, CodePointer newCalleeRet) {
Log.print(" \\---> callee return address (value to patch) ");
Log.print(calleeRet);
Log.print(" -> ");
Log.println(newCalleeRet);
if (!(calleeRet.toAddress().greaterEqual(tm.oldStart()) && tm.oldStart().plus(tm.size()).greaterEqual(calleeRet.toAddress()))) {
Log.print(" >>> old return address not in old method memory, but in ");
Log.println(calleeRet.toTargetMethod());
}
if (!(newCalleeRet.toAddress().greaterEqual(tm.start()) && tm.start().plus(tm.size()).greaterEqual(newCalleeRet.toAddress()))) {
Log.print(" >>> new return address not in new method memory, but in ");
Log.println(newCalleeRet.toTargetMethod());
}
}
@Override
protected void traceMove_ToMoved(TargetMethod callee, Address oldCalleeStart, Address newCalleeStart, Address epoffset, CodePointer newTarget) {
Log.print(" TO MOVED ");
Log.println(callee);
Log.print(" old start ");
Log.print(oldCalleeStart);
Log.print(" new start ");
Log.print(newCalleeStart);
Log.print(" epoffset ");
Log.println(epoffset);
Log.print(" NEW TARGET ");
Log.print(newTarget);
Log.print(' ');
if (!CodeManager.runtimeBaselineCodeRegion.isInToSpace(newTarget.toAddress())) {
Log.print("NOT IN TO-SPACE ");
}
CodeManager.runtimeBaselineCodeRegion.allowFromSpaceLookup = true;
Log.println(newTarget.toTargetMethod());
CodeManager.runtimeBaselineCodeRegion.allowFromSpaceLookup = false;
}
@Override
protected void traceMove_ToUnmoved(CodePointer iTarget) {
Log.print(" TO UNMOVED ");
Log.println(iTarget.toTargetMethod());
}
@Override
protected void traceMove_Progress(String s) {
Log.println(s);
}
@Override
protected void traceMove_MethodPatch(TargetMethod tm, boolean patch) {
if (patch) {
Log.print(" P ");
} else {
Log.print(" ");
}
Log.println(tm);
}
@Override
protected void traceDump() {
// The tracing associated with a dump is hand-coded and does not go via the VMLog
}
@Override
protected void traceBootToBaseline(TargetMethod tm) {
Log.print("boot->baseline ");
Log.println(tm);
}
@Override
protected void traceStats_Surviving(int lastSurvivorSize, int largestSurvivorSize) {
Log.print("amount surviving code eviction: ");
Log.print(lastSurvivorSize);
Log.print(" bytes, largest so far: ");
Log.print(largestSurvivorSize);
Log.println(" bytes");
}
@Override
protected void traceStackDump() {
// The tracing associated with a dump is hand-coded and does not go via the VMLog
}
@Override
protected void traceMove_ProcessThread(VmThread vmThread) {
Log.print("THREAD ");
Log.println(vmThread.getName());
}
}
// START GENERATED CODE
private static abstract class CodeEvictionLoggerAuto extends com.sun.max.vm.log.VMLogger {
public enum Operation {
BootToBaseline, Details_DirectCallReset, Details_DispatchTableReset,
Details_Mark, Details_MarkLevel, Details_PatchDetails, Details_PatchType,
Details_RelocateCodePointer, Details_StaleMethod, Details_VisitRefMapBits, Dump,
Move_CalleeReturnAddress, Move_CodeCacheBoundaries, Move_CodeMotion, Move_DirectCallInfo,
Move_FixCall, Move_FixCallForMovedCode, Move_MethodPatch, Move_NotCopying,
Move_OptMethod, Move_ProcessThread, Move_Progress, Move_ReturnAddressPatch,
Move_ToMoved, Move_ToUnmoved, Run, StackDump,
Stats_DirectCallNumbers, Stats_Fixed, Stats_Statistics, Stats_Surviving,
Stats_TimingResults;
@SuppressWarnings("hiding")
public static final Operation[] VALUES = values();
}
private static final int[] REFMAPS = new int[] {0x1, 0x5, 0x2, 0x3, 0x3, 0x1, 0x1, 0xe, 0x2, 0x1, 0x0, 0x0, 0x1, 0x1,
0xc, 0x5, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1, 0x7, 0x11, 0x1, 0x1, 0x0, 0x1,
0x1, 0x1, 0x0, 0x1};
protected CodeEvictionLoggerAuto(String name, String optionDescription) {
super(name, Operation.VALUES.length, optionDescription, REFMAPS);
}
@Override
public String operationName(int opCode) {
return Operation.VALUES[opCode].name();
}
@INLINE
public final void logBootToBaseline(TargetMethod tm) {
log(Operation.BootToBaseline.ordinal(), objectArg(tm));
}
protected abstract void traceBootToBaseline(TargetMethod tm);
@INLINE
public final void logDetails_DirectCallReset(TargetMethod tm, int i, TargetMethod callee) {
log(Operation.Details_DirectCallReset.ordinal(), objectArg(tm), intArg(i), objectArg(callee));
}
protected abstract void traceDetails_DirectCallReset(TargetMethod tm, int i, TargetMethod callee);
@INLINE
public final void logDetails_DispatchTableReset(char tableKind, Hub hub, int index) {
log(Operation.Details_DispatchTableReset.ordinal(), charArg(tableKind), classActorArg(hub.classActor), intArg(index));
}
protected abstract void traceDetails_DispatchTableReset(char tableKind, Hub hub, int index);
@INLINE
public final void logDetails_Mark(String s, TargetMethod tm) {
log(Operation.Details_Mark.ordinal(), objectArg(s), objectArg(tm));
}
protected abstract void traceDetails_Mark(String s, TargetMethod tm);
@INLINE
public final void logDetails_MarkLevel(String s, TargetMethod tm, int level) {
log(Operation.Details_MarkLevel.ordinal(), objectArg(s), objectArg(tm), intArg(level));
}
protected abstract void traceDetails_MarkLevel(String s, TargetMethod tm, int level);
@INLINE
public final void logDetails_PatchDetails(CodeEviction codeEviction) {
log(Operation.Details_PatchDetails.ordinal(), objectArg(codeEviction));
}
protected abstract void traceDetails_PatchDetails(CodeEviction codeEviction);
@INLINE
public final void logDetails_PatchType(String type) {
log(Operation.Details_PatchType.ordinal(), objectArg(type));
}
protected abstract void traceDetails_PatchType(String type);
@INLINE
public final void logDetails_RelocateCodePointer(int bitIndex, CodePointer from, CodePointer to, TargetMethod tm) {
log(Operation.Details_RelocateCodePointer.ordinal(), intArg(bitIndex), codePointerArg(from), codePointerArg(to), objectArg(tm));
}
protected abstract void traceDetails_RelocateCodePointer(int bitIndex, CodePointer from, CodePointer to, TargetMethod tm);
@INLINE
public final void logDetails_StaleMethod(int nStale, TargetMethod tm) {
log(Operation.Details_StaleMethod.ordinal(), intArg(nStale), objectArg(tm));
}
protected abstract void traceDetails_StaleMethod(int nStale, TargetMethod tm);
@INLINE
public final void logDetails_VisitRefMapBits(StackFrameCursor cursor, Pointer slotPointer, int refMap, int numBits) {
log(Operation.Details_VisitRefMapBits.ordinal(), objectArg(cursor), slotPointer, intArg(refMap), intArg(numBits));
}
protected abstract void traceDetails_VisitRefMapBits(StackFrameCursor cursor, Pointer slotPointer, int refMap, int numBits);
@INLINE
public final void logDump() {
log(Operation.Dump.ordinal());
}
protected abstract void traceDump();
@INLINE
public final void logMove_CalleeReturnAddress(Pointer ret) {
log(Operation.Move_CalleeReturnAddress.ordinal(), ret);
}
protected abstract void traceMove_CalleeReturnAddress(Pointer ret);
@INLINE
public final void logMove_CodeCacheBoundaries(SemiSpaceCodeRegion cr) {
log(Operation.Move_CodeCacheBoundaries.ordinal(), objectArg(cr));
}
protected abstract void traceMove_CodeCacheBoundaries(SemiSpaceCodeRegion cr);
@INLINE
public final void logMove_CodeMotion(TargetMethod tm, Pointer from, Pointer to, Size size) {
log(Operation.Move_CodeMotion.ordinal(), objectArg(tm), from, to, size);
}
protected abstract void traceMove_CodeMotion(TargetMethod tm, Pointer from, Pointer to, Size size);
@INLINE
public final void logMove_DirectCallInfo(int i, int callPos, CodePointer target, CodePointer itarget) {
log(Operation.Move_DirectCallInfo.ordinal(), intArg(i), intArg(callPos), codePointerArg(target), codePointerArg(itarget));
}
protected abstract void traceMove_DirectCallInfo(int i, int callPos, CodePointer target, CodePointer itarget);
@INLINE
public final void logMove_FixCall(TargetMethod tm, int i, CodePointer callTarget) {
log(Operation.Move_FixCall.ordinal(), objectArg(tm), intArg(i), codePointerArg(callTarget));
}
protected abstract void traceMove_FixCall(TargetMethod tm, int i, CodePointer callTarget);
@INLINE
public final void logMove_FixCallForMovedCode(TargetMethod tm, Offset d) {
log(Operation.Move_FixCallForMovedCode.ordinal(), objectArg(tm), d);
}
protected abstract void traceMove_FixCallForMovedCode(TargetMethod tm, Offset d);
@INLINE
public final void logMove_MethodPatch(TargetMethod tm, boolean patch) {
log(Operation.Move_MethodPatch.ordinal(), objectArg(tm), booleanArg(patch));
}
protected abstract void traceMove_MethodPatch(TargetMethod tm, boolean patch);
@INLINE
public final void logMove_NotCopying(TargetMethod tm) {
log(Operation.Move_NotCopying.ordinal(), objectArg(tm));
}
protected abstract void traceMove_NotCopying(TargetMethod tm);
@INLINE
public final void logMove_OptMethod(TargetMethod tm) {
log(Operation.Move_OptMethod.ordinal(), objectArg(tm));
}
protected abstract void traceMove_OptMethod(TargetMethod tm);
@INLINE
public final void logMove_ProcessThread(VmThread vmThread) {
log(Operation.Move_ProcessThread.ordinal(), vmThreadArg(vmThread));
}
protected abstract void traceMove_ProcessThread(VmThread vmThread);
@INLINE
public final void logMove_Progress(String s) {
log(Operation.Move_Progress.ordinal(), objectArg(s));
}
protected abstract void traceMove_Progress(String s);
@INLINE
public final void logMove_ReturnAddressPatch(TargetMethod tm, CodePointer calleeRet, CodePointer newCalleeRet) {
log(Operation.Move_ReturnAddressPatch.ordinal(), objectArg(tm), codePointerArg(calleeRet), codePointerArg(newCalleeRet));
}
protected abstract void traceMove_ReturnAddressPatch(TargetMethod tm, CodePointer calleeRet, CodePointer newCalleeRet);
@INLINE
public final void logMove_ToMoved(TargetMethod callee, Address oldCalleeStart, Address newCalleeStart, Address epoffset, CodePointer newTarget) {
log(Operation.Move_ToMoved.ordinal(), objectArg(callee), oldCalleeStart, newCalleeStart, epoffset, codePointerArg(newTarget));
}
protected abstract void traceMove_ToMoved(TargetMethod callee, Address oldCalleeStart, Address newCalleeStart, Address epoffset, CodePointer newTarget);
@INLINE
public final void logMove_ToUnmoved(CodePointer iTarget) {
log(Operation.Move_ToUnmoved.ordinal(), codePointerArg(iTarget));
}
protected abstract void traceMove_ToUnmoved(CodePointer iTarget);
@INLINE
public final void logRun(String mode, int evictionCount, VmThread callingThread) {
log(Operation.Run.ordinal(), objectArg(mode), intArg(evictionCount), vmThreadArg(callingThread));
}
protected abstract void traceRun(String mode, int evictionCount, VmThread callingThread);
@INLINE
public final void logStackDump() {
log(Operation.StackDump.ordinal());
}
protected abstract void traceStackDump();
@INLINE
public final void logStats_DirectCallNumbers(CodeEviction codeEviction) {
log(Operation.Stats_DirectCallNumbers.ordinal(), objectArg(codeEviction));
}
protected abstract void traceStats_DirectCallNumbers(CodeEviction codeEviction);
@INLINE
public final void logStats_Fixed(CodeEviction codeEviction) {
log(Operation.Stats_Fixed.ordinal(), objectArg(codeEviction));
}
protected abstract void traceStats_Fixed(CodeEviction codeEviction);
@INLINE
public final void logStats_Statistics(CodeEviction codeEviction) {
log(Operation.Stats_Statistics.ordinal(), objectArg(codeEviction));
}
protected abstract void traceStats_Statistics(CodeEviction codeEviction);
@INLINE
public final void logStats_Surviving(int lastSurvivorSize, int largestSurvivorSize) {
log(Operation.Stats_Surviving.ordinal(), intArg(lastSurvivorSize), intArg(largestSurvivorSize));
}
protected abstract void traceStats_Surviving(int lastSurvivorSize, int largestSurvivorSize);
@INLINE
public final void logStats_TimingResults(CodeEviction codeEviction) {
log(Operation.Stats_TimingResults.ordinal(), objectArg(codeEviction));
}
protected abstract void traceStats_TimingResults(CodeEviction codeEviction);
@Override
protected void trace(Record r) {
switch (r.getOperation()) {
case 0: { //BootToBaseline
traceBootToBaseline(toTargetMethod(r, 1));
break;
}
case 1: { //Details_DirectCallReset
traceDetails_DirectCallReset(toTargetMethod(r, 1), toInt(r, 2), toTargetMethod(r, 3));
break;
}
case 2: { //Details_DispatchTableReset
traceDetails_DispatchTableReset(toChar(r, 1), toHub(r, 2), toInt(r, 3));
break;
}
case 3: { //Details_Mark
traceDetails_Mark(toString(r, 1), toTargetMethod(r, 2));
break;
}
case 4: { //Details_MarkLevel
traceDetails_MarkLevel(toString(r, 1), toTargetMethod(r, 2), toInt(r, 3));
break;
}
case 5: { //Details_PatchDetails
traceDetails_PatchDetails(toCodeEviction(r, 1));
break;
}
case 6: { //Details_PatchType
traceDetails_PatchType(toString(r, 1));
break;
}
case 7: { //Details_RelocateCodePointer
traceDetails_RelocateCodePointer(toInt(r, 1), toCodePointer(r, 2), toCodePointer(r, 3), toTargetMethod(r, 4));
break;
}
case 8: { //Details_StaleMethod
traceDetails_StaleMethod(toInt(r, 1), toTargetMethod(r, 2));
break;
}
case 9: { //Details_VisitRefMapBits
traceDetails_VisitRefMapBits(toStackFrameCursor(r, 1), toPointer(r, 2), toInt(r, 3), toInt(r, 4));
break;
}
case 10: { //Dump
traceDump();
break;
}
case 11: { //Move_CalleeReturnAddress
traceMove_CalleeReturnAddress(toPointer(r, 1));
break;
}
case 12: { //Move_CodeCacheBoundaries
traceMove_CodeCacheBoundaries(toSemiSpaceCodeRegion(r, 1));
break;
}
case 13: { //Move_CodeMotion
traceMove_CodeMotion(toTargetMethod(r, 1), toPointer(r, 2), toPointer(r, 3), toSize(r, 4));
break;
}
case 14: { //Move_DirectCallInfo
traceMove_DirectCallInfo(toInt(r, 1), toInt(r, 2), toCodePointer(r, 3), toCodePointer(r, 4));
break;
}
case 15: { //Move_FixCall
traceMove_FixCall(toTargetMethod(r, 1), toInt(r, 2), toCodePointer(r, 3));
break;
}
case 16: { //Move_FixCallForMovedCode
traceMove_FixCallForMovedCode(toTargetMethod(r, 1), toOffset(r, 2));
break;
}
case 17: { //Move_MethodPatch
traceMove_MethodPatch(toTargetMethod(r, 1), toBoolean(r, 2));
break;
}
case 18: { //Move_NotCopying
traceMove_NotCopying(toTargetMethod(r, 1));
break;
}
case 19: { //Move_OptMethod
traceMove_OptMethod(toTargetMethod(r, 1));
break;
}
case 20: { //Move_ProcessThread
traceMove_ProcessThread(toVmThread(r, 1));
break;
}
case 21: { //Move_Progress
traceMove_Progress(toString(r, 1));
break;
}
case 22: { //Move_ReturnAddressPatch
traceMove_ReturnAddressPatch(toTargetMethod(r, 1), toCodePointer(r, 2), toCodePointer(r, 3));
break;
}
case 23: { //Move_ToMoved
traceMove_ToMoved(toTargetMethod(r, 1), toAddress(r, 2), toAddress(r, 3), toAddress(r, 4), toCodePointer(r, 5));
break;
}
case 24: { //Move_ToUnmoved
traceMove_ToUnmoved(toCodePointer(r, 1));
break;
}
case 25: { //Run
traceRun(toString(r, 1), toInt(r, 2), toVmThread(r, 3));
break;
}
case 26: { //StackDump
traceStackDump();
break;
}
case 27: { //Stats_DirectCallNumbers
traceStats_DirectCallNumbers(toCodeEviction(r, 1));
break;
}
case 28: { //Stats_Fixed
traceStats_Fixed(toCodeEviction(r, 1));
break;
}
case 29: { //Stats_Statistics
traceStats_Statistics(toCodeEviction(r, 1));
break;
}
case 30: { //Stats_Surviving
traceStats_Surviving(toInt(r, 1), toInt(r, 2));
break;
}
case 31: { //Stats_TimingResults
traceStats_TimingResults(toCodeEviction(r, 1));
break;
}
}
}
static CodeEviction toCodeEviction(Record r, int argNum) {
if (MaxineVM.isHosted()) {
return (CodeEviction) ObjectArg.getArg(r, argNum);
} else {
return asCodeEviction(toObject(r, argNum));
}
}
@INTRINSIC(UNSAFE_CAST)
private static native CodeEviction asCodeEviction(Object arg);
static Hub toHub(Record r, int argNum) {
if (MaxineVM.isHosted()) {
return (Hub) ObjectArg.getArg(r, argNum);
} else {
return asHub(toObject(r, argNum));
}
}
@INTRINSIC(UNSAFE_CAST)
private static native Hub asHub(Object arg);
static SemiSpaceCodeRegion toSemiSpaceCodeRegion(Record r, int argNum) {
if (MaxineVM.isHosted()) {
return (SemiSpaceCodeRegion) ObjectArg.getArg(r, argNum);
} else {
return asSemiSpaceCodeRegion(toObject(r, argNum));
}
}
@INTRINSIC(UNSAFE_CAST)
private static native SemiSpaceCodeRegion asSemiSpaceCodeRegion(Object arg);
static StackFrameCursor toStackFrameCursor(Record r, int argNum) {
if (MaxineVM.isHosted()) {
return (StackFrameCursor) ObjectArg.getArg(r, argNum);
} else {
return asStackFrameCursor(toObject(r, argNum));
}
}
@INTRINSIC(UNSAFE_CAST)
private static native StackFrameCursor asStackFrameCursor(Object arg);
}
// END GENERATED CODE
}