/*
* Copyright (c) 2015, 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.oracle.svm.core.code;
import java.util.Arrays;
import java.util.List;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.options.Option;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.c.function.CodePointer;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.Pointer;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.RestrictHeapAccess;
import com.oracle.svm.core.annotate.RestrictHeapAccess.Access;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.c.NonmovableArray;
import com.oracle.svm.core.c.NonmovableArrays;
import com.oracle.svm.core.deopt.DeoptimizedFrame;
import com.oracle.svm.core.deopt.SubstrateInstalledCode;
import com.oracle.svm.core.heap.CodeReferenceMapDecoder;
import com.oracle.svm.core.heap.ObjectReferenceVisitor;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.thread.JavaVMOperation;
import com.oracle.svm.core.thread.VMOperation;
import com.oracle.svm.core.util.Counter;
import com.oracle.svm.core.util.CounterFeature;
import com.oracle.svm.core.util.VMError;
import jdk.vm.ci.code.InstalledCode;
Provides the main entry points to look up metadata for code, either
ahead-of-time compiled code in the native image
or
code compiled at runtime
. Users of this class must take special care because code can be invalidated at arbitrary times and their metadata can be freed, see notes on CodeInfoAccess
.
/**
* Provides the main entry points to look up metadata for code, either {@link #getImageCodeCache()
* ahead-of-time compiled code in the native image} or {@link CodeInfoTable#getRuntimeCodeCache()
* code compiled at runtime}.
* <p>
* Users of this class must take special care because code can be invalidated at arbitrary times and
* their metadata can be freed, see notes on {@link CodeInfoAccess}.
*/
public class CodeInfoTable {
private static CodeInfo imageCodeInfo;
public static class Options {
@Option(help = "Count accesses to the image and runtime code info table")//
public static final HostedOptionKey<Boolean> CodeCacheCounters = new HostedOptionKey<>(false);
}
@Fold
public static ImageCodeInfo getImageCodeCache() {
return ImageSingletons.lookup(ImageCodeInfo.class);
}
@Fold
public static RuntimeCodeCache getRuntimeCodeCache() {
return ImageSingletons.lookup(RuntimeCodeCache.class);
}
@Uninterruptible(reason = "Executes during isolate creation.")
public static void prepareImageCodeInfo() {
// Stored in this class because ImageCodeInfo is immutable
imageCodeInfo = getImageCodeCache().prepareCodeInfo();
assert imageCodeInfo.notEqual(WordFactory.zero());
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static CodeInfo getImageCodeInfo() {
assert imageCodeInfo.notEqual(WordFactory.zero()) : "uninitialized";
return imageCodeInfo;
}
public static CodeInfoQueryResult lookupCodeInfoQueryResult(CodeInfo info, CodePointer absoluteIP) {
counters().lookupCodeInfoCount.inc();
if (info.isNull()) {
return null;
}
CodeInfoQueryResult result = new CodeInfoQueryResult();
result.ip = absoluteIP;
CodeInfoAccess.lookupCodeInfo(info, CodeInfoAccess.relativeIP(info, absoluteIP), result);
return result;
}
public static CodeInfoQueryResult lookupDeoptimizationEntrypoint(int deoptOffsetInImage, long encodedBci) {
counters().lookupDeoptimizationEntrypointCount.inc();
/* Deoptimization entry points are always in the image, i.e., never compiled at run time. */
CodeInfo info = getImageCodeInfo();
CodeInfoQueryResult result = new CodeInfoQueryResult();
long relativeIP = CodeInfoAccess.lookupDeoptimizationEntrypoint(info, deoptOffsetInImage, encodedBci, result);
if (relativeIP < 0) {
return null;
}
result.ip = CodeInfoAccess.absoluteIP(info, relativeIP);
return result;
}
public static boolean visitObjectReferences(Pointer sp, CodePointer ip, CodeInfo info, DeoptimizedFrame deoptimizedFrame, ObjectReferenceVisitor visitor) {
counters().visitObjectReferencesCount.inc();
if (deoptimizedFrame != null) {
/*
* It is a deoptimized frame. The DeoptimizedFrame object is stored in the frame, but it
* is pinned so we do not have to do anything.
*/
return true;
}
/*
* NOTE: if this code does not execute in a VM operation, it is possible for the visited
* frame to be deoptimized concurrently, and that one of the references is overwritten with
* the reference to the DeoptimizedFrame object, before, after, or during visiting it.
*/
NonmovableArray<Byte> referenceMapEncoding = NonmovableArrays.nullArray();
long referenceMapIndex = CodeInfoQueryResult.NO_REFERENCE_MAP;
if (info.isNonNull()) {
referenceMapEncoding = CodeInfoAccess.getStackReferenceMapEncoding(info);
referenceMapIndex = CodeInfoAccess.lookupStackReferenceMapIndex(info, CodeInfoAccess.relativeIP(info, ip));
}
if (referenceMapIndex == CodeInfoQueryResult.NO_REFERENCE_MAP) {
throw reportNoReferenceMap(sp, ip, info);
}
return CodeReferenceMapDecoder.walkOffsetsFromPointer(sp, referenceMapEncoding, referenceMapIndex, visitor);
}
public static RuntimeException reportNoReferenceMap(Pointer sp, CodePointer ip, CodeInfo info) {
Log.log().string("ip: ").hex(ip).string(" sp: ").hex(sp).string(" info:");
CodeInfoAccess.log(info, Log.log()).newline();
throw VMError.shouldNotReachHere("No reference map information found");
}
Retrieves the InstalledCode
that contains the provided instruction pointer. Returns null
if the instruction pointer is not within a runtime compile method. /**
* Retrieves the {@link InstalledCode} that contains the provided instruction pointer. Returns
* {@code null} if the instruction pointer is not within a runtime compile method.
*/
@Uninterruptible(reason = "Prevent the GC from freeing the CodeInfo object.")
public static SubstrateInstalledCode lookupInstalledCode(CodePointer ip) {
counters().lookupInstalledCodeCount.inc();
UntetheredCodeInfo untetheredInfo = lookupCodeInfo(ip);
if (untetheredInfo.isNull() || untetheredInfo.equal(getImageCodeInfo())) {
return null; // not within a runtime-compiled method
}
Object tether = CodeInfoAccess.acquireTether(untetheredInfo);
try {
CodeInfo info = CodeInfoAccess.convert(untetheredInfo, tether);
return getInstalledCode0(info);
} finally {
CodeInfoAccess.releaseTether(untetheredInfo, tether);
}
}
@Uninterruptible(reason = "Wrap the now safe call to interruptibly retrieve InstalledCode.", calleeMustBe = false)
private static SubstrateInstalledCode getInstalledCode0(CodeInfo info) {
return RuntimeCodeInfoAccess.getInstalledCode(info);
}
public static void invalidateInstalledCode(SubstrateInstalledCode installedCode) {
/* Captures "installedCode" for the VMOperation. */
JavaVMOperation.enqueueBlockingSafepoint("CodeInfoTable.invalidateInstalledCode", () -> {
counters().invalidateInstalledCodeCount.inc();
if (installedCode.isAlive()) { // could be invalid (non-entrant), but executing
invalidateInstalledCodeAtSafepoint(WordFactory.pointer(installedCode.getAddress()));
}
});
}
/**
* This invalidation is done at a safepoint and we acquire the tether of the {@link CodeInfo}
* object. Therefore, it is guaranteed that there is no conflict with the {@link CodeInfo}
* invalidation/freeing that the GC does because the tether is still reachable.
*/
@Uninterruptible(reason = "Must prevent the GC from freeing the CodeInfo object.")
private static void invalidateInstalledCodeAtSafepoint(CodePointer codePointer) {
UntetheredCodeInfo untetheredInfo = getRuntimeCodeCache().lookupCodeInfo(codePointer);
Object tether = CodeInfoAccess.acquireTether(untetheredInfo);
try {
assert tether != null : "Invalidation can't be triggered before the code was fully installed.";
CodeInfo info = CodeInfoAccess.convert(untetheredInfo, tether);
// Multiple threads could trigger this method - only the first one must do something.
if (CodeInfoAccess.isAlive(info)) {
invalidateCodeAtSafepoint0(info);
}
assert CodeInfoAccess.getState(info) == CodeInfo.STATE_PARTIALLY_FREED;
} finally {
CodeInfoAccess.releaseTether(untetheredInfo, tether);
}
}
@Uninterruptible(reason = "Wrap the now safe call to interruptibly retrieve InstalledCode.", calleeMustBe = false)
private static void invalidateCodeAtSafepoint0(CodeInfo info) {
invalidateCodeAtSafepoint(info);
}
private static void invalidateCodeAtSafepoint(CodeInfo info) {
VMOperation.guaranteeInProgressAtSafepoint("Must be at a safepoint");
RuntimeCodeCache codeCache = getRuntimeCodeCache();
long num = codeCache.logMethodOperation(info, RuntimeCodeCache.INFO_INVALIDATE);
codeCache.invalidateMethod(info);
codeCache.logMethodOperationEnd(num);
}
@RestrictHeapAccess(access = Access.NO_ALLOCATION, reason = "Called by the GC")
public static void invalidateNonStackCodeAtSafepoint(CodeInfo info) {
VMOperation.guaranteeGCInProgress("Must only be called during a GC.");
RuntimeCodeCache codeCache = getRuntimeCodeCache();
long num = codeCache.logMethodOperation(info, RuntimeCodeCache.INFO_INVALIDATE);
codeCache.invalidateNonStackMethod(info);
codeCache.logMethodOperationEnd(num);
}
@Uninterruptible(reason = "Prevent the GC from freeing the CodeInfo.", callerMustBe = true)
public static UntetheredCodeInfo lookupCodeInfo(CodePointer ip) {
counters().lookupCodeInfoCount.inc();
if (CodeInfoAccess.contains(getImageCodeInfo(), ip)) {
return getImageCodeInfo();
} else {
return getRuntimeCodeCache().lookupCodeInfo(ip);
}
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void tearDown() {
getRuntimeCodeCache().tearDown();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private static CodeInfoTableCounters counters() {
return ImageSingletons.lookup(CodeInfoTableCounters.class);
}
}
final class CodeInfoTableCounters {
private final Counter.Group counters = new Counter.Group(CodeInfoTable.Options.CodeCacheCounters, "CodeInfoTable");
final Counter lookupCodeInfoCount = new Counter(counters, "lookupCodeInfo", "");
final Counter lookupDeoptimizationEntrypointCount = new Counter(counters, "lookupDeoptimizationEntrypoint", "");
final Counter visitObjectReferencesCount = new Counter(counters, "visitObjectReferences", "");
final Counter lookupInstalledCodeCount = new Counter(counters, "lookupInstalledCode", "");
final Counter invalidateInstalledCodeCount = new Counter(counters, "invalidateInstalledCode", "");
}
@AutomaticFeature
class CodeInfoFeature implements Feature {
@Override
public List<Class<? extends Feature>> getRequiredFeatures() {
return Arrays.asList(CounterFeature.class);
}
@Override
public void duringSetup(DuringSetupAccess access) {
ImageSingletons.add(CodeInfoTableCounters.class, new CodeInfoTableCounters());
ImageSingletons.add(CodeInfoDecoderCounters.class, new CodeInfoDecoderCounters());
ImageSingletons.add(CodeInfoEncoder.Counters.class, new CodeInfoEncoder.Counters());
ImageSingletons.add(ImageCodeInfo.class, new ImageCodeInfo());
ImageSingletons.add(RuntimeCodeCache.class, new RuntimeCodeCache());
}
@Override
public void afterCompilation(AfterCompilationAccess config) {
ImageCodeInfo imageInfo = CodeInfoTable.getImageCodeCache();
config.registerAsImmutable(imageInfo);
config.registerAsImmutable(imageInfo.codeInfoIndex);
config.registerAsImmutable(imageInfo.codeInfoEncodings);
config.registerAsImmutable(imageInfo.referenceMapEncoding);
config.registerAsImmutable(imageInfo.frameInfoEncodings);
config.registerAsImmutable(imageInfo.frameInfoObjectConstants);
config.registerAsImmutable(imageInfo.frameInfoSourceClasses);
config.registerAsImmutable(imageInfo.frameInfoSourceMethodNames);
config.registerAsImmutable(imageInfo.frameInfoNames);
}
}