8367910: Reduce warnings about unsupported classes in AOT cache creation

Reviewed-by: dholmes, kvn, shade
This commit is contained in:
Ioi Lam 2025-09-25 19:59:52 +00:00
parent 3c9fd7688f
commit 52e777845f
8 changed files with 45 additions and 32 deletions

View File

@ -53,7 +53,7 @@ void DumpTimeSharedClassTable::iterate_all_live_classes(Function function) const
assert(k->is_loader_alive(), "must not change");
} else {
if (!SystemDictionaryShared::is_excluded_class(k)) {
SystemDictionaryShared::warn_excluded(k, "Class loader not alive");
SystemDictionaryShared::log_exclusion(k, "Class loader not alive");
SystemDictionaryShared::set_excluded_locked(k);
}
}

View File

@ -354,11 +354,13 @@ void SystemDictionaryShared::check_exclusion_for_self_and_dependencies(InstanceK
});
}
// Returns true so the caller can do: return warn_excluded(".....");
bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
void SystemDictionaryShared::log_exclusion(InstanceKlass* k, const char* reason, bool is_warning) {
ResourceMark rm;
aot_log_warning(aot)("Skipping %s: %s", k->name()->as_C_string(), reason);
return true;
if (is_warning) {
aot_log_warning(aot)("Skipping %s: %s", k->name()->as_C_string(), reason);
} else {
aot_log_info(aot)("Skipping %s: %s", k->name()->as_C_string(), reason);
}
}
bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) {
@ -377,23 +379,35 @@ bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) {
}
bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) {
bool log_warning = false;
const char* error = check_self_exclusion_helper(k, log_warning);
if (error != nullptr) {
log_exclusion(k, error, log_warning);
return true; // Should be excluded
} else {
return false; // Should not be excluded
}
}
const char* SystemDictionaryShared::check_self_exclusion_helper(InstanceKlass* k, bool& log_warning) {
assert_lock_strong(DumpTimeTable_lock);
if (CDSConfig::is_dumping_final_static_archive() && k->defined_by_other_loaders()
&& k->in_aot_cache()) {
return false; // Do not exclude: unregistered classes are passed from preimage to final image.
return nullptr; // 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");
log_warning = true;
return "In error state";
}
if (k->is_scratch_class()) {
return warn_excluded(k, "A scratch class");
return "A scratch class";
}
if (!k->is_loaded()) {
return warn_excluded(k, "Not in loaded state");
return "Not in loaded state";
}
if (has_been_redefined(k)) {
return warn_excluded(k, "Has been redefined");
return "Has been redefined";
}
if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) {
if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) {
@ -401,43 +415,42 @@ bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) {
if (CDSConfig::is_dumping_method_handles()) {
k->set_shared_classpath_index(0);
} else {
ResourceMark rm;
aot_log_info(aot)("Skipping %s because it is dynamically generated", k->name()->as_C_string());
return true; // exclude without warning
return "dynamically generated";
}
} else {
// These are classes loaded from unsupported locations (such as those loaded by JVMTI native
// agent during dump time).
return warn_excluded(k, "Unsupported location");
return "Unsupported location";
}
}
if (k->signers() != nullptr) {
// We cannot include signed classes in the archive because the certificates
// used during dump time may be different than those used during
// runtime (due to expiration, etc).
return warn_excluded(k, "Signed JAR");
return "Signed JAR";
}
if (is_jfr_event_class(k)) {
// We cannot include JFR event classes because they need runtime-specific
// instrumentation in order to work with -XX:FlightRecorderOptions:retransform=false.
// There are only a small number of these classes, so it's not worthwhile to
// support them and make CDS more complicated.
return warn_excluded(k, "JFR event class");
return "JFR event class";
}
if (!k->is_linked()) {
if (has_class_failed_verification(k)) {
return warn_excluded(k, "Failed verification");
log_warning = true;
return "Failed verification";
} else if (CDSConfig::is_dumping_aot_linked_classes()) {
// Most loaded classes should have been speculatively linked by AOTMetaspace::link_class_for_cds().
// Old classes may not be linked if CDSConfig::is_preserving_verification_constraints()==false.
// An unlinked 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");
return "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 the preimage. We don't want to get a VerifyError when linking this class.
return warn_excluded(k, "Unlinked class not supported by AOTConfiguration");
return "Unlinked class not supported by AOTConfiguration";
}
} else {
if (!k->can_be_verified_at_dumptime()) {
@ -447,17 +460,15 @@ bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) {
// won't work at runtime.
// As a result, we cannot store this class. It must be loaded and fully verified
// at runtime.
return warn_excluded(k, "Old class has been linked");
return "Old class has been linked";
}
}
if (UnregisteredClasses::check_for_exclusion(k)) {
ResourceMark rm;
aot_log_info(aot)("Skipping %s: used only when dumping CDS archive", k->name()->as_C_string());
return true;
return "used only when dumping CDS archive";
}
return false;
return nullptr;
}
// Returns true if DumpTimeClassInfo::is_excluded() is true for at least one of k's exclusion dependencies.
@ -511,7 +522,7 @@ bool SystemDictionaryShared::is_dependency_excluded(InstanceKlass* k, InstanceKl
DumpTimeClassInfo* dependency_info = get_info_locked(dependency);
if (dependency_info->is_excluded()) {
ResourceMark rm;
aot_log_warning(aot)("Skipping %s: %s %s is excluded", k->name()->as_C_string(), type, dependency->name()->as_C_string());
aot_log_info(aot)("Skipping %s: %s %s is excluded", k->name()->as_C_string(), type, dependency->name()->as_C_string());
return true;
}
return false;
@ -838,7 +849,7 @@ public:
InstanceKlass* k = _list.at(i);
bool i_am_first = SystemDictionaryShared::add_unregistered_class(_thread, k);
if (!i_am_first) {
SystemDictionaryShared::warn_excluded(k, "Duplicated unregistered class");
SystemDictionaryShared::log_exclusion(k, "Duplicated unregistered class");
SystemDictionaryShared::set_excluded_locked(k);
}
}
@ -967,7 +978,7 @@ bool SystemDictionaryShared::has_class_failed_verification(InstanceKlass* ik) {
}
void SystemDictionaryShared::set_from_class_file_load_hook(InstanceKlass* ik) {
warn_excluded(ik, "From ClassFileLoadHook");
log_exclusion(ik, "From ClassFileLoadHook");
set_excluded(ik);
}

