undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,61 @@
# Javac microbenchmarks
The Javac Microbenchmarks is a collection of microbenchmarks for measuring
the performance of Javac API using the
[JMH](http://openjdk.java.net/projects/code-tools/jmh/) framework.
## Building and running the project
Currently, the project can be built and run with JDK 9 and later. This is
a Maven project and is built by:
$ mvn clean install
After building, the executable jar is target/micros-javac-[version].jar.
Run the benchmarks with:
$ java -jar target/micros-javac-*.jar [optional jmh parameters]
See the entire list of benchmarks using:
$ java -jar target/micros-javacs-*.jar -l [optional regex to select benchmarks]
For example:
$ java -jar target/micros-javac-1.0-SNAPSHOT.jar -l
Benchmarks:
org.openjdk.bench.langtools.javac.GroupJavacBenchmark.coldGroup
org.openjdk.bench.langtools.javac.GroupJavacBenchmark.hotGroup
org.openjdk.bench.langtools.javac.SingleJavacBenchmark.compileCold
org.openjdk.bench.langtools.javac.SingleJavacBenchmark.compileHot
And the same regex syntax works to run some test:
$ java -jar target/micros-javac-1.0-SNAPSHOT.jar SingleJavacBenchmark.compileHot
## Troubleshooting
### Build of micros-javac module got stuck
If you build got stuck on `[get] Getting: https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip` then you are probably experiencing some networking or web proxy obstacles.
One solution is to download required reference JDK from [https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip](https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip) manually and then build the project with property pointing to the local copy:
$ mvn clean install -Djavac.benchmark.openjdk.zip.download.url=file:///<your download location>/openjdk-11+28_windows-x64_bin.zip
Note: Please use `openjdk-11+28_windows-x64_bin.zip` to build the project no matter what target platform is.
Another solution might be to add proxy settings:
$ mvn -Dhttps.proxyHost=... -Dhttps.proxyPort=... clean install
### Execution of micros-javac benchmarks takes several hours
micros-javac benchmarks consist of two sets of benchmarks:
* `SingleJavacBenchmark` (which is parametrized) measures each single javac compilation stage in an isolated run. This benchmark is designed for exact automated performance regression testing and it takes several hours to execute completely.
* `GroupJavacBenchmark` is grouping the measurements of all javac compilation stages into one run and its execution should take less than 30 minutes on a regular developers computer.
Solution to speed up javac benchmarking is to select only `GroupJavacBenchmark` for execution using following command line:
$ java -jar target/micros-javac-1.0-SNAPSHOT.jar .*GroupJavacBenchmark.*

View file

@ -0,0 +1,145 @@
<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/maven-v4_0_0.xsd">
<!--
Copyright (c) 2020, 2025, 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.
-->
<modelVersion>4.0.0</modelVersion>
<groupId>org.openjdk</groupId>
<artifactId>micros-javac</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>OpenJDK Microbenchmark of Java Compile</name>
<properties>
<!--
the code below is being compiled with source 25, see JDK-8372023,
if the source version being compiled changes to 26 or 26+, then
some adjustments will be needed at:
test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java
-->
<javac.benchmark.openjdk.zip.download.url>https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip</javac.benchmark.openjdk.zip.download.url>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jmh.version>1.36</jmh.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/BenchmarkList</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/CompilerHints</resource>
</transformer>
</transformers>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.4</version>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>process-resources</phase>
<configuration>
<target>
<mkdir dir="${project.build.outputDirectory}"/>
<get src="${javac.benchmark.openjdk.zip.download.url}" dest="${project.build.directory}/jdk-bin.zip" skipexisting="true" verbose="true"/>
<unzip src="${project.build.directory}/jdk-bin.zip" dest="${project.build.outputDirectory}">
<patternset>
<include name="*/lib/src.zip"/>
<include name="*/release"/>
</patternset>
<mapper type="flatten"/>
</unzip>
<loadfile srcFile="${project.build.outputDirectory}/release" property="release.info"/>
<echo>
-------------------------------------------------
Bundling JDK sources with following release info:
-------------------------------------------------
${release.info}
-------------------------------------------------
</echo>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,231 @@
/*
* Copyright (c) 2020, 2025, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package org.openjdk.bench.langtools.javac;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Group;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
@State(Scope.Benchmark)
public class GroupJavacBenchmark extends JavacBenchmark {
public static final String COLD_GROUP_NAME = "coldGroup";
public static final int COLD_ITERATION_WARMUPS = 0;
public static final int COLD_ITERATIONS = 1;
public static final int COLD_FORK_WARMUPS = 1;
public static final int COLD_FORKS = 15;
public static final String HOT_GROUP_NAME = "hotGroup";
public static final int HOT_ITERATION_WARMUPS = 8;
public static final int HOT_ITERATIONS = 10;
public static final int HOT_FORK_WARMUPS = 0;
public static final int HOT_FORKS = 1;
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold1_Init() throws InterruptedException {
Stage.Init.waitFor();
}
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold2_Parse() throws InterruptedException {
Stage.Parse.waitFor();
}
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold3_InitModules() throws InterruptedException {
Stage.InitModules.waitFor();
}
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold4_Enter() throws InterruptedException {
Stage.Enter.waitFor();
}
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold5_Attribute() throws InterruptedException {
Stage.Attribute.waitFor();
}
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold6_Flow() throws InterruptedException {
Stage.Flow.waitFor();
}
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold7_Desugar() throws InterruptedException {
Stage.Desugar.waitFor();
}
@Benchmark
@Group(COLD_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = COLD_ITERATION_WARMUPS)
@Measurement(iterations = COLD_ITERATIONS)
@Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void cold8_Generate(Blackhole bh) throws IOException {
compile(bh, Stage.Generate);
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot1_Init() throws InterruptedException {
Stage.Init.waitFor();
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot2_Parse() throws InterruptedException {
Stage.Parse.waitFor();
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot3_InitModules() throws InterruptedException {
Stage.InitModules.waitFor();
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot4_Enter() throws InterruptedException {
Stage.Enter.waitFor();
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot5_Attribute() throws InterruptedException {
Stage.Attribute.waitFor();
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot6_Flow() throws InterruptedException {
Stage.Flow.waitFor();
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot7_Desugar() throws InterruptedException {
Stage.Desugar.waitFor();
}
@Benchmark
@Group(HOT_GROUP_NAME)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = HOT_ITERATION_WARMUPS)
@Measurement(iterations = HOT_ITERATIONS)
@Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void hot8_Generate(Blackhole bh) throws IOException {
compile(bh, Stage.Generate);
}
}

View file

@ -0,0 +1,198 @@
/*
* Copyright (c) 2020, 2025, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package org.openjdk.bench.langtools.javac;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.main.Main;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Context.Factory;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Pair;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Queue;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.CONFIG;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.infra.Blackhole;
@State(Scope.Benchmark)
public class JavacBenchmark {
static final Logger LOG = Logger.getLogger(JavacBenchmark.class.getName());
public enum Stage {
Init, Parse, InitModules, Enter, Attribute, Flow, Desugar, Generate;
public synchronized void waitFor() throws InterruptedException {
wait();
}
public synchronized void notifyDone() {
notifyAll();
LOG.log(FINE, "{0} finished.", this.name());
}
public boolean isAfter(Stage other) {
return ordinal() > other.ordinal();
}
}
private Path root;
private Path srcList;
@Setup(Level.Trial)
public void setup(Blackhole bh) throws IOException, InterruptedException {
LOG.log(CONFIG, "Release info of the sources to be compiled by the benchmark:\n{0}", new String(JavacBenchmark.class.getResourceAsStream("/release").readAllBytes(), StandardCharsets.UTF_8));
root = Files.createTempDirectory("JavacBenchmarkRoot");
srcList = root.resolve("sources.list");
int i = 0;
try (PrintStream srcListOut = new PrintStream(srcList.toFile())) {
try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(JavacBenchmark.class.getResourceAsStream("/src.zip")))) {
for (ZipEntry entry; (entry = zis.getNextEntry()) != null;) {
final String ename = entry.getName();
if (!ename.startsWith("java.desktop") && !ename.startsWith("jdk.internal.vm.compiler") && !ename.startsWith("jdk.aot") && !ename.startsWith("jdk.accessibility") && !ename.startsWith("jdk.jsobject")) {
if (!entry.isDirectory() && ename.endsWith(".java")) {
Path dst = root.resolve(ename);
Files.createDirectories(dst.getParent());
Files.copy(zis, dst);
Files.readAllBytes(dst); //reads all the file back to exclude antivirus scanning time from following measurements
srcListOut.println(dst.toString());
i++;
}
}
}
}
}
Files.walk(root).map(Path::toFile).forEach(File::deleteOnExit); //mark all files and folders for deletion on JVM exit for cases when tearDown is not executed
Thread.sleep(10000); //give some more time for the system to catch a breath for more precise measurement
LOG.log(FINE, "Extracted {0} sources.", i);
}
@TearDown(Level.Trial)
public void tearDown() throws IOException {
Files.walk(root).sorted(Comparator.reverseOrder()).map(Path::toFile).forEachOrdered(File::delete);
LOG.fine("Sources deleted.");
}
protected void compile(Blackhole bh, final Stage stopAt) throws IOException {
final OutputStream bhos = new OutputStream() {
@Override
public void write(int b) throws IOException {
bh.consume(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
bh.consume(b);
}
};
final Context ctx = new Context();
//inject JavaCompiler wrapping all measured methods so they directly report to the benchmark
ctx.put(JavaCompiler.compilerKey, (Factory<JavaCompiler>)(c) -> {
return new JavaCompiler(c) {
@Override
public List<JCTree.JCCompilationUnit> parseFiles(Iterable<JavaFileObject> fileObjects) {
Stage.Init.notifyDone();
return stopAt.isAfter(Stage.Init) ? super.parseFiles(fileObjects) : List.nil();
}
@Override
public List<JCTree.JCCompilationUnit> initModules(List<JCTree.JCCompilationUnit> roots) {
Stage.Parse.notifyDone();
return stopAt.isAfter(Stage.Parse) ? super.initModules(roots) : List.nil();
}
@Override
public List<JCTree.JCCompilationUnit> enterTrees(List<JCTree.JCCompilationUnit> roots) {
Stage.InitModules.notifyDone();
return stopAt.isAfter(Stage.InitModules) ? super.enterTrees(roots) : List.nil();
}
@Override
public Queue<Env<AttrContext>> attribute(Queue<Env<AttrContext>> envs) {
Stage.Enter.notifyDone();
return stopAt.isAfter(Stage.Enter) ? super.attribute(envs) : new ListBuffer<>();
}
@Override
public Queue<Env<AttrContext>> flow(Queue<Env<AttrContext>> envs) {
Stage.Attribute.notifyDone();
return stopAt.isAfter(Stage.Attribute) ? super.flow(envs) : new ListBuffer<>();
}
@Override
public Queue<Pair<Env<AttrContext>, JCTree.JCClassDecl>> desugar(Queue<Env<AttrContext>> envs) {
Stage.Flow.notifyDone();
return stopAt.isAfter(Stage.Flow) ? super.desugar(envs) : new ListBuffer<>();
}
@Override
public void generate(Queue<Pair<Env<AttrContext>, JCTree.JCClassDecl>> queue) {
Stage.Desugar.notifyDone();
if (stopAt.isAfter(Stage.Desugar)) super.generate(queue);
}
};
});
//JavaFileManager directing all writes to a Blackhole to avoid measurement fluctuations due to delayed filesystem writes
try (JavacFileManager mngr = new JavacFileManager(ctx, true, null) {
@Override
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location arg0, String arg1, JavaFileObject.Kind arg2, FileObject arg3) throws IOException {
return new ForwardingJavaFileObject<JavaFileObject>(super.getJavaFileForOutput(arg0, arg1, arg2, arg3)) {
@Override
public OutputStream openOutputStream() throws IOException {
return bhos;
}
};
}
}) {
String[] cmdLine = new String[] {"-source", "25", "-XDcompilePolicy=simple", "-implicit:none", "-nowarn", "--module-source-path", root.toString(), "-d", root.toString(), "-XDignore.symbol.file=true", "@" + srcList.toString()};
if (new Main("javac").compile(cmdLine, ctx).exitCode != 0) {
throw new IOException("compilation failed");
}
}
LOG.fine("Compilation finished.");
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, 2025, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package org.openjdk.bench.langtools.javac;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
@State(Scope.Benchmark)
public class SingleJavacBenchmark extends JavacBenchmark {
@Param
public Stage stopStage;
@Benchmark
@Threads(1)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = 0)
@Measurement(iterations = 1)
@Fork(warmups = 1, value = 15, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void compileCold(Blackhole bh) throws IOException {
compile(bh, stopStage);
}
@Benchmark
@Threads(1)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = 8)
@Measurement(iterations = 10)
@Fork(warmups = 0, value = 1, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" })
@OutputTimeUnit(TimeUnit.SECONDS)
public void compileHot(Blackhole bh) throws IOException {
compile(bh, stopStage);
}
}

54
test/docs/ProblemList.txt Normal file
View file

@ -0,0 +1,54 @@
###########################################################################
#
# Copyright (c) 2024, 2026, 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.
#
###########################################################################
#############################################################################
#
# List of quarantined tests -- tests that should not be run by default, because
# they may fail due to known reason. The reason (CR#) must be mandatory specified.
#
# List items are testnames followed by labels, all MUST BE commented
# as to why they are here and use a label:
# generic-all Problems on all platforms
# generic-ARCH Where ARCH is one of: x64, i586, ppc64, ppc64le, s390x etc.
# OSNAME-all Where OSNAME is one of: linux, windows, macosx, aix
# OSNAME-ARCH Specific on to one OSNAME and ARCH, e.g. macosx-x64
# OSNAME-REV Specific on to one OSNAME and REV, e.g. macosx-10.7.4
#
# More than one label is allowed but must be on the same line.
#
#############################################################################
#############################################################################
# Preview project specific failures go here at the end of the file.
#
# These are NOT failures that occur with the '--enable-preview' option
# specified; those go in the appropriate ProblemList-enable-preview.txt file.
# These are failures that occur WITHOUT the '--enable-preview' option
# specified AND occur because of some issue with preview project code,
# in either implementation or test code.
#############################################################################

46
test/docs/TEST.ROOT Normal file
View file

@ -0,0 +1,46 @@
#
# Copyright (c) 2024, 2026, 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.
#
#
# This file identifies the root of the test-suite hierarchy.
# It also contains test-suite configuration information.
# The list of keywords supported in the entire test suite. The
# "intermittent" keyword marks tests known to fail intermittently.
# The "randomness" keyword marks tests using randomness with test
# cases differing from run to run. (A test using a fixed random seed
# would not count as "randomness" by this definition.) Extra care
# should be taken to handle test failures of intermittent or
# randomness tests.
# Group definitions
groups=TEST.groups
# Minimum jtreg version
requiredVersion=8.2.1+1
# Path to libraries in the topmost test directory. This is needed so @library
# does not need ../../ notation to reach them
external.lib.roots = ../../

29
test/docs/TEST.groups Normal file
View file

@ -0,0 +1,29 @@
# Copyright (c) 2024, 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.
#
# Docs-specific test groups
docs_all = \
/
tier2 = \
:docs_all

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
import doccheckutils.FileChecker;
import doccheckutils.FileProcessor;
import doccheckutils.HtmlFileChecker;
import doccheckutils.checkers.BadCharacterChecker;
import doccheckutils.checkers.DocTypeChecker;
import doccheckutils.checkers.LinkChecker;
import doccheckutils.checkers.TidyChecker;
import doccheckutils.checkers.ExtLinkChecker;
import toolbox.TestRunner;
import java.nio.file.Path;
import java.util.*;
/**
* DocCheck
* <p>
* For the sake of brevity, to run all of these checkers use
* <p>
* `make test-docs_all TEST_DEPS=docs-jdk`
* <p>
* This collection of tests provide a variety of checks for JDK documentation bundle.
* <p>
* It is meant to provide a convenient way to alert users of any errors in their documentation
* before a push and verify the quality of the documentation.
* It is not meant to replace more authoritative checkers; instead,
* it is more focused on providing a convenient, easy overview of any possible issues.
* <p>
* It supports the following checks:
* <p>
* *HTML* -- We use the standard `tidy` utility to check for HTML compliance,
* according to the declared version of HTML.
* The output from `tidy` is analysed to generate a report summarizing any issues that were found.
* <p>
* Version `5.9.20` of `tidy` is expected, or the output from the `--version` option should contain the string `version 5`.
* The test warns the user if he is using an earlier version.
* <p>
* *Bad Characters* -- We assumee that HTML files are encoded in UTF-8,
* and reports any character encoding issues that it finds.
* <p>
* *DocType* -- We assume that HTML files should use HTML5, and reports
* any files for which that is not the case.
* <p>
* *Links* -- We check links within a set of files, and reports on links
* to external resources, without otherwise checking them.
* <p>
* *External Links* -- We scan the files for URLs that refer to
* external resources, and validates those references using a "golden file" that includes a list of vetted links.
* <p>
* Each external reference is only checked once; but if an issue is found, all the files containing the
* reference will be reported.
*/
public class DocCheck extends TestRunner {
private static final String DOCCHECK_DIR = System.getProperty("doccheck.dir");
private static final Path DIR = Path.of(DOCCHECK_DIR != null ? DOCCHECK_DIR : "");
private static final Set<String> CHECKS_LIST = new HashSet<>();
private static Path DOCS_DIR;
private static boolean html;
private static boolean links;
private static boolean badchars;
private static boolean doctype;
private static boolean extlinks;
private List<Path> files;
public DocCheck() {
super(System.err);
init();
}
public static void main(String... args) throws Exception {
chooseCheckers();
DocCheck docCheck = new DocCheck();
docCheck.runTests();
}
private static void chooseCheckers() {
final String checks = System.getProperty("doccheck.checks");
if (!checks.isEmpty()) {
if (checks.contains(",")) {
CHECKS_LIST.addAll(Arrays.asList(checks.split(",")));
} else {
CHECKS_LIST.add(checks);
}
}
if (CHECKS_LIST.contains("all")) {
html = true;
links = true;
badchars = true;
doctype = true;
extlinks = true;
} else {
if (CHECKS_LIST.contains("html")) {
html = true;
}
if (CHECKS_LIST.contains("links")) {
links = true;
}
if (CHECKS_LIST.contains("badchars")) {
badchars = true;
}
if (CHECKS_LIST.contains("doctype")) {
doctype = true;
}
if (CHECKS_LIST.contains("extlinks")) {
extlinks = true;
}
}
}
public void init() {
var fileTester = new FileProcessor();
DOCS_DIR = DocTester.resolveDocs();
var baseDir = DOCS_DIR.resolve(DIR);
fileTester.processFiles(baseDir);
files = fileTester.getFiles();
if (html) {
new TidyChecker();
}
}
public List<FileChecker> getCheckers() {
List<FileChecker> checkers = new ArrayList<>();
if (html) {
checkers.add(new TidyChecker());
}
if (links) {
var linkChecker = new LinkChecker();
linkChecker.setBaseDir(DOCS_DIR);
checkers.add(new HtmlFileChecker(linkChecker, DOCS_DIR));
}
if (extlinks) {
checkers.add(new HtmlFileChecker(new ExtLinkChecker(), DOCS_DIR));
}
// there should be almost nothing reported from these two checkers
// most reports should be broken anchors/links, missing files and errors in html
if (badchars) {
checkers.add(new BadCharacterChecker());
}
if (doctype) {
checkers.add(new HtmlFileChecker(new DocTypeChecker(), DOCS_DIR));
}
return checkers;
}
@Test
public void test() throws Exception {
List<FileChecker> checkers = getCheckers();
runCheckersSequentially(checkers);
}
private void runCheckersSequentially(List<FileChecker> checkers) throws Exception {
List<Throwable> exceptions = new ArrayList<>();
for (FileChecker checker : checkers) {
try (checker) {
checker.checkFiles(files);
} catch (Exception e) {
exceptions.add(e);
}
}
if (!exceptions.isEmpty()) {
throw new Exception("One or more HTML checkers failed: " + exceptions);
}
}
}

View file

@ -0,0 +1,772 @@
# This file is used to check external links in the JDK generated documentation
# to prevent broken links from backsliding into the JDK source.
#
# The file serves as a "whitelist" of links that have been checked to be working as intended
# and JDK developers should add external links to this file whenever they add them to their documentation.
#
#
# The links in this file are checked before every release.
#
#
#
http://cldr.unicode.org/
http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
http://docs.oracle.com/javase/feedback.html
http://docs.oracle.com/javase/jndi/tutorial/index.html
http://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-12.html
http://docs.oracle.com/javase/tutorial/collections/index.html
http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
http://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html
http://docs.oracle.com/javase/tutorial/jdbc/
http://docs.oracle.com/javase/tutorial/jdbc/basics/index.html
http://docs.oracle.com/javase/tutorial/jdbc/basics/rowset.html
http://docs.oracle.com/javase/tutorial/uiswing/dnd/index.html
http://en.wikipedia.org/wiki/Skip_list
http://jclark.com/xml/xmlns.htm
http://jcp.org/en/jsr/detail?id=173
http://jcp.org/en/jsr/detail?id=268
http://relaxng.org/spec-20011203.html
http://sax.sourceforge.net/?selected=get-set
http://standards.iso.org/iso/9075/2002/12/sqlxml.xsd
http://svn.python.org/projects/python/trunk/Objects/listsort.txt
http://tools.ietf.org/html/rfc1421
http://tools.ietf.org/html/rfc5869
http://unicode.org/reports/tr35/
http://unicode.org/reports/tr35/tr35-numbers.html
http://web.archive.org/web/20051219043731/http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Demo/url-primer.html
http://www.cl.cam.ac.uk/~mgk25/time/utc-sls/
http://www.cs.rochester.edu/~scott/papers/1996_PODC_queues.pdf
http://www.iana.org/
http://www.iana.org/assignments/character-sets
http://www.iana.org/assignments/character-sets/character-sets.xhtml
http://www.iana.org/assignments/media-types/
http://www.iana.org/assignments/uri-schemes.html
http://www.ietf.org/
https://www.ietf.org/rfc/rfc793.txt
https://www.ietf.org/rfc/rfc822.txt
http://www.ietf.org/rfc/rfc1122.txt
http://www.ietf.org/rfc/rfc1123.txt
http://www.ietf.org/rfc/rfc1323.txt
http://www.ietf.org/rfc/rfc1349.txt
http://www.ietf.org/rfc/rfc1521.txt
http://www.ietf.org/rfc/rfc1522.txt
http://www.ietf.org/rfc/rfc1918.txt
http://www.ietf.org/rfc/rfc1950.txt
http://www.ietf.org/rfc/rfc1950.txt.pdf
http://www.ietf.org/rfc/rfc1951.txt
http://www.ietf.org/rfc/rfc1951.txt.pdf
http://www.ietf.org/rfc/rfc1952.txt
http://www.ietf.org/rfc/rfc1952.txt.pdf
http://www.ietf.org/rfc/rfc1964.txt
http://www.ietf.org/rfc/rfc2045.txt
http://www.ietf.org/rfc/rfc2046.txt
http://www.ietf.org/rfc/rfc2078.txt
http://www.ietf.org/rfc/rfc2104.txt
http://www.ietf.org/rfc/rfc2109.txt
http://www.ietf.org/rfc/rfc2222.txt
http://www.ietf.org/rfc/rfc2236.txt
http://www.ietf.org/rfc/rfc2245.txt
http://www.ietf.org/rfc/rfc2246.txt
http://www.ietf.org/rfc/rfc2251.txt
http://www.ietf.org/rfc/rfc2253.txt
http://www.ietf.org/rfc/rfc2254.txt
http://www.ietf.org/rfc/rfc2255.txt
http://www.ietf.org/rfc/rfc2268.txt
http://www.ietf.org/rfc/rfc2278.txt
http://www.ietf.org/rfc/rfc2279.txt
http://www.ietf.org/rfc/rfc2296.txt
http://www.ietf.org/rfc/rfc2365.txt
http://www.ietf.org/rfc/rfc2373.txt
http://www.ietf.org/rfc/rfc2396.txt
http://www.ietf.org/rfc/rfc2440.txt
http://www.ietf.org/rfc/rfc2474.txt
http://www.ietf.org/rfc/rfc2609.txt
http://www.ietf.org/rfc/rfc2616.txt
https://www.ietf.org/rfc/rfc2696.txt
http://www.ietf.org/rfc/rfc2710.txt
http://www.ietf.org/rfc/rfc2732.txt
http://www.ietf.org/rfc/rfc2743.txt
http://www.ietf.org/rfc/rfc2781.txt
http://www.ietf.org/rfc/rfc2782.txt
http://www.ietf.org/rfc/rfc2830.txt
http://www.ietf.org/rfc/rfc2831.txt
http://www.ietf.org/rfc/rfc2853.txt
http://www.ietf.org/rfc/rfc2891.txt
http://www.ietf.org/rfc/rfc2898.txt
http://www.ietf.org/rfc/rfc2965.txt
http://www.ietf.org/rfc/rfc3023.txt
http://www.ietf.org/rfc/rfc3111.txt
http://www.ietf.org/rfc/rfc3275.txt
http://www.ietf.org/rfc/rfc3279.txt
http://www.ietf.org/rfc/rfc3296.txt
http://www.ietf.org/rfc/rfc3330.txt
http://www.ietf.org/rfc/rfc3376.txt
http://www.ietf.org/rfc/rfc3454.txt
http://www.ietf.org/rfc/rfc3490.txt
http://www.ietf.org/rfc/rfc3491.txt
http://www.ietf.org/rfc/rfc3492.txt
http://www.ietf.org/rfc/rfc3530.txt
http://www.ietf.org/rfc/rfc3720.txt
http://www.ietf.org/rfc/rfc3720.txt.pdf
http://www.ietf.org/rfc/rfc3758.txt
http://www.ietf.org/rfc/rfc3810.txt
http://www.ietf.org/rfc/rfc3986.txt
http://www.ietf.org/rfc/rfc4120.txt
http://www.ietf.org/rfc/rfc4122.txt
http://www.ietf.org/rfc/rfc4366.txt
http://www.ietf.org/rfc/rfc4512.txt
http://www.ietf.org/rfc/rfc4648.txt
http://www.ietf.org/rfc/rfc5116.txt
http://www.ietf.org/rfc/rfc5280.txt
http://www.ietf.org/rfc/rfc5890.txt
http://www.ietf.org/rfc/rfc6066.txt
http://www.ietf.org/rfc/rfc7301.txt
http://www.ietf.org/rfc/rfc790.txt
http://www.ietf.org/rfc/rfc793.txt
http://www.ietf.org/rfc/rfc822.txt
http://www.ietf.org/rfc/rfc919.txt
http://www.info-zip.org/doc/appnote-19970311-iz.zip
http://www.ioplex.com/utilities/keytab.txt
http://www.iso.org/iso/home/standards/currency_codes.htm
http://www.jcp.org
http://www.jcp.org/en/jsr/detail?id=203
http://www.jpeg.org
http://www.libpng.org/pub/png/spec/
http://www.microsoft.com/typography/otspec/
http://www.midi.org
http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=dss
http://www.opengroup.org
http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html
http://www.oracle.com/technetwork/java/architecture-142923.html
http://www.oracle.com/technetwork/java/javase/documentation/serialized-criteria-137781.html
http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html
http://www.oracle.com/technetwork/java/javase/javasecarootcertsprogram-1876540.html
http://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html
http://www.oracle.com/technetwork/java/painting-140037.html
http://www.oracle.com/technetwork/java/persistence2-141443.html
http://www.oracle.com/technetwork/java/persistence3-139471.html
http://www.oracle.com/technetwork/java/persistence4-140124.html
http://www.oreilly.com/catalog/regex/
http://www.oreilly.com/catalog/regex3/
http://www.reactive-streams.org/
http://www.relaxng.org/
http://www.rfc-editor.org/rfc/bcp/bcp47.txt
http://www.saxproject.org
http://www.saxproject.org/
http://www.unicode.org
http://www.unicode.org/
http://www.unicode.org/glossary/
http://www.unicode.org/reports/tr15/
http://www.unicode.org/reports/tr18/
http://www.unicode.org/reports/tr24/
http://www.unicode.org/reports/tr27/
http://www.unicode.org/reports/tr36/
http://www.unicode.org/reports/tr44/
http://www.unicode.org/standard/standard.html
http://www.w3.org/2000/09/xmldsig
http://www.w3.org/2000/xmlns/
http://www.w3.org/2001/04/xmldsig-more
http://www.w3.org/2001/04/xmlenc
http://www.w3.org/2001/05/xmlschema-errata
http://www.w3.org/2001/10/xml-exc-c14n
http://www.w3.org/2002/06/xmldsig-filter2
http://www.w3.org/2007/05/xmldsig-more
http://www.w3.org/2009/xmldsig11
http://www.w3.org/2021/04/xmldsig-more
http://www.w3.org/Graphics/GIF/spec-gif89a.txt
http://www.w3.org/TR/1998/REC-CSS2-19980512
http://www.w3.org/TR/1999/REC-html401-19991224/
http://www.w3.org/TR/1999/REC-xml-names-19990114/
http://www.w3.org/TR/1999/REC-xpath-19991116
http://www.w3.org/TR/1999/REC-xslt-19991116
http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Views-20001113
http://www.w3.org/TR/2001/REC-xml-c14n-20010315
http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107
http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109
http://www.w3.org/TR/2003/REC-SVG11-20030114/
http://www.w3.org/TR/2003/REC-xptr-framework-20030325/
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html
http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407
http://www.w3.org/TR/2004/REC-DOM-Level-3-Val-20040127/
http://www.w3.org/TR/2004/REC-xml-20040204
http://www.w3.org/TR/2004/REC-xml-infoset-20040204
http://www.w3.org/TR/2004/REC-xml-infoset-20040204/
http://www.w3.org/TR/2004/REC-xml-names11-20040204/
http://www.w3.org/TR/2004/REC-xml11-20040204/
http://www.w3.org/TR/DOM-Level-2
http://www.w3.org/TR/DOM-Level-2-Core/
http://www.w3.org/TR/DOM-Level-3-Core
http://www.w3.org/TR/DOM-Level-3-LS
http://www.w3.org/TR/ElementTraversal/
http://www.w3.org/TR/NOTE-datetime
http://www.w3.org/TR/REC-CSS1
http://www.w3.org/TR/REC-html32.html
http://www.w3.org/TR/REC-xml
http://www.w3.org/TR/REC-xml-names
http://www.w3.org/TR/REC-xml-names/
http://www.w3.org/TR/REC-xml/
http://www.w3.org/TR/html4/
http://www.w3.org/TR/html40/appendix/notes.html
http://www.w3.org/TR/xinclude/
http://www.w3.org/TR/xml-exc-c14n/
http://www.w3.org/TR/xml-names11/
http://www.w3.org/TR/xml-stylesheet/
http://www.w3.org/TR/xml11/
http://www.w3.org/TR/xmldsig-core/
http://www.w3.org/TR/xmldsig-filter2
http://www.w3.org/TR/xmldsig-filter2/
http://www.w3.org/TR/xmlschema-1
http://www.w3.org/TR/xmlschema-1/
http://www.w3.org/TR/xmlschema-2/
http://www.w3.org/TR/xpath
http://www.w3.org/TR/xpath-datamodel
http://www.w3.org/TR/xpath/
http://www.w3.org/TR/xslt
http://www.w3.org/XML/1998/namespace
http://www.w3.org/XML/Schema
http://www.w3.org/XML/xml-V10-2e-errata
http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html
http://www.w3.org/pub/WWW/Protocols/
http://xmlns.jcp.org/xml/ns//jdbc/webrowset.xsd
https://bugreport.java.com/bugreport/
https://bugs.openjdk.org/secure/attachment/75649/JVM_CodeHeap_StateAnalytics_V2.pdf
https://cldr.unicode.org/index/downloads
https://csrc.nist.gov/publications/PubsFIPS.html
https://csrc.nist.gov/publications/fips/archive/fips186-2/fips186-2.pdf
https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
https://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
https://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
https://csrc.nist.gov/publications/fips/fips81/fips81.htm
https://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf
https://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
https://csrc.nist.gov/pubs/fips/203/final
https://csrc.nist.gov/pubs/fips/204/final
https://datatracker.ietf.org/doc/html/rfc5646
https://datatracker.ietf.org/doc/html/rfc8017
https://developer.apple.com/documentation
https://docs.oracle.com/en/java/javase/11/tools/java.html
https://docs.oracle.com/en/java/javase/12/language/index.html
https://docs.oracle.com/en/java/javase/12/tools/java.html
https://docs.oracle.com/en/java/javase/12/vm/compiler-control1.html
https://docs.oracle.com/en/java/javase/13/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/14/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/15/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/16/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/18/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/19/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/20/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/21/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/22/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/Double.html
https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/math/BigDecimal.html
https://docs.oracle.com/en/java/javase/23/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/24/docs/specs/man/java.html
https://docs.oracle.com/en/java/javase/@@JAVASE_VERSION@@/docs/api/java.base/java/lang/String.html
https://docs.oracle.com/en/java/javase/@@JAVASE_VERSION@@/docs/specs/javadoc/javadoc-search-spec.html
https://docs.oracle.com/en/java/javase/@@JAVASE_VERSION@@/docs/specs/man/javadoc.html
https://docs.oracle.com/en/java/javase/index.html
https://docs.oracle.com/javase/10/tools/java.htm
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
https://docs.oracle.com/javase/9/tools/java.htm
https://docs.oracle.com/javase/specs/
https://docs.oracle.com/javase/specs/jls/se10/html/index.html
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
https://docs.oracle.com/javase/specs/jls/se12/html/index.html
https://docs.oracle.com/javase/specs/jls/se13/html/index.html
https://docs.oracle.com/javase/specs/jls/se14/html/index.html
https://docs.oracle.com/javase/specs/jls/se15/html/index.html
https://docs.oracle.com/javase/specs/jls/se16/html/index.html
https://docs.oracle.com/javase/specs/jls/se17/html/index.html
https://docs.oracle.com/javase/specs/jls/se18/html/index.html
https://docs.oracle.com/javase/specs/jls/se19/html/index.html
https://docs.oracle.com/javase/specs/jls/se20/html/index.html
https://docs.oracle.com/javase/specs/jls/se21/html/index.html
https://docs.oracle.com/javase/specs/jls/se22/html/index.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-13.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-14.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-15.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-17.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-3.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-4.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-5.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-6.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-8.html
https://docs.oracle.com/javase/specs/jls/se22/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se23/html
https://docs.oracle.com/javase/specs/jls/se23/html/index.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-10.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-11.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-12.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-14.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-15.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-16.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-18.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-2.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-7.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html
https://docs.oracle.com/javase/specs/jls/se23/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se24/html/index.html
https://docs.oracle.com/javase/specs/jls/se24/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/index.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-10.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-11.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-12.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-13.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-14.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-15.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-17.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-18.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-3.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-4.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-5.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-6.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-7.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-8.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/html/jls-9.html
https://docs.oracle.com/javase/specs/jls/se@@JAVASE_VERSION@@/jls@@JAVASE_VERSION@@.pdf
https://docs.oracle.com/javase/specs/jls/se6/html/j3TOC.html
https://docs.oracle.com/javase/specs/jls/se7/html/index.html
https://docs.oracle.com/javase/specs/jls/se8/html/index.html
https://docs.oracle.com/javase/specs/jls/se9/html/index.html
https://docs.oracle.com/javase/specs/jvms/se10/html/index.html
https://docs.oracle.com/javase/specs/jvms/se11/html/index.html
https://docs.oracle.com/javase/specs/jvms/se12/html/index.html
https://docs.oracle.com/javase/specs/jvms/se13/html/index.html
https://docs.oracle.com/javase/specs/jvms/se14/html/index.html
https://docs.oracle.com/javase/specs/jvms/se15/html/index.html
https://docs.oracle.com/javase/specs/jvms/se16/html/index.html
https://docs.oracle.com/javase/specs/jvms/se17/html/index.html
https://docs.oracle.com/javase/specs/jvms/se18/html/index.html
https://docs.oracle.com/javase/specs/jvms/se19/html/index.html
https://docs.oracle.com/javase/specs/jvms/se20/html/index.html
https://docs.oracle.com/javase/specs/jvms/se21/html/index.html
https://docs.oracle.com/javase/specs/jvms/se22/html/index.html
https://docs.oracle.com/javase/specs/jvms/se23/html
https://docs.oracle.com/javase/specs/jvms/se23/html/index.html
https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html
https://docs.oracle.com/javase/specs/jvms/se24/html/index.html
https://docs.oracle.com/javase/specs/jvms/se24/html/jvms-4.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/index.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-1.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-2.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-3.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-4.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-5.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/html/jvms-6.html
https://docs.oracle.com/javase/specs/jvms/se@@JAVASE_VERSION@@/jvms@@JAVASE_VERSION@@.pdf
https://docs.oracle.com/javase/specs/jvms/se7/html/index.html
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
https://docs.oracle.com/javase/specs/jvms/se9/html/index.html
https://docs.oracle.com/javase/tutorial/
https://docs.oracle.com/javase/tutorial/2d/text/fonts.html
https://docs.oracle.com/javase/tutorial/extra/fullscreen/index.html
https://docs.oracle.com/javase/tutorial/index.html
https://docs.oracle.com/javase/tutorial/javabeans/
https://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html
https://docs.oracle.com/javase/tutorial/sound/
https://docs.oracle.com/javase/tutorial/uiswing/
https://docs.oracle.com/javase/tutorial/uiswing/components/applet.html
https://docs.oracle.com/javase/tutorial/uiswing/components/border.html
https://docs.oracle.com/javase/tutorial/uiswing/components/button.html
https://docs.oracle.com/javase/tutorial/uiswing/components/colorchooser.html
https://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html
https://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html
https://docs.oracle.com/javase/tutorial/uiswing/components/filechooser.html
https://docs.oracle.com/javase/tutorial/uiswing/components/frame.html
https://docs.oracle.com/javase/tutorial/uiswing/components/generaltext.html
https://docs.oracle.com/javase/tutorial/uiswing/components/icon.html
https://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html
https://docs.oracle.com/javase/tutorial/uiswing/components/jcomponent.html
https://docs.oracle.com/javase/tutorial/uiswing/components/label.html
https://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/list.html
https://docs.oracle.com/javase/tutorial/uiswing/components/menu.html
https://docs.oracle.com/javase/tutorial/uiswing/components/panel.html
https://docs.oracle.com/javase/tutorial/uiswing/components/progress.html
https://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/slider.html
https://docs.oracle.com/javase/tutorial/uiswing/components/spinner.html
https://docs.oracle.com/javase/tutorial/uiswing/components/splitpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html
https://docs.oracle.com/javase/tutorial/uiswing/components/table.html
https://docs.oracle.com/javase/tutorial/uiswing/components/text.html
https://docs.oracle.com/javase/tutorial/uiswing/components/textfield.html
https://docs.oracle.com/javase/tutorial/uiswing/components/toolbar.html
https://docs.oracle.com/javase/tutorial/uiswing/components/tooltip.html
https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
https://docs.oracle.com/javase/tutorial/uiswing/components/tree.html
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html
https://docs.oracle.com/javase/tutorial/uiswing/dnd/index.html
https://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/componentlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/containerlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/focuslistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/index.html
https://docs.oracle.com/javase/tutorial/uiswing/events/internalframelistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/itemlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/mousemotionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treeexpansionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treemodellistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treeselectionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/treewillexpandlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/windowlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/index.html
https://docs.oracle.com/javase/tutorial/uiswing/layout/box.html
https://docs.oracle.com/javase/tutorial/uiswing/layout/spring.html
https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/action.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=GUID-FE2D2E28-C991-4EF9-9DBE-2A4982726313
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=homepage
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=i18n_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=imf_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=jndi_ldap_gl_prop
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=jndi_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=logging_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=monitoring_and_management_using_jmx_technology
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=rmi_guide
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=secure_coding_guidelines_javase
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_impl_provider
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jca
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jca_provider
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jdk_providers
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_jgss_tutorial
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_overview
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_pki
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_sasl
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=security_guide_tools
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=serialization_filter_guide
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=serialver_tool_reference
https://docs.oracle.com/pls/topic/lookup?ctx=javase@@JAVASE_VERSION@@&id=using_jconsole
https://ftp.pwg.org/pub/pwg/candidates/cs-ippoutputbin10-20010207-5100.2.pdf
https://ftp.pwg.org/pub/pwg/standards/temp_archive/pwg5100.3.pdf
https://github.github.com/gfm/
https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles
https://html.spec.whatwg.org
https://html.spec.whatwg.org/multipage/
https://html.spec.whatwg.org/multipage/introduction.html
https://html.spec.whatwg.org/multipage/sections.html
https://html.spec.whatwg.org/multipage/semantics.html
https://jcp.org/aboutJava/communityprocess/maintenance/jsr924/index.html
https://jcp.org/aboutJava/communityprocess/maintenance/jsr924/index2.html
https://jcp.org/aboutJava/communityprocess/mrel/jsr160/index2.html
https://jcp.org/en/jsr/detail?id=14
https://jcp.org/en/jsr/detail?id=175
https://jcp.org/en/jsr/detail?id=201
https://jcp.org/en/jsr/detail?id=221
https://jcp.org/en/jsr/detail?id=269
https://jcp.org/en/jsr/detail?id=334
https://jcp.org/en/jsr/detail?id=335
https://jcp.org/en/jsr/detail?id=376
https://jcp.org/en/jsr/detail?id=41
https://jcp.org/en/procedures/jcp2
https://mermaid.js.org
https://msdn.microsoft.com/en-us/library/cc236621.aspx
https://msdn.microsoft.com/en-us/library/dd183391.aspx
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf
https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
https://openjdk.org/jeps/11
https://openjdk.org/jeps/12
https://openjdk.org/jeps/181
https://openjdk.org/jeps/213
https://openjdk.org/jeps/225
https://openjdk.org/jeps/261
https://openjdk.org/jeps/286
https://openjdk.org/jeps/306
https://openjdk.org/jeps/323
https://openjdk.org/jeps/361
https://openjdk.org/jeps/371
https://openjdk.org/jeps/378
https://openjdk.org/jeps/394
https://openjdk.org/jeps/395
https://openjdk.org/jeps/396
https://openjdk.org/jeps/403
https://openjdk.org/jeps/409
https://openjdk.org/jeps/421
https://openjdk.org/jeps/440
https://openjdk.org/jeps/441
https://openjdk.org/jeps/454
https://openjdk.org/jeps/456
https://openjdk.org/jeps/458
https://openjdk.org/jeps/467
https://openjdk.org/jeps/478
https://openjdk.org/jeps/487
https://openjdk.org/jeps/488
https://openjdk.org/jeps/492
https://openjdk.org/jeps/494
https://openjdk.org/jeps/495
https://openjdk.org/jeps/499
https://prismjs.com
https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_addr.html
https://relaxng.org/
https://reproducible-builds.org/
https://spec.commonmark.org/0.31.2
https://spec.commonmark.org/0.31.2/
https://standards.ieee.org/ieee/754/6210/
https://standards.iso.org/ittf/PubliclyAvailableStandards/c055982_ISO_IEC_19757-3_2016.zip
https://standards.iso.org/ittf/PubliclyAvailableStandards/index.html
https://support.pkware.com/pkzip/appnote
https://tools.ietf.org/html/rfc1319
https://tools.ietf.org/html/rfc1321
https://tools.ietf.org/html/rfc1779
https://tools.ietf.org/html/rfc2040
https://tools.ietf.org/html/rfc2104
https://tools.ietf.org/html/rfc2195
https://tools.ietf.org/html/rfc2222
https://tools.ietf.org/html/rfc2246
https://tools.ietf.org/html/rfc2253
https://tools.ietf.org/html/rfc2595
https://tools.ietf.org/html/rfc2616
https://tools.ietf.org/html/rfc2712
https://tools.ietf.org/html/rfc2818
https://tools.ietf.org/html/rfc2830
https://tools.ietf.org/html/rfc2831
https://tools.ietf.org/html/rfc3217
https://tools.ietf.org/html/rfc3278
https://tools.ietf.org/html/rfc3394
https://tools.ietf.org/html/rfc3986
https://tools.ietf.org/html/rfc4086
https://tools.ietf.org/html/rfc4121
https://tools.ietf.org/html/rfc4162
https://tools.ietf.org/html/rfc4178
https://tools.ietf.org/html/rfc4234
https://tools.ietf.org/html/rfc4279
https://tools.ietf.org/html/rfc4346
https://tools.ietf.org/html/rfc4347
https://tools.ietf.org/html/rfc4492
https://tools.ietf.org/html/rfc4512
https://tools.ietf.org/html/rfc4647
https://tools.ietf.org/html/rfc4785
https://tools.ietf.org/html/rfc4960
https://tools.ietf.org/html/rfc5054
https://tools.ietf.org/html/rfc5061
https://tools.ietf.org/html/rfc5084
https://tools.ietf.org/html/rfc5246
https://tools.ietf.org/html/rfc5280
https://tools.ietf.org/html/rfc5288
https://tools.ietf.org/html/rfc5289
https://tools.ietf.org/html/rfc5469
https://tools.ietf.org/html/rfc5487
https://tools.ietf.org/html/rfc5489
https://tools.ietf.org/html/rfc5639
https://tools.ietf.org/html/rfc5646
https://tools.ietf.org/html/rfc5649
https://tools.ietf.org/html/rfc5746
https://tools.ietf.org/html/rfc5932
https://tools.ietf.org/html/rfc6209
https://tools.ietf.org/html/rfc6347
https://tools.ietf.org/html/rfc6367
https://tools.ietf.org/html/rfc6454
https://tools.ietf.org/html/rfc6455
https://tools.ietf.org/html/rfc6655
https://tools.ietf.org/html/rfc6931
https://tools.ietf.org/html/rfc7230
https://tools.ietf.org/html/rfc7231
https://tools.ietf.org/html/rfc7251
https://tools.ietf.org/html/rfc7292
https://tools.ietf.org/html/rfc7507
https://tools.ietf.org/html/rfc7539
https://tools.ietf.org/html/rfc7540
https://tools.ietf.org/html/rfc7748
https://tools.ietf.org/html/rfc7905
https://tools.ietf.org/html/rfc7919
https://tools.ietf.org/html/rfc8017
https://tools.ietf.org/html/rfc8018
https://tools.ietf.org/html/rfc8032
https://tools.ietf.org/html/rfc8103
https://tools.ietf.org/html/rfc8353
https://tools.ietf.org/html/rfc8422
https://tools.ietf.org/html/rfc8446
https://tools.ietf.org/html/rfc8554
https://tools.ietf.org/id/draft-kaukonen-cipher-arcfour-03.txt
https://tools.ietf.org/rfc/rfc5280.txt
https://tools.ietf.org/rfc/rfc8017.txt
https://unicode.org/reports/tr31/
https://unicode.org/reports/tr35/
https://unicode.org/reports/tr35/tr35-dates.html
https://unicode.org/reports/tr35/tr35-numbers.html
https://unicode.org/reports/tr51/
https://web.mit.edu/kerberos/
https://webhome.phy.duke.edu/~rgb/General/dieharder.php
https://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
https://www.color.org
https://www.color.org/ICC1V42.pdf
https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml
https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
https://www.iana.org/time-zones
https://www.ietf.org/rfc/rfc2616.txt
https://www.ietf.org/rfc/rfc2818.txt
https://www.ietf.org/rfc/rfc6931.txt
https://www.ietf.org/rfc/rfc6943.html
https://www.iso.org/home.html
https://www.iso.org/iso-4217-currency-codes.html
https://www.iso.org/iso-8601-date-and-time-format.html
https://www.iso.org/standard/18114.html
https://www.itu.int/itudoc/itu-t/com16/tiff-fx/docs/tiff6.pdf
https://www.itu.int/rec/T-REC-X.509/en
https://www.netlib.org/fdlibm/
https://www.oasis-open.org
https://www.oasis-open.org/committees/download.php/14809/xml-catalogs.html
https://www.oracle.com/java/javase/terms/license/java@@JAVASE_VERSION@@speclicense.html
https://www.oracle.com/java/technologies/a-swing-architecture.html
https://www.oracle.com/java/technologies/javase/seccodeguide.html
https://www.oracle.com/java/technologies/javase/training-support.html
https://www.oracle.com/pls/topic/lookup?ctx=en/java/javase&id=security_guide_implement_provider_jca
https://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html
https://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html
https://www.oracle.com/technetwork/java/redist-137594.html
https://www.oracle.com/technetwork/java/seccodeguide-139067.html
https://www.owasp.org
https://www.rfc-editor.org/info/rfc1122
https://www.rfc-editor.org/info/rfc1123
https://www.rfc-editor.org/info/rfc1323
https://www.rfc-editor.org/info/rfc1349
https://www.rfc-editor.org/info/rfc1738
https://www.rfc-editor.org/info/rfc1779
https://www.rfc-editor.org/info/rfc1918
https://www.rfc-editor.org/info/rfc1950
https://www.rfc-editor.org/info/rfc1951
https://www.rfc-editor.org/info/rfc1952
https://www.rfc-editor.org/info/rfc2040
https://www.rfc-editor.org/info/rfc2045
https://www.rfc-editor.org/info/rfc2046
https://www.rfc-editor.org/info/rfc2109
https://www.rfc-editor.org/info/rfc2236
https://www.rfc-editor.org/info/rfc2246
https://www.rfc-editor.org/info/rfc2253
https://www.rfc-editor.org/info/rfc2268
https://www.rfc-editor.org/info/rfc2278
https://www.rfc-editor.org/info/rfc2279
https://www.rfc-editor.org/info/rfc2296
https://www.rfc-editor.org/info/rfc2306
https://www.rfc-editor.org/info/rfc2365
https://www.rfc-editor.org/info/rfc2368
https://www.rfc-editor.org/info/rfc2373
https://www.rfc-editor.org/info/rfc2396
https://www.rfc-editor.org/info/rfc2474
https://www.rfc-editor.org/info/rfc2560
https://www.rfc-editor.org/info/rfc2616
https://www.rfc-editor.org/info/rfc2710
https://www.rfc-editor.org/info/rfc2732
https://www.rfc-editor.org/info/rfc2781
https://www.rfc-editor.org/info/rfc2898
https://www.rfc-editor.org/info/rfc2911
https://www.rfc-editor.org/info/rfc2965
https://www.rfc-editor.org/info/rfc3279
https://www.rfc-editor.org/info/rfc3330
https://www.rfc-editor.org/info/rfc3376
https://www.rfc-editor.org/info/rfc3454
https://www.rfc-editor.org/info/rfc3490
https://www.rfc-editor.org/info/rfc3491
https://www.rfc-editor.org/info/rfc3492
https://www.rfc-editor.org/info/rfc3530
https://www.rfc-editor.org/info/rfc3720
https://www.rfc-editor.org/info/rfc3810
https://www.rfc-editor.org/info/rfc3986
https://www.rfc-editor.org/info/rfc4007
https://www.rfc-editor.org/info/rfc4086
https://www.rfc-editor.org/info/rfc4122
https://www.rfc-editor.org/info/rfc4234
https://www.rfc-editor.org/info/rfc4366
https://www.rfc-editor.org/info/rfc4512
https://www.rfc-editor.org/info/rfc4647
https://www.rfc-editor.org/info/rfc4648
https://www.rfc-editor.org/info/rfc5116
https://www.rfc-editor.org/info/rfc5280
https://www.rfc-editor.org/info/rfc5646
https://www.rfc-editor.org/info/rfc5869
https://www.rfc-editor.org/info/rfc5890
https://www.rfc-editor.org/info/rfc6066
https://www.rfc-editor.org/info/rfc7301
https://www.rfc-editor.org/info/rfc7539
https://www.rfc-editor.org/info/rfc790
https://www.rfc-editor.org/info/rfc793
https://www.rfc-editor.org/info/rfc8017
https://www.rfc-editor.org/info/rfc8032
https://www.rfc-editor.org/info/rfc822
https://www.rfc-editor.org/info/rfc919
https://www.rfc-editor.org/info/rfc9231
https://www.rfc-editor.org/rfc/rfc2315.txt
https://www.rfc-editor.org/rfc/rfc5208.html
https://www.rfc-editor.org/rfc/rfc5280.html
https://www.rfc-editor.org/rfc/rfc5646
https://www.rfc-editor.org/rfc/rfc5646.html
https://www.rfc-editor.org/rfc/rfc5869
https://www.rfc-editor.org/rfc/rfc6943.html
https://www.rfc-editor.org/rfc/rfc8017
https://www.rfc-editor.org/rfc/rfc8017.html
https://www.rfc-editor.org/rfc/rfc9180
https://www.schneier.com/blowfish.html
https://www.secg.org/sec2-v2.pdf
https://www.unicode.org/reports/tr15
https://www.unicode.org/reports/tr15/
https://www.unicode.org/reports/tr18
https://www.unicode.org/reports/tr24
https://www.unicode.org/reports/tr27
https://www.unicode.org/reports/tr29/
https://www.unicode.org/reports/tr31
https://www.unicode.org/reports/tr35
https://www.unicode.org/reports/tr35/
https://www.unicode.org/reports/tr35/tr35-collation.html
https://www.unicode.org/reports/tr35/tr35-dates.html
https://www.unicode.org/reports/tr35/tr35-general.html
https://www.unicode.org/reports/tr35/tr35.html
https://www.unicode.org/reports/tr36
https://www.unicode.org/reports/tr44
https://www.unicode.org/reports/tr44/
https://www.usno.navy.mil/USNO
https://www.usno.navy.mil/USNO/time/master-clock/systems-of-time
https://www.w3.org
https://www.w3.org/Daemon/User/Config/Logging.html
https://www.w3.org/TR/1998/REC-html40-19980424/
https://www.w3.org/TR/1999/REC-xpath-19991116/
https://www.w3.org/TR/2001/REC-xml-c14n-20010315
https://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/
https://www.w3.org/TR/2002/REC-xmldsig-filter2-20021108/
https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html
https://www.w3.org/TR/CSS22
https://www.w3.org/TR/CSS22/syndata.html
https://www.w3.org/TR/DOM-Level-3-XPath/
https://www.w3.org/TR/NOTE-datetime
https://www.w3.org/TR/REC-CSS1
https://www.w3.org/TR/REC-html32.html
https://www.w3.org/TR/REC-xml-names/
https://www.w3.org/TR/html4
https://www.w3.org/TR/html52
https://www.w3.org/TR/html52/dom.html
https://www.w3.org/TR/html52/syntax.html
https://www.w3.org/TR/xml
https://www.w3.org/TR/xml-c14n11/
https://www.w3.org/TR/xml/
https://www.w3.org/TR/xmldsig-core/
https://www.w3.org/TR/xmlschema-2
https://www.w3.org/WAI/standards-guidelines/wcag/
https://www.w3.org/XML/Schema
https://www.w3.org/XML/xml-names-19990114-errata.html
https://www.wapforum.org/what/technical/SPEC-WAESpec-19990524.pdf

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 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
* @bug 8337109
* @summary Check external links in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner
* @run main/othervm -Ddoccheck.checks=extlinks DocCheck
*/

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 2025, 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
* @bug 8337109
* @summary Check the html in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner jtreg.SkippedException
* @run main/othervm -Ddoccheck.checks=html DocCheck
*/

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 2025, 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
* @bug 8337109 8349369
* @summary Check Links in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner
* @run main/othervm -Ddoccheck.checks=links DocCheck
*/

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, 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
* @bug 8337109
* @summary Check doctype and character encoding in the generated documentation
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
* @build DocTester toolbox.TestRunner
* @run main/othervm -Ddoccheck.checks=doctype,badchars DocCheck
*/

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils;
import java.io.Closeable;
/**
* Base class for {@link FileChecker file checkers} and
*/
public interface Checker extends Closeable {
/**
* Writes a report at the end of a run, to summarize the results of the
* checking done by this checker.
*/
void report();
boolean isOK();
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils;
import java.nio.file.Path;
import java.util.List;
public interface FileChecker extends Checker {
void checkFiles(List<Path> files);
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
package doccheckutils;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
public class FileProcessor {
private final List<Path> files;
public FileProcessor() {
files = new ArrayList<>();
}
public List<Path> getFiles() {
return files;
}
public void processFiles(Path directory) {
try {
Files.walkFileTree(directory, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(".html"))
files.add(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new RuntimeException();
}
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils;
import java.nio.file.Path;
import java.util.Map;
/**
* Base class for HTML checkers.
* <p>
* For details on HTML syntax and the terms used in this API, see
* W3C <a href="https://html.spec.whatwg.org/multipage/syntax.html#syntax">The HTML syntax</a>.
*/
public interface HtmlChecker extends Checker {
/**
* Starts checking a new file,
* <p>
* The file becomes the <em>current</em> file until {@link #endFile endFile}
* is called.
*
* @param path the file.
*/
void startFile(Path path);
/**
* Ends checking the current file.
*/
void endFile();
/**
* Checks the content of a {@code <?xml ... ?>} declaration in the
* current file.
*
* @param line the line number on which the declaration was found
* @param attrs the content of the declaration
*/
void xml(int line, Map<String, String> attrs);
/**
* Checks the content of a {@code <!doctype ... >} declaration in the
* current file.
*
* @param line the line number on which the declaration was found
* @param docType the content of the declaration
*/
void docType(int line, String docType);
/**
* Checks the start of an HTML tag in the current file.
*
* @param line the line number on which the start tag for an element was found
* @param name the name of the tag
* @param attrs the attributes of the tag
* @param selfClosing whether the tag is self-closing
*/
void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing);
/**
* Checks the end of an HTML tag in the current file.
*
* @param line the line number on which the end tag for an element was found
* @param name the name of the tag
*/
void endElement(int line, String name);
/**
* Checks the content appearing in between HTML tags.
*
* @param line the line number on which the content was found
* @param content the content
*/
default void content(int line, String content) {
}
}

View file

@ -0,0 +1,389 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Reads an HTML file, and calls a series of{@link HtmlChecker HTML checkers}
* for the HTML constructs found therein.
*/
public class HtmlFileChecker implements FileChecker {
private final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.IGNORE)
.onUnmappableCharacter(CodingErrorAction.IGNORE);
private final Log log;
private final HtmlChecker htmlChecker;
private Path path;
private BufferedReader in;
private int ch;
private int lineNumber;
private boolean inScript;
private boolean xml;
public HtmlFileChecker(HtmlChecker htmlChecker, Path BaseDir) {
this.log = new Log();
log.setBaseDirectory(BaseDir);
this.htmlChecker = htmlChecker;
}
@Override
public void checkFiles(List<Path> files) {
for (Path file : files) {
read(file);
}
}
@Override
public void report() {
System.err.println(log);
}
@Override
public void close() throws IOException {
// report();
htmlChecker.close();
}
private void read(Path path) {
try (BufferedReader r = new BufferedReader(
new InputStreamReader(Files.newInputStream(path), decoder))) {
this.path = path;
this.in = r;
StringBuilder content = new StringBuilder();
startFile(path);
try {
lineNumber = 1;
xml = false;
nextChar();
while (ch != -1) {
if (ch == '<') {
content(content.toString());
content.setLength(0);
html();
} else {
content.append((char) ch);
if (ch == '\n') {
content(content.toString());
content.setLength(0);
}
nextChar();
}
}
} finally {
endFile();
}
} catch (IOException e) {
log.log(path, lineNumber, e);
} catch (Throwable t) {
log.log(path, lineNumber, t);
log.log(String.valueOf(t));
}
}
private void startFile(Path path) {
htmlChecker.startFile(path);
}
private void endFile() {
htmlChecker.endFile();
}
private void docType(String s) {
htmlChecker.docType(lineNumber, s);
}
private void startElement(String name, Map<String, String> attrs, boolean selfClosing) {
htmlChecker.startElement(lineNumber, name, attrs, selfClosing);
}
private void endElement(String name) {
htmlChecker.endElement(lineNumber, name);
}
private void content(String s) {
htmlChecker.content(lineNumber, s);
}
private void nextChar() throws IOException {
ch = in.read();
if (ch == '\n')
lineNumber++;
}
/**
* Read the start or end of an HTML tag, or an HTML comment
* {@literal <identifier attrs> } or {@literal </identifier> }
*
* @throws IOException if there is a problem reading the file
*/
protected void html() throws IOException {
nextChar();
if (isIdentifierStart((char) ch)) {
String name = readIdentifier().toLowerCase(Locale.US);
Map<String, String> attrs = htmlAttrs();
if (attrs != null) {
boolean selfClosing = false;
if (ch == '/') {
nextChar();
selfClosing = true;
}
if (ch == '>') {
nextChar();
startElement(name, attrs, selfClosing);
if (name.equals("script")) {
inScript = true;
}
return;
}
}
} else if (ch == '/') {
nextChar();
if (isIdentifierStart((char) ch)) {
String name = readIdentifier().toLowerCase(Locale.US);
skipWhitespace();
if (ch == '>') {
nextChar();
endElement(name);
if (name.equals("script")) {
inScript = false;
}
return;
}
}
} else if (ch == '!') {
nextChar();
if (ch == '-') {
nextChar();
if (ch == '-') {
nextChar();
while (ch != -1) {
int dash = 0;
while (ch == '-') {
dash++;
nextChar();
}
// Strictly speaking, a comment should not contain "--"
// so dash > 2 is an error, dash == 2 implies ch == '>'
// See http://www.w3.org/TR/html-markup/syntax.html#syntax-comments
// for more details.
if (dash >= 2 && ch == '>') {
nextChar();
return;
}
nextChar();
}
}
} else if (ch == '[') {
nextChar();
if (ch == 'C') {
nextChar();
if (ch == 'D') {
nextChar();
if (ch == 'A') {
nextChar();
if (ch == 'T') {
nextChar();
if (ch == 'A') {
nextChar();
if (ch == '[') {
while (true) {
nextChar();
if (ch == ']') {
nextChar();
if (ch == ']') {
nextChar();
if (ch == '>') {
nextChar();
return;
}
}
}
}
}
}
}
}
}
}
} else {
StringBuilder sb = new StringBuilder();
while (ch != -1 && ch != '>') {
sb.append((char) ch);
nextChar();
}
Pattern p = Pattern.compile("(?is)doctype\\s+html\\s?.*");
String s = sb.toString();
if (p.matcher(s).matches()) {
xml = s.contains("XHTML");
docType(s);
return;
}
}
} else if (ch == '?') {
nextChar();
if (ch == 'x') {
nextChar();
if (ch == 'm') {
nextChar();
if (ch == 'l') {
nextChar();
if (ch == '?') {
nextChar();
if (ch == '>') {
nextChar();
xml = true;
return;
}
}
}
}
}
}
if (!inScript) {
log.log(path, lineNumber, "bad html");
}
}
/**
* Read a series of HTML attributes, terminated by {@literal > }.
* Each attribute is of the form {@literal identifier[=value] }.
* "value" may be unquoted, single-quoted, or double-quoted.
*/
protected Map<String, String> htmlAttrs() throws IOException {
Map<String, String> map = new LinkedHashMap<>();
skipWhitespace();
while (isIdentifierStart((char) ch)) {
String name = readAttributeName().toLowerCase(Locale.US);
skipWhitespace();
String value = null;
if (ch == '=') {
nextChar();
skipWhitespace();
if (ch == '\'' || ch == '"') {
char quote = (char) ch;
nextChar();
StringBuilder sb = new StringBuilder();
while (ch != -1 && ch != quote) {
// if (ch == '\n') {
// error(path, lineNumber, "unterminated string");
// // No point trying to read more.
// // In fact, all attrs get discarded by the caller
// // and superseded by a malformed.html node because
// // the html tag itself is not terminated correctly.
// break loop;
// }
sb.append((char) ch);
nextChar();
}
value = sb.toString() // hack to replace common entities
.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&amp;", "&");
nextChar();
} else {
StringBuilder sb = new StringBuilder();
while (ch != -1 && !isUnquotedAttrValueTerminator((char) ch)) {
sb.append((char) ch);
nextChar();
}
value = sb.toString();
}
skipWhitespace();
}
map.put(name, value);
}
return map;
}
protected boolean isIdentifierStart(char ch) {
return Character.isUnicodeIdentifierStart(ch);
}
protected String readIdentifier() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append((char) ch);
nextChar();
while (ch != -1 && Character.isUnicodeIdentifierPart(ch)) {
sb.append((char) ch);
nextChar();
}
return sb.toString();
}
protected String readAttributeName() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append((char) ch);
nextChar();
while ((ch != -1 && Character.isUnicodeIdentifierPart(ch))
|| ch == '-'
|| (xml && ch == ':')) {
sb.append((char) ch);
nextChar();
}
return sb.toString();
}
protected boolean isWhitespace(char ch) {
return Character.isWhitespace(ch);
}
protected void skipWhitespace() throws IOException {
while (isWhitespace((char) ch)) {
nextChar();
}
}
protected boolean isUnquotedAttrValueTerminator(char ch) {
return switch (ch) {
case '\f', '\n', '\r', '\t', ' ', '"', '\'', '`', '=', '<', '>' -> true;
default -> false;
};
}
@Override
public boolean isOK() {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class Log {
private final ArrayList<String> errors;
private Path baseDir;
public Log() {
errors = new ArrayList<>();
}
public List<String> getErrors() {
return errors;
}
public void log(Path path, int line, String message, Object... args) {
errors.add(formatErrorMessage(path, line, message, args));
}
public String formatErrorMessage(Path path, int line, String message, Object... args) {
return path + ":" + line + ": " + formatErrorMessage(message, args);
}
public String formatErrorMessage(Path path, int line, Throwable t) {
return path + ":" + line + ": " + t;
}
public String formatErrorMessage(Path path, Throwable t) {
return path + ": " + t;
}
public String formatErrorMessage(String message, Object... args) {
return String.format(message, args);
}
public void log(String message) {
errors.add(message);
}
public void log(Path path, int lineNumber, String s, int errorsOnLine) {
log(formatErrorMessage(path, lineNumber, s, errorsOnLine));
}
public void log(Path path, int line, Throwable t) {
log(formatErrorMessage(path, line, t));
}
public void log(Path path, Throwable t) {
log(formatErrorMessage(path, t));
}
public void log(String message, Object... args) {
log(formatErrorMessage(message, args));
}
public void setBaseDirectory(Path baseDir) {
this.baseDir = baseDir.toAbsolutePath();
}
public Path relativize(Path path) {
return baseDir != null && path.startsWith(baseDir) ? baseDir.relativize(path) : path;
}
public boolean noErrors() {
return errors.isEmpty();
}
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils.checkers;
import doccheckutils.FileChecker;
import doccheckutils.Log;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Checks the contents of an HTML file for bad/unmappable characters.
* <p>
* The file encoding is determined from the file contents.
*/
public class BadCharacterChecker implements FileChecker, AutoCloseable {
private static final Pattern doctype = Pattern.compile("(?i)<!doctype html>");
private static final Pattern metaCharset = Pattern.compile("(?i)<meta\\s+charset=\"([^\"]+)\">");
private static final Pattern metaContentType = Pattern.compile("(?i)<meta\\s+http-equiv=\"Content-Type\"\\s+content=\"text/html;charset=([^\"]+)\">");
private final Log errors;
private int files = 0;
private int badFiles = 0;
public BadCharacterChecker() {
errors = new Log();
}
public void checkFile(Path path) {
files++;
boolean ok = true;
try (InputStream in = new BufferedInputStream(Files.newInputStream(path))) {
CharsetDecoder d = getCharset(in).newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
BufferedReader r = new BufferedReader(new InputStreamReader(in, d));
int lineNumber = 0;
String line;
try {
while ((line = r.readLine()) != null) {
lineNumber++;
int errorsOnLine = 0;
for (int i = 0; i < line.length(); i++) {
char ch = line.charAt(i);
if (ch == 0xFFFD) {
errorsOnLine++;
}
}
if (errorsOnLine > 0) {
errors.log(path, lineNumber, "found %d invalid characters", errorsOnLine);
ok = false;
}
}
} catch (IOException e) {
errors.log(path, lineNumber, e);
ok = false;
}
} catch (IOException e) {
errors.log(path, e);
ok = false;
}
if (!ok)
badFiles++;
}
@Override
public void checkFiles(List<Path> files) {
for (Path file : files) {
checkFile(file);
}
}
private Charset getCharset(InputStream in) throws IOException {
CharsetDecoder initial = StandardCharsets.US_ASCII.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
in.mark(1024);
try {
BufferedReader r = new BufferedReader(new InputStreamReader(in, initial));
char[] buf = new char[1024];
int n = r.read(buf, 0, buf.length);
String head = new String(buf, 0, n);
boolean html5 = doctype.matcher(head).find();
Matcher m1 = metaCharset.matcher(head);
if (m1.find()) {
return Charset.forName(m1.group(1));
}
Matcher m2 = metaContentType.matcher(head);
if (m2.find()) {
return Charset.forName(m2.group(1));
}
return html5 ? StandardCharsets.UTF_8 : StandardCharsets.ISO_8859_1;
} finally {
in.reset();
}
}
@Override
public void report() {
if (!errors.noErrors() && files > 0) {
System.err.println("Bad characters found in the generated HTML");
System.err.println(MessageFormat.format(
"""
Bad Characters Report
{0} files read
{1} files contained bad characters"
{2} bad characters or other errors found
""",
files, badFiles, files));
for (String s : errors.getErrors()) {
System.err.println(s);
}
throw new RuntimeException("Bad character found in the generated HTML");
}
}
@Override
public boolean isOK() {
return errors.noErrors();
}
@Override
public void close() {
report();
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils.checkers;
import doccheckutils.HtmlChecker;
import doccheckutils.Log;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Checks the DocType declared at the head of an HTML file.
*
* @see <a href="https://www.w3.org/TR/html5/syntax.html#syntax-doctype">
* W3C HTML5 8.1.1 The DOCTYPE</a>
*/
public class DocTypeChecker implements HtmlChecker {
private final Log log;
private final Map<String, Integer> counts = new HashMap<>();
private int html5;
private int html5_legacy;
private int xml;
private int other;
private Path path;
public DocTypeChecker() {
log = new Log();
}
@Override
public void startFile(Path path) {
this.path = path;
}
@Override
public void endFile() {
}
@Override
public void xml(int line, Map<String, String> attrs) {
xml++;
}
@Override
public void docType(int line, String docType) {
if (docType.equalsIgnoreCase("doctype html")) {
html5++;
} else {
Pattern p = Pattern.compile("(?i)doctype"
+ "\\s+html"
+ "\\s+([a-z]+)"
+ "\\s+(?:\"([^\"]+)\"|'([^']+)')"
+ "(?:\\s+(?:\"([^\"]+)\"|'([^']+)'))?"
+ "\\s*");
Matcher m = p.matcher(docType);
if (m.matches()) {
// See http://www.w3.org/tr/html52/syntax.html#the-doctype
if (m.group(1).equalsIgnoreCase("system")
&& m.group(2).equals("about:legacy-compat")) {
html5_legacy++;
} else {
String version = m.group(2);
List<String> allowedVersions = List.of(
"-//W3C//DTD XHTML 1.0 Strict//EN"
);
if (allowedVersions.stream().noneMatch(v -> v.equals(version))) {
log.log(path, line, "unexpected doctype: " + version);
}
counts.put(version, counts.getOrDefault(version, 0) + 1);
}
} else {
log.log(path, line, "doctype not recognized: " + docType);
other++;
}
}
}
@Override
public void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing) {
}
@Override
public void endElement(int line, String name) {
}
@Override
public void report() {
log.log("DocType Report");
if (xml > 0) {
log.log("%6d: XHTML%n", xml);
}
if (html5 > 0) {
log.log("%6d: HTML5%n", html5);
}
if (html5_legacy > 0) {
log.log("%6d: HTML5 (legacy)%n", html5_legacy);
}
Map<Integer, Set<String>> sortedCounts = new TreeMap<>(Comparator.reverseOrder());
for (Map.Entry<String, Integer> e : counts.entrySet()) {
String s = e.getKey();
Integer n = e.getValue();
Set<String> set = sortedCounts.computeIfAbsent(n, k -> new TreeSet<>());
set.add(s);
}
for (Map.Entry<Integer, Set<String>> e : sortedCounts.entrySet()) {
for (String p : e.getValue()) {
log.log("%6d: %s%n", e.getKey(), p);
}
}
if (other > 0) {
log.log("%6d: other/unrecognized%n", other);
}
for (var line : log.getErrors()) {
System.err.println(line);
}
}
@Override
public boolean isOK() {
return counts.isEmpty() && (other == 0);
}
@Override
public void close() {
if (!isOK()) {
report();
throw new RuntimeException("Found HTML files with missing doctype declaration");
}
}
}

View file

@ -0,0 +1,220 @@
/*
* Copyright (c) 2024, 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.
*/
package doccheckutils.checkers;
import doccheckutils.HtmlChecker;
import doccheckutils.Log;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* Checks the external links referenced in HTML files.
*/
public class ExtLinkChecker implements HtmlChecker, AutoCloseable {
private static final Path testBasePath = Path.of(System.getProperty("test.src"));
private static final Set<String> extLinks = new HashSet<>();
private static final String currentVersion = String.valueOf(Runtime.version().feature());
static {
String input = null;
try {
input = Files.readString(testBasePath.getParent().resolve("ExtLinksJdk.txt"));
} catch (IOException e) {
throw new RuntimeException(e);
}
extLinks.addAll(input.lines()
.filter(line -> !line.startsWith("#"))
.map(line -> line.replaceAll("\\@\\@JAVASE_VERSION\\@\\@", currentVersion))
.collect(Collectors.toUnmodifiableSet()));
}
private final Log log;
private final Map<URI, Set<Path>> allURIs;
private int badURIs;
private Path currFile;
public ExtLinkChecker() {
this.log = new Log();
allURIs = new TreeMap<>();
}
@Override
public void startFile(Path path) {
currFile = path.toAbsolutePath().normalize();
}
@Override
public void endFile() {
}
@Override
public void xml(int line, Map<String, String> attrs) {
}
@Override
public void docType(int line, String doctype) {
}
@Override
@SuppressWarnings("fallthrough")
public void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing) {
switch (name) {
case "a":
case "link":
String href = attrs.get("href");
if (href != null) {
foundReference(line, href);
}
break;
}
}
@Override
public void endElement(int line, String name) {
}
private void foundReference(int line, String ref) {
try {
String uriPath = ref;
String fragment = null;
// The checker runs into a problem with links that have more than one hash character.
// You cannot create a URI unless the second hash is escaped.
int firstHashIndex = ref.indexOf('#');
int lastHashIndex = ref.lastIndexOf('#');
if (firstHashIndex != -1 && firstHashIndex != lastHashIndex) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1).replace("#", "%23");
} else if (firstHashIndex != -1) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1);
}
URI uri = new URI(uriPath);
if (fragment != null) {
uri = new URI(uri + "#" + fragment);
}
if (uri.isAbsolute()) {
if (Objects.equals(uri.getScheme(), "javascript")) {
// ignore JavaScript URIs
return;
}
String rawFragment = uri.getRawFragment();
URI noFrag = new URI(uri.toString().replaceAll("#\\Q" + rawFragment + "\\E$", ""));
allURIs.computeIfAbsent(noFrag, _ -> new LinkedHashSet<>()).add(currFile);
}
} catch (URISyntaxException e) {
log.log(currFile, line, "invalid URI: " + e);
}
}
@Override
public void report() {
checkURIs();
}
@Override
public boolean isOK() {
return badURIs == 0;
}
@Override
public void close() {
report();
}
private void checkURIs() {
System.err.println("ExtLinkChecker: checking external links");
allURIs.forEach(this::checkURI);
System.err.println("ExtLinkChecker: finished checking external links");
}
private void checkURI(URI uri, Set<Path> files) {
try {
switch (uri.getScheme()) {
case "ftp":
case "http":
case "https":
isVettedLink(uri, files);
break;
default:
warning(files, uri);
}
} catch (Throwable t) {
badURIs++;
error(files, uri, t);
}
}
private void isVettedLink(URI uri, Set<Path> files) {
if (!extLinks.contains(uri.toString())) {
System.err.println(MessageFormat.format("""
The external link {0} needs to be added to the whitelist test/docs/jdk/javadoc/doccheck/ExtLinksJdk.txt in order to be checked regularly\s
The link is present in:
{1}\n
""", uri, files.stream().map(Path::toString).collect(Collectors.joining("\n "))));
}
}
private void warning(Set<Path> files, Object... args) {
Iterator<Path> iter = files.iterator();
Path first = iter.next();
log.log(String.valueOf(first), "URI not supported: %s", args);
reportAlsoFoundIn(iter);
}
private void error(Set<Path> files, Object... args) {
Iterator<Path> iter = files.iterator();
Path first = iter.next();
log.log(String.valueOf(first), "Exception accessing uri: %s%n [%s]", args);
reportAlsoFoundIn(iter);
}
private void reportAlsoFoundIn(Iterator<Path> iter) {
int MAX_EXTRA = 10;
int n = 0;
while (iter.hasNext()) {
log.log(" Also found in %s", log.relativize(iter.next()));
if (n++ == MAX_EXTRA) {
int rest = 0;
while (iter.hasNext()) {
iter.next();
rest++;
}
log.log(" ... and %d more", rest);
}
}
}
}

View file

@ -0,0 +1,442 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
package doccheckutils.checkers;
import doccheckutils.HtmlChecker;
import doccheckutils.Log;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
/**
* Checks the links defined by and referenced in HTML files.
*/
public class LinkChecker implements HtmlChecker {
private final Log log;
private final Map<Path, IDTable> allFiles;
private final Map<URI, IDTable> allURIs;
// left for debugging
private final boolean checkInwardReferencesOnly = false;
private int files;
private int links;
private int duplicateIds;
private int missingFiles;
private int missingIds;
private int badSchemes;
private Path currFile;
private IDTable currTable;
private boolean html5;
public LinkChecker() {
this.log = new Log();
allFiles = new HashMap<>();
allURIs = new HashMap<>();
}
public void setBaseDir(Path dir) {
log.setBaseDirectory(dir);
}
@Override
public void startFile(Path path) {
currFile = path.toAbsolutePath().normalize();
currTable = allFiles.computeIfAbsent(currFile, p -> new IDTable(log.relativize(p)));
html5 = false;
files++;
}
@Override
public void endFile() {
currTable.check();
}
//unused
public List<Path> getUncheckedFiles() {
return allFiles.entrySet().stream()
.filter(e -> !e.getValue().checked
&& e.getKey().toString().endsWith(".html")
&& Files.exists(e.getKey()))
.map(Map.Entry::getKey)
.toList();
}
public List<Path> getMissingFiles() {
return allFiles.keySet().stream()
.filter(idTable -> !Files.exists(idTable)).toList();
}
@Override
public void xml(int line, Map<String, String> attrs) {
}
@Override
public void docType(int line, String doctype) {
html5 = doctype.matches("(?i)<\\?doctype\\s+html>");
}
@Override
@SuppressWarnings("fallthrough")
public void startElement(int line, String name, Map<String, String> attrs, boolean selfClosing) {
switch (name) {
case "a":
String nameAttr = html5 ? null : attrs.get("name");
if (nameAttr != null) {
foundAnchor(line, nameAttr);
}
// fallthrough
case "link":
String href = attrs.get("href");
if (href != null && !checkInwardReferencesOnly) {
foundReference(line, href);
}
break;
}
String idAttr = attrs.get("id");
if (idAttr != null) {
foundAnchor(line, idAttr);
}
}
@Override
public void endElement(int line, String name) {
}
@Override
public void content(int line, String content) {
HtmlChecker.super.content(line, content);
}
@Override
public void report() {
List<Path> pathList = getMissingFiles();
log.log("");
log.log("Link Checker Report");
if (!pathList.isEmpty()) {
log.log("");
log.log("Missing files: (" + pathList.size() + ")");
pathList.stream()
.sorted()
.forEach(this::reportMissingFile);
}
int anchors = 0;
for (IDTable t : allFiles.values()) {
anchors += (int) t.map.values().stream()
.filter(e -> !e.getReferences().isEmpty())
.count();
}
for (IDTable t : allURIs.values()) {
anchors += (int) t.map.values().stream()
.filter(e -> !e.references.isEmpty())
.count();
}
log.log("Checked " + files + " files.");
log.log("Found " + links + " references to " + anchors + " anchors "
+ "in " + allFiles.size() + " files and " + allURIs.size() + " other URIs.");
if (!pathList.isEmpty()) {
log.log("%6d missing files", pathList.size());
}
if (duplicateIds > 0) {
log.log("%6d duplicate ids", duplicateIds);
}
if (missingIds > 0) {
log.log("%6d missing ids", missingIds);
}
Map<String, Integer> hostCounts = new TreeMap<>(new HostComparator());
for (URI uri : allURIs.keySet()) {
String host = uri.getHost();
if (host != null) {
hostCounts.put(host, hostCounts.computeIfAbsent(host, h -> 0) + 1);
}
}
// if (hostCounts.size() > 0) {
// log.log("");
// log.log("Hosts");
// hostCounts.forEach((h, n) -> log.log("%6d %s", n, h));
// }
for (String message : log.getErrors()) {
System.err.println(message);
}
}
private void reportMissingFile(Path file) {
log.log(log.relativize(file).toString());
IDTable table = allFiles.get(file);
Set<Path> refs = new TreeSet<>();
for (IDInfo id : table.map.values()) {
if (id.references != null) {
for (Position ref : id.references) {
refs.add(ref.path);
}
}
}
int n = 0;
int MAX_REFS = 10;
for (Path ref : refs) {
log.log(" in " + log.relativize(ref));
if (++n == MAX_REFS) {
log.log(" ... and %d more", refs.size() - n);
break;
}
}
missingFiles++;
}
@Override
public boolean isOK() {
return log.noErrors() && (missingFiles == 0);
}
@Override
public void close() {
if (!log.noErrors()) {
report();
throw new RuntimeException("LinkChecker encountered errors; see log above.");
}
}
private void foundAnchor(int line, String name) {
currTable.addID(line, name);
}
private void foundReference(int line, String ref) {
links++;
try {
String uriPath = ref;
String fragment = null;
// The checker runs into a problem with links that have more than one hash character.
// You cannot create a URI unless the second hash is escaped.
int firstHashIndex = ref.indexOf('#');
int lastHashIndex = ref.lastIndexOf('#');
if (firstHashIndex != -1 && firstHashIndex != lastHashIndex) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1).replace("#", "%23");
} else if (firstHashIndex != -1) {
uriPath = ref.substring(0, firstHashIndex);
fragment = ref.substring(firstHashIndex + 1);
}
URI uri = new URI(uriPath);
if (fragment != null) {
uri = new URI(uri + "#" + fragment);
}
if (uri.isAbsolute()) {
foundReference(line, uri);
} else {
Path p;
String resolvedUriPath = uri.getPath();
if (resolvedUriPath == null || resolvedUriPath.isEmpty()) {
p = currFile;
} else {
p = currFile.getParent().resolve(resolvedUriPath).normalize();
}
if (!Files.exists(p)) {
log.log(currFile, line, "missing file reference: " + log.relativize(p));
return;
}
if (fragment != null && !fragment.isEmpty()) {
foundReference(line, p, fragment);
}
}
} catch (URISyntaxException e) {
System.err.println("Failed to create URI: " + ref);
log.log(currFile, line, "invalid URI: " + e);
}
}
private void foundReference(int line, Path p, String fragment) {
IDTable t = allFiles.computeIfAbsent(p, key -> new IDTable(log.relativize(key)));
t.addReference(fragment, currFile, line);
}
private void foundReference(int line, URI uri) {
if (!isSchemeOK(uri.getScheme()) && !checkInwardReferencesOnly) {
log.log(currFile, line, "bad scheme in URI");
badSchemes++;
}
String fragment = uri.getRawFragment();
if (fragment != null && !fragment.isEmpty()) {
try {
URI noFrag = new URI(uri.toString().replaceAll("#\\Q" + fragment + "\\E$", ""));
IDTable t = allURIs.computeIfAbsent(noFrag, IDTable::new);
t.addReference(fragment, currFile, line);
} catch (URISyntaxException e) {
throw new Error(e);
}
}
}
private boolean isSchemeOK(String uriScheme) {
if (uriScheme == null) {
return true;
}
return switch (uriScheme) {
case "ftp", "http", "https", "javascript" -> true;
default -> false;
};
}
static class Position implements Comparable<Position> {
Path path;
int line;
Position(Path path, int line) {
this.path = path;
this.line = line;
}
@Override
public int compareTo(Position o) {
int v = path.compareTo(o.path);
return v != 0 ? v : Integer.compare(line, o.line);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null || getClass() != obj.getClass()) {
return false;
} else {
final Position other = (Position) obj;
return Objects.equals(this.path, other.path)
&& this.line == other.line;
}
}
@Override
public int hashCode() {
return Objects.hashCode(path) * 37 + line;
}
}
static class IDInfo {
boolean declared;
Set<Position> references;
Set<Position> getReferences() {
return references == null ? Collections.emptySet() : references;
}
}
static class HostComparator implements Comparator<String> {
@Override
public int compare(String h1, String h2) {
List<String> l1 = new ArrayList<>(Arrays.asList(h1.split("\\.")));
Collections.reverse(l1);
String r1 = String.join(".", l1);
List<String> l2 = new ArrayList<>(Arrays.asList(h2.split("\\.")));
Collections.reverse(l2);
String r2 = String.join(".", l2);
return r1.compareTo(r2);
}
}
class IDTable {
private final Map<String, IDInfo> map = new HashMap<>();
private final String pathOrURI;
private boolean checked;
IDTable(Path path) {
this.pathOrURI = path.toString();
}
IDTable(URI uri) {
this.pathOrURI = uri.toString();
}
void addID(int line, String name) {
if (checked) {
throw new IllegalStateException("Adding ID after file has been checked");
}
Objects.requireNonNull(name);
IDInfo info = map.computeIfAbsent(name, _ -> new IDInfo());
if (info.declared) {
if (info.references != null || !checkInwardReferencesOnly) {
// don't report error if we're only checking inbound references
// and there are no references to this ID.
log.log(log.relativize(currFile), line, "name already declared: " + name);
duplicateIds++;
}
} else {
info.declared = true;
}
}
void addReference(String name, Path from, int line) {
if (checked) {
if (name != null) {
IDInfo id = map.get(name);
if (id == null || !id.declared) {
log.log(log.relativize(from), line,
"id not found: " + this.pathOrURI + "#" + name);
LinkChecker.this.missingIds++;
}
}
} else {
IDInfo id = map.computeIfAbsent(name, x -> new IDInfo());
if (id.references == null) {
id.references = new TreeSet<>();
}
id.references.add(new Position(from, line));
}
}
void check() {
map.forEach((name, id) -> {
if (name != null && !id.declared) {
for (Position ref : id.references) {
log.log(log.relativize(ref.path), ref.line,
"id not found: " + this.pathOrURI + "#" + name);
}
missingIds++;
}
});
checked = true;
}
}
}

