diff --git a/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilerRuntime.java b/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilerRuntime.java index ba144087f576..3c4f55021354 100644 --- a/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilerRuntime.java +++ b/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilerRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -358,5 +358,4 @@ default ResolvedJavaType resolveType(MetaAccessProvider metaAccess, String class * silent. */ boolean isSuppressedFailure(TruffleCompilable compilable, Supplier serializedException); - } diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BackgroundCompileQueue.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BackgroundCompileQueue.java index bd4009f37cfa..05ac69f120a0 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BackgroundCompileQueue.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BackgroundCompileQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -218,7 +218,8 @@ public int getQueueSize() { /** * Return call targets waiting in queue. This does not include call targets currently being - * compiled. + * compiled. If {@code engine} is {@code null}, the call targets for all engines are returned, + * otherwise only the call targets belonging to {@code engine} will be returned. */ public Collection getQueuedTargets(EngineData engine) { BlockingQueue queue = this.compilationQueue; @@ -230,7 +231,7 @@ public Collection getQueuedTargets(EngineData engine) { CompilationTask.ExecutorServiceWrapper[] array = queue.toArray(new CompilationTask.ExecutorServiceWrapper[0]); for (CompilationTask.ExecutorServiceWrapper wrapper : array) { OptimizedCallTarget target = wrapper.compileTask.targetRef.get(); - if (target != null && target.engine == engine) { + if (target != null && (engine == null || target.engine == engine)) { queuedTargets.add(target); } } diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/EngineData.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/EngineData.java index 250483505272..09f0fa9d373f 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/EngineData.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/EngineData.java @@ -68,6 +68,7 @@ import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxCalleeSize; import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxPropagationDepth; import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingTraceEvents; +import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.StoppedCompilationRetryDelay; import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilation; import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilationDetails; import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceDeoptimizeFrame; @@ -85,6 +86,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.logging.Level; @@ -148,6 +150,7 @@ public final class EngineData { @CompilationFinal public boolean traceDeoptimizeFrame; @CompilationFinal public boolean compileAOTOnCreate; @CompilationFinal public boolean firstTierOnly; + @CompilationFinal public long stoppedCompilationRetryDelay; // compilation queue options @CompilationFinal public boolean priorityQueue; @@ -305,6 +308,7 @@ private void loadOptions(OptionValues options, SandboxPolicy sandboxPolicy) { this.firstTierOnly = options.get(Mode) == EngineModeEnum.LATENCY; this.propagateCallAndLoopCount = options.get(PropagateLoopCountToLexicalSingleCaller); this.propagateCallAndLoopCountMaxDepth = options.get(PropagateLoopCountToLexicalSingleCallerMaxDepth); + this.stoppedCompilationRetryDelay = options.get(StoppedCompilationRetryDelay); // compilation queue options priorityQueue = options.get(PriorityQueue); @@ -492,6 +496,16 @@ public TruffleLogger getLogger(String loggerId) { return polyglotEngine != null ? loggerFactory.apply(loggerId) : null; } + private final AtomicBoolean logShutdownCompilations = new AtomicBoolean(true); + + /** + * Only log compilation shutdowns (see {@code OptimizedCallTarget.isCompilationStopped()}) once + * per engine. + */ + public AtomicBoolean logShutdownCompilations() { + return logShutdownCompilations; + } + @SuppressWarnings("static-method") public void mergeLoadedSources(Source[] sources) { OptimizedRuntimeAccessor.SOURCE.mergeLoadedSources(sources); diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java index 028aafc0fabe..6335471962e5 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java @@ -43,6 +43,7 @@ import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -51,7 +52,9 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Supplier; +import java.util.logging.Level; +import com.oracle.truffle.api.TruffleLogger; import org.graalvm.options.OptionKey; import org.graalvm.options.OptionValues; @@ -81,6 +84,7 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.compiler.TruffleCompilable; import com.oracle.truffle.runtime.OptimizedRuntimeOptions.ExceptionAction; +import com.oracle.truffle.runtime.OptimizedTruffleRuntime.CompilationActivityMode; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.SpeculationLog; @@ -855,6 +859,62 @@ final boolean isCompilationFailed() { return compilationFailed; } + final CompilationActivityMode getCompilationActivityMode() { + CompilationActivityMode compilationActivityMode = runtime().getCompilationActivityMode(); + long sct = runtime().stoppedCompilationTime().get(); + if (compilationActivityMode == CompilationActivityMode.STOP_COMPILATION) { + if (sct != 0 && System.currentTimeMillis() - sct > engine.stoppedCompilationRetryDelay) { + runtime().stoppedCompilationTime().compareAndSet(sct, 0); + // Try again every StoppedCompilationRetryDelay milliseconds to potentially trigger + // a code cache sweep. + compilationActivityMode = CompilationActivityMode.RUN_COMPILATION; + } + } + + switch (compilationActivityMode) { + case RUN_COMPILATION: { + // This is the common case - compilations are not stopped. + return CompilationActivityMode.RUN_COMPILATION; + } + case STOP_COMPILATION: { + if (sct == 0) { + runtime().stoppedCompilationTime().compareAndSet(0, System.currentTimeMillis()); + } + // Flush the compilations queue for now. There's still a chance that compilation + // will be re-enabled eventually, if the hosts code cache can be cleaned up. + Collection targets = runtime().getCompileQueue().getQueuedTargets(null); + // If there's just a single compilation target in the queue, the chance is high that + // it is the one we've just added after the StoppedCompilationRetryDelay ran out, so + // keep it to potentially trigger a code cache sweep. + if (targets.size() > 1) { + for (OptimizedCallTarget target : targets) { + target.cancelCompilation("Compilation temporary disabled due to full code cache."); + } + } + return CompilationActivityMode.STOP_COMPILATION; + } + case SHUTDOWN_COMPILATION: { + // Compilation was shut down permanently because the hosts code cache ran full and + // the host was configured without support for code cache sweeping. + TruffleLogger logger = engine.getLogger("engine"); + // The logger can be null if the engine is closed. + if (logger != null && engine.logShutdownCompilations().compareAndExchange(true, false)) { + logger.log(Level.WARNING, "Truffle compilations permanently disabled because of full code cache. " + + "Increase the code cache size using '-XX:ReservedCodeCacheSize=' and/or run with '-XX:+UseCodeCacheFlushing -XX:+MethodFlushing'."); + } + // Flush the compilation queue and mark all methods as not compilable. + for (OptimizedCallTarget target : runtime().getCompileQueue().getQueuedTargets(null)) { + target.cancelCompilation("Compilation permanently disabled due to full code cache."); + target.compilationFailed = true; + } + return CompilationActivityMode.SHUTDOWN_COMPILATION; + } + default: + CompilerDirectives.shouldNotReachHere("Invalid compilation activity mode: " + compilationActivityMode); + } + return CompilationActivityMode.RUN_COMPILATION; + } + /** * Returns true if the call target was already compiled or was compiled * synchronously. Returns false if compilation was not scheduled or is happening in @@ -866,6 +926,7 @@ public final boolean compile(boolean lastTierCompilation) { if (!needsCompile(lastTier)) { return true; } + if (!isSubmittedForCompilation()) { if (!acceptForCompilation()) { // do not try to compile again @@ -873,6 +934,15 @@ public final boolean compile(boolean lastTierCompilation) { return false; } + CompilationActivityMode cam = getCompilationActivityMode(); + if (cam != CompilationActivityMode.RUN_COMPILATION) { + if (cam == CompilationActivityMode.SHUTDOWN_COMPILATION) { + // Compilation was shut down permanently. + compilationFailed = true; + } + return false; + } + CompilationTask task = null; // Do not try to compile this target concurrently, // but do not block other threads if compilation is not asynchronous. diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedRuntimeOptions.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedRuntimeOptions.java index 63cc586ba584..6ea147cb42ca 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedRuntimeOptions.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedRuntimeOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -158,6 +158,15 @@ public ExceptionAction apply(String s) { // TODO: GR-29949 public static final OptionKey CompilerIdleDelay = new OptionKey<>(10000L); + @Option(help = "Before the Truffle runtime submits an OptimizedCallTarget for compilation, it checks for the compilation " + + "activity mode in the host VM. If the activity mode indicates a full code cache, no new compilation " + + "requests are submitted and the compilation queue is flushed. After 'StoppedCompilationRetryDelay' " + + "milliseconds new compilations will be submitted again (which might trigger a sweep of the code " + + "cache and a reset of the compilation activity mode in the host JVM). The option is only supported on " + + "the HotSpot Truffle runtime. On runtimes which don't support it the option has no effect. default: 5000", // + usageSyntax = "", category = OptionCategory.EXPERT) // + public static final OptionKey StoppedCompilationRetryDelay = new OptionKey<>(5000L); + @Option(help = "Manually set the number of compiler threads. By default, the number of compiler threads is scaled with the number of available cores on the CPU.", usageSyntax = "[1, inf)", category = OptionCategory.EXPERT, // stability = OptionStability.STABLE, sandbox = SandboxPolicy.UNTRUSTED) // public static final OptionKey CompilerThreads = new OptionKey<>(-1); diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedTruffleRuntime.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedTruffleRuntime.java index 41bf7d063756..1d9f6eba845a 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedTruffleRuntime.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedTruffleRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -63,6 +63,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.logging.Level; @@ -912,6 +913,12 @@ private void notifyCompilationFailure(OptimizedCallTarget callTarget, Throwable protected void onEngineCreated(EngineData engine) { } + private final AtomicLong stoppedCompilationTime = new AtomicLong(0); + + public final AtomicLong stoppedCompilationTime() { + return stoppedCompilationTime; + } + @SuppressWarnings("try") public CompilationTask submitForCompilation(OptimizedCallTarget optimizedCallTarget, boolean lastTierCompilation) { Priority priority = new Priority(optimizedCallTarget.getCallAndLoopCount(), lastTierCompilation ? Priority.Tier.LAST : Priority.Tier.FIRST); @@ -1483,4 +1490,30 @@ static OptionCategory matchCategory(TruffleCompilerOptionDescriptor d) { } } + /** + * Represents HotSpot's compilation activity mode which is one of: {@code stop_compilation = 0}, + * {@code run_compilation = 1} or {@code shutdown_compilation = 2}. Should be in sync with the + * {@code CompilerActivity} enum in {@code hotspot/share/compiler/compileBroker.hpp}. + */ + public enum CompilationActivityMode { + STOP_COMPILATION, + RUN_COMPILATION, + SHUTDOWN_COMPILATION; + + public static CompilationActivityMode fromInteger(int i) { + return switch (i) { + case 0 -> STOP_COMPILATION; + case 1 -> RUN_COMPILATION; + case 2 -> SHUTDOWN_COMPILATION; + default -> throw new RuntimeException("Invalid CompilationActivityMode " + i); + }; + } + } + + /** + * Returns the current host compilation activity mode. The default is to run compilations. + */ + public CompilationActivityMode getCompilationActivityMode() { + return CompilationActivityMode.RUN_COMPILATION; + } } diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/HotSpotTruffleRuntime.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/HotSpotTruffleRuntime.java index c49fab962417..4743b430cd76 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/HotSpotTruffleRuntime.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/HotSpotTruffleRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -40,6 +40,9 @@ */ package com.oracle.truffle.runtime.hotspot; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.ref.Reference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -692,4 +695,31 @@ public boolean isLibGraalCompilationEnabled() { return compilationSupport instanceof LibGraalTruffleCompilationSupport; } + static final MethodHandle getCompilationActivityMode; + static { + MethodHandle mHandle = null; + try { + MethodType mt = MethodType.methodType(int.class); + mHandle = MethodHandles.lookup().findVirtual(HotSpotJVMCIRuntime.class, "getCompilationActivityMode", mt); + } catch (NoSuchMethodException | IllegalAccessException e) { + // Older JVMCI runtimes might not support `getCompilationActivityMode()` + } + getCompilationActivityMode = mHandle; + } + + /** + * Returns the current host compilation activity mode based on HotSpot's code cache state. + */ + @Override + public CompilationActivityMode getCompilationActivityMode() { + int activityMode = 1; // Default is to run compilations + if (getCompilationActivityMode != null) { + try { + activityMode = (int) getCompilationActivityMode.invokeExact(HotSpotJVMCIRuntime.runtime()); + } catch (Throwable t) { + throw new RuntimeException("Can't get HotSpot's compilation activity mode", t); + } + } + return CompilationActivityMode.fromInteger(activityMode); + } }