View File

@ -180,6 +180,7 @@ private:
// exclusion checks
static void check_exclusion_for_self_and_dependencies(InstanceKlass *k);
static bool check_self_exclusion(InstanceKlass* k);
static const char* check_self_exclusion_helper(InstanceKlass* k, bool& log_warning);
static bool check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
static bool check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name);
static bool is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type);
@ -277,7 +278,7 @@ public:
static void set_excluded(InstanceKlass* k);
static void set_excluded_locked(InstanceKlass* k);
static void set_from_class_file_load_hook(InstanceKlass* k) NOT_CDS_RETURN;
static bool warn_excluded(InstanceKlass* k, const char* reason);
static void log_exclusion(InstanceKlass* k, const char* reason, bool is_warning = false);
static void dumptime_classes_do(class MetaspaceClosure* it);
static void write_to_archive(bool is_static_archive = true);
static void serialize_dictionary_headers(class SerializeClosure* soc,

View File

@ -237,7 +237,7 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
// Exclude any classes that are verified with the old verifier, as the old verifier
// doesn't call SystemDictionaryShared::add_verification_constraint()
if (CDSConfig::is_dumping_archive()) {
SystemDictionaryShared::warn_excluded(klass, "Verified with old verifier");
SystemDictionaryShared::log_exclusion(klass, "Verified with old verifier");
SystemDictionaryShared::set_excluded(klass);
}
#endif

View File

@ -618,7 +618,7 @@ void CompileTrainingData::verify(bool verify_dep_counter) {
for (int i = 0; i < init_dep_count(); i++) {
KlassTrainingData* ktd = init_dep(i);
if (ktd->has_holder() && ktd->holder()->defined_by_other_loaders()) {
LogStreamHandle(Warning, training) log;
LogStreamHandle(Info, training) log;
if (log.is_enabled()) {
ResourceMark rm;
log.print("CTD "); print_value_on(&log);

View File

@ -65,6 +65,7 @@ public class OldClassSupport {
@Override
public String[] vmArgs(RunMode runMode) {
return new String[] {
"-Xlog:aot",
"-Xlog:aot+class=debug",
"-Xlog:aot+resolve=trace",
};

View File

@ -39,7 +39,7 @@ import jdk.test.lib.process.OutputAnalyzer;
public class VerifierFailOver {
public static void main(String... args) throws Exception {
SimpleCDSAppTester.of("VerifierFailOver")
.addVmArgs("-Xlog:aot+class=debug")
.addVmArgs("-Xlog:aot,aot+class=debug")
.classpath("app.jar")
.appCommandLine("VerifierFailOverApp")
.setTrainingChecker((OutputAnalyzer out) -> {

View File

@ -118,7 +118,7 @@ public class BulkLoaderTest {
@Override
public String[] vmArgs(RunMode runMode) {
return new String[] {
"-Xlog:cds,aot+load,cds+class=debug,aot+class=debug",
"-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug",
"-XX:+AOTClassLinking",
};
}