From 9e214c5e4eb7f70cf97cfbdf1071e0dc12a55756 Mon Sep 17 00:00:00 2001 From: iklam Date: Wed, 26 Mar 2025 20:40:38 -0700 Subject: [PATCH 1/7] 8353014: Exclude AOT tooling classes from AOT cache --- src/hotspot/share/cds/aotArtifactFinder.cpp | 9 ++- src/hotspot/share/cds/aotClassFilter.cpp | 27 ++++++++ src/hotspot/share/cds/aotClassFilter.hpp | 68 +++++++++++++++++++ src/hotspot/share/cds/cdsConfig.cpp | 18 +++++ src/hotspot/share/cds/cdsConfig.hpp | 1 + src/hotspot/share/cds/dumpTimeClassInfo.hpp | 17 ++++- src/hotspot/share/cds/dynamicArchive.cpp | 39 ++++++----- src/hotspot/share/cds/dynamicArchive.hpp | 3 +- src/hotspot/share/cds/lambdaFormInvokers.cpp | 53 ++++++++++----- .../share/cds/lambdaProxyClassDictionary.cpp | 5 ++ src/hotspot/share/cds/metaspaceShared.cpp | 66 +++++++++++------- src/hotspot/share/cds/metaspaceShared.hpp | 3 +- .../classfile/systemDictionaryShared.cpp | 23 +++---- .../classfile/systemDictionaryShared.hpp | 2 +- 14 files changed, 253 insertions(+), 81 deletions(-) create mode 100644 src/hotspot/share/cds/aotClassFilter.cpp create mode 100644 src/hotspot/share/cds/aotClassFilter.hpp diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index e644a5e6f5fd1..6a7faee51330c 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -109,7 +109,10 @@ void AOTArtifactFinder::find_artifacts() { // Add all the InstanceKlasses (and their array classes) that are always included. SystemDictionaryShared::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* ik, DumpTimeClassInfo& info) { - if (!info.is_excluded()) { + // Skip "AOT tooling classes" in this block. They will be included in the AOT cache only if + // - One of their subtypes is included + // - One of their instances is found by HeapShared. + if (!info.is_excluded() && !info.is_aot_tooling_class()) { bool add = false; if (!ik->is_hidden()) { // All non-hidden classes are always included into the AOT cache @@ -149,10 +152,10 @@ 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(); - assert(k->is_hidden(), "must be"); if (log_is_enabled(Info, cds)) { ResourceMark rm; - log_info(cds)("Skipping %s: Hidden class", k->name()->as_C_string()); + log_info(cds)("Skipping %s: %s class", k->name()->as_C_string(), + k->is_hidden() ? "Hidden" : "AOT tooling"); } } }); diff --git a/src/hotspot/share/cds/aotClassFilter.cpp b/src/hotspot/share/cds/aotClassFilter.cpp new file mode 100644 index 0000000000000..4ba5cf570b508 --- /dev/null +++ b/src/hotspot/share/cds/aotClassFilter.cpp @@ -0,0 +1,27 @@ +/* + * 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. + * + */ + +#include "cds/aotClassFilter.hpp" + +AOTClassFilter::FilterMark* AOTClassFilter::_current_mark = nullptr; diff --git a/src/hotspot/share/cds/aotClassFilter.hpp b/src/hotspot/share/cds/aotClassFilter.hpp new file mode 100644 index 0000000000000..945ac62ccde8f --- /dev/null +++ b/src/hotspot/share/cds/aotClassFilter.hpp @@ -0,0 +1,68 @@ +/* + * 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. + * + */ + +#ifndef SHARE_CDS_AOTCLASSFILTER_HPP +#define SHARE_CDS_AOTCLASSFILTER_HPP + +#include "memory/allStatic.hpp" +#include "utilities/debug.hpp" + +class InstanceKlass; +class JavaThread; + +// Used by SystemDictionaryShared/AOTArtifactFinder to filter out classes that +// shouldn't be included into the AOT cache -- for example, classes that are used only +// in the training/assembly phases for building contents in the AOT cache. +// +// The only use case today is in lambdaFormInvokers.cpp. +class AOTClassFilter : AllStatic { +public: + + // Filters should be defined using RAII pattern + class FilterMark { + public: + FilterMark() { + assert(_current_mark == nullptr, "sanity"); + _current_mark = this; + } + ~FilterMark() { + assert(_current_mark == this, "sanity"); + _current_mark = nullptr; + } + + virtual bool is_aot_tooling_class(InstanceKlass* ik) = 0; + }; + + // Called when ik is being loaded. Return true iff this class is loaded + // only because it's used by the AOT tooling code. + static bool is_aot_tooling_class(InstanceKlass* ik) { + return (_current_mark == nullptr) ? false : _current_mark->is_aot_tooling_class(ik); + } + +private: + // For the time being, we allow at most one filter. + static FilterMark* _current_mark; +}; + +#endif // SHARE_CDS_AOTCLASSFILTER_HPP diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 055b84ed284d2..47613e090086c 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -573,6 +573,24 @@ bool CDSConfig::is_logging_lambda_form_invokers() { return ClassListWriter::is_enabled() || is_dumping_dynamic_archive(); } +bool CDSConfig::is_dumping_regenerated_lambdaform_invokers() { + if (is_dumping_final_static_archive()) { + // No need to regenerate -- the lambda form invokers should have been regenerated + // in the preimage archive (if allowed) + return false; + } else if (is_dumping_dynamic_archive() && is_using_aot_linked_classes()) { + // The base archive has aot-linked classes that may have AOT-resolved CP references + // that point to the lambda form invokers in the base archive. Such pointers will + // be invalid if lambda form invokers are regenerated in the dynamic archive. + return false; + } else if (CDSConfig::is_dumping_method_handles()) { + // Work around JDK-8310831, as some methods in lambda form holder classes may not get generated. + return false; + } else { + return is_dumping_archive(); + } +} + void CDSConfig::stop_using_optimized_module_handling() { _is_using_optimized_module_handling = false; _is_dumping_full_module_graph = false; // This requires is_using_optimized_module_handling() diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index 8a3a060e3c396..f02258eb6feaa 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -139,6 +139,7 @@ class CDSConfig : public AllStatic { static void stop_using_optimized_module_handling() NOT_CDS_RETURN; static bool is_logging_lambda_form_invokers() NOT_CDS_RETURN_(false); + static bool is_dumping_regenerated_lambdaform_invokers() NOT_CDS_RETURN_(false); static bool is_dumping_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_using_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false); diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp index 79ede224bb6e7..0bb60dfbb29bc 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp @@ -39,9 +39,11 @@ class Method; class Symbol; class DumpTimeClassInfo: public CHeapObj { - bool _excluded; - bool _is_early_klass; - bool _has_checked_exclusion; + bool _excluded; + bool _is_aot_tooling_class; + bool _is_early_klass; + bool _has_checked_exclusion; + class DTLoaderConstraint { Symbol* _name; char _loader_type1; @@ -140,6 +142,7 @@ class DumpTimeClassInfo: public CHeapObj { _clsfile_size = -1; _clsfile_crc32 = -1; _excluded = false; + _is_aot_tooling_class = false; _is_early_klass = JvmtiExport::is_early_phase(); _verifier_constraints = nullptr; _verifier_constraint_flags = nullptr; @@ -199,6 +202,14 @@ class DumpTimeClassInfo: public CHeapObj { return _excluded || _failed_verification; } + bool is_aot_tooling_class() { + return _is_aot_tooling_class; + } + + void set_is_aot_tooling_class() { + _is_aot_tooling_class = true; + } + // Was this class loaded while JvmtiExport::is_early_phase()==true bool is_early_klass() { return _is_early_klass; diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 9b1e99512c6a7..40dce7d3e77d6 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -32,6 +32,8 @@ #include "cds/cdsConfig.hpp" #include "cds/dynamicArchive.hpp" #include "cds/lambdaProxyClassDictionary.hpp" +#include "cds/lambdaFormInvokers.hpp" +#include "cds/metaspaceShared.hpp" #include "cds/regeneratedClasses.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" @@ -496,6 +498,16 @@ void DynamicArchive::check_for_dynamic_dump() { } } +void DynamicArchive::dump_impl(bool jcmd_request, const char* archive_name, TRAPS) { + MetaspaceShared::link_shared_classes(CHECK); + if (!jcmd_request && CDSConfig::is_dumping_regenerated_lambdaform_invokers()) { + LambdaFormInvokers::regenerate_holder_classes(CHECK); + } + + VM_PopulateDynamicDumpSharedSpace op(archive_name); + VMThread::execute(&op); +} + void DynamicArchive::dump_at_exit(JavaThread* current, const char* archive_name) { ExceptionMark em(current); ResourceMark rm(current); @@ -508,20 +520,16 @@ void DynamicArchive::dump_at_exit(JavaThread* current, const char* archive_name) log_info(cds, dynamic)("Preparing for dynamic dump at exit in thread %s", current->name()); JavaThread* THREAD = current; // For TRAPS processing related to link_shared_classes - MetaspaceShared::link_shared_classes(false/*not from jcmd*/, THREAD); - if (!HAS_PENDING_EXCEPTION) { - VM_PopulateDynamicDumpSharedSpace op(archive_name); - VMThread::execute(&op); - return; + dump_impl(/*jcmd_request=*/false, archive_name, THREAD); + if (HAS_PENDING_EXCEPTION) { + // One of the prepatory steps failed + oop ex = current->pending_exception(); + log_error(cds)("Dynamic dump has failed"); + log_error(cds)("%s: %s", ex->klass()->external_name(), + java_lang_String::as_utf8_string(java_lang_Throwable::message(ex))); + CLEAR_PENDING_EXCEPTION; + CDSConfig::disable_dumping_dynamic_archive(); // Just for good measure } - - // One of the prepatory steps failed - oop ex = current->pending_exception(); - log_error(cds)("Dynamic dump has failed"); - log_error(cds)("%s: %s", ex->klass()->external_name(), - java_lang_String::as_utf8_string(java_lang_Throwable::message(ex))); - CLEAR_PENDING_EXCEPTION; - CDSConfig::disable_dumping_dynamic_archive(); // Just for good measure } // This is called by "jcmd VM.cds dynamic_dump" @@ -530,10 +538,7 @@ void DynamicArchive::dump_for_jcmd(const char* archive_name, TRAPS) { assert(CDSConfig::is_using_archive() && RecordDynamicDumpInfo, "already checked in arguments.cpp"); assert(ArchiveClassesAtExit == nullptr, "already checked in arguments.cpp"); assert(CDSConfig::is_dumping_dynamic_archive(), "already checked by check_for_dynamic_dump() during VM startup"); - MetaspaceShared::link_shared_classes(true/*from jcmd*/, CHECK); - // copy shared path table to saved. - VM_PopulateDynamicDumpSharedSpace op(archive_name); - VMThread::execute(&op); + dump_impl(/*jcmd_request=*/true, archive_name, CHECK); } bool DynamicArchive::validate(FileMapInfo* dynamic_info) { diff --git a/src/hotspot/share/cds/dynamicArchive.hpp b/src/hotspot/share/cds/dynamicArchive.hpp index eb5fd5f9aba8d..905c511c4e0ac 100644 --- a/src/hotspot/share/cds/dynamicArchive.hpp +++ b/src/hotspot/share/cds/dynamicArchive.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -66,6 +66,7 @@ class DynamicArchive : AllStatic { static void check_for_dynamic_dump(); static void dump_for_jcmd(const char* archive_name, TRAPS); static void dump_at_exit(JavaThread* current, const char* archive_name); + static void dump_impl(bool jcmd_request, const char* archive_name, TRAPS); static bool is_mapped() { return FileMapInfo::dynamic_info() != nullptr; } static bool validate(FileMapInfo* dynamic_info); static void dump_array_klasses(); diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp index ab22b4775b7c0..7832de8c6cc53 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.cpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp @@ -22,8 +22,10 @@ * */ +#include "cds/aotClassFilter.hpp" #include "cds/archiveBuilder.hpp" -#include "cds/lambdaFormInvokers.hpp" +#include "cds/cdsConfig.hpp" +#include "cds/lambdaFormInvokers.inline.hpp" #include "cds/metaspaceShared.hpp" #include "cds/regeneratedClasses.hpp" #include "classfile/classFileStream.hpp" @@ -88,30 +90,39 @@ class PrintLambdaFormMessage { } }; -void LambdaFormInvokers::regenerate_holder_classes(TRAPS) { - PrintLambdaFormMessage plm; - if (_lambdaform_lines == nullptr || _lambdaform_lines->length() == 0) { - log_info(cds)("Nothing to regenerate for holder classes"); - return; +class LambdaFormInvokersClassFilterMark : public AOTClassFilter::FilterMark { +public: + bool is_aot_tooling_class(InstanceKlass* ik) { + if (ik->name()->index_of_at(0, "$Species_", 9) > 0) { + // Classes like java.lang.invoke.BoundMethodHandle$Species_L should be included in AOT cache + return false; + } + if (LambdaFormInvokers::may_be_regenerated_class(ik->name())) { + // Regenerated holder classes should be included in AOT cache. + return false; + } + // Treat all other classes loaded during LambdaFormInvokers::regenerate_holder_classes() as + // "AOT tooling classes". + return true; } +}; - if (CDSConfig::is_dumping_static_archive() && CDSConfig::is_dumping_method_handles()) { - // Work around JDK-8310831, as some methods in lambda form holder classes may not get generated. - log_info(cds)("Archived MethodHandles may refer to lambda form holder classes. Cannot regenerate."); +void LambdaFormInvokers::regenerate_holder_classes(TRAPS) { + if (!CDSConfig::is_dumping_regenerated_lambdaform_invokers()) { return; } - if (CDSConfig::is_dumping_dynamic_archive() && CDSConfig::is_dumping_aot_linked_classes() && - CDSConfig::is_using_aot_linked_classes()) { - // The base archive may have some pre-resolved CP entries that point to the lambda form holder - // classes in the base archive. If we generate new versions of these classes, those CP entries - // will be pointing to invalid classes. - log_info(cds)("Base archive already has aot-linked lambda form holder classes. Cannot regenerate."); + PrintLambdaFormMessage plm; + if (_lambdaform_lines == nullptr || _lambdaform_lines->length() == 0) { + log_info(cds)("Nothing to regenerate for holder classes"); return; } ResourceMark rm(THREAD); + // Filter out AOT tooling classes like java.lang.invoke.GenerateJLIClassesHelper, etc. + LambdaFormInvokersClassFilterMark filter_mark; + Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); Klass* cds_klass = SystemDictionary::resolve_or_null(cds_name, THREAD); guarantee(cds_klass != nullptr, "jdk/internal/misc/CDS must exist!"); @@ -245,7 +256,7 @@ void LambdaFormInvokers::dump_static_archive_invokers() { } assert(index == count, "Should match"); } - log_debug(cds)("Total LF lines stored into static archive: %d", count); + log_debug(cds)("Total LF lines stored into %s: %d", CDSConfig::type_of_archive_being_written(), count); } } @@ -257,14 +268,20 @@ void LambdaFormInvokers::read_static_archive_invokers() { char* str = line->adr_at(0); append(str); } - log_debug(cds)("Total LF lines read from static archive: %d", _static_archive_invokers->length()); + log_debug(cds)("Total LF lines read from %s: %d", CDSConfig::type_of_archive_being_loaded(), _static_archive_invokers->length()); } } void LambdaFormInvokers::serialize(SerializeClosure* soc) { soc->do_ptr(&_static_archive_invokers); if (soc->reading() && CDSConfig::is_dumping_final_static_archive()) { - LambdaFormInvokers::read_static_archive_invokers(); + if (!CDSConfig::is_dumping_aot_linked_classes()) { + // See CDSConfig::is_dumping_regenerated_lambdaform_invokers() -- a dynamic archive can + // regenerate lambda form invokers only if the base archive does not contain aot-linked classes. + // If so, we copy the contents of _static_archive_invokers (from the preimage) into + //_lambdaform_lines, which will be written as _static_archive_invokers into final static archive. + LambdaFormInvokers::read_static_archive_invokers(); + } _static_archive_invokers = nullptr; } } diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp index c0e31eea399ee..6b2e432d55961 100644 --- a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp +++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotClassFilter.hpp" #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/cdsProtectionDomain.hpp" @@ -390,6 +391,10 @@ void LambdaProxyClassDictionary::add_to_dumptime_table(LambdaProxyClassKey& key, InstanceKlass* proxy_klass) { assert_lock_strong(DumpTimeTable_lock); + if (AOTClassFilter::is_aot_tooling_class(proxy_klass)) { + return; + } + bool created; DumpTimeLambdaProxyClassInfo* info = _dumptime_table->put_if_absent(key, &created); info->add_proxy_klass(proxy_klass); diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 3677b3baf5f55..299ff82401665 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -97,6 +97,8 @@ #include "utilities/ostream.hpp" #include "utilities/resourceHash.hpp" +#include + ReservedSpace MetaspaceShared::_symbol_rs; VirtualSpace MetaspaceShared::_symbol_vs; bool MetaspaceShared::_archive_loading_failed = false; @@ -730,23 +732,10 @@ bool MetaspaceShared::may_be_eagerly_linked(InstanceKlass* ik) { return true; } -bool MetaspaceShared::link_class_for_cds(InstanceKlass* ik, TRAPS) { - // Link the class to cause the bytecodes to be rewritten and the - // cpcache to be created. Class verification is done according - // to -Xverify setting. - bool res = MetaspaceShared::try_link_class(THREAD, ik); - AOTConstantPoolResolver::dumptime_resolve_constants(ik, CHECK_(false)); - return res; -} - -void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) { +void MetaspaceShared::link_shared_classes(TRAPS) { AOTClassLinker::initialize(); AOTClassInitializer::init_test_class(CHECK); - if (!jcmd_request && !CDSConfig::is_dumping_final_static_archive()) { - LambdaFormInvokers::regenerate_holder_classes(CHECK); - } - // Collect all loaded ClassLoaderData. CollectCLDClosure collect_cld(THREAD); { @@ -766,7 +755,7 @@ void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) { if (klass->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(klass); if (may_be_eagerly_linked(ik)) { - has_linked |= link_class_for_cds(ik, CHECK); + has_linked |= try_link_class(THREAD, ik); } } } @@ -779,6 +768,20 @@ void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) { // Keep scanning until we have linked no more classes. } + // Resolve constant pool entries -- we don't load any new classes during this stage + for (int i = 0; i < collect_cld.nof_cld(); i++) { + ClassLoaderData* cld = collect_cld.cld_at(i); + for (Klass* klass = cld->klasses(); klass != nullptr; klass = klass->next_link()) { + if (klass->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(klass); + if (ik->is_linked()) { + AOTConstantPoolResolver::dumptime_resolve_constants(ik, CHECK); + } + } + } + } + + if (CDSConfig::is_dumping_final_static_archive()) { FinalImageRecipes::apply_recipes(CHECK); } @@ -819,8 +822,14 @@ void MetaspaceShared::preload_and_dump(TRAPS) { // When the new -XX:AOTMode=create flag is used, we can't return // to the JLI launcher, as the launcher will fail when trying to // run the main class, which is not what we want. - tty->print_cr("AOTCache creation is complete: %s", AOTCache); - vm_exit(0); + struct stat st; + if (os::stat(AOTCache, &st) != 0) { + tty->print_cr("AOTCache creation failed: %s", AOTCache); + vm_exit(0); + } else { + tty->print_cr("AOTCache creation is complete: %s " INT64_FORMAT " bytes", AOTCache, (int64_t)(st.st_size)); + vm_exit(0); + } } } } @@ -922,6 +931,18 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS } } +#if INCLUDE_CDS_JAVA_HEAP + if (CDSConfig::is_dumping_heap()) { + assert(CDSConfig::allow_only_single_java_thread(), "Required"); + if (!HeapShared::is_archived_boot_layer_available(THREAD)) { + log_info(cds)("archivedBootLayer not available, disabling full module graph"); + CDSConfig::stop_dumping_full_module_graph(); + } + // Do this before link_shared_classes(), as the following line may load new classes. + HeapShared::init_for_dumping(CHECK); + } +#endif + if (CDSConfig::is_dumping_final_static_archive()) { if (ExtraSharedClassListFile) { log_info(cds)("Loading extra classes from %s ...", ExtraSharedClassListFile); @@ -937,16 +958,15 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS // were not explicitly specified in the classlist. E.g., if an interface implemented by class K // fails verification, all other interfaces that were not specified in the classlist but // are implemented by K are not verified. - link_shared_classes(false/*not from jcmd*/, CHECK); + link_shared_classes(CHECK); log_info(cds)("Rewriting and linking classes: done"); + if (CDSConfig::is_dumping_regenerated_lambdaform_invokers()) { + LambdaFormInvokers::regenerate_holder_classes(CHECK); + } + #if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_heap()) { - if (!HeapShared::is_archived_boot_layer_available(THREAD)) { - log_info(cds)("archivedBootLayer not available, disabling full module graph"); - CDSConfig::stop_dumping_full_module_graph(); - } - HeapShared::init_for_dumping(CHECK); ArchiveHeapWriter::init(); if (CDSConfig::is_dumping_full_module_graph()) { ClassLoaderDataShared::ensure_module_entry_tables_exist(); diff --git a/src/hotspot/share/cds/metaspaceShared.hpp b/src/hotspot/share/cds/metaspaceShared.hpp index 6d5f273041a83..8edbc8663aefd 100644 --- a/src/hotspot/share/cds/metaspaceShared.hpp +++ b/src/hotspot/share/cds/metaspaceShared.hpp @@ -131,8 +131,7 @@ class MetaspaceShared : AllStatic { } static bool try_link_class(JavaThread* current, InstanceKlass* ik); - static void link_shared_classes(bool jcmd_request, TRAPS) NOT_CDS_RETURN; - static bool link_class_for_cds(InstanceKlass* ik, TRAPS) NOT_CDS_RETURN_(false); + static void link_shared_classes(TRAPS) NOT_CDS_RETURN; static bool may_be_eagerly_linked(InstanceKlass* ik) NOT_CDS_RETURN_(false); #if INCLUDE_CDS diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index c4e061b8fa993..d86a47d96c2f2 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotClassFilter.hpp" #include "cds/aotClassLocation.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" @@ -34,6 +35,7 @@ #include "cds/filemap.hpp" #include "cds/heapShared.hpp" #include "cds/lambdaProxyClassDictionary.hpp" +#include "cds/lambdaFormInvokers.inline.hpp" #include "cds/metaspaceShared.hpp" #include "cds/runTimeClassInfo.hpp" #include "cds/unregisteredClasses.hpp" @@ -266,14 +268,9 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) { } if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) { if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) { - // This class is dynamically generated by the JDK - if (CDSConfig::is_dumping_method_handles()) { - k->set_shared_classpath_index(0); - } else { - ResourceMark rm; - log_info(cds)("Skipping %s because it is dynamically generated", k->name()->as_C_string()); - return true; // exclude without warning - } + // This class is dynamically generated by the JDK but has predictable contents. + // Keep it in the archive so it won't be generated again in production run. + k->set_shared_classpath_index(0); } else { // These are classes loaded from unsupported locations (such as those loaded by JVMTI native // agent during dump time). @@ -484,7 +481,10 @@ void SystemDictionaryShared::initialize() { void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) { MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); assert(SystemDictionaryShared::class_loading_may_happen(), "sanity"); - _dumptime_table->allocate_info(k); + DumpTimeClassInfo* info = _dumptime_table->allocate_info(k); + if (AOTClassFilter::is_aot_tooling_class(k)) { + info->set_is_aot_tooling_class(); + } } void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) { @@ -1056,10 +1056,7 @@ SystemDictionaryShared::find_record(RunTimeSharedDictionary* static_dict, RunTim if (DynamicArchive::is_mapped()) { // Use the regenerated holder classes in the dynamic archive as they // have more methods than those in the base archive. - if (name == vmSymbols::java_lang_invoke_Invokers_Holder() || - name == vmSymbols::java_lang_invoke_DirectMethodHandle_Holder() || - name == vmSymbols::java_lang_invoke_LambdaForm_Holder() || - name == vmSymbols::java_lang_invoke_DelegatingMethodHandle_Holder()) { + if (LambdaFormInvokers::may_be_regenerated_class(name)) { record = dynamic_dict->lookup(name, hash, 0); if (record != nullptr) { return record; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 671eb69b8c65a..e910bfb5d472e 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -244,7 +244,7 @@ class SystemDictionaryShared: public SystemDictionary { static bool check_linking_constraints(Thread* current, InstanceKlass* klass) NOT_CDS_RETURN_(false); static void record_linking_constraint(Symbol* name, InstanceKlass* klass, Handle loader1, Handle loader2) NOT_CDS_RETURN; - static bool is_builtin(InstanceKlass* k) { + static bool is_builtin(const InstanceKlass* k) { return (k->shared_classpath_index() != UNREGISTERED_INDEX); } static bool add_unregistered_class(Thread* current, InstanceKlass* k); From 13c8ae86e1633aecc565e577fd587b0b4b315dc1 Mon Sep 17 00:00:00 2001 From: iklam Date: Wed, 26 Mar 2025 23:33:42 -0700 Subject: [PATCH 2/7] Reverted some fixes in systemDictionaryShared.cpp that causes test failures --- .../share/classfile/systemDictionaryShared.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index d86a47d96c2f2..2fbee502b9372 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -268,9 +268,14 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) { } if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) { if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) { - // This class is dynamically generated by the JDK but has predictable contents. - // Keep it in the archive so it won't be generated again in production run. - k->set_shared_classpath_index(0); + // This class is dynamically generated by the JDK + if (CDSConfig::is_dumping_method_handles()) { + k->set_shared_classpath_index(0); + } else { + ResourceMark rm; + log_info(cds)("Skipping %s because it is dynamically generated", k->name()->as_C_string()); + return true; // exclude without warning + } } else { // These are classes loaded from unsupported locations (such as those loaded by JVMTI native // agent during dump time). From c994a2efcbfe14102f4bcbd93cbe6640b80615be Mon Sep 17 00:00:00 2001 From: iklam Date: Tue, 1 Apr 2025 09:30:55 -0700 Subject: [PATCH 3/7] Refactored CollectClassesForLinking for simplification --- src/hotspot/share/cds/metaspaceShared.cpp | 26 +++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 96526578b3ec8..85916ced3cfac 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -698,6 +698,16 @@ class CollectClassesForLinking : public KlassClosure { GrowableArray _mirrors; public: + CollectClassesForLinking() : _mirrors() { + // ClassLoaderDataGraph::loaded_classes_do_keepalive() requires ClassLoaderDataGraph_lock. + // We cannot link the classes while holding this lock (or else we may run into deadlock). + // Therefore, we need to first collect all the classes, keeping them alive by + // holding onto their java_mirrors in global OopHandles. We then link the classes after + // releasing the lock. + MutexLocker lock(ClassLoaderDataGraph_lock); + ClassLoaderDataGraph::loaded_classes_do_keepalive(this); + } + ~CollectClassesForLinking() { for (int i = 0; i < _mirrors.length(); i++) { _mirrors.at(i).release(Universe::vm_global()); @@ -743,17 +753,8 @@ void MetaspaceShared::link_shared_classes(TRAPS) { AOTClassInitializer::init_test_class(CHECK); while (true) { + ResourceMark rm(THREAD); CollectClassesForLinking collect_classes; - { - // ClassLoaderDataGraph::loaded_classes_do_keepalive() requires ClassLoaderDataGraph_lock. - // We cannot link the classes while holding this lock (or else we may run into deadlock). - // Therefore, we need to first collect all the classes, keeping them alive by - // holding onto their java_mirrors in global OopHandles. We then link the classes after - // releasing the lock. - MutexLocker lock(ClassLoaderDataGraph_lock); - ClassLoaderDataGraph::loaded_classes_do_keepalive(&collect_classes); - } - bool has_linked = false; const GrowableArray* mirrors = collect_classes.mirrors(); for (int i = 0; i < mirrors->length(); i++) { @@ -773,11 +774,8 @@ void MetaspaceShared::link_shared_classes(TRAPS) { // Resolve constant pool entries -- we don't load any new classes during this stage { + ResourceMark rm(THREAD); CollectClassesForLinking collect_classes; - { - MutexLocker lock(ClassLoaderDataGraph_lock); - ClassLoaderDataGraph::loaded_classes_do_keepalive(&collect_classes); - } const GrowableArray* mirrors = collect_classes.mirrors(); for (int i = 0; i < mirrors->length(); i++) { OopHandle mirror = mirrors->at(i); From d01b25335a94caf1787b23c847847d8bfdcdedc1 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 2 Apr 2025 19:21:28 -0700 Subject: [PATCH 4/7] Clean up CDS input/output path handling --- src/hotspot/share/cds/cdsConfig.cpp | 286 ++++++++++++------ src/hotspot/share/cds/cdsConfig.hpp | 44 +-- src/hotspot/share/cds/dynamicArchive.cpp | 28 +- src/hotspot/share/cds/dynamicArchive.hpp | 3 +- src/hotspot/share/cds/filemap.cpp | 19 +- src/hotspot/share/cds/filemap.hpp | 8 +- src/hotspot/share/cds/metaspaceShared.cpp | 26 +- src/hotspot/share/cds/metaspaceShared.hpp | 1 - src/hotspot/share/memory/universe.cpp | 3 +- src/hotspot/share/runtime/arguments.cpp | 2 +- src/hotspot/share/runtime/java.cpp | 3 +- .../share/native/libsaproc/ps_core_common.c | 6 +- .../SharedArchiveFileOption.java | 4 +- .../TestAutoCreateSharedArchive.java | 2 +- ...toCreateSharedArchiveNoDefaultArchive.java | 6 +- 15 files changed, 252 insertions(+), 189 deletions(-) diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 47613e090086c..507e1beb80224 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -51,9 +51,10 @@ bool CDSConfig::_old_cds_flags_used = false; bool CDSConfig::_new_aot_flags_used = false; bool CDSConfig::_disable_heap_dumping = false; -char* CDSConfig::_default_archive_path = nullptr; -char* CDSConfig::_static_archive_path = nullptr; -char* CDSConfig::_dynamic_archive_path = nullptr; +const char* CDSConfig::_default_archive_path = nullptr; +const char* CDSConfig::_input_static_archive_path = nullptr; +const char* CDSConfig::_input_dynamic_archive_path = nullptr; +const char* CDSConfig::_output_archive_path = nullptr; JavaThread* CDSConfig::_dumper_thread = nullptr; @@ -66,7 +67,11 @@ int CDSConfig::get_status() { (is_using_archive() ? IS_USING_ARCHIVE : 0); } -void CDSConfig::initialize() { +DEBUG_ONLY(static bool _cds_ergo_initialize_started = false); + +void CDSConfig::ergo_initialize() { + DEBUG_ONLY(_cds_ergo_initialize_started = true); + if (is_dumping_static_archive() && !is_dumping_final_static_archive()) { // Note: -Xshare and -XX:AOTMode flags are mutually exclusive. // - Classic workflow: -Xshare:on and -Xshare:dump cannot take effect at the same time. @@ -83,10 +88,13 @@ void CDSConfig::initialize() { // Initialize shared archive paths which could include both base and dynamic archive paths // This must be after set_ergonomics_flags() called so flag UseCompressedOops is set properly. - // - // UseSharedSpaces may be disabled if -XX:SharedArchiveFile is invalid. if (is_dumping_static_archive() || is_using_archive()) { - init_shared_archive_paths(); + ergo_check_dynamic_dump_options(); + if (new_aot_flags_used()) { + init_aot_paths(); + } else { + init_classic_archive_paths(); + } } if (!is_dumping_heap()) { @@ -94,7 +102,10 @@ void CDSConfig::initialize() { } } -char* CDSConfig::default_archive_path() { +const char* CDSConfig::default_archive_path() { + // The path depends on UseCompressedOops, etc, which are set by GC ergonomics just + // before CDSConfig::ergo_initialize() is called. + assert(_cds_ergo_initialize_started, "sanity"); if (_default_archive_path == nullptr) { stringStream tmp; const char* subdir = WINDOWS_ONLY("bin") NOT_WINDOWS("lib"); @@ -116,12 +127,12 @@ char* CDSConfig::default_archive_path() { return _default_archive_path; } -int CDSConfig::num_archives(const char* archive_path) { - if (archive_path == nullptr) { +int CDSConfig::num_archive_paths(const char* path_spec) { + if (path_spec == nullptr) { return 0; } int npaths = 1; - char* p = (char*)archive_path; + char* p = (char*)path_spec; while (*p != '\0') { if (*p == os::path_separator()[0]) { npaths++; @@ -131,9 +142,9 @@ int CDSConfig::num_archives(const char* archive_path) { return npaths; } -void CDSConfig::extract_shared_archive_paths(const char* archive_path, - char** base_archive_path, - char** top_archive_path) { +void CDSConfig::extract_archive_paths(const char* archive_path, + const char** base_archive_path, + const char** top_archive_path) { char* begin_ptr = (char*)archive_path; char* end_ptr = strchr((char*)archive_path, os::path_separator()[0]); if (end_ptr == nullptr || end_ptr == begin_ptr) { @@ -157,12 +168,10 @@ void CDSConfig::extract_shared_archive_paths(const char* archive_path, *top_archive_path = cur_path; } -void CDSConfig::init_shared_archive_paths() { +void CDSConfig::ergo_check_dynamic_dump_options() { + assert(_cds_ergo_initialize_started, "sanity"); if (ArchiveClassesAtExit != nullptr) { assert(!RecordDynamicDumpInfo, "already checked"); - if (is_dumping_static_archive()) { - vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump"); - } check_unsupported_dumping_module_options(); if (os::same_files(default_archive_path(), ArchiveClassesAtExit)) { @@ -170,91 +179,92 @@ void CDSConfig::init_shared_archive_paths() { "Cannot specify the default CDS archive for -XX:ArchiveClassesAtExit", default_archive_path()); } } +} +void CDSConfig::init_classic_archive_paths() { if (SharedArchiveFile == nullptr) { - _static_archive_path = default_archive_path(); + _input_static_archive_path = default_archive_path(); + if (is_dumping_static_archive()) { + _output_archive_path = _input_static_archive_path; + } } else { - int archives = num_archives(SharedArchiveFile); - assert(archives > 0, "must be"); + int num_archives = num_archive_paths(SharedArchiveFile); + assert(num_archives > 0, "must be"); - if (is_dumping_archive() && archives > 1) { + if (is_dumping_archive() && num_archives > 1) { vm_exit_during_initialization( - "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping"); + "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping"); } - if (is_dumping_static_archive()) { - assert(archives == 1, "must be"); - // Static dump is simple: only one archive is allowed in SharedArchiveFile. This file - // will be overwritten no matter regardless of its contents - _static_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); + if (is_dumping_static_archive()) { + assert(num_archives == 1, "just checked above"); + _output_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); } else { - // SharedArchiveFile may specify one or two files. In case (c), the path for base.jsa - // is read from top.jsa - // (a) 1 file: -XX:SharedArchiveFile=base.jsa - // (b) 2 files: -XX:SharedArchiveFile=base.jsa:top.jsa - // (c) 2 files: -XX:SharedArchiveFile=top.jsa - // - // However, if either RecordDynamicDumpInfo or ArchiveClassesAtExit is used, we do not - // allow cases (b) and (c). Case (b) is already checked above. + init_complex_archive_paths(num_archives); + } + } +} - if (archives > 2) { - vm_exit_during_initialization( - "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option"); - } - if (archives == 1) { - char* base_archive_path = nullptr; - bool success = - FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path); - if (!success) { - // If +AutoCreateSharedArchive and the specified shared archive does not exist, - // regenerate the dynamic archive base on default archive. - if (AutoCreateSharedArchive && !os::file_exists(SharedArchiveFile)) { - enable_dumping_dynamic_archive(); - ArchiveClassesAtExit = const_cast(SharedArchiveFile); - _static_archive_path = default_archive_path(); - SharedArchiveFile = nullptr; - } else { - if (AutoCreateSharedArchive) { - warning("-XX:+AutoCreateSharedArchive is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info."); - AutoCreateSharedArchive = false; - } - log_error(cds)("Not a valid %s (%s)", new_aot_flags_used() ? "AOT cache" : "archive", SharedArchiveFile); - Arguments::no_shared_spaces("invalid archive"); - } - } else if (base_archive_path == nullptr) { - // User has specified a single archive, which is a static archive. - _static_archive_path = const_cast(SharedArchiveFile); - } else { - // User has specified a single archive, which is a dynamic archive. - _dynamic_archive_path = const_cast(SharedArchiveFile); - _static_archive_path = base_archive_path; // has been c-heap allocated. - } +void CDSConfig::init_complex_archive_paths(int num_archives) { + // SharedArchiveFile may specify one or two files. In case (c), the path for base.jsa + // is read from top.jsa + // (a) 1 file: -XX:SharedArchiveFile=base.jsa + // (b) 2 files: -XX:SharedArchiveFile=base.jsa:top.jsa + // (c) 2 files: -XX:SharedArchiveFile=top.jsa + // + // However, if either RecordDynamicDumpInfo or ArchiveClassesAtExit is used, we do not + // allow cases (b) and (c). Case (b) is already checked above. + + if (num_archives > 2) { + vm_exit_during_initialization( + "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option"); + } + + if (num_archives == 1) { + const char* base_archive_path = nullptr; + bool success = + FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path); + if (!success) { + // If +AutoCreateSharedArchive and the specified shared archive does not exist, + // regenerate the dynamic archive base on default archive. + if (AutoCreateSharedArchive && !os::file_exists(SharedArchiveFile)) { + enable_dumping_dynamic_archive(SharedArchiveFile); + ArchiveClassesAtExit = os::strdup_check_oom(SharedArchiveFile, mtArguments); + _input_static_archive_path = default_archive_path(); } else { - extract_shared_archive_paths((const char*)SharedArchiveFile, - &_static_archive_path, &_dynamic_archive_path); - if (_static_archive_path == nullptr) { - assert(_dynamic_archive_path == nullptr, "must be"); - Arguments::no_shared_spaces("invalid archive"); - } - } - - if (_dynamic_archive_path != nullptr) { - // Check for case (c) - if (RecordDynamicDumpInfo) { - vm_exit_during_initialization("-XX:+RecordDynamicDumpInfo is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", - SharedArchiveFile); - } - if (ArchiveClassesAtExit != nullptr) { - vm_exit_during_initialization("-XX:ArchiveClassesAtExit is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", - SharedArchiveFile); + if (AutoCreateSharedArchive) { + warning("-XX:+AutoCreateSharedArchive is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info."); + AutoCreateSharedArchive = false; } + log_error(cds)("Not a valid archive (%s)", SharedArchiveFile); + Arguments::no_shared_spaces("invalid archive"); } + } else if (base_archive_path == nullptr) { + // User has specified a single archive, which is a static archive. + _input_static_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); + } else { + // User has specified a single archive, which is a dynamic archive. + _input_dynamic_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); + _input_static_archive_path = base_archive_path; // has been c-heap allocated. + } + } else { + extract_archive_paths((const char*)SharedArchiveFile, + &_input_static_archive_path, &_input_dynamic_archive_path); + if (_input_static_archive_path == nullptr) { + assert(_input_dynamic_archive_path == nullptr, "must be"); + Arguments::no_shared_spaces("invalid archive"); + } + } - if (ArchiveClassesAtExit != nullptr && os::same_files(SharedArchiveFile, ArchiveClassesAtExit)) { - vm_exit_during_initialization( - "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit", - SharedArchiveFile); - } + if (_input_dynamic_archive_path != nullptr) { + // Check for case (c) + if (RecordDynamicDumpInfo) { + vm_exit_during_initialization("-XX:+RecordDynamicDumpInfo is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", + SharedArchiveFile); + } + if (ArchiveClassesAtExit != nullptr) { + vm_exit_during_initialization("-XX:ArchiveClassesAtExit is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", + SharedArchiveFile); } } } @@ -364,6 +374,14 @@ void CDSConfig::check_flag_alias(bool alias_is_default, const char* alias_name) } } +#define CHECK_SINGLE_PATH(f) check_flag_single_path(#f, f) + +void CDSConfig::check_flag_single_path(const char* flag_name, const char* value) { + if (value != nullptr && num_archive_paths(value) != 1) { + vm_exit_during_initialization(err_msg("Option %s must specify a single file name", flag_name)); + } +} + void CDSConfig::check_aot_flags() { if (!FLAG_IS_DEFAULT(DumpLoadedClassList) || !FLAG_IS_DEFAULT(SharedClassListFile) || @@ -375,6 +393,9 @@ void CDSConfig::check_aot_flags() { CHECK_ALIAS(AOTConfiguration); CHECK_ALIAS(AOTMode); + CHECK_SINGLE_PATH(AOTCache); + CHECK_SINGLE_PATH(AOTConfiguration); + if (FLAG_IS_DEFAULT(AOTCache) && FLAG_IS_DEFAULT(AOTConfiguration) && FLAG_IS_DEFAULT(AOTMode)) { // AOTCache/AOTConfiguration/AOTMode not used. return; @@ -411,10 +432,10 @@ void CDSConfig::check_aotmode_auto_or_on() { vm_exit_during_initialization("AOTConfiguration can only be used with -XX:AOTMode=record or -XX:AOTMode=create"); } - if (!FLAG_IS_DEFAULT(AOTCache)) { - assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); - FLAG_SET_ERGO(SharedArchiveFile, AOTCache); - } +// if (!FLAG_IS_DEFAULT(AOTCache)) { +// assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); +// FLAG_SET_ERGO(SharedArchiveFile, AOTCache); +// } UseSharedSpaces = true; if (FLAG_IS_DEFAULT(AOTMode) || (strcmp(AOTMode, "auto") == 0)) { @@ -431,8 +452,8 @@ void CDSConfig::check_aotmode_record() { } assert(FLAG_IS_DEFAULT(DumpLoadedClassList), "already checked"); - assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); - FLAG_SET_ERGO(SharedArchiveFile, AOTConfiguration); +// assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); +// FLAG_SET_ERGO(SharedArchiveFile, AOTConfiguration); FLAG_SET_ERGO(DumpLoadedClassList, nullptr); UseSharedSpaces = false; RequireSharedSpaces = false; @@ -449,10 +470,10 @@ void CDSConfig::check_aotmode_create() { vm_exit_during_initialization("AOTCache must be specified when using -XX:AOTMode=create"); } - assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); +// assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); _is_dumping_final_static_archive = true; - FLAG_SET_ERGO(SharedArchiveFile, AOTConfiguration); +// FLAG_SET_ERGO(SharedArchiveFile, AOTConfiguration); UseSharedSpaces = true; RequireSharedSpaces = true; @@ -463,7 +484,28 @@ void CDSConfig::check_aotmode_create() { CDSConfig::enable_dumping_static_archive(); } +void CDSConfig::init_aot_paths() { + if (is_dumping_static_archive()) { + if (is_dumping_preimage_static_archive()) { + _output_archive_path = AOTConfiguration; + } else { + assert(is_dumping_final_static_archive(), "must be"); + _input_static_archive_path = AOTConfiguration; + _output_archive_path = AOTCache; + } + } else if (is_using_archive()) { + if (FLAG_IS_DEFAULT(AOTCache)) { + // Only -XX:AOTMode={auto,on} is specified + _input_static_archive_path = default_archive_path(); + } else { + _input_static_archive_path = AOTCache; + } + } +} + bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line) { + assert(!_cds_ergo_initialize_started, "This is called earlier than CDSConfig::ergo_initialize()"); + check_aot_flags(); if (!FLAG_IS_DEFAULT(AOTMode)) { @@ -502,6 +544,16 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla // Don't use SoftReferences so that objects used by java.lang.invoke tables can be archived. Arguments::PropertyList_add(new SystemProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "false", false)); + + if (ArchiveClassesAtExit != nullptr) { + vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump"); + } + } + + if (ArchiveClassesAtExit != nullptr && os::same_files(SharedArchiveFile, ArchiveClassesAtExit)) { + vm_exit_during_initialization( + "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit", + SharedArchiveFile); } // RecordDynamicDumpInfo is not compatible with ArchiveClassesAtExit @@ -514,7 +566,7 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla if (ArchiveClassesAtExit == nullptr && !RecordDynamicDumpInfo) { disable_dumping_dynamic_archive(); } else { - enable_dumping_dynamic_archive(); + enable_dumping_dynamic_archive(ArchiveClassesAtExit); } if (AutoCreateSharedArchive) { @@ -546,6 +598,34 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla return true; } +void CDSConfig::prepare_for_dumping() { + assert(CDSConfig::is_dumping_archive(), "sanity"); + + if (is_dumping_dynamic_archive() && !is_using_archive()) { + assert(!is_dumping_static_archive(), "cannot be dumping both static and dynamic archives"); + + // This could happen if SharedArchiveFile has failed to load: + // - -Xshare:off was specified + // - SharedArchiveFile points to an non-existent file. + // - SharedArchiveFile points to an archive that has failed CRC check + // - SharedArchiveFile is not specified and the VM doesn't have a compatible default archive + +#define __THEMSG " is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info." + if (RecordDynamicDumpInfo) { + log_error(cds)("-XX:+RecordDynamicDumpInfo%s", __THEMSG); + MetaspaceShared::unrecoverable_loading_error(); + } else { + assert(ArchiveClassesAtExit != nullptr, "sanity"); + log_warning(cds)("-XX:ArchiveClassesAtExit" __THEMSG); + } +#undef __THEMSG + disable_dumping_dynamic_archive(); + return; + } + + check_unsupported_dumping_module_options(); +} + bool CDSConfig::is_dumping_classic_static_archive() { return _is_dumping_static_archive && !is_dumping_preimage_static_archive() && @@ -560,6 +640,14 @@ bool CDSConfig::is_dumping_final_static_archive() { return _is_dumping_final_static_archive; } +void CDSConfig::enable_dumping_dynamic_archive(const char* output_path) { + _is_dumping_dynamic_archive = true; + if (output_path != nullptr) { + // Comment -- why can this be null? + _output_archive_path = os::strdup_check_oom(output_path, mtArguments); + } +} + bool CDSConfig::allow_only_single_java_thread() { // See comments in JVM_StartThread() return is_dumping_classic_static_archive() || is_dumping_final_static_archive(); diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index f02258eb6feaa..64ac6500125cb 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -42,9 +42,10 @@ class CDSConfig : public AllStatic { static bool _is_using_full_module_graph; static bool _has_aot_linked_classes; - static char* _default_archive_path; - static char* _static_archive_path; - static char* _dynamic_archive_path; + const static char* _default_archive_path; + const static char* _input_static_archive_path; + const static char* _input_dynamic_archive_path; + const static char* _output_archive_path; static bool _old_cds_flags_used; static bool _new_aot_flags_used; @@ -53,17 +54,26 @@ class CDSConfig : public AllStatic { static JavaThread* _dumper_thread; #endif - static void extract_shared_archive_paths(const char* archive_path, - char** base_archive_path, - char** top_archive_path); - static void init_shared_archive_paths(); + static void extract_archive_paths(const char* archive_path, + const char** base_archive_path, + const char** top_archive_path); + static void init_classic_archive_paths(); + static void init_complex_archive_paths(int num_archives); + static int num_archive_paths(const char* path_spec); + static void check_flag_single_path(const char* flag_name, const char* value); + static void init_aot_paths(); + // Checks before Arguments::apply_ergo() static void check_flag_alias(bool alias_is_default, const char* alias_name); static void check_aot_flags(); static void check_aotmode_off(); static void check_aotmode_auto_or_on(); static void check_aotmode_record(); static void check_aotmode_create(); + static void check_unsupported_dumping_module_options(); + + // Checks after Arguments::apply_ergo() has started + static void ergo_check_dynamic_dump_options(); public: // Used by jdk.internal.misc.CDS.getCDSConfigStatus(); @@ -76,24 +86,25 @@ class CDSConfig : public AllStatic { static int get_status() NOT_CDS_RETURN_(0); // Initialization and command-line checking - static void initialize() NOT_CDS_RETURN; + static void ergo_initialize() NOT_CDS_RETURN; static void set_old_cds_flags_used() { CDS_ONLY(_old_cds_flags_used = true); } static bool old_cds_flags_used() { return CDS_ONLY(_old_cds_flags_used) NOT_CDS(false); } static bool new_aot_flags_used() { return CDS_ONLY(_new_aot_flags_used) NOT_CDS(false); } static void check_internal_module_property(const char* key, const char* value) NOT_CDS_RETURN; static void check_incompatible_property(const char* key, const char* value) NOT_CDS_RETURN; - static void check_unsupported_dumping_module_options() NOT_CDS_RETURN; static bool has_unsupported_runtime_module_options() NOT_CDS_RETURN_(false); static bool check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line) NOT_CDS_RETURN_(true); static const char* type_of_archive_being_loaded(); static const char* type_of_archive_being_written(); + static void prepare_for_dumping(); // --- Basic CDS features // archive(s) in general static bool is_dumping_archive() { return is_dumping_static_archive() || is_dumping_dynamic_archive(); } + + // input archive(s) static bool is_using_archive() NOT_CDS_RETURN_(false); - static int num_archives(const char* archive_path) NOT_CDS_RETURN_(0); // static_archive static bool is_dumping_static_archive() { return CDS_ONLY(_is_dumping_static_archive) NOT_CDS(false); } @@ -125,7 +136,7 @@ class CDSConfig : public AllStatic { // dynamic_archive static bool is_dumping_dynamic_archive() { return CDS_ONLY(_is_dumping_dynamic_archive) NOT_CDS(false); } - static void enable_dumping_dynamic_archive() { CDS_ONLY(_is_dumping_dynamic_archive = true); } + static void enable_dumping_dynamic_archive(const char* output_path) NOT_CDS_RETURN; static void disable_dumping_dynamic_archive() { CDS_ONLY(_is_dumping_dynamic_archive = false); } // Misc CDS features @@ -147,12 +158,11 @@ class CDSConfig : public AllStatic { // archive_path - // Points to the classes.jsa in $JAVA_HOME - static char* default_archive_path() NOT_CDS_RETURN_(nullptr); - // The actual static archive (if any) selected at runtime - static const char* static_archive_path() { return CDS_ONLY(_static_archive_path) NOT_CDS(nullptr); } - // The actual dynamic archive (if any) selected at runtime - static const char* dynamic_archive_path() { return CDS_ONLY(_dynamic_archive_path) NOT_CDS(nullptr); } + // Points to the classes.jsa in $JAVA_HOME (could be input or output) + static const char* default_archive_path() NOT_CDS_RETURN_(nullptr); + static const char* input_static_archive_path() { return CDS_ONLY(_input_static_archive_path) NOT_CDS(nullptr); } + static const char* input_dynamic_archive_path() { return CDS_ONLY(_input_dynamic_archive_path) NOT_CDS(nullptr); } + static const char* output_archive_path() { return CDS_ONLY(_output_archive_path) NOT_CDS(nullptr); } // --- Archived java objects diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index c3a6db9e9b973..bf4257253e853 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -97,8 +97,6 @@ class DynamicArchiveBuilder : public ArchiveBuilder { void gather_array_klasses(); public: - DynamicArchiveBuilder() : ArchiveBuilder() { } - // Do this before and after the archive dump to see if any corruption // is caused by dynamic dumping. void verify_universe(const char* info) { @@ -348,7 +346,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio FileMapInfo* dynamic_info = FileMapInfo::dynamic_info(); assert(dynamic_info != nullptr, "Sanity"); - dynamic_info->open_for_write(); + dynamic_info->open_as_output(); ArchiveHeapInfo no_heap_for_dynamic_dump; ArchiveBuilder::write_archive(dynamic_info, &no_heap_for_dynamic_dump); @@ -476,27 +474,6 @@ int DynamicArchive::num_array_klasses() { return _array_klasses != nullptr ? _array_klasses->length() : 0; } -void DynamicArchive::check_for_dynamic_dump() { - if (CDSConfig::is_dumping_dynamic_archive() && !CDSConfig::is_using_archive()) { - // This could happen if SharedArchiveFile has failed to load: - // - -Xshare:off was specified - // - SharedArchiveFile points to an non-existent file. - // - SharedArchiveFile points to an archive that has failed CRC check - // - SharedArchiveFile is not specified and the VM doesn't have a compatible default archive - -#define __THEMSG " is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info." - if (RecordDynamicDumpInfo) { - log_error(cds)("-XX:+RecordDynamicDumpInfo%s", __THEMSG); - MetaspaceShared::unrecoverable_loading_error(); - } else { - assert(ArchiveClassesAtExit != nullptr, "sanity"); - log_warning(cds)("-XX:ArchiveClassesAtExit" __THEMSG); - } -#undef __THEMSG - CDSConfig::disable_dumping_dynamic_archive(); - } -} - void DynamicArchive::dump_impl(bool jcmd_request, const char* archive_name, TRAPS) { MetaspaceShared::link_shared_classes(CHECK); if (!jcmd_request && CDSConfig::is_dumping_regenerated_lambdaform_invokers()) { @@ -507,11 +484,12 @@ void DynamicArchive::dump_impl(bool jcmd_request, const char* archive_name, TRAP VMThread::execute(&op); } -void DynamicArchive::dump_at_exit(JavaThread* current, const char* archive_name) { +void DynamicArchive::dump_at_exit(JavaThread* current) { ExceptionMark em(current); ResourceMark rm(current); CDSConfig::DumperThreadMark dumper_thread_mark(current); + const char* archive_name = CDSConfig::output_archive_path(); if (!CDSConfig::is_dumping_dynamic_archive() || archive_name == nullptr) { return; } diff --git a/src/hotspot/share/cds/dynamicArchive.hpp b/src/hotspot/share/cds/dynamicArchive.hpp index 905c511c4e0ac..8c23750734c35 100644 --- a/src/hotspot/share/cds/dynamicArchive.hpp +++ b/src/hotspot/share/cds/dynamicArchive.hpp @@ -63,9 +63,8 @@ class DynamicArchive : AllStatic { static GrowableArray* _array_klasses; static Array* _dynamic_archive_array_klasses; public: - static void check_for_dynamic_dump(); static void dump_for_jcmd(const char* archive_name, TRAPS); - static void dump_at_exit(JavaThread* current, const char* archive_name); + static void dump_at_exit(JavaThread* current); static void dump_impl(bool jcmd_request, const char* archive_name, TRAPS); static bool is_mapped() { return FileMapInfo::dynamic_info() != nullptr; } static bool validate(FileMapInfo* dynamic_info); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index ebcd33f8bd521..580699b60b583 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -173,7 +173,7 @@ void FileMapInfo::populate_header(size_t core_region_alignment) { header_size = c_header_size; const char* default_base_archive_name = CDSConfig::default_archive_path(); - const char* current_base_archive_name = CDSConfig::static_archive_path(); + const char* current_base_archive_name = CDSConfig::input_static_archive_path(); if (!os::same_files(current_base_archive_name, default_base_archive_name)) { base_archive_name_size = strlen(current_base_archive_name) + 1; header_size += base_archive_name_size; @@ -209,7 +209,7 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, if (!info->is_static() && base_archive_name_size != 0) { // copy base archive name - copy_base_archive_name(CDSConfig::static_archive_path()); + copy_base_archive_name(CDSConfig::input_static_archive_path()); } _core_region_alignment = core_region_alignment; _obj_alignment = ObjectAlignmentInBytes; @@ -563,7 +563,7 @@ class FileHeaderHelper { // true && (*base_archive_name) != nullptr: // is a valid dynamic archive. bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name, - char** base_archive_name) { + const char** base_archive_name) { FileHeaderHelper file_helper(archive_name, false); *base_archive_name = nullptr; @@ -619,7 +619,7 @@ bool FileMapInfo::init_from_file(int fd) { // Good } else { if (CDSConfig::new_aot_flags_used()) { - log_warning(cds)("Not a valid %s %s", file_type, _full_path); + log_warning(cds)("Not a valid %s (%s)", file_type, _full_path); } else { log_warning(cds)("Not a base shared archive: %s", _full_path); } @@ -729,7 +729,7 @@ bool FileMapInfo::open_for_read() { // Write the FileMapInfo information to the file. -void FileMapInfo::open_for_write() { +void FileMapInfo::open_as_output() { LogMessage(cds) msg; if (msg.is_info()) { if (CDSConfig::is_dumping_preimage_static_archive()) { @@ -1759,7 +1759,7 @@ bool FileMapInfo::_memory_mapping_failed = false; // [1] validate_header() - done here. // [2] validate_shared_path_table - this is done later, because the table is in the RO // region of the archive, which is not mapped yet. -bool FileMapInfo::initialize() { +bool FileMapInfo::open_as_input() { assert(CDSConfig::is_using_archive(), "UseSharedSpaces expected."); assert(Arguments::has_jimage(), "The shared archive file cannot be used with an exploded module build."); @@ -1774,13 +1774,12 @@ bool FileMapInfo::initialize() { if (!open_for_read() || !init_from_file(_fd) || !validate_header()) { if (_is_static) { - log_info(cds)("Initialize static archive failed."); + log_info(cds)("Loading static archive failed."); return false; } else { - log_info(cds)("Initialize dynamic archive failed."); + log_info(cds)("Loading dynamic archive failed."); if (AutoCreateSharedArchive) { - CDSConfig::enable_dumping_dynamic_archive(); - ArchiveClassesAtExit = CDSConfig::dynamic_archive_path(); + CDSConfig::enable_dumping_dynamic_archive(_full_path); } return false; } diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index 50af87a5da768..8793e110948b4 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -268,7 +268,7 @@ class FileMapInfo : public CHeapObj { public: FileMapHeader *header() const { return _header; } static bool get_base_archive_name_from_header(const char* archive_name, - char** base_archive_name); + const char** base_archive_name); static bool is_preimage_static_archive(const char* file); bool init_from_file(int fd); @@ -346,9 +346,8 @@ class FileMapInfo : public CHeapObj { static void assert_mark(bool check); // File manipulation. - bool initialize() NOT_CDS_RETURN_(false); - bool open_for_read(); - void open_for_write(); + bool open_as_input() NOT_CDS_RETURN_(false); + void open_as_output(); void write_header(); void write_region(int region, char* base, size_t size, bool read_only, bool allow_exec); @@ -425,6 +424,7 @@ class FileMapInfo : public CHeapObj { } private: + bool open_for_read(); void seek_to_position(size_t pos); bool map_heap_region_impl() NOT_CDS_JAVA_HEAP_RETURN_(false); void dealloc_heap_region() NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 85916ced3cfac..ef2a6dcb8e63c 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -678,14 +678,11 @@ void VM_PopulateDumpSharedSpace::doit() { CppVtables::zero_archived_vtables(); // Write the archive file - const char* static_archive; if (CDSConfig::is_dumping_final_static_archive()) { - static_archive = AOTCache; - FileMapInfo::free_current_info(); - } else { - static_archive = CDSConfig::static_archive_path(); + FileMapInfo::free_current_info(); // FIXME: should not free current info } - assert(static_archive != nullptr, "SharedArchiveFile not set?"); + const char* static_archive = CDSConfig::output_archive_path(); + assert(static_archive != nullptr, "sanity"); _map_info = new FileMapInfo(static_archive, true); _map_info->populate_header(MetaspaceShared::core_region_alignment()); _map_info->set_early_serialized_data(early_serialized_data); @@ -789,11 +786,6 @@ void MetaspaceShared::link_shared_classes(TRAPS) { } } -void MetaspaceShared::prepare_for_dumping() { - assert(CDSConfig::is_dumping_archive(), "sanity"); - CDSConfig::check_unsupported_dumping_module_options(); -} - // Preload classes from a list, populate the shared spaces and dump to a // file. void MetaspaceShared::preload_and_dump(TRAPS) { @@ -1023,7 +1015,7 @@ bool MetaspaceShared::write_static_archive(ArchiveBuilder* builder, FileMapInfo* // without runtime relocation. builder->relocate_to_requested(); - map_info->open_for_write(); + map_info->open_as_output(); if (!map_info->is_open()) { return false; } @@ -1214,10 +1206,10 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() { } FileMapInfo* MetaspaceShared::open_static_archive() { - const char* static_archive = CDSConfig::static_archive_path(); + const char* static_archive = CDSConfig::input_static_archive_path(); assert(static_archive != nullptr, "sanity"); FileMapInfo* mapinfo = new FileMapInfo(static_archive, true); - if (!mapinfo->initialize()) { + if (!mapinfo->open_as_input()) { delete(mapinfo); return nullptr; } @@ -1228,13 +1220,13 @@ FileMapInfo* MetaspaceShared::open_dynamic_archive() { if (CDSConfig::is_dumping_dynamic_archive()) { return nullptr; } - const char* dynamic_archive = CDSConfig::dynamic_archive_path(); + const char* dynamic_archive = CDSConfig::input_dynamic_archive_path(); if (dynamic_archive == nullptr) { return nullptr; } FileMapInfo* mapinfo = new FileMapInfo(dynamic_archive, false); - if (!mapinfo->initialize()) { + if (!mapinfo->open_as_input()) { delete(mapinfo); if (RequireSharedSpaces) { MetaspaceShared::unrecoverable_loading_error("Failed to initialize dynamic archive"); @@ -1817,7 +1809,7 @@ void MetaspaceShared::initialize_shared_spaces() { if (PrintSharedArchiveAndExit) { // Print archive names if (dynamic_mapinfo != nullptr) { - tty->print_cr("\n\nBase archive name: %s", CDSConfig::static_archive_path()); + tty->print_cr("\n\nBase archive name: %s", CDSConfig::input_static_archive_path()); tty->print_cr("Base archive version %d", static_mapinfo->version()); } else { tty->print_cr("Static archive name: %s", static_mapinfo->full_path()); diff --git a/src/hotspot/share/cds/metaspaceShared.hpp b/src/hotspot/share/cds/metaspaceShared.hpp index 8881dc6d6fd0a..e03994be1b993 100644 --- a/src/hotspot/share/cds/metaspaceShared.hpp +++ b/src/hotspot/share/cds/metaspaceShared.hpp @@ -71,7 +71,6 @@ class MetaspaceShared : AllStatic { n_regions = 4 // total number of regions }; - static void prepare_for_dumping() NOT_CDS_RETURN; static void preload_and_dump(TRAPS) NOT_CDS_RETURN; #ifdef _LP64 static void adjust_heap_sizes_for_dumping() NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index fe9c960b7f237..6f415ab93a46f 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -897,14 +897,13 @@ jint universe_init() { ClassLoaderData::init_null_class_loader_data(); #if INCLUDE_CDS - DynamicArchive::check_for_dynamic_dump(); if (CDSConfig::is_using_archive()) { // Read the data structures supporting the shared spaces (shared // system dictionary, symbol table, etc.) MetaspaceShared::initialize_shared_spaces(); } if (CDSConfig::is_dumping_archive()) { - MetaspaceShared::prepare_for_dumping(); + CDSConfig::prepare_for_dumping(); } #endif diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 8de6f427c3f1d..f097742ab7da2 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -3708,7 +3708,7 @@ jint Arguments::apply_ergo() { CompressedKlassPointers::pre_initialize(); } - CDSConfig::initialize(); + CDSConfig::ergo_initialize(); // Initialize Metaspace flags and alignments Metaspace::ergo_initialize(); diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index bcf99c84ba465..b3ad15ae3ff68 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -426,11 +426,10 @@ void before_exit(JavaThread* thread, bool halt) { #if INCLUDE_CDS // Dynamic CDS dumping must happen whilst we can still reliably // run Java code. - DynamicArchive::dump_at_exit(thread, ArchiveClassesAtExit); + DynamicArchive::dump_at_exit(thread); assert(!thread->has_pending_exception(), "must be"); #endif - // Actual shutdown logic begins here. #if INCLUDE_JVMCI diff --git a/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c b/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c index bcce23f12216a..3c244aab0f379 100644 --- a/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c +++ b/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -265,7 +265,7 @@ bool read_string(struct ps_prochandle* ph, uintptr_t addr, char* buf, size_t siz #ifdef LINUX // mangled name of CDSConfig::_static_archive_path -#define SHARED_ARCHIVE_PATH_SYM "_ZN9CDSConfig20_static_archive_pathE" +#define SHARED_ARCHIVE_PATH_SYM "_ZN9CDSConfig26_input_static_archive_pathE" #define USE_SHARED_SPACES_SYM "UseSharedSpaces" #define SHARED_BASE_ADDRESS_SYM "SharedBaseAddress" #define LIBJVM_NAME "/libjvm.so" @@ -273,7 +273,7 @@ bool read_string(struct ps_prochandle* ph, uintptr_t addr, char* buf, size_t siz #ifdef __APPLE__ // mangled name of CDSConfig::_static_archive_path -#define SHARED_ARCHIVE_PATH_SYM "__ZN9CDSConfig20_static_archive_pathE" +#define SHARED_ARCHIVE_PATH_SYM "__ZN9CDSConfig26_input_static_archive_pathE" #define USE_SHARED_SPACES_SYM "_UseSharedSpaces" #define SHARED_BASE_ADDRESS_SYM "_SharedBaseAddress" #define LIBJVM_NAME "/libjvm.dylib" diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java index 489efdfed067b..a0fb638039b1d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -163,7 +163,7 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws testcase("A dynamic archive is already loaded when -XX:SharedArchiveFile is specified"); dump2(baseArchiveName /*this is overridden by -XX:SharedArchiveFile= below*/, - topArchiveName, + topArchiveName + "dummy", "-XX:SharedArchiveFile=" + topArchiveName, "-cp", appJar, mainClass) .assertAbnormalExit("-XX:ArchiveClassesAtExit is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile:"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchive.java index c8a04027a3dd9..3bc1e395b17dd 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchive.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchive.java @@ -337,7 +337,7 @@ private static void testAutoCreateSharedArchive() throws Exception { .shouldContain(HELLO_WORLD) .shouldContain("The shared archive file version " + hex(version2) + " does not match the required version " + hex(currentCDSVersion)) .shouldContain("The shared archive file has the wrong version") - .shouldContain("Initialize dynamic archive failed") + .shouldContain("Loading dynamic archive failed") .shouldContain("Dumping shared data to file"); }); ft2 = Files.getLastModifiedTime(Paths.get(modVersion)); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java index 133f44521d53a..4806f571dc6d5 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -112,7 +112,7 @@ public static void main(String[] args) throws Exception { "-version"); TestCommon.executeAndLog(pb, "show-version") .shouldHaveExitValue(0) - .shouldContain("Initialize static archive failed") + .shouldContain("Loading static archive failed") .shouldContain("Unable to map shared spaces") .shouldNotContain("sharing"); } @@ -132,7 +132,7 @@ public static void main(String[] args) throws Exception { mainClass); TestCommon.executeAndLog(pb, "no-default-archive") .shouldHaveExitValue(0) - .shouldContain("Initialize static archive failed") + .shouldContain("Loading static archive failed") .shouldContain("Unable to map shared spaces") .shouldNotContain("Dumping shared data to file"); if (jsaFile.exists()) { From 9e17fedb4ce2ca712a1194eed148c318394876b6 Mon Sep 17 00:00:00 2001 From: iklam Date: Wed, 2 Apr 2025 20:23:15 -0700 Subject: [PATCH 5/7] Minimized changes in ergo_init_classic_archive_paths() --- src/hotspot/share/cds/cdsConfig.cpp | 183 ++++++++---------- src/hotspot/share/cds/cdsConfig.hpp | 10 +- .../jtreg/runtime/cds/appcds/AOTFlags.java | 57 +++++- .../SharedArchiveFileOption.java | 4 +- 4 files changed, 146 insertions(+), 108 deletions(-) diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 507e1beb80224..1d4e45af789fb 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -89,11 +89,10 @@ void CDSConfig::ergo_initialize() { // Initialize shared archive paths which could include both base and dynamic archive paths // This must be after set_ergonomics_flags() called so flag UseCompressedOops is set properly. if (is_dumping_static_archive() || is_using_archive()) { - ergo_check_dynamic_dump_options(); if (new_aot_flags_used()) { - init_aot_paths(); + ergo_init_aot_paths(); } else { - init_classic_archive_paths(); + ergo_init_classic_archive_paths(); } } @@ -168,10 +167,13 @@ void CDSConfig::extract_archive_paths(const char* archive_path, *top_archive_path = cur_path; } -void CDSConfig::ergo_check_dynamic_dump_options() { +void CDSConfig::ergo_init_classic_archive_paths() { assert(_cds_ergo_initialize_started, "sanity"); if (ArchiveClassesAtExit != nullptr) { assert(!RecordDynamicDumpInfo, "already checked"); + if (is_dumping_static_archive()) { + vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump"); + } check_unsupported_dumping_module_options(); if (os::same_files(default_archive_path(), ArchiveClassesAtExit)) { @@ -179,9 +181,7 @@ void CDSConfig::ergo_check_dynamic_dump_options() { "Cannot specify the default CDS archive for -XX:ArchiveClassesAtExit", default_archive_path()); } } -} -void CDSConfig::init_classic_archive_paths() { if (SharedArchiveFile == nullptr) { _input_static_archive_path = default_archive_path(); if (is_dumping_static_archive()) { @@ -193,78 +193,83 @@ void CDSConfig::init_classic_archive_paths() { if (is_dumping_archive() && num_archives > 1) { vm_exit_during_initialization( - "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping"); + "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping"); } - if (is_dumping_static_archive()) { + if (is_dumping_static_archive()) { assert(num_archives == 1, "just checked above"); - _output_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); + // Static dump is simple: only one archive is allowed in SharedArchiveFile. This file + // will be overwritten regardless of its contents + _output_archive_path = SharedArchiveFile; } else { - init_complex_archive_paths(num_archives); - } - } -} + // SharedArchiveFile may specify one or two files. In case (c), the path for base.jsa + // is read from top.jsa + // (a) 1 file: -XX:SharedArchiveFile=base.jsa + // (b) 2 files: -XX:SharedArchiveFile=base.jsa:top.jsa + // (c) 2 files: -XX:SharedArchiveFile=top.jsa + // + // However, if either RecordDynamicDumpInfo or ArchiveClassesAtExit is used, we do not + // allow cases (b) and (c). Case (b) is already checked above. -void CDSConfig::init_complex_archive_paths(int num_archives) { - // SharedArchiveFile may specify one or two files. In case (c), the path for base.jsa - // is read from top.jsa - // (a) 1 file: -XX:SharedArchiveFile=base.jsa - // (b) 2 files: -XX:SharedArchiveFile=base.jsa:top.jsa - // (c) 2 files: -XX:SharedArchiveFile=top.jsa - // - // However, if either RecordDynamicDumpInfo or ArchiveClassesAtExit is used, we do not - // allow cases (b) and (c). Case (b) is already checked above. - - if (num_archives > 2) { - vm_exit_during_initialization( - "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option"); - } - - if (num_archives == 1) { - const char* base_archive_path = nullptr; - bool success = - FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path); - if (!success) { - // If +AutoCreateSharedArchive and the specified shared archive does not exist, - // regenerate the dynamic archive base on default archive. - if (AutoCreateSharedArchive && !os::file_exists(SharedArchiveFile)) { - enable_dumping_dynamic_archive(SharedArchiveFile); - ArchiveClassesAtExit = os::strdup_check_oom(SharedArchiveFile, mtArguments); - _input_static_archive_path = default_archive_path(); + if (num_archives > 2) { + vm_exit_during_initialization( + "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option"); + } + + if (num_archives == 1) { + const char* base_archive_path = nullptr; + bool success = + FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path); + if (!success) { + // If +AutoCreateSharedArchive and the specified shared archive does not exist, + // regenerate the dynamic archive base on default archive. + if (AutoCreateSharedArchive && !os::file_exists(SharedArchiveFile)) { + enable_dumping_dynamic_archive(SharedArchiveFile); + ArchiveClassesAtExit = SharedArchiveFile; + _input_static_archive_path = default_archive_path(); + SharedArchiveFile = nullptr; + } else { + if (AutoCreateSharedArchive) { + warning("-XX:+AutoCreateSharedArchive is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info."); + AutoCreateSharedArchive = false; + } + log_error(cds)("Not a valid archive (%s)", SharedArchiveFile); + Arguments::no_shared_spaces("invalid archive"); + } + } else if (base_archive_path == nullptr) { + // User has specified a single archive, which is a static archive. + _input_static_archive_path = SharedArchiveFile; + } else { + // User has specified a single archive, which is a dynamic archive. + _input_dynamic_archive_path = SharedArchiveFile; + _input_static_archive_path = base_archive_path; // has been c-heap allocated. + } } else { - if (AutoCreateSharedArchive) { - warning("-XX:+AutoCreateSharedArchive is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info."); - AutoCreateSharedArchive = false; + extract_archive_paths(SharedArchiveFile, + &_input_static_archive_path, &_input_dynamic_archive_path); + if (_input_static_archive_path == nullptr) { + assert(_input_dynamic_archive_path == nullptr, "must be"); + Arguments::no_shared_spaces("invalid archive"); } - log_error(cds)("Not a valid archive (%s)", SharedArchiveFile); - Arguments::no_shared_spaces("invalid archive"); } - } else if (base_archive_path == nullptr) { - // User has specified a single archive, which is a static archive. - _input_static_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); - } else { - // User has specified a single archive, which is a dynamic archive. - _input_dynamic_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); - _input_static_archive_path = base_archive_path; // has been c-heap allocated. - } - } else { - extract_archive_paths((const char*)SharedArchiveFile, - &_input_static_archive_path, &_input_dynamic_archive_path); - if (_input_static_archive_path == nullptr) { - assert(_input_dynamic_archive_path == nullptr, "must be"); - Arguments::no_shared_spaces("invalid archive"); - } - } - if (_input_dynamic_archive_path != nullptr) { - // Check for case (c) - if (RecordDynamicDumpInfo) { - vm_exit_during_initialization("-XX:+RecordDynamicDumpInfo is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", - SharedArchiveFile); - } - if (ArchiveClassesAtExit != nullptr) { - vm_exit_during_initialization("-XX:ArchiveClassesAtExit is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", - SharedArchiveFile); + if (_input_dynamic_archive_path != nullptr) { + // Check for case (c) + if (RecordDynamicDumpInfo) { + vm_exit_during_initialization("-XX:+RecordDynamicDumpInfo is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", + SharedArchiveFile); + } + if (ArchiveClassesAtExit != nullptr) { + vm_exit_during_initialization("-XX:ArchiveClassesAtExit is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile", + SharedArchiveFile); + } + } + + if (ArchiveClassesAtExit != nullptr && os::same_files(SharedArchiveFile, ArchiveClassesAtExit)) { + vm_exit_during_initialization( + "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit", + SharedArchiveFile); + } } } } @@ -363,14 +368,14 @@ bool CDSConfig::has_unsupported_runtime_module_options() { return false; } -#define CHECK_ALIAS(f) check_flag_alias(FLAG_IS_DEFAULT(f), #f) +#define CHECK_NEW_FLAG(f) check_new_flag(FLAG_IS_DEFAULT(f), #f) -void CDSConfig::check_flag_alias(bool alias_is_default, const char* alias_name) { - if (old_cds_flags_used() && !alias_is_default) { +void CDSConfig::check_new_flag(bool new_flag_is_default, const char* new_flag_name) { + if (old_cds_flags_used() && !new_flag_is_default) { vm_exit_during_initialization(err_msg("Option %s cannot be used at the same time with " "-Xshare:on, -Xshare:auto, -Xshare:off, -Xshare:dump, " "DumpLoadedClassList, SharedClassListFile, or SharedArchiveFile", - alias_name)); + new_flag_name)); } } @@ -389,9 +394,10 @@ void CDSConfig::check_aot_flags() { _old_cds_flags_used = true; } - CHECK_ALIAS(AOTCache); - CHECK_ALIAS(AOTConfiguration); - CHECK_ALIAS(AOTMode); + // "New" AOT flags must not be mixed with "classic" flags such as -Xshare:dump + CHECK_NEW_FLAG(AOTCache); + CHECK_NEW_FLAG(AOTConfiguration); + CHECK_NEW_FLAG(AOTMode); CHECK_SINGLE_PATH(AOTCache); CHECK_SINGLE_PATH(AOTConfiguration); @@ -432,11 +438,6 @@ void CDSConfig::check_aotmode_auto_or_on() { vm_exit_during_initialization("AOTConfiguration can only be used with -XX:AOTMode=record or -XX:AOTMode=create"); } -// if (!FLAG_IS_DEFAULT(AOTCache)) { -// assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); -// FLAG_SET_ERGO(SharedArchiveFile, AOTCache); -// } - UseSharedSpaces = true; if (FLAG_IS_DEFAULT(AOTMode) || (strcmp(AOTMode, "auto") == 0)) { RequireSharedSpaces = false; @@ -451,10 +452,6 @@ void CDSConfig::check_aotmode_record() { vm_exit_during_initialization("AOTCache must not be specified when using -XX:AOTMode=record"); } - assert(FLAG_IS_DEFAULT(DumpLoadedClassList), "already checked"); -// assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); -// FLAG_SET_ERGO(SharedArchiveFile, AOTConfiguration); - FLAG_SET_ERGO(DumpLoadedClassList, nullptr); UseSharedSpaces = false; RequireSharedSpaces = false; _is_dumping_static_archive = true; @@ -470,10 +467,7 @@ void CDSConfig::check_aotmode_create() { vm_exit_during_initialization("AOTCache must be specified when using -XX:AOTMode=create"); } -// assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked"); - _is_dumping_final_static_archive = true; -// FLAG_SET_ERGO(SharedArchiveFile, AOTConfiguration); UseSharedSpaces = true; RequireSharedSpaces = true; @@ -484,7 +478,8 @@ void CDSConfig::check_aotmode_create() { CDSConfig::enable_dumping_static_archive(); } -void CDSConfig::init_aot_paths() { +void CDSConfig::ergo_init_aot_paths() { + assert(_cds_ergo_initialize_started, "sanity"); if (is_dumping_static_archive()) { if (is_dumping_preimage_static_archive()) { _output_archive_path = AOTConfiguration; @@ -544,16 +539,6 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla // Don't use SoftReferences so that objects used by java.lang.invoke tables can be archived. Arguments::PropertyList_add(new SystemProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "false", false)); - - if (ArchiveClassesAtExit != nullptr) { - vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump"); - } - } - - if (ArchiveClassesAtExit != nullptr && os::same_files(SharedArchiveFile, ArchiveClassesAtExit)) { - vm_exit_during_initialization( - "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit", - SharedArchiveFile); } // RecordDynamicDumpInfo is not compatible with ArchiveClassesAtExit diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index 64ac6500125cb..e96291f653487 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -57,14 +57,11 @@ class CDSConfig : public AllStatic { static void extract_archive_paths(const char* archive_path, const char** base_archive_path, const char** top_archive_path); - static void init_classic_archive_paths(); - static void init_complex_archive_paths(int num_archives); static int num_archive_paths(const char* path_spec); static void check_flag_single_path(const char* flag_name, const char* value); - static void init_aot_paths(); // Checks before Arguments::apply_ergo() - static void check_flag_alias(bool alias_is_default, const char* alias_name); + static void check_new_flag(bool new_flag_is_default, const char* new_flag_name); static void check_aot_flags(); static void check_aotmode_off(); static void check_aotmode_auto_or_on(); @@ -72,8 +69,9 @@ class CDSConfig : public AllStatic { static void check_aotmode_create(); static void check_unsupported_dumping_module_options(); - // Checks after Arguments::apply_ergo() has started - static void ergo_check_dynamic_dump_options(); + // Called after Arguments::apply_ergo() has started + static void ergo_init_classic_archive_paths(); + static void ergo_init_aot_paths(); public: // Used by jdk.internal.misc.CDS.getCDSConfigStatus(); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java index aeb0476346dde..09458f49b4ab3 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java @@ -25,13 +25,14 @@ /* * @test * @summary "AOT" aliases for traditional CDS command-line options - * @requires vm.cds + * @requires vm.cds & vm.compMode != "Xcomp" * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build Hello * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello * @run driver AOTFlags */ +import java.io.File; import jdk.test.lib.cds.CDSTestUtils; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; @@ -298,6 +299,60 @@ static void negativeTests() throws Exception { "specified when the AOTConfiguration file was recorded"); out.shouldContain("Unable to use create AOT cache"); out.shouldHaveExitValue(1); + + //---------------------------------------------------------------------- + printTestCase("Cannot use multiple paths in AOTConfiguration"); + + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTMode=record", + "-XX:AOTConfiguration=" + aotConfigFile + File.pathSeparator + "dummy", + "-cp", "noSuchJar.jar"); + + out = CDSTestUtils.executeAndLog(pb, "neg"); + out.shouldContain("Option AOTConfiguration must specify a single file name"); + out.shouldHaveExitValue(1); + + //---------------------------------------------------------------------- + printTestCase("Cannot use multiple paths in AOTCache"); + + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTMode=create", + "-XX:AOTConfiguration=" + aotConfigFile, + "-XX:AOTCache=" + aotCacheFile + File.pathSeparator + "dummy", + "-cp", "noSuchJar.jar"); + + out = CDSTestUtils.executeAndLog(pb, "neg"); + out.shouldContain("Option AOTCache must specify a single file name"); + out.shouldHaveExitValue(1); + + //---------------------------------------------------------------------- + printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache"); + String staticArchive = "static.jsa"; + String dynamicArchive = "dynamic.jsa"; + + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xshare:dump", + "-XX:SharedArchiveFile=" + staticArchive); + out = CDSTestUtils.executeAndLog(pb, "static"); + out.shouldHaveExitValue(0); + + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:SharedArchiveFile=" + staticArchive, + "-XX:ArchiveClassesAtExit=" + dynamicArchive, + "--version"); + out = CDSTestUtils.executeAndLog(pb, "dynamic"); + out.shouldHaveExitValue(0); + + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xlog:cds", + "-XX:AOTMode=on", + "-XX:AOTCache=" + dynamicArchive, + "--version"); + + out = CDSTestUtils.executeAndLog(pb, "neg"); + out.shouldContain("Unable to use AOT cache."); + out.shouldContain("Not a valid AOT cache (dynamic.jsa)"); + out.shouldHaveExitValue(1); } static int testNum = 0; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java index a0fb638039b1d..489efdfed067b 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/SharedArchiveFileOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, 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 @@ -163,7 +163,7 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws testcase("A dynamic archive is already loaded when -XX:SharedArchiveFile is specified"); dump2(baseArchiveName /*this is overridden by -XX:SharedArchiveFile= below*/, - topArchiveName + "dummy", + topArchiveName, "-XX:SharedArchiveFile=" + topArchiveName, "-cp", appJar, mainClass) .assertAbnormalExit("-XX:ArchiveClassesAtExit is unsupported when a dynamic CDS archive is specified in -XX:SharedArchiveFile:"); From 3ad42a3e8f260793e45db74d9b427e5096aca8eb Mon Sep 17 00:00:00 2001 From: iklam Date: Thu, 3 Apr 2025 08:42:44 -0700 Subject: [PATCH 6/7] more clean up --- src/hotspot/share/cds/cdsConfig.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 1d4e45af789fb..64ad07b0cf816 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -225,9 +225,9 @@ void CDSConfig::ergo_init_classic_archive_paths() { // regenerate the dynamic archive base on default archive. if (AutoCreateSharedArchive && !os::file_exists(SharedArchiveFile)) { enable_dumping_dynamic_archive(SharedArchiveFile); - ArchiveClassesAtExit = SharedArchiveFile; + FLAG_SET_ERGO(ArchiveClassesAtExit, SharedArchiveFile); _input_static_archive_path = default_archive_path(); - SharedArchiveFile = nullptr; + FLAG_SET_ERGO(SharedArchiveFile, nullptr); } else { if (AutoCreateSharedArchive) { warning("-XX:+AutoCreateSharedArchive is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info."); @@ -627,8 +627,12 @@ bool CDSConfig::is_dumping_final_static_archive() { void CDSConfig::enable_dumping_dynamic_archive(const char* output_path) { _is_dumping_dynamic_archive = true; - if (output_path != nullptr) { - // Comment -- why can this be null? + if (output_path == nullptr) { + // output_path can be null when the VM is started with -XX:+RecordDynamicDumpInfo + // in anticipation of "jcmd VM.cds dynamic_dump", which will provide the actual + // output path. + _output_archive_path = nullptr; + } else { _output_archive_path = os::strdup_check_oom(output_path, mtArguments); } } From 90b4b688777ae8a4b791b889f7087847047f9ae8 Mon Sep 17 00:00:00 2001 From: iklam Date: Thu, 3 Apr 2025 13:27:30 -0700 Subject: [PATCH 7/7] @lmesnik comments --- test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java index 09458f49b4ab3..46f76fab36859 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java @@ -25,7 +25,8 @@ /* * @test * @summary "AOT" aliases for traditional CDS command-line options - * @requires vm.cds & vm.compMode != "Xcomp" + * @requires vm.cds + * @requires vm.flagless * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build Hello * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello