--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/internal/jimage/VerifyJimage.java Wed Dec 03 14:22:58 2014 +0000
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2014, 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.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import jdk.internal.jimage.BasicImageReader;
+import jdk.internal.jimage.ImageLocation;
+
+/*
+ * @test
+ * @summary Verify jimage
+ * @run main/othervm VerifyJimage
+ */
+
+/**
+ * This test runs in two modes:
+ * (1) No argument: it verifies the jimage by loading all classes in the runtime
+ * (2) path of exploded modules: it compares bytes of each file in the exploded
+ * module with the entry in jimage
+ *
+ * FIXME: exception thrown when findLocation from jimage by multiple threads
+ * -Djdk.test.threads=<n> to specify the number of threads.
+ */
+public class VerifyJimage {
+ private static final Deque<String> failed = new ConcurrentLinkedDeque<>();
+
+ public static void main(String... args) throws Exception {
+ long start = System.nanoTime();
+ int numThreads = Integer.getInteger("jdk.test.threads", 1);
+ List<JImageReader> readers = newJImageReaders();
+ VerifyJimage verify = new VerifyJimage(readers, numThreads);
+ if (args.length == 0) {
+ // load classes from jimage
+ verify.loadClasses();
+ } else {
+ Path dir = Paths.get(args[0]);
+ if (Files.notExists(dir) || !Files.isDirectory(dir)) {
+ throw new RuntimeException("Invalid argument: " + dir);
+ }
+ verify.compareExplodedModules(dir);
+ }
+ verify.waitForCompletion();
+ long end = System.nanoTime();
+ int entries = readers.stream()
+ .mapToInt(JImageReader::entries)
+ .sum();
+ System.out.format("%d entries %d files verified: %d ms %d errors%n",
+ entries, verify.count.get(),
+ TimeUnit.NANOSECONDS.toMillis(end - start), failed.size());
+ for (String f : failed) {
+ System.err.println(f);
+ }
+ }
+
+ private final AtomicInteger count = new AtomicInteger(0);
+ private final List<JImageReader> readers;
+ private final ExecutorService pool;
+
+ VerifyJimage(List<JImageReader> readers, int numThreads) {
+ this.readers = readers;
+ this.pool = Executors.newFixedThreadPool(numThreads);
+ }
+
+ private void waitForCompletion() throws InterruptedException {
+ pool.shutdown();
+ pool.awaitTermination(20, TimeUnit.SECONDS);
+ }
+
+ private void compareExplodedModules(Path dir) throws IOException {
+ System.out.println("comparing jimage with " + dir);
+
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
+ for (Path mdir : stream) {
+ if (Files.isDirectory(mdir)) {
+ pool.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Files.find(mdir, Integer.MAX_VALUE, (Path p, BasicFileAttributes attr)
+ -> !Files.isDirectory(p) &&
+ !mdir.relativize(p).toString().startsWith("_") &&
+ !p.getFileName().toString().equals("MANIFEST.MF"))
+ .forEach(p -> compare(mdir, p, readers));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+
+ private final List<String> BOOT_RESOURCES = Arrays.asList(
+ "java.base/META-INF/services/java.nio.file.spi.FileSystemProvider"
+ );
+ private final List<String> EXT_RESOURCES = Arrays.asList(
+ "jdk.zipfs/META-INF/services/java.nio.file.spi.FileSystemProvider"
+ );
+ private final List<String> APP_RESOURCES = Arrays.asList(
+ "jdk.hotspot.agent/META-INF/services/com.sun.jdi.connect.Connector",
+ "jdk.jdi/META-INF/services/com.sun.jdi.connect.Connector"
+ );
+
+ private void compare(Path mdir, Path p, List<JImageReader> readers) {
+ String entry = mdir.relativize(p).toString().replace(File.separatorChar, '/');
+
+ count.incrementAndGet();
+ String file = mdir.getFileName().toString() + "/" + entry;
+ if (APP_RESOURCES.contains(file)) {
+ // skip until the service config file is merged
+ System.out.println("Skipped " + file);
+ return;
+ }
+
+ String jimage;
+ if (BOOT_RESOURCES.contains(file)) {
+ jimage = "bootmodules.jimage";
+ } else if (EXT_RESOURCES.contains(file)) {
+ jimage = "extmodules.jimage";
+ } else {
+ jimage = "";
+ }
+ JImageReader reader = readers.stream()
+ .filter(r -> r.findLocation(entry) != null)
+ .filter(r -> jimage.isEmpty() || r.imageName().equals(jimage))
+ .findFirst().orElse(null);
+ if (reader == null) {
+ failed.add(entry + " not found: " + p.getFileName().toString());
+ } else {
+ reader.compare(entry, p);
+ }
+ }
+
+ private void loadClasses() {
+ ClassLoader loader = ClassLoader.getSystemClassLoader();
+ for (JImageReader reader : readers) {
+ Arrays.stream(reader.getEntryNames())
+ .filter(n -> n.endsWith(".class"))
+ .forEach(n -> {
+ String cn = n.substring(0, n.length()-6).replace('/', '.');
+ count.incrementAndGet();
+ try {
+ Class.forName(cn, false, loader);
+ } catch (ClassNotFoundException e) {
+ failed.add(reader.imageName() + ": " + cn + " not found");
+ }
+ });
+ }
+ }
+
+
+ private static List<JImageReader> newJImageReaders() throws IOException {
+ String home = System.getProperty("java.home");
+ Path mlib = Paths.get(home, "lib", "modules");
+ try (Stream<Path> paths = Files.list(mlib)) {
+ Set<Path> jimages = paths.filter(p -> p.toString().endsWith(".jimage"))
+ .collect(Collectors.toSet());
+ List<JImageReader> result = new ArrayList<>();
+ for (Path jimage: jimages) {
+ result.add(new JImageReader(jimage));
+ System.out.println("opened " + jimage);
+ }
+ return result;
+ }
+ }
+
+ static class JImageReader extends BasicImageReader {
+ final Path jimage;
+ JImageReader(Path p) throws IOException {
+ super(p.toString());
+ this.jimage = p;
+ }
+
+ String imageName() {
+ return jimage.getFileName().toString();
+ }
+
+ int entries() {
+ return getHeader().getLocationCount();
+ }
+
+ void compare(String entry, Path p) {
+ try {
+ byte[] bytes = Files.readAllBytes(p);
+ byte[] imagebytes = getResource(entry);
+ if (!Arrays.equals(bytes, imagebytes)) {
+ failed.add(imageName() + ": bytes differs than " + p.toString());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+}