diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 192791ebd9c..b662c5a1b47 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -464,6 +464,7 @@ void AOTClassLocationConfig::dumptime_init_helper(TRAPS) { AOTClassLocation* jrt = AOTClassLocation::allocate(THREAD, ClassLoader::get_jrt_entry()->name(), 0, Group::MODULES_IMAGE, /*from_cpattr*/false, /*is_jrt*/true); + log_info(class, path)("path [%d] = (modules image)", tmp_array.length()); tmp_array.append(jrt); parse(THREAD, tmp_array, all_css.boot_cp(), Group::BOOT_CLASSPATH, /*parse_manifest*/true); @@ -573,6 +574,7 @@ void AOTClassLocationConfig::parse(JavaThread* current, GrowableClassLocationArr void AOTClassLocationConfig::add_class_location(JavaThread* current, GrowableClassLocationArray& tmp_array, const char* path, Group group, bool parse_manifest, bool from_cpattr) { AOTClassLocation* cs = AOTClassLocation::allocate(current, path, tmp_array.length(), group, from_cpattr); + log_info(class, path)("path [%d] = %s%s", tmp_array.length(), path, from_cpattr ? " (from cpattr)" : ""); tmp_array.append(cs); if (!parse_manifest) { @@ -726,6 +728,8 @@ bool AOTClassLocationConfig::is_valid_classpath_index(int classpath_index, Insta } AOTClassLocationConfig* AOTClassLocationConfig::write_to_archive() const { + log_locations(CDSConfig::output_archive_path(), /*is_write=*/true); + Array* archived_copy = ArchiveBuilder::new_ro_array(_class_locations->length()); for (int i = 0; i < _class_locations->length(); i++) { archived_copy->at_put(i, _class_locations->at(i)->write_to_archive()); @@ -773,7 +777,7 @@ bool AOTClassLocationConfig::check_classpaths(bool is_boot_classpath, bool has_a effective_dumptime_path = substitute(effective_dumptime_path, _dumptime_lcp_len, runtime_lcp, runtime_lcp_len); } - log_info(class, path)("Checking '%s' %s%s", effective_dumptime_path, cs->file_type_string(), + log_info(class, path)("Checking [%d] '%s' %s%s", i, effective_dumptime_path, cs->file_type_string(), cs->from_cpattr() ? " (from JAR manifest ClassPath attribute)" : ""); if (!cs->from_cpattr() && file_exists(effective_dumptime_path)) { if (!runtime_css.has_next()) { @@ -961,11 +965,14 @@ bool AOTClassLocationConfig::need_lcp_match_helper(int start, int end, ClassLoca return true; } -bool AOTClassLocationConfig::validate(bool has_aot_linked_classes, bool* has_extra_module_paths) const { +bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_linked_classes, bool* has_extra_module_paths) const { ResourceMark rm; AllClassLocationStreams all_css; + log_locations(cache_filename, /*is_write=*/false); + const char* jrt = ClassLoader::get_jrt_entry()->name(); + log_info(class, path)("Checking [0] (modules image)"); bool success = class_location_at(0)->check(jrt, has_aot_linked_classes); log_info(class, path)("Modules image %s validation: %s", jrt, success ? "passed" : "failed"); if (!success) { @@ -1036,6 +1043,17 @@ bool AOTClassLocationConfig::validate(bool has_aot_linked_classes, bool* has_ext return success; } +void AOTClassLocationConfig::log_locations(const char* cache_filename, bool is_write) const { + if (log_is_enabled(Info, class, path)) { + LogStreamHandle(Info, class, path) st; + st.print_cr("%s classpath(s) %s %s (size = %d)", + is_write ? "Writing" : "Reading", + is_write ? "into" : "from", + cache_filename, class_locations()->length()); + print_on(&st); + } +} + void AOTClassLocationConfig::print() { if (CDSConfig::is_dumping_archive()) { tty->print_cr("AOTClassLocationConfig::_dumptime_instance = %p", _dumptime_instance); @@ -1052,8 +1070,15 @@ void AOTClassLocationConfig::print() { } void AOTClassLocationConfig::print_on(outputStream* st) const { + const char* type = "boot"; int n = class_locations()->length(); for (int i = 0; i < n; i++) { + if (i >= boot_cp_end_index()) { + type = "app"; + } + if (i >= app_cp_end_index()) { + type = "module"; + } const AOTClassLocation* cs = class_location_at(i); const char* path; if (i == 0) { @@ -1061,12 +1086,6 @@ void AOTClassLocationConfig::print_on(outputStream* st) const { } else { path = cs->path(); } - st->print_cr("[%d] = %s", i, path); - if (i == boot_cp_end_index() && i < n) { - st->print_cr("--- end of boot"); - } - if (i == app_cp_end_index() && i < n) { - st->print_cr("--- end of app"); - } + st->print_cr("(%-6s) [%d] = %s", type, i, path); } } diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index f93bb899a4c..460788930a4 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -204,6 +204,7 @@ class AOTClassLocationConfig : public CHeapObj { const char* prepend, size_t prepend_len) const; void print_on(outputStream* st) const; + void log_locations(const char* cache_filename, bool is_writing) const; public: static AOTClassLocationConfig* dumptime() { @@ -269,7 +270,7 @@ public: AOTClassLocationConfig* write_to_archive() const; // Functions used only during runtime - bool validate(bool has_aot_linked_classes, bool* has_extra_module_paths) const; + bool validate(const char* cache_filename, bool has_aot_linked_classes, bool* has_extra_module_paths) const; bool is_valid_classpath_index(int classpath_index, InstanceKlass* ik); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 723b5298646..a413aa2d8e8 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -325,7 +325,7 @@ bool FileMapInfo::validate_class_location() { AOTClassLocationConfig* config = header()->class_location_config(); bool has_extra_module_paths = false; - if (!config->validate(header()->has_aot_linked_classes(), &has_extra_module_paths)) { + if (!config->validate(full_path(), header()->has_aot_linked_classes(), &has_extra_module_paths)) { if (PrintSharedArchiveAndExit) { MetaspaceShared::set_archive_loading_failed(); return true; diff --git a/src/hotspot/share/utilities/classpathStream.cpp b/src/hotspot/share/utilities/classpathStream.cpp index cf0b84b18fb..8581c7831ab 100644 --- a/src/hotspot/share/utilities/classpathStream.cpp +++ b/src/hotspot/share/utilities/classpathStream.cpp @@ -26,18 +26,40 @@ #include "runtime/os.hpp" #include "utilities/classpathStream.hpp" -const char* ClasspathStream::get_next() { - while (_class_path[_end] != '\0' && _class_path[_end] != os::path_separator()[0]) { - _end++; +ClasspathStream::ClasspathStream(const char* classpath) { + _cp = classpath; + skip_blank_paths(); +} + +char ClasspathStream::separator() { + // All supported platforms have a single character path separator. + return os::path_separator()[0]; +} + +void ClasspathStream::skip_blank_paths() { + while (*_cp == separator()) { + _cp++; } - int path_len = _end - _start; +} + +const char* ClasspathStream::get_next() { + assert(has_next(), "call this only after you checked has_next()"); + assert(*_cp != separator(), "ensured by constructor and get_next()"); + + const char* end = _cp + 1; + while (*end != separator() && *end != '\0') { + end++; + } + + int path_len = end - _cp; char* path = NEW_RESOURCE_ARRAY(char, path_len + 1); - strncpy(path, &_class_path[_start], path_len); + strncpy(path, _cp, path_len); path[path_len] = '\0'; - while (_class_path[_end] == os::path_separator()[0]) { - _end++; - } - _start = _end; + assert(strlen(path) > 0, "must be"); + + _cp = end; + skip_blank_paths(); + return path; } diff --git a/src/hotspot/share/utilities/classpathStream.hpp b/src/hotspot/share/utilities/classpathStream.hpp index 3c29a07262d..87f7649872d 100644 --- a/src/hotspot/share/utilities/classpathStream.hpp +++ b/src/hotspot/share/utilities/classpathStream.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 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 @@ -26,23 +26,20 @@ #define SHARE_UTILITIES_CLASSPATHSTREAM_HPP class ClasspathStream : public StackObj { - const char* _class_path; - int _len; - int _start; - int _end; - + const char* _cp; + static char separator(); + void skip_blank_paths(); public: - ClasspathStream(const char* class_path) { - _class_path = class_path; - _len = (int)strlen(class_path); - _start = 0; - _end = 0; - } + // The caller should ensure that class_path is alive during the + // lifetime of this ClasspathStream. + ClasspathStream(const char* class_path); bool has_next() { - return _start < _len; + return *_cp != '\0'; } + // Call this only after you checked has_next(). + // Returns a resource-allocated string. const char* get_next(); }; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java b/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java index c0dce14ab82..23dcc7e5fcf 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java @@ -59,9 +59,9 @@ public class PrintSharedArchiveAndExit { String appJar = JarBuilder.getOrCreateHelloJar(); String appJar2 = JarBuilder.build("PrintSharedArchiveAndExit-more", "HelloMore"); String cp = appJar + File.pathSeparator + appJar2; - String firstCheckShortMsg = "Checking 'hello.jar' file"; // the first JAR to check (without directory prefix) - String firstCheckMsg = "Checking '" + appJar + "' file"; // the first JAR to check - String lastCheckMsg = "Checking '" + appJar2 + "' file"; // the last JAR to check + String firstCheckShortMsg = "Checking [1] 'hello.jar' file"; // the first JAR to check (without directory prefix) + String firstCheckMsg = "Checking [1] '" + appJar + "' file"; // the first JAR to check + String lastCheckMsg = "Checking [2] '" + appJar2 + "' file"; // the last JAR to check TestCommon.testDump(cp, TestCommon.list("Hello", "HelloMore")); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ClassPathLogging.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ClassPathLogging.java new file mode 100644 index 00000000000..0d5bca34d68 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ClassPathLogging.java @@ -0,0 +1,64 @@ +/* + * 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 + * @summary verify the output of -Xlog:class+path when using AOT cache + * @bug 8356308 + * @requires vm.cds.supports.aot.class.linking + * @requires vm.flagless + * @library /test/lib + * @build ClassPathLogging + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar ClassPathLoggingApp + * @run driver ClassPathLogging + */ + +import java.io.File; +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; + +public class ClassPathLogging { + public static void main(String... args) throws Exception { + String sep = File.pathSeparator; + SimpleCDSAppTester.of("ClassPathLogging") + .addVmArgs("-Xlog:class+path=debug") + .classpath(sep + "foo.jar" + sep + sep + sep + "app.jar" + sep) // all empty paths should be skipped. + .appCommandLine("ClassPathLoggingApp") + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("HelloWorld") + .shouldContain("Reading classpath(s) from ClassPathLogging.aot (size = 3)") + .shouldMatch("boot.*0.*=.*modules") + .shouldContain("(app ) [1] = foo.jar") + .shouldContain("(app ) [2] = app.jar"); + }) + .runAOTWorkflow(); + } +} + +class ClassPathLoggingApp { + public static void main(String[] args) { + System.out.println("HelloWorld"); + } +}