1 /*
  2  * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /**
 25  * @test
 26  * @bug 8241071
 27  * @summary The same JDK build should always generate the same archive file (no randomness).
 28  * @requires vm.cds
 29  * @library /test/lib
 30  * @run driver DeterministicDump
 31  */
 32 
 33 import jdk.test.lib.cds.CDSOptions;
 34 import jdk.test.lib.cds.CDSTestUtils;
 35 import jdk.test.lib.Platform;
 36 import java.io.FileInputStream;
 37 import java.io.IOException;
 38 import java.util.ArrayList;
 39 
 40 public class DeterministicDump {
 41     public static void main(String[] args) throws Exception {
 42         doTest(false);
 43 
 44         if (Platform.is64bit()) {
 45             // There's no oop/klass compression on 32-bit.
 46             doTest(true);
 47         }
 48     }
 49 
 50     public static void doTest(boolean compressed) throws Exception {
 51         ArrayList<String> baseArgs = new ArrayList<>();
 52 
 53         // Use the same heap size as make/Images.gmk
 54         baseArgs.add("-Xmx128M");
 55 
 56         if (Platform.is64bit()) {
 57             // This option is available only on 64-bit.
 58             String sign = (compressed) ?  "+" : "-";
 59             baseArgs.add("-XX:" + sign + "UseCompressedOops");
 60         }
 61 
 62         String baseArchive = dump(baseArgs);
 63 
 64         // (1) Dump with the same args. Should produce the same archive.
 65         String baseArchive2 = dump(baseArgs);
 66         compare(baseArchive, baseArchive2);
 67 
 68         // (2) This will cause the archive to be relocated during dump time. We should
 69         //     still get the same bits. This simulates relocation that happens when
 70         //     Address Space Layout Randomization prevents the archive space to
 71         //     be mapped at the default location.
 72         String relocatedArchive = dump(baseArgs, "-XX:+UnlockDiagnosticVMOptions", "-XX:ArchiveRelocationMode=1");
 73         compare(baseArchive, relocatedArchive);
 74     }
 75 
 76     static int id = 0;
 77     static String dump(ArrayList<String> args, String... more) throws Exception {
 78         String logName = "SharedArchiveFile" + (id++);
 79         String archiveName = logName + ".jsa";
 80         String mapName = logName + ".map";
 81         CDSOptions opts = (new CDSOptions())
 82             .addPrefix("-Xint") // Override any -Xmixed/-Xcomp flags from jtreg -vmoptions
 83             .addPrefix("-Xlog:cds=debug")
 84             .addPrefix("-Xlog:cds+map*=trace:file=" + mapName + ":none:filesize=0")
 85             .addPrefix("-XX:-PreloadSharedClasses")
 86             .addPrefix("-XX:-ArchiveFieldReferences")
 87             .addPrefix("-XX:-ArchiveMethodReferences")
 88             .setArchiveName(archiveName)
 89             .addSuffix(args)
 90             .addSuffix(more);
 91         CDSTestUtils.createArchiveAndCheck(opts);
 92 
 93         return archiveName;
 94     }
 95 
 96     static void compare(String file0, String file1) throws Exception {
 97         byte[] buff0 = new byte[4096];
 98         byte[] buff1 = new byte[4096];
 99         try (FileInputStream in0 = new FileInputStream(file0);
100              FileInputStream in1 = new FileInputStream(file1)) {
101             int total = 0;
102             while (true) {
103                 int n0 = read(in0, buff0);
104                 int n1 = read(in1, buff1);
105                 if (n0 != n1) {
106                     throw new RuntimeException("File contents (file sizes?) are different after " + total + " bytes; n0 = "
107                                                + n0 + ", n1 = " + n1);
108                 }
109                 if (n0 == 0) {
110                     System.out.println("File contents are the same: " + total + " bytes");
111                     break;
112                 }
113                 for (int i = 0; i < n0; i++) {
114                     byte b0 = buff0[i];
115                     byte b1 = buff1[i];
116                     if (b0 != b1) {
117                         throw new RuntimeException("File content different at byte #" + (total + i) + ", b0 = " + b0 + ", b1 = " + b1);
118                     }
119                 }
120                 total += n0;
121             }
122         }
123     }
124 
125     static int read(FileInputStream in, byte[] buff) throws IOException {
126         int total = 0;
127         while (total < buff.length) {
128             int n = in.read(buff, total, buff.length - total);
129             if (n <= 0) {
130                 return total;
131             }
132             total += n;
133         }
134 
135         return total;
136     }
137 }