Report silent pregen failures

- Add PregenDiagnostics.probeFailed so capability probes log their name, class and message at debug instead of vanishing
- Report the moonrise worker pool, mantle radius, engine access and Folia submit probe failures through that helper
- Report async pregen mantle cleanup failures through IrisLogging.reportError with the trace instead of dropping them
- Report a stranded plugin chunk ticket through IrisLogging.reportError so the leaked pin is visible
- Emit the chunk load failure trace before the metrics snapshot so a failing snapshot can no longer silence it
- Report the pregen cancellation and mantle handle probe failures in PregenMantleBackpressure
- Report the Medieval engine resolution probe failure
- Record the real Vector API initialization failure in SimdSupport and print the accurate kernel selection reason
- Add PregenDiagnosticsTest, PregenSilentFailureContractTest and SimdSupportStatusTest
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-08 04:33:31 -04:00
1 parent 9d22a18a3a
commit e030ac3be5
8 files changed
+513 -40

No files matched your search

@@ -0,0 +1,43 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.core.pregenerator;
import art.arcane.iris.spi.IrisLogging;
public final class PregenDiagnostics {
private PregenDiagnostics() {
}
public static void probeFailed(String probe, Throwable failure) {
IrisLogging.debug("Pregen probe " + describeProbe(probe) + " failed: " + describeFailure(failure));
}
public static String describeProbe(String probe) {
return probe == null || probe.isBlank() ? "unnamed" : probe;
}
public static String describeFailure(Throwable failure) {
if (failure == null) {
return "no throwable reported";
}
String message = failure.getMessage();
return message == null ? failure.getClass().getName() : failure.getClass().getName() + ": " + message;
}
}
@@ -170,7 +170,8 @@ public final class PregenMantleBackpressure {
try {
return cancelled.getAsBoolean();
} catch (Throwable ignored) {
} catch (Throwable e) {
PregenDiagnostics.probeFailed("pregen cancellation state", e);
return false;
}
}
@@ -178,7 +179,8 @@ public final class PregenMantleBackpressure {
private Mantle resolveMantle() {
try {
return mantleSupplier.get();
} catch (Throwable ignored) {
} catch (Throwable e) {
PregenDiagnostics.probeFailed("mantle handle for backpressure", e);
return null;
}
}
@@ -22,6 +22,7 @@ import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.core.IrisPaperLikeBackendMode;
import art.arcane.iris.core.IrisRuntimeSchedulerMode;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.pregenerator.PregenDiagnostics;
import art.arcane.iris.core.pregenerator.PregenListener;
import art.arcane.iris.core.pregenerator.PregenMantleBackpressure;
import art.arcane.iris.core.pregenerator.PregeneratorMethod;
@@ -212,7 +213,8 @@ public class AsyncPregenMethod implements PregeneratorMethod {
if (coreThreads instanceof Thread[] threadsArray) {
return threadsArray.length;
}
} catch (Throwable ignored) {
} catch (Throwable e) {
PregenDiagnostics.probeFailed("moonrise worker pool size", e);
}
return -1;
@@ -238,7 +240,8 @@ public class AsyncPregenMethod implements PregeneratorMethod {
int resolved = radius > 0 ? Math.max(1, (int) Math.ceil(radius / 32.0)) : 2;
evictionWindowRegions = resolved;
return resolved;
} catch (Throwable ignored) {
} catch (Throwable e) {
PregenDiagnostics.probeFailed("mantle radius for eviction window", e);
return 2;
}
}
@@ -431,7 +434,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private void unloadChunkSafely(int cx, int cz) {
try {
world.removePluginChunkTicket(cx, cz, BukkitPlatform.plugin());
} catch (Throwable ignored) {
} catch (Throwable e) {
IrisLogging.reportError("Async pregen could not release the plugin chunk ticket at " + cx + "," + cz
+ " in world " + world.getName() + "; the chunk stays pinned in memory.", e);
}
try {
@@ -492,11 +497,12 @@ public class AsyncPregenMethod implements PregeneratorMethod {
}
private Chunk onChunkFutureFailure(int x, int z, Throwable throwable) {
try {
IrisLogging.warn("Failed async pregen chunk load at " + x + "," + z + ". " + metricsSnapshot());
IrisLogging.reportError("Failed async pregen chunk load at " + x + "," + z + ".", throwable);
IrisLogging.reportError(throwable);
try {
IrisLogging.warn("Async pregen state at the failed chunk " + x + "," + z + ". " + metricsSnapshot());
} catch (Throwable e) {
PregenDiagnostics.probeFailed("pregen metrics snapshot", e);
}
return null;
@@ -738,7 +744,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
if (engine != null) {
try {
engine.getMantle().cleanupChunksCoveredBy(x, z, true, listener::onChunkCleaned);
} catch (Throwable ignored) {
} catch (Throwable e) {
IrisLogging.reportError("Async pregen mantle cleanup failed at chunk " + x + "," + z
+ " in world " + world.getName() + "; tectonic plates for that chunk stay resident.", e);
}
}
}
@@ -759,7 +767,8 @@ public class AsyncPregenMethod implements PregeneratorMethod {
metricsEngine = resolvedEngine;
}
return resolvedEngine;
} catch (Throwable ignored) {
} catch (Throwable e) {
PregenDiagnostics.probeFailed("engine access for world " + world.getName(), e);
return null;
}
}
@@ -1094,7 +1103,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
requestMonitoredChunkAsync(x, z)
.whenComplete((chunk, throwable) -> completeChunk(x, z, listener, chunk, throwable));
return;
} catch (Throwable ignored) {
} catch (Throwable e) {
PregenDiagnostics.probeFailed("folia direct chunk request at " + x + "," + z
+ ", falling back to a region task", e);
}
Runnable regionTask = () -> {
@@ -20,6 +20,7 @@ package art.arcane.iris.core.pregenerator.methods;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.core.pregenerator.PregenDiagnostics;
import art.arcane.iris.core.pregenerator.PregenListener;
import art.arcane.iris.core.pregenerator.PregeneratorMethod;
import art.arcane.iris.core.tools.IrisToolbelt;
@@ -102,7 +103,8 @@ public class MedievalPregenMethod implements PregeneratorMethod {
if (cached != null) {
cachedEngine = cached;
}
} catch (Throwable ignored) {
} catch (Throwable e) {
PregenDiagnostics.probeFailed("engine access for world " + world.getName(), e);
}
return cached;
}
@@ -3,10 +3,15 @@ package art.arcane.iris.util.simd;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.core.IrisSettings;
import java.util.concurrent.atomic.AtomicReference;
public final class SimdSupport {
private static final String VECTOR_MODULE = "jdk.incubator.vector";
private static final boolean MODULE_PRESENT = ModuleLayer.boot().findModule(VECTOR_MODULE).isPresent();
private static final AtomicReference<Throwable> KERNEL_FAILURE = new AtomicReference<>();
private static final AtomicReference<Throwable> NOISE_KERNEL_FAILURE = new AtomicReference<>();
private static final SimdKernels KERNELS = selectKernels();
private static final NoiseKernels2D NOISE_KERNELS_2D = selectNoiseKernels2D();
private SimdSupport() {
}
@@ -15,6 +20,10 @@ public final class SimdSupport {
return KERNELS;
}
public static NoiseKernels2D noiseKernels2D() {
return NOISE_KERNELS_2D;
}
public static boolean isVectorModulePresent() {
return MODULE_PRESENT;
}
@@ -23,13 +32,75 @@ public final class SimdSupport {
return !(KERNELS instanceof ScalarSimdKernels);
}
public static Throwable kernelInitializationFailure() {
return KERNEL_FAILURE.get();
}
public static Throwable noiseKernelInitializationFailure() {
return NOISE_KERNEL_FAILURE.get();
}
public static String kernelStatus() {
return kernelStatus(isVectorized(), KERNELS.describe(), MODULE_PRESENT, KERNEL_FAILURE.get(), simdEnabledInSettings());
}
public static String noiseKernelStatus() {
return noiseKernelStatus(!(NOISE_KERNELS_2D instanceof ScalarNoiseKernels2D), NOISE_KERNELS_2D.describe(),
MODULE_PRESENT, NOISE_KERNEL_FAILURE.get(), simdEnabledInSettings());
}
static String kernelStatus(boolean vectorized, String description, boolean modulePresent, Throwable failure, boolean settingEnabled) {
if (vectorized) {
return "vector kernels enabled (" + description + ")";
}
if (!modulePresent) {
return "scalar kernels active; add --add-modules " + VECTOR_MODULE + " to JVM flags to enable vectorized generation kernels";
}
if (failure != null) {
return "scalar kernels active; vector kernel initialization failed: " + describe(failure);
}
if (!settingEnabled) {
return "vector kernels disabled (performance.simdKernels=false)";
}
return "scalar kernels active; the Vector API reported no usable vector shape on this CPU";
}
static String noiseKernelStatus(boolean vectorized, String description, boolean modulePresent, Throwable failure, boolean settingEnabled) {
if (vectorized) {
return "2D noise vector kernels enabled (" + description + ")";
}
if (!modulePresent) {
return "2D noise scalar kernels active; the " + VECTOR_MODULE + " module is not on this JVM";
}
if (failure != null) {
return "2D noise scalar kernels active; vector kernel initialization failed: " + describe(failure);
}
if (!settingEnabled) {
return "2D noise vector kernels disabled (performance.simdKernels=false)";
}
return "2D noise scalar kernels active; this CPU has fewer than the 4 double lanes the vector kernel needs to pay for itself";
}
public static void install() {
if (isVectorized()) {
IrisLogging.info("SIMD: vector kernels enabled (" + KERNELS.describe() + ")");
} else if (!MODULE_PRESENT) {
IrisLogging.info("SIMD: scalar kernels active; add --add-modules " + VECTOR_MODULE + " to JVM flags to enable vectorized generation kernels");
} else {
IrisLogging.info("SIMD: vector kernels disabled (performance.simdKernels=false)");
IrisLogging.info("SIMD: " + kernelStatus());
IrisLogging.info("SIMD: " + noiseKernelStatus());
Throwable failure = KERNEL_FAILURE.get();
if (failure != null) {
IrisLogging.reportError("Iris could not initialize its Vector API kernels; generation runs on the scalar kernels instead.", failure);
}
Throwable noiseFailure = NOISE_KERNEL_FAILURE.get();
if (noiseFailure != null) {
IrisLogging.reportError("Iris could not initialize its Vector API 2D noise kernels; noise runs on the scalar kernels instead.", noiseFailure);
}
}
@@ -41,6 +112,25 @@ public final class SimdSupport {
try {
return (SimdKernels) Class.forName("art.arcane.iris.util.simd.VectorSimdKernels").getDeclaredConstructor().newInstance();
} catch (Throwable e) {
KERNEL_FAILURE.set(e);
return null;
}
}
public static NoiseKernels2D createVectorNoiseKernels2D() {
if (!MODULE_PRESENT) {
return null;
}
try {
Class<?> cls = Class.forName("art.arcane.iris.util.simd.VectorNoiseKernels2D");
boolean profitable = (boolean) cls.getMethod("profitable").invoke(null);
if (!profitable) {
return null;
}
return (NoiseKernels2D) cls.getDeclaredConstructor().newInstance();
} catch (Throwable e) {
NOISE_KERNEL_FAILURE.set(e);
return null;
}
}
@@ -54,36 +144,25 @@ public final class SimdSupport {
return vector == null ? new ScalarSimdKernels() : vector;
}
private static final NoiseKernels2D NOISE_KERNELS_2D = selectNoiseKernels2D();
public static NoiseKernels2D noiseKernels2D() {
return NOISE_KERNELS_2D;
}
public static NoiseKernels2D createVectorNoiseKernels2D() {
if (!MODULE_PRESENT) {
return null;
}
try {
Class<?> cls = Class.forName("art.arcane.iris.util.simd.VectorNoiseKernels2D");
boolean profitable = (boolean) cls.getMethod("profitable").invoke(null);
if (!profitable) {
return null;
}
return (NoiseKernels2D) cls.getDeclaredConstructor().newInstance();
} catch (Throwable e) {
return null;
}
}
private static NoiseKernels2D selectNoiseKernels2D() {
if (!simdEnabledInSettings()) {
return new ScalarNoiseKernels2D();
}
NoiseKernels2D vector = createVectorNoiseKernels2D();
return vector == null ? new ScalarNoiseKernels2D() : vector;
}
private static String describe(Throwable failure) {
Throwable root = failure;
while (root.getCause() != null && root.getCause() != root) {
root = root.getCause();
}
String message = root.getMessage();
return message == null ? root.getClass().getName() : root.getClass().getName() + ": " + message;
}
private static boolean simdEnabledInSettings() {
try {
return IrisSettings.get().getPerformance().isSimdKernels();