From e3f26b056e6b8403e6744b8a4cf59ccf4d217d89 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 9 Apr 2025 20:57:15 +0000 Subject: [PATCH] 8351319: AOT cache support for custom class loaders broken since JDK-8348426 Reviewed-by: ccheung, matsaave, jrose --- src/hotspot/share/cds/aotArtifactFinder.cpp | 11 ++- .../share/cds/dumpTimeClassInfo.inline.hpp | 8 ++- src/hotspot/share/cds/finalImageRecipes.cpp | 6 +- .../classfile/systemDictionaryShared.cpp | 17 ++++- .../classfile/systemDictionaryShared.hpp | 1 + src/hotspot/share/oops/constantPool.cpp | 16 ++++- src/hotspot/share/oops/klass.cpp | 12 +++- .../aotClassLinking/BulkLoaderTest.java | 71 ++++++++++++++++--- .../dynamicArchive/LambdaCustomLoader.java | 4 +- .../LambdaProxyCallerIsHidden.java | 6 +- .../dynamicArchive/RegularHiddenClass.java | 4 +- .../cds/appcds/test-classes/SimpleCusty.java | 30 ++++++++ test/lib/jdk/test/lib/cds/CDSAppTester.java | 33 +++++++-- 13 files changed, 185 insertions(+), 34 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/test-classes/SimpleCusty.java diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index 6a7faee5133..95d242c2089 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -152,10 +152,11 @@ void AOTArtifactFinder::find_artifacts() { SystemDictionaryShared::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) { if (!info.is_excluded() && _seen_classes->get(k) == nullptr) { info.set_excluded(); - if (log_is_enabled(Info, cds)) { + info.set_has_checked_exclusion(); + if (log_is_enabled(Debug, cds)) { ResourceMark rm; - log_info(cds)("Skipping %s: %s class", k->name()->as_C_string(), - k->is_hidden() ? "Hidden" : "AOT tooling"); + log_debug(cds)("Skipping %s: %s class", k->name()->as_C_string(), + k->is_hidden() ? "Unreferenced hidden" : "AOT tooling"); } } }); @@ -211,6 +212,10 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { _seen_classes->put_if_absent(ik, &created); if (created) { _all_cached_classes->append(ik); + if (CDSConfig::is_dumping_final_static_archive() && ik->is_shared_unregistered_class()) { + // The following are not appliable to unregistered classes + return; + } scan_oops_in_instance_class(ik); if (ik->is_hidden() && CDSConfig::is_initing_classes_at_dump_time()) { bool succeed = AOTClassLinker::try_add_candidate(ik); diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp index 042a0dadb20..6f5ca6d7915 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, 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 @@ -28,6 +28,7 @@ #include "cds/dumpTimeClassInfo.hpp" +#include "cds/cdsConfig.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/classLoaderData.inline.hpp" #include "oops/instanceKlass.hpp" @@ -44,7 +45,10 @@ void DumpTimeSharedClassTable::iterate_all_live_classes(Function function) const auto wrapper = [&] (InstanceKlass* k, DumpTimeClassInfo& info) { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); assert_lock_strong(DumpTimeTable_lock); - if (k->is_loader_alive()) { + if (CDSConfig::is_dumping_final_static_archive() && !k->is_loaded()) { + assert(k->is_shared_unregistered_class(), "must be"); + function(k, info); + } else if (k->is_loader_alive()) { function(k, info); assert(k->is_loader_alive(), "must not change"); } else { diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 55855679a1c..bdfd261355b 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -102,7 +102,11 @@ void FinalImageRecipes::load_all_classes(TRAPS) { Klass* k = _all_klasses->at(i); if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); - if (!ik->is_shared_unregistered_class() && !ik->is_hidden()) { + if (ik->is_shared_unregistered_class()) { + SystemDictionaryShared::init_dumptime_info(ik); + SystemDictionaryShared::add_unregistered_class(THREAD, ik); + SystemDictionaryShared::copy_unregistered_class_size_and_crc32(ik); + } else if (!ik->is_hidden()) { Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK); if (actual != ik) { ResourceMark rm(THREAD); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 2fbee502b93..5c4ee3f9452 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -465,6 +465,21 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla return (klass == *v); } +void SystemDictionaryShared::copy_unregistered_class_size_and_crc32(InstanceKlass* klass) { + precond(CDSConfig::is_dumping_final_static_archive()); + precond(klass->is_shared()); + + // A shared class must have a RunTimeClassInfo record + const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary, + nullptr, klass->name()); + precond(record != nullptr); + precond(record->klass() == klass); + + DumpTimeClassInfo* info = get_info(klass); + info->_clsfile_size = record->crc()->_clsfile_size; + info->_clsfile_crc32 = record->crc()->_clsfile_crc32; +} + void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) { assert(CDSConfig::is_dumping_archive(), "sanity"); assert(!is_builtin(k), "must be unregistered class"); @@ -667,7 +682,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) { } void SystemDictionaryShared::finish_exclusion_checks() { - if (CDSConfig::is_dumping_dynamic_archive()) { + if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) { // Do this first -- if a base class is excluded due to duplication, // all of its subclasses will also be excluded. ResourceMark rm; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index e910bfb5d47..22fc3fd825d 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -248,6 +248,7 @@ public: return (k->shared_classpath_index() != UNREGISTERED_INDEX); } static bool add_unregistered_class(Thread* current, InstanceKlass* k); + static void copy_unregistered_class_size_and_crc32(InstanceKlass* klass); static void finish_exclusion_checks(); static DumpTimeSharedClassTable* dumptime_table() { return _dumptime_table; } diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 0b6f3543b7a..f6a94a984ce 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -476,9 +476,23 @@ void ConstantPool::remove_unshareable_info() { return; } + bool update_resolved_reference = true; + if (CDSConfig::is_dumping_final_static_archive()) { + ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this); + InstanceKlass* src_holder = src_cp->pool_holder(); + if (src_holder->is_shared_unregistered_class()) { + // Unregistered classes are not loaded in the AOT assembly phase. The resolved reference length + // is already saved during the training run. + precond(!src_holder->is_loaded()); + precond(resolved_reference_length() >= 0); + precond(resolved_references() == nullptr); + update_resolved_reference = false; + } + } + // resolved_references(): remember its length. If it cannot be restored // from the archived heap objects at run time, we need to dynamically allocate it. - if (cache() != nullptr) { + if (update_resolved_reference && cache() != nullptr) { set_resolved_reference_length( resolved_references() != nullptr ? resolved_references()->length() : 0); set_resolved_references(OopHandle()); diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 9f166e13751..71da5e1da09 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -828,9 +828,15 @@ void Klass::remove_java_mirror() { if (CDSConfig::is_dumping_heap()) { Klass* src_k = ArchiveBuilder::current()->get_source_addr(this); oop orig_mirror = src_k->java_mirror(); - oop scratch_mirror = HeapShared::scratch_java_mirror(orig_mirror); - if (scratch_mirror != nullptr) { - _archived_mirror_index = HeapShared::append_root(scratch_mirror); + if (orig_mirror == nullptr) { + assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); + assert(is_instance_klass(), "sanity"); + assert(InstanceKlass::cast(this)->is_shared_unregistered_class(), "sanity"); + } else { + oop scratch_mirror = HeapShared::scratch_java_mirror(orig_mirror); + if (scratch_mirror != nullptr) { + _archived_mirror_index = HeapShared::append_root(scratch_mirror); + } } } #endif diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java index 9db886e20a5..cbc4644d5ca 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java @@ -31,11 +31,14 @@ * @requires vm.cds.supports.aot.class.linking * @comment work around JDK-8345635 * @requires !vm.jvmci.enabled - * @library /test/jdk/lib/testlibrary /test/lib + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build InitiatingLoaderTester BadOldClassA BadOldClassB - * @build BulkLoaderTest + * @build jdk.test.whitebox.WhiteBox BulkLoaderTest SimpleCusty * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester * BadOldClassA BadOldClassB + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar + * SimpleCusty + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox * @run driver BulkLoaderTest STATIC */ @@ -44,13 +47,15 @@ * @requires vm.cds.supports.aot.class.linking * @comment work around JDK-8345635 * @requires !vm.jvmci.enabled - * @library /test/jdk/lib/testlibrary /test/lib + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build InitiatingLoaderTester BadOldClassA BadOldClassB - * @build jdk.test.whitebox.WhiteBox BulkLoaderTest + * @build jdk.test.whitebox.WhiteBox BulkLoaderTest SimpleCusty * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester * BadOldClassA BadOldClassB - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. BulkLoaderTest DYNAMIC + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar + * SimpleCusty + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:WhiteBox.jar BulkLoaderTest DYNAMIC */ /* @@ -58,16 +63,21 @@ * @requires vm.cds.supports.aot.class.linking * @comment work around JDK-8345635 * @requires !vm.jvmci.enabled - * @library /test/jdk/lib/testlibrary /test/lib - * @build InitiatingLoaderTester BadOldClassA BadOldClassB - * @build BulkLoaderTest + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build jdk.test.whitebox.WhiteBox InitiatingLoaderTester BadOldClassA BadOldClassB + * @build BulkLoaderTest SimpleCusty * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester * BadOldClassA BadOldClassB + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar + * SimpleCusty * @run driver BulkLoaderTest AOT */ import java.io.File; import java.lang.StackWalker.StackFrame; +import java.net.URL; +import java.net.URLClassLoader; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -76,6 +86,7 @@ import java.util.Set; import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; public class BulkLoaderTest { static final String appJar = ClassFileInstaller.getJarPath("BulkLoaderTestApp.jar"); @@ -114,6 +125,7 @@ public class BulkLoaderTest { static class Tester extends CDSAppTester { public Tester() { super(mainClass); + useWhiteBox(ClassFileInstaller.getJarPath("WhiteBox.jar")); } @Override @@ -124,7 +136,7 @@ public class BulkLoaderTest { @Override public String[] vmArgs(RunMode runMode) { return new String[] { - "-Xlog:cds,cds+aot+load", + "-Xlog:cds,cds+aot+load,cds+class=debug", "-XX:+AOTClassLinking", }; } @@ -140,6 +152,12 @@ public class BulkLoaderTest { public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { if (isAOTWorkflow() && runMode == RunMode.TRAINING) { out.shouldContain("Skipping BadOldClassA: Unlinked class not supported by AOTConfiguration"); + out.shouldContain("Skipping SimpleCusty: Duplicated unregistered class"); + } + + if (isDumping(runMode)) { + // Check that we are archiving classes for custom class loaders. + out.shouldMatch("cds,class.* SimpleCusty"); } } } @@ -152,6 +170,7 @@ class BulkLoaderTestApp { checkClasses(); checkInitiatingLoader(); checkOldClasses(); + checkCustomLoader(); } // Check the ClassLoader/Module/Package/ProtectionDomain/CodeSource of classes that are aot-linked @@ -275,6 +294,38 @@ class BulkLoaderTestApp { System.out.println("Caught VerifyError for BadOldClassB: " + e); } } + + + static void checkCustomLoader() throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + for (int i = 0; i < 2; i++) { + Object o = initFromCustomLoader(); + System.out.println(o); + Class c = o.getClass(); + if (wb.isSharedClass(BulkLoaderTestApp.class)) { + // We are running with BulkLoaderTestApp from the AOT cache (or CDS achive) + if (i == 0) { + if (!wb.isSharedClass(c)) { + throw new RuntimeException("The first loader should load SimpleCusty from AOT cache (or CDS achive)"); + } + } else { + if (wb.isSharedClass(c)) { + throw new RuntimeException("The second loader should not load SimpleCusty from AOT cache (or CDS achive)"); + } + } + } + } + } + + static Object initFromCustomLoader() throws Exception { + String path = "cust.jar"; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + URLClassLoader urlClassLoader = + new URLClassLoader("MyLoader", urls, null); + Class c = Class.forName("SimpleCusty", true, urlClassLoader); + return c.newInstance(); + } } class MyUtil { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java index 205901e3769..4563aa60c89 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -51,7 +51,7 @@ public class LambdaCustomLoader extends DynamicArchiveTestBase { "-Xlog:class+load,cds=debug,cds+dynamic", "-cp", appJar, mainClass, appJar, "init", "keep-alive") .assertNormalExit(output -> { - output.shouldMatch("Skipping.LambHello[$][$]Lambda.*0x.*:.Hidden.class") + output.shouldMatch("Skipping.LambHello[$][$]Lambda.*0x.*:.Unreferenced.hidden.class") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java index 0c8cad0b3a6..4ec7e817378 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -59,8 +59,8 @@ public class LambdaProxyCallerIsHidden extends DynamicArchiveTestBase { "-Xlog:class+load,cds+dynamic,cds=debug", "-cp", appJar, mainClass) .assertNormalExit(output -> { - output.shouldMatch("Skipping.LambdaHello_0x.*[$][$]Lambda.*:.Hidden.class") - .shouldMatch("Skipping.LambdaHello.0x.*:.Hidden.class") + output.shouldMatch("Skipping.LambdaHello_0x.*[$][$]Lambda.*:.Unreferenced.hidden.class") + .shouldMatch("Skipping.LambdaHello.0x.*:.Unreferenced.hidden.class") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java index 957e4cb6479..7793fd07bc3 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -57,7 +57,7 @@ public class RegularHiddenClass extends DynamicArchiveTestBase { "-Xlog:class+load=debug,cds+dynamic,cds=debug", "-cp", appJar, mainClass, "keep-alive") .assertNormalExit(output -> { - output.shouldMatch("cds.*Skipping.TestClass.0x.*Hidden.class") + output.shouldMatch("cds.*Skipping.TestClass.0x.*Unreferenced.hidden.class") .shouldNotMatch("cds.dynamic.*Archiving.hidden.TestClass.*") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/SimpleCusty.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/SimpleCusty.java new file mode 100644 index 00000000000..9c82e0eb15d --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/SimpleCusty.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +// This class is to be loaded by a custom class loader. +public class SimpleCusty { + public String toString() { + return "Instance of SimpleCusty"; + } +} diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index 4de6778e4c2..f08cbf0e7e2 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -50,6 +50,7 @@ abstract public class CDSAppTester { private final String dynamicArchiveFileLog; private final String tempBaseArchiveFile; private int numProductionRuns = 0; + private String whiteBoxJar = null; public CDSAppTester(String name) { if (CDSTestUtils.DYNAMIC_DUMP) { @@ -150,6 +151,10 @@ abstract public class CDSAppTester { checkExitValue = b; } + public final void useWhiteBox(String whiteBoxJar) { + this.whiteBoxJar = whiteBoxJar; + } + public final boolean isStaticWorkflow() { return workflow == Workflow.STATIC; } @@ -199,6 +204,12 @@ abstract public class CDSAppTester { return output; } + private String[] addCommonVMArgs(RunMode runMode, String[] cmdLine) { + cmdLine = addClassOrModulePath(runMode, cmdLine); + cmdLine = addWhiteBox(cmdLine); + return cmdLine; + } + private String[] addClassOrModulePath(RunMode runMode, String[] cmdLine) { String cp = classpath(runMode); if (cp == null) { @@ -214,6 +225,16 @@ abstract public class CDSAppTester { return cmdLine; } + private String[] addWhiteBox(String[] cmdLine) { + if (whiteBoxJar != null) { + cmdLine = StringArrayUtils.concat(cmdLine, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:" + whiteBoxJar); + } + return cmdLine; + } + private OutputAnalyzer recordAOTConfiguration() throws Exception { RunMode runMode = RunMode.TRAINING; String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), @@ -223,7 +244,7 @@ abstract public class CDSAppTester { "class+load=debug", "cds=debug", "cds+class=debug")); - cmdLine = addClassOrModulePath(runMode, cmdLine); + cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, aotConfigurationFile, aotConfigurationFileLog); } @@ -235,7 +256,7 @@ abstract public class CDSAppTester { "-XX:DumpLoadedClassList=" + classListFile, logToFile(classListFileLog, "class+load=debug")); - cmdLine = addClassOrModulePath(runMode, cmdLine); + cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, classListFile, classListFileLog); } @@ -253,7 +274,7 @@ abstract public class CDSAppTester { "cds+class=debug", "cds+heap=warning", "cds+resolve=debug")); - cmdLine = addClassOrModulePath(runMode, cmdLine); + cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog); } @@ -271,7 +292,7 @@ abstract public class CDSAppTester { "cds+class=debug", "cds+heap=warning", "cds+resolve=debug")); - cmdLine = addClassOrModulePath(runMode, cmdLine); + cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog); } @@ -317,7 +338,7 @@ abstract public class CDSAppTester { "cds+class=debug", "cds+resolve=debug", "class+load=debug")); - cmdLine = addClassOrModulePath(runMode, cmdLine); + cmdLine = addCommonVMArgs(runMode, cmdLine); } if (baseArchive != null) { cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + baseArchive); @@ -342,7 +363,7 @@ abstract public class CDSAppTester { "-XX:+UnlockDiagnosticVMOptions", "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good. logToFile(productionRunLog(), "cds")); - cmdLine = addClassOrModulePath(runMode, cmdLine); + cmdLine = addCommonVMArgs(runMode, cmdLine); if (isStaticWorkflow()) { cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + staticArchiveFile);