Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests graalvm-community-jdk21u-issues-28 #306

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions apps/jdkreflections/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jdkreflections</groupId>
<artifactId>jdkreflections</artifactId>
<version>1</version>

<name>jdkreflections</name>

<parent>
<groupId>org.graalvm.tests.integration</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<properties>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
</properties>

<build>
<finalName>jdkreflections</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifest>
<mainClass>jdkreflections.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
88 changes: 88 additions & 0 deletions apps/jdkreflections/src/main/java/jdkreflections/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2024, Red Hat Inc. All rights reserved.
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package jdkreflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class Main {

/**
* An intentionally elaborate way to do this to test the reflection support
* on java.lang.Thread for native-image build configuration.
* @param name
* @return
*/
static ExecutorService createVirtualThreadExecutor(String name) {
try {
final Method virtualThreadBuilderMethod = Arrays.stream(Thread.class.getMethods())
.filter(m -> m.getName().equals("ofVirtual"))
.findAny().orElseThrow();
Object virtualThreadBuilder = virtualThreadBuilderMethod.invoke(null);
final Method nameVirtualThreadBuilderMethod = Arrays.stream(virtualThreadBuilderMethod.getReturnType().getMethods())
.filter(m -> m.getName().equals("name") && m.getParameterCount() == 2)
.findAny().orElseThrow();
virtualThreadBuilder = nameVirtualThreadBuilderMethod.invoke(virtualThreadBuilder, name, 10000L);
final Method factoryVirtualThreadBuilderMethod = Arrays.stream(Class.forName("java.lang.Thread$Builder").getMethods())
.filter(m -> m.getName().equals("factory"))
.findAny().orElseThrow();
final ThreadFactory factory = (ThreadFactory) factoryVirtualThreadBuilderMethod.invoke(virtualThreadBuilder);
final Method newThreadPerTaskExecutorMethod = Arrays.stream(Executors.class.getMethods())
.filter(m -> m.getName().equals("newThreadPerTaskExecutor") && m.getGenericParameterTypes()[0].equals(ThreadFactory.class))
.findAny().orElseThrow();
return (ExecutorService) newThreadPerTaskExecutorMethod.invoke(null, factory);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Fail :-)", e);
}
}

public static void main(String[] args) throws InterruptedException {
final ExecutorService executor = createVirtualThreadExecutor("meh-");
executor.submit(() -> {
try {
final Method currentThreadMethod = Thread.class.getDeclaredMethod("currentThread");
final Thread currentThread = (Thread) currentThreadMethod.invoke(null);
final Method isVirtualMethod = Thread.class.getDeclaredMethod("isVirtual");
final boolean isVirtual = (boolean) isVirtualMethod.invoke(currentThread);
System.out.println("Hello from a " + (isVirtual ? "virtual" : "") + " thread called " + currentThread.getName());
final Field interruptedField = Thread.class.getDeclaredField("interrupted");
interruptedField.setAccessible(true);
System.out.println("interrupted: " + interruptedField.getBoolean(currentThread));
final Method holdsLockMethod = Arrays.stream(Thread.class.getMethods())
.filter(m -> m.getName().equals("holdsLock") && m.getGenericParameterTypes()[0].equals(Object.class))
.findAny().orElseThrow();
System.out.println("holdsLock: " + ((boolean) holdsLockMethod.invoke(null, new Object())));
final Method threadIdMethod = Thread.class.getDeclaredMethod("threadId");
System.out.println("getId: " + ((long) threadIdMethod.invoke(currentThread)));
final Method getNextThreadIdOffsetMethod = Thread.class.getDeclaredMethod("getNextThreadIdOffset");
getNextThreadIdOffsetMethod.setAccessible(true);
System.out.println("getNextThreadIdOffset: " + (long) getNextThreadIdOffsetMethod.invoke(null));
} catch (Exception e) {
e.printStackTrace();
}
});
executor.shutdown();
executor.awaitTermination(1, java.util.concurrent.TimeUnit.SECONDS);
}
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<module>apps/debug-symbols-smoke</module>
<module>apps/helidon-quickstart-se</module>
<module>apps/imageio</module>
<module>apps/jdkreflections</module>
<module>apps/jfr-native-image-performance</module>
<module>apps/quarkus-full-microprofile</module>
<module>apps/quarkus-json</module>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,63 @@ public void monitorFieldOffsetNOK(TestInfo testInfo, Apps app) throws IOExceptio
}
}

