From 7ece9e90c0198f92cdf8d620e346c4a9832724cd Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Wed, 13 Dec 2023 17:34:37 +0000 Subject: [PATCH] 8321400: java/foreign/TestStubAllocFailure.java fails with code cache exhaustion Reviewed-by: mcimadamore --- .../java/foreign/TestAddressDereference.java | 4 +- .../java/foreign/TestStubAllocFailure.java | 47 +++++++++++++---- .../jdk/java/foreign/TestUpcallException.java | 4 +- test/jdk/java/foreign/UpcallTestHelper.java | 51 +++---------------- .../foreign/critical/TestCriticalUpcall.java | 4 +- .../passheapsegment/TestPassHeapSegment.java | 4 +- .../jdk/test/lib/process/OutputAnalyzer.java | 9 ++++ 7 files changed, 64 insertions(+), 59 deletions(-) diff --git a/test/jdk/java/foreign/TestAddressDereference.java b/test/jdk/java/foreign/TestAddressDereference.java index 3ebec596263..54477bafe13 100644 --- a/test/jdk/java/foreign/TestAddressDereference.java +++ b/test/jdk/java/foreign/TestAddressDereference.java @@ -128,8 +128,8 @@ public class TestAddressDereference extends UpcallTestHelper { if (!badAlign) return; runInNewProcess(UpcallTestRunner.class, true, new String[] {Long.toString(alignment), layout.toString() }) - .assertFailed() - .assertStdErrContains("alignment constraint for address"); + .shouldNotHaveExitValue(0) + .stderrShouldContain("alignment constraint for address"); } public static class UpcallTestRunner { diff --git a/test/jdk/java/foreign/TestStubAllocFailure.java b/test/jdk/java/foreign/TestStubAllocFailure.java index d3a1ef6e3de..b20b3e88ea2 100644 --- a/test/jdk/java/foreign/TestStubAllocFailure.java +++ b/test/jdk/java/foreign/TestStubAllocFailure.java @@ -31,12 +31,15 @@ */ import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.io.IOException; import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Stream; import org.testng.annotations.Test; -import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; public class TestStubAllocFailure extends UpcallTestHelper { @@ -44,18 +47,23 @@ public class TestStubAllocFailure extends UpcallTestHelper { @Test public void testUpcallAllocFailure() throws IOException, InterruptedException { runInNewProcess(UpcallRunner.class, true, List.of("-XX:ReservedCodeCacheSize=3M"), List.of()) - .assertSuccess(); + .shouldNotHaveExitValue(0) + .shouldNotHaveFatalError(); + } + + @Test + public void testUDowncallAllocFailure() throws IOException, InterruptedException { + runInNewProcess(DowncallRunner.class, true, List.of("-XX:ReservedCodeCacheSize=3M"), List.of()) + .shouldNotHaveExitValue(0) + .shouldNotHaveFatalError(); } public static class UpcallRunner extends NativeTestHelper { public static void main(String[] args) throws Throwable { - try (Arena arena = Arena.ofConfined()) { - while (true) { - // allocate stubs until we crash - upcallStub(UpcallRunner.class, "target", FunctionDescriptor.ofVoid(), arena); - } - } catch (OutOfMemoryError e) { - assertTrue(e.getMessage().contains("Failed to allocate upcall stub")); + FunctionDescriptor descriptor = FunctionDescriptor.ofVoid(); + MethodHandle target = MethodHandles.lookup().findStatic(UpcallRunner.class, "target", descriptor.toMethodType()); + while (true) { + LINKER.upcallStub(target, descriptor, Arena.ofAuto()); } } @@ -63,4 +71,25 @@ public class TestStubAllocFailure extends UpcallTestHelper { fail("Should not get here"); } } + + public static class DowncallRunner extends NativeTestHelper { + + private static final int MAX_ARITY = 5; + + private static void mapper(FunctionDescriptor fd, Consumer sink) { + for (MemoryLayout l : List.of(C_INT, C_LONG_LONG, C_DOUBLE, C_FLOAT, C_SHORT)) { + sink.accept(fd.appendArgumentLayouts(l)); + } + } + + public static void main(String[] args) throws Throwable { + Linker linker = Linker.nativeLinker(); + Stream stream = Stream.of(FunctionDescriptor.ofVoid()); + for (int i = 0; i < MAX_ARITY; i++) { + stream = stream.mapMulti(DowncallRunner::mapper); + } + + stream.forEach(linker::downcallHandle); + } + } } diff --git a/test/jdk/java/foreign/TestUpcallException.java b/test/jdk/java/foreign/TestUpcallException.java index f7dca7bc19c..beaa33f5e61 100644 --- a/test/jdk/java/foreign/TestUpcallException.java +++ b/test/jdk/java/foreign/TestUpcallException.java @@ -48,8 +48,8 @@ public class TestUpcallException extends UpcallTestHelper { @Test(dataProvider = "exceptionCases") public void testException(Class target, boolean useSpec) throws InterruptedException, IOException { runInNewProcess(target, useSpec) - .assertFailed() - .assertStdErrContains("Testing upcall exceptions"); + .shouldNotHaveExitValue(0) + .stderrShouldContain("Testing upcall exceptions"); } @DataProvider diff --git a/test/jdk/java/foreign/UpcallTestHelper.java b/test/jdk/java/foreign/UpcallTestHelper.java index e24c35e498c..bbc01959ccd 100644 --- a/test/jdk/java/foreign/UpcallTestHelper.java +++ b/test/jdk/java/foreign/UpcallTestHelper.java @@ -21,54 +21,24 @@ * questions. */ +import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.Utils; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertTrue; public class UpcallTestHelper extends NativeTestHelper { - public record Output(int result, List stdout, List stderr) { - private static void assertContains(List lines, String shouldInclude, String name) { - assertTrue(lines.stream().anyMatch(line -> line.contains(shouldInclude)), - "Did not find '" + shouldInclude + "' in " + name); - } - public Output assertFailed() { - assertNotEquals(result, 0); - return this; - } - - public Output assertSuccess() { - assertEquals(result, 0); - return this; - } - - public Output assertStdErrContains(String shouldInclude) { - assertContains(stderr, shouldInclude, "stderr"); - return this; - } - - public Output assertStdOutContains(String shouldInclude) { - assertContains(stdout, shouldInclude, "stdout"); - return this; - } - } - - public Output runInNewProcess(Class target, boolean useSpec, String... programArgs) throws IOException, InterruptedException { + public OutputAnalyzer runInNewProcess(Class target, boolean useSpec, String... programArgs) throws IOException, InterruptedException { return runInNewProcess(target, useSpec, List.of(), List.of(programArgs)); } - public Output runInNewProcess(Class target, boolean useSpec, List vmArgs, List programArgs) throws IOException, InterruptedException { + public OutputAnalyzer runInNewProcess(Class target, boolean useSpec, List vmArgs, List programArgs) throws IOException, InterruptedException { assert !target.isArray(); List command = new ArrayList<>(List.of( @@ -86,17 +56,10 @@ public class UpcallTestHelper extends NativeTestHelper { boolean completed = process.waitFor(timeOut, TimeUnit.MINUTES); assertTrue(completed, "Time out while waiting for process"); - List outLines = linesFromStream(process.getInputStream()); - outLines.forEach(System.out::println); - List errLines = linesFromStream(process.getErrorStream()); - errLines.forEach(System.err::println); + OutputAnalyzer output = new OutputAnalyzer(process); + output.outputTo(System.out); + output.errorTo(System.err); - return new Output(process.exitValue(), outLines, errLines); - } - - private static List linesFromStream(InputStream stream) throws IOException { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { - return reader.lines().toList(); - } + return output; } } diff --git a/test/jdk/java/foreign/critical/TestCriticalUpcall.java b/test/jdk/java/foreign/critical/TestCriticalUpcall.java index c2f2d5572c1..862c008b080 100644 --- a/test/jdk/java/foreign/critical/TestCriticalUpcall.java +++ b/test/jdk/java/foreign/critical/TestCriticalUpcall.java @@ -43,7 +43,9 @@ public class TestCriticalUpcall extends UpcallTestHelper { @Test public void testUpcallFailure() throws IOException, InterruptedException { // test to see if we catch a trivial downcall doing an upcall - runInNewProcess(Runner.class, true).assertFailed().assertStdOutContains("wrong thread state for upcall"); + runInNewProcess(Runner.class, true) + .shouldNotHaveExitValue(0) + .stdoutShouldContain("wrong thread state for upcall"); } public static class Runner extends NativeTestHelper { diff --git a/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java index 5f97dbf288d..fadcdf1ba24 100644 --- a/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java +++ b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java @@ -53,7 +53,9 @@ public class TestPassHeapSegment extends UpcallTestHelper { @Test(dataProvider = "specs") public void testNoHeapReturns(boolean spec) throws IOException, InterruptedException { - runInNewProcess(Runner.class, spec).assertFailed().assertStdErrContains("Heap segment not allowed"); + runInNewProcess(Runner.class, spec) + .shouldNotHaveExitValue(0) + .stderrShouldContain("Heap segment not allowed"); } public static class Runner { diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index ae04d9d1bc4..e316248fe8e 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java @@ -42,6 +42,8 @@ public final class OutputAnalyzer { private static final String deprecatedmsg = ".* VM warning:.* deprecated.*"; + private static final String FATAL_ERROR_PAT = "# A fatal error has been detected.*"; + private final OutputBuffer buffer; /** * Create an OutputAnalyzer, a utility class for verifying output and exit @@ -862,4 +864,11 @@ public final class OutputAnalyzer { shouldContainMultiLinePattern(needles, true); } + /** + * Assert that we did not crash with a hard VM error (generating an hs_err_pidXXX.log) + */ + public void shouldNotHaveFatalError() { + shouldNotMatch(FATAL_ERROR_PAT); + } + }