Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8339288: Improve diagnostic logging runtime/cds/DeterministicDump.java #21913

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 142 additions & 9 deletions test/hotspot/jtreg/runtime/cds/DeterministicDump.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -60,17 +75,19 @@ public static void doTest(boolean compressed) throws Exception {
}

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;
Expand All @@ -87,14 +104,14 @@ static String dump(ArrayList<String> args, String... more) throws Exception {
.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);
Expand All @@ -111,7 +128,12 @@ static void compare(String file0, String file1) throws Exception {
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;
Expand All @@ -131,4 +153,115 @@ static int read(FileInputStream in, byte[] buff) throws IOException {

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<String> prefix0 = new ArrayDeque<String>();
ArrayDeque<String> prefix1 = new ArrayDeque<String>();

// 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<String> 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;
}
}
7 changes: 7 additions & 0 deletions test/lib/jdk/test/lib/cds/CDSArchiveUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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; }



Expand Down Expand Up @@ -494,6 +495,12 @@ public static File deleteBytesAtTheEnd(File orgFile, String newFileName) throws
return dstFile;
}

// used region size
public static long usedRegionSize(File archiveFile, int region) throws Exception {
long offset = spOffset + cdsFileMapRegionSize * region + spUsedOffset;
return readInt(archiveFile, offset, sizetSize);
}

// used region size
public static long usedRegionSizeAligned(File archiveFile, int region) throws Exception {
long offset = spOffset + cdsFileMapRegionSize * region + spUsedOffset;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you just call usedRegionSize like the following?
long used = usedRegionSize(archiveFile, region);

Expand Down