mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8348426: Generate binary file for -XX:AOTMode=record -XX:AOTConfiguration=file
Reviewed-by: ccheung, asmehra, kvn, iveresov
This commit is contained in:
parent
267d69bed6
commit
86024ebdb0
@ -989,8 +989,14 @@ bool AOTClassLocationConfig::validate(bool has_aot_linked_classes, bool* has_ext
|
||||
const char* hint_msg = log_is_enabled(Info, class, path) ?
|
||||
"" : " (hint: enable -Xlog:class+path=info to diagnose the failure)";
|
||||
if (RequireSharedSpaces && !PrintSharedArchiveAndExit) {
|
||||
log_error(cds)("%s%s", mismatch_msg, hint_msg);
|
||||
MetaspaceShared::unrecoverable_loading_error();
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
log_error(cds)("class path and/or module path are not compatible with the "
|
||||
"ones specified when the AOTConfiguration file was recorded%s", hint_msg);
|
||||
vm_exit_during_initialization("Unable to use create AOT cache.", nullptr);
|
||||
} else {
|
||||
log_error(cds)("%s%s", mismatch_msg, hint_msg);
|
||||
MetaspaceShared::unrecoverable_loading_error();
|
||||
}
|
||||
} else {
|
||||
log_warning(cds)("%s%s", mismatch_msg, hint_msg);
|
||||
}
|
||||
|
||||
@ -507,9 +507,8 @@ bool ArchiveBuilder::is_excluded(Klass* klass) {
|
||||
return SystemDictionaryShared::is_excluded_class(ik);
|
||||
} else if (klass->is_objArray_klass()) {
|
||||
Klass* bottom = ObjArrayKlass::cast(klass)->bottom_klass();
|
||||
if (MetaspaceShared::is_shared_static(bottom)) {
|
||||
if (CDSConfig::is_dumping_dynamic_archive() && MetaspaceShared::is_shared_static(bottom)) {
|
||||
// The bottom class is in the static archive so it's clearly not excluded.
|
||||
assert(CDSConfig::is_dumping_dynamic_archive(), "sanity");
|
||||
return false;
|
||||
} else if (bottom->is_instance_klass()) {
|
||||
return SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(bottom));
|
||||
@ -521,7 +520,7 @@ bool ArchiveBuilder::is_excluded(Klass* klass) {
|
||||
|
||||
ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref *ref) {
|
||||
address obj = ref->obj();
|
||||
if (MetaspaceShared::is_in_shared_metaspace(obj)) {
|
||||
if (CDSConfig::is_dumping_dynamic_archive() && MetaspaceShared::is_in_shared_metaspace(obj)) {
|
||||
// Don't dump existing shared metadata again.
|
||||
return point_to_it;
|
||||
} else if (ref->msotype() == MetaspaceObj::MethodDataType ||
|
||||
|
||||
@ -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
|
||||
@ -255,11 +255,23 @@ public:
|
||||
};
|
||||
|
||||
class ArchiveUtils {
|
||||
template <typename T> static Array<T>* archive_non_ptr_array(GrowableArray<T>* tmp_array);
|
||||
template <typename T> static Array<T>* archive_ptr_array(GrowableArray<T>* tmp_array);
|
||||
|
||||
public:
|
||||
static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF;
|
||||
static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN;
|
||||
static bool has_aot_initialized_mirror(InstanceKlass* src_ik);
|
||||
template <typename T> static Array<T>* archive_array(GrowableArray<T>* tmp_array);
|
||||
|
||||
template <typename T, ENABLE_IF(!std::is_pointer<T>::value)>
|
||||
static Array<T>* archive_array(GrowableArray<T>* tmp_array) {
|
||||
return archive_non_ptr_array(tmp_array);
|
||||
}
|
||||
|
||||
template <typename T, ENABLE_IF(std::is_pointer<T>::value)>
|
||||
static Array<T>* archive_array(GrowableArray<T>* tmp_array) {
|
||||
return archive_ptr_array(tmp_array);
|
||||
}
|
||||
|
||||
// The following functions translate between a u4 offset and an address in the
|
||||
// the range of the mapped CDS archive (e.g., Metaspace::is_in_shared_metaspace()).
|
||||
|
||||
@ -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
|
||||
@ -28,6 +28,8 @@
|
||||
#include "cds/archiveUtils.hpp"
|
||||
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/metaspaceShared.hpp"
|
||||
#include "oops/array.hpp"
|
||||
#include "utilities/bitMap.inline.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
@ -52,13 +54,40 @@ inline bool SharedDataRelocator::do_bit(size_t offset) {
|
||||
|
||||
// Returns the address of an Array<T> that's allocated in the ArchiveBuilder "buffer" space.
|
||||
template <typename T>
|
||||
Array<T>* ArchiveUtils::archive_array(GrowableArray<T>* tmp_array) {
|
||||
Array<T>* ArchiveUtils::archive_non_ptr_array(GrowableArray<T>* tmp_array) {
|
||||
ArchiveBuilder* builder = ArchiveBuilder::current();
|
||||
|
||||
Array<T>* archived_array = ArchiveBuilder::new_ro_array<T>(tmp_array->length());
|
||||
for (int i = 0; i < tmp_array->length(); i++) {
|
||||
archived_array->at_put(i, tmp_array->at(i));
|
||||
if (std::is_pointer<T>::value) {
|
||||
}
|
||||
|
||||
return archived_array;
|
||||
}
|
||||
|
||||
// Returns the address of an Array<T> that's allocated in the ArchiveBuilder "buffer" space.
|
||||
// All pointers in tmp_array must point to:
|
||||
// - a buffered object; or
|
||||
// - a source object that has been archived; or
|
||||
// - (only when dumping dynamic archive) an object in the static archive.
|
||||
template <typename T>
|
||||
Array<T>* ArchiveUtils::archive_ptr_array(GrowableArray<T>* tmp_array) {
|
||||
ArchiveBuilder* builder = ArchiveBuilder::current();
|
||||
const bool is_dynamic_dump = CDSConfig::is_dumping_dynamic_archive();
|
||||
|
||||
Array<T>* archived_array = ArchiveBuilder::new_ro_array<T>(tmp_array->length());
|
||||
for (int i = 0; i < tmp_array->length(); i++) {
|
||||
T ptr = tmp_array->at(i);
|
||||
if (!builder->is_in_buffer_space(ptr)) {
|
||||
if (is_dynamic_dump && MetaspaceShared::is_in_shared_metaspace(ptr)) {
|
||||
// We have a pointer that lives in the dynamic archive but points into
|
||||
// the static archive.
|
||||
} else {
|
||||
ptr = builder->get_buffered_addr(ptr);
|
||||
}
|
||||
}
|
||||
archived_array->at_put(i, ptr);
|
||||
ArchivePtrMarker::mark_pointer(archived_array->adr_at(i));
|
||||
}
|
||||
}
|
||||
|
||||
return archived_array;
|
||||
|
||||
@ -40,6 +40,8 @@
|
||||
#include "utilities/formatBuffer.hpp"
|
||||
|
||||
bool CDSConfig::_is_dumping_static_archive = false;
|
||||
bool CDSConfig::_is_dumping_preimage_static_archive = false;
|
||||
bool CDSConfig::_is_dumping_final_static_archive = false;
|
||||
bool CDSConfig::_is_dumping_dynamic_archive = false;
|
||||
bool CDSConfig::_is_using_optimized_module_handling = true;
|
||||
bool CDSConfig::_is_dumping_full_module_graph = true;
|
||||
@ -47,6 +49,7 @@ bool CDSConfig::_is_using_full_module_graph = true;
|
||||
bool CDSConfig::_has_aot_linked_classes = false;
|
||||
bool CDSConfig::_has_archived_invokedynamic = false;
|
||||
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;
|
||||
@ -64,7 +67,7 @@ int CDSConfig::get_status() {
|
||||
}
|
||||
|
||||
void CDSConfig::initialize() {
|
||||
if (is_dumping_static_archive()) {
|
||||
if (is_dumping_static_archive() && !is_dumping_final_static_archive()) {
|
||||
if (RequireSharedSpaces) {
|
||||
warning("Cannot dump shared archive while using shared archive");
|
||||
}
|
||||
@ -210,6 +213,7 @@ void CDSConfig::init_shared_archive_paths() {
|
||||
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) {
|
||||
@ -333,7 +337,11 @@ bool CDSConfig::has_unsupported_runtime_module_options() {
|
||||
if (RequireSharedSpaces) {
|
||||
warning("CDS is disabled when the %s option is specified.", option);
|
||||
} else {
|
||||
log_info(cds)("CDS is disabled when the %s option is specified.", option);
|
||||
if (new_aot_flags_used()) {
|
||||
log_warning(cds)("AOT cache is disabled when the %s option is specified.", option);
|
||||
} else {
|
||||
log_info(cds)("CDS is disabled when the %s option is specified.", option);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -343,7 +351,7 @@ bool CDSConfig::has_unsupported_runtime_module_options() {
|
||||
#define CHECK_ALIAS(f) check_flag_alias(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) {
|
||||
if (old_cds_flags_used() && !alias_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",
|
||||
@ -351,7 +359,7 @@ void CDSConfig::check_flag_alias(bool alias_is_default, const char* alias_name)
|
||||
}
|
||||
}
|
||||
|
||||
void CDSConfig::check_flag_aliases() {
|
||||
void CDSConfig::check_aot_flags() {
|
||||
if (!FLAG_IS_DEFAULT(DumpLoadedClassList) ||
|
||||
!FLAG_IS_DEFAULT(SharedClassListFile) ||
|
||||
!FLAG_IS_DEFAULT(SharedArchiveFile)) {
|
||||
@ -363,30 +371,16 @@ void CDSConfig::check_flag_aliases() {
|
||||
CHECK_ALIAS(AOTMode);
|
||||
|
||||
if (FLAG_IS_DEFAULT(AOTCache) && FLAG_IS_DEFAULT(AOTConfiguration) && FLAG_IS_DEFAULT(AOTMode)) {
|
||||
// Aliases not used.
|
||||
// AOTCache/AOTConfiguration/AOTMode not used.
|
||||
return;
|
||||
} else {
|
||||
_new_aot_flags_used = true;
|
||||
}
|
||||
|
||||
if (FLAG_IS_DEFAULT(AOTMode) || strcmp(AOTMode, "auto") == 0 || strcmp(AOTMode, "on") == 0) {
|
||||
if (!FLAG_IS_DEFAULT(AOTConfiguration)) {
|
||||
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;
|
||||
} else {
|
||||
assert(strcmp(AOTMode, "on") == 0, "already checked");
|
||||
RequireSharedSpaces = true;
|
||||
}
|
||||
check_aotmode_auto_or_on();
|
||||
} else if (strcmp(AOTMode, "off") == 0) {
|
||||
UseSharedSpaces = false;
|
||||
RequireSharedSpaces = false;
|
||||
check_aotmode_off();
|
||||
} else {
|
||||
// AOTMode is record or create
|
||||
if (FLAG_IS_DEFAULT(AOTConfiguration)) {
|
||||
@ -394,32 +388,78 @@ void CDSConfig::check_flag_aliases() {
|
||||
}
|
||||
|
||||
if (strcmp(AOTMode, "record") == 0) {
|
||||
if (!FLAG_IS_DEFAULT(AOTCache)) {
|
||||
vm_exit_during_initialization("AOTCache must not be specified when using -XX:AOTMode=record");
|
||||
}
|
||||
|
||||
assert(FLAG_IS_DEFAULT(DumpLoadedClassList), "already checked");
|
||||
FLAG_SET_ERGO(DumpLoadedClassList, AOTConfiguration);
|
||||
UseSharedSpaces = false;
|
||||
RequireSharedSpaces = false;
|
||||
check_aotmode_record();
|
||||
} else {
|
||||
assert(strcmp(AOTMode, "create") == 0, "checked by AOTModeConstraintFunc");
|
||||
if (FLAG_IS_DEFAULT(AOTCache)) {
|
||||
vm_exit_during_initialization("AOTCache must be specified when using -XX:AOTMode=create");
|
||||
}
|
||||
|
||||
assert(FLAG_IS_DEFAULT(SharedClassListFile), "already checked");
|
||||
FLAG_SET_ERGO(SharedClassListFile, AOTConfiguration);
|
||||
assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked");
|
||||
FLAG_SET_ERGO(SharedArchiveFile, AOTCache);
|
||||
|
||||
CDSConfig::enable_dumping_static_archive();
|
||||
check_aotmode_create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDSConfig::check_aotmode_off() {
|
||||
UseSharedSpaces = false;
|
||||
RequireSharedSpaces = false;
|
||||
}
|
||||
|
||||
void CDSConfig::check_aotmode_auto_or_on() {
|
||||
if (!FLAG_IS_DEFAULT(AOTConfiguration)) {
|
||||
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;
|
||||
} else {
|
||||
assert(strcmp(AOTMode, "on") == 0, "already checked");
|
||||
RequireSharedSpaces = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CDSConfig::check_aotmode_record() {
|
||||
if (!FLAG_IS_DEFAULT(AOTCache)) {
|
||||
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;
|
||||
_is_dumping_preimage_static_archive = true;
|
||||
|
||||
// At VM exit, the module graph may be contaminated with program states.
|
||||
// We will rebuild the module graph when dumping the CDS final image.
|
||||
disable_heap_dumping();
|
||||
}
|
||||
|
||||
void CDSConfig::check_aotmode_create() {
|
||||
if (FLAG_IS_DEFAULT(AOTCache)) {
|
||||
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;
|
||||
|
||||
if (!FileMapInfo::is_preimage_static_archive(AOTConfiguration)) {
|
||||
vm_exit_during_initialization("Must be a valid AOT configuration generated by the current JVM", AOTConfiguration);
|
||||
}
|
||||
|
||||
CDSConfig::enable_dumping_static_archive();
|
||||
}
|
||||
|
||||
bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line) {
|
||||
check_flag_aliases();
|
||||
check_aot_flags();
|
||||
|
||||
if (!FLAG_IS_DEFAULT(AOTMode)) {
|
||||
// Using any form of the new AOTMode switch enables enhanced optimizations.
|
||||
@ -435,7 +475,9 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
|
||||
}
|
||||
|
||||
if (is_dumping_static_archive()) {
|
||||
if (!mode_flag_cmd_line) {
|
||||
if (is_dumping_preimage_static_archive()) {
|
||||
// Don't tweak execution mode
|
||||
} else if (!mode_flag_cmd_line) {
|
||||
// By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive.
|
||||
//
|
||||
// If your classlist is large and you don't care about deterministic dumping, you can use
|
||||
@ -499,6 +541,20 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDSConfig::is_dumping_classic_static_archive() {
|
||||
return _is_dumping_static_archive &&
|
||||
!is_dumping_preimage_static_archive() &&
|
||||
!is_dumping_final_static_archive();
|
||||
}
|
||||
|
||||
bool CDSConfig::is_dumping_preimage_static_archive() {
|
||||
return _is_dumping_preimage_static_archive;
|
||||
}
|
||||
|
||||
bool CDSConfig::is_dumping_final_static_archive() {
|
||||
return _is_dumping_final_static_archive;
|
||||
}
|
||||
|
||||
bool CDSConfig::allow_only_single_java_thread() {
|
||||
// See comments in JVM_StartThread()
|
||||
return is_dumping_static_archive();
|
||||
@ -534,6 +590,26 @@ bool CDSConfig::current_thread_is_vm_or_dumper() {
|
||||
return t != nullptr && (t->is_VM_thread() || t == _dumper_thread);
|
||||
}
|
||||
|
||||
const char* CDSConfig::type_of_archive_being_loaded() {
|
||||
if (is_dumping_final_static_archive()) {
|
||||
return "AOT configuration file";
|
||||
} else if (new_aot_flags_used()) {
|
||||
return "AOT cache";
|
||||
} else {
|
||||
return "shared archive file";
|
||||
}
|
||||
}
|
||||
|
||||
const char* CDSConfig::type_of_archive_being_written() {
|
||||
if (is_dumping_preimage_static_archive()) {
|
||||
return "AOT configuration file";
|
||||
} else if (new_aot_flags_used()) {
|
||||
return "AOT cache";
|
||||
} else {
|
||||
return "shared archive file";
|
||||
}
|
||||
}
|
||||
|
||||
// If an incompatible VM options is found, return a text message that explains why
|
||||
static const char* check_options_incompatible_with_dumping_heap() {
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
@ -574,12 +650,11 @@ bool CDSConfig::are_vm_options_incompatible_with_dumping_heap() {
|
||||
|
||||
|
||||
bool CDSConfig::is_dumping_heap() {
|
||||
if (!is_dumping_static_archive() // heap dump is not supported in dynamic dump
|
||||
if (!(is_dumping_classic_static_archive() || is_dumping_final_static_archive())
|
||||
|| are_vm_options_incompatible_with_dumping_heap()
|
||||
|| _disable_heap_dumping) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -627,7 +702,9 @@ void CDSConfig::stop_using_full_module_graph(const char* reason) {
|
||||
}
|
||||
|
||||
bool CDSConfig::is_dumping_aot_linked_classes() {
|
||||
if (is_dumping_dynamic_archive()) {
|
||||
if (is_dumping_preimage_static_archive()) {
|
||||
return false;
|
||||
} else if (is_dumping_dynamic_archive()) {
|
||||
return is_using_full_module_graph() && AOTClassLinking;
|
||||
} else if (is_dumping_static_archive()) {
|
||||
return is_dumping_full_module_graph() && AOTClassLinking;
|
||||
|
||||
@ -34,6 +34,8 @@ class JavaThread;
|
||||
class CDSConfig : public AllStatic {
|
||||
#if INCLUDE_CDS
|
||||
static bool _is_dumping_static_archive;
|
||||
static bool _is_dumping_preimage_static_archive;
|
||||
static bool _is_dumping_final_static_archive;
|
||||
static bool _is_dumping_dynamic_archive;
|
||||
static bool _is_using_optimized_module_handling;
|
||||
static bool _is_dumping_full_module_graph;
|
||||
@ -46,6 +48,7 @@ class CDSConfig : public AllStatic {
|
||||
static char* _dynamic_archive_path;
|
||||
|
||||
static bool _old_cds_flags_used;
|
||||
static bool _new_aot_flags_used;
|
||||
static bool _disable_heap_dumping;
|
||||
|
||||
static JavaThread* _dumper_thread;
|
||||
@ -57,7 +60,11 @@ class CDSConfig : public AllStatic {
|
||||
static void init_shared_archive_paths();
|
||||
|
||||
static void check_flag_alias(bool alias_is_default, const char* alias_name);
|
||||
static void check_flag_aliases();
|
||||
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();
|
||||
|
||||
public:
|
||||
// Used by jdk.internal.misc.CDS.getCDSConfigStatus();
|
||||
@ -71,11 +78,14 @@ public:
|
||||
static void 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();
|
||||
|
||||
// --- Basic CDS features
|
||||
|
||||
@ -88,6 +98,30 @@ public:
|
||||
static bool is_dumping_static_archive() { return CDS_ONLY(_is_dumping_static_archive) NOT_CDS(false); }
|
||||
static void enable_dumping_static_archive() { CDS_ONLY(_is_dumping_static_archive = true); }
|
||||
|
||||
// A static CDS archive can be dumped in three modes:
|
||||
//
|
||||
// "classic" - This is the traditional CDS workflow of
|
||||
// "java -Xshare:dump -XX:SharedClassListFile=file.txt".
|
||||
//
|
||||
// "preimage" - This happens when we execute the JEP 483 training run, e.g:
|
||||
// "java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconfig -cp app.jar App"
|
||||
// The above command writes app.aotconfig as a "CDS preimage". This
|
||||
// is a binary file that contains all the classes loaded during the
|
||||
// training run, plus profiling data (e.g., the resolved constant pool entries).
|
||||
//
|
||||
// "final" - This happens when we execute the JEP 483 assembly phase, e.g:
|
||||
// "java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconfig -XX:AOTCache=app.aot -cp app.jar"
|
||||
// The above command loads all classes from app.aotconfig, perform additional linking,
|
||||
// and writes app.aot as a "CDS final image" file.
|
||||
//
|
||||
// The main structural difference between "preimage" and "final" is that the preimage
|
||||
// - has a different magic number (0xcafea07c)
|
||||
// - does not have any archived Java heap objects
|
||||
// - does not have aot-linked classes
|
||||
static bool is_dumping_classic_static_archive() NOT_CDS_RETURN_(false);
|
||||
static bool is_dumping_preimage_static_archive() NOT_CDS_RETURN_(false);
|
||||
static bool is_dumping_final_static_archive() NOT_CDS_RETURN_(false);
|
||||
|
||||
// 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); }
|
||||
@ -135,7 +169,6 @@ public:
|
||||
static void stop_dumping_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static void stop_using_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
|
||||
|
||||
// Some CDS functions assume that they are called only within a single-threaded context. I.e.,
|
||||
// they are called from:
|
||||
// - The VM thread (e.g., inside VM_PopulateDumpSharedSpace)
|
||||
|
||||
@ -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
|
||||
@ -105,7 +105,9 @@
|
||||
constraint(AOTModeConstraintFunc, AtParse) \
|
||||
\
|
||||
product(ccstr, AOTConfiguration, nullptr, \
|
||||
"Configuration information used by CreateAOTCache") \
|
||||
"The configuration file written by -XX:AOTMode=record, and " \
|
||||
"loaded by -XX:AOTMode=create. This file contains profiling data "\
|
||||
"for deciding what contents should be added to AOTCache. ") \
|
||||
\
|
||||
product(ccstr, AOTCache, nullptr, \
|
||||
"Cache for improving start up and warm up") \
|
||||
|
||||
@ -189,12 +189,22 @@ enum ClonedVtableKind {
|
||||
_num_cloned_vtable_kinds
|
||||
};
|
||||
|
||||
// This is a map of all the original vtptrs. E.g., for
|
||||
// _orig_cpp_vtptrs and _archived_cpp_vtptrs are used for type checking in
|
||||
// CppVtables::get_archived_vtable().
|
||||
//
|
||||
// _orig_cpp_vtptrs is a map of all the original vtptrs. E.g., for
|
||||
// ConstantPool *cp = new (...) ConstantPool(...) ; // a dynamically allocated constant pool
|
||||
// the following holds true:
|
||||
// _orig_cpp_vtptrs[ConstantPool_Kind] == ((intptr_t**)cp)[0]
|
||||
static intptr_t* _orig_cpp_vtptrs[_num_cloned_vtable_kinds];
|
||||
// _orig_cpp_vtptrs[ConstantPool_Kind] == ((intptr_t**)cp)[0]
|
||||
//
|
||||
// _archived_cpp_vtptrs is a map of all the vptprs used by classes in a preimage. E.g., for
|
||||
// InstanceKlass* k = a class loaded from the preimage;
|
||||
// ConstantPool* cp = k->constants();
|
||||
// the following holds true:
|
||||
// _archived_cpp_vtptrs[ConstantPool_Kind] == ((intptr_t**)cp)[0]
|
||||
static bool _orig_cpp_vtptrs_inited = false;
|
||||
static intptr_t* _orig_cpp_vtptrs[_num_cloned_vtable_kinds];
|
||||
static intptr_t* _archived_cpp_vtptrs[_num_cloned_vtable_kinds];
|
||||
|
||||
template <class T>
|
||||
void CppVtableCloner<T>::init_orig_cpp_vtptr(int kind) {
|
||||
@ -212,15 +222,27 @@ void CppVtableCloner<T>::init_orig_cpp_vtptr(int kind) {
|
||||
// _index[InstanceKlass_Kind]->cloned_vtable() == ((intptr_t**)ik)[0]
|
||||
static CppVtableInfo* _index[_num_cloned_vtable_kinds];
|
||||
|
||||
// Vtables are all fixed offsets from ArchiveBuilder::current()->mapped_base()
|
||||
// E.g. ConstantPool is at offset 0x58. We can archive these offsets in the
|
||||
// RO region and use them to alculate their location at runtime without storing
|
||||
// the pointers in the RW region
|
||||
// This marks the location in the archive where _index[0] is stored. This location
|
||||
// will be stored as FileMapHeader::_cloned_vtables_offset into the archive header.
|
||||
// Serviceability Agent uses this information to determine the vtables of
|
||||
// archived Metadata objects.
|
||||
char* CppVtables::_vtables_serialized_base = nullptr;
|
||||
|
||||
void CppVtables::dumptime_init(ArchiveBuilder* builder) {
|
||||
assert(CDSConfig::is_dumping_static_archive(), "cpp tables are only dumped into static archive");
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
// When dumping final archive, _index[kind] at this point is in the preimage.
|
||||
// Remember these vtable pointers in _archived_cpp_vtptrs, as _index[kind] will now be rewritten
|
||||
// to point to the runtime vtable data.
|
||||
for (int i = 0; i < _num_cloned_vtable_kinds; i++) {
|
||||
assert(_index[i] != nullptr, "must have been restored by CppVtables::serialize()");
|
||||
_archived_cpp_vtptrs[i] = _index[i]->cloned_vtable();
|
||||
}
|
||||
} else {
|
||||
memset(_archived_cpp_vtptrs, 0, sizeof(_archived_cpp_vtptrs));
|
||||
}
|
||||
|
||||
CPP_VTABLE_TYPES_DO(ALLOCATE_AND_INITIALIZE_VTABLE);
|
||||
|
||||
size_t cpp_tables_size = builder->rw_region()->top() - builder->rw_region()->base();
|
||||
@ -267,7 +289,8 @@ intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address ob
|
||||
break;
|
||||
default:
|
||||
for (kind = 0; kind < _num_cloned_vtable_kinds; kind ++) {
|
||||
if (vtable_of((Metadata*)obj) == _orig_cpp_vtptrs[kind]) {
|
||||
if (vtable_of((Metadata*)obj) == _orig_cpp_vtptrs[kind] ||
|
||||
vtable_of((Metadata*)obj) == _archived_cpp_vtptrs[kind]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -295,5 +318,6 @@ void CppVtables::zero_archived_vtables() {
|
||||
|
||||
bool CppVtables::is_valid_shared_method(const Method* m) {
|
||||
assert(MetaspaceShared::is_in_shared_metaspace(m), "must be");
|
||||
return vtable_of(m) == _index[Method_Kind]->cloned_vtable();
|
||||
return vtable_of(m) == _index[Method_Kind]->cloned_vtable() ||
|
||||
vtable_of(m) == _archived_cpp_vtptrs[Method_Kind];
|
||||
}
|
||||
|
||||
@ -66,9 +66,9 @@ void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* na
|
||||
|
||||
GrowableArray<char>* vcflags_array = _verifier_constraint_flags;
|
||||
char c = 0;
|
||||
c |= from_field_is_protected ? SystemDictionaryShared::FROM_FIELD_IS_PROTECTED : 0;
|
||||
c |= from_is_array ? SystemDictionaryShared::FROM_IS_ARRAY : 0;
|
||||
c |= from_is_object ? SystemDictionaryShared::FROM_IS_OBJECT : 0;
|
||||
c |= from_field_is_protected ? RunTimeClassInfo::FROM_FIELD_IS_PROTECTED : 0;
|
||||
c |= from_is_array ? RunTimeClassInfo::FROM_IS_ARRAY : 0;
|
||||
c |= from_is_object ? RunTimeClassInfo::FROM_IS_OBJECT : 0;
|
||||
vcflags_array->append(c);
|
||||
|
||||
if (log_is_enabled(Trace, cds, verification)) {
|
||||
@ -142,7 +142,7 @@ bool DumpTimeClassInfo::is_builtin() {
|
||||
}
|
||||
|
||||
DumpTimeClassInfo* DumpTimeSharedClassTable::allocate_info(InstanceKlass* k) {
|
||||
assert(!k->is_shared(), "Do not call with shared classes");
|
||||
assert(CDSConfig::is_dumping_final_static_archive() || !k->is_shared(), "Do not call with shared classes");
|
||||
bool created;
|
||||
DumpTimeClassInfo* p = put_if_absent(k, &created);
|
||||
assert(created, "must not exist in table");
|
||||
@ -151,7 +151,7 @@ DumpTimeClassInfo* DumpTimeSharedClassTable::allocate_info(InstanceKlass* k) {
|
||||
}
|
||||
|
||||
DumpTimeClassInfo* DumpTimeSharedClassTable::get_info(InstanceKlass* k) {
|
||||
assert(!k->is_shared(), "Do not call with shared classes");
|
||||
assert(CDSConfig::is_dumping_final_static_archive() || !k->is_shared(), "Do not call with shared classes");
|
||||
DumpTimeClassInfo* p = get(k);
|
||||
assert(p != nullptr, "we must not see any non-shared InstanceKlass* that's "
|
||||
"not stored with SystemDictionaryShared::init_dumptime_info");
|
||||
|
||||
@ -388,8 +388,9 @@ public:
|
||||
void doit() {
|
||||
ResourceMark rm;
|
||||
if (AllowArchivingWithJavaAgent) {
|
||||
log_warning(cds)("This archive was created with AllowArchivingWithJavaAgent. It should be used "
|
||||
"for testing purposes only and should not be used in a production environment");
|
||||
log_warning(cds)("This %s was created with AllowArchivingWithJavaAgent. It should be used "
|
||||
"for testing purposes only and should not be used in a production environment",
|
||||
CDSConfig::type_of_archive_being_loaded());
|
||||
}
|
||||
AOTClassLocationConfig::dumptime_check_nonempty_dirs();
|
||||
_builder.doit();
|
||||
|
||||
@ -151,6 +151,13 @@ FileMapInfo::~FileMapInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
void FileMapInfo::free_current_info() {
|
||||
assert(CDSConfig::is_dumping_final_static_archive(), "only supported in this mode");
|
||||
assert(_current_info != nullptr, "sanity");
|
||||
delete _current_info;
|
||||
assert(_current_info == nullptr, "sanity"); // Side effect expected from the above "delete" operator.
|
||||
}
|
||||
|
||||
void FileMapInfo::populate_header(size_t core_region_alignment) {
|
||||
assert(_header == nullptr, "Sanity check");
|
||||
size_t c_header_size;
|
||||
@ -191,7 +198,13 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
|
||||
set_header_size((unsigned int)header_size);
|
||||
set_base_archive_name_offset((unsigned int)base_archive_name_offset);
|
||||
set_base_archive_name_size((unsigned int)base_archive_name_size);
|
||||
set_magic(CDSConfig::is_dumping_dynamic_archive() ? CDS_DYNAMIC_ARCHIVE_MAGIC : CDS_ARCHIVE_MAGIC);
|
||||
if (CDSConfig::is_dumping_dynamic_archive()) {
|
||||
set_magic(CDS_DYNAMIC_ARCHIVE_MAGIC);
|
||||
} else if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
set_magic(CDS_PREIMAGE_ARCHIVE_MAGIC);
|
||||
} else {
|
||||
set_magic(CDS_ARCHIVE_MAGIC);
|
||||
}
|
||||
set_version(CURRENT_CDS_ARCHIVE_VERSION);
|
||||
|
||||
if (!info->is_static() && base_archive_name_size != 0) {
|
||||
@ -386,7 +399,7 @@ public:
|
||||
assert(_archive_name != nullptr, "Archive name is null");
|
||||
_fd = os::open(_archive_name, O_RDONLY | O_BINARY, 0);
|
||||
if (_fd < 0) {
|
||||
log_info(cds)("Specified shared archive not found (%s)", _archive_name);
|
||||
log_info(cds)("Specified %s not found (%s)", CDSConfig::type_of_archive_being_loaded(), _archive_name);
|
||||
return false;
|
||||
}
|
||||
return initialize(_fd);
|
||||
@ -397,30 +410,32 @@ public:
|
||||
assert(_archive_name != nullptr, "Archive name is null");
|
||||
assert(fd != -1, "Archive must be opened already");
|
||||
// First read the generic header so we know the exact size of the actual header.
|
||||
const char* file_type = CDSConfig::type_of_archive_being_loaded();
|
||||
GenericCDSFileMapHeader gen_header;
|
||||
size_t size = sizeof(GenericCDSFileMapHeader);
|
||||
os::lseek(fd, 0, SEEK_SET);
|
||||
size_t n = ::read(fd, (void*)&gen_header, (unsigned int)size);
|
||||
if (n != size) {
|
||||
log_warning(cds)("Unable to read generic CDS file map header from shared archive");
|
||||
log_warning(cds)("Unable to read generic CDS file map header from %s", file_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gen_header._magic != CDS_ARCHIVE_MAGIC &&
|
||||
gen_header._magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||||
log_warning(cds)("The shared archive file has a bad magic number: %#x", gen_header._magic);
|
||||
gen_header._magic != CDS_DYNAMIC_ARCHIVE_MAGIC &&
|
||||
gen_header._magic != CDS_PREIMAGE_ARCHIVE_MAGIC) {
|
||||
log_warning(cds)("The %s has a bad magic number: %#x", file_type, gen_header._magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gen_header._version < CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION) {
|
||||
log_warning(cds)("Cannot handle shared archive file version 0x%x. Must be at least 0x%x.",
|
||||
gen_header._version, CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION);
|
||||
log_warning(cds)("Cannot handle %s version 0x%x. Must be at least 0x%x.",
|
||||
file_type, gen_header._version, CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gen_header._version != CURRENT_CDS_ARCHIVE_VERSION) {
|
||||
log_warning(cds)("The shared archive file version 0x%x does not match the required version 0x%x.",
|
||||
gen_header._version, CURRENT_CDS_ARCHIVE_VERSION);
|
||||
log_warning(cds)("The %s version 0x%x does not match the required version 0x%x.",
|
||||
file_type, gen_header._version, CURRENT_CDS_ARCHIVE_VERSION);
|
||||
}
|
||||
|
||||
size_t filelen = os::lseek(fd, 0, SEEK_END);
|
||||
@ -435,7 +450,7 @@ public:
|
||||
os::lseek(fd, 0, SEEK_SET);
|
||||
n = ::read(fd, (void*)_header, (unsigned int)size);
|
||||
if (n != size) {
|
||||
log_warning(cds)("Unable to read actual CDS file map header from shared archive");
|
||||
log_warning(cds)("Unable to read file map header from %s", file_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -462,6 +477,18 @@ public:
|
||||
return _base_archive_name;
|
||||
}
|
||||
|
||||
bool is_static_archive() const {
|
||||
return _header->_magic == CDS_ARCHIVE_MAGIC;
|
||||
}
|
||||
|
||||
bool is_dynamic_archive() const {
|
||||
return _header->_magic == CDS_DYNAMIC_ARCHIVE_MAGIC;
|
||||
}
|
||||
|
||||
bool is_preimage_static_archive() const {
|
||||
return _header->_magic == CDS_PREIMAGE_ARCHIVE_MAGIC;
|
||||
}
|
||||
|
||||
private:
|
||||
bool check_header_crc() const {
|
||||
if (VerifySharedSpaces) {
|
||||
@ -487,7 +514,8 @@ public:
|
||||
name_offset, name_size);
|
||||
return false;
|
||||
}
|
||||
if (_header->_magic == CDS_ARCHIVE_MAGIC) {
|
||||
|
||||
if (is_static_archive() || is_preimage_static_archive()) {
|
||||
if (name_offset != 0) {
|
||||
log_warning(cds)("static shared archive must have zero _base_archive_name_offset");
|
||||
return false;
|
||||
@ -497,7 +525,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(_header->_magic == CDS_DYNAMIC_ARCHIVE_MAGIC, "must be");
|
||||
assert(is_dynamic_archive(), "must be");
|
||||
if ((name_size == 0 && name_offset != 0) ||
|
||||
(name_size != 0 && name_offset == 0)) {
|
||||
// If either is zero, both must be zero. This indicates that we are using the default base archive.
|
||||
@ -545,7 +573,12 @@ bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
|
||||
return false;
|
||||
}
|
||||
GenericCDSFileMapHeader* header = file_helper.get_generic_file_header();
|
||||
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||||
switch (header->_magic) {
|
||||
case CDS_PREIMAGE_ARCHIVE_MAGIC:
|
||||
return false; // This is a binary config file, not a proper archive
|
||||
case CDS_DYNAMIC_ARCHIVE_MAGIC:
|
||||
break;
|
||||
default:
|
||||
assert(header->_magic == CDS_ARCHIVE_MAGIC, "must be");
|
||||
if (AutoCreateSharedArchive) {
|
||||
log_warning(cds)("AutoCreateSharedArchive is ignored because %s is a static archive", archive_name);
|
||||
@ -563,6 +596,14 @@ bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileMapInfo::is_preimage_static_archive(const char* file) {
|
||||
FileHeaderHelper file_helper(file, false);
|
||||
if (!file_helper.initialize()) {
|
||||
return false;
|
||||
}
|
||||
return file_helper.is_preimage_static_archive();
|
||||
}
|
||||
|
||||
// Read the FileMapInfo information from the file.
|
||||
|
||||
bool FileMapInfo::init_from_file(int fd) {
|
||||
@ -573,9 +614,17 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
}
|
||||
GenericCDSFileMapHeader* gen_header = file_helper.get_generic_file_header();
|
||||
|
||||
const char* file_type = CDSConfig::type_of_archive_being_loaded();
|
||||
if (_is_static) {
|
||||
if (gen_header->_magic != CDS_ARCHIVE_MAGIC) {
|
||||
log_warning(cds)("Not a base shared archive: %s", _full_path);
|
||||
if ((gen_header->_magic == CDS_ARCHIVE_MAGIC) ||
|
||||
(gen_header->_magic == CDS_PREIMAGE_ARCHIVE_MAGIC && CDSConfig::is_dumping_final_static_archive())) {
|
||||
// Good
|
||||
} else {
|
||||
if (CDSConfig::new_aot_flags_used()) {
|
||||
log_warning(cds)("Not a valid %s %s", file_type, _full_path);
|
||||
} else {
|
||||
log_warning(cds)("Not a base shared archive: %s", _full_path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -597,7 +646,7 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
if (header()->version() != CURRENT_CDS_ARCHIVE_VERSION) {
|
||||
log_info(cds)("_version expected: 0x%x", CURRENT_CDS_ARCHIVE_VERSION);
|
||||
log_info(cds)(" actual: 0x%x", header()->version());
|
||||
log_warning(cds)("The shared archive file has the wrong version.");
|
||||
log_warning(cds)("The %s has the wrong version.", file_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -609,7 +658,7 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
log_info(cds)("_header_size: " UINT32_FORMAT, header_size);
|
||||
log_info(cds)("base_archive_name_size: " UINT32_FORMAT, header()->base_archive_name_size());
|
||||
log_info(cds)("base_archive_name_offset: " UINT32_FORMAT, header()->base_archive_name_offset());
|
||||
log_warning(cds)("The shared archive file has an incorrect header size.");
|
||||
log_warning(cds)("The %s has an incorrect header size.", file_type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -626,8 +675,8 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
if (strncmp(actual_ident, expected_ident, JVM_IDENT_MAX-1) != 0) {
|
||||
log_info(cds)("_jvm_ident expected: %s", expected_ident);
|
||||
log_info(cds)(" actual: %s", actual_ident);
|
||||
log_warning(cds)("The shared archive file was created by a different"
|
||||
" version or build of HotSpot");
|
||||
log_warning(cds)("The %s was created by a different"
|
||||
" version or build of HotSpot", file_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -638,7 +687,7 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
for (int i = 0; i < MetaspaceShared::n_regions; i++) {
|
||||
FileMapRegion* r = region_at(i);
|
||||
if (r->file_offset() > len || len - r->file_offset() < r->used()) {
|
||||
log_warning(cds)("The shared archive file has been truncated.");
|
||||
log_warning(cds)("The %s has been truncated.", file_type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -658,18 +707,21 @@ bool FileMapInfo::open_for_read() {
|
||||
if (_file_open) {
|
||||
return true;
|
||||
}
|
||||
log_info(cds)("trying to map %s", _full_path);
|
||||
const char* file_type = CDSConfig::type_of_archive_being_loaded();
|
||||
const char* info = CDSConfig::is_dumping_final_static_archive() ?
|
||||
"AOTConfiguration file " : "";
|
||||
log_info(cds)("trying to map %s%s", info, _full_path);
|
||||
int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_info(cds)("Specified shared archive not found (%s)", _full_path);
|
||||
log_info(cds)("Specified %s not found (%s)", file_type, _full_path);
|
||||
} else {
|
||||
log_warning(cds)("Failed to open shared archive file (%s)",
|
||||
log_warning(cds)("Failed to open %s (%s)", file_type,
|
||||
os::strerror(errno));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
log_info(cds)("Opened archive %s.", _full_path);
|
||||
log_info(cds)("Opened %s %s.", file_type, _full_path);
|
||||
}
|
||||
|
||||
_fd = fd;
|
||||
@ -682,20 +734,25 @@ bool FileMapInfo::open_for_read() {
|
||||
void FileMapInfo::open_for_write() {
|
||||
LogMessage(cds) msg;
|
||||
if (msg.is_info()) {
|
||||
msg.info("Dumping shared data to file: ");
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
msg.info("Writing binary AOTConfiguration file: ");
|
||||
} else {
|
||||
msg.info("Dumping shared data to file: ");
|
||||
}
|
||||
msg.info(" %s", _full_path);
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS // On Windows, need WRITE permission to remove the file.
|
||||
chmod(_full_path, _S_IREAD | _S_IWRITE);
|
||||
chmod(_full_path, _S_IREAD | _S_IWRITE);
|
||||
#endif
|
||||
|
||||
// Use remove() to delete the existing file because, on Unix, this will
|
||||
// allow processes that have it open continued access to the file.
|
||||
remove(_full_path);
|
||||
int fd = os::open(_full_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0444);
|
||||
int mode = CDSConfig::is_dumping_preimage_static_archive() ? 0666 : 0444;
|
||||
int fd = os::open(_full_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, mode);
|
||||
if (fd < 0) {
|
||||
log_error(cds)("Unable to create shared archive file %s: (%s).", _full_path,
|
||||
log_error(cds)("Unable to create %s %s: (%s).", CDSConfig::type_of_archive_being_written(), _full_path,
|
||||
os::strerror(errno));
|
||||
MetaspaceShared::writing_error();
|
||||
return;
|
||||
@ -951,7 +1008,14 @@ void FileMapInfo::write_bytes(const void* buffer, size_t nbytes) {
|
||||
// If the shared archive is corrupted, close it and remove it.
|
||||
close();
|
||||
remove(_full_path);
|
||||
MetaspaceShared::writing_error("Unable to write to shared archive file.");
|
||||
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
MetaspaceShared::writing_error("Unable to write to AOT configuration file.");
|
||||
} else if (CDSConfig::new_aot_flags_used()) {
|
||||
MetaspaceShared::writing_error("Unable to write to AOT cache.");
|
||||
} else {
|
||||
MetaspaceShared::writing_error("Unable to write to shared archive.");
|
||||
}
|
||||
}
|
||||
_file_offset += nbytes;
|
||||
}
|
||||
@ -1793,15 +1857,16 @@ int FileMapHeader::compute_crc() {
|
||||
|
||||
// This function should only be called during run time with UseSharedSpaces enabled.
|
||||
bool FileMapHeader::validate() {
|
||||
const char* file_type = CDSConfig::type_of_archive_being_loaded();
|
||||
if (_obj_alignment != ObjectAlignmentInBytes) {
|
||||
log_info(cds)("The shared archive file's ObjectAlignmentInBytes of %d"
|
||||
log_info(cds)("The %s's ObjectAlignmentInBytes of %d"
|
||||
" does not equal the current ObjectAlignmentInBytes of %d.",
|
||||
_obj_alignment, ObjectAlignmentInBytes);
|
||||
file_type, _obj_alignment, ObjectAlignmentInBytes);
|
||||
return false;
|
||||
}
|
||||
if (_compact_strings != CompactStrings) {
|
||||
log_info(cds)("The shared archive file's CompactStrings setting (%s)"
|
||||
" does not equal the current CompactStrings setting (%s).",
|
||||
log_info(cds)("The %s's CompactStrings setting (%s)"
|
||||
" does not equal the current CompactStrings setting (%s).", file_type,
|
||||
_compact_strings ? "enabled" : "disabled",
|
||||
CompactStrings ? "enabled" : "disabled");
|
||||
return false;
|
||||
@ -1825,8 +1890,8 @@ bool FileMapHeader::validate() {
|
||||
|
||||
if (!_verify_local && BytecodeVerificationLocal) {
|
||||
// we cannot load boot classes, so there's no point of using the CDS archive
|
||||
log_info(cds)("The shared archive file's BytecodeVerificationLocal setting (%s)"
|
||||
" does not equal the current BytecodeVerificationLocal setting (%s).",
|
||||
log_info(cds)("The %s's BytecodeVerificationLocal setting (%s)"
|
||||
" does not equal the current BytecodeVerificationLocal setting (%s).", file_type,
|
||||
_verify_local ? "enabled" : "disabled",
|
||||
BytecodeVerificationLocal ? "enabled" : "disabled");
|
||||
return false;
|
||||
@ -1837,8 +1902,8 @@ bool FileMapHeader::validate() {
|
||||
if (_has_platform_or_app_classes
|
||||
&& !_verify_remote // we didn't verify the archived platform/app classes
|
||||
&& BytecodeVerificationRemote) { // but we want to verify all loaded platform/app classes
|
||||
log_info(cds)("The shared archive file was created with less restrictive "
|
||||
"verification setting than the current setting.");
|
||||
log_info(cds)("The %s was created with less restrictive "
|
||||
"verification setting than the current setting.", file_type);
|
||||
// Pretend that we didn't have any archived platform/app classes, so they won't be loaded
|
||||
// by SystemDictionaryShared.
|
||||
_has_platform_or_app_classes = false;
|
||||
@ -1850,32 +1915,32 @@ bool FileMapHeader::validate() {
|
||||
// while AllowArchivingWithJavaAgent is set during the current run.
|
||||
if (_allow_archiving_with_java_agent && !AllowArchivingWithJavaAgent) {
|
||||
log_warning(cds)("The setting of the AllowArchivingWithJavaAgent is different "
|
||||
"from the setting in the shared archive.");
|
||||
"from the setting in the %s.", file_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_allow_archiving_with_java_agent) {
|
||||
log_warning(cds)("This archive was created with AllowArchivingWithJavaAgent. It should be used "
|
||||
"for testing purposes only and should not be used in a production environment");
|
||||
log_warning(cds)("This %s was created with AllowArchivingWithJavaAgent. It should be used "
|
||||
"for testing purposes only and should not be used in a production environment", file_type);
|
||||
}
|
||||
|
||||
log_info(cds)("Archive was created with UseCompressedOops = %d, UseCompressedClassPointers = %d, UseCompactObjectHeaders = %d",
|
||||
compressed_oops(), compressed_class_pointers(), compact_headers());
|
||||
log_info(cds)("The %s was created with UseCompressedOops = %d, UseCompressedClassPointers = %d, UseCompactObjectHeaders = %d",
|
||||
file_type, compressed_oops(), compressed_class_pointers(), compact_headers());
|
||||
if (compressed_oops() != UseCompressedOops || compressed_class_pointers() != UseCompressedClassPointers) {
|
||||
log_warning(cds)("Unable to use shared archive.\nThe saved state of UseCompressedOops and UseCompressedClassPointers is "
|
||||
"different from runtime, CDS will be disabled.");
|
||||
log_warning(cds)("Unable to use %s.\nThe saved state of UseCompressedOops and UseCompressedClassPointers is "
|
||||
"different from runtime, CDS will be disabled.", file_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compact_headers() != UseCompactObjectHeaders) {
|
||||
log_warning(cds)("Unable to use shared archive.\nThe shared archive file's UseCompactObjectHeaders setting (%s)"
|
||||
" does not equal the current UseCompactObjectHeaders setting (%s).",
|
||||
log_warning(cds)("Unable to use %s.\nThe %s's UseCompactObjectHeaders setting (%s)"
|
||||
" does not equal the current UseCompactObjectHeaders setting (%s).", file_type, file_type,
|
||||
_compact_headers ? "enabled" : "disabled",
|
||||
UseCompactObjectHeaders ? "enabled" : "disabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_use_optimized_module_handling) {
|
||||
if (!_use_optimized_module_handling && !CDSConfig::is_dumping_final_static_archive()) {
|
||||
CDSConfig::stop_using_optimized_module_handling();
|
||||
log_info(cds)("optimized module handling: disabled because archive was created without optimized module handling");
|
||||
}
|
||||
|
||||
@ -270,12 +270,15 @@ public:
|
||||
FileMapHeader *header() const { return _header; }
|
||||
static bool get_base_archive_name_from_header(const char* archive_name,
|
||||
char** base_archive_name);
|
||||
static bool is_preimage_static_archive(const char* file);
|
||||
|
||||
bool init_from_file(int fd);
|
||||
|
||||
void log_paths(const char* msg, int start_idx, int end_idx);
|
||||
|
||||
FileMapInfo(const char* full_apth, bool is_static);
|
||||
~FileMapInfo();
|
||||
static void free_current_info();
|
||||
|
||||
// Accessors
|
||||
int compute_header_crc() const { return header()->compute_crc(); }
|
||||
|
||||
167
src/hotspot/share/cds/finalImageRecipes.cpp
Normal file
167
src/hotspot/share/cds/finalImageRecipes.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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/aotConstantPoolResolver.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/archiveUtils.inline.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/finalImageRecipes.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "classfile/vmClasses.hpp"
|
||||
#include "memory/oopFactory.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/constantPool.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
|
||||
static FinalImageRecipes* _final_image_recipes = nullptr;
|
||||
|
||||
void* FinalImageRecipes::operator new(size_t size) throw() {
|
||||
return ArchiveBuilder::current()->ro_region_alloc(size);
|
||||
}
|
||||
|
||||
void FinalImageRecipes::record_recipes_impl() {
|
||||
assert(CDSConfig::is_dumping_preimage_static_archive(), "must be");
|
||||
ResourceMark rm;
|
||||
GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses();
|
||||
|
||||
// Record the indys that have been resolved in the training run. These indys will be
|
||||
// resolved during the final image assembly.
|
||||
|
||||
GrowableArray<InstanceKlass*> tmp_indy_klasses;
|
||||
GrowableArray<Array<int>*> tmp_indy_cp_indices;
|
||||
int total_indys_to_resolve = 0;
|
||||
for (int i = 0; i < klasses->length(); i++) {
|
||||
Klass* k = klasses->at(i);
|
||||
if (k->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
GrowableArray<int> indices;
|
||||
|
||||
if (ik->constants()->cache() != nullptr) {
|
||||
Array<ResolvedIndyEntry>* tmp_indy_entries = ik->constants()->cache()->resolved_indy_entries();
|
||||
if (tmp_indy_entries != nullptr) {
|
||||
for (int i = 0; i < tmp_indy_entries->length(); i++) {
|
||||
ResolvedIndyEntry* rie = tmp_indy_entries->adr_at(i);
|
||||
int cp_index = rie->constant_pool_index();
|
||||
if (rie->is_resolved()) {
|
||||
indices.append(cp_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (indices.length() > 0) {
|
||||
tmp_indy_klasses.append(ArchiveBuilder::current()->get_buffered_addr(ik));
|
||||
tmp_indy_cp_indices.append(ArchiveUtils::archive_array(&indices));
|
||||
total_indys_to_resolve += indices.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_all_klasses = ArchiveUtils::archive_array(klasses);
|
||||
ArchivePtrMarker::mark_pointer(&_all_klasses);
|
||||
|
||||
assert(tmp_indy_klasses.length() == tmp_indy_cp_indices.length(), "must be");
|
||||
if (tmp_indy_klasses.length() > 0) {
|
||||
_indy_klasses = ArchiveUtils::archive_array(&tmp_indy_klasses);
|
||||
_indy_cp_indices = ArchiveUtils::archive_array(&tmp_indy_cp_indices);
|
||||
|
||||
ArchivePtrMarker::mark_pointer(&_indy_klasses);
|
||||
ArchivePtrMarker::mark_pointer(&_indy_cp_indices);
|
||||
}
|
||||
log_info(cds)("%d indies in %d classes will be resolved in final CDS image", total_indys_to_resolve, tmp_indy_klasses.length());
|
||||
}
|
||||
|
||||
void FinalImageRecipes::load_all_classes(TRAPS) {
|
||||
assert(CDSConfig::is_dumping_final_static_archive(), "sanity");
|
||||
Handle class_loader(THREAD, SystemDictionary::java_system_loader());
|
||||
for (int i = 0; i < _all_klasses->length(); i++) {
|
||||
Klass* k = _all_klasses->at(i);
|
||||
if (k->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
if (!ik->is_shared_unregistered_class() && !ik->is_hidden()) {
|
||||
Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK);
|
||||
if (actual != ik) {
|
||||
ResourceMark rm(THREAD);
|
||||
log_error(cds)("Unable to resolve class from CDS archive: %s", ik->external_name());
|
||||
log_error(cds)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual));
|
||||
log_error(cds)("Please check if your VM command-line is the same as in the training run");
|
||||
MetaspaceShared::unrecoverable_writing_error();
|
||||
}
|
||||
assert(ik->is_loaded(), "must be");
|
||||
ik->link_class(CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FinalImageRecipes::apply_recipes_for_invokedynamic(TRAPS) {
|
||||
assert(CDSConfig::is_dumping_final_static_archive(), "must be");
|
||||
|
||||
if (CDSConfig::is_dumping_invokedynamic() && _indy_klasses != nullptr) {
|
||||
assert(_indy_cp_indices != nullptr, "must be");
|
||||
for (int i = 0; i < _indy_klasses->length(); i++) {
|
||||
InstanceKlass* ik = _indy_klasses->at(i);
|
||||
ConstantPool* cp = ik->constants();
|
||||
Array<int>* cp_indices = _indy_cp_indices->at(i);
|
||||
GrowableArray<bool> preresolve_list(cp->length(), cp->length(), false);
|
||||
for (int j = 0; j < cp_indices->length(); j++) {
|
||||
preresolve_list.at_put(cp_indices->at(j), true);
|
||||
}
|
||||
AOTConstantPoolResolver::preresolve_indy_cp_entries(THREAD, ik, &preresolve_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FinalImageRecipes::record_recipes() {
|
||||
_final_image_recipes = new FinalImageRecipes();
|
||||
_final_image_recipes->record_recipes_impl();
|
||||
}
|
||||
|
||||
void FinalImageRecipes::apply_recipes(TRAPS) {
|
||||
assert(CDSConfig::is_dumping_final_static_archive(), "must be");
|
||||
if (_final_image_recipes != nullptr) {
|
||||
_final_image_recipes->apply_recipes_impl(THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
log_error(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(),
|
||||
java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION)));
|
||||
log_error(cds)("Please check if your VM command-line is the same as in the training run");
|
||||
MetaspaceShared::unrecoverable_writing_error("Unexpected exception, use -Xlog:cds,exceptions=trace for detail");
|
||||
}
|
||||
}
|
||||
|
||||
// Set it to null as we don't need to write this table into the final image.
|
||||
_final_image_recipes = nullptr;
|
||||
}
|
||||
|
||||
void FinalImageRecipes::apply_recipes_impl(TRAPS) {
|
||||
load_all_classes(CHECK);
|
||||
apply_recipes_for_invokedynamic(CHECK);
|
||||
}
|
||||
|
||||
void FinalImageRecipes::serialize(SerializeClosure* soc) {
|
||||
soc->do_ptr((void**)&_final_image_recipes);
|
||||
}
|
||||
76
src/hotspot/share/cds/finalImageRecipes.hpp
Normal file
76
src/hotspot/share/cds/finalImageRecipes.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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_FINALIMAGERECIPES_HPP
|
||||
#define SHARE_CDS_FINALIMAGERECIPES_HPP
|
||||
|
||||
#include "oops/oopsHierarchy.hpp"
|
||||
#include "utilities/exceptions.hpp"
|
||||
|
||||
class InstanceKlass;
|
||||
class Klass;
|
||||
|
||||
template <typename T> class GrowableArray;
|
||||
template <typename T> class Array;
|
||||
|
||||
// This class is used for transferring information from the AOTConfiguration file (aka the "preimage")
|
||||
// to the JVM that creates the AOTCache (aka the "final image").
|
||||
// - The recipes are recorded when CDSConfig::is_dumping_preimage_static_archive() is true.
|
||||
// - The recipes are applied when CDSConfig::is_dumping_final_static_archive() is true.
|
||||
// The following information are recorded:
|
||||
// - The list of all classes that are stored in the AOTConfiguration file.
|
||||
// - The list of all classes that require AOT resolution of invokedynamic call sites.
|
||||
class FinalImageRecipes {
|
||||
// A list of all the archived classes from the preimage. We want to transfer all of these
|
||||
// into the final image.
|
||||
Array<Klass*>* _all_klasses;
|
||||
|
||||
// The classes who have resolved at least one indy CP entry during the training run.
|
||||
// _indy_cp_indices[i] is a list of all resolved CP entries for _indy_klasses[i].
|
||||
Array<InstanceKlass*>* _indy_klasses;
|
||||
Array<Array<int>*>* _indy_cp_indices;
|
||||
|
||||
FinalImageRecipes() : _indy_klasses(nullptr), _indy_cp_indices(nullptr) {}
|
||||
|
||||
void* operator new(size_t size) throw();
|
||||
|
||||
// Called when dumping preimage
|
||||
void record_recipes_impl();
|
||||
|
||||
// Called when dumping final image
|
||||
void apply_recipes_impl(TRAPS);
|
||||
void load_all_classes(TRAPS);
|
||||
void apply_recipes_for_invokedynamic(TRAPS);
|
||||
|
||||
public:
|
||||
static void serialize(SerializeClosure* soc);
|
||||
|
||||
// Called when dumping preimage
|
||||
static void record_recipes();
|
||||
|
||||
// Called when dumping final image
|
||||
static void apply_recipes(TRAPS);
|
||||
};
|
||||
|
||||
#endif // SHARE_CDS_FINALIMAGERECIPES_HPP
|
||||
@ -401,6 +401,11 @@ objArrayOop HeapShared::scratch_resolved_references(ConstantPool* src) {
|
||||
return (objArrayOop)_scratch_references_table->get_oop(src);
|
||||
}
|
||||
|
||||
void HeapShared::init_dumping() {
|
||||
_scratch_java_mirror_table = new (mtClass)MetaspaceObjToOopHandleTable();
|
||||
_scratch_references_table = new (mtClass)MetaspaceObjToOopHandleTable();
|
||||
}
|
||||
|
||||
void HeapShared::init_scratch_objects(TRAPS) {
|
||||
for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
|
||||
BasicType bt = (BasicType)i;
|
||||
@ -409,8 +414,6 @@ void HeapShared::init_scratch_objects(TRAPS) {
|
||||
_scratch_basic_type_mirrors[i] = OopHandle(Universe::vm_global(), m);
|
||||
}
|
||||
}
|
||||
_scratch_java_mirror_table = new (mtClass)MetaspaceObjToOopHandleTable();
|
||||
_scratch_references_table = new (mtClass)MetaspaceObjToOopHandleTable();
|
||||
}
|
||||
|
||||
// Given java_mirror that represents a (primitive or reference) type T,
|
||||
|
||||
@ -405,6 +405,7 @@ private:
|
||||
static void write_heap(ArchiveHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static objArrayOop scratch_resolved_references(ConstantPool* src);
|
||||
static void add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static void init_dumping() NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static void init_scratch_objects(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static void init_box_classes(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static bool is_heap_region(int idx) {
|
||||
|
||||
@ -263,4 +263,8 @@ void LambdaFormInvokers::read_static_archive_invokers() {
|
||||
|
||||
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();
|
||||
_static_archive_invokers = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
#include "cds/dumpAllocStats.hpp"
|
||||
#include "cds/dynamicArchive.hpp"
|
||||
#include "cds/filemap.hpp"
|
||||
#include "cds/finalImageRecipes.hpp"
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "cds/lambdaFormInvokers.hpp"
|
||||
#include "cds/metaspaceShared.hpp"
|
||||
@ -489,6 +490,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
|
||||
HeapShared::serialize_tables(soc);
|
||||
SystemDictionaryShared::serialize_dictionary_headers(soc);
|
||||
AOTLinkedClassBulkLoader::serialize(soc, true);
|
||||
FinalImageRecipes::serialize(soc);
|
||||
InstanceMirrorKlass::serialize_offsets(soc);
|
||||
|
||||
// Dump/restore well known classes (pointers)
|
||||
@ -609,6 +611,9 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*&
|
||||
SystemDictionaryShared::write_to_archive();
|
||||
cl_config = AOTClassLocationConfig::dumptime()->write_to_archive();
|
||||
AOTClassLinker::write_to_archive();
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
FinalImageRecipes::record_recipes();
|
||||
}
|
||||
MetaspaceShared::write_method_handle_intrinsics();
|
||||
|
||||
// Write lambform lines into archive
|
||||
@ -624,7 +629,9 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*&
|
||||
}
|
||||
|
||||
void VM_PopulateDumpSharedSpace::doit() {
|
||||
guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump");
|
||||
if (!CDSConfig::is_dumping_final_static_archive()) {
|
||||
guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump");
|
||||
}
|
||||
|
||||
DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
|
||||
|
||||
@ -679,7 +686,13 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
CppVtables::zero_archived_vtables();
|
||||
|
||||
// Write the archive file
|
||||
const char* static_archive = CDSConfig::static_archive_path();
|
||||
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();
|
||||
}
|
||||
assert(static_archive != nullptr, "SharedArchiveFile not set?");
|
||||
_map_info = new FileMapInfo(static_archive, true);
|
||||
_map_info->populate_header(MetaspaceShared::core_region_alignment());
|
||||
@ -743,7 +756,7 @@ bool MetaspaceShared::link_class_for_cds(InstanceKlass* ik, TRAPS) {
|
||||
void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) {
|
||||
AOTClassLinker::initialize();
|
||||
|
||||
if (!jcmd_request) {
|
||||
if (!jcmd_request && !CDSConfig::is_dumping_final_static_archive()) {
|
||||
LambdaFormInvokers::regenerate_holder_classes(CHECK);
|
||||
}
|
||||
|
||||
@ -778,6 +791,10 @@ void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) {
|
||||
// Class linking includes verification which may load more classes.
|
||||
// Keep scanning until we have linked no more classes.
|
||||
}
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
FinalImageRecipes::apply_recipes(CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
void MetaspaceShared::prepare_for_dumping() {
|
||||
@ -804,13 +821,18 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!CDSConfig::old_cds_flags_used()) {
|
||||
// The JLI launcher only recognizes the "old" -Xshare:dump flag.
|
||||
// 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);
|
||||
if (CDSConfig::new_aot_flags_used()) {
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
tty->print_cr("AOTConfiguration recorded: %s", AOTConfiguration);
|
||||
vm_exit(0);
|
||||
} else {
|
||||
// The JLI launcher only recognizes the "old" -Xshare:dump flag.
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -910,12 +932,34 @@ void MetaspaceShared::exercise_runtime_cds_code(TRAPS) {
|
||||
}
|
||||
|
||||
void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS) {
|
||||
preload_classes(CHECK);
|
||||
if (CDSConfig::is_dumping_classic_static_archive()) {
|
||||
// We are running with -Xshare:dump
|
||||
preload_classes(CHECK);
|
||||
|
||||
if (SharedArchiveConfigFile) {
|
||||
log_info(cds)("Reading extra data from %s ...", SharedArchiveConfigFile);
|
||||
read_extra_data(THREAD, SharedArchiveConfigFile);
|
||||
log_info(cds)("Reading extra data: done.");
|
||||
if (SharedArchiveConfigFile) {
|
||||
log_info(cds)("Reading extra data from %s ...", SharedArchiveConfigFile);
|
||||
read_extra_data(THREAD, SharedArchiveConfigFile);
|
||||
log_info(cds)("Reading extra data: done.");
|
||||
}
|
||||
}
|
||||
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
log_info(cds)("Reading lambda form invokers from JDK default classlist ...");
|
||||
char default_classlist[JVM_MAXPATHLEN];
|
||||
get_default_classlist(default_classlist, sizeof(default_classlist));
|
||||
struct stat statbuf;
|
||||
if (os::stat(default_classlist, &statbuf) == 0) {
|
||||
ClassListParser::parse_classlist(default_classlist,
|
||||
ClassListParser::_parse_lambda_forms_invokers_only, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
if (ExtraSharedClassListFile) {
|
||||
log_info(cds)("Loading extra classes from %s ...", ExtraSharedClassListFile);
|
||||
ClassListParser::parse_classlist(ExtraSharedClassListFile,
|
||||
ClassListParser::_parse_all, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite and link classes
|
||||
@ -993,8 +1037,8 @@ bool MetaspaceShared::write_static_archive(ArchiveBuilder* builder, FileMapInfo*
|
||||
builder->write_archive(map_info, heap_info);
|
||||
|
||||
if (AllowArchivingWithJavaAgent) {
|
||||
log_warning(cds)("This archive was created with AllowArchivingWithJavaAgent. It should be used "
|
||||
"for testing purposes only and should not be used in a production environment");
|
||||
log_warning(cds)("This %s was created with AllowArchivingWithJavaAgent. It should be used "
|
||||
"for testing purposes only and should not be used in a production environment", CDSConfig::type_of_archive_being_loaded());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1004,7 +1048,13 @@ bool MetaspaceShared::try_link_class(JavaThread* current, InstanceKlass* ik) {
|
||||
ExceptionMark em(current);
|
||||
JavaThread* THREAD = current; // For exception macros.
|
||||
assert(CDSConfig::is_dumping_archive(), "sanity");
|
||||
if (!ik->is_shared() && ik->is_loaded() && !ik->is_linked() && ik->can_be_verified_at_dumptime() &&
|
||||
|
||||
if (ik->is_shared() && !CDSConfig::is_dumping_final_static_archive()) {
|
||||
assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ik->is_loaded() && !ik->is_linked() && ik->can_be_verified_at_dumptime() &&
|
||||
!SystemDictionaryShared::has_class_failed_verification(ik)) {
|
||||
bool saved = BytecodeVerificationLocal;
|
||||
if (ik->is_shared_unregistered_class() && ik->class_loader() == nullptr) {
|
||||
@ -1072,11 +1122,18 @@ bool MetaspaceShared::is_shared_static(void* p) {
|
||||
// - When -XX:+RequireSharedSpaces is specified, AND the JVM cannot load the archive(s) due
|
||||
// to version or classpath mismatch.
|
||||
void MetaspaceShared::unrecoverable_loading_error(const char* message) {
|
||||
log_error(cds)("An error has occurred while processing the shared archive file.");
|
||||
log_error(cds)("An error has occurred while processing the %s.", CDSConfig::type_of_archive_being_loaded());
|
||||
if (message != nullptr) {
|
||||
log_error(cds)("%s", message);
|
||||
}
|
||||
vm_exit_during_initialization("Unable to use shared archive.", nullptr);
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
vm_exit_during_initialization("Must be a valid AOT configuration generated by the current JVM", AOTConfiguration);
|
||||
} else if (CDSConfig::new_aot_flags_used()) {
|
||||
vm_exit_during_initialization("Unable to use AOT cache.", nullptr);
|
||||
} else {
|
||||
vm_exit_during_initialization("Unable to use shared archive.", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called when the JVM is unable to write the specified CDS archive due to an
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,7 +41,13 @@ class Method;
|
||||
class Symbol;
|
||||
|
||||
class RunTimeClassInfo {
|
||||
public:
|
||||
public:
|
||||
enum : char {
|
||||
FROM_FIELD_IS_PROTECTED = 1 << 0,
|
||||
FROM_IS_ARRAY = 1 << 1,
|
||||
FROM_IS_OBJECT = 1 << 2
|
||||
};
|
||||
|
||||
struct CrcInfo {
|
||||
int _clsfile_size;
|
||||
int _clsfile_crc32;
|
||||
@ -202,6 +208,17 @@ public:
|
||||
return verifier_constraint_flags()[i];
|
||||
}
|
||||
|
||||
bool from_field_is_protected(int i) {
|
||||
return (verifier_constraint_flag(i) & FROM_FIELD_IS_PROTECTED) != 0;
|
||||
}
|
||||
|
||||
bool from_is_array(int i) {
|
||||
return (verifier_constraint_flag(i) & FROM_IS_ARRAY) != 0;
|
||||
}
|
||||
bool from_is_object(int i) {
|
||||
return (verifier_constraint_flag(i) & FROM_IS_OBJECT) != 0;
|
||||
}
|
||||
|
||||
int num_enum_klass_static_fields(int i) const {
|
||||
return enum_klass_static_fields_addr()->_num;
|
||||
}
|
||||
|
||||
@ -413,7 +413,13 @@ ModuleEntry* ModuleEntry::allocate_archived_entry() const {
|
||||
_archive_modules_entries->put(this, archived_entry);
|
||||
DEBUG_ONLY(_num_archived_module_entries++);
|
||||
|
||||
assert(archived_entry->shared_protection_domain() == nullptr, "never set during -Xshare:dump");
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
OopHandle null_handle;
|
||||
archived_entry->_shared_pd = null_handle;
|
||||
} else {
|
||||
assert(archived_entry->shared_protection_domain() == nullptr, "never set during -Xshare:dump");
|
||||
}
|
||||
|
||||
// Clear handles and restore at run time. Handles cannot be archived.
|
||||
OopHandle null_handle;
|
||||
archived_entry->_module = null_handle;
|
||||
|
||||
@ -1177,6 +1177,10 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData
|
||||
|
||||
// notify a class loaded from shared object
|
||||
ClassLoadingService::notify_class_loaded(ik, true /* shared class */);
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
SystemDictionaryShared::init_dumptime_info_from_preimage(ik);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cds/aotClassLocation.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/archiveHeapLoader.hpp"
|
||||
#include "cds/archiveUtils.hpp"
|
||||
@ -193,23 +194,20 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread(
|
||||
// k must not be a shared class.
|
||||
DumpTimeClassInfo* SystemDictionaryShared::get_info(InstanceKlass* k) {
|
||||
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert(!k->is_shared(), "sanity");
|
||||
return get_info_locked(k);
|
||||
}
|
||||
|
||||
DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) {
|
||||
assert_lock_strong(DumpTimeTable_lock);
|
||||
assert(!k->is_shared(), "sanity");
|
||||
DumpTimeClassInfo* info = _dumptime_table->get_info(k);
|
||||
assert(info != nullptr, "must be");
|
||||
return info;
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||
if (MetaspaceShared::is_in_shared_metaspace(k)) {
|
||||
if (CDSConfig::is_dumping_dynamic_archive() && MetaspaceShared::is_in_shared_metaspace(k)) {
|
||||
// We have reached a super type that's already in the base archive. Treat it
|
||||
// as "not excluded".
|
||||
assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -277,6 +275,11 @@ bool SystemDictionaryShared::is_hidden_lambda_proxy(InstanceKlass* ik) {
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||||
if (CDSConfig::is_dumping_final_static_archive() && k->is_shared_unregistered_class()
|
||||
&& k->is_shared()) {
|
||||
return false; // Do not exclude: unregistered classes are passed from preimage to final image.
|
||||
}
|
||||
|
||||
if (k->is_in_error_state()) {
|
||||
return warn_excluded(k, "In error state");
|
||||
}
|
||||
@ -329,6 +332,10 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||||
// class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
|
||||
// causing the JVM to fail at bootstrap.
|
||||
return warn_excluded(k, "Unlinked class not supported by AOTClassLinking");
|
||||
} else if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
// When dumping the final static archive, we will unconditionally load and link all
|
||||
// classes from tje preimage. We don't want to get a VerifyError when linking this class.
|
||||
return warn_excluded(k, "Unlinked class not supported by AOTConfiguration");
|
||||
}
|
||||
} else {
|
||||
if (!k->can_be_verified_at_dumptime()) {
|
||||
@ -497,6 +504,9 @@ void SystemDictionaryShared::initialize() {
|
||||
_dumptime_table = new (mtClass) DumpTimeSharedClassTable;
|
||||
_dumptime_lambda_proxy_class_dictionary =
|
||||
new (mtClass) DumpTimeLambdaProxyClassDictionary;
|
||||
if (CDSConfig::is_dumping_heap()) {
|
||||
HeapShared::init_dumping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,6 +547,18 @@ void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
|
||||
}
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::init_dumptime_info_from_preimage(InstanceKlass* k) {
|
||||
init_dumptime_info(k);
|
||||
copy_verification_constraints_from_preimage(k);
|
||||
copy_linking_constraints_from_preimage(k);
|
||||
|
||||
if (SystemDictionary::is_platform_class_loader(k->class_loader())) {
|
||||
AOTClassLocationConfig::dumptime_set_has_platform_classes();
|
||||
} else if (SystemDictionary::is_system_class_loader(k->class_loader())) {
|
||||
AOTClassLocationConfig::dumptime_set_has_app_classes();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a class or any of its supertypes has been redefined.
|
||||
bool SystemDictionaryShared::has_been_redefined(InstanceKlass* k) {
|
||||
if (k->has_been_redefined()) {
|
||||
@ -723,7 +745,10 @@ void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) {
|
||||
assert_lock_strong(DumpTimeTable_lock);
|
||||
|
||||
auto do_klass = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
|
||||
if (k->is_loader_alive() && !info.is_excluded()) {
|
||||
if (CDSConfig::is_dumping_final_static_archive() && !k->is_loaded()) {
|
||||
assert(k->is_shared_unregistered_class(), "must be");
|
||||
info.metaspace_pointers_do(it);
|
||||
} else if (k->is_loader_alive() && !info.is_excluded()) {
|
||||
info.metaspace_pointers_do(it);
|
||||
}
|
||||
};
|
||||
@ -795,6 +820,10 @@ void SystemDictionaryShared::add_lambda_proxy_class(InstanceKlass* caller_ik,
|
||||
// There's no need to remember them in a separate table.
|
||||
return;
|
||||
}
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
// Information about lambda proxies are recorded in FinalImageRecipes.
|
||||
return;
|
||||
}
|
||||
|
||||
assert(caller_ik->class_loader() == lambda_ik->class_loader(), "mismatched class loader");
|
||||
assert(caller_ik->class_loader_data() == lambda_ik->class_loader_data(), "mismatched class loader data");
|
||||
@ -832,6 +861,10 @@ InstanceKlass* SystemDictionaryShared::get_shared_lambda_proxy_class(InstanceKla
|
||||
Symbol* method_type,
|
||||
Method* member_method,
|
||||
Symbol* instantiated_method_type) {
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(caller_ik != nullptr, "sanity");
|
||||
assert(invoked_name != nullptr, "sanity");
|
||||
assert(invoked_type != nullptr, "sanity");
|
||||
@ -956,7 +989,7 @@ InstanceKlass* SystemDictionaryShared::prepare_shared_lambda_proxy_class(Instanc
|
||||
|
||||
void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
|
||||
TRAPS) {
|
||||
assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
|
||||
assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
|
||||
RunTimeClassInfo* record = RunTimeClassInfo::get_for(klass);
|
||||
|
||||
int length = record->num_verifier_constraints();
|
||||
@ -965,21 +998,16 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
|
||||
RunTimeClassInfo::RTVerifierConstraint* vc = record->verifier_constraint_at(i);
|
||||
Symbol* name = vc->name();
|
||||
Symbol* from_name = vc->from_name();
|
||||
char c = record->verifier_constraint_flag(i);
|
||||
|
||||
if (log_is_enabled(Trace, cds, verification)) {
|
||||
ResourceMark rm(THREAD);
|
||||
log_trace(cds, verification)("check_verification_constraint: %s: %s must be subclass of %s [0x%x]",
|
||||
klass->external_name(), from_name->as_klass_external_name(),
|
||||
name->as_klass_external_name(), c);
|
||||
name->as_klass_external_name(), record->verifier_constraint_flag(i));
|
||||
}
|
||||
|
||||
bool from_field_is_protected = (c & SystemDictionaryShared::FROM_FIELD_IS_PROTECTED) ? true : false;
|
||||
bool from_is_array = (c & SystemDictionaryShared::FROM_IS_ARRAY) ? true : false;
|
||||
bool from_is_object = (c & SystemDictionaryShared::FROM_IS_OBJECT) ? true : false;
|
||||
|
||||
bool ok = VerificationType::resolve_and_check_assignability(klass, name,
|
||||
from_name, from_field_is_protected, from_is_array, from_is_object, CHECK);
|
||||
bool ok = VerificationType::resolve_and_check_assignability(klass, name, from_name,
|
||||
record->from_field_is_protected(i), record->from_is_array(i), record->from_is_object(i), CHECK);
|
||||
if (!ok) {
|
||||
ResourceMark rm(THREAD);
|
||||
stringStream ss;
|
||||
@ -995,6 +1023,24 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
|
||||
}
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::copy_verification_constraints_from_preimage(InstanceKlass* klass) {
|
||||
assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
|
||||
DumpTimeClassInfo* dt_info = get_info(klass);
|
||||
RunTimeClassInfo* rt_info = RunTimeClassInfo::get_for(klass); // from preimage
|
||||
|
||||
int length = rt_info->num_verifier_constraints();
|
||||
if (length > 0) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
RunTimeClassInfo::RTVerifierConstraint* vc = rt_info->verifier_constraint_at(i);
|
||||
Symbol* name = vc->name();
|
||||
Symbol* from_name = vc->from_name();
|
||||
|
||||
dt_info->add_verification_constraint(klass, name, from_name,
|
||||
rt_info->from_field_is_protected(i), rt_info->from_is_array(i), rt_info->from_is_object(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static oop get_class_loader_by(char type) {
|
||||
if (type == (char)ClassLoader::BOOT_LOADER) {
|
||||
return (oop)nullptr;
|
||||
@ -1066,7 +1112,7 @@ void SystemDictionaryShared::record_linking_constraint(Symbol* name, InstanceKla
|
||||
// returns true IFF there's no need to re-initialize the i/v-tables for klass for
|
||||
// the purpose of checking class loader constraints.
|
||||
bool SystemDictionaryShared::check_linking_constraints(Thread* current, InstanceKlass* klass) {
|
||||
assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
|
||||
assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
|
||||
LogTarget(Info, class, loader, constraints) log;
|
||||
if (klass->is_shared_boot_class()) {
|
||||
// No class loader constraint check performed for boot classes.
|
||||
@ -1112,6 +1158,24 @@ bool SystemDictionaryShared::check_linking_constraints(Thread* current, Instance
|
||||
return false;
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::copy_linking_constraints_from_preimage(InstanceKlass* klass) {
|
||||
assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
|
||||
JavaThread* current = JavaThread::current();
|
||||
if (klass->is_shared_platform_class() || klass->is_shared_app_class()) {
|
||||
RunTimeClassInfo* rt_info = RunTimeClassInfo::get_for(klass); // from preimage
|
||||
|
||||
if (rt_info->num_loader_constraints() > 0) {
|
||||
for (int i = 0; i < rt_info->num_loader_constraints(); i++) {
|
||||
RunTimeClassInfo::RTLoaderConstraint* lc = rt_info->loader_constraint_at(i);
|
||||
Symbol* name = lc->constraint_name();
|
||||
Handle loader1(current, get_class_loader_by(lc->_loader_type1));
|
||||
Handle loader2(current, get_class_loader_by(lc->_loader_type2));
|
||||
record_linking_constraint(name, klass, loader1, loader2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::is_supported_invokedynamic(BootstrapInfo* bsi) {
|
||||
LogTarget(Debug, cds, lambda) log;
|
||||
if (bsi->arg_values() == nullptr || !bsi->arg_values()->is_objArray()) {
|
||||
|
||||
@ -152,13 +152,6 @@ class SystemDictionaryShared: public SystemDictionary {
|
||||
void print_table_statistics(const char* prefix, outputStream* st);
|
||||
};
|
||||
|
||||
public:
|
||||
enum : char {
|
||||
FROM_FIELD_IS_PROTECTED = 1 << 0,
|
||||
FROM_IS_ARRAY = 1 << 1,
|
||||
FROM_IS_OBJECT = 1 << 2
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
static DumpTimeSharedClassTable* _dumptime_table;
|
||||
@ -199,6 +192,9 @@ private:
|
||||
static InstanceKlass* retrieve_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info) NOT_CDS_RETURN_(nullptr);
|
||||
DEBUG_ONLY(static bool _class_loading_may_happen;)
|
||||
|
||||
static void copy_verification_constraints_from_preimage(InstanceKlass* klass);
|
||||
static void copy_linking_constraints_from_preimage(InstanceKlass* klass);
|
||||
|
||||
public:
|
||||
static bool is_registered_lambda_proxy_class(InstanceKlass* ik);
|
||||
static bool is_hidden_lambda_proxy(InstanceKlass* ik);
|
||||
@ -229,6 +225,7 @@ public:
|
||||
|
||||
static void initialize() NOT_CDS_RETURN;
|
||||
static void init_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
||||
static void init_dumptime_info_from_preimage(InstanceKlass* k) NOT_CDS_RETURN;
|
||||
static void handle_class_unloading(InstanceKlass* k) NOT_CDS_RETURN;
|
||||
|
||||
static Dictionary* boot_loader_dictionary() {
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
#define NUM_CDS_REGIONS 4 // this must be the same as MetaspaceShared::n_regions
|
||||
#define CDS_ARCHIVE_MAGIC 0xf00baba2
|
||||
#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8
|
||||
#define CDS_PREIMAGE_ARCHIVE_MAGIC 0xcafea07c
|
||||
#define CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION 13
|
||||
#define CURRENT_CDS_ARCHIVE_VERSION 18
|
||||
|
||||
|
||||
@ -716,7 +716,6 @@ void Metaspace::global_initialize() {
|
||||
metaspace::ChunkHeaderPool::initialize();
|
||||
|
||||
if (CDSConfig::is_dumping_static_archive()) {
|
||||
assert(!CDSConfig::is_using_archive(), "sanity");
|
||||
MetaspaceShared::initialize_for_static_dump();
|
||||
}
|
||||
|
||||
|
||||
@ -454,6 +454,11 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive() && resolved_references() != nullptr) {
|
||||
objArrayOop scratch_references = oopFactory::new_objArray(vmClasses::Object_klass(), resolved_references()->length(), CHECK);
|
||||
HeapShared::add_scratch_resolved_references(this, scratch_references);
|
||||
}
|
||||
}
|
||||
|
||||
void ConstantPool::remove_unshareable_info() {
|
||||
|
||||
@ -2707,6 +2707,7 @@ void InstanceKlass::remove_unshareable_info() {
|
||||
}
|
||||
init_shared_package_entry();
|
||||
_dep_context_last_cleaned = 0;
|
||||
DEBUG_ONLY(_shared_class_load_count = 0);
|
||||
|
||||
remove_unshareable_flags();
|
||||
}
|
||||
|
||||
@ -1365,11 +1365,19 @@ void Arguments::set_mode_flags(Mode mode) {
|
||||
// incompatible command line options were chosen.
|
||||
void Arguments::no_shared_spaces(const char* message) {
|
||||
if (RequireSharedSpaces) {
|
||||
jio_fprintf(defaultStream::error_stream(),
|
||||
"Class data sharing is inconsistent with other specified options.\n");
|
||||
vm_exit_during_initialization("Unable to use shared archive", message);
|
||||
log_error(cds)("%s is incompatible with other specified options.",
|
||||
CDSConfig::new_aot_flags_used() ? "AOT cache" : "CDS");
|
||||
if (CDSConfig::new_aot_flags_used()) {
|
||||
vm_exit_during_initialization("Unable to use AOT cache", message);
|
||||
} else {
|
||||
vm_exit_during_initialization("Unable to use shared archive", message);
|
||||
}
|
||||
} else {
|
||||
log_info(cds)("Unable to use shared archive: %s", message);
|
||||
if (CDSConfig::new_aot_flags_used()) {
|
||||
log_warning(cds)("Unable to use AOT cache: %s", message);
|
||||
} else {
|
||||
log_info(cds)("Unable to use shared archive: %s", message);
|
||||
}
|
||||
UseSharedSpaces = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "cds/cds_globals.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/classListWriter.hpp"
|
||||
#include "cds/dynamicArchive.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
@ -440,6 +441,10 @@ void before_exit(JavaThread* thread, bool halt) {
|
||||
|
||||
#if INCLUDE_CDS
|
||||
ClassListWriter::write_resolved_constants();
|
||||
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
MetaspaceShared::preload_and_dump(thread);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Hang forever on exit if we're reporting an error.
|
||||
|
||||
@ -853,7 +853,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
_vm_complete = true;
|
||||
#endif
|
||||
|
||||
if (CDSConfig::is_dumping_static_archive()) {
|
||||
if (CDSConfig::is_dumping_classic_static_archive()) {
|
||||
// Classic -Xshare:dump, aka "old workflow"
|
||||
MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
|
||||
} else if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
tty->print_cr("Reading AOTConfiguration %s and writing AOTCache %s", AOTConfiguration, AOTCache);
|
||||
MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
|
||||
}
|
||||
|
||||
|
||||
@ -405,6 +405,7 @@ hotspot_cds_only = \
|
||||
|
||||
hotspot_appcds_dynamic = \
|
||||
runtime/cds/appcds/ \
|
||||
-runtime/cds/appcds/aotClassLinking \
|
||||
-runtime/cds/appcds/applications \
|
||||
-runtime/cds/appcds/cacheObject \
|
||||
-runtime/cds/appcds/complexURI \
|
||||
@ -423,7 +424,6 @@ hotspot_appcds_dynamic = \
|
||||
-runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.java \
|
||||
-runtime/cds/appcds/lambdaForm/DefaultClassListLFInvokers.java \
|
||||
-runtime/cds/appcds/methodHandles \
|
||||
-runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java \
|
||||
-runtime/cds/appcds/sharedStrings \
|
||||
-runtime/cds/appcds/resolvedConstants \
|
||||
-runtime/cds/appcds/ArchiveRelocationTest.java \
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 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
|
||||
@ -50,7 +50,7 @@ public class ArchiveDoesNotExist {
|
||||
// -Xshare=on
|
||||
OutputAnalyzer out = CDSTestUtils.runWithArchive(opts);
|
||||
CDSTestUtils.checkMappingFailure(out);
|
||||
out.shouldContain("Specified shared archive not found")
|
||||
out.shouldContain("Specified shared archive file not found")
|
||||
.shouldHaveExitValue(1);
|
||||
|
||||
// -Xshare=auto
|
||||
|
||||
@ -49,17 +49,21 @@ public class AOTFlags {
|
||||
}
|
||||
|
||||
static void positiveTests() throws Exception {
|
||||
// (1) Training Run
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Training Run");
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=record",
|
||||
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||
"-Xlog:cds=debug",
|
||||
"-cp", appJar, helloClass);
|
||||
|
||||
OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train");
|
||||
out.shouldContain("Hello World");
|
||||
out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// (2) Assembly Phase (AOTClassLinking unspecified -> should be enabled by default)
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Assembly Phase (AOTClassLinking unspecified -> should be enabled by default)");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=create",
|
||||
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||
@ -71,18 +75,20 @@ public class AOTFlags {
|
||||
out.shouldMatch("cds.*hello[.]aot");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// (3) Production Run with AOTCache
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Production Run with AOTCache");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"-Xlog:cds",
|
||||
"-cp", appJar, helloClass);
|
||||
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||
out.shouldContain("Using AOT-linked classes: true (static archive: has aot-linked classes)");
|
||||
out.shouldContain("Opened archive hello.aot.");
|
||||
out.shouldContain("Opened AOT cache hello.aot.");
|
||||
out.shouldContain("Hello World");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// (4) AOTMode=off
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("AOTMode=off");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"--show-version",
|
||||
@ -91,11 +97,12 @@ public class AOTFlags {
|
||||
"-cp", appJar, helloClass);
|
||||
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||
out.shouldNotContain(", sharing");
|
||||
out.shouldNotContain("Opened archive hello.aot.");
|
||||
out.shouldNotContain("Opened AOT cache hello.aot.");
|
||||
out.shouldContain("Hello World");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// (5) AOTMode=auto
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("AOTMode=auto");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"--show-version",
|
||||
@ -104,11 +111,12 @@ public class AOTFlags {
|
||||
"-cp", appJar, helloClass);
|
||||
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||
out.shouldContain(", sharing");
|
||||
out.shouldContain("Opened archive hello.aot.");
|
||||
out.shouldContain("Opened AOT cache hello.aot.");
|
||||
out.shouldContain("Hello World");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// (6) AOTMode=on
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("AOTMode=on");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"--show-version",
|
||||
@ -117,11 +125,12 @@ public class AOTFlags {
|
||||
"-cp", appJar, helloClass);
|
||||
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||
out.shouldContain(", sharing");
|
||||
out.shouldContain("Opened archive hello.aot.");
|
||||
out.shouldContain("Opened AOT cache hello.aot.");
|
||||
out.shouldContain("Hello World");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// (7) Assembly Phase with -XX:-AOTClassLinking
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Assembly Phase with -XX:-AOTClassLinking");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=create",
|
||||
"-XX:-AOTClassLinking",
|
||||
@ -134,20 +143,22 @@ public class AOTFlags {
|
||||
out.shouldMatch("cds.*hello[.]aot");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// (8) Production Run with AOTCache, which was created with -XX:-AOTClassLinking
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Production Run with AOTCache, which was created with -XX:-AOTClassLinking");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"-Xlog:cds",
|
||||
"-cp", appJar, helloClass);
|
||||
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||
out.shouldContain("Using AOT-linked classes: false (static archive: no aot-linked classes)");
|
||||
out.shouldContain("Opened archive hello.aot.");
|
||||
out.shouldContain("Opened AOT cache hello.aot.");
|
||||
out.shouldContain("Hello World");
|
||||
out.shouldHaveExitValue(0);
|
||||
}
|
||||
|
||||
static void negativeTests() throws Exception {
|
||||
// (1) Mixing old and new options
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Mixing old and new options");
|
||||
String mixOldNewErrSuffix = " cannot be used at the same time with -Xshare:on, -Xshare:auto, "
|
||||
+ "-Xshare:off, -Xshare:dump, DumpLoadedClassList, SharedClassListFile, "
|
||||
+ "or SharedArchiveFile";
|
||||
@ -169,7 +180,8 @@ public class AOTFlags {
|
||||
out.shouldContain("Option AOTCache" + mixOldNewErrSuffix);
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
// (2) Use AOTConfiguration without AOTMode
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Use AOTConfiguration without AOTMode");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||
"-cp", appJar, helloClass);
|
||||
@ -178,7 +190,8 @@ public class AOTFlags {
|
||||
out.shouldContain("AOTConfiguration can only be used with -XX:AOTMode=record or -XX:AOTMode=create");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
// (3) Use AOTMode without AOTConfiguration
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Use AOTMode without AOTConfiguration");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=record",
|
||||
"-cp", appJar, helloClass);
|
||||
@ -194,7 +207,8 @@ public class AOTFlags {
|
||||
out.shouldContain("-XX:AOTMode=create cannot be used without setting AOTConfiguration");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
// (4) Bad AOTMode
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Bad AOTMode");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=foo",
|
||||
"-cp", appJar, helloClass);
|
||||
@ -203,7 +217,8 @@ public class AOTFlags {
|
||||
out.shouldContain("Unrecognized value foo for AOTMode. Must be one of the following: off, record, create, auto, on");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
// (5) AOTCache specified with -XX:AOTMode=record
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("AOTCache specified with -XX:AOTMode=record");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=record",
|
||||
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||
@ -214,7 +229,8 @@ public class AOTFlags {
|
||||
out.shouldContain("AOTCache must not be specified when using -XX:AOTMode=record");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
// (5) AOTCache not specified with -XX:AOTMode=create
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("AOTCache not specified with -XX:AOTMode=create");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=create",
|
||||
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||
@ -224,5 +240,69 @@ public class AOTFlags {
|
||||
out.shouldContain("AOTCache must be specified when using -XX:AOTMode=create");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("No such config file");
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=create",
|
||||
"-XX:AOTConfiguration=no-such-file",
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"-cp", appJar, helloClass);
|
||||
|
||||
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||
out.shouldContain("Must be a valid AOT configuration generated by the current JVM: no-such-file");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("AOTConfiguration file cannot be used as a CDS archive");
|
||||
|
||||
// first make sure we have a valid aotConfigFile
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=record",
|
||||
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||
"-cp", appJar, helloClass);
|
||||
|
||||
out = CDSTestUtils.executeAndLog(pb, "train");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// Cannot use this config file as a AOT cache
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=on",
|
||||
"-XX:AOTCache=" + aotConfigFile,
|
||||
"-cp", appJar, helloClass);
|
||||
|
||||
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||
out.shouldContain("Not a valid AOT cache (hello.aotconfig)");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
// Cannot use this config file as a CDS archive
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + aotConfigFile,
|
||||
"-cp", appJar, helloClass);
|
||||
|
||||
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||
out.shouldContain("Not a valid archive (hello.aotconfig)");
|
||||
out.shouldNotHaveExitValue(0);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
printTestCase("Classpath mismatch when creating archive");
|
||||
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:AOTMode=create",
|
||||
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"-cp", "noSuchJar.jar");
|
||||
|
||||
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||
out.shouldContain("class path and/or module path are not compatible with the ones " +
|
||||
"specified when the AOTConfiguration file was recorded");
|
||||
out.shouldContain("Unable to use create AOT cache");
|
||||
out.shouldHaveExitValue(1);
|
||||
}
|
||||
|
||||
static int testNum = 0;
|
||||
static void printTestCase(String s) {
|
||||
System.out.println("vvvvvvv TEST CASE " + testNum + ": " + s + " starts here vvvvvvv");
|
||||
testNum++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test Make sure loader constraints are passed from AOT preimage to final image.
|
||||
* @bug 8348426
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @comment work around JDK-8345635
|
||||
* @requires !vm.jvmci.enabled
|
||||
* @library /test/jdk/lib/testlibrary /test/lib
|
||||
* @build AOTLoaderConstraintsTest BootClass
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar BootClass
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar AOTLoaderConstraintsTestApp.jar AOTLoaderConstraintsTestApp AppClass
|
||||
* @run driver AOTLoaderConstraintsTest AOT
|
||||
*/
|
||||
|
||||
import jdk.test.lib.cds.CDSAppTester;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public class AOTLoaderConstraintsTest {
|
||||
static final String appJar = ClassFileInstaller.getJarPath("AOTLoaderConstraintsTestApp.jar");
|
||||
static final String mainClass = "AOTLoaderConstraintsTestApp";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Tester t = new Tester();
|
||||
t.run(args);
|
||||
}
|
||||
|
||||
static class Tester extends CDSAppTester {
|
||||
public Tester() {
|
||||
super(mainClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String classpath(RunMode runMode) {
|
||||
return appJar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] vmArgs(RunMode runMode) {
|
||||
return new String[] {
|
||||
"-Xbootclasspath/a:boot.jar",
|
||||
"-Xlog:class+loader+constraints=debug",
|
||||
"-Xlog:class+path=debug",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] appCommandLine(RunMode runMode) {
|
||||
return new String[] {
|
||||
mainClass,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
|
||||
switch (runMode) {
|
||||
case RunMode.ASSEMBLY: // JEP 485 + binary AOTConfiguration -- should load AppClass from preimage
|
||||
case RunMode.PRODUCTION:
|
||||
out.shouldContain("CDS add loader constraint for class AppClass symbol java/lang/String loader[0] 'app' loader[1] 'bootstrap'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AOTLoaderConstraintsTestApp {
|
||||
public static void main(String args[]) throws Exception {
|
||||
AppClass obj = new AppClass();
|
||||
obj.func("Hello");
|
||||
}
|
||||
}
|
||||
|
||||
class AppClass extends BootClass {
|
||||
@Override
|
||||
public void func(String s) {
|
||||
// This method overrides BootClass, which is loaded by the boot loader.
|
||||
// AppClass is loaded by the app loader. To make sure that you cannot use
|
||||
// type masquerade attacks, we need to add a loader constraint that says:
|
||||
// app and boot loaders must resolve the symbol "java/lang/String" to the same type.
|
||||
super.func(s + " From AppClass");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
// This is a test class to be loaded by the boot loader.
|
||||
public class BootClass {
|
||||
public void func(String s) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
@ -53,6 +53,19 @@
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. BulkLoaderTest DYNAMIC
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=aot
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @comment work around JDK-8345635
|
||||
* @requires !vm.jvmci.enabled
|
||||
* @library /test/jdk/lib/testlibrary /test/lib
|
||||
* @build InitiatingLoaderTester BadOldClassA BadOldClassB
|
||||
* @build BulkLoaderTest
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester
|
||||
* BadOldClassA BadOldClassB
|
||||
* @run driver BulkLoaderTest AOT
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.util.List;
|
||||
@ -122,6 +135,13 @@ public class BulkLoaderTest {
|
||||
mainClass,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
|
||||
if (isAOTWorkflow() && runMode == RunMode.TRAINING) {
|
||||
out.shouldContain("Skipping BadOldClassA: Unlinked class not supported by AOTConfiguration");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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
|
||||
@ -40,6 +40,15 @@
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. JavacBench DYNAMIC
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=aot
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @summary Run JavacBenchApp with AOT cache (JEP 483)
|
||||
* @requires vm.cds
|
||||
* @library /test/lib
|
||||
* @run driver JavacBench AOT
|
||||
*/
|
||||
|
||||
import jdk.test.lib.cds.CDSAppTester;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
|
||||
|
||||
@ -188,7 +188,7 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
nonExistBaseFile.delete();
|
||||
runTwo(nonExistBase, topArchiveName,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistBase + ")");
|
||||
"Specified shared archive file not found (" + nonExistBase + ")");
|
||||
|
||||
startTest("9. Non-exist top archive");
|
||||
String nonExistTop = "non-exist-top.jsa";
|
||||
@ -196,11 +196,11 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
nonExistTopFile.delete();
|
||||
runTwo(baseArchiveName, nonExistTop,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistTop + ")");
|
||||
"Specified shared archive file not found (" + nonExistTop + ")");
|
||||
|
||||
startTest("10. nost-exist-base and non-exist-top");
|
||||
runTwo(nonExistBase, nonExistTop,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistBase + ")");
|
||||
"Specified shared archive file not found (" + nonExistBase + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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
|
||||
@ -663,7 +663,7 @@ public class TestAutoCreateSharedArchive extends DynamicArchiveTestBase {
|
||||
"-cp", appJar,
|
||||
mainAppClass)
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Specified shared archive not found (" + nonExistTop + ")")
|
||||
output.shouldContain("Specified shared archive file not found (" + nonExistTop + ")")
|
||||
.shouldContain(HELLO_WORLD)
|
||||
.shouldContain("Dumping shared data to file:");
|
||||
});
|
||||
@ -688,7 +688,7 @@ public class TestAutoCreateSharedArchive extends DynamicArchiveTestBase {
|
||||
"-cp", appJar,
|
||||
mainAppClass)
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Specified shared archive not found (" + nonExistBase + ")")
|
||||
output.shouldContain("Specified shared archive file not found (" + nonExistBase + ")")
|
||||
.shouldContain(HELLO_WORLD)
|
||||
.shouldNotContain("Dumping shared data to file:");
|
||||
});
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2023, 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
|
||||
@ -162,7 +162,7 @@ public class TestAutoCreateSharedArchiveUpgrade {
|
||||
}
|
||||
|
||||
static void assertJSANotFound(OutputAnalyzer output) {
|
||||
output.shouldContain("Specified shared archive not found");
|
||||
output.shouldContain("Specified shared archive file not found");
|
||||
}
|
||||
|
||||
static void assertCreatedJSA(OutputAnalyzer output) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -48,13 +48,12 @@ public class DumpingWithJavaAgent {
|
||||
};
|
||||
|
||||
public static String warningMessages[] = {
|
||||
"This archive was created with AllowArchivingWithJavaAgent",
|
||||
"This shared archive file was created with AllowArchivingWithJavaAgent",
|
||||
"It should be used for testing purposes only and should not be used in a production environment",
|
||||
};
|
||||
|
||||
public static String errorMessage =
|
||||
"The setting of the AllowArchivingWithJavaAgent is different from the setting in the shared archive.";
|
||||
|
||||
"The setting of the AllowArchivingWithJavaAgent is different from the setting in the shared archive file.";
|
||||
|
||||
public static String diagnosticOption = "-XX:+AllowArchivingWithJavaAgent";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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
|
||||
@ -40,8 +40,12 @@ abstract public class CDSAppTester {
|
||||
private final String name;
|
||||
private final String classListFile;
|
||||
private final String classListFileLog;
|
||||
private final String aotConfigurationFile;
|
||||
private final String aotConfigurationFileLog;
|
||||
private final String staticArchiveFile;
|
||||
private final String staticArchiveFileLog;
|
||||
private final String aotCacheFile;
|
||||
private final String aotCacheFileLog;
|
||||
private final String dynamicArchiveFile;
|
||||
private final String dynamicArchiveFileLog;
|
||||
private final String tempBaseArchiveFile;
|
||||
@ -56,8 +60,12 @@ abstract public class CDSAppTester {
|
||||
this.name = name;
|
||||
classListFile = name() + ".classlist";
|
||||
classListFileLog = classListFile + ".log";
|
||||
aotConfigurationFile = name() + ".aotconfig";
|
||||
aotConfigurationFileLog = aotConfigurationFile + ".log";
|
||||
staticArchiveFile = name() + ".static.jsa";
|
||||
staticArchiveFileLog = staticArchiveFile + ".log";
|
||||
aotCacheFile = name() + ".aot";
|
||||
aotCacheFileLog = aotCacheFile + ".log";
|
||||
dynamicArchiveFile = name() + ".dynamic.jsa";
|
||||
dynamicArchiveFileLog = dynamicArchiveFile + ".log";
|
||||
tempBaseArchiveFile = name() + ".temp-base.jsa";
|
||||
@ -74,13 +82,15 @@ abstract public class CDSAppTester {
|
||||
private enum Workflow {
|
||||
STATIC, // classic -Xshare:dump workflow
|
||||
DYNAMIC, // classic -XX:ArchiveClassesAtExit
|
||||
AOT, // JEP 483 Ahead-of-Time Class Loading & Linking
|
||||
}
|
||||
|
||||
public enum RunMode {
|
||||
CLASSLIST,
|
||||
DUMP_STATIC,
|
||||
DUMP_DYNAMIC,
|
||||
PRODUCTION;
|
||||
TRAINING, // -XX:DumpLoadedClassList OR {-XX:AOTMode=create -XX:AOTConfiguration}
|
||||
DUMP_STATIC, // -Xshare:dump
|
||||
DUMP_DYNAMIC, // -XX:ArchiveClassesArExit
|
||||
ASSEMBLY, // JEP 483
|
||||
PRODUCTION; // Running with the CDS archive produced from the above steps
|
||||
|
||||
public boolean isStaticDump() {
|
||||
return this == DUMP_STATIC;
|
||||
@ -126,6 +136,10 @@ abstract public class CDSAppTester {
|
||||
return workflow == Workflow.DYNAMIC;
|
||||
}
|
||||
|
||||
public final boolean isAOTWorkflow() {
|
||||
return workflow == Workflow.AOT;
|
||||
}
|
||||
|
||||
private String logToFile(String logFile, String... logTags) {
|
||||
StringBuilder sb = new StringBuilder("-Xlog:");
|
||||
String prefix = "";
|
||||
@ -163,8 +177,20 @@ abstract public class CDSAppTester {
|
||||
return output;
|
||||
}
|
||||
|
||||
private OutputAnalyzer recordAOTConfiguration() throws Exception {
|
||||
RunMode runMode = RunMode.TRAINING;
|
||||
String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
|
||||
"-XX:AOTMode=record",
|
||||
"-XX:AOTConfiguration=" + aotConfigurationFile,
|
||||
"-cp", classpath(runMode),
|
||||
logToFile(aotConfigurationFileLog,
|
||||
"class+load=debug"));
|
||||
cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
|
||||
return executeAndCheck(cmdLine, runMode, aotConfigurationFile, aotConfigurationFileLog);
|
||||
}
|
||||
|
||||
private OutputAnalyzer createClassList() throws Exception {
|
||||
RunMode runMode = RunMode.CLASSLIST;
|
||||
RunMode runMode = RunMode.TRAINING;
|
||||
String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
|
||||
"-Xshare:off",
|
||||
"-XX:DumpLoadedClassList=" + classListFile,
|
||||
@ -192,6 +218,23 @@ abstract public class CDSAppTester {
|
||||
return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog);
|
||||
}
|
||||
|
||||
private OutputAnalyzer createAOTCache() throws Exception {
|
||||
RunMode runMode = RunMode.ASSEMBLY;
|
||||
String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+heap=error",
|
||||
"-XX:AOTMode=create",
|
||||
"-XX:AOTConfiguration=" + aotConfigurationFile,
|
||||
"-XX:AOTCache=" + aotCacheFile,
|
||||
"-cp", classpath(runMode),
|
||||
logToFile(aotCacheFileLog,
|
||||
"cds=debug",
|
||||
"cds+class=debug",
|
||||
"cds+heap=warning",
|
||||
"cds+resolve=debug"));
|
||||
return executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog);
|
||||
}
|
||||
|
||||
// Creating a dynamic CDS archive (with -XX:ArchiveClassesAtExit=<foo>.jsa) requires that the current
|
||||
// JVM process is using a static archive (which is usually the default CDS archive included in the JDK).
|
||||
// However, if the JDK doesn't include a default CDS archive that's compatible with the set of
|
||||
@ -264,6 +307,8 @@ abstract public class CDSAppTester {
|
||||
cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + staticArchiveFile);
|
||||
} else if (isDynamicWorkflow()) {
|
||||
cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + dynamicArchiveFile);
|
||||
} else if (isAOTWorkflow()) {
|
||||
cmdLine = StringArrayUtils.concat(cmdLine, "-XX:AOTMode=on", "-XX:AOTCache=" + aotCacheFile);
|
||||
}
|
||||
|
||||
if (extraVmArgs != null) {
|
||||
@ -296,6 +341,8 @@ abstract public class CDSAppTester {
|
||||
runStaticWorkflow();
|
||||
} else if (args[0].equals("DYNAMIC")) {
|
||||
runDynamicWorkflow();
|
||||
} else if (args[0].equals("AOT")) {
|
||||
runAOTWorkflow();
|
||||
} else {
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
@ -314,4 +361,12 @@ abstract public class CDSAppTester {
|
||||
dumpDynamicArchive();
|
||||
productionRun();
|
||||
}
|
||||
|
||||
// See JEP 485
|
||||
private void runAOTWorkflow() throws Exception {
|
||||
this.workflow = Workflow.AOT;
|
||||
recordAOTConfiguration();
|
||||
createAOTCache();
|
||||
productionRun();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user