From bd7315648f2bb18cba9cfbeca00e6132b8eb95ef Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Fri, 17 Oct 2025 00:36:54 +0000 Subject: [PATCH] 8369856: AOT map does not include unregistered classes Co-authored-by: Ashutosh Mehra Reviewed-by: kvn, matsaave --- .../classfile/systemDictionaryShared.cpp | 4 + .../{CDSMapReader.java => AOTMapReader.java} | 77 +++++++++++++++++-- .../cds/{CDSMapTest.java => AOTMapTest.java} | 12 +-- .../cds/appcds/aotCache/AOTMapTest.java | 55 +++++++++---- 4 files changed, 120 insertions(+), 28 deletions(-) rename test/hotspot/jtreg/runtime/cds/{CDSMapReader.java => AOTMapReader.java} (81%) rename test/hotspot/jtreg/runtime/cds/{CDSMapTest.java => AOTMapTest.java} (92%) diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index b092e71f4e7..2d31a7c49f6 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -1420,6 +1420,10 @@ void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, Gr get_archive(is_static_archive)->_builtin_dictionary.iterate([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); + + get_archive(is_static_archive)->_unregistered_dictionary.iterate([&] (const RunTimeClassInfo* record) { + classes->append(record->klass()); + }); } class SharedDictionaryPrinter : StackObj { diff --git a/test/hotspot/jtreg/runtime/cds/CDSMapReader.java b/test/hotspot/jtreg/runtime/cds/AOTMapReader.java similarity index 81% rename from test/hotspot/jtreg/runtime/cds/CDSMapReader.java rename to test/hotspot/jtreg/runtime/cds/AOTMapReader.java index f25455b2f03..e407d4e2ecc 100644 --- a/test/hotspot/jtreg/runtime/cds/CDSMapReader.java +++ b/test/hotspot/jtreg/runtime/cds/AOTMapReader.java @@ -26,6 +26,7 @@ import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -33,7 +34,7 @@ import java.util.regex.Pattern; This is a simple parser for parsing the output of - java -Xshare:dump -Xlog:aot+map=debug,aot+map+oops=trace:file=cds.map:none:filesize=0 + java -Xshare:dump -Xlog:aot+map=debug,aot+map+oops=trace:file=aot.map:none:filesize=0 The map file contains patterns like this for the heap objects: @@ -59,8 +60,9 @@ more analysis on the HeapObjects. */ -public class CDSMapReader { +public class AOTMapReader { public static class MapFile { + HashSet classes = new HashSet<>(); ArrayList heapObjects = new ArrayList<>(); HashMap oopToObject = new HashMap<>(); HashMap narrowOopToObject = new HashMap<>(); @@ -80,6 +82,20 @@ public class CDSMapReader { public int heapObjectCount() { return heapObjects.size(); } + + void addClass(String className) { + classes.add(className); + } + + public boolean hasClass(String className) { + return classes.contains(className); + } + + public void shouldHaveClass(String className) { + if (!hasClass(className)) { + throw new RuntimeException("AOT map file is missing class " + className); + } + } } public static class HeapAddress { @@ -140,13 +156,17 @@ public class CDSMapReader { this.name = name; this.offset = Integer.parseInt(offset); this.referentAddress = new HeapAddress(oopStr, narrowOopStr); - this.lineCount = CDSMapReader.lineCount; + this.lineCount = AOTMapReader.lineCount; } } // 0x00000007ffc00000: 4a5b8701 00000063 00010290 00000000 00010100 fff80003 static Pattern rawDataPattern = Pattern.compile("^0x([0-9a-f]+): *( [0-9a-f]+)+ *$"); + // ------------------------------------------------------------------------------- + // Patterns for heap objects + // ------------------------------------------------------------------------------- + // (one address) // 0x00000007ffc00000: @@ Object java.lang.String static Pattern objPattern1 = Pattern.compile("^0x([0-9a-f]+): @@ Object ([^ ]*)"); @@ -179,6 +199,15 @@ public class CDSMapReader { // - injected 'module_entry' 'J' @16 0 (0x0000000000000000) static Pattern moduleEntryPattern = Pattern.compile("- injected 'module_entry' 'J' @[0-9]+[ ]+([0-9]+)"); + // ------------------------------------------------------------------------------- + // Patterns for metaspace objects + // ------------------------------------------------------------------------------- + + // 0x00000008000d1698: @@ Class 512 [Ljdk.internal.vm.FillerElement; + // 0x00000008000d18a0: @@ Class 520 java.lang.Cloneable + static Pattern classPattern = Pattern.compile("^0x([0-9a-f]+): @@ Class [ ]*([0-9]+) (.*)"); + + private static Matcher match(String line, Pattern pattern) { Matcher m = pattern.matcher(line); if (m.find()) { @@ -253,6 +282,11 @@ public class CDSMapReader { } } + private static void parseClassObject(String className, String addr, String size) throws IOException { + mapFile.addClass(className); + nextLine(); + } + static MapFile mapFile; static BufferedReader reader; static String line = null; // current line being parsed @@ -277,6 +311,8 @@ public class CDSMapReader { parseHeapObject(m.group(3), m.group(1), m.group(2)); } else if ((m = match(line, objPattern1)) != null) { parseHeapObject(m.group(2), m.group(1), null); + } else if ((m = match(line, classPattern)) != null) { + parseClassObject(m.group(3), m.group(1), m.group(2)); // name, addr, size } else { nextLine(); } @@ -303,8 +339,15 @@ public class CDSMapReader { } } + public static void validate(MapFile mapFile, String classLoadLogFile) throws IOException { + validateOops(mapFile); + if (classLoadLogFile != null) { + validateClasses(mapFile, classLoadLogFile); + } + } + // Check that each oop fields in the HeapObjects must point to a valid HeapObject. - public static void validate(MapFile mapFile) { + static void validateOops(MapFile mapFile) { int count1 = 0; int count2 = 0; for (HeapObject heapObject : mapFile.heapObjects) { @@ -333,10 +376,10 @@ public class CDSMapReader { if (mapFile.heapObjectCount() > 0) { // heapObjectCount() may be zero if the selected GC doesn't support heap object archiving. if (mapFile.stringCount <= 0) { - throw new RuntimeException("CDS map file should contain at least one string"); + throw new RuntimeException("AOT map file should contain at least one string"); } if (count1 < mapFile.stringCount) { - throw new RuntimeException("CDS map file seems incorrect: " + mapFile.heapObjectCount() + + throw new RuntimeException("AOT map file seems incorrect: " + mapFile.heapObjectCount() + " objects (" + mapFile.stringCount + " strings). Each string should" + " have one non-null oop field but we found only " + count1 + " non-null oop field references"); @@ -344,8 +387,26 @@ public class CDSMapReader { } } - public static void main(String args[]) { + // classLoadLogFile should be generated with -Xlog:class+load:file=:none:filesize=0 + // Check that every class loaded from "source: shared objects file" have an entry inside the mapFile. + static void validateClasses(MapFile mapFile, String classLoadLogFile) throws IOException { + try (BufferedReader r = new BufferedReader(new FileReader(classLoadLogFile))) { + String line; + String suffix = " source: shared objects file"; + int suffixLen = suffix.length(); + while ((line = r.readLine()) != null) { + if (line.endsWith(suffix)) { + String className = line.substring(0, line.length() - suffixLen); + if (!mapFile.hasClass(className)) { + throw new RuntimeException("AOT map file is missing class " + className); + } + } + } + } + } + + public static void main(String args[]) throws IOException { MapFile mapFile = read(args[0]); - validate(mapFile); + validate(mapFile, null); } } diff --git a/test/hotspot/jtreg/runtime/cds/CDSMapTest.java b/test/hotspot/jtreg/runtime/cds/AOTMapTest.java similarity index 92% rename from test/hotspot/jtreg/runtime/cds/CDSMapTest.java rename to test/hotspot/jtreg/runtime/cds/AOTMapTest.java index 5a9fa82552b..dff98090859 100644 --- a/test/hotspot/jtreg/runtime/cds/CDSMapTest.java +++ b/test/hotspot/jtreg/runtime/cds/AOTMapTest.java @@ -27,7 +27,7 @@ * @summary Test the contents of -Xlog:aot+map * @requires vm.cds * @library /test/lib - * @run driver/timeout=240 CDSMapTest + * @run driver/timeout=240 AOTMapTest */ import jdk.test.lib.cds.CDSOptions; @@ -37,7 +37,7 @@ import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import java.util.ArrayList; -public class CDSMapTest { +public class AOTMapTest { public static void main(String[] args) throws Exception { doTest(false); @@ -79,8 +79,8 @@ public class CDSMapTest { .addSuffix(args); CDSTestUtils.createArchiveAndCheck(opts); - CDSMapReader.MapFile mapFile = CDSMapReader.read(mapName); - CDSMapReader.validate(mapFile); + AOTMapReader.MapFile mapFile = AOTMapReader.read(mapName); + AOTMapReader.validate(mapFile, null); return archiveName; } @@ -98,7 +98,7 @@ public class CDSMapTest { OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "exec"); out.shouldHaveExitValue(0); - CDSMapReader.MapFile mapFile = CDSMapReader.read(mapName); - CDSMapReader.validate(mapFile); + AOTMapReader.MapFile mapFile = AOTMapReader.read(mapName); + AOTMapReader.validate(mapFile, null); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java index bcd2c71fea0..6cbfcbbd3c3 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java @@ -26,9 +26,10 @@ * @bug 8362566 * @summary Test the contents of -Xlog:aot+map with AOT workflow * @requires vm.cds.supports.aot.class.linking - * @library /test/lib /test/hotspot/jtreg/runtime/cds - * @build AOTMapTest + * @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build AOTMapTest Hello * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar Hello * @run driver/timeout=240 AOTMapTest AOT --two-step-training */ @@ -37,15 +38,18 @@ * @bug 8362566 * @summary Test the contents of -Xlog:aot+map with dynamic CDS archive * @requires vm.cds.supports.aot.class.linking - * @library /test/lib /test/hotspot/jtreg/runtime/cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @build AOTMapTest + * @build AOTMapTest Hello * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar Hello * @run main/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. AOTMapTest DYNAMIC */ - +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.helpers.ClassFileInstaller; @@ -54,6 +58,7 @@ import jdk.test.lib.Platform; public class AOTMapTest { static final String appJar = ClassFileInstaller.getJarPath("app.jar"); static final String mainClass = "AOTMapTestApp"; + static final String classLoadLogFile = "production.class.load.log"; public static void main(String[] args) throws Exception { doTest(args); @@ -63,13 +68,25 @@ public class AOTMapTest { Tester tester = new Tester(); tester.run(args); - validate(tester.dumpMapFile); - validate(tester.runMapFile); + if (tester.isDynamicWorkflow()) { + // For dynamic workflow, the AOT map file doesn't include classes in the base archive, so + // AOTMapReader.validateClasses() will fail. + validate(tester.dumpMapFile, false); + } else { + validate(tester.dumpMapFile, true); + } + validate(tester.runMapFile, true); } - static void validate(String mapFileName) { - CDSMapReader.MapFile mapFile = CDSMapReader.read(mapFileName); - CDSMapReader.validate(mapFile); + static void validate(String mapFileName, boolean checkClases) throws Exception { + AOTMapReader.MapFile mapFile = AOTMapReader.read(mapFileName); + if (checkClases) { + AOTMapReader.validate(mapFile, classLoadLogFile); + } else { + AOTMapReader.validate(mapFile, null); + } + mapFile.shouldHaveClass("AOTMapTestApp"); // built-in class + mapFile.shouldHaveClass("Hello"); // unregistered class } static class Tester extends CDSAppTester { @@ -97,12 +114,13 @@ public class AOTMapTest { // filesize=0 ensures that a large map file not broken up in multiple files. String logMapPrefix = "-Xlog:aot+map=debug,aot+map+oops=trace:file="; - String logMapSuffix = ":none:filesize=0"; + String logSuffix = ":none:filesize=0"; if (runMode == RunMode.ASSEMBLY || runMode == RunMode.DUMP_DYNAMIC) { - vmArgs.add(logMapPrefix + dumpMapFile + logMapSuffix); + vmArgs.add(logMapPrefix + dumpMapFile + logSuffix); } else if (runMode == RunMode.PRODUCTION) { - vmArgs.add(logMapPrefix + runMapFile + logMapSuffix); + vmArgs.add(logMapPrefix + runMapFile + logSuffix); + vmArgs.add("-Xlog:class+load:file=" + classLoadLogFile + logSuffix); } return vmArgs.toArray(new String[vmArgs.size()]); @@ -118,7 +136,16 @@ public class AOTMapTest { } class AOTMapTestApp { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { System.out.println("Hello AOTMapTestApp"); + testCustomLoader(); + } + + static void testCustomLoader() throws Exception { + File custJar = new File("cust.jar"); + URL[] urls = new URL[] {custJar.toURI().toURL()}; + URLClassLoader loader = new URLClassLoader(urls, AOTMapTestApp.class.getClassLoader()); + Class c = loader.loadClass("Hello"); + System.out.println(c); } }