@@ -596,6 +599,37 @@ element of the appropriate @Artifact class. (See
JTREG="JAVA_OPTIONS=-Djdk.test.lib.artifacts.nsslib-linux_aarch64=/path/to/NSS-libs"
For more notes about the PKCS11 tests, please refer to
test/jdk/sun/security/pkcs11/README.
+
Testing Ahead-of-time
+Optimizations
+
One way to improve test coverage of ahead-of-time (AOT) optimizations
+in the JDK is to run existing jtreg test cases in a special "AOT_JDK"
+mode. Example:
+
$ make test JTREG="AOT_JDK=onestep" \
+ TEST=open/test/hotspot/jtreg/runtime/invokedynamic
+
In this testing mode, we first perform an AOT training run (see
+https://openjdk.org/jeps/483) of a special test program (test/setup_aot/TestSetupAOT.java)
+that accesses about 5,0000 classes in the JDK core libraries.
+Optimization artifacts for these classes (such as pre-linked lambda
+expressions, execution profiles, and pre-generated native code) are
+stored into an AOT cache file, which will be used by all the JVMs
+launched by the selected jtreg test cases.
+
When the jtreg tests call into the core libraries classes that are in
+the AOT cache, we will be able to test the AOT optimizations that were
+used on those classes.
Also, test cases that were written specifically to test AOT, such as
+the tests under test/hotspot/jtreg/runtime/cds,
+cannot be executed with the AOT_JDK mode.
+
Valid values for AOT_JDK are onestep and
+twostep. These control how the AOT cache is generated. See
+https://openjdk.org/jeps/514 for details. All other values are
+ignored.
Testing with
alternative security providers
Some security tests use a hardcoded provider for
diff --git a/doc/testing.md b/doc/testing.md
index f6a25457b5e..bb56c05c295 100644
--- a/doc/testing.md
+++ b/doc/testing.md
@@ -611,6 +611,43 @@ $ make test TEST="jtreg:sun/security/pkcs11/Secmod/AddTrustedCert.java" \
For more notes about the PKCS11 tests, please refer to
test/jdk/sun/security/pkcs11/README.
+### Testing Ahead-of-time Optimizations
+-------------------------------------------------------------------------------
+One way to improve test coverage of ahead-of-time (AOT) optimizations in
+the JDK is to run existing jtreg test cases in a special "AOT_JDK" mode.
+Example:
+
+```
+$ make test JTREG="AOT_JDK=onestep" \
+ TEST=open/test/hotspot/jtreg/runtime/invokedynamic
+```
+
+In this testing mode, we first perform an AOT training run
+(see https://openjdk.org/jeps/483) of a special test program
+([test/setup_aot/TestSetupAOT.java](../test/setup_aot/TestSetupAOT.java))
+that accesses about 5,0000 classes in the JDK core libraries.
+Optimization artifacts for these classes (such as pre-linked
+lambda expressions, execution profiles, and pre-generated native code)
+are stored into an AOT cache file, which will be used by all the JVMs
+launched by the selected jtreg test cases.
+
+When the jtreg tests call into the core libraries classes that are in
+the AOT cache, we will be able to test the AOT optimizations that were
+used on those classes.
+
+Please note that not all existing jtreg test cases can be executed with
+the AOT_JDK mode. See
+[test/hotspot/jtreg/ProblemList-AotJdk.txt](../test/hotspot/jtreg/ProblemList-AotJdk.txt)
+and [test/jdk/ProblemList-AotJdk.txt](../test/jdk/ProblemList-AotJdk.txt).
+
+Also, test cases that were written specifically to test AOT, such as the tests
+under [test/hotspot/jtreg/runtime/cds](../test/hotspot/jtreg/runtime/cds/),
+cannot be executed with the AOT_JDK mode.
+
+Valid values for `AOT_JDK` are `onestep` and `twostep`. These control how
+the AOT cache is generated. See https://openjdk.org/jeps/514 for details.
+All other values are ignored.
+
### Testing with alternative security providers
Some security tests use a hardcoded provider for `KeyFactory`, `Cipher`,
diff --git a/make/RunTests.gmk b/make/RunTests.gmk
index 51c75cc4872..60ae1bd4763 100644
--- a/make/RunTests.gmk
+++ b/make/RunTests.gmk
@@ -725,6 +725,7 @@ endef
# Parameter 1 is the name of the rule.
#
# Remaining parameters are named arguments.
+# TRAINING The AOT training mode: onestep or twostep
# VM_OPTIONS List of JVM arguments to use when creating AOT cache
#
# After calling this, the following variables are defined
@@ -753,23 +754,39 @@ define SetupAOTBody
$$($1_AOT_JDK_CACHE): $$(JDK_IMAGE_DIR)/release
$$(call MakeDir, $$($1_AOT_JDK_OUTPUT_DIR))
- $$(call LogWarn, AOT: Create cache configuration) \
- $$(call ExecuteWithLog, $$($1_AOT_JDK_OUTPUT_DIR), ( \
- cd $$($1_AOT_JDK_OUTPUT_DIR); \
- $(JAR) --extract --file $(TEST_IMAGE_DIR)/setup_aot/TestSetupAOT.jar; \
- $$(FIXPATH) $(JDK_UNDER_TEST)/bin/java $$($1_VM_OPTIONS) \
- -Xlog:class+load,aot,aot+class=debug:file=$$($1_AOT_JDK_CONF).log -Xlog:cds*=error -Xlog:aot*=error \
- -XX:AOTMode=record -XX:AOTConfiguration=$$($1_AOT_JDK_CONF) \
- TestSetupAOT $$($1_AOT_JDK_OUTPUT_DIR) > $$($1_AOT_JDK_LOG) \
- ))
+ ifeq ($$($1_TRAINING), onestep)
- $$(call LogWarn, AOT: Generate AOT cache $$($1_AOT_JDK_CACHE) with flags: $$($1_VM_OPTIONS))
- $$(call ExecuteWithLog, $$($1_AOT_JDK_OUTPUT_DIR), ( \
- $$(FIXPATH) $(JDK_UNDER_TEST)/bin/java \
- $$($1_VM_OPTIONS) -Xlog:aot,aot+class=debug:file=$$($1_AOT_JDK_CACHE).log -Xlog:cds*=error -Xlog:aot*=error \
- -XX:ExtraSharedClassListFile=$(JDK_UNDER_TEST)/lib/classlist \
- -XX:AOTMode=create -XX:AOTConfiguration=$$($1_AOT_JDK_CONF) -XX:AOTCache=$$($1_AOT_JDK_CACHE) \
- ))
+ $$(call LogWarn, AOT: Create AOT cache $$($1_AOT_JDK_CACHE) in one step with flags: $$($1_VM_OPTIONS)) \
+ $$(call ExecuteWithLog, $$($1_AOT_JDK_OUTPUT_DIR), ( \
+ cd $$($1_AOT_JDK_OUTPUT_DIR); \
+ $(JAR) --extract --file $(TEST_IMAGE_DIR)/setup_aot/TestSetupAOT.jar; \
+ $$(FIXPATH) $(JDK_UNDER_TEST)/bin/java $$($1_VM_OPTIONS) \
+ -Xlog:class+load,aot,aot+class=debug:file=$$($1_AOT_JDK_CACHE).log -Xlog:cds*=error -Xlog:aot*=error \
+ -XX:AOTMode=record -XX:AOTCacheOutput=$$($1_AOT_JDK_CACHE) \
+ TestSetupAOT $$($1_AOT_JDK_OUTPUT_DIR) > $$($1_AOT_JDK_LOG) \
+ ))
+
+ else
+
+ $$(call LogWarn, AOT: Create cache configuration) \
+ $$(call ExecuteWithLog, $$($1_AOT_JDK_OUTPUT_DIR), ( \
+ cd $$($1_AOT_JDK_OUTPUT_DIR); \
+ $(JAR) --extract --file $(TEST_IMAGE_DIR)/setup_aot/TestSetupAOT.jar; \
+ $$(FIXPATH) $(JDK_UNDER_TEST)/bin/java $$($1_VM_OPTIONS) \
+ -Xlog:class+load,aot,aot+class=debug:file=$$($1_AOT_JDK_CONF).log -Xlog:cds*=error -Xlog:aot*=error \
+ -XX:AOTMode=record -XX:AOTConfiguration=$$($1_AOT_JDK_CONF) \
+ TestSetupAOT $$($1_AOT_JDK_OUTPUT_DIR) > $$($1_AOT_JDK_LOG) \
+ ))
+
+ $$(call LogWarn, AOT: Generate AOT cache $$($1_AOT_JDK_CACHE) with flags: $$($1_VM_OPTIONS))
+ $$(call ExecuteWithLog, $$($1_AOT_JDK_OUTPUT_DIR), ( \
+ $$(FIXPATH) $(JDK_UNDER_TEST)/bin/java \
+ $$($1_VM_OPTIONS) -Xlog:aot,aot+class=debug:file=$$($1_AOT_JDK_CACHE).log -Xlog:cds*=error -Xlog:aot*=error \
+ -XX:ExtraSharedClassListFile=$(JDK_UNDER_TEST)/lib/classlist \
+ -XX:AOTMode=create -XX:AOTConfiguration=$$($1_AOT_JDK_CONF) -XX:AOTCache=$$($1_AOT_JDK_CACHE) \
+ ))
+
+ endif
$1_AOT_TARGETS += $$($1_AOT_JDK_CACHE)
@@ -835,7 +852,7 @@ define SetupRunJtregTestBody
JTREG_RETRY_COUNT ?= 0
JTREG_REPEAT_COUNT ?= 0
JTREG_REPORT ?= files
- JTREG_AOT_JDK ?= false
+ JTREG_AOT_JDK ?= none
ifneq ($$(JTREG_RETRY_COUNT), 0)
ifneq ($$(JTREG_REPEAT_COUNT), 0)
@@ -975,12 +992,12 @@ define SetupRunJtregTestBody
endif
endif
- ifeq ($$(JTREG_AOT_JDK), true)
+ ifneq ($$(filter $$(JTREG_AOT_JDK), onestep twostep), )
$$(call LogWarn, Add AOT target for $1)
- $$(eval $$(call SetupAOT, $1, VM_OPTIONS := $$(JTREG_ALL_OPTIONS) ))
-
+ $$(eval $$(call SetupAOT, $1, \
+ TRAINING := $$(JTREG_AOT_JDK), \
+ VM_OPTIONS := $$(JTREG_ALL_OPTIONS) ))
$$(call LogWarn, AOT_JDK_CACHE=$$($1_AOT_JDK_CACHE))
-
$1_JTREG_BASIC_OPTIONS += -vmoption:-XX:AOTCache="$$($1_AOT_JDK_CACHE)"
endif
diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp
index c8888fe6bb3..b3c13b63aff 100644
--- a/src/hotspot/share/cds/cdsConfig.cpp
+++ b/src/hotspot/share/cds/cdsConfig.cpp
@@ -49,6 +49,8 @@ bool CDSConfig::_is_using_optimized_module_handling = true;
bool CDSConfig::_is_dumping_full_module_graph = true;
bool CDSConfig::_is_using_full_module_graph = true;
bool CDSConfig::_has_aot_linked_classes = false;
+bool CDSConfig::_is_single_command_training = false;
+bool CDSConfig::_has_temp_aot_config_file = false;
bool CDSConfig::_old_cds_flags_used = false;
bool CDSConfig::_new_aot_flags_used = false;
bool CDSConfig::_disable_heap_dumping = false;
@@ -398,12 +400,14 @@ void CDSConfig::check_aot_flags() {
_old_cds_flags_used = true;
}
- // "New" AOT flags must not be mixed with "classic" flags such as -Xshare:dump
+ // "New" AOT flags must not be mixed with "classic" CDS flags such as -Xshare:dump
CHECK_NEW_FLAG(AOTCache);
+ CHECK_NEW_FLAG(AOTCacheOutput);
CHECK_NEW_FLAG(AOTConfiguration);
CHECK_NEW_FLAG(AOTMode);
CHECK_SINGLE_PATH(AOTCache);
+ CHECK_SINGLE_PATH(AOTCacheOutput);
CHECK_SINGLE_PATH(AOTConfiguration);
if (FLAG_IS_DEFAULT(AOTCache) && AOTAdapterCaching) {
@@ -413,29 +417,41 @@ void CDSConfig::check_aot_flags() {
log_debug(aot,codecache,init)("AOTCache is not specified - AOTStubCaching is ignored");
}
- if (FLAG_IS_DEFAULT(AOTCache) && FLAG_IS_DEFAULT(AOTConfiguration) && FLAG_IS_DEFAULT(AOTMode)) {
- // AOTCache/AOTConfiguration/AOTMode not used -> using the "classic CDS" workflow.
+ bool has_cache = !FLAG_IS_DEFAULT(AOTCache);
+ bool has_cache_output = !FLAG_IS_DEFAULT(AOTCacheOutput);
+ bool has_config = !FLAG_IS_DEFAULT(AOTConfiguration);
+ bool has_mode = !FLAG_IS_DEFAULT(AOTMode);
+
+ if (!has_cache && !has_cache_output && !has_config && !has_mode) {
+ // AOT flags are not used. Use classic CDS workflow
return;
- } else {
- _new_aot_flags_used = true;
}
+ if (has_cache && has_cache_output) {
+ vm_exit_during_initialization("Only one of AOTCache or AOTCacheOutput can be specified");
+ }
+
+ if (!has_cache && (!has_mode || strcmp(AOTMode, "auto") == 0)) {
+ if (has_cache_output) {
+ // If AOTCacheOutput has been set, effective mode is "record".
+ // Default value for AOTConfiguration, if necessary, will be assigned in check_aotmode_record().
+ log_info(aot)("Selected AOTMode=record because AOTCacheOutput is specified");
+ FLAG_SET_ERGO(AOTMode, "record");
+ }
+ }
+
+ // At least one AOT flag has been used
+ _new_aot_flags_used = true;
+
if (FLAG_IS_DEFAULT(AOTMode) || strcmp(AOTMode, "auto") == 0 || strcmp(AOTMode, "on") == 0) {
check_aotmode_auto_or_on();
} else if (strcmp(AOTMode, "off") == 0) {
check_aotmode_off();
+ } else if (strcmp(AOTMode, "record") == 0) {
+ check_aotmode_record();
} else {
- // AOTMode is record or create
- if (FLAG_IS_DEFAULT(AOTConfiguration)) {
- vm_exit_during_initialization(err_msg("-XX:AOTMode=%s cannot be used without setting AOTConfiguration", AOTMode));
- }
-
- if (strcmp(AOTMode, "record") == 0) {
- check_aotmode_record();
- } else {
- assert(strcmp(AOTMode, "create") == 0, "checked by AOTModeConstraintFunc");
- check_aotmode_create();
- }
+ assert(strcmp(AOTMode, "create") == 0, "checked by AOTModeConstraintFunc");
+ check_aotmode_create();
}
// This is an old flag used by CDS regression testing only. It doesn't apply
@@ -450,7 +466,8 @@ void CDSConfig::check_aotmode_off() {
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");
+ vm_exit_during_initialization(err_msg("AOTConfiguration can only be used with when AOTMode is record or create (selected AOTMode = %s)",
+ FLAG_IS_DEFAULT(AOTMode) ? "auto" : AOTMode));
}
UseSharedSpaces = true;
@@ -462,11 +479,63 @@ void CDSConfig::check_aotmode_auto_or_on() {
}
}
+// %p substitution in AOTCache, AOTCacheOutput and AOTCacheConfiguration
+static void substitute_aot_filename(JVMFlagsEnum flag_enum) {
+ JVMFlag* flag = JVMFlag::flag_from_enum(flag_enum);
+ const char* filename = flag->read();
+ assert(filename != nullptr, "must not have default value");
+
+ // For simplicity, we don't allow %p/%t to be specified twice, because make_log_name()
+ // substitutes only the first occurrence. Otherwise, if we run with
+ // java -XX:AOTCacheOutput=%p%p.aot
+ // it will end up with both the pid of the training process and the assembly process.
+ const char* first_p = strstr(filename, "%p");
+ if (first_p != nullptr && strstr(first_p + 2, "%p") != nullptr) {
+ vm_exit_during_initialization(err_msg("%s cannot contain more than one %%p", flag->name()));
+ }
+ const char* first_t = strstr(filename, "%t");
+ if (first_t != nullptr && strstr(first_t + 2, "%t") != nullptr) {
+ vm_exit_during_initialization(err_msg("%s cannot contain more than one %%t", flag->name()));
+ }
+
+ // Note: with single-command training, %p will be the pid of the training process, not the
+ // assembly process.
+ const char* new_filename = make_log_name(filename, nullptr);
+ if (strcmp(filename, new_filename) != 0) {
+ JVMFlag::Error err = JVMFlagAccess::set_ccstr(flag, &new_filename, JVMFlagOrigin::ERGONOMIC);
+ assert(err == JVMFlag::SUCCESS, "must never fail");
+ }
+ FREE_C_HEAP_ARRAY(char, new_filename);
+}
+
void CDSConfig::check_aotmode_record() {
+ bool has_config = !FLAG_IS_DEFAULT(AOTConfiguration);
+ bool has_output = !FLAG_IS_DEFAULT(AOTCacheOutput);
+
+ if (!has_output && !has_config) {
+ vm_exit_during_initialization("At least one of AOTCacheOutput and AOTConfiguration must be specified when using -XX:AOTMode=record");
+ }
+
+ if (has_output) {
+ _is_single_command_training = true;
+ substitute_aot_filename(FLAG_MEMBER_ENUM(AOTCacheOutput));
+ if (!has_config) {
+ // Too early; can't use resource allocation yet.
+ size_t len = strlen(AOTCacheOutput) + 10;
+ char* temp = AllocateHeap(len, mtArguments);
+ jio_snprintf(temp, len, "%s.config", AOTCacheOutput);
+ FLAG_SET_ERGO(AOTConfiguration, temp);
+ FreeHeap(temp);
+ _has_temp_aot_config_file = true;
+ }
+ }
+
if (!FLAG_IS_DEFAULT(AOTCache)) {
vm_exit_during_initialization("AOTCache must not be specified when using -XX:AOTMode=record");
}
+ substitute_aot_filename(FLAG_MEMBER_ENUM(AOTConfiguration));
+
UseSharedSpaces = false;
RequireSharedSpaces = false;
_is_dumping_static_archive = true;
@@ -478,10 +547,27 @@ void CDSConfig::check_aotmode_record() {
}
void CDSConfig::check_aotmode_create() {
- if (FLAG_IS_DEFAULT(AOTCache)) {
- vm_exit_during_initialization("AOTCache must be specified when using -XX:AOTMode=create");
+ if (FLAG_IS_DEFAULT(AOTConfiguration)) {
+ vm_exit_during_initialization("AOTConfiguration must be specified when using -XX:AOTMode=create");
}
+ bool has_cache = !FLAG_IS_DEFAULT(AOTCache);
+ bool has_cache_output = !FLAG_IS_DEFAULT(AOTCacheOutput);
+
+ assert(!(has_cache && has_cache_output), "already checked");
+
+ if (!has_cache && !has_cache_output) {
+ vm_exit_during_initialization("AOTCache or AOTCacheOutput must be specified when using -XX:AOTMode=create");
+ }
+
+ if (!has_cache) {
+ precond(has_cache_output);
+ FLAG_SET_ERGO(AOTCache, AOTCacheOutput);
+ }
+ // No need to check for (!has_cache_output), as we don't look at AOTCacheOutput after here.
+
+ substitute_aot_filename(FLAG_MEMBER_ENUM(AOTCache));
+
_is_dumping_final_static_archive = true;
UseSharedSpaces = true;
RequireSharedSpaces = true;
diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp
index 22378f5c42c..1fd229ff34f 100644
--- a/src/hotspot/share/cds/cdsConfig.hpp
+++ b/src/hotspot/share/cds/cdsConfig.hpp
@@ -41,6 +41,8 @@ class CDSConfig : public AllStatic {
static bool _is_dumping_full_module_graph;
static bool _is_using_full_module_graph;
static bool _has_aot_linked_classes;
+ static bool _is_single_command_training;
+ static bool _has_temp_aot_config_file;
const static char* _default_archive_path;
const static char* _input_static_archive_path;
@@ -142,6 +144,9 @@ public:
// Misc CDS features
static bool allow_only_single_java_thread() NOT_CDS_RETURN_(false);
+ static bool is_single_command_training() { return CDS_ONLY(_is_single_command_training) NOT_CDS(false); }
+ static bool has_temp_aot_config_file() { return CDS_ONLY(_has_temp_aot_config_file) NOT_CDS(false); }
+
// This is *Legacy* optimization for lambdas before JEP 483. May be removed in the future.
static bool is_dumping_lambdas_in_legacy_mode() NOT_CDS_RETURN_(false);
diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp
index aab5647e0b8..e51dd26ff06 100644
--- a/src/hotspot/share/cds/cds_globals.hpp
+++ b/src/hotspot/share/cds/cds_globals.hpp
@@ -119,6 +119,9 @@
"Cache for improving start up and warm up") \
constraint(AOTCacheConstraintFunc, AtParse) \
\
+ product(ccstr, AOTCacheOutput, nullptr, \
+ "Specifies the file name for writing the AOT cache") \
+ \
product(bool, AOTInvokeDynamicLinking, false, DIAGNOSTIC, \
"AOT-link JVM_CONSTANT_InvokeDynamic entries in cached " \
"ConstantPools") \
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index 7094e1c326d..375a3ccffbc 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -744,7 +744,7 @@ void FileMapInfo::open_as_output() {
if (CDSConfig::is_dumping_preimage_static_archive()) {
log_info(aot)("Writing binary AOTConfiguration file: %s", _full_path);
} else {
- log_info(aot)("Writing binary AOTConfiguration file: %s", _full_path);
+ log_info(aot)("Writing AOTCache file: %s", _full_path);
}
} else {
aot_log_info(aot)("Dumping shared data to file: %s", _full_path);
diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp
index 4e82395dba9..233e64c5e3e 100644
--- a/src/hotspot/share/cds/metaspaceShared.cpp
+++ b/src/hotspot/share/cds/metaspaceShared.cpp
@@ -72,6 +72,7 @@
#include "memory/memoryReserver.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspaceClosure.hpp"
+#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "nmt/memTracker.hpp"
@@ -828,7 +829,6 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
// We are in the JVM that runs the training run. Continue execution,
// so that it can finish all clean-up and return the correct exit
// code to the OS.
- tty->print_cr("AOTConfiguration recorded: %s", AOTConfiguration);
} else {
// The JLI launcher only recognizes the "old" -Xshare:dump flag.
// When the new -XX:AOTMode=create flag is used, we can't return
@@ -1027,7 +1027,16 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS
CDSConfig::disable_dumping_aot_code();
}
- if (!write_static_archive(&builder, op.map_info(), op.heap_info())) {
+ bool status = write_static_archive(&builder, op.map_info(), op.heap_info());
+ if (status && CDSConfig::is_dumping_preimage_static_archive()) {
+ tty->print_cr("%s AOTConfiguration recorded: %s",
+ CDSConfig::has_temp_aot_config_file() ? "Temporary" : "", AOTConfiguration);
+ if (CDSConfig::is_single_command_training()) {
+ fork_and_dump_final_static_archive(CHECK);
+ }
+ }
+
+ if (!status) {
THROW_MSG(vmSymbols::java_io_IOException(), "Encountered error while dumping");
}
}
@@ -1050,6 +1059,132 @@ bool MetaspaceShared::write_static_archive(ArchiveBuilder* builder, FileMapInfo*
return true;
}
+static void print_java_launcher(outputStream* st) {
+ st->print("%s%sbin%sjava", Arguments::get_java_home(), os::file_separator(), os::file_separator());
+}
+
+static void append_args(GrowableArray* args, const char* arg, TRAPS) {
+ Handle string = java_lang_String::create_from_str(arg, CHECK);
+ args->append(string);
+}
+
+// Pass all options in Arguments::jvm_args_array() to a child JVM process
+// using the JAVA_TOOL_OPTIONS environment variable.
+static int exec_jvm_with_java_tool_options(const char* java_launcher_path, TRAPS) {
+ ResourceMark rm(THREAD);
+ HandleMark hm(THREAD);
+ GrowableArray args;
+
+ const char* cp = Arguments::get_appclasspath();
+ if (cp != nullptr && strlen(cp) > 0 && strcmp(cp, ".") != 0) {
+ // We cannot use "-cp", because "-cp" is only interpreted by the java launcher,
+ // and is not interpreter by arguments.cpp when it loads args from JAVA_TOOL_OPTIONS
+ stringStream ss;
+ ss.print("-Djava.class.path=");
+ ss.print_raw(cp);
+ append_args(&args, ss.freeze(), CHECK_0);
+ // CDS$ProcessLauncher::execWithJavaToolOptions() must unset CLASSPATH, which has
+ // a higher priority than -Djava.class.path=
+ }
+
+ // Pass all arguments. These include those from JAVA_TOOL_OPTIONS and _JAVA_OPTIONS.
+ for (int i = 0; i < Arguments::num_jvm_args(); i++) {
+ const char* arg = Arguments::jvm_args_array()[i];
+ if (strstr(arg, "-XX:AOTCacheOutput=") == arg || // arg starts with ...
+ strstr(arg, "-XX:AOTConfiguration=") == arg ||
+ strstr(arg, "-XX:AOTMode=") == arg) {
+ // Filter these out. They wiill be set below.
+ } else {
+ append_args(&args, arg, CHECK_0);
+ }
+ }
+
+ // Note: because we are running in AOTMode=record, JDK_AOT_VM_OPTIONS have not been
+ // parsed, so they are not in Arguments::jvm_args_array. If JDK_AOT_VM_OPTIONS is in
+ // the environment, it will be inherited and parsed by the child JVM process
+ // in Arguments::parse_java_tool_options_environment_variable().
+ precond(strcmp(AOTMode, "record") == 0);
+
+ // We don't pass Arguments::jvm_flags_array(), as those will be added by
+ // the child process when it loads .hotspotrc
+
+ {
+ // If AOTCacheOutput contains %p, it should have been already substituted with the
+ // pid of the training process.
+ stringStream ss;
+ ss.print("-XX:AOTCacheOutput=");
+ ss.print_raw(AOTCacheOutput);
+ append_args(&args, ss.freeze(), CHECK_0);
+ }
+ {
+ // If AOTCacheConfiguration contains %p, it should have been already substituted with the
+ // pid of the training process.
+ // If AOTCacheConfiguration was not explicitly specified, it should have been assigned a
+ // temporary file name.
+ stringStream ss;
+ ss.print("-XX:AOTConfiguration=");
+ ss.print_raw(AOTConfiguration);
+ append_args(&args, ss.freeze(), CHECK_0);
+ }
+
+ append_args(&args, "-XX:AOTMode=create", CHECK_0);
+
+ Symbol* klass_name = SymbolTable::new_symbol("jdk/internal/misc/CDS$ProcessLauncher");
+ Klass* k = SystemDictionary::resolve_or_fail(klass_name, true, CHECK_0);
+ Symbol* methodName = SymbolTable::new_symbol("execWithJavaToolOptions");
+ Symbol* methodSignature = SymbolTable::new_symbol("(Ljava/lang/String;[Ljava/lang/String;)I");
+
+ Handle launcher = java_lang_String::create_from_str(java_launcher_path, CHECK_0);
+ objArrayOop array = oopFactory::new_objArray(vmClasses::String_klass(), args.length(), CHECK_0);
+ for (int i = 0; i < args.length(); i++) {
+ array->obj_at_put(i, args.at(i)());
+ }
+ objArrayHandle launcher_args(THREAD, array);
+
+ // The following call will pass all options inside the JAVA_TOOL_OPTIONS env variable to
+ // the child process. It will also clear the _JAVA_OPTIONS and CLASSPATH env variables for
+ // the child process.
+ //
+ // Note: the env variables are set only for the child process. They are not changed
+ // for the current process. See java.lang.ProcessBuilder::environment().
+ JavaValue result(T_OBJECT);
+ JavaCallArguments javacall_args(2);
+ javacall_args.push_oop(launcher);
+ javacall_args.push_oop(launcher_args);
+ JavaCalls::call_static(&result,
+ InstanceKlass::cast(k),
+ methodName,
+ methodSignature,
+ &javacall_args,
+ CHECK_0);
+ return result.get_jint();
+}
+
+void MetaspaceShared::fork_and_dump_final_static_archive(TRAPS) {
+ assert(CDSConfig::is_dumping_preimage_static_archive(), "sanity");
+
+ ResourceMark rm;
+ stringStream ss;
+ print_java_launcher(&ss);
+ const char* cmd = ss.freeze();
+ tty->print_cr("Launching child process %s to assemble AOT cache %s using configuration %s", cmd, AOTCacheOutput, AOTConfiguration);
+ int status = exec_jvm_with_java_tool_options(cmd, CHECK);
+ if (status != 0) {
+ log_error(aot)("Child process failed; status = %d", status);
+ // We leave the temp config file for debugging
+ } else if (CDSConfig::has_temp_aot_config_file()) {
+ const char* tmp_config = AOTConfiguration;
+ // On Windows, need WRITE permission to remove the file.
+ WINDOWS_ONLY(chmod(tmp_config, _S_IREAD | _S_IWRITE));
+ status = remove(tmp_config);
+ if (status != 0) {
+ log_error(aot)("Failed to remove temporary AOT configuration file %s", tmp_config);
+ } else {
+ tty->print_cr("Removed temporary AOT configuration file %s", tmp_config);
+ }
+ }
+}
+
// Returns true if the class's status has changed.
bool MetaspaceShared::try_link_class(JavaThread* current, InstanceKlass* ik) {
ExceptionMark em(current);
diff --git a/src/hotspot/share/cds/metaspaceShared.hpp b/src/hotspot/share/cds/metaspaceShared.hpp
index 2423141b8fa..130e7fe4484 100644
--- a/src/hotspot/share/cds/metaspaceShared.hpp
+++ b/src/hotspot/share/cds/metaspaceShared.hpp
@@ -179,6 +179,7 @@ public:
private:
static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN;
+ static void fork_and_dump_final_static_archive(TRAPS);
static bool write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, ArchiveHeapInfo* heap_info);
static FileMapInfo* open_static_archive();
static FileMapInfo* open_dynamic_archive();
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index c949119d363..8bd09a0d947 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -309,7 +309,7 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
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.
+ // classes from the preimage. We don't want to get a VerifyError when linking this class.
return warn_excluded(k, "Unlinked class not supported by AOTConfiguration");
}
} else {
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 62fcc65743a..b7ce095b41b 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -136,6 +136,11 @@ static bool xshare_auto_cmd_line = false;
// True if -Xint/-Xmixed/-Xcomp were specified
static bool mode_flag_cmd_line = false;
+struct VMInitArgsGroup {
+ const JavaVMInitArgs* _args;
+ JVMFlagOrigin _origin;
+};
+
bool PathString::set_value(const char *value, AllocFailType alloc_failmode) {
char* new_value = AllocateHeap(strlen(value)+1, mtArguments, alloc_failmode);
if (new_value == nullptr) {
@@ -1950,12 +1955,7 @@ Arguments::ArgsRange Arguments::parse_memory_size(const char* s,
return check_memory_size(*long_arg, min_size, max_size);
}
-// Parse JavaVMInitArgs structure
-
-jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
- const JavaVMInitArgs *java_tool_options_args,
- const JavaVMInitArgs *java_options_args,
- const JavaVMInitArgs *cmd_line_args) {
+jint Arguments::parse_vm_init_args(GrowableArrayCHeap* all_args) {
// Save default settings for some mode flags
Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods;
Arguments::_UseOnStackReplacement = UseOnStackReplacement;
@@ -1968,30 +1968,12 @@ jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
// Setup flags for mixed which is the default
set_mode_flags(_mixed);
- // Parse args structure generated from java.base vm options resource
- jint result = parse_each_vm_init_arg(vm_options_args, JVMFlagOrigin::JIMAGE_RESOURCE);
- if (result != JNI_OK) {
- return result;
- }
-
- // Parse args structure generated from JAVA_TOOL_OPTIONS environment
- // variable (if present).
- result = parse_each_vm_init_arg(java_tool_options_args, JVMFlagOrigin::ENVIRON_VAR);
- if (result != JNI_OK) {
- return result;
- }
-
- // Parse args structure generated from the command line flags.
- result = parse_each_vm_init_arg(cmd_line_args, JVMFlagOrigin::COMMAND_LINE);
- if (result != JNI_OK) {
- return result;
- }
-
- // Parse args structure generated from the _JAVA_OPTIONS environment
- // variable (if present) (mimics classic VM)
- result = parse_each_vm_init_arg(java_options_args, JVMFlagOrigin::ENVIRON_VAR);
- if (result != JNI_OK) {
- return result;
+ jint result;
+ for (int i = 0; i < all_args->length(); i++) {
+ result = parse_each_vm_init_arg(all_args->at(i)._args, all_args->at(i)._origin);
+ if (result != JNI_OK) {
+ return result;
+ }
}
// Disable CDS for exploded image
@@ -3093,6 +3075,50 @@ jint Arguments::parse_java_tool_options_environment_variable(ScopedVMInitArgs* a
return parse_options_environment_variable("JAVA_TOOL_OPTIONS", args);
}
+static JavaVMOption* get_last_aotmode_arg(const JavaVMInitArgs* args) {
+ for (int index = args->nOptions - 1; index >= 0; index--) {
+ JavaVMOption* option = args->options + index;
+ if (strstr(option->optionString, "-XX:AOTMode=") == option->optionString) {
+ return option;
+ }
+ }
+
+ return nullptr;
+}
+
+jint Arguments::parse_jdk_aot_vm_options_environment_variable(GrowableArrayCHeap* all_args,
+ ScopedVMInitArgs* jdk_aot_vm_options_args) {
+ // Don't bother scanning all the args if this env variable is not set
+ if (::getenv("JDK_AOT_VM_OPTIONS") == nullptr) {
+ return JNI_OK;
+ }
+
+ // Scan backwards and find the last occurrence of -XX:AOTMode=xxx, which will decide the value
+ // of AOTMode.
+ JavaVMOption* option = nullptr;
+ for (int i = all_args->length() - 1; i >= 0; i--) {
+ if ((option = get_last_aotmode_arg(all_args->at(i)._args)) != nullptr) {
+ break;
+ }
+ }
+
+ if (option != nullptr) {
+ // We have found the last -XX:AOTMode=xxx. At this point