8356308: Assert with -Xlog:class+path when classpath has an empty element

Reviewed-by: dholmes, ccheung
This commit is contained in:
Ioi Lam 2025-06-02 16:52:05 +00:00
parent 8b6a11f7e0
commit bce2bd24ef
7 changed files with 139 additions and 36 deletions

View File

@ -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<AOTClassLocation*>* archived_copy = ArchiveBuilder::new_ro_array<AOTClassLocation*>(_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);
}
}

View File

@ -204,6 +204,7 @@ class AOTClassLocationConfig : public CHeapObj<mtClassShared> {
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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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();
};

View File

@ -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"));

View File

@ -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");
}
}