@Test
@Tag("builder-image")
@IfMandrelVersion(minJDK = "21.0.3", inContainer = true)
Karm marked this conversation as resolved.
Show resolved Hide resolved
public void jdkReflectionsContainerTest(TestInfo testInfo) throws IOException, InterruptedException {
jdkReflections(testInfo, Apps.JDK_REFLECTIONS_BUILDER_IMAGE);
}

@Test
@IfMandrelVersion(minJDK = "21.0.3")
Karm marked this conversation as resolved.
Show resolved Hide resolved
public void jdkReflectionsTest(TestInfo testInfo) throws IOException, InterruptedException {
jdkReflections(testInfo, Apps.JDK_REFLECTIONS);
}

public void jdkReflections(TestInfo testInfo, Apps app) throws IOException, InterruptedException {
LOGGER.info("Testing app: " + app);
Process process = null;
File buildLog = null;
File runLog = null;
final StringBuilder report = new StringBuilder();
final File appDir = Path.of(BASE_DIR, app.dir).toFile();
final String cn = testInfo.getTestClass().get().getCanonicalName();
final String mn = testInfo.getTestMethod().get().getName();
final boolean inContainer = app.runtimeContainer != ContainerNames.NONE;
try {
// Cleanup
cleanTarget(app);
if (inContainer) {
removeContainers(app.runtimeContainer.name);
}
Files.createDirectories(Paths.get(appDir.getAbsolutePath() + File.separator + "logs"));
buildLog = Path.of(appDir.getAbsolutePath(), "logs", "build.log").toFile();
runLog = Path.of(appDir.getAbsolutePath(), "logs", "run.log").toFile();
builderRoutine(app, report, cn, mn, appDir, buildLog);
LOGGER.info("Running...");
final List<String> cmd = getRunCommand(app.buildAndRunCmds.runCommands[0]);
process = runCommand(cmd, appDir, runLog, app);
assertNotNull(process, "The test application failed to run. Check " + getLogsDir(cn, mn) + File.separator + buildLog.getName() +
" and also check https://github.com/graalvm/graalvm-community-jdk21u/issues/28");
process.waitFor(5, TimeUnit.SECONDS);
Logs.appendln(report, appDir.getAbsolutePath());
Logs.appendlnSection(report, String.join(" ", cmd));
Logs.checkLog(cn, mn, app, buildLog);
Logs.checkLog(cn, mn, app, runLog);
processStopper(process, true);
final Pattern p = Pattern.compile(".*Hello from a virtual thread called meh-10000.*");
assertTrue(searchLogLines(p, runLog, Charset.defaultCharset()),
"Expected pattern " + p + " was not found in the log. Check " + getLogsDir(cn, mn) + File.separator + runLog.getName() +
" and also check https://github.com/graalvm/graalvm-community-jdk21u/issues/28");
final Pattern p1 = Pattern.compile(".*java.lang.NoSuchMethodException: java.lang.Thread.getNextThreadIdOffset.*");
assertTrue(searchLogLines(p, runLog, Charset.defaultCharset()),
"Expected pattern " + p1 + " was not found in the log. Check " + getLogsDir(cn, mn) + File.separator + runLog.getName() +
". The method getNextThreadIdOffset is deleted from native-image intentionally.");
} finally {
cleanup(process, cn, mn, report, app, buildLog, runLog);
}
}

@Test
@Tag("builder-image")
@IfMandrelVersion(min = "23.1.5", max = "23.1.999", inContainer = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,17 @@ public enum Apps {
URLContent.NONE,
WhitelistLogLines.FOR_SERIALIZATION,
BuildAndRunCmds.FOR_SERIALIZATION_BUILDER_IMAGE,
ContainerNames.FOR_SERIALIZATION_BUILDER_IMAGE);
ContainerNames.FOR_SERIALIZATION_BUILDER_IMAGE),
JDK_REFLECTIONS("apps" + File.separator + "jdkreflections",
URLContent.NONE,
WhitelistLogLines.JDK_REFLECTIONS,
BuildAndRunCmds.JDK_REFLECTIONS,
ContainerNames.NONE),
JDK_REFLECTIONS_BUILDER_IMAGE("apps" + File.separator + "jdkreflections",
URLContent.NONE,
WhitelistLogLines.JDK_REFLECTIONS,
BuildAndRunCmds.JDK_REFLECTIONS_BUILDER_IMAGE,
ContainerNames.JDK_REFLECTIONS_BUILDER_IMAGE);

