From 3b283543c33df8c225e10b9186b7bc3cefd1a347 Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Wed, 13 Nov 2024 23:18:31 +0000 Subject: [PATCH] 8339288: Improve diagnostic logging runtime/cds/DeterministicDump.java Reviewed-by: ccheung, iklam --- .../jtreg/runtime/cds/DeterministicDump.java | 151 ++++++++++++++++-- .../lib/jdk/test/lib/cds/CDSArchiveUtils.java | 12 +- 2 files changed, 151 insertions(+), 12 deletions(-) diff --git a/test/hotspot/jtreg/runtime/cds/DeterministicDump.java b/test/hotspot/jtreg/runtime/cds/DeterministicDump.java index 6b6431c0e5e..92643d648b7 100644 --- a/test/hotspot/jtreg/runtime/cds/DeterministicDump.java +++ b/test/hotspot/jtreg/runtime/cds/DeterministicDump.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -27,17 +27,32 @@ * @summary The same JDK build should always generate the same archive file (no randomness). * @requires vm.cds & vm.flagless * @library /test/lib - * @run driver DeterministicDump + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI DeterministicDump */ +import jdk.test.lib.cds.CDSArchiveUtils; import jdk.test.lib.cds.CDSOptions; import jdk.test.lib.cds.CDSTestUtils; import jdk.test.lib.Platform; +import java.io.BufferedReader; +import java.io.File; import java.io.FileInputStream; +import java.io.FileReader; import java.io.IOException; +import java.util.ArrayDeque; import java.util.ArrayList; public class DeterministicDump { + + static long HEADER_SIZE; // Size of header in bytes + static int HEADER_LEN = 106; // Number of lines in CDS map file header + static int LINE_OFFSET = 22; // Offset from address to first word of data + static int NUM_LINES = 5; // Number of lines to be printed + static int WORD_LEN = 16 + 1; // Length of word in map file + public static void main(String[] args) throws Exception { doTest(false); @@ -62,17 +77,19 @@ public class DeterministicDump { } String baseArchive = dump(baseArgs); + File baseArchiveFile = new File(baseArchive + ".jsa"); + HEADER_SIZE = CDSArchiveUtils.fileHeaderSize(baseArchiveFile); // (1) Dump with the same args. Should produce the same archive. String baseArchive2 = dump(baseArgs); - compare(baseArchive, baseArchive2); + compare(baseArchive, baseArchive2, baseArchiveFile); // (2) This will cause the archive to be relocated during dump time. We should // still get the same bits. This simulates relocation that happens when // Address Space Layout Randomization prevents the archive space to // be mapped at the default location. String relocatedArchive = dump(baseArgs, "-XX:+UnlockDiagnosticVMOptions", "-XX:ArchiveRelocationMode=1"); - compare(baseArchive, relocatedArchive); + compare(baseArchive, relocatedArchive, baseArchiveFile); } static int id = 0; @@ -89,14 +106,14 @@ public class DeterministicDump { .addSuffix(more); CDSTestUtils.createArchiveAndCheck(opts); - return archiveName; + return logName; } - static void compare(String file0, String file1) throws Exception { + static void compare(String file0, String file1, File archiveFile) throws Exception { byte[] buff0 = new byte[4096]; byte[] buff1 = new byte[4096]; - try (FileInputStream in0 = new FileInputStream(file0); - FileInputStream in1 = new FileInputStream(file1)) { + try (FileInputStream in0 = new FileInputStream(file0 + ".jsa"); + FileInputStream in1 = new FileInputStream(file1 + ".jsa")) { int total = 0; while (true) { int n0 = read(in0, buff0); @@ -113,7 +130,12 @@ public class DeterministicDump { byte b0 = buff0[i]; byte b1 = buff1[i]; if (b0 != b1) { - throw new RuntimeException("File content different at byte #" + (total + i) + ", b0 = " + b0 + ", b1 = " + b1); + // The checksums are stored in the header so it should be skipped + // since we want to see the first meaningful diff between the archives + if (total + i > HEADER_SIZE) { + print_diff(file0 + ".map", file1 + ".map", archiveFile, total + i); + throw new RuntimeException("File content different at byte #" + (total + i) + ", b0 = " + b0 + ", b1 = " + b1); + } } } total += n0; @@ -133,4 +155,115 @@ public class DeterministicDump { return total; } + + // CDS map file doesn't print the alignment bytes so they need to be considered + // when mapping the byte number in the archive to the word in the map file + static int archiveByteToMapWord(File archiveFile, int location) throws Exception { + int totalSize = 0; + int word = location; + + long len = HEADER_SIZE; + long aligned = CDSArchiveUtils.fileHeaderSizeAligned(archiveFile); + for (int i = 0; i < CDSArchiveUtils.num_regions(); i++) { + if (i != 0) { + len = CDSArchiveUtils.usedRegionSize(archiveFile, i); + aligned = CDSArchiveUtils.usedRegionSizeAligned(archiveFile, i); + } + totalSize += len; + if (location > totalSize) { + word -= (aligned - len - 16); + } + } + return word/8; + } + + // Read the mapfile and print out the lines associated with the location + static void print_diff(String mapName0, String mapName1, File archiveFile, int location) throws Exception { + FileReader f0 = new FileReader(mapName0); + BufferedReader b0 = new BufferedReader(f0); + + FileReader f1 = new FileReader(mapName1); + BufferedReader b1 = new BufferedReader(f1); + + int word = archiveByteToMapWord(archiveFile, location); + int wordOffset = word % 4; // Each line in the map file prints four words + String region = ""; + + // Skip header text and go to first line + for (int i = 0; i < HEADER_LEN; i++) { + b0.readLine(); + b1.readLine(); + } + + int line_num = HEADER_LEN; + String s0 = ""; + String s1 = ""; + int count = 0; + + // Store lines before and including the diff + ArrayDeque prefix0 = new ArrayDeque(); + ArrayDeque prefix1 = new ArrayDeque(); + + // A line may contain 1-4 words so we iterate by word + do { + s0 = b0.readLine(); + s1 = b1.readLine(); + line_num++; + + if (prefix0.size() >= NUM_LINES / 2 + 1) { + prefix0.removeFirst(); + prefix1.removeFirst(); + } + prefix0.addLast(s0); + prefix1.addLast(s1); + + // Skip lines with headers when counting words e.g. + // [rw region 0x0000000800000000 - 0x00000008005a1f88 5906312 bytes] + // or + // 0x0000000800000b28: @@ TypeArrayU1 16 + if (!s0.contains(": @@") && !s0.contains("bytes]")) { + int words = (s0.length() - LINE_OFFSET - 70) / 8; + count += words; + } else if (s0.contains("bytes]")) { + region = s0; + } + } while (count < word); + + // Print the diff with the region name above it + System.out.println("[First diff: map file #1 (" + mapName0 + ")]"); + System.out.println(region); + String diff0 = print_diff_helper(b0, wordOffset, prefix0); + + System.out.println("\n[First diff: map file #2 (" + mapName1 + ")]"); + System.out.println(region); + String diff1 = print_diff_helper(b1, wordOffset, prefix1); + + System.out.printf("\nByte #%d at line #%d word #%d:\n", location, line_num, wordOffset); + System.out.printf("%s: %s\n%s: %s\n", mapName0, diff0, mapName1, diff1); + + f0.close(); + f1.close(); + } + + static String print_diff_helper(BufferedReader b, int wordOffset, ArrayDeque prefix) throws Exception { + int start = LINE_OFFSET + WORD_LEN * wordOffset; + int end = start + WORD_LEN; + String line = prefix.getLast(); + String diff = line.substring(start, end); + + // Print previous lines + for (String s : prefix) { + if (s.equals(line)) { + System.out.println(">" + s); + } else { + System.out.println(" " + s); + } + } + + // Print extra lines + for (int i = 0; i < NUM_LINES / 2; i++) { + System.out.println(" " + b.readLine()); + } + return diff; + } } diff --git a/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java b/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java index 38748ec432b..fd76df92ef6 100644 --- a/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -145,6 +145,7 @@ public class CDSArchiveUtils { public static int dynamicArchiveHeaderSize() { return dynamicArchiveHeaderSize; } public static int cdsFileMapRegionSize() { return cdsFileMapRegionSize; } public static long alignment() { return alignment; } + public static int num_regions() { return num_regions; } @@ -495,9 +496,14 @@ public class CDSArchiveUtils { } // used region size - public static long usedRegionSizeAligned(File archiveFile, int region) throws Exception { + public static long usedRegionSize(File archiveFile, int region) throws Exception { long offset = spOffset + cdsFileMapRegionSize * region + spUsedOffset; - long used = readInt(archiveFile, offset, sizetSize); + return readInt(archiveFile, offset, sizetSize); + } + + // used region size + public static long usedRegionSizeAligned(File archiveFile, int region) throws Exception { + long used = usedRegionSize(archiveFile, region); return alignUpWithAlignment(used); } }