View file

@ -0,0 +1,259 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
package doccheckutils.checkers;
import doccheckutils.FileChecker;
import doccheckutils.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jtreg.SkippedException;
public class TidyChecker implements FileChecker, AutoCloseable {
private final Path TIDY;
final Map<Pattern, Integer> counts = new HashMap<>();
final Pattern okPattern = Pattern.compile("No warnings or errors were found.");
final Pattern countPattern = Pattern.compile("([0-9]+) warnings, ([0-9]+) errors were found!.*?(Not all warnings/errors were shown.)?");
final Pattern countPattern2 = Pattern.compile("Tidy found ([0-9]+) warning[s]? and ([0-9]+) error[s]?!.*?(Not all warnings/errors were shown.)?");
final Pattern cssPattern = Pattern.compile("You are recommended to use CSS.*");
final Pattern guardPattern = Pattern.compile("(line [0-9]+ column [0-9]+ - |[^:]+:[0-9]+:[0-9]+: )(Error|Warning):.*");
final Pattern[] patterns = {
Pattern.compile(".*Error: <.*> is not recognized!"),
Pattern.compile(".*Error: missing quote mark for attribute value"),
Pattern.compile(".*Warning: '<' \\+ '/' \\+ letter not allowed here"),
Pattern.compile(".*Warning: <.*> anchor \".*\" already defined"),
Pattern.compile(".*Warning: <.*> attribute \".*\" has invalid value \".*\""),
Pattern.compile(".*Warning: <.*> attribute \".*\" lacks value"),
Pattern.compile(".*Warning: <.*> attribute \".*\" lacks value"),
Pattern.compile(".*Warning: <.*> attribute with missing trailing quote mark"),
Pattern.compile(".*Warning: <.*> dropping value \".*\" for repeated attribute \".*\""),
Pattern.compile(".*Warning: <.*> inserting \".*\" attribute"),
Pattern.compile(".*Warning: <.*> is probably intended as </.*>"),
Pattern.compile(".*Warning: <.*> isn't allowed in <.*> elements"),
Pattern.compile(".*Warning: <.*> lacks \".*\" attribute"),
Pattern.compile(".*Warning: <.*> missing '>' for end of tag"),
Pattern.compile(".*Warning: <.*> proprietary attribute \".*\""),
Pattern.compile(".*Warning: <.*> unexpected or duplicate quote mark"),
Pattern.compile(".*Warning: <a> id and name attribute value mismatch"),
Pattern.compile(".*Warning: <a> cannot copy name attribute to id"),
Pattern.compile(".*Warning: <a> escaping malformed URI reference"),
Pattern.compile(".*Warning: <blockquote> proprietary attribute \"pre\""),
Pattern.compile(".*Warning: discarding unexpected <.*>"),
Pattern.compile(".*Warning: discarding unexpected </.*>"),
Pattern.compile(".*Warning: entity \".*\" doesn't end in ';'"),
Pattern.compile(".*Warning: inserting implicit <.*>"),
Pattern.compile(".*Warning: inserting missing 'title' element"),
Pattern.compile(".*Warning: missing <!DOCTYPE> declaration"),
Pattern.compile(".*Warning: missing <.*>"),
Pattern.compile(".*Warning: missing </.*> before <.*>"),
Pattern.compile(".*Warning: nested emphasis <.*>"),
Pattern.compile(".*Warning: plain text isn't allowed in <.*> elements"),
Pattern.compile(".*Warning: removing whitespace preceding XML Declaration"),
Pattern.compile(".*Warning: replacing <p> (by|with) <br>"),
Pattern.compile(".*Warning: replacing invalid numeric character reference .*"),
Pattern.compile(".*Warning: replacing obsolete element <xmp> with <pre>"),
Pattern.compile(".*Warning: replacing unexpected .* (by|with) </.*>"),
Pattern.compile(".*Warning: trimming empty <.*>"),
Pattern.compile(".*Warning: unescaped & or unknown entity \".*\""),
Pattern.compile(".*Warning: unescaped & which should be written as &amp;"),
Pattern.compile(".*Warning: using <br> in place of <p>"),
Pattern.compile(".*Warning: <.*> element removed from HTML5"),
Pattern.compile(".*Warning: <.*> attribute \".*\" not allowed for HTML5"),
Pattern.compile(".*Warning: The summary attribute on the <table> element is obsolete in HTML5"),
Pattern.compile(".*Warning: replacing invalid UTF-8 bytes \\(char. code U\\+.*\\)")
};
private final Log errors;
private int files = 0;
private int ok;
private int warns;
private int errs;
private int css;
private int overflow;
public TidyChecker() {
TIDY = initTidy();
errors = new Log();
}
@Override
public void checkFiles(List<Path> sb) {
files += sb.size();
try {
for (int i = 0; i < sb.size(); i += 1024) {
List<String> command = new ArrayList<>();
command.add(TIDY.toString());
command.add("-q");
command.add("-e");
command.add("--gnu-emacs");
command.add("true");
List<Path> sublist = sb.subList(i, Math.min(i + 1024, sb.size()));
for (Path p : sublist) {
command.add(p.toString());
}
Process p = new ProcessBuilder()
.command(command)
.redirectErrorStream(true)
.start();
try (BufferedReader r =
new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = r.readLine()) != null) {
checkLine(line);
}
}
}
} catch (IOException e) {
throw new RuntimeException();
}
}
private Path initTidy() {
Path tidyExePath;
String tidyProperty = System.getProperty("tidy");
if (tidyProperty != null) {
tidyExePath = Path.of(tidyProperty);
if (!Files.exists(tidyExePath)) {
System.err.println("tidy not found: " + tidyExePath);
}
if (!Files.isExecutable(tidyExePath)) {
System.err.println("tidy not executable: " + tidyExePath);
}
} else {
boolean isWindows = System.getProperty("os.name")
.toLowerCase(Locale.US)
.startsWith("windows");
String tidyExe = isWindows ? "tidy.exe" : "tidy";
Optional<Path> p = Stream.of(System.getenv("PATH")
.split(File.pathSeparator))
.map(Path::of)
.map(d -> d.resolve(tidyExe))
.filter(Files::exists)
.filter(Files::isExecutable)
.findFirst();
if (p.isPresent()) {
tidyExePath = p.get();
} else {
throw new jtreg.SkippedException("tidy not found on PATH");
}
}
try {
Process p = new ProcessBuilder()
.command(tidyExePath.toString(), "-version")
.redirectErrorStream(true)
.start();
try (BufferedReader r =
new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
List<String> lines = r.lines().collect(Collectors.toList());
// Look for a line containing "version" and a dotted identifier beginning 5.
// If not found, look for known old/bad versions, to report in error message
Pattern version = Pattern.compile("version.* [5678]\\.\\d+(\\.\\d+)");
if (lines.stream().noneMatch(line -> version.matcher(line).find())) {
Pattern oldVersion = Pattern.compile("2006"); // 2006 implies old macOS version
String lineSep = System.lineSeparator();
String message = lines.stream().anyMatch(line -> oldVersion.matcher(line).find())
? "old version of 'tidy' found on the PATH\n"
: "could not determine the version of 'tidy' on the PATH\n";
System.err.println(message + String.join(lineSep, lines));
}
}
} catch (IOException e) {
System.err.println("Could not execute 'tidy -version': " + e);
}
return tidyExePath;
}
@Override
public void report() {
if (files > 0) {
System.err.println("Tidy found errors in the generated HTML");
if (!errors.noErrors()) {
for (String s : errors.getErrors()) {
System.err.println(s);
}
System.err.println("Tidy output end.");
System.err.println();
System.err.println();
throw new RuntimeException("Tidy found errors in the generated HTML");
}
}
}
@Override
public boolean isOK() {
return (ok == files)
&& (overflow == 0)
&& (errs == 0)
&& (warns == 0)
&& (css == 0);
}
void checkLine(String line) {
Matcher m;
if (okPattern.matcher(line).matches()) {
ok++;
} else if ((m = countPattern.matcher(line)).matches() || (m = countPattern2.matcher(line)).matches()) {
warns += Integer.parseInt(m.group(1));
errs += Integer.parseInt(m.group(2));
if (m.group(3) != null)
overflow++;
} else if (guardPattern.matcher(line).matches()) {
boolean found = false;
for (Pattern p : patterns) {
if (p.matcher(line).matches()) {
errors.log("%s", line);
found = true;
count(p);
break;
}
}
if (!found)
errors.log("unrecognized line: " + line);
} else if (cssPattern.matcher(line).matches()) {
css++;
}
}
void count(Pattern p) {
Integer i = counts.get(p);
counts.put(p, (i == null) ? 1 : i + 1);
}
@Override
public void close() {
report();
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2024, 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.
*/
import jtreg.SkippedException;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Test framework for performing tests on the generated documentation.
*/
public class DocTester {
private final static String DIR = System.getenv("DOCS_JDK_IMAGE_DIR");
private static final Path firstCandidate = Path.of(System.getProperty("test.jdk"))
.getParent().resolve("docs");
public static Path resolveDocs() {
if (DIR != null && !DIR.isBlank() && Files.exists(Path.of(DIR))) {
return Path.of(DIR);
} else if (Files.exists(firstCandidate)) {
return firstCandidate;
}else {
throw new SkippedException("docs folder not found in either location");
}
}
}

View file

@ -0,0 +1,109 @@
#
# Copyright (c) 2015, 2021, 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.
#
#
# This is a temporary standalone makefile
#
BUILD_DIR := $(shell pwd)/build
CLASSES_DIR := ${BUILD_DIR}/classes
IMAGE_DIR := ${BUILD_DIR}/image
RUN_DIR := $(shell pwd)/run
CLASSPATH := ${JTREG_HOME}/lib/jtreg.jar:${JAVA_HOME}/lib/tools.jar
SRC_DIR := src/share/classes/
SOURCES := ${SRC_DIR}/jdk/test/failurehandler/*.java \
${SRC_DIR}/jdk/test/failurehandler/action/*.java \
${SRC_DIR}/jdk/test/failurehandler/jtreg/*.java \
${SRC_DIR}/jdk/test/failurehandler/value/*.java
CONF_DIR = src/share/conf
JAVA_RELEASE = 15
TARGET_JAR = ${IMAGE_DIR}/lib/jtregFailureHandler.jar
OS_NAME := $(shell uname -o 2>&1)
ifeq ("${OS_NAME}", "Cygwin")
BUILD_DIR := $(shell cygpath -m "${BUILD_DIR}")
CLASSES_DIR := $(shell cygpath -m "${CLASSES_DIR}")
IMAGE_DIR := $(shell cygpath -m "${IMAGE_DIR}")
RUN_DIR := $(shell cygpath -m "${RUN_DIR}")
SRC_DIR := $(shell cygpath -m "${SRC_DIR}")
JAVA_HOME := $(shell cygpath -m "${JAVA_HOME}")
JTREG_HOME := $(shell cygpath -m "${JTREG_HOME}")
CLASSPATH := $(shell cygpath -pm "${CLASSPATH}")
endif
all: clean test
check_defined = $(foreach 1,$1,$(__check_defined))
__check_defined = $(if $(value $1),, $(error $1 is not set))
classes: require_env
mkdir -p ${IMAGE_DIR}/bin ${IMAGE_DIR}/lib ${CLASSES_DIR}
"${JAVA_HOME}"/bin/javac -target ${JAVA_RELEASE} -source ${JAVA_RELEASE} \
-sourcepath "$(shell pwd)" \
-cp "${CLASSPATH}" \
-d ${CLASSES_DIR} \
${SOURCES}
"${JAVA_HOME}"/bin/jar cf "${TARGET_JAR}" -C "${CLASSES_DIR}" .
"${JAVA_HOME}"/bin/jar uf "${TARGET_JAR}" -C "${CONF_DIR}" .
#
# Use JTREG_TEST_OPTS for test VM options
# Use JTREG_TESTS for jtreg tests parameter
#
test: require_env build
rm -rf "${RUN_DIR}"
mkdir -p "${RUN_DIR}"
"${JTREG_HOME}"/bin/jtreg \
-jdk:"${JAVA_HOME}" \
${JTREG_TEST_OPTS} \
-timeout:0.1 -va -retain:all \
-noreport \
-agentvm \
-thd:"${TARGET_JAR}" \
-th:jdk.test.failurehandler.jtreg.GatherProcessInfoTimeoutHandler \
-thtimeout:0 \
-od:"${TARGET_JAR}" \
-o:jdk.test.failurehandler.jtreg.GatherDiagnosticInfoObserver \
-w:"${RUN_DIR}/JTwork" \
-r:"${RUN_DIR}/JTreport" \
$(if ${JTREG_TESTS}, ${JTREG_TESTS}, test) \
&& false || true
debug: JTREG_TEST_OPTS += "-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'"
debug: test
require_env:
$(call check_defined, JAVA_HOME)
$(call check_defined, JTREG_HOME)
clean:
rm -rf "${BUILD_DIR}" "${RUN_DIR}"
build: classes
.PHONY: all build classes test require_env clean
.DEFAULT: all

116
test/failure_handler/README Normal file
View file

@ -0,0 +1,116 @@
Copyright (c) 2015, 2024, 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.
DESCRIPTION
The purpose of this library is gathering diagnostic information on test
failures and timeouts. The library runs platform specific tools, which are
configured in the way described below. The collected data will be available
in HTML format next to JTR files.
The library uses JTHarness Observer and jtreg TimeoutHandler extensions points.
DEPENDENCES
The library requires jtreg 4b13+ and JDK 15+.
BUILDING
The library is built using the top level build-test-failure-handler target and
is automatically included in the test image and picked up by hotspot and jdk
test makefiles.
CONFIGURATION
Properties files are used to configure the library. They define which actions
to be performed in case of individual test failure or timeout. Each platform
family uses its own property file (named '<platform>.properties'). For platform
independent actions, 'common.properties' is used.
Actions to be performed on each failure are listed in 'environment' property.
Extra actions for timeouts are listed in 'onTimeout'.
Each action is defined via the following parameters:
- 'javaOnly' -- run the action only for java applications, false by default
- 'app' -- an application to run, mandatory parameter
- 'args' -- application command line arguments, none by default
- 'params' -- a structure which defines how an application should be run,
described below
Actions listed in 'onTimeout' are "patterned" actions. Besides the parameters
listed above, they also have 'pattern' parameter -- a string which will be
replaced by PID in 'args' parameter before action execution.
'params' structure has the following parameters:
- repeat -- how many times an action will be run, 1 by default
- pause -- delay in ms between iterations, 500 by default
- timeout -- time limitation for iteration in ms, 20 000 by default
- stopOnError -- if true, an action will be interrupted after the first error,
false by default
From '<platform>.properties', the library reads the following parameters
- 'config.execSuffix' -- a suffix for all binary application file names
- 'config.getChildren' -- a "patterned" action used to get the list of all
children
For simplicity we use parameter values inheritance. This means that we are
looking for the most specified parameter value. If we do not find it, we are
trying to find less specific value by reducing prefix.
For example, if properties contains 'p1=A', 'a.p1=B', 'a.b.p1=C', then
parameter 'p1' will be:
- 'C' for 'a.b.c'
- 'B' for 'a.c'
- 'A' for 'b.c'
RUNNING
To enable the library in jtreg, the following options should be set:
- '-timeoutHandlerDir' points to the built jar ('jtregFailureHandler.jar')
- '-observerDir' points to the built jar
- '-timeoutHandler' equals to jdk.test.failurehandler.jtreg.GatherProcessInfoTimeoutHandler
- '-observer' equals to jdk.test.failurehandler.jtreg.GatherDiagnosticInfoObserver
In case of environment issues during an action execution, such as missing
application, hung application, lack of disk space, etc, the corresponding
warning appears and the library proceeds to next action.
EXAMPLES
$ ${JTREG_HOME}/bin/jtreg -jdk:${JAVA_HOME} \
-timeoutHandlerDir:./image/lib/jtregFailureHandler.jar \
-observerDir:./image/lib/jtregFailureHandler.jar \
-timeoutHandler:jdk.test.failurehandler.jtreg.GatherProcessInfoTimeoutHandler\
-observer:jdk.test.failurehandler.jtreg.GatherDiagnosticInfoObserver \
${WS}/hotspot/test/
TESTING
There are a few make targets for testing the failure_handler itself.
- Everything in `test/failure_handler/Makefile`
- The `test-failure-handler` target in `make/RunTests.gmk`
- The `test` target in `make/test/BuildFailureHandler.gmk`
All of these targets are written for manual testing only. They rely on
manual inspection of generated artifacts and cannot be run as part of a CI.
They are tests which timeout, crash, fail in various ways and you can check
the failure_handler output yourself. They might also leave processes running
on your machine so be extra careful about cleaning up.

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, 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.
*/
package jdk.test.failurehandler;
import java.nio.file.Path;
public interface CoreInfoGatherer {
void gatherCoreInfo(HtmlSection section, Path core);
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;
public class ElapsedTimePrinter implements AutoCloseable {
private final String name;
private final PrintWriter out;
private final Stopwatch stopwatch;
public ElapsedTimePrinter(Stopwatch stopwatch, String name,
PrintWriter out) {
this.stopwatch = stopwatch;
this.name = name;
this.out = out;
stopwatch.start();
}
@Override
public void close() {
stopwatch.stop();
out.printf("%s took %d s%n", name,
TimeUnit.NANOSECONDS.toSeconds(stopwatch.getElapsedTimeNs()));
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler;
public interface EnvironmentInfoGatherer {
void gatherEnvironmentInfo(HtmlSection section);
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2015, 2021, 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.
*/
package jdk.test.failurehandler;
import jdk.test.failurehandler.action.ActionHelper;
import jdk.test.failurehandler.value.InvalidValueException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.util.Properties;
public final class GathererFactory {
private final Path workdir;
private final Path[] jdks;
private final PrintWriter log;
private final String osName;
public GathererFactory(String osName, Path workdir, PrintWriter log, Path... jdks) {
this.osName = osName;
this.workdir = workdir;
this.log = log;
this.jdks = jdks;
}
public EnvironmentInfoGatherer getEnvironmentInfoGatherer() {
return create();
}
public ProcessInfoGatherer getProcessInfoGatherer() {
return create();
}
public CoreInfoGatherer getCoreInfoGatherer() {
return create();
}
private ToolKit create() {
Properties osProperty = Utils.getProperties(osName);
try {
ActionHelper helper = new ActionHelper(workdir, "config", osProperty, jdks);
// os-specific action set must be last, b/c they can kill the process
return new ToolKit(helper, log, "common", osName);
} catch (InvalidValueException e) {
throw new IllegalStateException("can't create tool kit", e);
}
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2015, 2026, 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.
*/
package jdk.test.failurehandler;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
public class HtmlPage implements AutoCloseable {
static final String STYLE_SHEET_FILENAME = "failure-handler-style.css";
static final String SCRIPT_FILENAME = "failure-handler-script.js";
private final PrintWriter writer;
private final HtmlSection rootSection;
/**
* Constructs a {@code HtmlPage}
*
* @param dir The directory into which the HTML file and related resources will be created
* @param htmlFileName The HTML file name
* @param append if {@code true} then the content will be appended to the file represented
* by the {@code htmlFileName}, else the {@code htmlFileName} will be overwritten
* with the new content
* @throws IllegalArgumentException if {@code dir} is not a directory or if the
* {@code htmlFileName} is {@linkplain String#isBlank() blank}
* @throws IOException if there is an error constructing file resource(s) for this HTML page
*/
public HtmlPage(final Path dir, final String htmlFileName, final boolean append)
throws IOException {
Objects.requireNonNull(dir, "directory cannot be null");
Objects.requireNonNull(htmlFileName, "HTML file name cannot be null");
if (!Files.isDirectory(dir)) {
throw new IllegalArgumentException(dir + " is not a directory");
}
if (htmlFileName.isBlank()) {
throw new IllegalArgumentException("HTML file name cannot be blank");
}
final FileWriter fileWriter = new FileWriter(dir.resolve(htmlFileName).toFile(), append);
this.writer = new PrintWriter(fileWriter, true);
createScriptFile(dir);
createStyleSheetFile(dir);
rootSection = new HtmlSection(writer);
}
@Override
public void close() {
writer.close();
}
public HtmlSection getRootSection() {
return rootSection;
}
private static void createStyleSheetFile(final Path destDir) throws IOException {
final Path styleSheet = destDir.resolve(STYLE_SHEET_FILENAME);
if (Files.exists(styleSheet)) {
return;
}
final String content = """
div { display:none;}
""";
Files.writeString(styleSheet, content);
}
private static void createScriptFile(final Path destDir) throws IOException {
final Path script = destDir.resolve(SCRIPT_FILENAME);
if (Files.exists(script)) {
return;
}
final String content = """
function doShow(e) {
while (e != null) {
if (e.tagName == 'DIV') {
e.style.display = 'block';
}
e = e.parentNode;
}
}
function showHandler(event) {
elementId = this.dataset.show;
elementToShow = document.getElementById(elementId);
doShow(elementToShow);
}
function toggleHandler(event) {
toggleElementId = this.dataset.toggle;
elementToToggle = document.getElementById(toggleElementId);
d = elementToToggle.style.display;
if (d == 'block') {
elementToToggle.style.display = 'none';
} else {
doShow(elementToToggle);
}
}
function bodyLoadHandler() {
const index = location.href.indexOf("#");
if (index != -1) {
doShow(document.getElementById(location.href.substring(index + 1)));
}
// elements that require the "toggleHandler" function to be registered
// as an event handler for the onclick event
const requiringToggleHandler = document.querySelectorAll("[data-toggle]");
for (const e of requiringToggleHandler) {
e.addEventListener("click", toggleHandler);
}
// elements that require the "showHandler" function to be registered
// as an event handler for the onclick event
const requiringShowHandler = document.querySelectorAll("[data-show]");
for (const e of requiringShowHandler) {
e.addEventListener("click", showHandler);
}
}
// register a onload event handler
window.addEventListener("DOMContentLoaded", bodyLoadHandler);
""";
Files.writeString(script, content);
}
}

View file

@ -0,0 +1,220 @@
/*
* Copyright (c) 2015, 2026, 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.
*/
package jdk.test.failurehandler;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class HtmlSection {
protected final HtmlSection rootSection;
protected final String id;
protected final String name;
public PrintWriter getWriter() {
return textWriter;
}
protected final PrintWriter pw;
protected final PrintWriter textWriter;
protected boolean closed;
private HtmlSection child;
public HtmlSection(PrintWriter pw) {
this(pw, "", null, null);
}
private HtmlSection(PrintWriter pw, String id, String name, HtmlSection rootSection) {
this.pw = pw;
textWriter = new PrintWriter(new HtmlFilterWriter(pw), true);
this.id = id;
this.name = name;
child = null;
// main
if (rootSection == null) {
this.rootSection = this;
this.pw.println("<html>");
this.pw.println("<head>");
this.pw.println(
"<link href=\"" + HtmlPage.STYLE_SHEET_FILENAME + "\" rel=\"stylesheet\" type=\"text/css\" />");
this.pw.println(
"<script src=\"" + HtmlPage.SCRIPT_FILENAME + "\" type=\"text/javascript\" ></script>");
this.pw.println("</head>");
this.pw.println("<body>");
} else {
this.rootSection = rootSection;
this.pw.print("<ul>");
}
}
public HtmlSection createChildren(String section) {
if (child != null) {
if (child.name.equals(section)) {
return child;
}
child.close();
}
child = new SubSection(this, section, rootSection);
return child;
}
protected final void removeChild(HtmlSection child) {
if (this.child == child) {
this.child = null;
}
}
public void close() {
closeChild();
if (closed) {
return;
}
closed = true;
if (rootSection == this) {
pw.println("</body>");
pw.println("</html>");
pw.close();
} else {
pw.println("</ul>");
}
}
protected final void closeChild() {
if (child != null) {
child.close();
child = null;
}
}
public void link(HtmlSection section, String child, String name) {
String path = section.id;
if (path.isEmpty()) {
path = child;
} else if (child != null) {
path = String.format("%s.%s", path, child);
}
pw.printf("<a href=\"#%1$s\" data-show=\"%1$s\" >%2$s</a>%n",
path, name);
}
/**
* Creates a {@code <a href></a>} link with {@code targetAddress} being the value for {@code href}
* and the {@code linkText} being the text for the link.
*
* @param targetAddress the target address
* @param linkText the text for the link
*/
public void createLink(String targetAddress, String linkText) {
pw.printf("<a href=\"%1$s\">%2$s</a>%n", targetAddress, linkText);
}
public HtmlSection createChildren(String[] sections) {
int i = 0;
int n = sections.length;
HtmlSection current = this;
for (; i < n && current.child != null;
++i, current = current.child) {
if (!sections[i].equals(current.child.name)) {
break;
}
}
for (; i < n; ++i) {
current = current.createChildren(sections[i]);
}
return current;
}
private static class SubSection extends HtmlSection {
private final HtmlSection parent;
public SubSection(HtmlSection parent, String name,
HtmlSection rootSection) {
super(parent.pw,
parent.id.isEmpty()
? name
: String.format("%s.%s", parent.id, name),
name, rootSection);
this.parent = parent;
pw.printf("<li><a name='%1$s'/><a href='#%1$s' data-toggle=\"%1$s\" >%2$s</a><div id='%1$s'><code><pre>",
id, name);
}
@Override
public void close() {
closeChild();
if (closed) {
return;
}
pw.print("</pre></code></div></li><!-- " + id + "-->");
parent.removeChild(this);
super.close();
}
}
private static class HtmlFilterWriter extends FilterWriter {
public HtmlFilterWriter(PrintWriter pw) {
super(pw);
}
@Override
public void write(int c) throws IOException {
switch (c) {
case '<':
super.write("&lt;", 0, 4);
break;
case '>':
super.write("&gt;", 0, 4);
break;
case '"':
super.write("&quot;", 0, 5);
break;
case '&':
super.write("&amp;", 0, 4);
break;
default:
super.write(c);
}
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
for (int i = off; i < len; ++i){
write(cbuf[i]);
}
}
@Override
public void write(String str, int off, int len) throws IOException {
for (int i = off; i < len; ++i){
write(str.charAt(i));
}
}
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler;
public interface ProcessInfoGatherer {
void gatherProcessInfo(HtmlSection section, long pid);
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler;
public final class Stopwatch {
protected boolean isResultAvailable;
protected boolean isRunning;
private long startTimeNs;
private long stopTimeNs;
public Stopwatch() {
isResultAvailable = false;
}
/**
* Starts measuring time.
*/
public void start() {
startTimeNs = System.nanoTime();
isRunning = true;
}
/**
* Stops measuring time.
*/
public void stop() {
if (!isRunning) {
throw new IllegalStateException(" hasn't been started");
}
stopTimeNs = System.nanoTime();
isRunning = false;
isResultAvailable = true;
}
/**
* @return time in nanoseconds measured between
* calls of {@link #start()} and {@link #stop()} methods.
*
* @throws IllegalStateException if called without preceding
* {@link #start()} {@link #stop()} method
*/
public long getElapsedTimeNs() {
if (isRunning) {
throw new IllegalStateException("hasn't been stopped");
}
if (!isResultAvailable) {
throw new IllegalStateException("was not run");
}
return stopTimeNs - startTimeNs;
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2015, 2021, 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.
*/
package jdk.test.failurehandler;
import jdk.test.failurehandler.action.ActionSet;
import jdk.test.failurehandler.action.ActionHelper;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Deque;
import java.util.zip.GZIPInputStream;
public class ToolKit implements EnvironmentInfoGatherer, ProcessInfoGatherer, CoreInfoGatherer {
private final List<ActionSet> actions = new ArrayList<>();
private final ActionHelper helper;
private final PrintWriter log;
public ToolKit(ActionHelper helper, PrintWriter log, String... names) {
this.helper = helper;
this.log = log;
for (String name : names) {
actions.add(new ActionSet(helper, log, name));
}
}
@Override
public void gatherEnvironmentInfo(HtmlSection section) {
for (ActionSet set : actions) {
set.gatherEnvironmentInfo(section);
}
}
@Override
public void gatherCoreInfo(HtmlSection section, Path core) {
if (core.getFileName().toString().endsWith(".gz")) {
Path unpackedCore = Path.of(core.toString().replace(".gz", ""));
try (GZIPInputStream gzis = new GZIPInputStream(Files.newInputStream(core))) {
Files.copy(gzis, unpackedCore);
for (ActionSet set : actions) {
set.gatherCoreInfo(section, unpackedCore);
}
Files.delete(unpackedCore);
} catch (IOException ioe) {
log.printf("Unexpected exception whilc opening %s", core.getFileName().toString());
ioe.printStackTrace(log);
}
} else {
for (ActionSet set : actions) {
set.gatherCoreInfo(section, core);
}
}
}
@Override
public void gatherProcessInfo(HtmlSection section, long pid) {
// as some of actions can kill a process, we need to get children of all
// test process first, and run the actions starting from the leaves
// and going up by the process tree
Deque<Long> orderedPids = new LinkedList<>();
Queue<Long> testPids = new LinkedList<>();
testPids.add(pid);
HtmlSection ptreeSection = section.createChildren("test_processes");
for (Long p = testPids.poll(); p != null; p = testPids.poll()) {
orderedPids.addFirst(p);
List<Long> children = helper.getChildren(ptreeSection, p);
if (!children.isEmpty()) {
HtmlSection s = ptreeSection.createChildren("" + p);
for (Long c : children) {
s.link(section, c.toString(), c.toString());
}
testPids.addAll(children);
}
}
for (Long p : orderedPids) {
for (ActionSet set : actions) {
set.gatherProcessInfo(section, p);
}
}
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
public final class Utils {
private static final int BUFFER_LENGTH = 1024;
public static String prependPrefix(String prefix, String name) {
return (prefix == null || prefix.isEmpty())
? name
: (name == null || name.isEmpty())
? prefix
: String.format("%s.%s", prefix, name);
}
public static void copyStream(InputStream in, OutputStream out)
throws IOException {
int n;
byte[] buffer = new byte[BUFFER_LENGTH];
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
out.flush();
}
public static void copyStream(Reader in, Writer out)
throws IOException {
int n;
char[] buffer = new char[BUFFER_LENGTH];
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
out.flush();
}
public static Properties getProperties(String name) {
Properties properties = new Properties();
String resourceName = String.format(
"/%s.%s", name.toLowerCase(), "properties");
InputStream stream = Utils.class.getResourceAsStream(resourceName);
if (stream == null) {
throw new IllegalStateException(String.format(
"resource '%s' doesn't exist%n", resourceName));
}
try {
try {
properties.load(stream);
} finally {
stream.close();
}
} catch (IOException e) {
throw new IllegalStateException(String.format(
"can't read resource '%s' : %s%n",
resourceName, e.getMessage()), e);
}
return properties;
}
private Utils() { }
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.action;
import jdk.test.failurehandler.HtmlSection;
public interface Action {
boolean isJavaOnly();
HtmlSection getSection(HtmlSection section);
ActionParameters getParameters();
}

View file

@ -0,0 +1,404 @@
/*
* Copyright (c) 2015, 2024, 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.
*/
package jdk.test.failurehandler.action;
import jdk.test.failurehandler.value.InvalidValueException;
import jdk.test.failurehandler.value.Value;
import jdk.test.failurehandler.value.ValueHandler;
import jdk.test.failurehandler.HtmlSection;
import jdk.test.failurehandler.Stopwatch;
import jdk.test.failurehandler.Utils;
import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
public class ActionHelper {
private final Path workDir;
@Value(name = "execSuffix")
private String executableSuffix = "";
private Path[] paths;
private final PatternAction getChildren;
public ActionHelper(Path workDir, String prefix, Properties properties,
Path... jdks) throws InvalidValueException {
this.workDir = workDir.toAbsolutePath();
getChildren = new PatternAction(null,
Utils.prependPrefix(prefix, "getChildren"), properties);
ValueHandler.apply(this, properties, prefix);
String[] pathStrings = System.getenv("PATH").split(File.pathSeparator);
paths = new Path[pathStrings.length];
for (int i = 0; i < paths.length; ++i) {
paths[i] = Paths.get(pathStrings[i]);
}
addJdks(jdks);
}
public List<Long> getChildren(HtmlSection section, long pid) {
String pidStr = "" + pid;
ProcessBuilder pb = getChildren.prepareProcess(section, this, pidStr);
HtmlSection childrenSection = getChildren.getSection(section);
PrintWriter log = childrenSection.getWriter();
CharArrayWriter writer = new CharArrayWriter();
ExitCode code = run(childrenSection, log, writer, pb, getChildren.getParameters());
Reader output = new CharArrayReader(writer.toCharArray());
if (!ExitCode.OK.equals(code)) {
log.println("WARNING: get children pids action failed");
try {
Utils.copyStream(output, log);
} catch (IOException e) {
e.printStackTrace(log);
}
return Collections.emptyList();
}
List<Long> result = new ArrayList<>();
try {
try (BufferedReader reader = new BufferedReader(output)) {
String line;
while ((line = reader.readLine()) != null) {
String value = line.trim();
if (value.isEmpty()) {
// ignore empty lines
continue;
}
try {
result.add(Long.valueOf(value));
} catch (NumberFormatException e) {
log.printf("WARNING: can't parse child pid %s : %s%n",
line, e.getMessage());
e.printStackTrace(log);
}
}
}
} catch (IOException e) {
e.printStackTrace(log);
}
return result;
}
public ProcessBuilder prepareProcess(PrintWriter log, String app,
String... args) {
File appBin = findApp(app);
if (appBin == null) {
log.printf("ERROR: can't find %s in %s.%n",
app, Arrays.toString(paths));
return null;
}
List<String> command = new ArrayList<>(args.length + 1);
command.add(appBin.toString());
Collections.addAll(command, args);
return new ProcessBuilder()
.command(command)
.directory(workDir.toFile());
}
public File findApp(String app) {
String name = app + executableSuffix;
for (Path pathElem : paths) {
File result = pathElem.resolve(name).toFile();
if (result.exists()) {
return result;
}
}
return null;
}
private void addJdks(Path[] jdkPaths) {
if (jdkPaths != null && jdkPaths.length != 0) {
Path[] result = new Path[jdkPaths.length + paths.length];
for (int i = 0; i < jdkPaths.length; ++i) {
result[i] = jdkPaths[i].resolve("bin");
}
System.arraycopy(paths, 0, result, jdkPaths.length, paths.length);
paths = result;
}
}
private ExitCode run(HtmlSection section, PrintWriter log, Writer out, ProcessBuilder pb,
ActionParameters params) {
char[] lineChars = new char[40];
Arrays.fill(lineChars, '-');
String line = new String(lineChars);
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
log.printf("%s%n[%tF %<tT] %s timeout=%s in %s%n%1$s%n", line, new Date(), pb.command(), params.timeout, pb.directory());
Process process;
KillerTask killer;
ExitCode result = ExitCode.NEVER_STARTED;
try {
process = pb.start();
killer = new KillerTask(process);
killer.schedule(params.timeout);
Utils.copyStream(new InputStreamReader(process.getInputStream()),
out);
try {
result = new ExitCode(process.waitFor());
} catch (InterruptedException e) {
log.println("WARNING: interrupted when waiting for the tool:%n");
e.printStackTrace(log);
} finally {
killer.cancel();
}
if (killer.hasTimedOut()) {
log.printf(
"WARNING: tool timed out: killed process after %d ms%n",
params.timeout);
result = ExitCode.TIMED_OUT;
}
} catch (IOException e) {
log.printf("WARNING: caught IOException while running tool%n");
e.printStackTrace(log);
result = ExitCode.LAUNCH_ERROR;
}
stopwatch.stop();
log.printf("%s%n[%tF %<tT] exit code: %d time: %d ms%n%1$s%n",
line, new Date(), result.value,
TimeUnit.NANOSECONDS.toMillis(stopwatch.getElapsedTimeNs()));
// upon successful completion of the action, generate links to any successArtifacts
// that have been declared for this action
if (ExitCode.OK.equals(result) && params.successArtifacts != null) {
final StringTokenizer t = new StringTokenizer(params.successArtifacts, ",");
while (t.hasMoreTokens()) {
final String artifactPath = t.nextToken().trim();
if (artifactPath.isEmpty()) {
continue;
}
// create a link to the artifact
section.createLink(artifactPath, artifactPath);
}
}
return result;
}
public void runPatternAction(SimpleAction action, HtmlSection section) {
if (action != null) {
HtmlSection subSection = action.getSection(section);
PrintWriter log = subSection.getWriter();
ProcessBuilder pb = action.prepareProcess(log, this);
exec(subSection, pb, action.getParameters());
}
}
public void runPatternAction(PatternAction action, HtmlSection section,
String value) {
if (action != null) {
ProcessBuilder pb = action.prepareProcess(section, this, value);
HtmlSection subSection = action.getSection(section);
exec(subSection, pb, action.getParameters());
}
}
public boolean isJava(long pid, PrintWriter log) {
ProcessBuilder pb = prepareProcess(log, "jps", "-q");
if (pb == null) {
return false;
}
pb.redirectErrorStream(true);
boolean result = false;
String pidStr = "" + pid;
try {
Process process = pb.start();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null){
if (pidStr.equals(line)) {
result = true;
}
}
}
process.waitFor();
} catch (IOException e) {
log.printf("WARNING: can't run jps : %s%n", e.getMessage());
e.printStackTrace(log);
} catch (InterruptedException e) {
log.printf("WARNING: interrupted%n");
e.printStackTrace(log);
}
return result;
}
private static class KillerTask extends TimerTask {
private static final Timer WATCHDOG = new Timer("WATCHDOG", true);
private final Process process;
private boolean timedOut;
public KillerTask(Process process) {
this.process = process;
}
public void run() {
try {
process.exitValue();
} catch (IllegalThreadStateException e) {
process.destroyForcibly();
timedOut = true;
}
}
public boolean hasTimedOut() {
return timedOut;
}
public void schedule(long timeout) {
if (timeout > 0) {
WATCHDOG.schedule(this, timeout);
}
}
}
private void exec(HtmlSection section, ProcessBuilder process,
ActionParameters params) {
if (process == null) {
return;
}
PrintWriter sectionWriter = section.getWriter();
if (params.repeat > 1) {
// hold on to the original command and successArtifacts values which potentially
// contain the %iterCount token, since we need to replace it with different value
// on each iteration
String originalSuccessArtifacts = params.successArtifacts;
List<String> originalCommand = process.command();
for (int i = 0, n = params.repeat; i < n; ++i) {
HtmlSection iteration = section.createChildren(
String.format("iteration_%d", i));
PrintWriter writer = iteration.getWriter();
// use the original values with the token (if any)
params.successArtifacts = originalSuccessArtifacts;
process.command(originalCommand);
// replace the %iterCount token (if any)
prepareIteration(i, process, params);
ExitCode exitCode = run(section, writer, writer, process, params);
if (params.stopOnError && !ExitCode.OK.equals(exitCode)) {
sectionWriter.printf(
"ERROR: non zero exit code[%d] -- break.",
exitCode.value);
break;
}
// sleep, if this is not the last iteration
if (i < n - 1) {
try {
Thread.sleep(params.pause);
} catch (InterruptedException e) {
sectionWriter.printf(
"WARNING: interrupted while sleeping between invocations");
e.printStackTrace(sectionWriter);
}
}
}
} else {
prepareIteration(0, process, params);
run(section, section.getWriter(), section.getWriter(), process, params);
}
}
// replaces the occurrences of %iterCount from the process builder command/arguments
// and the action params' "successArtifacts" paths, with the iteration count
private void prepareIteration(int iterationCount, ProcessBuilder pb,
ActionParameters actionParams) {
List<String> command = new ArrayList<>();
for (String arg : pb.command()) {
arg = arg.replaceAll("%iterCount", String.valueOf(iterationCount)) ;
command.add(arg);
}
pb.command(command);
String successArtifacts = actionParams.successArtifacts;
if (successArtifacts != null) {
actionParams.successArtifacts = successArtifacts.replaceAll("%iterCount",
String.valueOf(iterationCount));
}
}
/**
* Special values for prepareProcess exit code.
*
* <p>Can we clash with normal codes?
* On Linux, only [0..255] are returned.
* On Windows, prepareProcess exit codes are stored in unsigned int.
* On MacOSX no limits (except it should fit C int type)
* are defined in the exit() man pages.
*/
private static class ExitCode {
/** Process exits gracefully */
public static final ExitCode OK = new ExitCode(0);
/** Error launching prepareProcess */
public static final ExitCode LAUNCH_ERROR = new ExitCode(-1);
/** Application prepareProcess has been killed by watchdog due to timeout */
public static final ExitCode TIMED_OUT = new ExitCode(-2);
/** Application prepareProcess has never been started due to program logic */
public static final ExitCode NEVER_STARTED = new ExitCode(-3);
public final int value;
private ExitCode(int value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ExitCode exitCode = (ExitCode) o;
return value == exitCode.value;
}
@Override
public int hashCode() {
return value;
}
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.action;
import jdk.test.failurehandler.value.DefaultValue;
import jdk.test.failurehandler.value.Value;
public class ActionParameters {
@Value (name = "repeat")
@DefaultValue (value = "1")
public int repeat = 1;
@Value (name = "pause")
@DefaultValue (value = "500")
public long pause = 500;
@Value (name = "stopOnError")
@DefaultValue (value = "false")
public boolean stopOnError = false;
@Value (name = "timeout")
@DefaultValue (value = "" + 20_000L)
public long timeout = -1L;
@Value (name = "successArtifacts")
@DefaultValue (value = "")
public String successArtifacts = "";
public ActionParameters() { }
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2015, 2024, 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.
*/
package jdk.test.failurehandler.action;
import jdk.test.failurehandler.CoreInfoGatherer;
import jdk.test.failurehandler.ProcessInfoGatherer;
import jdk.test.failurehandler.EnvironmentInfoGatherer;
import jdk.test.failurehandler.HtmlSection;
import jdk.test.failurehandler.Utils;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.zip.GZIPInputStream;
public class ActionSet implements ProcessInfoGatherer, EnvironmentInfoGatherer, CoreInfoGatherer {
private static final String ENVIRONMENT_PROPERTY = "environment";
private static final String ON_PID_PROPERTY = "onTimeout";
private static final String CORES_PROPERTY = "cores";
private final ActionHelper helper;
public String getName() {
return name;
}
private final String name;
private final List<SimpleAction> environmentActions;
private final List<PatternAction> processActions;
private final List<PatternAction> coreActions;
public ActionSet(ActionHelper helper, PrintWriter log, String name) {
this.helper = helper;
this.name = name;
Properties p = Utils.getProperties(name);
environmentActions = getSimpleActions(log, p, ENVIRONMENT_PROPERTY);
processActions = getPatternActions(log, p, ON_PID_PROPERTY);
coreActions = getPatternActions(log, p, CORES_PROPERTY);
}
private List<SimpleAction> getSimpleActions(PrintWriter log, Properties p,
String key) {
String[] tools = getTools(log, p, key);
List<SimpleAction> result = new ArrayList<>(tools.length);
for (String tool : tools) {
try {
SimpleAction action = new SimpleAction(
Utils.prependPrefix(name, tool), tool, p);
result.add(action);
} catch (Exception e) {
log.printf("ERROR: %s cannot be created : %s %n",
tool, e.getMessage());
e.printStackTrace(log);
}
}
return result;
}
private List<PatternAction> getPatternActions(PrintWriter log,
Properties p, String key) {
String[] tools = getTools(log, p, key);
List<PatternAction> result = new ArrayList<>(tools.length);
for (String tool : tools) {
try {
PatternAction action = new PatternAction(
Utils.prependPrefix(name, tool), tool, p);
result.add(action);
} catch (Exception e) {
log.printf("ERROR: %s cannot be created : %s %n",
tool, e.getMessage());
e.printStackTrace(log);
}
}
return result;
}
private String[] getTools(PrintWriter writer, Properties p, String key) {
String value = p.getProperty(key);
if (value == null) {
writer.printf("ERROR: '%s' property is not set%n", key);
return new String[]{};
}
if (value.isEmpty()) {
return new String[]{};
}
return value.split(" ");
}
@Override
public void gatherProcessInfo(HtmlSection section, long pid) {
String pidStr = "" + pid;
for (PatternAction action : processActions) {
if (action.isJavaOnly()) {
if (helper.isJava(pid, section.getWriter())) {
helper.runPatternAction(action, section, pidStr);
}
} else {
helper.runPatternAction(action, section, pidStr);
}
}
}
@Override
public void gatherEnvironmentInfo(HtmlSection section) {
for (SimpleAction action : environmentActions) {
helper.runPatternAction(action, section);
}
}
@Override
public void gatherCoreInfo(HtmlSection section, Path core) {
for (PatternAction action : coreActions) {
helper.runPatternAction(action, section, core.toString());
}
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.action;
import jdk.test.failurehandler.value.InvalidValueException;
import jdk.test.failurehandler.HtmlSection;
import jdk.test.failurehandler.value.Value;
import jdk.test.failurehandler.value.ValueHandler;
import java.util.Properties;
public class PatternAction implements Action {
@Value(name = "pattern")
private String pattern = null;
private final SimpleAction action;
private final String[] originalArgs;
private final String originalSuccessArtifacts;
public PatternAction(String id, Properties properties)
throws InvalidValueException {
this(id, id, properties);
}
public PatternAction(String name, String id, Properties properties)
throws InvalidValueException {
action = new SimpleAction(name != null ? ("pattern." + name) : "pattern", id, properties);
ValueHandler.apply(this, properties, id);
originalArgs = action.args.clone();
ActionParameters params = action.getParameters();
// just like the "args" the "successArtifacts" param can also contain pattern that
// this PatternAction will (sometimes repeatedly) replace, so we keep track of
// the original (un-replaced text)
originalSuccessArtifacts = params == null ? null : params.successArtifacts;
}
public ProcessBuilder prepareProcess(HtmlSection section,
ActionHelper helper, String value) {
action.sections[0] = value;
section = getSection(section);
String[] args = action.args;
System.arraycopy(originalArgs, 0, args, 0, originalArgs.length);
for (int i = 0, n = args.length; i < n; ++i) {
args[i] = args[i].replace(pattern, value) ;
}
for (int i = 0, n = args.length; i < n; ++i) {
args[i] = args[i].replace("%java", helper.findApp("java").getAbsolutePath());
}
// replace occurrences of the pattern in the "successArtifacts" param
if (originalSuccessArtifacts != null) {
action.getParameters().successArtifacts = originalSuccessArtifacts.replaceAll(pattern,
value);
}
return action.prepareProcess(section.getWriter(), helper);
}
@Override
public HtmlSection getSection(HtmlSection section) {
return action.getSection(section);
}
@Override
public ActionParameters getParameters() {
return action.getParameters();
}
@Override
public boolean isJavaOnly() {
return action.isJavaOnly();
}
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.action;
import jdk.test.failurehandler.HtmlSection;
import jdk.test.failurehandler.value.InvalidValueException;
import jdk.test.failurehandler.value.SubValues;
import jdk.test.failurehandler.value.Value;
import jdk.test.failurehandler.value.ValueHandler;
import jdk.test.failurehandler.value.DefaultValue;
import java.io.PrintWriter;
import java.util.Properties;
public class SimpleAction implements Action {
/* package-private */ final String[] sections;
@Value(name = "javaOnly")
@DefaultValue(value = "false")
private boolean javaOnly = false;
@Value (name = "app")
private String app = null;
@Value (name = "args")
@DefaultValue (value = "")
/* package-private */ String[] args = new String[]{};
@SubValues(prefix = "params")
private final ActionParameters params;
public SimpleAction(String id, Properties properties)
throws InvalidValueException {
this(id, id, properties);
}
public SimpleAction(String name, String id, Properties properties)
throws InvalidValueException {
sections = name.split("\\.");
this.params = new ActionParameters();
ValueHandler.apply(this, properties, id);
}
public ProcessBuilder prepareProcess(PrintWriter log, ActionHelper helper) {
ProcessBuilder process = helper.prepareProcess(log, app, args);
if (process != null) {
process.redirectErrorStream(true);
}
return process;
}
@Override
public boolean isJavaOnly() {
return javaOnly;
}
@Override
public HtmlSection getSection(HtmlSection section) {
return section.createChildren(sections);
}
@Override
public ActionParameters getParameters() {
return params;
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2015, 2026, 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.
*/
package jdk.test.failurehandler.jtreg;
import com.sun.javatest.Harness;
import com.sun.javatest.Parameters;
import com.sun.javatest.TestResult;
import com.sun.javatest.regtest.config.RegressionParameters;
import jdk.test.failurehandler.*;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;
/**
* The jtreg test execution observer, which gathers info about
* system and dumps it to a file.
*/
public class GatherDiagnosticInfoObserver implements Harness.Observer {
public static final String LOG_FILENAME = "environment.log";
public static final String ENVIRONMENT_OUTPUT = "environment.html";
public static final String CORES_OUTPUT = "cores.html";
private Path compileJdk;
private Path testJdk;
/*
* The harness calls this method after each test.
*/
@Override
public void finishedTest(TestResult tr) {
if (!tr.getStatus().isError() && !tr.getStatus().isFailed()) {
return;
}
String jtrFile = tr.getFile().toString();
final Path workDir = Paths.get(
jtrFile.substring(0, jtrFile.lastIndexOf('.')));
workDir.toFile().mkdir();
String name = getClass().getName();
PrintWriter log1;
boolean needClose = false;
try {
log1 = new PrintWriter(new FileWriter(
workDir.resolve(LOG_FILENAME).toFile(), true), true);
needClose = true;
} catch (IOException e) {
log1 = new PrintWriter(System.out);
log1.printf("ERROR: %s cannot open log file %s", name,
LOG_FILENAME);
e.printStackTrace(log1);
}
final PrintWriter log = log1;
try {
log.printf("%s ---%n", name);
GathererFactory gathererFactory = new GathererFactory(
OS.current().family, workDir, log,
testJdk, compileJdk);
gatherEnvInfo(workDir, name, log,
gathererFactory.getEnvironmentInfoGatherer());
// generate a cores.html file after parsing the core dump files (if any)
List<Path> coreFiles;
try (Stream<Path> paths = Files.walk(workDir)) {
coreFiles = paths.filter(Files::isRegularFile)
.filter(f -> (f.getFileName().toString().contains("core")
|| f.getFileName().toString().contains("mdmp")))
.toList();
}
gatherCoreInfo(workDir, name, coreFiles, log, gathererFactory.getCoreInfoGatherer());
} catch (Throwable e) {
log.printf("ERROR: exception in observer %s:", name);
e.printStackTrace(log);
} finally {
log.printf("--- %s%n", name);
if (needClose) {
log.close();
} else {
log.flush();
}
}
}
private void gatherCoreInfo(Path workDir, String name, List<Path> coreFiles,
PrintWriter log, CoreInfoGatherer gatherer) {
if (coreFiles.isEmpty()) {
return;
}
try (HtmlPage html = new HtmlPage(workDir, CORES_OUTPUT, true)) {
try (ElapsedTimePrinter timePrinter
= new ElapsedTimePrinter(new Stopwatch(), name, log)) {
// gather information from the contents of each core file
for (Path coreFile : coreFiles) {
gatherer.gatherCoreInfo(html.getRootSection(), coreFile);
}
}
} catch (Throwable e) {
log.printf("ERROR: exception in %s observer while gathering information from"
+ " core dump file", name);
e.printStackTrace(log);
}
}
private void gatherEnvInfo(Path workDir, String name, PrintWriter log,
EnvironmentInfoGatherer gatherer) {
try (HtmlPage html = new HtmlPage(workDir, ENVIRONMENT_OUTPUT, true)) {
try (ElapsedTimePrinter timePrinter
= new ElapsedTimePrinter(new Stopwatch(), name, log)) {
gatherer.gatherEnvironmentInfo(html.getRootSection());
}
} catch (Throwable e) {
log.printf("ERROR: exception in observer on getting environment "
+ "information %s:", name);
e.printStackTrace(log);
}
}
/*
* The harness calls this method one time per run, not per test.
*/
@Override
public void startingTestRun(Parameters params) {
RegressionParameters rp = (RegressionParameters) params;
compileJdk = rp.getCompileJDK().getAbsoluteFile().toPath();
testJdk = rp.getTestJDK().getAbsoluteFile().toPath();
}
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2015, 2026, 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.
*/
package jdk.test.failurehandler.jtreg;
import com.sun.javatest.regtest.TimeoutHandler;
import jdk.test.failurehandler.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
/**
* A timeout handler for jtreg, which gathers information about the timed out
* process and its children.
*/
public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
private static final String LOG_FILENAME = "processes.log";
private static final String OUTPUT_FILENAME = "processes.html";
public GatherProcessInfoTimeoutHandler(PrintWriter jtregLog, File outputDir,
File testJdk) {
super(jtregLog, outputDir, testJdk);
}
/**
* Runs various actions for jtreg timeout handler.
*
* <p>Please see method code for the actions.
*/
@Override
protected void runActions(Process process, long pid)
throws InterruptedException {
Path workDir = outputDir.toPath();
String name = getClass().getName();
PrintWriter actionsLog;
try {
// try to open a separate file for action log
actionsLog = new PrintWriter(new FileWriter(
workDir.resolve(LOG_FILENAME).toFile(), true), true);
} catch (IOException e) {
// use jtreg log as a fallback
actionsLog = log;
actionsLog.printf("ERROR: %s cannot open log file %s : %s", name,
LOG_FILENAME, e.getMessage());
}
try {
actionsLog.printf("%s ---%n", name);
runGatherer(name, actionsLog, pid);
} finally {
actionsLog.printf("--- %s%n", name);
// don't close jtreg log
if (actionsLog != log) {
actionsLog.close();
} else {
log.flush();
}
}
}
private void runGatherer(String name, PrintWriter log, long pid) {
Path workDir = outputDir.toPath();
try (HtmlPage html = new HtmlPage(workDir, OUTPUT_FILENAME, true)) {
ProcessInfoGatherer gatherer = new GathererFactory(
OS.current().family,
workDir, log, testJdk.toPath()).getProcessInfoGatherer();
try (ElapsedTimePrinter timePrinter
= new ElapsedTimePrinter(new Stopwatch(), name, log)) {
gatherer.gatherProcessInfo(html.getRootSection(), pid);
}
} catch (Throwable e) {
log.printf("ERROR: exception in timeout handler %s:", name);
e.printStackTrace(log);
}
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016, 2020, 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.
*/
package jdk.test.failurehandler.jtreg;
// Stripped down version of jtreg internal class com.sun.javatest.regtest.config.OS
class OS {
public final String family;
private static OS current;
public static OS current() {
if (current == null) {
String name = System.getProperty("os.name");
current = new OS(name);
}
return current;
}
private OS(String name) {
if (name.startsWith("AIX")) {
family = "aix";
} else if (name.startsWith("Linux")) {
family = "linux";
} else if (name.startsWith("Mac") || name.startsWith("Darwin")) {
family = "mac";
} else if (name.startsWith("Windows")) {
family = "windows";
} else {
// use first word of name
family = name.replaceFirst("^([^ ]+).*", "$1");
}
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import java.lang.reflect.Array;
import java.util.Objects;
public class ArrayParser implements ValueParser {
private final ValueParser parser;
public ArrayParser(ValueParser parser) {
Objects.requireNonNull(parser);
this.parser = parser;
}
@Override
public Object parse(Class<?> type, String value, String delimiter) {
Class<?> component = type.getComponentType();
if (component.isArray()) {
throw new IllegalArgumentException(
"multidimensional array fields aren't supported");
}
String[] values = (value == null || value.isEmpty())
? new String[]{}
: value.split(delimiter);
Object result = Array.newInstance(component, values.length);
for (int i = 0, n = values.length; i < n; ++i) {
Array.set(result, i, parser.parse(component, values[i], delimiter));
}
return result;
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import java.util.HashMap;
import java.util.Map;
public class DefaultParser implements ValueParser {
private static final Map<Class<?>, BasicParser> PARSERS = new HashMap<>();
static {
BasicParser.init();
}
@Override
public Object parse(Class<?> type, String value, String s) {
if (type.isArray()) {
return new ArrayParser(this).parse(type, value, s);
}
ValueParser parser = PARSERS.get(type);
if (parser == null) {
throw new IllegalArgumentException("can't find parser for "
+ type.getName());
}
return parser.parse(type, value, s);
}
private static enum BasicParser implements ValueParser {
BOOL(boolean.class, Boolean.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return Boolean.valueOf(value);
}
},
BYTE(byte.class, Byte.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return Byte.decode(value);
}
},
CHAR(char.class, Character.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
if (value.length() != 1) {
throw new IllegalArgumentException(
String.format("can't cast %s to char", value));
}
return value.charAt(0);
}
},
SHORT(short.class, Short.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return Short.decode(value);
}
},
INT(int.class, Integer.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return Integer.decode(value);
}
},
LONG(long.class, Long.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return Long.decode(value);
}
},
FLOAT(float.class, Float.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return Float.parseFloat(value);
}
},
DOUBLE(double.class, Double.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return Double.parseDouble(value);
}
},
STRING(String.class, Object.class) {
@Override
public Object parse(Class<?> type, String value, String s) {
return value;
}
};
private BasicParser(Class<?>... classes) {
for (Class<?> aClass : classes) {
DefaultParser.PARSERS.put(aClass, this);
}
}
private static void init() {
// no-op used to provoke <cinit>
}
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface DefaultValue {
String value();
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
public class InvalidValueException extends Exception {
public InvalidValueException() { }
public InvalidValueException(String message) {
super(message);
}
public InvalidValueException(String s, Throwable e) {
super(s, e);
}
public InvalidValueException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import java.io.File;
public class PathValueParser implements ValueParser {
@Override
public Object parse(Class<?> type, String value, String delimiter) {
if (type.isArray()) {
return new ArrayParser(this).parse(type, value, delimiter);
}
return new File(value).toPath();
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface SubValues {
String prefix();
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface Value {
String name();
Class<? extends ValueParser> parser() default DefaultParser.class;
}

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2015, 2020, 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.
*/
package jdk.test.failurehandler.value;
import jdk.test.failurehandler.Utils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.Properties;
public final class ValueHandler {
public static <T> void apply(T object, Properties properties,
String prefix) throws InvalidValueException {
Objects.requireNonNull(object, "object cannot be null");
Objects.requireNonNull(properties, "properties cannot be null");
Class<?> aClass = object.getClass();
while (aClass != null) {
for (Field field : aClass.getDeclaredFields()) {
Value p = field.getAnnotation(Value.class);
if (p != null) {
applyToField(p, object, field, properties, prefix);
} else {
SubValues sub
= field.getAnnotation(SubValues.class);
if (sub != null) {
getAccess(field);
try {
apply(field.get(object), properties,
Utils.prependPrefix(prefix, sub.prefix()));
} catch (IllegalAccessException e) {
throw new InvalidValueException(String.format(
"can't apply sub properties to %s.",
field.getName()));
}
}
}
}
aClass = aClass.getSuperclass();
}
}
private static void applyToField(Value property, Object object,
Field field, Properties properties, String prefix)
throws InvalidValueException {
getAccess(field);
if (Modifier.isFinal(field.getModifiers())) {
throw new InvalidValueException(
String.format("field '%s' is final", field));
}
String name = Utils.prependPrefix(prefix, property.name());
String value = getProperty(properties, prefix, property.name());
if (value == null) {
DefaultValue defaultValue
= field.getAnnotation(DefaultValue.class);
value = defaultValue == null ? null : defaultValue.value();
}
if (value == null) {
throw new InvalidValueException(String.format(
"can't set '%s', because properties don't have '%s'.",
field.getName(), name));
}
String delimiter = getProperty(properties,
Utils.prependPrefix(prefix, property.name()), "delimiter");
delimiter = delimiter == null ? " " : delimiter;
Class<? extends ValueParser> parserClass = property.parser();
try {
field.set(object, parserClass.getDeclaredConstructor().newInstance().parse(
field.getType(), value, delimiter));
} catch (ReflectiveOperationException | IllegalArgumentException e) {
throw new InvalidValueException(
String.format("can't set field '%s' : %s",
field.getName(), e.getMessage()), e);
}
}
private static String getProperty(Properties properties,
String prefix, String name) {
if (prefix == null || prefix.isEmpty()) {
return properties.getProperty(name);
}
int index = prefix.length();
do {
String value = properties.getProperty(
Utils.prependPrefix(prefix.substring(0, index), name));
if (value != null) {
return value;
}
index = prefix.lastIndexOf('.', index - 1);
} while (index > 0);
return properties.getProperty(name);
}
private static void getAccess(Field field) {
int modifiers = field.getModifiers();
if (!Modifier.isPublic(modifiers)) {
field.setAccessible(true);
}
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
public interface ValueParser {
Object parse(Class<?> type, String value, String delimiter);
}

View file

@ -0,0 +1,97 @@
#
# Copyright (c) 2015, 2025, 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.
#
pattern=%p
javaOnly=true
args=%p
################################################################################
# process info to gather
################################################################################
# It's important to retain the order of these actions and run the thread dump
# generating commands before the rest, to allow for capturing the test
# process' call stack as soon as the timeout has occurred. That reduces the chances
# of the test completing and thus missing crucial details from the thread dump
# while these timeout actions were being run.
onTimeout=\
thread_dump \
jinfo \
jcmd.compiler.codecache jcmd.compiler.codelist \
jcmd.compiler.queue \
jcmd.vm.classloader_stats jcmd.vm.stringtable \
jcmd.vm.symboltable jcmd.vm.uptime jcmd.vm.dynlibs \
jcmd.vm.system_properties jcmd.vm.info \
jcmd.gc.heap_info jcmd.gc.class_histogram jcmd.gc.finalizer_info jcmd.thread.dump_to_file jcmd.thread.vthread_scheduler \
jstack jhsdb.jstack.live.default jhsdb.jstack.live.mixed
jinfo.app=jinfo
jcmd.app=jcmd
jcmd.compiler.codecache.args=%p Compiler.codecache
jcmd.compiler.codelist.args=%p Compiler.codelist
jcmd.compiler.queue.args=%p Compiler.queue
jcmd.vm.classloader_stats.args=%p VM.classloader_stats
jcmd.vm.stringtable.args=%p VM.stringtable
jcmd.vm.symboltable.args=%p VM.symboltable
jcmd.vm.uptime.args=%p VM.uptime
jcmd.vm.dynlibs.args=%p VM.dynlibs
jcmd.vm.system_properties.args=%p VM.system_properties
jcmd.vm.info.args=%p VM.info
jcmd.gc.class_histogram.args=%p GC.class_histogram
jcmd.gc.finalizer_info.args=%p GC.finalizer_info
jcmd.gc.heap_info.args=%p GC.heap_info
jcmd.thread.dump_to_file.args=%p Thread.dump_to_file -format=json JavaThread.dump.%p.%iterCount
jcmd.thread.dump_to_file.params.repeat=6
jcmd.thread.dump_to_file.params.successArtifacts=JavaThread.dump.%p.%iterCount
jcmd.thread.vthread_scheduler.args=%p Thread.vthread_scheduler
# use jstack to generate one thread dump
thread_dump.app=jstack
thread_dump.args=-e -l %p
jstack.app=jstack
jstack.args=-e -l %p
jstack.params.repeat=5
jhsdb.app=jhsdb
jhsdb.jstack.live.default.args=jstack --pid %p
jhsdb.jstack.live.default.params.repeat=6
jhsdb.jstack.live.mixed.args=jstack --mixed --pid %p
jhsdb.jstack.live.mixed.params.repeat=6
cores=jhsdb.jstack.core.default jhsdb.jstack.core.mixed
# Assume that java standard laucher has been used
jhsdb.jstack.core.default.args=jstack --core %p --exe %java
jhsdb.jstack.core.mixed.args=jstack --mixed --core %p --exe %java
################################################################################
# environment info to gather
################################################################################
environment=jps
jps.app=jps
jps.args=-mlv
################################################################################

View file

@ -0,0 +1,155 @@
#
# Copyright (c) 2015, 2025, 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.
#
config.execSuffix=
config.getChildren.pattern=%p
config.getChildren.app=ps
config.getChildren.args=--no-headers -o pid --ppid %p
################################################################################
# process info to gather
################################################################################
onTimeout=\
native.pmap.normal native.pmap.everything \
native.files native.locks \
native.stack native.core
################################################################################
native.pattern=%p
native.javaOnly=false
native.args=%p
native.pmap.app=pmap
native.pmap.normal.args=-p %p
native.pmap.everything.args=-XXp %p
native.files.app=lsof
native.files.args=-p %p
native.locks.app=lslocks
native.locks.args=-u --pid %p
native.stack.app=gdb
native.stack.args=--pid=%p\0-batch\0-ex\0info threads\0-ex\0thread apply all backtrace
native.stack.args.delimiter=\0
native.stack.params.repeat=6
# has to be the last command
native.core.app=bash
# The below trick was found on https://stackoverflow.com/a/41613532
native.core.args=-c\0kill -ABRT %p && tail --pid=%p -f /dev/null
native.core.args.delimiter=\0
native.core.timeout=600000
cores=native.gdb
native.gdb.app=gdb
# Assume that java standard laucher has been used
native.gdb.args=%java\0-c\0%p\0-batch\0-ex\0info threads\0-ex\0thread apply all backtrace
native.gdb.args.delimiter=\0
################################################################################
# environment info to gather
################################################################################
environment=\
users.current users.logged users.last \
disk \
env \
ulimit \
system.dmesg system.sysctl \
process.top process.ps \
memory.free memory.vmstat.default memory.vmstat.statistics \
memory.vmstat.slabinfo memory.vmstat.disk \
memory.proc_meminfo memory.proc_vmstat \
memory.thp \
files \
locks \
net.sockets net.statistics net.ifconfig net.hostsfile \
screenshot
################################################################################
users.current.app=id
users.current.args=-a
users.logged.app=who
users.logged.args=-a
users.last.app=last
users.last.args=-10
disk.app=df
disk.args=-h
env.app=env
ulimit.app=bash
ulimit.args=-c\0ulimit -a
ulimit.args.delimiter=\0
system.dmesg.app=dmesg
system.sysctl.app=sysctl
system.sysctl.args=-a
process.top.app=top
process.top.args=-b -n 1
process.ps.app=ps
process.ps.args=-eo pid,pcpu,cputime,start,pmem,vsz,rssize,stackp,stat,sgi_p,wchan,user,args
memory.free.app=free
memory.free.args=-h
memory.vmstat.app=vmstat
memory.vmstat.default.args=3 3
memory.vmstat.statistics.args=-s
memory.vmstat.slabinfo.args=-m
memory.vmstat.disk.args=-d
memory.proc_meminfo.app=bash
memory.proc_meminfo.args=-c\0cat /proc/meminfo
memory.proc_meminfo.delimiter=\0
memory.proc_vmstat.app=bash
memory.proc_vmstat.args=-c\0cat /proc/vmstat
memory.proc_vmstat.delimiter=\0
memory.thp.app=bash
memory.thp.args=-c\0cat /sys/kernel/mm/transparent_hugepage/{enabled,defrag,shmem_enabled}
memory.thp.delimiter=\0
files.app=lsof
locks.app=lslocks
locks.args=-u
net.sockets.app=netstat
net.sockets.args=-aeeopv
net.statistics.app=netstat
net.statistics.args=-sv
net.ifconfig.app=ifconfig
net.ifconfig.args=-a
net.hostsfile.app=cat
net.hostsfile.args=/etc/hosts
screenshot.app=bash
screenshot.args=-c\0\
echo '\
var robot = new java.awt.Robot();\
var ge = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();\
var bounds = ge.getDefaultScreenDevice().getDefaultConfiguration().getBounds();\
var capture = robot.createScreenCapture(bounds);\
var file = new java.io.File("screen.png");\
javax.imageio.ImageIO.write(capture, "png", file);\
' | jshell -
screenshot.args.delimiter=\0
################################################################################

View file

@ -0,0 +1,153 @@
#
# Copyright (c) 2015, 2025, 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.
#
config.execSuffix=
config.getChildren.pattern=%p
config.getChildren.app=pgrep
config.getChildren.args=-P %p
################################################################################
# process info to gather
################################################################################
onTimeout=\
native.DevToolsSecurity \
native.vmmap native.heap native.leaks native.spindump \
native.stack native.core
################################################################################
native.pattern=%p
native.javaOnly=false
native.args=%p
native.DevToolsSecurity.app=DevToolsSecurity
native.DevToolsSecurity.args=--status
# spindump requires root privileges
native.spindump.app=sudo
native.spindump.args=spindump %p -stdout
native.vmmap.app=bash
native.vmmap.delimiter=\0
native.vmmap.args=-c\0DevToolsSecurity --status | grep -q enabled && vmmap %p
native.leaks.app=bash
native.leaks.delimiter=\0
native.leaks.args=-c\0DevToolsSecurity --status | grep -q enabled && leaks %p
native.heap.app=bash
native.heap.delimiter=\0
native.heap.args=-c\0DevToolsSecurity --status | grep -q enabled && heap %p
native.stack.app=bash
native.stack.delimiter=\0
native.stack.params.repeat=6
native.stack.args=-c\0DevToolsSecurity --status | grep -q enabled && lldb -o 'attach %p' -o 'thread backtrace all' -o 'detach' -o 'quit'
# has to be the last command
native.core.app=bash
# The below trick was found on https://stackoverflow.com/a/41613532
native.core.args=-c\0kill -ABRT %p && lsof -p %p +r 1 &>/dev/null
native.core.delimiter=\0
native.core.timeout=600000
cores=native.lldb
native.lldb.app=lldb
native.lldb.delimiter=\0
# Core files can be very big and take a long time to load on macosx-aarch64.
# The 20 seconds default timeout is not nearly enough.
native.lldb.timeout=120000
# Assume that java standard laucher has been used
native.lldb.args=--core\0%p\0%java\0-o\0thread backtrace all\0-o\0quit
################################################################################
# environment info to gather
################################################################################
environment=\
users.current users.logged users.last \
disk \
env \
ulimit \
system.dmesg system.sysctl \
process.ps process.top \
memory.vmstat \
files \
net.netstat.anv net.netstat.av net.netstat.aL net.netstat.m net.netstat.s net.netstat.g net.netstat.r \
net.ifconfig net.hostsfile \
fw.up \
scutil.nwi scutil.proxy \
screenshot
################################################################################
users.current.app=id
users.current.args=-a
users.logged.app=who
users.logged.args=-a
users.last.app=last
users.last.args=-10
disk.app=df
disk.args=-h
env.app=env
ulimit.app=bash
ulimit.args=-c\0ulimit -a
ulimit.args.delimiter=\0
system.dmesg.app=sudo
system.dmesg.args=dmesg
system.sysctl.app=sysctl
system.sysctl.args=-a
process.ps.app=ps
process.ps.args=-Meo pid,pcpu,cputime,start,pmem,vsz,rss,state,wchan,user,args
process.top.app=top
process.top.args=-l 2
memory.vmstat.app=vm_stat
memory.vmstat.args=-c 3 3
files.app=lsof
net.netstat.app=netstat
net.netstat.av.args=-av
net.netstat.anv.args=-anv
net.netstat.aL.args=-aL
net.netstat.m.args=-mm
net.netstat.s.args=-s
net.netstat.g.args=-gs
net.netstat.r.args=-rn
net.ifconfig.app=ifconfig
net.ifconfig.args=-a
net.hostsfile.app=cat
net.hostsfile.args=/etc/hosts
scutil.app=scutil
scutil.nwi.args=--nwi
scutil.proxy.args=--proxy
screenshot.app=screencapture
screenshot.args=-x screen1.png screen2.png screen3.png screen4.png screen5.png
fw.app=/usr/libexec/ApplicationFirewall/socketfilterfw
fw.up.args=--getglobalstate
################################################################################

View file

@ -0,0 +1,143 @@
#
# Copyright (c) 2015, 2025, 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.
#
config.execSuffix=.exe
config.getChildren.app=powershell
config.getChildren.pattern=%p
config.getChildren.args.delimiter=\0
config.getChildren.args=-NoLogo\0-Command\0"Get-CimInstance Win32_Process -Filter \\\"ParentProcessId = %p\\\" | Select-Object ProcessId" | tail -n+4
################################################################################
# process info to gather
################################################################################
onTimeout=\
native.info \
native.pmap.normal native.pmap.everything \
native.files native.locks \
native.stack native.core
################################################################################
native.pattern=%p
native.javaOnly=false
native.args=%p
native.info.app=powershell
native.info.delimiter=\0
native.info.args=-NoLogo\0-Command\0"Get-WmiObject Win32_Process -Filter \\\"ProcessId = %p\\\" | Format-List -Property *"
native.pmap.app=pmap
native.pmap.normal.args=%p
native.pmap.everything.args=-x %p
native.files.app=handle
native.files.args=-p %p
# TODO
native.locks.app=lslocks
native.locks.args=-u --pid %p
native.stack.app=cdb
native.stack.args=-c "~*kP n;qd" -p %p
native.stack.params.repeat=6
native.core.app=cdb
native.core.args=-c ".dump /mA core.%p;qd" -p %p
native.core.params.timeout=600000
cores=
################################################################################
# environment info to gather
################################################################################
environment=\
users.current users.logged \
disk \
env \
ulimit \
system.events.system system.events.application system.os \
process.top process.ps process.tasklist \
memory.free memory.vmstat.default memory.vmstat.statistics \
memory.vmstat.slabinfo memory.vmstat.disk \
files \
net.sockets net.statistics net.ipconfig net.hostsfile \
screenshot
################################################################################
users.current.app=id
users.current.args=-a
users.logged.app=query
users.logged.args=user
disk.app=df
disk.args=-h
env.app=env
ulimit.app=bash
ulimit.args=-c\0ulimit -a
ulimit.args.delimiter=\0
system.events.app=powershell
system.events.delimiter=\0
system.events.system.args=-NoLogo\0-Command\0Get-EventLog System -After (Get-Date).AddDays(-1) | Format-List
system.events.application.args=-NoLogo\0-Command\0Get-EventLog Application -After (Get-Date).AddDays(-1) | Format-List
system.os.app=powershell
system.os.delimiter=\0
system.os.args=-NoLogo\0-Command\0Get-WmiObject Win32_OperatingSystem | Format-List -Property *
process.top.app=top
process.top.args=-b -n 1
process.ps.app=ps
process.ps.args=-efW
process.tasklist.app=tasklist
process.tasklist.args=/V
memory.free.app=free
memory.vmstat.app=vmstat
memory.vmstat.statistics.args=-s
memory.vmstat.slabinfo.args=-m
memory.vmstat.disk.args=-d
files.app=openfiles
files.args=/query
net.sockets.app=bash
net.sockets.args=-c\0netstat -b -a -t -o || netstat -a -t -o
net.sockets.args.delimiter=\0
net.statistics.app=netstat
net.statistics.args=-s -e
net.ipconfig.app=ipconfig
net.ipconfig.args=/all
net.hostsfile.app=bash
net.hostsfile.args.delimiter=\0
net.hostsfile.args=-c\0cat $WINDIR/System32/drivers/etc/hosts
screenshot.app=bash
screenshot.args=-c\0\
echo '\
var robot = new java.awt.Robot();\
var ge = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();\
var bounds = ge.getDefaultScreenDevice().getDefaultConfiguration().getBounds();\
var capture = robot.createScreenCapture(bounds);\
var file = new java.io.File(""screen.png"");\
javax.imageio.ImageIO.write(capture, ""png"", file);\
' | jshell -
screenshot.args.delimiter=\0
################################################################################

View file

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2015, 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.
*/
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/*
* @test
* @run main/othervm Crash
*/
public class Crash {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe u = (Unsafe) f.get(null);
u.setMemory(0, 42, (byte) 0xFF);
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2015, 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 Deadlocked client
*/
public class Deadlock {
public double e;
private volatile int i;
public static void main(String[] args) {
new Deadlock().test();
}
private void test() {
final Object a = new Object();
final Object b = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (a) {
do {
i |= 1;
} while (i != 3);
synchronized (b) {
e = 1;
}
}
}}).start();
synchronized (b) {
do {
i |= 2;
} while (i != 3);
synchronized (a) {
e = 2;
}
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2015, 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 Busy infinite loop client, calculating E number
*/
public class Livelock {
public static double elim;
public static void main(String[] args) {
System.out.printf(
"%24s %24s %24s %24s %24s %24s%n",
"n", "n!", "e = lim(...)", "e = taylor series",
"err e-lim", "err e-taylor");
while (true) {
double esum = 2;
double nfac = 1;
double iter = 1;
for (double n = 1; !Double.isInfinite(n) && !Double.isNaN(n) ; n = n * 2) {
elim = Math.pow(1 + 1 / n, n);
iter += 1;
nfac *= iter;
esum += 1 / nfac;
System.out.printf("% 24.16e % 24.16e % 24.16e % 24.16e"
+ "%- 24.16e %- 24.16e%n",
n, nfac, elim, esum, (Math.E - elim), (Math.E - esum));
}
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 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.
*/
import java.util.LinkedList;
/*
* @test
* @summary Slowly eat all memory in an infinite loop
* @run main/othervm Crash
*/
public class OOME {
@SuppressWarnings ("UnusedDeclaration")
private static Object garbage;
public static void main(String args[]) {
int chunkSize = 0x8000;
LinkedList<int[]> list = new LinkedList<>();
garbage = list;
while (true) {
try {
list.add(new int[chunkSize]);
} catch (OutOfMemoryError e) {
chunkSize >>= 1;
}
}
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015, 2020, 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 Suicide test
* @run main/othervm Suicide
*/
public class Suicide {
public static void main(String[] args) {
String cmd = null;
try {
long pid = ProcessHandle.current().pid();
String osName = System.getProperty("os.name");
if (osName.contains("Windows")) {
cmd = "taskkill.exe /F /PID " + pid;
} else {
cmd = "kill -9 " + pid;
}
System.out.printf("executing `%s'%n", cmd);
Runtime.getRuntime().exec(cmd);
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.err.printf("TEST/ENV BUG: %s didn't kill JVM%n", cmd);
System.exit(1);
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2015, 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
* @run main/othervm SystemExit
*/
public class SystemExit {
public static void main(String[] args) {
System.exit(1);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2015, 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
*/
public class ThrowError {
public static void main(String[] args) {
throw new Error("TEST FAIL");
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2015, 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.
*/
import java.io.IOException;
import java.nio.file.Paths;
/*
* @test
* @build Deadlock
* @run driver WaitForDeadlock
*/
public class WaitForDeadlock {
public static void main(String[] args) throws Exception {
System.out.println("START");
ProcessBuilder pb = new ProcessBuilder(Paths.get(
System.getProperty("test.jdk"), "bin", "java").toString(),
"-cp", System.getProperty("java.class.path"),
Deadlock.class.getName());
pb.redirectError(ProcessBuilder.Redirect.to(Paths.get("out").toFile()));
pb.redirectOutput(ProcessBuilder.Redirect.to(Paths.get("err").toFile()));
int r = pb.start().waitFor();
System.out.println("END. " + r);
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import org.junit.Assert;
import org.junit.Test;
public class DefaultParserTest {
@Test
public void testParseStringArray() throws Exception {
DefaultParser parser = new DefaultParser();
String line = "a aa aaa";
String[] result = {"a", "aa", "", "", "aaa"};
Assert.assertArrayEquals(result,
(Object[]) parser.parse(result.getClass(), line, " "));
line = null;
result = new String[]{};
Assert.assertArrayEquals(result,
(Object[]) parser.parse(result.getClass(), line, " "));
}
@Test
public void testParseObjectArray() throws Exception {
DefaultParser parser = new DefaultParser();
String line = "a aa aaa";
String[] result = {"a", "aa", "", "", "aaa"};
Assert.assertArrayEquals(result,
(String[]) parser.parse(result.getClass(), line, " "));
Object[] result2 = {"a", "aa", "", "", "aaa"};
Assert.assertArrayEquals(result2,
(Object[]) parser.parse(result.getClass(), line, " "));
}
@Test
public void testParseCharArray() throws Exception {
DefaultParser parser = new DefaultParser();
String line = "a b c a";
char[] result = {'a', 'b', 'c', 'a'};
Assert.assertArrayEquals(result,
(char[]) parser.parse(result.getClass(), line, " "));
Character[] result2 = {'a', 'b', 'c', 'a'};
Assert.assertArrayEquals(result2,
(Character[]) parser.parse(result2.getClass(), line, " "));
}
@Test
public void testParseBoolean() throws Exception {
DefaultParser parser = new DefaultParser();
String line = "a b c a";
Assert.assertEquals(false,
(boolean) parser.parse(boolean.class, line, " "));
Assert.assertEquals(Boolean.FALSE,
parser.parse(Boolean.class, line, " "));
line = "trUe";
Assert.assertEquals(true,
(boolean) parser.parse(boolean.class, line, " "));
Assert.assertEquals(Boolean.TRUE,
parser.parse(Boolean.class, line, " "));
}
@Test
public void testParseShort() throws Exception {
DefaultParser parser = new DefaultParser();
Assert.assertSame("10", (short) 10,
parser.parse(short.class, "10", " "));
Assert.assertSame("010", (short) 8,
parser.parse(short.class, "010", " "));
Assert.assertSame("0x10", (short) 16,
parser.parse(short.class, "0x10", " "));
}
@Test
public void testParseByte() throws Exception {
DefaultParser parser = new DefaultParser();
Assert.assertSame("11", (byte) 11,
parser.parse(byte.class, "11", " "));
Assert.assertSame("011", (byte) 9,
parser.parse(byte.class, "011", " "));
Assert.assertSame("0x11", (byte) 17,
parser.parse(byte.class, "0x11", " "));
}
@Test
public void testParseInt() throws Exception {
DefaultParser parser = new DefaultParser();
Assert.assertEquals("20", (int) 20,
parser.parse(int.class, "20", " "));
Assert.assertEquals("020", (int) 16,
parser.parse(int.class, "020", " "));
Assert.assertEquals("0x20", (int) 32,
parser.parse(int.class, "0x20", " "));
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2015, 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.
*/
package jdk.test.failurehandler.value;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Field;
import java.util.Properties;
public class ValueHandlerTest {
@Test
public void testApplyAnonymousPrivateFinalInt() throws Exception {
Properties p = new Properties();
p.put("int", "010");
Object o = new Object() {
@Value (name = "int")
private final int i1 = -1;
};
Field f = o.getClass().getDeclaredField("i1");
f.setAccessible(true);
int value = f.getInt(o);
Assert.assertEquals(value, -1);
f.setAccessible(false);
ValueHandler.apply(o, p, null);
f.setAccessible(true);
value = f.getInt(o);
Assert.assertEquals(value, 8);
f.setAccessible(false);
}
@Test
public void testApplyPublicStaticWithDefault() throws Exception {
Assert.assertEquals(StaticDefaultCase.s, null);
Properties p = new Properties();
StaticDefaultCase o = new StaticDefaultCase();
ValueHandler.apply(o, p, "prefix");
Assert.assertEquals(StaticDefaultCase.s, "default");
p.put("s", "new2");
ValueHandler.apply(o, p, "prefix");
Assert.assertEquals(StaticDefaultCase.s, "new2");
p.put("prefix.s", "new");
ValueHandler.apply(o, p, "prefix");
Assert.assertEquals(StaticDefaultCase.s, "new");
ValueHandler.apply(o, p, null);
Assert.assertEquals(StaticDefaultCase.s, "new2");
}
protected class InnerClass1 {
@Value (name = "innerClass")
String[] arr = null;
}
public class InnerClass2 extends InnerClass1 {
@Value (name = "float")
float f = 0.0f;
@SubValues (prefix = "inner")
InnerClass1 inner1 = new InnerClass1();
@SubValues (prefix = "")
InnerClass1 inner2 = new InnerClass1();
}
@Test
public void testApplySub() throws Exception {
InnerClass2 o = new InnerClass2();
Assert.assertArrayEquals(o.arr, null);
Assert.assertArrayEquals(o.inner1.arr, null);
Assert.assertArrayEquals(o.inner2.arr, null);
Assert.assertEquals(o.f, 0.0f, Float.MIN_VALUE);
Properties p = new Properties();
p.put("float", "1.f");
p.put("innerClass", "a b");
p.put("inner.innerClass", "a b c");
ValueHandler.apply(o, p, "");
Assert.assertArrayEquals(o.arr, new String[]{"a", "b"});
Assert.assertArrayEquals(o.inner1.arr, new String[]{"a", "b", "c"});
Assert.assertArrayEquals(o.inner2.arr, new String[]{"a", "b"});
Assert.assertEquals(o.f, 1.0f, Float.MIN_VALUE);
}
}
class StaticDefaultCase {
@Value (name = "s")
@DefaultValue (value = "default")
public static String s;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,457 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, Red Hat Inc. 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.
*/
#if defined(AARCH64) && !defined(ZERO)
#include "asm/assembler.hpp"
#include "asm/assembler.inline.hpp"
#include "asm/macroAssembler.hpp"
#include "compiler/disassembler.hpp"
#include "memory/resourceArea.hpp"
#include "nativeInst_aarch64.hpp"
#include "unittest.hpp"
#define __ _masm.
static void asm_check(const unsigned int *insns, const unsigned int *insns1, size_t len) {
bool ok = true;
for (unsigned int i = 0; i < len; i++) {
if (insns[i] != insns1[i]) {
ResourceMark rm;
stringStream ss;
ss.print_cr("Ours:");
Disassembler::decode((address)&insns1[i], (address)&insns1[i+1], &ss);
ss.print_cr("Theirs:");
Disassembler::decode((address)&insns[i], (address)&insns[i+1], &ss);
EXPECT_EQ(insns[i], insns1[i]) << ss.as_string();
}
}
}
TEST_VM(AssemblerAArch64, validate) {
// Smoke test for assembler
BufferBlob* b = BufferBlob::create("aarch64Test", 500000);
CodeBuffer code(b);
Assembler _masm(&code);
address entry = __ pc();
// python aarch64-asmtest.py | expand > asmtest.out.h
#include "asmtest.out.h"
asm_check((unsigned int *)entry, insns, sizeof insns / sizeof insns[0]);
{
address PC = __ pc();
__ ld1(v0, __ T16B, Address(r16)); // No offset
__ ld1(v0, __ T8H, __ post(r16, 16)); // Post-index
__ ld2(v0, v1, __ T8H, __ post(r24, 16 * 2)); // Post-index
__ ld1(v0, __ T16B, __ post(r16, r17)); // Register post-index
static const unsigned int vector_insns[] = {
0x4c407200, // ld1 {v0.16b}, [x16]
0x4cdf7600, // ld1 {v0.8h}, [x16], #16
0x4cdf8700, // ld2 {v0.8h, v1.8h}, [x24], #32
0x4cd17200, // ld1 {v0.16b}, [x16], x17
};
asm_check((unsigned int *)PC, vector_insns,
sizeof vector_insns / sizeof vector_insns[0]);
}
BufferBlob::free(b);
}
constexpr uint32_t test_encode_dmb_ld = 0xd50339bf;
constexpr uint32_t test_encode_dmb_st = 0xd5033abf;
constexpr uint32_t test_encode_dmb = 0xd5033bbf;
constexpr uint32_t test_encode_nop = 0xd503201f;
static void asm_dump(address start, address end) {
ResourceMark rm;
stringStream ss;
ss.print_cr("Insns:");
Disassembler::decode(start, end, &ss);
printf("%s\n", ss.as_string());
}
void test_merge_dmb() {
BufferBlob* b = BufferBlob::create("aarch64Test", 400);
CodeBuffer code(b);
MacroAssembler _masm(&code);
{
// merge with same type
__ membar(Assembler::Membar_mask_bits::StoreStore);
__ membar(Assembler::Membar_mask_bits::StoreStore);
__ membar(Assembler::Membar_mask_bits::StoreStore);
__ nop();
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ nop();
// merge with high rank
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ membar(Assembler::Membar_mask_bits::AnyAny);
__ membar(Assembler::Membar_mask_bits::StoreStore);
__ membar(Assembler::Membar_mask_bits::StoreStore);
__ nop();
// merge with different type
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ membar(Assembler::Membar_mask_bits::StoreStore);
__ membar(Assembler::Membar_mask_bits::LoadStore);
__ membar(Assembler::Membar_mask_bits::StoreStore);
}
asm_dump(code.insts()->start(), code.insts()->end());
// AlwaysMergeDMB
static const unsigned int insns1[] = {
test_encode_dmb_st,
test_encode_nop,
test_encode_dmb_ld,
test_encode_nop,
test_encode_dmb,
test_encode_nop,
test_encode_dmb,
};
// !AlwaysMergeDMB
static const unsigned int insns2[] = {
test_encode_dmb_st,
test_encode_nop,
test_encode_dmb_ld,
test_encode_nop,
test_encode_dmb,
test_encode_nop,
test_encode_dmb_ld,
test_encode_dmb_st,
};
if (AlwaysMergeDMB) {
EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns1));
asm_check((const unsigned int *)code.insts()->start(), insns1, sizeof insns1 / sizeof insns1[0]);
} else {
EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns2));
asm_check((const unsigned int *)code.insts()->start(), insns2, sizeof insns2 / sizeof insns2[0]);
}
BufferBlob::free(b);
}
TEST_VM(AssemblerAArch64, merge_dmb_1) {
FlagSetting fs(AlwaysMergeDMB, true);
test_merge_dmb();
}
TEST_VM(AssemblerAArch64, merge_dmb_2) {
FlagSetting fs(AlwaysMergeDMB, false);
test_merge_dmb();
}
TEST_VM(AssemblerAArch64, merge_dmb_block_by_label) {
BufferBlob* b = BufferBlob::create("aarch64Test", 400);
CodeBuffer code(b);
MacroAssembler _masm(&code);
{
Label l;
// merge can not cross the label
__ membar(Assembler::Membar_mask_bits::StoreStore);
__ bind(l);
__ membar(Assembler::Membar_mask_bits::StoreStore);
}
asm_dump(code.insts()->start(), code.insts()->end());
static const unsigned int insns[] = {
0xd5033abf, // dmb.ishst
0xd5033abf, // dmb.ishst
};
EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns));
asm_check((const unsigned int *)code.insts()->start(), insns, sizeof insns / sizeof insns[0]);
BufferBlob::free(b);
}
TEST_VM(AssemblerAArch64, merge_dmb_after_expand) {
ResourceMark rm;
BufferBlob* b = BufferBlob::create("aarch64Test", 400);
CodeBuffer code(b);
code.set_blob(b);
MacroAssembler _masm(&code);
{
__ membar(Assembler::Membar_mask_bits::StoreStore);
code.insts()->maybe_expand_to_ensure_remaining(50000);
__ membar(Assembler::Membar_mask_bits::StoreStore);
}
asm_dump(code.insts()->start(), code.insts()->end());
static const unsigned int insns[] = {
0xd5033abf, // dmb.ishst
};
EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns));
asm_check((const unsigned int *)code.insts()->start(), insns, sizeof insns / sizeof insns[0]);
}
void expect_dmbld(void* addr) {
if (*((uint32_t *) addr) != test_encode_dmb_ld) {
tty->print_cr("Expected dmb.ld");
FAIL();
}
}
void expect_dmbst(void* addr) {
if (*((uint32_t *) addr) != test_encode_dmb_st) {
tty->print_cr("Expected dmb.st");
FAIL();
}
}
void expect_dmb(void* addr) {
if (*((uint32_t *) addr) != test_encode_dmb) {
tty->print_cr("Expected dmb");
FAIL();
}
}
void expect_any_dmb(void* addr) {
uint32_t encode = *((uint32_t *) addr);
if (encode != test_encode_dmb && encode != test_encode_dmb_ld && encode != test_encode_dmb_st) {
tty->print_cr("Expected a dmb.* instruction");
FAIL();
}
}
void expect_different_dmb_kind(void* addr) {
uint32_t pos1 = *((uint32_t *) addr);
uint32_t pos2 = *(((uint32_t *) addr) + 1);
if (pos1 == pos2) {
tty->print_cr("Expected different dmb kind");
FAIL();
}
}
void expect_dmb_at_least_one(void* addr) {
uint32_t pos1 = *((uint32_t *) addr);
uint32_t pos2 = *(((uint32_t *) addr) + 1);
if (pos1 != test_encode_dmb && pos2 != test_encode_dmb) {
tty->print_cr("Expected at least one dmb");
FAIL();
}
}
void expect_dmb_none(void* addr) {
uint32_t pos1 = *((uint32_t *) addr);
uint32_t pos2 = *(((uint32_t *) addr) + 1);
if (pos1 == test_encode_dmb || pos2 == test_encode_dmb) {
tty->print_cr("Expected no dmb");
FAIL();
}
}
void test_merge_dmb_all_kinds() {
BufferBlob* b = BufferBlob::create("aarch64Test", 20000);
CodeBuffer code(b);
MacroAssembler _masm(&code);
constexpr int count = 5;
struct {
const char* label;
Assembler::Membar_mask_bits flavor;
// Two groups of two bits describing the ordering, can be OR-ed to figure out composite semantics.
// First group describes ops before the barrier. Second group describes ops after the barrier.
// "01" means "load", "10" means "store", "100" means "any".
int mask;
} kind[count] = {
{"storestore", Assembler::StoreStore, 0b010010},
{"loadstore", Assembler::LoadStore, 0b001010},
{"loadload", Assembler::LoadLoad, 0b001001},
{"storeload", Assembler::StoreLoad, 0b100100}, // quirk: StoreLoad is as powerful as AnyAny
{"anyany", Assembler::AnyAny, 0b100100},
};
for (int b1 = 0; b1 < count; b1++) {
for (int b2 = 0; b2 < count; b2++) {
for (int b3 = 0; b3 < count; b3++) {
for (int b4 = 0; b4 < count; b4++) {
// tty->print_cr("%s + %s + %s + %s", kind[b1].label, kind[b2].label, kind[b3].label, kind[b4].label);
address start = __ pc();
__ membar(kind[b1].flavor);
__ membar(kind[b2].flavor);
__ membar(kind[b3].flavor);
__ membar(kind[b4].flavor);
address end = __ pc();
__ nop();
size_t size = pointer_delta(end, start, 1);
if (AlwaysMergeDMB) {
// Expect only a single barrier.
EXPECT_EQ(size, (size_t) NativeMembar::instruction_size);
} else {
EXPECT_LE(size, (size_t) NativeMembar::instruction_size * 2);
}
// Composite ordering for this group of barriers.
int composite_mask = kind[b1].mask | kind[b2].mask | kind[b3].mask | kind[b4].mask;
if (size == NativeMembar::instruction_size) {
// If there is a single barrier, we can easily test its type.
switch (composite_mask) {
case 0b001001:
case 0b001010:
case 0b001011:
case 0b001101:
case 0b001110:
case 0b001111:
// Any combination of Load(Load|Store|Any) gets dmb.ld
expect_dmbld(start);
break;
case 0b010010:
// Only StoreStore gets dmb.st
expect_dmbst(start);
break;
default:
// Everything else gets folded into full dmb
expect_dmb(start);
break;
}
} else if (size == 2 * NativeMembar::instruction_size) {
// There are two barriers. Make a few sanity checks.
// They must be different kind
expect_any_dmb(start);
expect_any_dmb(start + NativeMembar::instruction_size);
expect_different_dmb_kind(start);
if ((composite_mask & 0b100100) != 0) {
// There was "any" barrier in the group, a full dmb is expected
expect_dmb_at_least_one(start);
} else {
// Otherwise expect no full dmb
expect_dmb_none(start);
}
} else {
// Merging code does not produce this result.
FAIL();
}
}
}
}
}
BufferBlob::free(b);
}
TEST_VM(AssemblerAArch64, merge_dmb_all_kinds_1) {
FlagSetting fs(AlwaysMergeDMB, true);
test_merge_dmb_all_kinds();
}
TEST_VM(AssemblerAArch64, merge_dmb_all_kinds_2) {
FlagSetting fs(AlwaysMergeDMB, false);
test_merge_dmb_all_kinds();
}
TEST_VM(AssemblerAArch64, merge_ldst) {
BufferBlob* b = BufferBlob::create("aarch64Test", 400);
CodeBuffer code(b);
MacroAssembler _masm(&code);
{
Label l;
// merge ld/st into ldp/stp
__ ldr(r0, Address(sp, 8));
__ ldr(r1, Address(sp, 0));
__ nop();
__ str(r0, Address(sp, 0));
__ str(r1, Address(sp, 8));
__ nop();
__ ldrw(r0, Address(sp, 0));
__ ldrw(r1, Address(sp, 4));
__ nop();
__ strw(r0, Address(sp, 4));
__ strw(r1, Address(sp, 0));
__ nop();
// can not merge
__ ldrw(r0, Address(sp, 4));
__ ldr(r1, Address(sp, 8));
__ nop();
__ ldrw(r0, Address(sp, 0));
__ ldrw(r1, Address(sp, 8));
__ nop();
__ str(r0, Address(sp, 0));
__ bind(l); // block by label
__ str(r1, Address(sp, 8));
__ nop();
}
asm_dump(code.insts()->start(), code.insts()->end());
static const unsigned int insns1[] = {
0xa94003e1, // ldp x1, x0, [sp]
0xd503201f, // nop
0xa90007e0, // stp x0, x1, [sp]
0xd503201f, // nop
0x294007e0, // ldp w0, w1, [sp]
0xd503201f, // nop
0x290003e1, // stp w1, w0, [sp]
0xd503201f, // nop
0xb94007e0, // ldr w0, [sp, 4]
0xf94007e1, // ldr x1, [sp, 8]
0xd503201f, // nop
0xb94003e0, // ldr w0, [sp]
0xb9400be1, // ldr w1, [sp, 8]
0xd503201f, // nop
0xf90003e0, // str x0, [sp]
0xf90007e1, // str x1, [sp, 8]
0xd503201f, // nop
};
EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns1));
asm_check((const unsigned int *)code.insts()->start(), insns1, sizeof insns1 / sizeof insns1[0]);
BufferBlob::free(b);
}
TEST_VM(AssemblerAArch64, merge_ldst_after_expand) {
ResourceMark rm;
BufferBlob* b = BufferBlob::create("aarch64Test", 400);
CodeBuffer code(b);
code.set_blob(b);
MacroAssembler _masm(&code);
{
__ ldr(r0, Address(sp, 8));
code.insts()->maybe_expand_to_ensure_remaining(10000);
__ ldr(r1, Address(sp, 0));
__ nop();
__ str(r0, Address(sp, 0));
code.insts()->maybe_expand_to_ensure_remaining(100000);
__ str(r1, Address(sp, 8));
__ nop();
}
asm_dump(code.insts()->start(), code.insts()->end());
static const unsigned int insns[] = {
0xa94003e1, // ldp x1, x0, [sp]
0xd503201f, // nop
0xa90007e0, // stp x0, x1, [sp]
0xd503201f, // nop
};
EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns));
asm_check((const unsigned int *)code.insts()->start(), insns, sizeof insns / sizeof insns[0]);
}
#endif // AARCH64

View file

@ -0,0 +1,35 @@
/*
* Copyright Amazon.com Inc. 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.
*/
// Skip Windows to prevent GTestWrapper.java from failing because
// SpinPause is not implemented on Windows (and therefore returns 0)
#if defined(AARCH64) && !defined(ZERO) && !defined(_WINDOWS)
#include "utilities/spinYield.hpp"
#include "unittest.hpp"
TEST_VM(SpinPause, sanity) {
ASSERT_EQ(SpinPause(), 1);
}
#endif // AARCH64

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright Amazon.com Inc. 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.
*
*/
#include "cds/archiveUtils.hpp"
#include "runtime/atomic.hpp"
#include "unittest.hpp"
class TestArchiveWorkerTask : public ArchiveWorkerTask {
private:
Atomic<int> _sum;
Atomic<int> _max;
public:
TestArchiveWorkerTask() : ArchiveWorkerTask("Test"), _sum(0), _max(0) {}
void work(int chunk, int max_chunks) override {
_sum.add_then_fetch(chunk);
_max.store_relaxed(max_chunks);
}
int sum() { return _sum.load_relaxed(); }
int max() { return _max.load_relaxed(); }
};
// Test a repeated cycle of workers init/shutdown without task works.
TEST_VM(ArchiveWorkersTest, continuous_restart) {
for (int c = 0; c < 1000; c++) {
ArchiveWorkers workers;
}
}
// Test a repeated cycle of sample task works.
TEST_VM(ArchiveWorkersTest, single_task) {
for (int c = 0; c < 1000; c++) {
TestArchiveWorkerTask task;
ArchiveWorkers workers;
workers.run_task(&task);
ASSERT_EQ(task.max() * (task.max() - 1) / 2, task.sum());
}
}
// Test that reusing the workers fails.
#ifdef ASSERT
TEST_VM_ASSERT_MSG(ArchiveWorkersTest, multiple_tasks, ".* Should be unused yet") {
TestArchiveWorkerTask task;
ArchiveWorkers workers;
workers.run_task(&task);
workers.run_task(&task);
}
#endif // ASSERT

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2026 salesforce.com, inc. 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.
*
*/
#include "cds/aotCompressedPointers.hpp"
#include "unittest.hpp"
#include "utilities/globalDefinitions.hpp"
#include <cstdint>
TEST_VM(ScaledOffsetsTest, constants) {
#ifdef _LP64
ASSERT_EQ((size_t)3, AOTCompressedPointers::MetadataOffsetShift);
ASSERT_TRUE(is_aligned(AOTCompressedPointers::MaxMetadataOffsetBytes, (size_t)1 << AOTCompressedPointers::MetadataOffsetShift));
ASSERT_EQ((size_t)(3584ULL * M), AOTCompressedPointers::MaxMetadataOffsetBytes);
#else
ASSERT_EQ((size_t)0, AOTCompressedPointers::MetadataOffsetShift);
ASSERT_EQ((size_t)0x7FFFFFFF, AOTCompressedPointers::MaxMetadataOffsetBytes);
#endif
}
TEST_VM(ScaledOffsetsTest, encode_decode_roundtrip) {
// Test that encoding and decoding via get_byte_offset produces correct results
const size_t unit = (size_t)1 << AOTCompressedPointers::MetadataOffsetShift;
// Test that get_byte_offset correctly applies the shift
// Note: We can't directly test encode_byte_offset as it's private, but we can verify
// the shift value is applied correctly in get_byte_offset
AOTCompressedPointers::narrowPtr np1 = static_cast<AOTCompressedPointers::narrowPtr>(1);
ASSERT_EQ(unit, AOTCompressedPointers::get_byte_offset(np1));
AOTCompressedPointers::narrowPtr np2 = static_cast<AOTCompressedPointers::narrowPtr>(2);
ASSERT_EQ(2 * unit, AOTCompressedPointers::get_byte_offset(np2));
AOTCompressedPointers::narrowPtr np1024 = static_cast<AOTCompressedPointers::narrowPtr>(1024);
ASSERT_EQ(1024 * unit, AOTCompressedPointers::get_byte_offset(np1024));
#ifdef _LP64
const uint64_t max_units = (uint64_t)UINT32_MAX;
AOTCompressedPointers::narrowPtr np_max = static_cast<AOTCompressedPointers::narrowPtr>(UINT32_MAX);
const uint64_t max_bytes = max_units << AOTCompressedPointers::MetadataOffsetShift;
ASSERT_EQ(max_bytes, AOTCompressedPointers::get_byte_offset(np_max));
ASSERT_GE(max_bytes, AOTCompressedPointers::MaxMetadataOffsetBytes - unit);
#endif
}
TEST_VM(ScaledOffsetsTest, null_handling) {
// Test that null() returns 0
ASSERT_EQ(static_cast<AOTCompressedPointers::narrowPtr>(0), AOTCompressedPointers::null());
ASSERT_EQ((size_t)0, AOTCompressedPointers::get_byte_offset(AOTCompressedPointers::null()));
}

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2016, 2025, 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.
*/
#include "classfile/altHashing.hpp"
#include "utilities/debug.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/globalDefinitions.hpp"
#include "unittest.hpp"
class AltHashingTest : public ::testing::Test {
public:
static void testHalfsiphash_32_ByteArray() {
const int factor = 4;
uint8_t vector[256];
uint8_t hashes[factor * 256];
for (int i = 0; i < 256; i++) {
vector[i] = (uint8_t) i;
}
// Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255}
for (int i = 0; i < 256; i++) {
uint32_t hash = AltHashing::halfsiphash_32(256 - i, vector, i);
hashes[i * factor] = (uint8_t) hash;
hashes[i * factor + 1] = (uint8_t)(hash >> 8);
hashes[i * factor + 2] = (uint8_t)(hash >> 16);
hashes[i * factor + 3] = (uint8_t)(hash >> 24);
}
// hash to get const result.
uint32_t final_hash = AltHashing::halfsiphash_32(0, hashes, factor*256);
// Value found using reference implementation for the hashes array.
//uint64_t k = 0; // seed
//uint32_t reference;
//halfsiphash((const uint8_t*)hashes, factor*256, (const uint8_t *)&k, (uint8_t*)&reference, 4);
//printf("0x%x", reference);
static const uint32_t HALFSIPHASH_32_BYTE_CHECK_VALUE = 0xd2be7fd8;
ASSERT_EQ(HALFSIPHASH_32_BYTE_CHECK_VALUE, final_hash) <<
err_msg(
"Calculated hash result not as expected. Expected " UINT32_FORMAT " got " UINT32_FORMAT,
HALFSIPHASH_32_BYTE_CHECK_VALUE,
final_hash);
}
static void testHalfsiphash_32_CharArray() {
const int factor = 2;
uint16_t vector[256];
uint16_t hashes[factor * 256];
for (int i = 0; i < 256; i++) {
vector[i] = (uint16_t) i;
}
// Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255}
for (int i = 0; i < 256; i++) {
uint32_t hash = AltHashing::halfsiphash_32(256 - i, vector, i);
hashes[i * factor] = (uint16_t) hash;
hashes[i * factor + 1] = (uint16_t)(hash >> 16);
}
// hash to get const result.
uint32_t final_hash = AltHashing::halfsiphash_32(0, hashes, factor*256);
// Value found using reference implementation for the hashes array.
//uint64_t k = 0; // seed
//uint32_t reference;
//halfsiphash((const uint8_t*)hashes, 2*factor*256, (const uint8_t *)&k, (uint8_t*)&reference, 4);
//printf("0x%x", reference);
static const uint32_t HALFSIPHASH_32_CHAR_CHECK_VALUE = 0x428bf8a5;
ASSERT_EQ(HALFSIPHASH_32_CHAR_CHECK_VALUE, final_hash) <<
err_msg(
"Calculated hash result not as expected. Expected " UINT32_FORMAT " got " UINT32_FORMAT,
HALFSIPHASH_32_CHAR_CHECK_VALUE,
final_hash);
}
// Test against sample hashes published with the reference implementation:
// https://github.com/veorq/SipHash
static void testHalfsiphash_64_FromReference() {
const uint64_t seed = 0x0706050403020100;
const uint64_t results[16] = {
0xc83cb8b9591f8d21, 0xa12ee55b178ae7d5,
0x8c85e4bc20e8feed, 0x99c7f5ae9f1fc77b,
0xb5f37b5fd2aa3673, 0xdba7ee6f0a2bf51b,
0xf1a63fae45107470, 0xb516001efb5f922d,
0x6c6211d8469d7028, 0xdc7642ec407ad686,
0x4caec8671cc8385b, 0x5ab1dc27adf3301e,
0x3e3ea94bc0a8eaa9, 0xe150f598795a4402,
0x1d5ff142f992a4a1, 0x60e426bf902876d6
};
uint32_t vector[16];
for (int i = 0; i < 16; i++)
vector[i] = 0x03020100 + i * 0x04040404;
for (int i = 0; i < 16; i++) {
uint64_t hash = AltHashing::halfsiphash_64(seed, vector, i);
ASSERT_EQ(results[i], hash) <<
err_msg(
"Calculated hash result not as expected. Round %d: "
"Expected " UINT64_FORMAT_X " got " UINT64_FORMAT_X "\n",
i,
results[i],
hash);
}
}
};
TEST_F(AltHashingTest, halfsiphash_test_ByteArray) {
AltHashingTest::testHalfsiphash_32_ByteArray();
}
TEST_F(AltHashingTest, halfsiphash_test_CharArray) {
AltHashingTest::testHalfsiphash_32_CharArray();
}
TEST_F(AltHashingTest, halfsiphash_test_FromReference) {
AltHashingTest::testHalfsiphash_64_FromReference();
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2023, 2025, 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.
*/
#include "classfile/classLoaderData.hpp"
#include "classfile/placeholders.hpp"
#include "classfile/symbolTable.hpp"
#include "oops/symbol.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "threadHelper.inline.hpp"
#include "unittest.hpp"
// Test that multiple threads calling handle_parallel_super_load don't underflow supername refcount.
TEST_VM(PlaceholderTable, supername) {
JavaThread* THREAD = JavaThread::current();
JavaThread* T2 = THREAD;
// the thread should be in vm to use locks
ThreadInVMfromNative tivfn(THREAD);
// Assert messages assume these symbols are unique, and the refcounts start at one.
Symbol* A = SymbolTable::new_symbol("abc2_8_2023_class");
Symbol* D = SymbolTable::new_symbol("def2_8_2023_class");
Symbol* super = SymbolTable::new_symbol("super2_8_2023_supername");
Symbol* interf = SymbolTable::new_symbol("interface2_8_2023_supername");
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
{
MutexLocker ml(THREAD, SystemDictionary_lock);
PlaceholderTable::classloadAction super_action = PlaceholderTable::DETECT_CIRCULARITY;
PlaceholderTable::classloadAction define_action = PlaceholderTable::DEFINE_CLASS;
// DefineClass A and D
PlaceholderTable::find_and_add(A, loader_data, define_action, nullptr, THREAD);
PlaceholderTable::find_and_add(D, loader_data, define_action, nullptr, T2);
// Load interfaces first to get supername replaced
PlaceholderTable::find_and_add(A, loader_data, super_action, interf, THREAD);
PlaceholderTable::find_and_remove(A, loader_data, super_action, THREAD);
PlaceholderTable::find_and_add(D, loader_data, super_action, interf, T2);
PlaceholderTable::find_and_remove(D, loader_data, super_action, T2);
ASSERT_EQ(interf->refcount(), 1) << "supername is replaced with null";
// Add placeholder to the table for loading A and super, and D also loading super
PlaceholderTable::find_and_add(A, loader_data, super_action, super, THREAD);
PlaceholderTable::find_and_add(D, loader_data, super_action, super, T2);
// Another thread comes in and finds A loading Super
PlaceholderEntry* placeholder = PlaceholderTable::get_entry(A, loader_data);
SymbolHandle supername = placeholder->next_klass_name();
// Other thread is done before handle_parallel_super_load
PlaceholderTable::find_and_remove(A, loader_data, super_action, THREAD);
// if THREAD drops reference to supername (loading failed or class unloaded), we're left with
// a supername without refcount
super->decrement_refcount();
// handle_parallel_super_load (same thread doesn't assert)
PlaceholderTable::find_and_add(A, loader_data, super_action, supername, T2);
// Refcount should be 3: one in table for class A, one in table for class D
// and one locally with SymbolHandle keeping it alive
placeholder = PlaceholderTable::get_entry(A, loader_data);
supername = placeholder->next_klass_name();
EXPECT_EQ(super->refcount(), 3) << "super class name refcount should be 3";
// Second thread's done too
PlaceholderTable::find_and_remove(D, loader_data, super_action, T2);
// Other threads are done.
PlaceholderTable::find_and_remove(A, loader_data, super_action, THREAD);
// Remove A and D define_class placeholder
PlaceholderTable::find_and_remove(A, loader_data, define_action, THREAD);
PlaceholderTable::find_and_remove(D, loader_data, define_action, T2);
placeholder = PlaceholderTable::get_entry(A, loader_data);
ASSERT_TRUE(placeholder == nullptr) << "placeholder should be removed";
placeholder = PlaceholderTable::get_entry(D, loader_data);
ASSERT_TRUE(placeholder == nullptr) << "placeholder should be removed";
EXPECT_EQ(super->refcount(), 1) << "super class name refcount should be 1 - kept alive in this scope";
}
EXPECT_EQ(A->refcount(), 1) << "first lass name refcount should be 1";
EXPECT_EQ(D->refcount(), 1) << "second class name refcount should be 1";
EXPECT_EQ(super->refcount(), 0) << "super class name refcount should be 0 - was unloaded.";
// clean up temporary symbols
A->decrement_refcount();
D->decrement_refcount();
interf->decrement_refcount();
}

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
#include "runtime/interfaceSupport.inline.hpp"
#include "unittest.hpp"
// Tests that string functions (hash code/equals) stay consistant when comparing equal strings and converting between strings types
// Simple ASCII string "Java(R)!!"
// Same length in both UTF8 and Unicode
static const char static_ascii_utf8_str[] = {0x4A, 0x61, 0x76, 0x61, 0x28, 0x52, 0x29, 0x21, 0x21};
static const jchar static_ascii_unicode_str[] = {0x004A, 0x0061, 0x0076, 0x0061, 0x0028, 0x0052, 0x0029, 0x0021, 0x0021};
// Complex string "Jāvá®!☺☻", UTF8 has character lengths 13122133 = 16
static const unsigned char static_utf8_str[] = {0x4A, 0x61, 0xCC, 0x84, 0x76, 0xC3, 0xA1, 0xC2, 0xAE, 0x21, 0xE2, 0x98, 0xBA, 0xE2, 0x98, 0xBB};
static const jchar static_unicode_str[] = { 0x004A, 0x0061, 0x0304, 0x0076, 0x00E1, 0x00AE, 0x0021, 0x263A, 0x263B};
static const int ASCII_LENGTH = 9;
static const size_t UTF8_LENGTH = 16;
static const int UNICODE_LENGTH = 9;
void compare_utf8_utf8(const char* utf8_str1, const char* utf8_str2, size_t utf8_len) {
EXPECT_EQ(java_lang_String::hash_code(utf8_str1, utf8_len), java_lang_String::hash_code(utf8_str2, utf8_len));
EXPECT_STREQ(utf8_str1, utf8_str2);
}
void compare_utf8_unicode(const char* utf8_str, const jchar* unicode_str, size_t utf8_len, int unicode_len) {
EXPECT_EQ(java_lang_String::hash_code(utf8_str, utf8_len), java_lang_String::hash_code(unicode_str, unicode_len));
}
void compare_utf8_oop(const char* utf8_str, Handle oop_str, size_t utf8_len, int unicode_len) {
EXPECT_EQ(java_lang_String::hash_code(utf8_str, utf8_len), java_lang_String::hash_code(oop_str()));
EXPECT_TRUE(java_lang_String::equals(oop_str(), utf8_str, utf8_len));
}
void compare_unicode_unicode(const jchar* unicode_str1, const jchar* unicode_str2, int unicode_len) {
EXPECT_EQ(java_lang_String::hash_code(unicode_str1, unicode_len), java_lang_String::hash_code(unicode_str2, unicode_len));
for (int i = 0; i < unicode_len; i++) {
EXPECT_EQ(unicode_str1[i], unicode_str2[i]);
}
}
void compare_unicode_oop(const jchar* unicode_str, Handle oop_str, int unicode_len) {
EXPECT_EQ(java_lang_String::hash_code(unicode_str, unicode_len), java_lang_String::hash_code(oop_str()));
EXPECT_TRUE(java_lang_String::equals(oop_str(), unicode_str, unicode_len));
}
void compare_oop_oop(Handle oop_str1, Handle oop_str2) {
EXPECT_EQ(java_lang_String::hash_code(oop_str1()), java_lang_String::hash_code(oop_str2()));
EXPECT_TRUE(java_lang_String::equals(oop_str1(), oop_str2()));
}
void test_utf8_convert(const char* utf8_str, size_t utf8_len, int unicode_len) {
EXPECT_TRUE(UTF8::is_legal_utf8((unsigned char*)utf8_str, strlen(utf8_str), false));
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative ThreadInVMfromNative(THREAD);
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
jchar* unicode_str_from_utf8 = NEW_RESOURCE_ARRAY(jchar, unicode_len);
UTF8::convert_to_unicode(utf8_str, unicode_str_from_utf8, unicode_len);
Handle oop_str_from_utf8 = java_lang_String::create_from_str(utf8_str, THREAD);
compare_utf8_unicode(utf8_str, unicode_str_from_utf8, utf8_len, unicode_len);
compare_utf8_oop(utf8_str, oop_str_from_utf8, utf8_len, unicode_len);
size_t length = unicode_len;
const char* utf8_str_from_unicode = UNICODE::as_utf8(unicode_str_from_utf8, length);
const char* utf8_str_from_oop = java_lang_String::as_utf8_string(oop_str_from_utf8());
EXPECT_TRUE(UTF8::is_legal_utf8((unsigned char*)utf8_str_from_unicode, strlen(utf8_str_from_unicode), false));
EXPECT_TRUE(UTF8::is_legal_utf8((unsigned char*)utf8_str_from_oop, strlen(utf8_str_from_oop), false));
compare_utf8_utf8(utf8_str, utf8_str_from_unicode, utf8_len);
compare_utf8_utf8(utf8_str, utf8_str_from_oop, utf8_len);
}
void test_unicode_convert(const jchar* unicode_str, size_t utf8_len, int unicode_len) {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative ThreadInVMfromNative(THREAD);
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
size_t length = unicode_len;
const char* utf8_str_from_unicode = UNICODE::as_utf8(unicode_str, length);
Handle oop_str_from_unicode = java_lang_String::create_from_unicode(unicode_str, unicode_len, THREAD);
EXPECT_TRUE(UTF8::is_legal_utf8((unsigned char*)utf8_str_from_unicode, strlen(utf8_str_from_unicode), false));
compare_utf8_unicode(utf8_str_from_unicode, unicode_str, utf8_len, unicode_len);
compare_unicode_oop(unicode_str, oop_str_from_unicode, unicode_len);
int _;
jchar* unicode_str_from_utf8 = NEW_RESOURCE_ARRAY(jchar, unicode_len);
UTF8::convert_to_unicode(utf8_str_from_unicode, unicode_str_from_utf8, unicode_len);
const jchar* unicode_str_from_oop = java_lang_String::as_unicode_string(oop_str_from_unicode(), _, THREAD);
compare_unicode_unicode(unicode_str, unicode_str_from_utf8, unicode_len);
compare_unicode_unicode(unicode_str, unicode_str_from_oop, unicode_len);
}
void test_utf8_unicode_cross(const char* utf8_str, const jchar* unicode_str, size_t utf8_len, int unicode_len) {
compare_utf8_unicode(utf8_str, unicode_str, utf8_len, unicode_len);
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative ThreadInVMfromNative(THREAD);
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
size_t length = unicode_len;
const char* utf8_str_from_unicode = UNICODE::as_utf8(unicode_str, length);
jchar* unicode_str_from_utf8 = NEW_RESOURCE_ARRAY(jchar, unicode_len);
UTF8::convert_to_unicode(utf8_str, unicode_str_from_utf8, unicode_len);
Handle oop_str_from_unicode = java_lang_String::create_from_unicode(unicode_str, unicode_len, THREAD);
Handle oop_str_from_utf8 = java_lang_String::create_from_str(utf8_str, THREAD);
compare_utf8_utf8(utf8_str, utf8_str_from_unicode, utf8_len);
compare_utf8_oop(utf8_str, oop_str_from_unicode, utf8_len, unicode_len);
compare_unicode_unicode(unicode_str, unicode_str_from_utf8, unicode_len);
compare_unicode_oop(unicode_str, oop_str_from_utf8, unicode_len);
compare_utf8_oop(utf8_str_from_unicode, oop_str_from_utf8, utf8_len, unicode_len);
compare_unicode_oop(unicode_str_from_utf8, oop_str_from_unicode, unicode_len);
compare_utf8_unicode(utf8_str_from_unicode, unicode_str_from_utf8, utf8_len, unicode_len);
compare_oop_oop(oop_str_from_utf8, oop_str_from_unicode);
}
TEST_VM(StringConversion, fromUTF8_ascii) {
const char utf8_str[ASCII_LENGTH + 1] = { };
memcpy((unsigned char*)utf8_str, static_ascii_utf8_str, ASCII_LENGTH);
test_utf8_convert(utf8_str, ASCII_LENGTH, ASCII_LENGTH);
}
TEST_VM(StringConversion, fromUTF8_varlen) {
const char utf8_str[UTF8_LENGTH + 1] = { };
memcpy((unsigned char*)utf8_str, static_utf8_str, UTF8_LENGTH);
test_utf8_convert(utf8_str, UTF8_LENGTH, UNICODE_LENGTH);
}
TEST_VM(StringConversion, fromUnicode_ascii) {
jchar unicode_str[ASCII_LENGTH] = { };
memcpy(unicode_str, static_ascii_unicode_str, ASCII_LENGTH * sizeof(jchar));
test_unicode_convert(unicode_str, ASCII_LENGTH, ASCII_LENGTH);
}
TEST_VM(StringConversion, fromUnicode_varlen) {
jchar unicode_str[UNICODE_LENGTH] = { };
memcpy(unicode_str, static_unicode_str, UNICODE_LENGTH * sizeof(jchar));
test_unicode_convert(unicode_str, UTF8_LENGTH, UNICODE_LENGTH);
}
TEST_VM(StringConversion, cross_ascii) {
const char utf8_str[ASCII_LENGTH + 1] = { };
jchar unicode_str[ASCII_LENGTH] = { };
memcpy((unsigned char*)utf8_str, static_ascii_utf8_str, ASCII_LENGTH);
memcpy(unicode_str, static_ascii_unicode_str, ASCII_LENGTH * sizeof(jchar));
test_utf8_unicode_cross(utf8_str, unicode_str, ASCII_LENGTH, ASCII_LENGTH);
}
TEST_VM(StringConversion, cross_varlen) {
const char utf8_str[UTF8_LENGTH + 1] = { };
jchar unicode_str[UNICODE_LENGTH] = { };
memcpy((unsigned char*)utf8_str, static_utf8_str, UTF8_LENGTH);
memcpy(unicode_str, static_unicode_str, UNICODE_LENGTH * sizeof(jchar));
test_utf8_unicode_cross(utf8_str, unicode_str, UTF8_LENGTH, UNICODE_LENGTH);
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "unittest.hpp"
// Tests that strings are interned and returns the same string when interning from different string types
// Simple ASCII string "Java(R)!!"
static const char static_ascii_utf8_str[] = {0x4A, 0x61, 0x76, 0x61, 0x28, 0x52, 0x29, 0x21, 0x21};
static const size_t ASCII_LENGTH = 9;
// Complex string "Jāvá®!☺☻", has character lengths 13122133 = 16
static const unsigned char static_utf8_str[] = {0x4A, 0x61, 0xCC, 0x84, 0x76, 0xC3, 0xA1, 0xC2, 0xAE, 0x21, 0xE2, 0x98, 0xBA, 0xE2, 0x98, 0xBB};
static const size_t COMPLEX_LENGTH = 16;
void test_intern(const char* utf8_str, size_t utf8_length) {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative ThreadInVMfromNative(THREAD);
HandleMark hm(THREAD);
oop interned_string_from_utf8 = StringTable::intern(utf8_str, THREAD);
EXPECT_TRUE(java_lang_String::equals(interned_string_from_utf8, utf8_str, utf8_length));
EXPECT_EQ(java_lang_String::hash_code(utf8_str, utf8_length),java_lang_String::hash_code(interned_string_from_utf8));
Symbol* symbol_from_utf8 = SymbolTable::new_symbol(utf8_str, static_cast<int>(utf8_length));
oop interned_string_from_symbol = StringTable::intern(symbol_from_utf8, THREAD);
EXPECT_EQ(interned_string_from_utf8, interned_string_from_symbol);
oop interned_string_from_oop1 = StringTable::intern(interned_string_from_utf8, THREAD);
EXPECT_EQ(interned_string_from_utf8, interned_string_from_oop1);
}
TEST_VM(StringIntern, intern_ascii) {
const char utf8_str[ASCII_LENGTH + 1] = { };
memcpy((unsigned char*)utf8_str, static_ascii_utf8_str, ASCII_LENGTH);
test_intern(utf8_str, ASCII_LENGTH);
}
TEST_VM(StringIntern, intern_varlen) {
const char utf8_str[COMPLEX_LENGTH + 1] = { };
memcpy((unsigned char*)utf8_str, static_utf8_str, COMPLEX_LENGTH);
test_intern(utf8_str, COMPLEX_LENGTH);
}

View file

@ -0,0 +1,195 @@
/*
* Copyright (c) 2016, 2025, 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.
*/
#include "classfile/symbolTable.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "threadHelper.inline.hpp"
#include "unittest.hpp"
// Helper to avoid interference from the cleanup delay queue by draining it
// immediately after creation.
static TempNewSymbol stable_temp_symbol(Symbol* sym) {
TempNewSymbol t = sym;
TempSymbolCleanupDelayer::drain_queue();
return t;
}
TEST_VM(SymbolTable, temp_new_symbol) {
// Assert messages assume these symbols are unique, and the refcounts start at
// one, but code does not rely on this.
JavaThread* THREAD = JavaThread::current();
// the thread should be in vm to use locks
ThreadInVMfromNative ThreadInVMfromNative(THREAD);
Symbol* abc = SymbolTable::new_symbol("abc");
int abccount = abc->refcount();
TempNewSymbol ss = stable_temp_symbol(abc);
ASSERT_EQ(ss->refcount(), abccount) << "only one abc";
ASSERT_EQ(ss->refcount(), abc->refcount()) << "should match TempNewSymbol";
Symbol* efg = SymbolTable::new_symbol("efg");
Symbol* hij = SymbolTable::new_symbol("hij");
int efgcount = efg->refcount();
int hijcount = hij->refcount();
TempNewSymbol s1 = stable_temp_symbol(efg);
TempNewSymbol s2 = stable_temp_symbol(hij);
ASSERT_EQ(s1->refcount(), efgcount) << "one efg";
ASSERT_EQ(s2->refcount(), hijcount) << "one hij";
// Assignment operator
s1 = s2;
ASSERT_EQ(hij->refcount(), hijcount + 1) << "should be two hij";
ASSERT_EQ(efg->refcount(), efgcount - 1) << "should be no efg";
s1 = ss; // s1 is abc
ASSERT_EQ(s1->refcount(), abccount + 1) << "should be two abc (s1 and ss)";
ASSERT_EQ(hij->refcount(), hijcount) << "should only have one hij now (s2)";
s1 = *&s1; // self assignment
ASSERT_EQ(s1->refcount(), abccount + 1) << "should still be two abc (s1 and ss)";
TempNewSymbol s3;
Symbol* klm = SymbolTable::new_symbol("klm");
int klmcount = klm->refcount();
s3 = stable_temp_symbol(klm); // assignment
ASSERT_EQ(s3->refcount(), klmcount) << "only one klm now";
Symbol* xyz = SymbolTable::new_symbol("xyz");
int xyzcount = xyz->refcount();
{ // inner scope
TempNewSymbol s_inner = stable_temp_symbol(xyz);
}
ASSERT_EQ(xyz->refcount(), xyzcount - 1)
<< "Should have been decremented by dtor in inner scope";
// Test overflowing refcount making symbol permanent
Symbol* bigsym = SymbolTable::new_symbol("bigsym");
for (int i = 0; i < PERM_REFCOUNT + 100; i++) {
bigsym->increment_refcount();
}
ASSERT_EQ(bigsym->refcount(), PERM_REFCOUNT) << "should not have overflowed";
// Test that PERM_REFCOUNT is sticky
for (int i = 0; i < 10; i++) {
bigsym->decrement_refcount();
}
ASSERT_EQ(bigsym->refcount(), PERM_REFCOUNT) << "should be sticky";
}
// TODO: Make two threads one decrementing the refcount and the other trying to increment.
// try_increment_refcount should return false
TEST_VM(SymbolTable, test_symbol_refcount_parallel) {
constexpr int symbol_name_length = 30;
char symbol_name[symbol_name_length];
// Find a symbol where there will probably be only one instance.
for (int i = 0; i < 100; i++) {
os::snprintf_checked(symbol_name, symbol_name_length, "some_symbol%d", i);
TempNewSymbol ts = SymbolTable::new_symbol(symbol_name);
if (ts->refcount() == 1) {
EXPECT_TRUE(ts->refcount() == 1) << "Symbol is just created";
break; // found a unique symbol
}
}
constexpr int symTestThreadCount = 5;
auto symbolThread= [&](Thread* _current, int _id) {
for (int i = 0; i < 1000; i++) {
TempNewSymbol sym = SymbolTable::new_symbol(symbol_name);
// Create and destroy new symbol
EXPECT_TRUE(sym->refcount() != 0) << "Symbol refcount unexpectedly zeroed";
}
};
TestThreadGroup<decltype(symbolThread)> ttg(symbolThread, symTestThreadCount);
ttg.doit();
ttg.join();
}
TEST_VM_FATAL_ERROR_MSG(SymbolTable, test_symbol_underflow, ".*refcount has gone to zero.*") {
Symbol* my_symbol = SymbolTable::new_symbol("my_symbol2023");
EXPECT_TRUE(my_symbol->refcount() == 1) << "Symbol refcount just created is 1";
my_symbol->decrement_refcount();
my_symbol->increment_refcount(); // Should crash even in PRODUCT mode
}
TEST_VM(SymbolTable, test_cleanup_leak) {
// Check that dead entry cleanup doesn't increment refcount of live entry in same bucket.
// Create symbol and release ref, marking it available for cleanup.
Symbol* entry1 = SymbolTable::new_symbol("hash_collision_123");
entry1->decrement_refcount();
// Create a new symbol in the same bucket, which will notice the dead entry and trigger cleanup.
// Note: relies on SymbolTable's use of String::hashCode which collides for these two values.
Symbol* entry2 = SymbolTable::new_symbol("hash_collision_397476851");
ASSERT_EQ(entry2->refcount(), 1) << "Symbol refcount just created is 1";
}
TEST_VM(SymbolTable, test_cleanup_delay) {
// Check that new temp symbols have an extra refcount increment, which is then
// decremented when the queue spills over.
TempNewSymbol s1 = SymbolTable::new_symbol("temp-s1");
ASSERT_EQ(s1->refcount(), 2) << "TempNewSymbol refcount just created is 2";
// Fill up the queue
constexpr int symbol_name_length = 30;
char symbol_name[symbol_name_length];
for (uint i = 1; i < TempSymbolCleanupDelayer::QueueSize; i++) {
os::snprintf_checked(symbol_name, symbol_name_length, "temp-filler-%d", i);
TempNewSymbol s = SymbolTable::new_symbol(symbol_name);
ASSERT_EQ(s->refcount(), 2) << "TempNewSymbol refcount just created is 2";
}
// Add one more
TempNewSymbol spillover = SymbolTable::new_symbol("temp-spillover");
ASSERT_EQ(spillover->refcount(), 2) << "TempNewSymbol refcount just created is 2";
// The first symbol should have been removed from the queue and decremented
ASSERT_EQ(s1->refcount(), 1) << "TempNewSymbol off queue refcount is 1";
}
TEST_VM(SymbolTable, test_cleanup_delay_drain) {
// Fill up the queue
constexpr int symbol_name_length = 30;
char symbol_name[symbol_name_length];
TempNewSymbol symbols[TempSymbolCleanupDelayer::QueueSize] = {};
for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
os::snprintf_checked(symbol_name, symbol_name_length, "temp-%d", i);
TempNewSymbol s = SymbolTable::new_symbol(symbol_name);
symbols[i] = s;
}
// While in the queue refcounts are incremented
for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
ASSERT_EQ(symbols[i]->refcount(), 2) << "TempNewSymbol refcount in queue is 2";
}
// Draining the queue should decrement the refcounts
TempSymbolCleanupDelayer::drain_queue();
for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
ASSERT_EQ(symbols[i]->refcount(), 1) << "TempNewSymbol refcount after drain is 1";
}
}

View file

@ -0,0 +1,280 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
#ifndef PRODUCT
#ifndef ZERO
#include "asm/macroAssembler.inline.hpp"
#include "compiler/disassembler.hpp"
#include "memory/resourceArea.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/vmassert_uninstall.hpp"
BEGIN_ALLOW_FORBIDDEN_FUNCTIONS
#include <regex>
END_ALLOW_FORBIDDEN_FUNCTIONS
#include "utilities/vmassert_reinstall.hpp"
#include "unittest.hpp"
static const char* replace_addr_expr(const char* str)
{
// Remove any address expression "0x0123456789abcdef" found in order to
// aid string comparison. Also remove any trailing printout from a padded
// buffer (too brittle?).
std::basic_string<char> tmp1 = std::regex_replace(str, std::regex("0x[0-9a-fA-F]+"), "<addr>");
// Padding: aarch64
std::basic_string<char> tmp2 = std::regex_replace(tmp1, std::regex("\\s+<addr>:\\s+\\.inst\\t<addr> ; undefined"), "");
std::basic_string<char> tmp3 = std::regex_replace(tmp2, std::regex("\\s+<addr>:\\s+udf\\t#0"), "");
// Padding: riscv
std::basic_string<char> tmp4 = std::regex_replace(tmp3, std::regex("\\s+<addr>:\\s+unimp"), "");
// Padding: x64
std::basic_string<char> tmp5 = std::regex_replace(tmp4, std::regex("\\s+<addr>:\\s+hlt[ \\t]+(?!\\n\\s+;;)"), "");
std::basic_string<char> red = std::regex_replace(tmp5, std::regex("(\\s+<addr>:\\s+nop)[ \\t]*"), "$1");
return os::strdup(red.c_str());
}
static const char* delete_header_line(const char* str)
{
// Remove (second) header line in output, e.g.:
// Decoding CodeBlob, name: CodeStringTest, at [<addr>, <addr>] 8 bytes\n
std::basic_string<char> red = std::regex_replace(str, std::regex("Decoding.+bytes\\n"), "");
return os::strdup(red.c_str());
}
static void asm_remarks_check(const AsmRemarks &rem1,
const AsmRemarks &rem2)
{
ASSERT_EQ(rem1.ref(), rem2.ref()) << "Should share the same collection.";
}
static void dbg_strings_check(const DbgStrings &dbg1,
const DbgStrings &dbg2)
{
ASSERT_EQ(dbg1.ref(), dbg2.ref()) << "Should share the same collection.";
}
static void disasm_string_check(CodeBuffer* cbuf, CodeBlob* blob)
{
if (Disassembler::is_abstract())
{
return; // No disassembler available (no comments will be used).
}
stringStream out1, out2;
Disassembler::decode(cbuf->insts_begin(), cbuf->insts_end(), &out1, &cbuf->asm_remarks());
Disassembler::decode(blob->code_begin(), blob->code_end(), &out2, &blob->asm_remarks());
EXPECT_STREQ(replace_addr_expr(out1.as_string()),
replace_addr_expr(out2.as_string()))
<< "1. Output should be identical.";
stringStream out3;
Disassembler::decode(blob, &out3);
EXPECT_STREQ(replace_addr_expr(out2.as_string()),
replace_addr_expr(delete_header_line(out3.as_string())))
<< "2. Output should be identical.";
}
static void copy_and_compare(CodeBuffer* cbuf)
{
bool remarks_empty = cbuf->asm_remarks().is_empty();
bool strings_empty = cbuf->dbg_strings().is_empty();
BufferBlob* blob = BufferBlob::create("CodeBuffer Copy&Compare", cbuf);
// 1. Check Assembly Remarks are shared by buffer and blob.
asm_remarks_check(cbuf->asm_remarks(), blob->asm_remarks());
// 2. Check Debug Strings are shared by buffer and blob.
dbg_strings_check(cbuf->dbg_strings(), blob->dbg_strings());
// 3. Check that the disassembly output matches.
disasm_string_check(cbuf, blob);
BufferBlob::free(blob);
ASSERT_EQ(remarks_empty, cbuf->asm_remarks().is_empty())
<< "Expecting property to be unchanged.";
ASSERT_EQ(strings_empty, cbuf->dbg_strings().is_empty())
<< "Expecting property to be unchanged.";
}
static void code_buffer_test()
{
constexpr int BUF_SZ = 256;
ResourceMark rm;
CodeBuffer cbuf("CodeStringTest", BUF_SZ, BUF_SZ);
MacroAssembler as(&cbuf);
ASSERT_TRUE(cbuf.asm_remarks().is_empty());
ASSERT_TRUE(cbuf.dbg_strings().is_empty());
ASSERT_TRUE(cbuf.blob()->asm_remarks().is_empty());
ASSERT_TRUE(cbuf.blob()->dbg_strings().is_empty());
int re, sz, n;
re = cbuf.insts_remaining();
// 1. Generate a first entry.
as.block_comment("First block comment.");
as.nop();
sz = re - cbuf.insts_remaining();
ASSERT_TRUE(sz > 0);
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_TRUE(cbuf.dbg_strings().is_empty());
ASSERT_TRUE(cbuf.blob()->asm_remarks().is_empty());
ASSERT_TRUE(cbuf.blob()->dbg_strings().is_empty());
copy_and_compare(&cbuf);
n = re/sz;
ASSERT_TRUE(n > 0);
// 2. Generate additional entries without causing the buffer to expand.
for (unsigned i = 0; i < unsigned(n)/2; i++)
{
ASSERT_FALSE(cbuf.insts()->maybe_expand_to_ensure_remaining(sz));
ASSERT_TRUE(cbuf.insts_remaining()/sz >= n/2);
stringStream strm;
strm.print("Comment No. %d", i);
as.block_comment(strm.as_string());
as.nop();
}
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
copy_and_compare(&cbuf);
re = cbuf.insts_remaining();
// 3. Generate a single code with a debug string.
as.unimplemented("First debug string.");
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_FALSE(cbuf.dbg_strings().is_empty());
sz = re - cbuf.insts_remaining();
n = (re - sz)/sz;
ASSERT_TRUE(n > 0);
// 4. Generate additional code with debug strings.
for (unsigned i = 0; i < unsigned(n); i++)
{
ASSERT_TRUE(cbuf.insts_remaining() >= sz);
stringStream strm;
strm.print("Fixed address string No. %d", i);
as.unimplemented(strm.as_string());
}
ASSERT_TRUE(cbuf.insts_remaining() >= 0);
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_FALSE(cbuf.dbg_strings().is_empty());
ASSERT_TRUE(cbuf.blob()->asm_remarks().is_empty());
ASSERT_TRUE(cbuf.blob()->dbg_strings().is_empty());
copy_and_compare(&cbuf);
}
static void buffer_blob_test()
{
constexpr int BUF_SZ = 256;
ResourceMark rm;
BufferBlob* blob = BufferBlob::create("BufferBlob Test", BUF_SZ);
CodeBuffer cbuf(blob);
MacroAssembler as(&cbuf);
ASSERT_FALSE(cbuf.insts()->has_locs());
// The x86-64 version of 'stop' will use relocation info. that will result
// in tainting the location start and limit if no location info. buffer is
// present.
static uint8_t s_loc_buf[BUF_SZ]; // Raw memory buffer used for relocInfo.
cbuf.insts()->initialize_shared_locs((relocInfo*)&s_loc_buf[0], BUF_SZ);
int re = cbuf.insts_remaining();
as.block_comment("First block comment.");
as.nop();
as.unimplemented("First debug string.");
int sz = re - cbuf.insts_remaining();
ASSERT_TRUE(sz > 0);
constexpr int LIM_GEN = 51; // Limit number of entries generated.
for (unsigned i = 0; i < LIM_GEN; i++)
{
if (cbuf.insts_remaining() < sz) break;
stringStream strm1;
strm1.print("Comment No. %d", i);
as.block_comment(strm1.as_string());
as.nop();
stringStream strm2;
strm2.print("Fixed address string No. %d", i);
as.unimplemented(strm2.as_string());
}
ASSERT_TRUE(cbuf.insts_remaining() >= 0);
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_FALSE(cbuf.dbg_strings().is_empty());
copy_and_compare(&cbuf);
ASSERT_TRUE(blob->asm_remarks().is_empty());
ASSERT_TRUE(blob->dbg_strings().is_empty());
BufferBlob::free(blob);
}
#if defined(PPC) || defined(S390)
// Neither ppc nor s390 compiler use code strings
TEST_VM(codestrings, DISABLED_validate)
#else
TEST_VM(codestrings, validate)
#endif
{
code_buffer_test();
buffer_blob_test();
}
#endif // not ZERO
#endif // not PRODUCT

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2017, 2025, 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.
*
*/
#include "code/vtableStubs.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "unittest.hpp"
#ifndef ZERO
TEST_VM(code, vtableStubs) {
// Should be in VM to use locks
ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current());
VtableStubs::find_vtable_stub(0); // min vtable index
for (int i = 0; i < 15; i++) {
VtableStubs::find_vtable_stub((1 << i) - 1);
VtableStubs::find_vtable_stub((1 << i));
}
VtableStubs::find_vtable_stub((1 << 15) - 1); // max vtable index
}
TEST_VM(code, itableStubs) {
// Should be in VM to use locks
ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current());
VtableStubs::find_itable_stub(0); // min itable index
for (int i = 0; i < 15; i++) {
VtableStubs::find_itable_stub((1 << i) - 1);
VtableStubs::find_itable_stub((1 << i));
}
VtableStubs::find_itable_stub((1 << 15) - 1); // max itable index
}
#endif

View file

@ -0,0 +1,224 @@
/*
* Copyright (c) 2016, 2025, 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.
*/
#include "compiler/directivesParser.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/thread.hpp"
#include "unittest.hpp"
#include <locale.h>
class DirectivesParserTest : public ::testing::Test{
protected:
char* const _locale;
ResourceMark rm;
stringStream stream;
// These tests require the "C" locale to correctly parse decimal values
DirectivesParserTest() : _locale(os::strdup(setlocale(LC_NUMERIC, nullptr), mtTest)) {
setlocale(LC_NUMERIC, "C");
}
~DirectivesParserTest() {
setlocale(LC_NUMERIC, _locale);
os::free(_locale);
}
void test_negative(const char* text) {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative ThreadInVMfromNative(THREAD);
DirectivesParser cd(text, &stream, false);
cd.clean_tmp();
EXPECT_FALSE(cd.valid()) << "text: " << std::endl << text << std::endl << stream.as_string();
}
void test_positive(const char* text) {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative ThreadInVMfromNative(THREAD);
DirectivesParser cd(text, &stream, false);
cd.clean_tmp();
EXPECT_TRUE(cd.valid()) << "text: " << std::endl << text << std::endl << stream.as_string();
}
};
TEST_VM_F(DirectivesParserTest, empty_object) {
test_negative("{}");
}
TEST_VM_F(DirectivesParserTest, empty_array) {
test_positive("[]");
}
TEST_VM_F(DirectivesParserTest, empty_object_in_array) {
test_negative("[{}]");
}
TEST_VM_F(DirectivesParserTest, empty_objects_in_array) {
test_negative("[{},{}]");
}
TEST_VM_F(DirectivesParserTest, empty_objects) {
test_negative("{},{}");
}
TEST_VM_F(DirectivesParserTest, simple_match) {
test_positive(
"[" "\n"
" {" "\n"
" match: \"foo/bar.*\"," "\n"
" inline : \"+java/util.*\"," "\n"
" PrintAssembly: true," "\n"
" BreakAtExecute: true," "\n"
" }" "\n"
"]" "\n");
}
TEST_VM_F(DirectivesParserTest, control_intrinsic) {
test_positive(
"[" "\n"
" {" "\n"
" match: \"foo/bar.*\"," "\n"
" c2: {" "\n"
" DisableIntrinsic: \"_compareToL\"," "\n"
" ControlIntrinsic: \"+_mulAdd,+_getInt,-_arraycopy,+_compareToL\"" "\n"
" }" "\n"
" }" "\n"
"]" "\n");
}
TEST_VM_F(DirectivesParserTest, nesting_arrays) {
test_negative(
"[" "\n"
" [" "\n"
" {" "\n"
" match: \"foo/bar.*\"," "\n"
" inline : \"+java/util.*\"," "\n"
" PrintAssembly: true," "\n"
" BreakAtExecute: true," "\n"
" }" "\n"
" ]" "\n"
"]" "\n");
}
TEST_VM_F(DirectivesParserTest, c1_block) {
test_positive(
"[" "\n"
" {" "\n"
" match: \"foo/bar.*\"," "\n"
" c1: {"
" PrintInlining: false," "\n"
" }" "\n"
" }" "\n"
"]" "\n");
}
TEST_VM_F(DirectivesParserTest, c2_block) {
test_positive(
"[" "\n"
" {" "\n"
" match: \"foo/bar.*\"," "\n"
" c2: {" "\n"
" PrintInlining: false," "\n"
" }" "\n"
" }" "\n"
"]" "\n");
}
TEST_VM_F(DirectivesParserTest, boolean_array) {
test_negative(
"[" "\n"
" {" "\n"
" match: \"foo/bar.*\"," "\n"
" PrintInlining: [" "\n"
" true," "\n"
" false" "\n"
" ]," "\n"
" }" "\n"
"]" "\n");
}
TEST_VM_F(DirectivesParserTest, multiple_objects) {
test_positive(
"[" "\n"
" {"
" // pattern to match against class+method+signature" "\n"
" // leading and trailing wildcard (*) allowed" "\n"
" match: \"foo/bar.*\"," "\n"
"" "\n"
" // override defaults for specified compiler" "\n"
" // we may differentiate between levels too. TBD." "\n"
" c1: {" "\n"
" //override c1 presets " "\n"
" DumpReplay: false," "\n"
" BreakAtCompile: true," "\n"
" }," "\n"
"" "\n"
" c2: {" "\n"
" // control inlining of method" "\n"
" // + force inline, - dont inline" "\n"
" inline : \"+java/util.*\"," "\n"
" PrintInlining: true," "\n"
" }," "\n"
"" "\n"
" // directives outside a specific preset applies to all compilers" "\n"
" inline : [ \"+java/util.*\", \"-com/sun.*\"]," "\n"
" BreakAtExecute: true," "\n"
" Log: true," "\n"
" }," "\n"
" {" "\n"
" // matching several patterns require an array" "\n"
" match: [\"baz.*\",\"frob.*\"]," "\n"
"" "\n"
" // applies to all compilers" "\n"
" // + force inline, - dont inline" "\n"
" inline : [ \"+java/util.*\", \"-com/sun.*\" ]," "\n"
" PrintInlining: true," "\n"
"" "\n"
" // force matching compiles to be blocking/syncronous" "\n"
" PrintNMethods: true" "\n"
" }," "\n"
"]" "\n");
}
// Test max stack depth
TEST_VM_F(DirectivesParserTest, correct_max_stack_depth) {
test_positive(
"[" "\n" // depth 1: type_dir_array
" {" "\n" // depth 2: type_directives
" match: \"*.*\"," // match required
" c1:" "\n" // depth 3: type_c1
" {" "\n"
" inline:" "\n" // depth 4: type_inline
" [" "\n" // depth 5: type_value_array
" \"foo\"," "\n"
" \"bar\"," "\n"
" ]" "\n" // depth 3: pop type_value_array and type_inline keys
" }" "\n" // depth 2: pop type_c1 key
" }" "\n" // depth 1: pop type_directives key
"]" "\n"); // depth 0: pop type_dir_array key
}
// Test max stack depth
TEST_VM_F(DirectivesParserTest, incorrect_max_stack_depth) {
test_negative("[{c1:{c1:{c1:{c1:{c1:{c1:{c1:{}}}}}}}}]");
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2021, 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.
*/
#ifndef GTEST_CONCURRENT_TEST_RUNNER_INLINE_HPP
#define GTEST_CONCURRENT_TEST_RUNNER_INLINE_HPP
#include "memory/allocation.hpp"
#include "runtime/semaphore.hpp"
#include "runtime/thread.inline.hpp"
#include "threadHelper.inline.hpp"
// This file contains helper classes to run unit tests concurrently in multiple threads.
// Base class for test runnable. Override runUnitTest() to specify what to run.
class TestRunnable {
public:
virtual void runUnitTest() const = 0;
};
// This class represents a thread for a unit test.
class UnitTestThread : public JavaTestThread {
public:
// runnableArg - what to run
// doneArg - a semaphore to notify when the thread is done running
// testDurationArg - how long to run (in milliseconds)
UnitTestThread(TestRunnable* const runnableArg, Semaphore* doneArg, const long testDurationArg) :
JavaTestThread(doneArg), runnable(runnableArg), testDuration(testDurationArg) {}
// from JavaTestThread
void main_run() {
long stopTime = os::javaTimeMillis() + testDuration;
while (os::javaTimeMillis() < stopTime) {
runnable->runUnitTest();
}
}
private:
TestRunnable* const runnable;
const long testDuration;
};
// Helper class for running a given unit test concurrently in multiple threads.
class ConcurrentTestRunner {
public:
// runnableArg - what to run
// nrOfThreadsArg - how many threads to use concurrently
// testDurationMillisArg - duration for each test run
ConcurrentTestRunner(TestRunnable* const runnableArg, int nrOfThreadsArg, long testDurationMillisArg) :
unitTestRunnable(runnableArg),
nrOfThreads(nrOfThreadsArg),
testDurationMillis(testDurationMillisArg) {}
void run() {
Semaphore done(0);
UnitTestThread** t = NEW_C_HEAP_ARRAY(UnitTestThread*, nrOfThreads, mtInternal);
for (int i = 0; i < nrOfThreads; i++) {
t[i] = new UnitTestThread(unitTestRunnable, &done, testDurationMillis);
}
for (int i = 0; i < nrOfThreads; i++) {
t[i]->doit();
}
for (int i = 0; i < nrOfThreads; i++) {
done.wait();
}
FREE_C_HEAP_ARRAY(UnitTestThread**, t);
}
private:
TestRunnable* const unitTestRunnable;
const int nrOfThreads;
const long testDurationMillis;
};
#endif // GTEST_CONCURRENT_TEST_RUNNER_INLINE_HPP

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2011, 2025, 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.
*/
#include "gc/g1/g1BlockOffsetTable.inline.hpp"
#include "gc/g1/g1CardSet.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1HeapRegion.inline.hpp"
#include "gc/g1/g1HeapRegionSet.hpp"
#include "gc/g1/g1RegionToSpaceMapper.hpp"
#include "memory/allocation.hpp"
#include "memory/memoryReserver.hpp"
#include "memory/memRegion.hpp"
#include "unittest.hpp"
// @requires UseG1GC
TEST_OTHER_VM(G1FreeRegionList, length) {
if (!UseG1GC) {
return;
}
G1FreeRegionList l("test");
const uint num_regions_in_test = 5;
// Create a fake heap. It does not need to be valid, as the G1HeapRegion constructor
// does not access it.
const size_t szw = num_regions_in_test * G1HeapRegion::GrainWords;
const size_t sz = szw * BytesPerWord;
char* addr = os::reserve_memory_aligned(sz, G1HeapRegion::GrainBytes, mtTest);
MemRegion heap((HeapWord*)addr, szw);
// Allocate a fake BOT because the G1HeapRegion constructor initializes
// the BOT.
size_t bot_size = G1BlockOffsetTable::compute_size(heap.word_size());
HeapWord* bot_data = NEW_C_HEAP_ARRAY(HeapWord, bot_size, mtGC);
ReservedSpace bot_rs = MemoryReserver::reserve(G1BlockOffsetTable::compute_size(heap.word_size()), mtGC);
G1RegionToSpaceMapper* bot_storage =
G1RegionToSpaceMapper::create_mapper(bot_rs,
bot_rs.size(),
os::vm_page_size(),
G1HeapRegion::GrainBytes,
CardTable::card_size(),
mtGC);
G1BlockOffsetTable bot(heap, bot_storage);
bot_storage->commit_regions(0, num_regions_in_test);
// Set up memory regions for the heap regions.
MemRegion mr0(heap.start(), G1HeapRegion::GrainWords);
MemRegion mr1(mr0.end(), G1HeapRegion::GrainWords);
MemRegion mr2(mr1.end(), G1HeapRegion::GrainWords);
MemRegion mr3(mr2.end(), G1HeapRegion::GrainWords);
MemRegion mr4(mr3.end(), G1HeapRegion::GrainWords);
G1CardSetConfiguration config;
G1HeapRegion hr0(0, &bot, mr0, &config);
G1HeapRegion hr1(1, &bot, mr1, &config);
G1HeapRegion hr2(2, &bot, mr2, &config);
G1HeapRegion hr3(3, &bot, mr3, &config);
G1HeapRegion hr4(4, &bot, mr4, &config);
l.add_ordered(&hr1);
l.add_ordered(&hr0);
l.add_ordered(&hr3);
l.add_ordered(&hr4);
l.add_ordered(&hr2);
EXPECT_EQ(l.length(), num_regions_in_test) << "Wrong free region list length";
l.verify_list();
bot_storage->uncommit_regions(0, num_regions_in_test);
delete bot_storage;
os::release_memory(addr, sz);
FREE_C_HEAP_ARRAY(HeapWord, bot_data);
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2017, 2025, 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.
*
*/
#include "gc/g1/g1Analytics.hpp"
#include "gc/g1/g1Predictions.hpp"
#include "unittest.hpp"
TEST_VM(G1Analytics, is_initialized) {
G1Predictions p(0.888888); // the actual sigma value doesn't matter
G1Analytics a(&p);
ASSERT_EQ(a.long_term_gc_time_ratio(), 0.0);
ASSERT_EQ(a.short_term_gc_time_ratio(), 0.0);
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2021, 2026, 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.
*
*/
#include "cppstdlib/new.hpp"
#include "gc/g1/g1BatchedTask.hpp"
#include "gc/shared/workerThread.hpp"
#include "runtime/atomic.hpp"
#include "unittest.hpp"
class G1BatchedTaskWorkers : AllStatic {
static WorkerThreads* _workers;
static WorkerThreads* workers() {
if (_workers == nullptr) {
_workers = new WorkerThreads("G1 Small Workers", MaxWorkers);
_workers->initialize_workers();
_workers->set_active_workers(MaxWorkers);
}
return _workers;
}
public:
static const uint MaxWorkers = 4;
static void run_task(WorkerTask* task) {
workers()->run_task(task);
}
};
WorkerThreads* G1BatchedTaskWorkers::_workers = nullptr;
class G1TestSubTask : public G1AbstractSubTask {
mutable uint _phase;
Atomic<uint> _num_do_work; // Amount of do_work() has been called.
void check_and_inc_phase(uint expected) const {
ASSERT_EQ(_phase, expected);
_phase++;
}
Atomic<bool>* _do_work_called_by;
protected:
uint _max_workers;
void do_work_called(uint worker_id) {
_num_do_work.add_then_fetch(1u);
bool orig_value = _do_work_called_by[worker_id].compare_exchange(false, true);
ASSERT_EQ(orig_value, false);
}
void verify_do_work_called_by(uint num_workers) {
ASSERT_EQ(_num_do_work.load_relaxed(), num_workers);
// Do not need to check the _do_work_called_by array. The count is already verified
// by above statement, and we already check that a given flag is only set once.
}
public:
// Actual use of GCParPhasesSentinel will cause an assertion failure when trying
// to add timing information - this should be disabled here.
G1TestSubTask() : G1AbstractSubTask(G1GCPhaseTimes::GCParPhasesSentinel),
_phase(0),
_num_do_work(0),
_do_work_called_by(nullptr),
_max_workers(0) {
check_and_inc_phase(0);
}
~G1TestSubTask() {
check_and_inc_phase(3);
FREE_C_HEAP_ARRAY(Atomic<bool>, _do_work_called_by);
}
double worker_cost() const override {
check_and_inc_phase(1);
return 1.0;
}
// Called by G1BatchedTask to provide information about the maximum
// number of workers for all subtasks after it has been determined.
void set_max_workers(uint max_workers) override {
assert(max_workers >= 1, "must be");
check_and_inc_phase(2);
_do_work_called_by = NEW_C_HEAP_ARRAY(Atomic<bool>, max_workers, mtInternal);
for (uint i = 0; i < max_workers; i++) {
::new (&_do_work_called_by[i]) Atomic<bool>{false};
}
_max_workers = max_workers;
}
void do_work(uint worker_id) override {
do_work_called(worker_id);
}
};
class G1SerialTestSubTask : public G1TestSubTask {
public:
G1SerialTestSubTask() : G1TestSubTask() { }
~G1SerialTestSubTask() {
verify_do_work_called_by(1);
}
double worker_cost() const override {
G1TestSubTask::worker_cost();
return 1.0;
}
};
class G1ParallelTestSubTask : public G1TestSubTask {
public:
G1ParallelTestSubTask() : G1TestSubTask() { }
~G1ParallelTestSubTask() {
verify_do_work_called_by(_max_workers);
}
double worker_cost() const override {
G1TestSubTask::worker_cost();
return 2.0;
}
};
class G1TestBatchedTask : public G1BatchedTask {
public:
G1TestBatchedTask() : G1BatchedTask("Batched Test Task", nullptr) {
add_serial_task(new G1SerialTestSubTask());
add_parallel_task(new G1ParallelTestSubTask());
}
};
TEST_VM(G1BatchedTask, check) {
G1TestBatchedTask task;
uint tasks = task.num_workers_estimate();
ASSERT_EQ(tasks, 3u);
task.set_max_workers(G1BatchedTaskWorkers::MaxWorkers);
G1BatchedTaskWorkers::run_task(&task);
}

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2013, 2025, 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.
*/
#include "gc/g1/g1BiasedArray.hpp"
#include "unittest.hpp"
class TestMappedArray : public G1BiasedMappedArray<int> {
void verify_biased_index_inclusive_end(idx_t biased_index) const {
guarantee(_biased_base != 0, "Array not initialized");
guarantee(biased_index >= bias() && biased_index <= (bias() + length()),
"Biased index out of inclusive bounds, index: %zu bias: %zu length: %zu",
biased_index, bias(), length());
}
public:
virtual int default_value() const {
return 0xBAADBABE;
}
int* my_address_mapped_to(HeapWord* address) {
idx_t biased_index = ((uintptr_t)address) >> shift_by();
verify_biased_index_inclusive_end(biased_index);
return biased_base_at(biased_index);
}
int* base() const { return G1BiasedMappedArray<int>::base(); }
};
TEST_VM(G1BiasedArray, simple) {
const size_t REGION_SIZE_IN_WORDS = 512;
const size_t NUM_REGIONS = 20;
// Any value that is non-zero
HeapWord* fake_heap =
(HeapWord*) LP64_ONLY(0xBAAA00000) NOT_LP64(0xBA000000);
TestMappedArray array;
MemRegion range(fake_heap, fake_heap + REGION_SIZE_IN_WORDS * NUM_REGIONS);
array.initialize(range, REGION_SIZE_IN_WORDS * HeapWordSize);
const int DEFAULT_VALUE = array.default_value();
// Check address calculation (bounds)
ASSERT_EQ(fake_heap, array.bottom_address_mapped())
<< "bottom mapped address should be "
<< p2i(array.bottom_address_mapped())
<< ", but is "
<< p2i(fake_heap);
ASSERT_EQ(fake_heap + REGION_SIZE_IN_WORDS * NUM_REGIONS,
array.end_address_mapped());
int* bottom = array.my_address_mapped_to(fake_heap);
ASSERT_EQ((void*) bottom, (void*) array.base());
int* end = array.my_address_mapped_to(fake_heap +
REGION_SIZE_IN_WORDS * NUM_REGIONS);
ASSERT_EQ((void*) end, (void*) (array.base() + array.length()));
// The entire array should contain default value elements
for (int* current = bottom; current < end; current++) {
ASSERT_EQ(DEFAULT_VALUE, *current);
}
// Test setting values in the table
HeapWord* region_start_address =
fake_heap + REGION_SIZE_IN_WORDS * (NUM_REGIONS / 2);
HeapWord* region_end_address =
fake_heap + (REGION_SIZE_IN_WORDS * (NUM_REGIONS / 2) +
REGION_SIZE_IN_WORDS - 1);
// Set/get by address tests: invert some value; first retrieve one
int actual_value = array.get_by_index(NUM_REGIONS / 2);
array.set_by_index(NUM_REGIONS / 2, ~actual_value);
// Get the same value by address, should correspond to the start of the "region"
int value = array.get_by_address(region_start_address);
ASSERT_EQ(value, ~actual_value);
// Get the same value by address, at one HeapWord before the start
value = array.get_by_address(region_start_address - 1);
ASSERT_EQ(DEFAULT_VALUE, value);
// Get the same value by address, at the end of the "region"
value = array.get_by_address(region_end_address);
ASSERT_EQ(value, ~actual_value);
// Make sure the next value maps to another index
value = array.get_by_address(region_end_address + 1);
ASSERT_EQ(DEFAULT_VALUE, value);
// Reset the value in the array
array.set_by_address(region_start_address +
(region_end_address - region_start_address) / 2,
actual_value);
// The entire array should have the default value again
for (int* current = bottom; current < end; current++) {
ASSERT_EQ(DEFAULT_VALUE, *current);
}
// Set/get by index tests: invert some value
size_t index = NUM_REGIONS / 2;
actual_value = array.get_by_index(index);
array.set_by_index(index, ~actual_value);
value = array.get_by_index(index);
ASSERT_EQ(~actual_value, value);
value = array.get_by_index(index - 1);
ASSERT_EQ(DEFAULT_VALUE, value);
value = array.get_by_index(index + 1);
ASSERT_EQ(DEFAULT_VALUE, value);
array.set_by_index(0, 0);
value = array.get_by_index(0);
ASSERT_EQ(0, value);
array.set_by_index(array.length() - 1, 0);
value = array.get_by_index(array.length() - 1);
ASSERT_EQ(0, value);
array.set_by_index(index, 0);
// The array should have three zeros, and default values otherwise
size_t num_zeros = 0;
for (int* current = bottom; current < end; current++) {
ASSERT_TRUE(*current == DEFAULT_VALUE || *current == 0);
if (*current == 0) {
num_zeros++;
}
}
ASSERT_EQ((size_t) 3, num_zeros);
}

View file

@ -0,0 +1,488 @@
/*
* Copyright (c) 2021, 2026, 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.
*/
#include "gc/g1/g1CardSet.inline.hpp"
#include "gc/g1/g1CardSetContainers.hpp"
#include "gc/g1/g1CardSetMemory.hpp"
#include "gc/g1/g1HeapRegionRemSet.hpp"
#include "gc/g1/g1MonotonicArenaFreePool.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/workerThread.hpp"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
#include "runtime/atomic.hpp"
#include "unittest.hpp"
#include "utilities/powerOfTwo.hpp"
class G1CardSetTest : public ::testing::Test {
class G1CountCardsClosure : public G1CardSet::CardClosure {
public:
size_t _num_cards;
G1CountCardsClosure() : _num_cards(0) { }
void do_card(uint region_idx, uint card_idx) override {
_num_cards++;
}
};
static WorkerThreads* _workers;
static uint _max_workers;
static WorkerThreads* workers() {
if (_workers == nullptr) {
_max_workers = os::processor_count();
_workers = new WorkerThreads("G1CardSetTest Workers", _max_workers);
_workers->initialize_workers();
_workers->set_active_workers(_max_workers);
}
return _workers;
}
// Check whether iteration agrees with the expected number of entries. If the
// add has been single-threaded, we can also check whether the occupied()
// (which is an estimate in that case) agrees.
static void check_iteration(G1CardSet* card_set,
const size_t expected,
const bool add_was_single_threaded = true);
public:
G1CardSetTest() { }
~G1CardSetTest() { }
static uint next_random(uint& seed, uint i) {
// Park-Miller random number generator
seed = (seed * 279470273u) % 0xfffffffb;
return (seed % i);
}
static void cardset_basic_test();
static void cardset_mt_test();
static void add_cards(G1CardSet* card_set, uint cards_per_region, uint* cards, uint num_cards, G1AddCardResult* results);
static void contains_cards(G1CardSet* card_set, uint cards_per_region, uint* cards, uint num_cards);
static void translate_cards(uint cards_per_region, uint region_idx, uint* cards, uint num_cards);
static void iterate_cards(G1CardSet* card_set, G1CardSet::CardClosure* cl);
};
WorkerThreads* G1CardSetTest::_workers = nullptr;
uint G1CardSetTest::_max_workers = 0;
void G1CardSetTest::add_cards(G1CardSet* card_set, uint cards_per_region, uint* cards, uint num_cards, G1AddCardResult* results) {
for (uint i = 0; i < num_cards; i++) {
uint region_idx = cards[i] / cards_per_region;
uint card_idx = cards[i] % cards_per_region;
G1AddCardResult res = card_set->add_card(region_idx, card_idx);
if (results != nullptr) {
ASSERT_TRUE(res == results[i]);
}
}
}
class G1CheckCardClosure : public G1CardSet::CardClosure {
G1CardSet* _card_set;
uint _cards_per_region;
uint* _cards_to_expect;
uint _num_cards;
bool _wrong_region_idx;
public:
G1CheckCardClosure(G1CardSet* card_set, uint cards_per_region, uint* cards_to_expect, uint num_cards) :
_card_set(card_set),
_cards_per_region(cards_per_region),
_cards_to_expect(cards_to_expect),
_num_cards(num_cards),
_wrong_region_idx(false) {
}
void do_card(uint region_idx, uint card_idx) override {
uint card = _cards_per_region * region_idx + card_idx;
for (uint i = 0; i < _num_cards; i++) {
if (_cards_to_expect[i] == card) {
_cards_to_expect[i] = (uint)-1;
}
}
}
bool all_found() const {
bool all_good = true;
for (uint i = 0; i < _num_cards; i++) {
if (_cards_to_expect[i] != (uint)-1) {
log_error(gc)("Could not find card %u in region %u",
_cards_to_expect[i] % _cards_per_region,
_cards_to_expect[i] / _cards_per_region);
all_good = false;
}
}
return all_good;
}
};
void G1CardSetTest::contains_cards(G1CardSet* card_set, uint cards_per_region, uint* cards, uint num_cards) {
for (uint i = 0; i < num_cards; i++) {
uint region_idx = cards[i] / cards_per_region;
uint card_idx = cards[i] % cards_per_region;
ASSERT_TRUE(card_set->contains_card(region_idx, card_idx));
}
G1CheckCardClosure cl(card_set, cards_per_region, cards, num_cards);
card_set->iterate_cards(cl);
ASSERT_TRUE(cl.all_found());
}
// Offsets the card indexes in the cards array by the region_idx.
void G1CardSetTest::translate_cards(uint cards_per_region, uint region_idx, uint* cards, uint num_cards) {
for (uint i = 0; i < num_cards; i++) {
cards[i] = cards_per_region * region_idx + cards[i];
}
}
class G1CountCardsOccupied : public G1CardSet::ContainerPtrClosure {
size_t _num_occupied;
public:
G1CountCardsOccupied() : _num_occupied(0) { }
void do_containerptr(uint region_idx, size_t num_occupied, G1CardSet::ContainerPtr container) override {
_num_occupied += num_occupied;
}
size_t num_occupied() const { return _num_occupied; }
};
void G1CardSetTest::check_iteration(G1CardSet* card_set, const size_t expected, const bool single_threaded) {
class CheckIterator : public G1CardSet::CardClosure {
public:
G1CardSet* _card_set;
size_t _num_found;
CheckIterator(G1CardSet* card_set) : _card_set(card_set), _num_found(0) { }
void do_card(uint region_idx, uint card_idx) override {
ASSERT_TRUE(_card_set->contains_card(region_idx, card_idx));
_num_found++;
}
} cl(card_set);
card_set->iterate_cards(cl);
ASSERT_TRUE(expected == cl._num_found);
// We can assert this only if we are single-threaded.
if (single_threaded) {
ASSERT_EQ(card_set->occupied(), cl._num_found);
}
}
void G1CardSetTest::cardset_basic_test() {
const uint CardsPerRegion = 2048;
const double FullCardSetThreshold = 0.8;
const double BitmapCoarsenThreshold = 0.9;
G1CardSetConfiguration config(28,
BitmapCoarsenThreshold,
8,
FullCardSetThreshold,
CardsPerRegion,
0);
G1CardSetFreePool free_pool(config.num_mem_object_types());
G1CardSetMemoryManager mm(&config, &free_pool);
{
G1CardSet card_set(&config, &mm);
uint cards1[] = { 1, 2, 3 };
G1AddCardResult results1[] = { Added, Added, Added };
translate_cards(CardsPerRegion, 99, cards1, ARRAY_SIZE(cards1));
add_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1), results1);
contains_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1));
ASSERT_TRUE(card_set.occupied() == ARRAY_SIZE(cards1));
G1CountCardsClosure count_cards;
card_set.iterate_cards(count_cards);
ASSERT_TRUE(count_cards._num_cards == ARRAY_SIZE(cards1));
check_iteration(&card_set, card_set.occupied());
card_set.clear();
ASSERT_TRUE(card_set.occupied() == 0);
check_iteration(&card_set, 0);
}
{
G1CardSet card_set(&config, &mm);
uint cards1[] = { 0, 2047, 17, 17 };
G1AddCardResult results1[] = { Added, Added, Added, Found };
translate_cards(CardsPerRegion, 100, cards1, ARRAY_SIZE(cards1));
add_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1), results1);
// -1 because of the duplicate at the end.
contains_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1) - 1);
ASSERT_TRUE(card_set.occupied() == ARRAY_SIZE(cards1) - 1);
G1CountCardsClosure count_cards;
card_set.iterate_cards(count_cards);
ASSERT_TRUE(count_cards._num_cards == ARRAY_SIZE(cards1) - 1);
check_iteration(&card_set, card_set.occupied());
card_set.clear();
ASSERT_TRUE(card_set.occupied() == 0);
}
{
G1CardSet card_set(&config, &mm);
uint cards1[] = { 0, 2047, 17, 18 /* for region 100 */,
1, 128, 35, 17 /* for region 990 */
};
translate_cards(CardsPerRegion, 100, &cards1[0], 4);
translate_cards(CardsPerRegion, 990, &cards1[4], 4);
add_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1), nullptr);
contains_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1));
ASSERT_TRUE(card_set.occupied() == ARRAY_SIZE(cards1));
G1CountCardsClosure count_cards;
card_set.iterate_cards(count_cards);
ASSERT_TRUE(count_cards._num_cards == ARRAY_SIZE(cards1));
check_iteration(&card_set, card_set.occupied());
card_set.clear();
ASSERT_TRUE(card_set.occupied() == 0);
}
{
G1CardSet card_set(&config, &mm);
uint cards1[100];
for (uint i = 0; i < ARRAY_SIZE(cards1); i++) {
cards1[i] = i + 3;
translate_cards(CardsPerRegion, i, &cards1[i], 1);
}
add_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1), nullptr);
contains_cards(&card_set, CardsPerRegion, cards1, ARRAY_SIZE(cards1));
ASSERT_TRUE(card_set.num_containers() == ARRAY_SIZE(cards1));
ASSERT_TRUE(card_set.occupied() == ARRAY_SIZE(cards1));
G1CountCardsClosure count_cards;
card_set.iterate_cards(count_cards);
ASSERT_TRUE(count_cards._num_cards == ARRAY_SIZE(cards1));
check_iteration(&card_set, card_set.occupied());
card_set.clear();
ASSERT_TRUE(card_set.occupied() == 0);
}
{
G1CardSet card_set(&config, &mm);
// Generate non-prime numbers from 1 to 1000
uint count = 0;
for (uint i = 2; i < 33; i++) {
if (!card_set.contains_card(100, i)) {
for (uint j = i * i; j < 1000; j += i) {
G1AddCardResult res = card_set.add_card(100, j);
count += (res == Added);
}
}
}
G1CountCardsOccupied cl;
card_set.iterate_containers(&cl);
ASSERT_TRUE(count == card_set.occupied());
ASSERT_TRUE(card_set.occupied() == cl.num_occupied());
check_iteration(&card_set, card_set.occupied());
card_set.clear();
ASSERT_TRUE(card_set.occupied() == 0);
}
{ // Test coarsening to full
G1CardSet card_set(&config, &mm);
uint count = 0;
uint i = 10;
uint bitmap_threshold = config.cards_in_howl_bitmap_threshold();
for (; i < bitmap_threshold + 10; i++) {
G1AddCardResult res = card_set.add_card(99, i);
ASSERT_TRUE(res == Added);
count++;
ASSERT_TRUE(count == card_set.occupied());
}
G1AddCardResult res = card_set.add_card(99, config.max_cards_in_howl_bitmap() - 1);
// Adding above card should have coarsened Bitmap -> Full.
ASSERT_TRUE(res == Added);
ASSERT_TRUE(config.max_cards_in_howl_bitmap() == card_set.occupied());
res = card_set.add_card(99, config.max_cards_in_howl_bitmap() - 2);
ASSERT_TRUE(res == Found);
uint threshold = config.cards_in_howl_threshold();
uint adjusted_threshold = config.cards_in_howl_bitmap_threshold() * config.num_buckets_in_howl();
i = config.max_cards_in_howl_bitmap();
count = i;
for (; i < threshold; i++) {
G1AddCardResult res = card_set.add_card(99, i);
ASSERT_TRUE(res == Added);
count++;
ASSERT_TRUE(count == card_set.occupied());
}
res = card_set.add_card(99, CardsPerRegion - 1);
// Adding above card should have coarsened Howl -> Full.
ASSERT_TRUE(res == Added);
ASSERT_TRUE(CardsPerRegion == card_set.occupied());
check_iteration(&card_set, card_set.occupied());
res = card_set.add_card(99, CardsPerRegion - 2);
ASSERT_TRUE(res == Found);
G1CountCardsClosure count_cards;
card_set.iterate_cards(count_cards);
ASSERT_TRUE(count_cards._num_cards == config.max_cards_in_region());
card_set.clear();
ASSERT_TRUE(card_set.occupied() == 0);
}
}
class G1CardSetMtTestTask : public WorkerTask {
G1CardSet* _card_set;
Atomic<size_t> _added;
Atomic<size_t> _found;
public:
G1CardSetMtTestTask(G1CardSet* card_set) :
WorkerTask(""),
_card_set(card_set),
_added(0),
_found(0) { }
void work(uint worker_id) {
uint seed = worker_id;
size_t added = 0;
size_t found = 0;
for (uint i = 0; i < 100000; i++) {
uint region = G1CardSetTest::next_random(seed, 1000);
uint card = G1CardSetTest::next_random(seed, 10000);
G1AddCardResult res = _card_set->add_card(region, card);
ASSERT_TRUE(res == Added || res == Found);
if (res == Added) {
added++;
} else if (res == Found) {
found++;
}
}
_added.add_then_fetch(added);
_found.add_then_fetch(found);
}
size_t added() const { return _added.load_relaxed(); }
size_t found() const { return _found.load_relaxed(); }
};
void G1CardSetTest::cardset_mt_test() {
const uint CardsPerRegion = 16384;
const double FullCardSetThreshold = 1.0;
const uint BitmapCoarsenThreshold = 1.0;
G1CardSetConfiguration config(120,
BitmapCoarsenThreshold,
8,
FullCardSetThreshold,
CardsPerRegion,
0);
G1CardSetFreePool free_pool(config.num_mem_object_types());
G1CardSetMemoryManager mm(&config, &free_pool);
G1CardSet card_set(&config, &mm);
const uint num_workers = workers()->active_workers();
G1CardSetMtTestTask cl(&card_set);
{
GCTraceTime(Error, gc) x("Cardset test");
_workers->run_task(&cl, num_workers);
}
size_t num_found = 0;
// Now check the contents of the card set.
for (uint i = 0; i < num_workers; i++) {
uint seed = i;
for (uint j = 0; j < 100000; j++) {
uint region = G1CardSetTest::next_random(seed, 1000);
uint card = G1CardSetTest::next_random(seed, 10000);
bool contains = card_set.contains_card(region, card);
ASSERT_TRUE(contains);
num_found += contains;
}
}
ASSERT_TRUE(num_found == cl.added() + cl.found());
G1CountCardsClosure count_cards;
card_set.iterate_cards(count_cards);
check_iteration(&card_set, count_cards._num_cards, false /* add_was_single_threaded */);
// During coarsening we try to unblock concurrent threads as soon as possible,
// so we do not add the cards from the smaller CardSetContainer to the larger
// one immediately, allowing addition by concurrent threads after allocating
// the space immediately. So the amount of "successfully added" results may be
// (and in case of many threads typically is) higher than the number of unique
// cards.
ASSERT_TRUE(count_cards._num_cards <= cl.added());
}
TEST_VM(G1CardSetTest, basic_cardset_test) {
G1CardSetTest::cardset_basic_test();
}
TEST_VM(G1CardSetTest, mt_cardset_test) {
G1CardSetTest::cardset_mt_test();
}

View file

@ -0,0 +1,263 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
#include "gc/g1/g1CardSetContainers.inline.hpp"
#include "gc/g1/g1HeapRegionBounds.inline.hpp"
#include "gc/shared/cardTable.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/atomic.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/powerOfTwo.hpp"
#include "unittest.hpp"
class G1CardSetContainersTest : public ::testing::Test {
public:
G1CardSetContainersTest() { }
~G1CardSetContainersTest() { }
static uint cards_per_inlineptr_set(uint bits_per_card) {
return G1CardSetInlinePtr::max_cards_in_inline_ptr(bits_per_card);
}
static void cardset_inlineptr_test(uint bits_per_card);
static void cardset_array_test(uint cards_per_array);
static void cardset_bitmap_test(uint threshold, uint size_in_bits);
};
class G1FindCardsInRange : public StackObj {
uint _num_cards;
uint _range_min;
bool* _cards_found;
public:
G1FindCardsInRange(uint range_min, uint range_max) :
_num_cards(range_max - range_min + 1),
_range_min(range_min),
_cards_found(NEW_C_HEAP_ARRAY(bool, _num_cards, mtGC)) {
for (uint i = 0; i < _num_cards; i++) {
_cards_found[i] = false;
}
}
void verify_all_found() {
verify_part_found(_num_cards);
}
void verify_part_found(uint num) {
for (uint i = 0; i < num; i++) {
ASSERT_TRUE(_cards_found[i]);
}
}
~G1FindCardsInRange() {
FREE_C_HEAP_ARRAY(mtGC, _cards_found);
}
void operator()(uint card) {
ASSERT_TRUE((card - _range_min) < _num_cards);
ASSERT_FALSE(_cards_found[card - _range_min]); // Must not have been found yet.
_cards_found[card - _range_min] = true;
}
};
void G1CardSetContainersTest::cardset_inlineptr_test(uint bits_per_card) {
const uint CardsPerSet = cards_per_inlineptr_set(bits_per_card);
G1AddCardResult res;
Atomic<G1CardSet::ContainerPtr> value{};
for (uint i = 0; i < CardsPerSet; i++) {
{
G1CardSetInlinePtr cards(&value, value.load_relaxed());
res = cards.add(i + 1, bits_per_card, CardsPerSet);
ASSERT_TRUE(res == Added);
}
{
G1CardSetInlinePtr cards(&value, value.load_relaxed());
ASSERT_TRUE(cards.contains(i + 1, bits_per_card));
}
}
for (uint i = 0; i < CardsPerSet; i++) {
G1CardSetInlinePtr cards(value.load_relaxed());
ASSERT_TRUE(cards.contains(i + 1, bits_per_card));
}
// Try to add again, should all return that the card had been added.
for (uint i = 0; i < CardsPerSet; i++) {
G1CardSetInlinePtr cards(&value, value.load_relaxed());
res = cards.add(i + 1, bits_per_card, CardsPerSet);
ASSERT_TRUE(res == Found);
}
// Should be no more space in set.
{
G1CardSetInlinePtr cards(&value, value.load_relaxed());
res = cards.add(CardsPerSet + 1, bits_per_card, CardsPerSet);
ASSERT_TRUE(res == Overflow);
}
// Cards should still be in the set.
for (uint i = 0; i < CardsPerSet; i++) {
G1CardSetInlinePtr cards(value.load_relaxed());
ASSERT_TRUE(cards.contains(i + 1, bits_per_card));
}
// Boundary cards should not be in the set.
{
G1CardSetInlinePtr cards(value.load_relaxed());
ASSERT_TRUE(!cards.contains(0, bits_per_card));
ASSERT_TRUE(!cards.contains(CardsPerSet + 1, bits_per_card));
}
// Verify iteration finds all cards too and only those.
{
G1FindCardsInRange found(1, CardsPerSet);
G1CardSetInlinePtr cards(value.load_relaxed());
cards.iterate(found, bits_per_card);
found.verify_all_found();
}
}
void G1CardSetContainersTest::cardset_array_test(uint cards_per_array) {
uint8_t* cardset_data = NEW_C_HEAP_ARRAY(uint8_t, G1CardSetArray::size_in_bytes(cards_per_array), mtGC);
G1CardSetArray* cards = new (cardset_data) G1CardSetArray(1, cards_per_array);
ASSERT_TRUE(cards->contains(1)); // Added during initialization
ASSERT_TRUE(cards->num_entries() == 1); // Check it's the only one.
G1AddCardResult res;
// Add some elements
for (uint i = 1; i < cards_per_array; i++) {
res = cards->add(i + 1);
ASSERT_TRUE(res == Added);
}
// Check they are in the container.
for (uint i = 0; i < cards_per_array; i++) {
ASSERT_TRUE(cards->contains(i + 1));
}
// Try to add again, should all return that the card had been added.
for (uint i = 0; i < cards_per_array; i++) {
res = cards->add(i + 1);
ASSERT_TRUE(res == Found);
}
// Should be no more space in set.
{
res = cards->add(cards_per_array + 1);
ASSERT_TRUE(res == Overflow);
}
// Cards should still be in the set.
for (uint i = 0; i < cards_per_array; i++) {
ASSERT_TRUE(cards->contains(i + 1));
}
ASSERT_TRUE(!cards->contains(0));
ASSERT_TRUE(!cards->contains(cards_per_array + 1));
// Verify iteration finds all cards too.
{
G1FindCardsInRange found(1, cards_per_array);
cards->iterate(found);
found.verify_all_found();
}
FREE_C_HEAP_ARRAY(mtGC, cardset_data);
}
void G1CardSetContainersTest::cardset_bitmap_test(uint threshold, uint size_in_bits) {
uint8_t* cardset_data = NEW_C_HEAP_ARRAY(uint8_t, G1CardSetBitMap::size_in_bytes(size_in_bits), mtGC);
G1CardSetBitMap* cards = new (cardset_data) G1CardSetBitMap(1, size_in_bits);
ASSERT_TRUE(cards->contains(1, size_in_bits)); // Added during initialization
ASSERT_TRUE(cards->num_bits_set() == 1); // Should be the only one.
G1AddCardResult res;
for (uint i = 1; i < threshold; i++) {
res = cards->add(i + 1, threshold, size_in_bits);
ASSERT_TRUE(res == Added);
}
for (uint i = 0; i < threshold; i++) {
ASSERT_TRUE(cards->contains(i + 1, size_in_bits));
}
// Try to add again, should all return that the card had been added.
for (uint i = 0; i < threshold; i++) {
res = cards->add(i + 1, threshold, size_in_bits);
ASSERT_TRUE(res == Found);
}
// Should be no more space in set.
{
res = cards->add(threshold + 1, threshold, size_in_bits);
ASSERT_TRUE(res == Overflow);
}
// Cards should still be in the set.
for (uint i = 0; i < threshold; i++) {
ASSERT_TRUE(cards->contains(i + 1, size_in_bits));
}
ASSERT_TRUE(!cards->contains(0, size_in_bits));
// Verify iteration finds all cards too.
{
G1FindCardsInRange found(1, threshold + 1);
cards->iterate(found, size_in_bits, 0);
found.verify_part_found(threshold);
}
FREE_C_HEAP_ARRAY(mtGC, cardset_data);
}
TEST_VM_F(G1CardSetContainersTest, basic_cardset_inptr_test) {
uint const min = (uint)log2i(G1HeapRegionBounds::min_size());
uint const max = (uint)log2i(G1HeapRegionBounds::max_size());
for (uint i = min; i <= max; i++) {
G1CardSetContainersTest::cardset_inlineptr_test(i - CardTable::card_shift());
}
}
TEST_VM_F(G1CardSetContainersTest, basic_cardset_array_test) {
uint array_sizes[] = { 5, 9, 63, 77, 127 };
for (uint i = 0; i < ARRAY_SIZE(array_sizes); i++) {
size_t const max_cards_in_set = ARRAY_SIZE(array_sizes);
G1CardSetContainersTest::cardset_array_test(max_cards_in_set);
}
}
TEST_VM_F(G1CardSetContainersTest, basic_cardset_bitmap_test) {
uint bit_sizes[] = { 64, 2048 };
uint threshold_sizes[] = { 17, 330 };
for (uint i = 0; i < ARRAY_SIZE(bit_sizes); i++) {
G1CardSetContainersTest::cardset_bitmap_test(threshold_sizes[i], bit_sizes[i]);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2014, 2025, 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.
*/
#include "gc/g1/g1CodeRootSet.hpp"
#include "unittest.hpp"
TEST_VM(G1CodeRootSet, g1_code_cache_rem_set) {
G1CodeRootSet root_set;
ASSERT_TRUE(root_set.is_empty()) << "Code root set must be initially empty "
"but is not.";
root_set.add((nmethod*) 1);
ASSERT_EQ(root_set.length(), (size_t) 1) << "Added exactly one element, but"
" set contains " << root_set.length() << " elements";
const size_t num_to_add = 1000;
for (size_t i = 1; i <= num_to_add; i++) {
root_set.add((nmethod*) 1);
}
ASSERT_EQ(root_set.length(), (size_t) 1)
<< "Duplicate detection should not have increased the set size but "
<< "is " << root_set.length();
for (size_t i = 2; i <= num_to_add; i++) {
root_set.add((nmethod*) (uintptr_t) (i));
}
ASSERT_EQ(root_set.length(), num_to_add)
<< "After adding in total " << num_to_add << " distinct code roots, "
"they need to be in the set, but there are only " << root_set.length();
size_t num_popped = 0;
for (size_t i = 1; i <= num_to_add; i++) {
bool removed = root_set.remove((nmethod*) i);
if (removed) {
num_popped += 1;
} else {
break;
}
}
ASSERT_EQ(num_popped, num_to_add)
<< "Managed to pop " << num_popped << " code roots, but only "
<< num_to_add << " were added";
ASSERT_EQ(root_set.length(), 0u)
<< "should be empty";
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2017, 2025, 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.
*
*/
#include "gc/g1/g1Arguments.hpp"
#include "gc/g1/g1HeapVerifier.hpp"
#include "logging/logConfiguration.hpp"
#include "logging/logTag.hpp"
#include "logging/logTestFixture.hpp"
#include "unittest.hpp"
class G1HeapVerifierTest : public LogTestFixture {
protected:
static void parse_verification_type(const char* type) {
G1Arguments::parse_verification_type(type);
}
};
TEST_VM_F(G1HeapVerifierTest, parse) {
LogConfiguration::configure_stdout(LogLevel::Off, true, LOG_TAGS(gc, verify));
// Default is to verify everything.
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyYoungNormal));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyConcurrentStart));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyMixed));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyYoungEvacFail));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyRemark));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyCleanup));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyFull));
// Setting one will disable all other.
G1HeapVerifierTest::parse_verification_type("full");
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyYoungNormal));
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyConcurrentStart));
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyMixed));
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyYoungEvacFail));
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyRemark));
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyCleanup));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyFull));
// Verify case sensitivity.
G1HeapVerifierTest::parse_verification_type("YOUNG-NORMAL");
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyYoungNormal));
G1HeapVerifierTest::parse_verification_type("young-normal");
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyYoungNormal));
// Verify perfect match
G1HeapVerifierTest::parse_verification_type("mixedgc");
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyMixed));
G1HeapVerifierTest::parse_verification_type("mixe");
ASSERT_FALSE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyMixed));
G1HeapVerifierTest::parse_verification_type("mixed");
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyMixed));
// Verify the last three
G1HeapVerifierTest::parse_verification_type("concurrent-start");
G1HeapVerifierTest::parse_verification_type("remark");
G1HeapVerifierTest::parse_verification_type("cleanup");
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyRemark));
ASSERT_TRUE(G1HeapVerifier::should_verify(G1HeapVerifier::G1VerifyCleanup));
}

