8316969: Improve CDS module graph support for --module option

Reviewed-by: iklam, alanb
This commit is contained in:
Calvin Cheung 2023-11-02 16:03:14 +00:00
parent 7a7b1e5a92
commit e318cd25cb
13 changed files with 296 additions and 21 deletions

View File

@ -573,6 +573,12 @@ void ArchiveBuilder::verify_estimate_size(size_t estimate, const char* which) {
_other_region_used_bytes = 0;
}
char* ArchiveBuilder::ro_strdup(const char* s) {
char* archived_str = ro_region_alloc((int)strlen(s) + 1);
strcpy(archived_str, s);
return archived_str;
}
void ArchiveBuilder::dump_rw_metadata() {
ResourceMark rm;
log_info(cds)("Allocating RW objects ... ");

View File

@ -374,6 +374,8 @@ public:
return align_up(byte_size, SharedSpaceObjectAlignment);
}
char* ro_strdup(const char* s);
void dump_rw_metadata();
void dump_ro_metadata();
void relocate_metaspaceobj_embedded_pointers();

View File

@ -125,7 +125,7 @@ static ArchivableStaticFieldInfo archive_subgraph_entry_fields[] = {
// full module graph
static ArchivableStaticFieldInfo fmg_archive_subgraph_entry_fields[] = {
{"jdk/internal/loader/ArchivedClassLoaders", "archivedClassLoaders"},
{"jdk/internal/module/ArchivedBootLayer", "archivedBootLayer"},
{ARCHIVED_BOOT_LAYER_CLASS, ARCHIVED_BOOT_LAYER_FIELD},
{"java/lang/Module$ArchivedData", "archivedData"},
{nullptr, nullptr},
};
@ -964,7 +964,14 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP
// Initialize from archived data. Currently this is done only
// during VM initialization time. No lock is needed.
if (record != nullptr) {
if (record == nullptr) {
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm(THREAD);
log_info(cds, heap)("subgraph %s is not recorded",
k->external_name());
}
return nullptr;
} else {
if (record->is_full_module_graph() && !MetaspaceShared::use_full_module_graph()) {
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm(THREAD);
@ -1751,4 +1758,26 @@ void HeapShared::print_stats() {
avg_size(_total_obj_size, _total_obj_count));
}
bool HeapShared::is_archived_boot_layer_available(JavaThread* current) {
TempNewSymbol klass_name = SymbolTable::new_symbol(ARCHIVED_BOOT_LAYER_CLASS);
InstanceKlass* k = SystemDictionary::find_instance_klass(current, klass_name, Handle(), Handle());
if (k == nullptr) {
return false;
} else {
TempNewSymbol field_name = SymbolTable::new_symbol(ARCHIVED_BOOT_LAYER_FIELD);
TempNewSymbol field_signature = SymbolTable::new_symbol("Ljdk/internal/module/ArchivedBootLayer;");
fieldDescriptor fd;
if (k->find_field(field_name, field_signature, true, &fd) != nullptr) {
oop m = k->java_mirror();
oop f = m->obj_field(fd.offset());
if (CompressedOops::is_null(f)) {
return false;
}
} else {
return false;
}
}
return true;
}
#endif // INCLUDE_CDS_JAVA_HEAP

View File

@ -49,6 +49,9 @@ class ResourceBitMap;
struct ArchivableStaticFieldInfo;
class ArchiveHeapInfo;
#define ARCHIVED_BOOT_LAYER_CLASS "jdk/internal/module/ArchivedBootLayer"
#define ARCHIVED_BOOT_LAYER_FIELD "archivedBootLayer"
// A dump time sub-graph info for Klass _k. It includes the entry points
// (static fields in _k's mirror) of the archived sub-graphs reachable
// from _k's mirror. It also contains a list of Klasses of the objects
@ -160,6 +163,7 @@ public:
// Scratch objects for archiving Klass::java_mirror()
static oop scratch_java_mirror(BasicType t) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
static oop scratch_java_mirror(Klass* k) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
static bool is_archived_boot_layer_available(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN_(false);
private:
#if INCLUDE_CDS_JAVA_HEAP

View File

@ -45,6 +45,7 @@
#include "classfile/classLoaderExt.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/loaderConstraints.hpp"
#include "classfile/modules.hpp"
#include "classfile/placeholders.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
@ -387,6 +388,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
SystemDictionaryShared::serialize_vm_classes(soc);
soc->do_tag(--tag);
CDS_JAVA_HEAP_ONLY(Modules::serialize(soc);)
CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc);)
LambdaFormInvokers::serialize(soc);
@ -479,6 +481,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
// Write lambform lines into archive
LambdaFormInvokers::dump_static_archive_invokers();
// Write module name into archive
CDS_JAVA_HEAP_ONLY(Modules::dump_main_module_name();)
// Write the other data to the output array.
DumpRegion* ro_region = ArchiveBuilder::current()->ro_region();
char* start = ro_region->top();
@ -763,8 +767,6 @@ void MetaspaceShared::preload_and_dump_impl(TRAPS) {
log_info(cds)("Reading extra data: done.");
}
HeapShared::init_for_dumping(CHECK);
// Rewrite and link classes
log_info(cds)("Rewriting and linking classes ...");
@ -778,6 +780,11 @@ void MetaspaceShared::preload_and_dump_impl(TRAPS) {
#if INCLUDE_CDS_JAVA_HEAP
if (CDSConfig::is_dumping_heap()) {
StringTable::allocate_shared_strings_array(CHECK);
if (!HeapShared::is_archived_boot_layer_available(THREAD)) {
log_info(cds)("archivedBootLayer not available, disabling full module graph");
disable_full_module_graph();
}
HeapShared::init_for_dumping(CHECK);
ArchiveHeapWriter::init();
if (use_full_module_graph()) {
HeapShared::reset_archived_object_states(CHECK);
@ -1163,8 +1170,8 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
static_mapinfo->map_or_load_heap_region();
}
#endif // _LP64
log_info(cds)("optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled");
log_info(cds)("full module graph: %s", MetaspaceShared::use_full_module_graph() ? "enabled" : "disabled");
log_info(cds)("initial optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled");
log_info(cds)("initial full module graph: %s", MetaspaceShared::use_full_module_graph() ? "enabled" : "disabled");
} else {
unmap_archive(static_mapinfo);
unmap_archive(dynamic_mapinfo);

View File

@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/metaspaceShared.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classLoader.hpp"
@ -559,6 +560,49 @@ void Modules::verify_archived_modules() {
ModuleEntry::verify_archived_module_entries();
}
#if INCLUDE_CDS_JAVA_HEAP
char* Modules::_archived_main_module_name = nullptr;
#endif
void Modules::dump_main_module_name() {
const char* module_name = Arguments::get_property("jdk.module.main");
if (module_name != nullptr) {
_archived_main_module_name = ArchiveBuilder::current()->ro_strdup(module_name);
}
ArchivePtrMarker::mark_pointer(&_archived_main_module_name);
}
void Modules::serialize(SerializeClosure* soc) {
soc->do_ptr(&_archived_main_module_name);
if (soc->reading()) {
const char* runtime_main_module = Arguments::get_property("jdk.module.main");
log_info(cds)("_archived_main_module_name %s",
_archived_main_module_name != nullptr ? _archived_main_module_name : "(null)");
bool disable = false;
if (runtime_main_module == nullptr) {
if (_archived_main_module_name != nullptr) {
log_info(cds)("Module %s specified during dump time but not during runtime", _archived_main_module_name);
disable = true;
}
} else {
if (_archived_main_module_name == nullptr) {
log_info(cds)("Module %s specified during runtime but not during dump time", runtime_main_module);
disable = true;
} else if (strcmp(runtime_main_module, _archived_main_module_name) != 0) {
log_info(cds)("Mismatched modules: runtime %s dump time %s", runtime_main_module, _archived_main_module_name);
disable = true;
}
}
if (disable) {
log_info(cds)("Disabling optimized module handling");
MetaspaceShared::disable_optimized_module_handling();
}
log_info(cds)("optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled");
log_info(cds)("full module graph: %s", MetaspaceShared::use_full_module_graph() ? "enabled" : "disabled");
}
}
void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) {
assert(UseSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be");

View File

@ -59,6 +59,12 @@ public:
static void define_archived_modules(Handle h_platform_loader, Handle h_system_loader,
TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN;
static void dump_main_module_name() NOT_CDS_JAVA_HEAP_RETURN;
static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
#if INCLUDE_CDS_JAVA_HEAP
static char* _archived_main_module_name;
#endif
// Provides the java.lang.Module for the unnamed module defined
// to the boot loader.

View File

@ -1262,8 +1262,7 @@ bool Arguments::add_property(const char* prop, PropertyWriteable writeable, Prop
}
#if INCLUDE_CDS
if (is_internal_module_property(key) ||
strcmp(key, "jdk.module.main") == 0) {
if (is_internal_module_property(key)) {
MetaspaceShared::disable_optimized_module_handling();
log_info(cds)("optimized module handling: disabled due to incompatible property: %s=%s", key, value);
}

View File

@ -44,10 +44,12 @@ public class CDS {
private static final boolean isDumpingClassList;
private static final boolean isDumpingArchive;
private static final boolean isSharingEnabled;
private static final boolean isDumpingStaticArchive;
static {
isDumpingClassList = isDumpingClassList0();
isDumpingArchive = isDumpingArchive0();
isSharingEnabled = isSharingEnabled0();
isDumpingStaticArchive = isDumpingArchive && !isSharingEnabled;
}
/**
@ -71,6 +73,13 @@ public class CDS {
return isSharingEnabled;
}
/**
* Is dumping static archive.
*/
public static boolean isDumpingStaticArchive() {
return isDumpingStaticArchive;
}
private static native boolean isDumpingClassList0();
private static native boolean isDumpingArchive0();
private static native boolean isSharingEnabled0();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2023, 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
@ -24,6 +24,7 @@
*/
package jdk.internal.module;
import java.util.Objects;
import java.util.function.Function;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
@ -41,17 +42,20 @@ class ArchivedModuleGraph {
private final ModuleFinder finder;
private final Configuration configuration;
private final Function<String, ClassLoader> classLoaderFunction;
private final String mainModule;
private ArchivedModuleGraph(boolean hasSplitPackages,
boolean hasIncubatorModules,
ModuleFinder finder,
Configuration configuration,
Function<String, ClassLoader> classLoaderFunction) {
Function<String, ClassLoader> classLoaderFunction,
String mainModule) {
this.hasSplitPackages = hasSplitPackages;
this.hasIncubatorModules = hasIncubatorModules;
this.finder = finder;
this.configuration = configuration;
this.classLoaderFunction = classLoaderFunction;
this.mainModule = mainModule;
}
ModuleFinder finder() {
@ -79,8 +83,7 @@ class ArchivedModuleGraph {
*/
static ArchivedModuleGraph get(String mainModule) {
ArchivedModuleGraph graph = archivedModuleGraph;
// We only allow the unnamed module (default) case for now
if (mainModule == null) {
if ((graph != null) && Objects.equals(graph.mainModule, mainModule)) {
return graph;
} else {
return null;
@ -94,12 +97,14 @@ class ArchivedModuleGraph {
boolean hasIncubatorModules,
ModuleFinder finder,
Configuration configuration,
Function<String, ClassLoader> classLoaderFunction) {
Function<String, ClassLoader> classLoaderFunction,
String mainModule) {
archivedModuleGraph = new ArchivedModuleGraph(hasSplitPackages,
hasIncubatorModules,
finder,
configuration,
classLoaderFunction);
classLoaderFunction,
mainModule);
}
static {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2023, 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
@ -141,7 +141,6 @@ public final class ModuleBootstrap {
return getProperty("jdk.module.upgrade.path") == null &&
getProperty("jdk.module.path") == null &&
getProperty("jdk.module.patch.0") == null && // --patch-module
getProperty("jdk.module.main") == null && // --module
getProperty("jdk.module.addmods.0") == null && // --add-modules
getProperty("jdk.module.limitmods") == null && // --limit-modules
getProperty("jdk.module.addreads.0") == null && // --add-reads
@ -228,7 +227,8 @@ public final class ModuleBootstrap {
systemModules = SystemModuleFinders.systemModules(mainModule);
if (systemModules != null && !isPatched) {
needResolution = (traceOutput != null);
canArchive = true;
if (CDS.isDumpingStaticArchive())
canArchive = true;
}
}
if (systemModules == null) {
@ -469,14 +469,30 @@ public final class ModuleBootstrap {
limitedFinder = new SafeModuleFinder(finder);
}
// If -Xshare:dump and mainModule are specified, check if the mainModule
// is in the runtime image and not on the upgrade module path. If so,
// set canArchive to true so that the module graph can be archived.
if (CDS.isDumpingStaticArchive() && mainModule != null) {
String scheme = systemModuleFinder.find(mainModule)
.stream()
.map(ModuleReference::location)
.flatMap(Optional::stream)
.findAny()
.map(URI::getScheme)
.orElse(null);
if ("jrt".equalsIgnoreCase(scheme)) {
canArchive = true;
}
}
// Archive module graph and boot layer can be archived at CDS dump time.
// Only allow the unnamed module case for now.
if (canArchive && (mainModule == null)) {
if (canArchive) {
ArchivedModuleGraph.archive(hasSplitPackages,
hasIncubatorModules,
systemModuleFinder,
cf,
clf);
clf,
mainModule);
if (!hasSplitPackages && !hasIncubatorModules) {
ArchivedBootLayer.archive(bootLayer);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2023, 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
@ -78,5 +78,27 @@ public class ArchivedModuleCompareTest {
TestCommon.checkOutputStrings(
moduleResolutionOut1, moduleResolutionOut2, "\n");
}
// Test case 3)
// dump an archive with the -m jdk.compiler option
// run with -Xshare:off without -m option
// run with archive without -m option
// the list of modules from both runs should be the same
System.out.println("---------------- Test case 3 -----------------");
output = TestCommon.dump(appJar,
TestCommon.list("PrintSystemModulesApp"),
"-m", "jdk.compiler");
TestCommon.checkDump(output);
output = TestCommon.execOff("-cp", appJar, "PrintSystemModulesApp");
output.shouldHaveExitValue(0);
bootModules1 = TestCommon.filterOutLogs(output.getStdout());
output = TestCommon.exec(appJar, "PrintSystemModulesApp");
TestCommon.checkExec(output);
if (output.getStderr().contains("sharing")) {
String bootModules2 = TestCommon.filterOutLogs(output.getStdout());
TestCommon.checkOutputStrings(bootModules1, bootModules2, ", ");
}
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2023, 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
* @bug 8316969
* @summary Test handling of module option (-m).
* @requires vm.cds.write.archived.java.heap
* @requires vm.flagless
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @run driver ModuleOption
*/
import jdk.test.lib.process.OutputAnalyzer;
public class ModuleOption {
public static void main(String[] args) throws Exception {
final String moduleOption = "jdk.httpserver/sun.net.httpserver.simpleserver.Main";
final String incubatorModule = "jdk.incubator.vector";
final String loggingOption = "-Xlog:cds=debug,cds+module=debug,cds+heap=info,module=trace";
final String versionPattern = "java.[0-9][0-9][-].*";
final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled";
String archiveName = TestCommon.getNewArchiveName("module-option");
TestCommon.setCurrentArchiveName(archiveName);
// dump a base archive with -m jdk.httpserver
OutputAnalyzer oa = TestCommon.dumpBaseArchive(
archiveName,
loggingOption,
"-m", moduleOption,
"-version");
oa.shouldHaveExitValue(0);
// same module specified during runtime
oa = TestCommon.execCommon(
loggingOption,
"-m", moduleOption,
"-version");
oa.shouldHaveExitValue(0)
// version of the jdk.httpserver module, e.g. java 22-ea
.shouldMatch(versionPattern)
.shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.httpserver");
// different module specified during runtime
oa = TestCommon.execCommon(
loggingOption,
"-m", "jdk.compiler/com.sun.tools.javac.Main",
"-version");
oa.shouldHaveExitValue(0)
.shouldContain("Mismatched modules: runtime jdk.compiler dump time jdk.httpserver")
.shouldContain(subgraphCannotBeUsed);
// no module specified during runtime
oa = TestCommon.execCommon(
loggingOption,
"-version");
oa.shouldHaveExitValue(0)
.shouldContain("Module jdk.httpserver specified during dump time but not during runtime")
.shouldContain(subgraphCannotBeUsed);
// dump an archive without the module option
archiveName = TestCommon.getNewArchiveName("no-module-option");
TestCommon.setCurrentArchiveName(archiveName);
oa = TestCommon.dumpBaseArchive(
archiveName,
loggingOption,
"-version");
oa.shouldHaveExitValue(0);
// run with module option
oa = TestCommon.execCommon(
loggingOption,
"-m", moduleOption,
"-version");
oa.shouldHaveExitValue(0)
.shouldContain("Module jdk.httpserver specified during runtime but not during dump time")
// version of the jdk.httpserver module, e.g. java 22-ea
.shouldMatch(versionPattern)
.shouldContain(subgraphCannotBeUsed);
// dump an archive with an incubator module, -m jdk.incubator.vector
archiveName = TestCommon.getNewArchiveName("incubator-module");
TestCommon.setCurrentArchiveName(archiveName);
oa = TestCommon.dumpBaseArchive(
archiveName,
loggingOption,
"-m", incubatorModule,
"-version");
oa.shouldHaveExitValue(0)
// module graph won't be archived with an incubator module
.shouldContain("archivedBootLayer not available, disabling full module graph");
// run with the same incubator module
oa = TestCommon.execCommon(
loggingOption,
"-m", incubatorModule,
"-version");
oa.shouldContain("full module graph: disabled")
// module is not restored from archive
.shouldContain("define_module(): creation of module: jdk.incubator.vector")
.shouldContain("WARNING: Using incubator modules: jdk.incubator.vector")
.shouldContain("subgraph jdk.internal.module.ArchivedBootLayer is not recorde")
.shouldContain("module jdk.incubator.vector does not have a ModuleMainClass attribute, use -m <module>/<main-class>")
.shouldHaveExitValue(1);
}
}