/* * Copyright (c) 2023, 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. */ /* * @test * @summary Tests handling of an exception thrown by HotSpotJVMCIRuntime.compileMethod. * Requires a debug VM as it uses test.jvmci.compileMethodExceptionIsFatal * which is only read in a debug VM. * @requires vm.jvmci * @requires vm.debug * @library /test/lib / * @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot * jdk.internal.vm.ci/jdk.vm.ci.code * jdk.internal.vm.ci/jdk.vm.ci.meta * jdk.internal.vm.ci/jdk.vm.ci.runtime * jdk.internal.vm.ci/jdk.vm.ci.services * @run driver jdk.test.lib.FileInstaller ./TestUncaughtErrorInCompileMethod.config * ./META-INF/services/jdk.vm.ci.services.JVMCIServiceLocator * @run driver compiler.jvmci.TestUncaughtErrorInCompileMethod */ package compiler.jvmci; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import jdk.vm.ci.services.JVMCIServiceLocator; import jdk.vm.ci.runtime.JVMCICompiler; import jdk.vm.ci.runtime.JVMCICompilerFactory; import jdk.vm.ci.runtime.JVMCIRuntime; import java.io.File; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestUncaughtErrorInCompileMethod extends JVMCIServiceLocator { static volatile boolean compilerCreationErrorOccurred; /** * @param args if args.length != 0, then executing in subprocess */ public static void main(String[] args) throws Exception { if (args.length == 0) { testSubprocess(false); testSubprocess(true); } else { int total = 0; while (!compilerCreationErrorOccurred) { // Do some random work to trigger compilation total += getTime(); total += String.valueOf(total).hashCode(); } System.out.println(total); } } private static long getTime() { return System.currentTimeMillis(); } static void testSubprocess(boolean fatalError) throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:+UnlockExperimentalVMOptions", "-XX:+UseJVMCICompiler", "-Djvmci.Compiler=ErrorCompiler", "-XX:-TieredCompilation", "-XX:+PrintCompilation", "--add-exports=jdk.internal.vm.ci/jdk.vm.ci.services=ALL-UNNAMED", "-Dtest.jvmci.compileMethodExceptionIsFatal=" + (fatalError ? "true" : "false"), "-XX:+PrintWarnings", "-Xbootclasspath/a:.", TestUncaughtErrorInCompileMethod.class.getName(), "true"); Process p = pb.start(); OutputAnalyzer output = new OutputAnalyzer(p); if (!waitForProcess(p)) { // The subprocess might not enter JVMCI compilation. // Print the subprocess output and pass the test in this case. System.out.println(output.getOutput()); return; } if (fatalError) { output.shouldContain("testing JVMCI fatal exception handling"); output.shouldNotHaveExitValue(0); File hs_err_file = openHsErrFileFromOutput(output); Path hsErrPath = hs_err_file.toPath(); if (!Files.exists(hsErrPath)) { throw new RuntimeException("hs_err_pid file missing at " + hsErrPath); } String hsErr = Files.readString(hsErrPath); /* * JVMCI Events (11 events): * ... * Event: 0.274 Thread 0x0000000146819210 compiler.jvmci.TestUncaughtErrorInCompileMethod$CompilerCreationError * Event: 0.274 Thread 0x0000000146819210 at compiler.jvmci.TestUncaughtErrorInCompileMethod$1.createCompiler(TestUncaughtErrorInCompileMethod.java:147) * Event: 0.274 Thread 0x0000000146819210 at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.getCompiler(HotSpotJVMCIRuntime.java:829) * Event: 0.274 Thread 0x0000000146819210 at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.compileMethod(HotSpotJVMCIRuntime.java:943) */ // Check that hs-err contains the stack trace of the fatal exception (sample shown above) String[] stackTraceSubstrings = { "at compiler.jvmci.TestUncaughtErrorInCompileMethod$1.createCompiler(TestUncaughtErrorInCompileMethod.java", "at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.compileMethod(HotSpotJVMCIRuntime.java" }; for (String expect : stackTraceSubstrings) { if (!hsErr.contains(expect)) { throw new RuntimeException("Could not find \"" + expect + "\" in " + hsErrPath); } } } else { output.shouldContain("COMPILE SKIPPED: uncaught exception in call_HotSpotJVMCIRuntime_compileMethod [compiler.jvmci.TestUncaughtErrorInCompileMethod$CompilerCreationError"); output.shouldHaveExitValue(0); } } /** * @return true if {@code p} exited on its own, false if it had to be destroyed */ private static boolean waitForProcess(Process p) { while (true) { try { boolean exited = p.waitFor(10, TimeUnit.SECONDS); if (!exited) { System.out.println("destroying process: " + p); p.destroy(); Thread.sleep(1000); while (p.isAlive()) { System.out.println("forcibly destroying process: " + p); Thread.sleep(1000); p.destroyForcibly(); } return false; } return true; } catch (InterruptedException e) { e.printStackTrace(System.out); } } } public TestUncaughtErrorInCompileMethod() { } static class CompilerCreationError extends InternalError { CompilerCreationError(int attempt) { super("attempt " + attempt); } } @Override public S getProvider(Class service) { if (service == JVMCICompilerFactory.class) { return service.cast(new JVMCICompilerFactory() { final AtomicInteger counter = new AtomicInteger(); @Override public String getCompilerName() { return "ErrorCompiler"; } @Override public JVMCICompiler createCompiler(JVMCIRuntime runtime) { int attempt = counter.incrementAndGet(); CompilerCreationError e = new CompilerCreationError(attempt); e.printStackTrace(); if (attempt >= 10) { // Delay notifying the loop in main so that compilation failures // have time to be reported by -XX:+PrintCompilation. compilerCreationErrorOccurred = true; } throw e; } }); } return null; } /** * Given the output of a java VM that crashed, extract the name of the hs-err file from the output */ public static String extractHsErrFileNameFromOutput(OutputAnalyzer output) { output.shouldMatch("# A fatal error has been detected.*"); // extract hs-err file String hs_err_file = output.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1); if (hs_err_file == null) { throw new RuntimeException("Did not find hs-err file in output.\n"); } return hs_err_file; } /** * Given the output of a java VM that crashed, extract the name of the hs-err file from the output, * open that file and return its File. */ public static File openHsErrFileFromOutput(OutputAnalyzer output) { String name = extractHsErrFileNameFromOutput(output); File f = new File(name); if (!f.exists()) { throw new RuntimeException("Cannot find hs-err file at " + f.getAbsolutePath()); } return f; } }