d52a995f35
Reviewed-by: lmesnik, dholmes, rriggs, stefank
230 lines
9.1 KiB
Java
230 lines
9.1 KiB
Java
/*
|
|
* 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> S getProvider(Class<S> 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;
|
|
}
|
|
}
|