public final String dir;
public final URLContent urlContent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,32 @@ public enum BuildAndRunCmds {
new String[][] {
{ IS_THIS_WINDOWS ? "target\\timezones.exe" : "./target/timezones" } }
),
JDK_REFLECTIONS(
new String[][] {
{ "mvn", "package" },
{ "java", "--add-opens=java.base/java.lang=ALL-UNNAMED", "-agentlib:native-image-agent=config-output-dir=./target/AGENT",
"-cp", "target/jdkreflections.jar", "jdkreflections.Main" },
{ "native-image", "-J--add-opens=java.base/java.lang=ALL-UNNAMED", "-H:ConfigurationFileDirectories=./target/AGENT",
"--link-at-build-time=", "--no-fallback", "-march=native", "-jar", "target/jdkreflections.jar", "target/jdkreflections" }
},
new String[][] {
{ IS_THIS_WINDOWS ? "target\\jdkreflections.exe" : "./target/jdkreflections" } }
),
JDK_REFLECTIONS_BUILDER_IMAGE(
new String[][] {
{ "mvn", "package" },
{ CONTAINER_RUNTIME, "run", IS_THIS_WINDOWS ? "" : "-u", IS_THIS_WINDOWS ? "" : getUnixUIDGID(),
"-t", "--entrypoint", "java", "-v", BASE_DIR + File.separator + "apps" + File.separator + "jdkreflections:/project:z",
BUILDER_IMAGE, "--add-opens=java.base/java.lang=ALL-UNNAMED",
"-agentlib:native-image-agent=config-output-dir=./target/AGENT", "-cp", "target/jdkreflections.jar", "jdkreflections.Main" },
{ CONTAINER_RUNTIME, "run", IS_THIS_WINDOWS ? "" : "-u", IS_THIS_WINDOWS ? "" : getUnixUIDGID(),
"-v", BASE_DIR + File.separator + "apps" + File.separator + "jdkreflections:/project:z",
BUILDER_IMAGE, "-J--add-opens=java.base/java.lang=ALL-UNNAMED",
"-H:ConfigurationFileDirectories=./target/AGENT", "--link-at-build-time=", "--no-fallback", "-march=native",
"-jar", "target/jdkreflections.jar", "target/jdkreflections" } },
new String[][] {
{ IS_THIS_WINDOWS ? "target\\jdkreflections.exe" : "./target/jdkreflections" } }
),
CALENDARS(
new String[][] {
{ "mvn", "package" },
Expand Down Expand Up @@ -525,7 +551,7 @@ private static String[] hyperfoil() {
}
}

final String[][] buildCommands;
public final String[][] buildCommands;
public final String[][] runCommands;

BuildAndRunCmds(String[][] buildCommands, String[][] runCommands)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ public static Process runCommand(List<String> command, File directory, File logF
if (logFile != null) {
final String c = "Command: " + String.join(" ", command) + "\n";
LOGGER.infof("Command: %s", command);
Files.write(logFile.toPath(), c.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
Files.write(logFile.toPath(), c.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile));
}
if (input != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public enum ContainerNames {
HYPERFOIL("hyperfoil-container"),
MONITOR_OFFSET_BUILDER_IMAGE("my-monitor-offset-runner"),
FOR_SERIALIZATION_BUILDER_IMAGE("my-for-serialization-runner"),
JDK_REFLECTIONS_BUILDER_IMAGE("my-jdkreflections-runner"),
NONE("NO_CONTAINER");

public final String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,13 @@ public Pattern[] get(boolean inContainer) {
Pattern.compile(".*sun.reflect.ReflectionFactory is internal proprietary API.*")
};
}
},
JDK_REFLECTIONS {
@Override
public Pattern[] get(boolean inContainer) {
return new Pattern[]{
};
Karm marked this conversation as resolved.
Show resolved Hide resolved
}
};

public abstract Pattern[] get(boolean inContainer);
Expand Down