View file

@ -0,0 +1,247 @@
/*
* Copyright (c) 2016, 2025, 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.
*/
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1IHOPControl.hpp"
#include "gc/g1/g1OldGenAllocationTracker.hpp"
#include "gc/g1/g1Predictions.hpp"
#include "unittest.hpp"
static void test_update_allocation_tracker(G1OldGenAllocationTracker* alloc_tracker,
size_t alloc_amount) {
alloc_tracker->add_allocated_bytes_since_last_gc(alloc_amount);
alloc_tracker->reset_after_gc((size_t)0);
}
static void test_update(G1IHOPControl* ctrl,
G1OldGenAllocationTracker* alloc_tracker,
double alloc_time, size_t alloc_amount,
size_t young_size, double mark_time) {
test_update_allocation_tracker(alloc_tracker, alloc_amount);
for (int i = 0; i < 100; i++) {
ctrl->update_allocation_info(alloc_time, young_size);
ctrl->update_marking_length(mark_time);
}
}
static void test_update_humongous(G1IHOPControl* ctrl,
G1OldGenAllocationTracker* alloc_tracker,
double alloc_time,
size_t alloc_amount_non_hum,
size_t alloc_amount_hum,
size_t humongous_bytes_after_last_gc,
size_t young_size,
double mark_time) {
alloc_tracker->add_allocated_bytes_since_last_gc(alloc_amount_non_hum);
alloc_tracker->add_allocated_humongous_bytes_since_last_gc(alloc_amount_hum);
alloc_tracker->reset_after_gc(humongous_bytes_after_last_gc);
for (int i = 0; i < 100; i++) {
ctrl->update_allocation_info(alloc_time, young_size);
ctrl->update_marking_length(mark_time);
}
}
// @requires UseG1GC
TEST_VM(G1IHOPControl, static_simple) {
// Test requires G1
if (!UseG1GC) {
return;
}
const bool is_adaptive = false;
const size_t initial_ihop = 45;
G1OldGenAllocationTracker alloc_tracker;
G1IHOPControl ctrl(initial_ihop, &alloc_tracker, is_adaptive, nullptr, 0, 0);
ctrl.update_target_occupancy(100);
size_t threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(initial_ihop, threshold);
test_update_allocation_tracker(&alloc_tracker, 100);
ctrl.update_allocation_info(100.0, 100);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(initial_ihop, threshold);
ctrl.update_marking_length(1000.0);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(initial_ihop, threshold);
// Whatever we pass, the IHOP value must stay the same.
test_update(&ctrl, &alloc_tracker, 2, 10, 10, 3);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(initial_ihop, threshold);
test_update(&ctrl, &alloc_tracker, 12, 10, 10, 3);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(initial_ihop, threshold);
}
// @requires UseG1GC
TEST_VM(G1IHOPControl, adaptive_simple) {
// Test requires G1
if (!UseG1GC) {
return;
}
const bool is_adaptive = true;
const size_t initial_threshold = 45;
const size_t young_size = 10;
const size_t target_size = 100;
// The final IHOP value is always
// target_size - (young_size + alloc_amount/alloc_time * marking_time)
G1OldGenAllocationTracker alloc_tracker;
G1Predictions pred(0.95);
G1IHOPControl ctrl(initial_threshold, &alloc_tracker, is_adaptive, &pred, 0, 0);
ctrl.update_target_occupancy(target_size);
// First "load".
const size_t alloc_time1 = 2;
const size_t alloc_amount1 = 10;
const size_t marking_time1 = 2;
const size_t settled_ihop1 = target_size
- (young_size + alloc_amount1 / alloc_time1 * marking_time1);
size_t threshold;
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(initial_threshold, threshold);
for (size_t i = 0; i < G1AdaptiveIHOPNumInitialSamples - 1; i++) {
test_update_allocation_tracker(&alloc_tracker, alloc_amount1);
ctrl.update_allocation_info(alloc_time1, young_size);
ctrl.update_marking_length(marking_time1);
// Not enough data yet.
threshold = ctrl.get_conc_mark_start_threshold();
ASSERT_EQ(initial_threshold, threshold) << "on step " << i;
}
test_update(&ctrl, &alloc_tracker, alloc_time1, alloc_amount1, young_size, marking_time1);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(settled_ihop1, threshold);
// Second "load". A bit higher allocation rate.
const size_t alloc_time2 = 2;
const size_t alloc_amount2 = 30;
const size_t marking_time2 = 2;
const size_t settled_ihop2 = target_size
- (young_size + alloc_amount2 / alloc_time2 * marking_time2);
test_update(&ctrl, &alloc_tracker, alloc_time2, alloc_amount2, young_size, marking_time2);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_LT(threshold, settled_ihop1);
// Third "load". Very high (impossible) allocation rate.
const size_t alloc_time3 = 1;
const size_t alloc_amount3 = 50;
const size_t marking_time3 = 2;
const size_t settled_ihop3 = 0;
test_update(&ctrl, &alloc_tracker, alloc_time3, alloc_amount3, young_size, marking_time3);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_EQ(settled_ihop3, threshold);
// And back to some arbitrary value.
test_update(&ctrl, &alloc_tracker, alloc_time2, alloc_amount2, young_size, marking_time2);
threshold = ctrl.get_conc_mark_start_threshold();
EXPECT_GT(threshold, settled_ihop3);
}
TEST_VM(G1IHOPControl, adaptive_humongous) {
// Test requires G1
if (!UseG1GC) {
return;
}
const bool is_adaptive = true;
const size_t initial_threshold = 45;
const size_t young_size = 10;
const size_t target_size = 100;
const double duration = 10.0;
const size_t marking_time = 2;
G1OldGenAllocationTracker alloc_tracker;
G1Predictions pred(0.95);
G1IHOPControl ctrl(initial_threshold, &alloc_tracker, is_adaptive, &pred, 0, 0);
ctrl.update_target_occupancy(target_size);
size_t old_bytes = 100;
size_t humongous_bytes = 200;
size_t humongous_bytes_after_gc = 150;
size_t humongous_bytes_after_last_gc = 50;
// Load 1
test_update_humongous(&ctrl, &alloc_tracker, duration, 0, humongous_bytes,
humongous_bytes_after_last_gc, young_size, marking_time);
// Test threshold
size_t threshold;
threshold = ctrl.get_conc_mark_start_threshold();
// Adjusted allocated bytes:
// Total bytes: humongous_bytes
// Freed hum bytes: humongous_bytes - humongous_bytes_after_last_gc
double alloc_rate = humongous_bytes_after_last_gc / duration;
size_t target_threshold = target_size - (size_t)(young_size + alloc_rate * marking_time);
EXPECT_EQ(threshold, target_threshold);
// Load 2
G1IHOPControl ctrl2(initial_threshold, &alloc_tracker, is_adaptive, &pred, 0, 0);
ctrl2.update_target_occupancy(target_size);
test_update_humongous(&ctrl2, &alloc_tracker, duration, old_bytes, humongous_bytes,
humongous_bytes_after_gc, young_size, marking_time);
threshold = ctrl2.get_conc_mark_start_threshold();
// Adjusted allocated bytes:
// Total bytes: old_bytes + humongous_bytes
// Freed hum bytes: humongous_bytes - (humongous_bytes_after_gc - humongous_bytes_after_last_gc)
alloc_rate = (old_bytes + (humongous_bytes_after_gc - humongous_bytes_after_last_gc)) / duration;
target_threshold = target_size - (size_t)(young_size + alloc_rate * marking_time);
EXPECT_EQ(threshold, target_threshold);
// Load 3
humongous_bytes_after_last_gc = humongous_bytes_after_gc;
humongous_bytes_after_gc = 50;
G1IHOPControl ctrl3(initial_threshold, &alloc_tracker, is_adaptive, &pred, 0, 0);
ctrl3.update_target_occupancy(target_size);
test_update_humongous(&ctrl3, &alloc_tracker, duration, old_bytes, humongous_bytes,
humongous_bytes_after_gc, young_size, marking_time);
threshold = ctrl3.get_conc_mark_start_threshold();
// Adjusted allocated bytes:
// All humongous are cleaned up since humongous_bytes_after_gc < humongous_bytes_after_last_gc
// Total bytes: old_bytes + humongous_bytes
// Freed hum bytes: humongous_bytes
alloc_rate = old_bytes / duration;
target_threshold = target_size - (size_t)(young_size + alloc_rate * marking_time);
EXPECT_EQ(threshold, target_threshold);
}

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2016, 2025, 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.
*
*/
#include "gc/g1/g1Predictions.hpp"
#include "unittest.hpp"
#include "utilities/ostream.hpp"
static const double epsilon = 1e-6;
// Some basic formula tests with confidence = 0.0
TEST_VM(G1Predictions, basic_predictions) {
G1Predictions predictor(0.0);
TruncatedSeq s;
double p0 = predictor.predict(&s);
ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
s.add(5.0);
double p1 = predictor.predict(&s);
ASSERT_NEAR(p1, 5.0, epsilon);
for (int i = 0; i < 40; i++) {
s.add(5.0);
}
double p2 = predictor.predict(&s);
ASSERT_NEAR(p2, 5.0, epsilon);
}
// The following tests checks that the initial predictions are based on
// the average of the sequence and not on the stddev (which is 0).
TEST_VM(G1Predictions, average_not_stdev_predictions) {
G1Predictions predictor(0.5);
TruncatedSeq s;
s.add(1.0);
double p1 = predictor.predict(&s);
ASSERT_GT(p1, s.davg()) << "First prediction must be greater than average";
s.add(1.0);
double p2 = predictor.predict(&s);
ASSERT_GT(p1, p2) << "First prediction must be greater than second";
s.add(1.0);
double p3 = predictor.predict(&s);
ASSERT_GT(p2, p3) << "Second prediction must be greater than third";
s.add(1.0);
s.add(1.0); // Five elements are now in the sequence.
double p4 = predictor.predict(&s);
ASSERT_LT(p4, p3) << "Fourth prediction must be smaller than third";
ASSERT_NEAR(p4, 1.0, epsilon);
}
// The following tests checks that initially prediction based on
// the average is used, that gets overridden by the stddev prediction at
// the end.
TEST_VM(G1Predictions, average_stdev_predictions) {
G1Predictions predictor(0.5);
TruncatedSeq s;
s.add(0.5);
double p1 = predictor.predict(&s);
ASSERT_GT(p1, s.davg()) << "First prediction must be greater than average";
s.add(0.2);
double p2 = predictor.predict(&s);
ASSERT_GT(p1, p2) << "First prediction must be greater than second";
s.add(0.5);
double p3 = predictor.predict(&s);
ASSERT_GT(p2, p3) << "Second prediction must be greater than third";
s.add(0.2);
s.add(2.0);
double p4 = predictor.predict(&s);
ASSERT_GT(p4, p3) << "Fourth prediction must be greater than third";
}
// Some tests to verify bounding between [0 .. 1]
TEST_VM(G1Predictions, unit_predictions) {
G1Predictions predictor(0.5);
TruncatedSeq s;
double p0 = predictor.predict_in_unit_interval(&s);
ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
s.add(100.0);
double p1 = predictor.predict_in_unit_interval(&s);
ASSERT_NEAR(p1, 1.0, epsilon);
// Feed the sequence additional positive values to test the high bound.
for (int i = 0; i < 3; i++) {
s.add(2.0);
}
ASSERT_NEAR(predictor.predict_in_unit_interval(&s), 1.0, epsilon);
// Feed the sequence additional large negative value to test the low bound.
for (int i = 0; i < 4; i++) {
s.add(-200.0);
}
ASSERT_NEAR(predictor.predict_in_unit_interval(&s), 0.0, epsilon);
}
// Some tests to verify bounding between [0 .. +inf]
TEST_VM(G1Predictions, lower_bound_zero_predictions) {
G1Predictions predictor(0.5);
TruncatedSeq s;
double p0 = predictor.predict_zero_bounded(&s);
ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
s.add(100.0);
// Feed the sequence additional positive values to see that the high bound is not
// bounded by e.g. 1.0
for (int i = 0; i < 3; i++) {
s.add(2.0);
}
ASSERT_GT(predictor.predict_zero_bounded(&s), 1.0);
// Feed the sequence additional large negative value to test the low bound.
for (int i = 0; i < 4; i++) {
s.add(-200.0);
}
ASSERT_NEAR(predictor.predict_zero_bounded(&s), 0.0, epsilon);
}

