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  * @requires vm.flagless
 30  * @library /test/lib
 31  * @run driver DeterministicDump
 32  */
 33 
 34 import jdk.test.lib.cds.CDSOptions;
 35 import jdk.test.lib.cds.CDSTestUtils;
 36 import jdk.test.lib.Platform;
 37 import java.io.FileInputStream;
 38 import java.io.IOException;
 39 import java.util.ArrayList;
 40 
 41 public class DeterministicDump {
 42     public static void main(String[] args) throws Exception {
 43         doTest(false);
 44 
 45         if (Platform.is64bit()) {
 46             // There's no oop/klass compression on 32-bit.
 47             doTest(true);
 48         }
 49     }
 50 
 51     public static void doTest(boolean compressed) throws Exception {
 52         ArrayList<String> baseArgs = new ArrayList<>();
 53 
 54         // Use the same heap size as make/Images.gmk
 55         baseArgs.add("-Xmx128M");
 56 
 57         if (Platform.is64bit()) {
 58             // This option is available only on 64-bit.
 59             String sign = (compressed) ?  "+" : "-";
 60             baseArgs.add("-XX:" + sign + "UseCompressedOops");
 61         }
 62 
 63         String baseArchive = dump(baseArgs);
 64 
 65         // (1) Dump with the same args. Should produce the same archive.
 66         String baseArchive2 = dump(baseArgs);
 67         compare(baseArchive, baseArchive2);
 68 
 69         // (2) This will cause the archive to be relocated during dump time. We should
 70         //     still get the same bits. This simulates relocation that happens when
 71         //     Address Space Layout Randomization prevents the archive space to
 72         //     be mapped at the default location.
 73         String relocatedArchive = dump(baseArgs, "-XX:+UnlockDiagnosticVMOptions", "-XX:ArchiveRelocationMode=1");
 74         compare(baseArchive, relocatedArchive);
 75     }
 76 
 77     static int id = 0;
 78     static String dump(ArrayList<String> args, String... more) throws Exception {
 79         String logName = "SharedArchiveFile" + (id++);
 80         String archiveName = logName + ".jsa";
 81         String mapName = logName + ".map";
 82         CDSOptions opts = (new CDSOptions())
 83             .addPrefix("-Xint") // Override any -Xmixed/-Xcomp flags from jtreg -vmoptions
 84             .addPrefix("-Xlog:cds=debug")
 85             .addPrefix("-Xlog:cds+map*=trace:file=" + mapName + ":none:filesize=0")
 86             .setArchiveName(archiveName)
 87             .addSuffix(args)
 88             .addSuffix(more);
 89         CDSTestUtils.createArchiveAndCheck(opts);
 90 
 91         return archiveName;
 92     }
 93 
 94     static void compare(String file0, String file1) throws Exception {
 95         byte[] buff0 = new byte[4096];
 96         byte[] buff1 = new byte[4096];
 97         try (FileInputStream in0 = new FileInputStream(file0);
 98              FileInputStream in1 = new FileInputStream(file1)) {
 99             int total = 0;
100             while (true) {
101                 int n0 = read(in0, buff0);
102                 int n1 = read(in1, buff1);
103                 if (n0 != n1) {
104                     throw new RuntimeException("File contents (file sizes?) are different after " + total + " bytes; n0 = "
105                                                + n0 + ", n1 = " + n1);
106                 }
107                 if (n0 == 0) {
108                     System.out.println("File contents are the same: " + total + " bytes");
109                     break;
110                 }
111                 for (int i = 0; i < n0; i++) {
112                     byte b0 = buff0[i];
113                     byte b1 = buff1[i];
114                     if (b0 != b1) {
115                         throw new RuntimeException("File content different at byte #" + (total + i) + ", b0 = " + b0 + ", b1 = " + b1);
116                     }
117                 }
118                 total += n0;
119             }
120         }
121     }
122 
123     static int read(FileInputStream in, byte[] buff) throws IOException {
124         int total = 0;
125         while (total < buff.length) {
126             int n = in.read(buff, total, buff.length - total);
127             if (n <= 0) {
128                 return total;
129             }
130             total += n;
131         }
132 
133         return total;
134     }
135 }