Path: blob/master/test/jdk/tools/jimage/VerifyJimage.java
41144 views
/*1* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.File;24import java.io.IOException;25import java.io.UncheckedIOException;26import java.nio.file.DirectoryStream;27import java.nio.file.Files;28import java.nio.file.Path;29import java.nio.file.Paths;30import java.nio.file.attribute.BasicFileAttributes;31import java.util.ArrayList;32import java.util.Arrays;33import java.util.Deque;34import java.util.List;35import java.util.Set;36import java.util.concurrent.ConcurrentLinkedDeque;37import java.util.concurrent.ExecutorService;38import java.util.concurrent.Executors;39import java.util.concurrent.TimeUnit;40import java.util.concurrent.atomic.AtomicInteger;41import java.util.stream.Collectors;42import java.util.stream.Stream;4344import jdk.internal.jimage.BasicImageReader;45import jdk.internal.jimage.ImageLocation;4647/*48* @test49* @summary Verify jimage50* @modules java.base/jdk.internal.jimage51* @run main/othervm --add-modules ALL-SYSTEM VerifyJimage52*/5354/**55* This test runs in two modes:56* (1) No argument: it verifies the jimage by loading all classes in the runtime57* (2) path of exploded modules: it compares bytes of each file in the exploded58* module with the entry in jimage59*60* FIXME: exception thrown when findLocation from jimage by multiple threads61* -Djdk.test.threads=<n> to specify the number of threads.62*/63public class VerifyJimage {64private static final String MODULE_INFO = "module-info.class";65private static final Deque<String> failed = new ConcurrentLinkedDeque<>();6667public static void main(String... args) throws Exception {6869String home = System.getProperty("java.home");70Path bootimagePath = Paths.get(home, "lib", "modules");71if (Files.notExists(bootimagePath)) {72System.out.println("Test skipped, not an images build");73return;74}7576long start = System.nanoTime();77int numThreads = Integer.getInteger("jdk.test.threads", 1);78JImageReader reader = newJImageReader();79VerifyJimage verify = new VerifyJimage(reader, numThreads);80if (args.length == 0) {81// load classes from jimage82verify.loadClasses();83} else {84Path dir = Paths.get(args[0]);85if (Files.notExists(dir) || !Files.isDirectory(dir)) {86throw new RuntimeException("Invalid argument: " + dir);87}88verify.compareExplodedModules(dir);89}90verify.waitForCompletion();91long end = System.nanoTime();92int entries = reader.entries();93System.out.format("%d entries %d files verified: %d ms %d errors%n",94entries, verify.count.get(),95TimeUnit.NANOSECONDS.toMillis(end - start), failed.size());96for (String f : failed) {97System.err.println(f);98}99if (!failed.isEmpty()) {100throw new AssertionError("Test failed");101}102}103104private final AtomicInteger count = new AtomicInteger(0);105private final JImageReader reader;106private final ExecutorService pool;107108VerifyJimage(JImageReader reader, int numThreads) {109this.reader = reader;110this.pool = Executors.newFixedThreadPool(numThreads);111}112113private void waitForCompletion() throws InterruptedException {114pool.shutdown();115pool.awaitTermination(20, TimeUnit.SECONDS);116}117118private void compareExplodedModules(Path dir) throws IOException {119System.out.println("comparing jimage with " + dir);120121try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {122for (Path mdir : stream) {123if (Files.isDirectory(mdir)) {124pool.execute(new Runnable() {125@Override126public void run() {127try {128Files.find(mdir, Integer.MAX_VALUE, (Path p, BasicFileAttributes attr)129-> !Files.isDirectory(p) &&130!mdir.relativize(p).toString().startsWith("_") &&131!p.getFileName().toString().equals("MANIFEST.MF"))132.forEach(p -> compare(mdir, p, reader));133} catch (IOException e) {134throw new UncheckedIOException(e);135}136}137});138}139}140}141}142143private final List<String> BOOT_RESOURCES = Arrays.asList(144"java.base/META-INF/services/java.nio.file.spi.FileSystemProvider"145);146private final List<String> EXT_RESOURCES = Arrays.asList(147"jdk.zipfs/META-INF/services/java.nio.file.spi.FileSystemProvider"148);149private final List<String> APP_RESOURCES = Arrays.asList(150"jdk.hotspot.agent/META-INF/services/com.sun.jdi.connect.Connector",151"jdk.jdi/META-INF/services/com.sun.jdi.connect.Connector"152);153154private void compare(Path mdir, Path p, JImageReader reader) {155String entry = p.getFileName().toString().equals(MODULE_INFO)156? mdir.getFileName().toString() + "/" + MODULE_INFO157: mdir.relativize(p).toString().replace(File.separatorChar, '/');158159count.incrementAndGet();160String file = mdir.getFileName().toString() + "/" + entry;161if (APP_RESOURCES.contains(file)) {162// skip until the service config file is merged163System.out.println("Skipped " + file);164return;165}166167if (reader.findLocation(entry) != null) {168reader.compare(entry, p);169}170}171172private void loadClasses() {173ClassLoader loader = ClassLoader.getSystemClassLoader();174Stream.of(reader.getEntryNames())175.filter(this::accept)176.map(this::toClassName)177.forEach(cn -> {178count.incrementAndGet();179try {180System.out.println("Loading " + cn);181Class.forName(cn, false, loader);182} catch (VerifyError ve) {183System.err.println("VerifyError for " + cn);184failed.add(reader.imageName() + ": " + cn + " not verified: " + ve.getMessage());185} catch (ClassNotFoundException e) {186failed.add(reader.imageName() + ": " + cn + " not found");187}188});189}190191private String toClassName(String entry) {192int index = entry.indexOf('/', 1);193return entry.substring(index + 1, entry.length())194.replaceAll("\\.class$", "").replace('/', '.');195}196197private static Set<String> EXCLUDED_MODULES =198Set.of("javafx.deploy", "jdk.deploy", "jdk.plugin", "jdk.javaws",199// All JVMCI packages other than jdk.vm.ci.services are dynamically200// exported to jdk.internal.vm.compiler201"jdk.internal.vm.compiler"202);203204private boolean accept(String entry) {205int index = entry.indexOf('/', 1);206String mn = index > 1 ? entry.substring(1, index) : "";207// filter deployment modules208209if (mn.isEmpty() || EXCLUDED_MODULES.contains(mn)) {210return false;211}212return entry.endsWith(".class") && !entry.endsWith(MODULE_INFO);213}214215private static JImageReader newJImageReader() throws IOException {216String home = System.getProperty("java.home");217Path jimage = Paths.get(home, "lib", "modules");218System.out.println("opened " + jimage);219return new JImageReader(jimage);220}221222static class JImageReader extends BasicImageReader {223final Path jimage;224JImageReader(Path p) throws IOException {225super(p);226this.jimage = p;227}228229String imageName() {230return jimage.getFileName().toString();231}232233int entries() {234return getHeader().getTableLength();235}236237void compare(String entry, Path p) {238try {239byte[] bytes = Files.readAllBytes(p);240byte[] imagebytes = getResource(entry);241if (!Arrays.equals(bytes, imagebytes)) {242failed.add(imageName() + ": bytes differs than " + p.toString());243}244} catch (IOException e) {245throw new UncheckedIOException(e);246}247}248}249}250251252