View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 2020, 2025, 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.
*
*/
#include "gc/g1/g1CommittedRegionMap.inline.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
class G1CommittedRegionMapSerial : public G1CommittedRegionMap {
public:
static const uint TestRegions = 512;
void verify_counts() {
verify_active_count(0, TestRegions, num_active());
verify_inactive_count(0, TestRegions, num_inactive());
}
protected:
void guarantee_mt_safety_active() const { }
void guarantee_mt_safety_inactive() const { }
};
static bool mutate() {
return os::random() % 2 == 0;
}
static void generate_random_map(G1CommittedRegionMap* map) {
for (uint i = 0; i < G1CommittedRegionMapSerial::TestRegions; i++) {
if (mutate()) {
map->activate(i, i+1);
}
}
if (map->num_active() == 0) {
// If we randomly activated 0 regions, activate the first half
// to have some regions to test.
map->activate(0, G1CommittedRegionMapSerial::TestRegions / 2);
}
}
static void random_deactivate(G1CommittedRegionMap* map) {
uint current_offset = 0;
do {
G1HeapRegionRange current = map->next_active_range(current_offset);
if (mutate()) {
if (current.length() < 5) {
// For short ranges, deactivate whole.
map->deactivate(current.start(), current.end());
} else {
// For larger ranges, deactivate half.
map->deactivate(current.start(), current.end() - (current.length() / 2));
}
}
current_offset = current.end();
} while (current_offset != G1CommittedRegionMapSerial::TestRegions);
}
static void random_uncommit_or_reactive(G1CommittedRegionMap* map) {
uint current_offset = 0;
do {
G1HeapRegionRange current = map->next_inactive_range(current_offset);
// Randomly either reactivate or uncommit
if (mutate()) {
map->reactivate(current.start(), current.end());
} else {
map->uncommit(current.start(), current.end());
}
current_offset = current.end();
} while (current_offset != G1CommittedRegionMapSerial::TestRegions);
}
static void random_activate_free(G1CommittedRegionMap* map) {
uint current_offset = 0;
do {
G1HeapRegionRange current = map->next_committable_range(current_offset);
// Randomly either reactivate or uncommit
if (mutate()) {
if (current.length() < 5) {
// For short ranges, deactivate whole.
map->activate(current.start(), current.end());
} else {
// For larger ranges, deactivate half.
map->activate(current.start(), current.end() - (current.length() / 2));
}
}
current_offset = current.end();
} while (current_offset != G1CommittedRegionMapSerial::TestRegions);
}
TEST(G1CommittedRegionMapTest, serial) {
G1CommittedRegionMapSerial serial_map;
serial_map.initialize(G1CommittedRegionMapSerial::TestRegions);
// Activate some regions
generate_random_map(&serial_map);
// Work through the map and mutate it
for (int i = 0; i < 500; i++) {
random_deactivate(&serial_map);
serial_map.verify_counts();
random_uncommit_or_reactive(&serial_map);
serial_map.verify_counts();
random_activate_free(&serial_map);
serial_map.verify_counts();
ASSERT_EQ(serial_map.num_inactive(), 0u);
}
}

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2021, 2025, 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.
*
*/
#include "gc/g1/g1ServiceThread.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/os.hpp"
#include "utilities/autoRestore.hpp"
#include "unittest.hpp"
class CheckTask : public G1ServiceTask {
int _execution_count;
bool _reschedule;
public:
CheckTask(const char* name) :
G1ServiceTask(name),
_execution_count(0),
_reschedule(true) { }
virtual void execute() {
_execution_count++;
if (_reschedule) {
schedule(100);
}
}
int execution_count() { return _execution_count;}
void set_reschedule(bool reschedule) { _reschedule = reschedule; }
};
static void stop_service_thread(G1ServiceThread* thread) {
ThreadInVMfromNative tvn(JavaThread::current());
thread->stop();
}
// Test that a task that is added during runtime gets run.
TEST_VM(G1ServiceThread, test_add) {
// Create thread and let it start.
G1ServiceThread* st = new G1ServiceThread();
os::naked_short_sleep(500);
CheckTask ct("AddAndRun");
st->register_task(&ct);
// Give CheckTask time to run.
os::naked_short_sleep(500);
stop_service_thread(st);
ASSERT_GT(ct.execution_count(), 0);
}
// Test that a task that is added while the service thread is
// waiting gets run in a timely manner.
TEST_VM(G1ServiceThread, test_add_while_waiting) {
// Make sure default tasks use long intervals so that the service thread
// is doing a long wait for the next execution.
AutoModifyRestore<uintx> f1(G1PeriodicGCInterval, 100000);
// Create thread and let it start.
G1ServiceThread* st = new G1ServiceThread();
os::naked_short_sleep(500);
// Register a new task that should run right away.
CheckTask ct("AddWhileWaiting");
st->register_task(&ct);
// Give CheckTask time to run.
os::naked_short_sleep(500);
stop_service_thread(st);
ASSERT_GT(ct.execution_count(), 0);
}
// Test that a task with negative timeout is not rescheduled.
TEST_VM(G1ServiceThread, test_add_run_once) {
// Create thread and let it start.
G1ServiceThread* st = new G1ServiceThread();
os::naked_short_sleep(500);
// Set reschedule to false to only run once.
CheckTask ct("AddRunOnce");
ct.set_reschedule(false);
st->register_task(&ct);
// Give CheckTask time to run.
os::naked_short_sleep(500);
stop_service_thread(st);
// Should be exactly 1 since negative timeout should
// prevent rescheduling.
ASSERT_EQ(ct.execution_count(), 1);
}
class TestTask : public G1ServiceTask {
jlong _delay_ms;
public:
TestTask(jlong delay) :
G1ServiceTask("TestTask"),
_delay_ms(delay) {
set_time(delay);
}
virtual void execute() {}
void update_time(jlong now, int multiplier) {
set_time(now + (_delay_ms * multiplier));
}
};
TEST_VM(G1ServiceTaskQueue, add_ordered) {
G1ServiceTaskQueue queue;
int num_test_tasks = 5;
for (int i = 1; i <= num_test_tasks; i++) {
// Create tasks with different timeout.
TestTask* task = new TestTask(100 * i);
queue.add_ordered(task);
}
// Now fake a run-loop, that reschedules the tasks using a
// random multiplier.
for (jlong now = 0; now < 1000000; now++) {
// Random multiplier is at least 1 to ensure progress.
int multiplier = 1 + os::random() % 10;
while (queue.front()->time() < now) {
TestTask* task = (TestTask*) queue.front();
queue.remove_front();
// Update delay multiplier.
task->execute();
task->update_time(now, multiplier);
// All additions will verify that the queue is sorted.
queue.add_ordered(task);
}
}
while (!queue.is_empty()) {
G1ServiceTask* task = queue.front();
queue.remove_front();
delete task;
}
}
#ifdef ASSERT
TEST_VM_ASSERT_MSG(G1ServiceTaskQueue, remove_from_empty,
".*Should never try to verify empty queue") {
G1ServiceTaskQueue queue;
queue.remove_front();
}
TEST_VM_ASSERT_MSG(G1ServiceTaskQueue, get_from_empty,
".*Should never try to verify empty queue") {
G1ServiceTaskQueue queue;
queue.front();
}
TEST_VM_ASSERT_MSG(G1ServiceTaskQueue, set_time_in_queue,
".*Not allowed to update time while in queue") {
G1ServiceTaskQueue queue;
TestTask a(100);
queue.add_ordered(&a);
// Not allowed to update time while in queue.
a.update_time(500, 1);
}
#endif

View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 2017, 2025, 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.
*/
#include "gc/g1/g1BlockOffsetTable.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp"
#include "gc/g1/g1HeapRegion.inline.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/vmOperations.hpp"
#include "runtime/vmThread.hpp"
#include "unittest.hpp"
class VerifyAndCountMarkClosure : public StackObj {
int _count;
G1CMBitMap* _bm;
void ensure_marked(HeapWord* addr) {
ASSERT_TRUE(_bm->is_marked(addr));
}
public:
VerifyAndCountMarkClosure(G1CMBitMap* bm) : _count(0), _bm(bm) { }
virtual size_t apply(oop object) {
_count++;
ensure_marked(cast_from_oop<HeapWord*>(object));
// Must return positive size to advance the iteration.
return MinObjAlignment;
}
void reset() {
_count = 0;
}
int count() {
return _count;
}
};
#define MARK_OFFSET_1 ( 17 * MinObjAlignment)
#define MARK_OFFSET_2 ( 99 * MinObjAlignment)
#define MARK_OFFSET_3 (337 * MinObjAlignment)
class VM_HeapRegionApplyToMarkedObjectsTest : public VM_GTestExecuteAtSafepoint {
public:
void doit();
};
void VM_HeapRegionApplyToMarkedObjectsTest::doit() {
G1CollectedHeap* heap = G1CollectedHeap::heap();
// Using region 0 for testing.
G1HeapRegion* region = heap->heap_region_containing(heap->bottom_addr_for_region(0));
// Mark some "oops" in the bitmap.
G1CMBitMap* bitmap = heap->concurrent_mark()->mark_bitmap();
bitmap->par_mark(region->bottom());
bitmap->par_mark(region->bottom() + MARK_OFFSET_1);
bitmap->par_mark(region->bottom() + MARK_OFFSET_2);
bitmap->par_mark(region->bottom() + MARK_OFFSET_3);
VerifyAndCountMarkClosure cl(bitmap);
HeapWord* old_top = region->top();
// When top is equal to bottom the closure should not be
// applied to any object because apply_to_marked_objects
// will stop at G1HeapRegion::scan_limit which is equal to top.
region->set_top(region->bottom());
region->apply_to_marked_objects(bitmap, &cl);
EXPECT_EQ(0, cl.count());
cl.reset();
// Set top to offset_1 and expect only to find 1 entry (bottom)
region->set_top(region->bottom() + MARK_OFFSET_1);
region->apply_to_marked_objects(bitmap, &cl);
EXPECT_EQ(1, cl.count());
cl.reset();
// Set top to (offset_2 + 1) and expect only to find 3
// entries (bottom, offset_1 and offset_2)
region->set_top(region->bottom() + MARK_OFFSET_2 + MinObjAlignment);
region->apply_to_marked_objects(bitmap, &cl);
EXPECT_EQ(3, cl.count());
cl.reset();
// Still expect same 3 entries when top is (offset_3 - 1)
region->set_top(region->bottom() + MARK_OFFSET_3 - MinObjAlignment);
region->apply_to_marked_objects(bitmap, &cl);
EXPECT_EQ(3, cl.count());
cl.reset();
// Setting top to end should render 4 entries.
region->set_top(region->end());
region->apply_to_marked_objects(bitmap, &cl);
EXPECT_EQ(4, cl.count());
cl.reset();
region->set_top(old_top);
}
TEST_OTHER_VM(G1HeapRegion, apply_to_marked_object) {
if (!UseG1GC) {
return;
}
// Run the test in our very own safepoint, because otherwise it
// modifies a region behind the back of a possibly using allocation
// or running GC.
VM_HeapRegionApplyToMarkedObjectsTest op;
ThreadInVMfromNative invm(JavaThread::current());
VMThread::execute(&op);
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2020, 2026, 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.
*
*/
#include "gc/g1/g1BlockOffsetTable.hpp"
#include "gc/g1/g1RegionToSpaceMapper.hpp"
#include "gc/shared/workerThread.hpp"
#include "memory/memoryReserver.hpp"
#include "runtime/atomic.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
class G1MapperWorkers : AllStatic {
static WorkerThreads* _workers;
static WorkerThreads* workers() {
if (_workers == nullptr) {
_workers = new WorkerThreads("G1 Small Workers", MaxWorkers);
_workers->initialize_workers();
_workers->set_active_workers(MaxWorkers);
}
return _workers;
}
public:
static const uint MaxWorkers = 4;
static void run_task(WorkerTask* task) {
workers()->run_task(task);
}
};
WorkerThreads* G1MapperWorkers::_workers = nullptr;
class G1TestCommitUncommit : public WorkerTask {
G1RegionToSpaceMapper* _mapper;
Atomic<uint> _claim_id;
public:
G1TestCommitUncommit(G1RegionToSpaceMapper* mapper) :
WorkerTask("Stress mapper"),
_mapper(mapper),
_claim_id(0) { }
void work(uint worker_id) {
uint index = _claim_id.fetch_then_add(1u);
for (int i = 0; i < 100000; i++) {
// Stress commit and uncommit of a single region. The same
// will be done for multiple adjacent region to make sure
// we properly handle bitmap updates as well as updates for
// regions sharing the same underlying OS page.
_mapper->commit_regions(index);
_mapper->uncommit_regions(index);
}
}
};
TEST_VM(G1RegionToSpaceMapper, smallStressAdjacent) {
// Fake a heap with 1m regions and create a BOT like mapper. This
// will give a G1RegionsSmallerThanCommitSizeMapper to stress.
uint num_regions = G1MapperWorkers::MaxWorkers;
size_t region_size = 1*M;
size_t size = G1BlockOffsetTable::compute_size(num_regions * region_size / HeapWordSize);
size_t page_size = os::vm_page_size();
ReservedSpace rs = MemoryReserver::reserve(size,
os::vm_allocation_granularity(),
os::vm_page_size(),
mtTest);
G1RegionToSpaceMapper* small_mapper =
G1RegionToSpaceMapper::create_mapper(rs,
size,
page_size,
region_size,
G1BlockOffsetTable::heap_map_factor(),
mtTest);
G1TestCommitUncommit task(small_mapper);
G1MapperWorkers::run_task(&task);
}
TEST_VM(G1RegionToSpaceMapper, largeStressAdjacent) {
// Fake a heap with 2m regions and create a BOT like mapper. This
// will give a G1RegionsLargerThanCommitSizeMapper to stress.
uint num_regions = G1MapperWorkers::MaxWorkers;
size_t region_size = 2*M;
size_t size = G1BlockOffsetTable::compute_size(num_regions * region_size / HeapWordSize);
size_t page_size = os::vm_page_size();
ReservedSpace rs = MemoryReserver::reserve(size,
os::vm_allocation_granularity(),
os::vm_page_size(),
mtTest);
G1RegionToSpaceMapper* large_mapper =
G1RegionToSpaceMapper::create_mapper(rs,
size,
page_size,
region_size,
G1BlockOffsetTable::heap_map_factor(),
mtTest);
G1TestCommitUncommit task(large_mapper);
G1MapperWorkers::run_task(&task);
}

Some files were not shown because too many files have changed in this diff Show more