8279366: CDS should allow alternative locations for JAR files in classpath
Reviewed-by: iklam, coleenp
This commit is contained in:
parent
3a873d3c5b
commit
427f50624f
src/hotspot/share
test
hotspot/jtreg/runtime/cds/appcds
lib/jdk/test/lib/cds
@ -35,6 +35,7 @@ CDSConst CDSConstants::offsets[] = {
|
||||
{ "GenericCDSFileMapHeader::_crc", offset_of(GenericCDSFileMapHeader, _crc) },
|
||||
{ "GenericCDSFileMapHeader::_version", offset_of(GenericCDSFileMapHeader, _version) },
|
||||
{ "GenericCDSFileMapHeader::_header_size", offset_of(GenericCDSFileMapHeader, _header_size) },
|
||||
{ "GenericCDSFileMapHeader::_common_app_classpath_prefix_size", offset_of(GenericCDSFileMapHeader, _common_app_classpath_prefix_size) },
|
||||
{ "GenericCDSFileMapHeader::_base_archive_name_offset", offset_of(GenericCDSFileMapHeader, _base_archive_name_offset) },
|
||||
{ "GenericCDSFileMapHeader::_base_archive_name_size", offset_of(GenericCDSFileMapHeader, _base_archive_name_size) },
|
||||
{ "CDSFileMapHeaderBase::_space[0]", offset_of(CDSFileMapHeaderBase, _space) },
|
||||
|
@ -204,6 +204,7 @@ void FileMapInfo::populate_header(size_t core_region_alignment) {
|
||||
size_t header_size;
|
||||
size_t base_archive_name_size = 0;
|
||||
size_t base_archive_name_offset = 0;
|
||||
size_t longest_common_prefix_size = 0;
|
||||
if (is_static()) {
|
||||
c_header_size = sizeof(FileMapHeader);
|
||||
header_size = c_header_size;
|
||||
@ -221,24 +222,30 @@ void FileMapInfo::populate_header(size_t core_region_alignment) {
|
||||
}
|
||||
FREE_C_HEAP_ARRAY(const char, default_base_archive_name);
|
||||
}
|
||||
ResourceMark rm;
|
||||
GrowableArray<const char*>* app_cp_array = create_dumptime_app_classpath_array();
|
||||
int len = app_cp_array->length();
|
||||
longest_common_prefix_size = longest_common_app_classpath_prefix_len(len, app_cp_array);
|
||||
_header = (FileMapHeader*)os::malloc(header_size, mtInternal);
|
||||
memset((void*)_header, 0, header_size);
|
||||
_header->populate(this,
|
||||
core_region_alignment,
|
||||
header_size,
|
||||
base_archive_name_size,
|
||||
base_archive_name_offset);
|
||||
base_archive_name_offset,
|
||||
longest_common_prefix_size);
|
||||
}
|
||||
|
||||
void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
|
||||
size_t header_size, size_t base_archive_name_size,
|
||||
size_t base_archive_name_offset) {
|
||||
size_t base_archive_name_offset, size_t common_app_classpath_prefix_size) {
|
||||
// 1. We require _generic_header._magic to be at the beginning of the file
|
||||
// 2. FileMapHeader also assumes that _generic_header is at the beginning of the file
|
||||
assert(offset_of(FileMapHeader, _generic_header) == 0, "must be");
|
||||
set_header_size((unsigned int)header_size);
|
||||
set_base_archive_name_offset((unsigned int)base_archive_name_offset);
|
||||
set_base_archive_name_size((unsigned int)base_archive_name_size);
|
||||
set_common_app_classpath_prefix_size((unsigned int)common_app_classpath_prefix_size);
|
||||
set_magic(DynamicDumpSharedSpaces ? CDS_DYNAMIC_ARCHIVE_MAGIC : CDS_ARCHIVE_MAGIC);
|
||||
set_version(CURRENT_CDS_ARCHIVE_VERSION);
|
||||
|
||||
@ -311,6 +318,7 @@ void FileMapHeader::print(outputStream* st) {
|
||||
st->print_cr("- crc: 0x%08x", crc());
|
||||
st->print_cr("- version: 0x%x", version());
|
||||
st->print_cr("- header_size: " UINT32_FORMAT, header_size());
|
||||
st->print_cr("- common_app_classpath_size: " UINT32_FORMAT, common_app_classpath_prefix_size());
|
||||
st->print_cr("- base_archive_name_offset: " UINT32_FORMAT, base_archive_name_offset());
|
||||
st->print_cr("- base_archive_name_size: " UINT32_FORMAT, base_archive_name_size());
|
||||
|
||||
@ -808,6 +816,17 @@ bool FileMapInfo::check_paths_existence(const char* paths) {
|
||||
return exist;
|
||||
}
|
||||
|
||||
GrowableArray<const char*>* FileMapInfo::create_dumptime_app_classpath_array() {
|
||||
Arguments::assert_is_dumping_archive();
|
||||
GrowableArray<const char*>* path_array = new GrowableArray<const char*>(10);
|
||||
ClassPathEntry* cpe = ClassLoader::app_classpath_entries();
|
||||
while (cpe != NULL) {
|
||||
path_array->append(cpe->name());
|
||||
cpe = cpe->next();
|
||||
}
|
||||
return path_array;
|
||||
}
|
||||
|
||||
GrowableArray<const char*>* FileMapInfo::create_path_array(const char* paths) {
|
||||
GrowableArray<const char*>* path_array = new GrowableArray<const char*>(10);
|
||||
JavaThread* current = JavaThread::current();
|
||||
@ -842,23 +861,48 @@ bool FileMapInfo::classpath_failure(const char* msg, const char* name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileMapInfo::check_paths(int shared_path_start_idx, int num_paths, GrowableArray<const char*>* rp_array) {
|
||||
unsigned int FileMapInfo::longest_common_app_classpath_prefix_len(int num_paths,
|
||||
GrowableArray<const char*>* rp_array) {
|
||||
if (num_paths == 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned int pos;
|
||||
for (pos = 0; ; pos++) {
|
||||
for (int i = 0; i < num_paths; i++) {
|
||||
if (rp_array->at(i)[pos] != '\0' && rp_array->at(i)[pos] == rp_array->at(0)[pos]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// search backward for the pos before the file separator char
|
||||
while (pos > 0 && rp_array->at(0)[--pos] != *os::file_separator());
|
||||
// return the file separator char position
|
||||
return pos + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FileMapInfo::check_paths(int shared_path_start_idx, int num_paths, GrowableArray<const char*>* rp_array,
|
||||
unsigned int dumptime_prefix_len, unsigned int runtime_prefix_len) {
|
||||
int i = 0;
|
||||
int j = shared_path_start_idx;
|
||||
bool mismatch = false;
|
||||
while (i < num_paths && !mismatch) {
|
||||
while (i < num_paths) {
|
||||
while (shared_path(j)->from_class_path_attr()) {
|
||||
// shared_path(j) was expanded from the JAR file attribute "Class-Path:"
|
||||
// during dump time. It's not included in the -classpath VM argument.
|
||||
j++;
|
||||
}
|
||||
if (!os::same_files(shared_path(j)->name(), rp_array->at(i))) {
|
||||
mismatch = true;
|
||||
assert(strlen(shared_path(j)->name()) > (size_t)dumptime_prefix_len, "sanity");
|
||||
const char* dumptime_path = shared_path(j)->name() + dumptime_prefix_len;
|
||||
assert(strlen(rp_array->at(i)) > (size_t)runtime_prefix_len, "sanity");
|
||||
const char* runtime_path = runtime_path = rp_array->at(i) + runtime_prefix_len;
|
||||
if (!os::same_files(dumptime_path, runtime_path)) {
|
||||
return true;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
return mismatch;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileMapInfo::validate_boot_class_paths() {
|
||||
@ -912,7 +956,7 @@ bool FileMapInfo::validate_boot_class_paths() {
|
||||
// check the full runtime boot path, must match with dump time
|
||||
num = rp_len;
|
||||
}
|
||||
mismatch = check_paths(1, num, rp_array);
|
||||
mismatch = check_paths(1, num, rp_array, 0, 0);
|
||||
} else {
|
||||
// create_path_array() ignores non-existing paths. Although the dump time and runtime boot classpath lengths
|
||||
// are the same initially, after the call to create_path_array(), the runtime boot classpath length could become
|
||||
@ -961,9 +1005,20 @@ bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) {
|
||||
// run 2: -cp x.jar:NE4:b.jar -> x.jar:b.jar -> mismatched
|
||||
|
||||
int j = header()->app_class_paths_start_index();
|
||||
mismatch = check_paths(j, shared_app_paths_len, rp_array);
|
||||
mismatch = check_paths(j, shared_app_paths_len, rp_array, 0, 0);
|
||||
if (mismatch) {
|
||||
return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
|
||||
// To facilitate app deployment, we allow the JAR files to be moved *together* to
|
||||
// a different location, as long as they are still stored under the same directory
|
||||
// structure. E.g., the following is OK.
|
||||
// java -Xshare:dump -cp /a/Foo.jar:/a/b/Bar.jar ...
|
||||
// java -Xshare:auto -cp /x/y/Foo.jar:/x/y/b/Bar.jar ...
|
||||
unsigned int dumptime_prefix_len = header()->common_app_classpath_prefix_size();
|
||||
unsigned int runtime_prefix_len = longest_common_app_classpath_prefix_len(shared_app_paths_len, rp_array);
|
||||
mismatch = check_paths(j, shared_app_paths_len, rp_array,
|
||||
dumptime_prefix_len, runtime_prefix_len);
|
||||
if (mismatch) {
|
||||
return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -991,7 +1046,7 @@ bool FileMapInfo::check_module_paths() {
|
||||
}
|
||||
ResourceMark rm;
|
||||
GrowableArray<const char*>* rp_array = create_path_array(rp);
|
||||
return check_paths(header()->app_module_paths_start_index(), num_paths, rp_array);
|
||||
return check_paths(header()->app_module_paths_start_index(), num_paths, rp_array, 0, 0);
|
||||
}
|
||||
|
||||
bool FileMapInfo::validate_shared_path_table() {
|
||||
@ -1205,6 +1260,10 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!check_common_app_classpath_prefix_len()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All fields in the GenericCDSFileMapHeader has been validated.
|
||||
_is_valid = true;
|
||||
return true;
|
||||
@ -1282,6 +1341,16 @@ public:
|
||||
_base_archive_name = name;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_common_app_classpath_prefix_len() {
|
||||
int common_path_size = _header->_common_app_classpath_prefix_size;
|
||||
if (common_path_size < 0) {
|
||||
FileMapInfo::fail_continue("common app classpath prefix len < 0");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -1364,8 +1433,9 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
if (base_offset != 0 && name_size != 0) {
|
||||
if (header_size != base_offset + name_size) {
|
||||
log_info(cds)("_header_size: " UINT32_FORMAT, header_size);
|
||||
log_info(cds)("base_archive_name_size: " UINT32_FORMAT, name_size);
|
||||
log_info(cds)("base_archive_name_offset: " UINT32_FORMAT, base_offset);
|
||||
log_info(cds)("common_app_classpath_size: " UINT32_FORMAT, header()->common_app_classpath_prefix_size());
|
||||
log_info(cds)("base_archive_name_size: " UINT32_FORMAT, header()->base_archive_name_size());
|
||||
log_info(cds)("base_archive_name_offset: " UINT32_FORMAT, header()->base_archive_name_offset());
|
||||
FileMapInfo::fail_continue("The shared archive file has an incorrect header size.");
|
||||
return false;
|
||||
}
|
||||
|
@ -245,6 +245,7 @@ public:
|
||||
unsigned int header_size() const { return _generic_header._header_size; }
|
||||
unsigned int base_archive_name_offset() const { return _generic_header._base_archive_name_offset; }
|
||||
unsigned int base_archive_name_size() const { return _generic_header._base_archive_name_size; }
|
||||
unsigned int common_app_classpath_prefix_size() const { return _generic_header._common_app_classpath_prefix_size; }
|
||||
|
||||
void set_magic(unsigned int m) { _generic_header._magic = m; }
|
||||
void set_crc(int crc_value) { _generic_header._crc = crc_value; }
|
||||
@ -252,6 +253,7 @@ public:
|
||||
void set_header_size(unsigned int s) { _generic_header._header_size = s; }
|
||||
void set_base_archive_name_offset(unsigned int s) { _generic_header._base_archive_name_offset = s; }
|
||||
void set_base_archive_name_size(unsigned int s) { _generic_header._base_archive_name_size = s; }
|
||||
void set_common_app_classpath_prefix_size(unsigned int s) { _generic_header._common_app_classpath_prefix_size = s; }
|
||||
|
||||
size_t core_region_alignment() const { return _core_region_alignment; }
|
||||
int obj_alignment() const { return _obj_alignment; }
|
||||
@ -311,7 +313,8 @@ public:
|
||||
}
|
||||
|
||||
void populate(FileMapInfo *info, size_t core_region_alignment, size_t header_size,
|
||||
size_t base_archive_name_size, size_t base_archive_name_offset);
|
||||
size_t base_archive_name_size, size_t base_archive_name_offset,
|
||||
size_t common_app_classpath_size);
|
||||
static bool is_valid_region(int region) {
|
||||
return (0 <= region && region < NUM_CDS_REGIONS);
|
||||
}
|
||||
@ -556,10 +559,16 @@ public:
|
||||
char* skip_first_path_entry(const char* path) NOT_CDS_RETURN_(NULL);
|
||||
int num_paths(const char* path) NOT_CDS_RETURN_(0);
|
||||
bool check_paths_existence(const char* paths) NOT_CDS_RETURN_(false);
|
||||
GrowableArray<const char*>* create_dumptime_app_classpath_array() NOT_CDS_RETURN_(NULL);
|
||||
GrowableArray<const char*>* create_path_array(const char* path) NOT_CDS_RETURN_(NULL);
|
||||
bool classpath_failure(const char* msg, const char* name) NOT_CDS_RETURN_(false);
|
||||
unsigned int longest_common_app_classpath_prefix_len(int num_paths,
|
||||
GrowableArray<const char*>* rp_array)
|
||||
NOT_CDS_RETURN_(0);
|
||||
bool check_paths(int shared_path_start_idx, int num_paths,
|
||||
GrowableArray<const char*>* rp_array) NOT_CDS_RETURN_(false);
|
||||
GrowableArray<const char*>* rp_array,
|
||||
unsigned int dumptime_prefix_len,
|
||||
unsigned int runtime_prefix_len) NOT_CDS_RETURN_(false);
|
||||
bool validate_boot_class_paths() NOT_CDS_RETURN_(false);
|
||||
bool validate_app_class_paths(int shared_app_paths_len) NOT_CDS_RETURN_(false);
|
||||
bool map_heap_regions(int first, int max, bool is_open_archive,
|
||||
|
@ -73,6 +73,8 @@ typedef struct GenericCDSFileMapHeader {
|
||||
int _crc; // header crc checksum, start from _base_archive_name_offset
|
||||
int _version; // CURRENT_CDS_ARCHIVE_VERSION of the jdk that dumped the this archive
|
||||
unsigned int _header_size; // total size of the header, in bytes
|
||||
unsigned int _common_app_classpath_prefix_size; // size of the common prefix of app class paths
|
||||
// 0 if no common prefix exists
|
||||
unsigned int _base_archive_name_offset; // offset where the base archive name is stored
|
||||
// static archive: 0
|
||||
// dynamic archive:
|
||||
@ -85,6 +87,7 @@ typedef struct GenericCDSFileMapHeader {
|
||||
// dynamic:
|
||||
// 0 for default base archive
|
||||
// non-zero for non-default base archive
|
||||
|
||||
} GenericCDSFileMapHeader;
|
||||
|
||||
// This type is used by the Serviceability Agent to access the contents of
|
||||
|
148
test/hotspot/jtreg/runtime/cds/appcds/CommonAppClasspath.java
Normal file
148
test/hotspot/jtreg/runtime/cds/appcds/CommonAppClasspath.java
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 8279366
|
||||
* @summary Test app class paths checking with the longest common path taken into account.
|
||||
* @requires vm.cds
|
||||
* @library /test/lib
|
||||
* @compile test-classes/Hello.java
|
||||
* @compile test-classes/HelloMore.java
|
||||
* @run driver CommonAppClasspath
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
|
||||
public class CommonAppClasspath {
|
||||
|
||||
private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir());
|
||||
private static final String failedMessage = "APP classpath mismatch";
|
||||
private static final String successMessage1 = "Hello source: shared objects file";
|
||||
private static final String successMessage2 = "HelloMore source: shared objects file";
|
||||
|
||||
private static void runtimeTest(String classPath, String mainClass, int expectedExitValue,
|
||||
String ... checkMessages) throws Exception {
|
||||
CDSTestUtils.Result result = TestCommon.run(
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + TestCommon.getCurrentArchiveName(),
|
||||
"-cp", classPath,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
mainClass);
|
||||
if (expectedExitValue == 0) {
|
||||
result.assertNormalExit( output -> {
|
||||
for (String s : checkMessages) {
|
||||
output.shouldContain(s);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
result.assertAbnormalExit( output -> {
|
||||
for (String s : checkMessages) {
|
||||
output.shouldContain(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String appJar = JarBuilder.getOrCreateHelloJar();
|
||||
String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", "HelloMore");
|
||||
// dump an archive with only appJar in the original location
|
||||
String jars = appJar;
|
||||
TestCommon.testDump(jars, TestCommon.list("Hello"));
|
||||
|
||||
// copy hello.jar to tmp dir
|
||||
Path destPath = CDSTestUtils.copyFile(appJar, System.getProperty("java.io.tmpdir"));
|
||||
|
||||
// Run with appJar relocated to tmp dir - should PASS
|
||||
runtimeTest(destPath.toString(), "Hello", 0, successMessage1);
|
||||
|
||||
// dump an archive with both jars in the original location
|
||||
jars = appJar + File.pathSeparator + appJar2;
|
||||
TestCommon.testDump(jars, TestCommon.list("Hello", "HelloMore"));
|
||||
|
||||
// copy hello.jar to USER_DIR/deploy
|
||||
String newDir = USER_DIR.toString() + File.separator + "deploy";
|
||||
destPath = CDSTestUtils.copyFile(appJar, newDir);
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/deploy
|
||||
Path destPath2 = CDSTestUtils.copyFile(appJar2, newDir);
|
||||
|
||||
// Run with both jars relocated to USER_DIR/dpeloy - should PASS
|
||||
runtimeTest(destPath.toString() + File.pathSeparator + destPath2.toString(),
|
||||
"HelloMore", 0, successMessage1, successMessage2);
|
||||
|
||||
// Run with relocation of only the second jar - should FAIL
|
||||
runtimeTest(appJar + File.pathSeparator + destPath2.toString(),
|
||||
"HelloMore", 1, failedMessage);
|
||||
|
||||
// Run with relocation of only the first jar - should FAIL
|
||||
runtimeTest(destPath.toString() + File.pathSeparator + appJar2,
|
||||
"HelloMore", 1, failedMessage);
|
||||
|
||||
// Dump CDS archive with the first jar relocated.
|
||||
jars = destPath.toString() + File.pathSeparator + appJar2;
|
||||
TestCommon.testDump(jars, TestCommon.list("Hello", "HelloMore"));
|
||||
|
||||
// Run with first jar relocated - should PASS
|
||||
runtimeTest(destPath.toString() + File.pathSeparator + appJar2,
|
||||
"HelloMore", 0, successMessage1, successMessage2);
|
||||
|
||||
// Run with both jars relocated - should FAIL
|
||||
runtimeTest(destPath.toString() + File.pathSeparator + destPath2.toString(),
|
||||
"HelloMore", 1, failedMessage);
|
||||
|
||||
// Copy hello.jar to USER_DIR/a
|
||||
destPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "a");
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/aa
|
||||
destPath2 = CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "aa");
|
||||
|
||||
// Dump CDS archive with the both jar files relocated
|
||||
// appJar to USER_DIR/a
|
||||
// appJar2 to USER_DIR/aa
|
||||
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
|
||||
TestCommon.testDump(jars, TestCommon.list("Hello", "HelloMore"));
|
||||
|
||||
// Copy hello.jar to USER_DIR/x/a
|
||||
Path runPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/aa
|
||||
Path runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "aa");
|
||||
|
||||
// Run with both jars relocated to USER_DIR/x/a and USER_DIR/x/aa dirs - should PASS
|
||||
runtimeTest(runPath.toString() + File.pathSeparator + runPath2.toString(),
|
||||
"HelloMore", 0, successMessage1, successMessage2);
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/a
|
||||
runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
|
||||
|
||||
// Run with both jars relocated to USER_DIR/x/a dir - should FAIL
|
||||
runtimeTest(runPath.toString() + File.pathSeparator + runPath2.toString(),
|
||||
"HelloMore", 1, failedMessage);
|
||||
}
|
||||
}
|
@ -271,5 +271,15 @@ public class SharedArchiveConsistency {
|
||||
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
|
||||
System.out.println("new baseArchiveNameOffset = " + baseArchiveNameOffset);
|
||||
testAndCheck(verifyExecArgs);
|
||||
|
||||
// modify _common_app_classpath_size
|
||||
String wrongCommonAppClasspathOffset = startNewArchive("wrongCommonAppClasspathOffset");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, wrongCommonAppClasspathOffset);
|
||||
int commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa);
|
||||
System.out.println(" commonAppClasspathPrefixSize = " + commonAppClasspathPrefixSize);
|
||||
CDSArchiveUtils.writeData(copiedJsa, CDSArchiveUtils.offsetCommonAppClasspathPrefixSize(), commonAppClasspathPrefixSize * 2);
|
||||
commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa);
|
||||
System.out.println("new commonAppClasspathPrefixSize = " + commonAppClasspathPrefixSize);
|
||||
testAndCheck(verifyExecArgs);
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,23 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Base archive name is damaged");
|
||||
|
||||
startTest("5. Make base archive name not terminated with '\0'");
|
||||
startTest("5a. Modify common app classpath size");
|
||||
String wrongCommonAppClasspathOffset = getNewArchiveName("wrongCommonAppClasspathOffset");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongCommonAppClasspathOffset);
|
||||
int commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa);
|
||||
CDSArchiveUtils.writeData(copiedJsa, CDSArchiveUtils.offsetCommonAppClasspathPrefixSize(), -1);
|
||||
runTwo(baseArchiveName, wrongCommonAppClasspathOffset,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"common app classpath prefix len < 0");
|
||||
|
||||
startTest("5b. Modify common app classpath size, run with -XX:-VerifySharedSpaces");
|
||||
VERIFY_CRC = true;
|
||||
runTwo(baseArchiveName, modTop,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Header checksum verification failed");
|
||||
VERIFY_CRC = false;
|
||||
|
||||
startTest("6. Make base archive name not terminated with '\0'");
|
||||
String wrongBaseName = getNewArchiveName("wrongBaseName");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseName);
|
||||
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
|
||||
@ -162,7 +178,7 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Base archive name is damaged");
|
||||
|
||||
startTest("6. Modify base archive name to a file that doesn't exist");
|
||||
startTest("7. Modify base archive name to a file that doesn't exist");
|
||||
String wrongBaseName2 = getNewArchiveName("wrongBaseName2");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseName2);
|
||||
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
|
||||
@ -182,7 +198,7 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
// -XX:SharedArchiveFile=non-exist-base.jsa:top.jsa
|
||||
// -XX:SharedArchiveFile=base.jsa:non-exist-top.jsa
|
||||
// -XX:SharedArchiveFile=non-exist-base.jsa:non-exist-top.jsa
|
||||
startTest("7. Non-exist base archive");
|
||||
startTest("8. Non-exist base archive");
|
||||
String nonExistBase = "non-exist-base.jsa";
|
||||
File nonExistBaseFile = new File(nonExistBase);
|
||||
nonExistBaseFile.delete();
|
||||
@ -190,7 +206,7 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistBase + ")");
|
||||
|
||||
startTest("8. Non-exist top archive");
|
||||
startTest("9. Non-exist top archive");
|
||||
String nonExistTop = "non-exist-top.jsa";
|
||||
File nonExistTopFile = new File(nonExistTop);
|
||||
nonExistTopFile.delete();
|
||||
@ -198,7 +214,7 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistTop + ")");
|
||||
|
||||
startTest("9. nost-exist-base and non-exist-top");
|
||||
startTest("10. nost-exist-base and non-exist-top");
|
||||
runTwo(nonExistBase, nonExistTop,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistBase + ")");
|
||||
@ -209,7 +225,7 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
if (!isUseSharedSpacesDisabled()) {
|
||||
new File(baseArchiveName).delete();
|
||||
|
||||
startTest("10. -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=" + topArchiveName);
|
||||
startTest("11. -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=" + topArchiveName);
|
||||
run(topArchiveName,
|
||||
"-Xshare:auto",
|
||||
"-XX:+AutoCreateSharedArchive",
|
||||
@ -219,7 +235,7 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
output.shouldContain("warning: -XX:+AutoCreateSharedArchive is unsupported when base CDS archive is not loaded");
|
||||
});
|
||||
|
||||
startTest("11. -XX:SharedArchiveFile=" + topArchiveName + " -XX:ArchiveClassesAtExit=" + getNewArchiveName("top3"));
|
||||
startTest("12. -XX:SharedArchiveFile=" + topArchiveName + " -XX:ArchiveClassesAtExit=" + getNewArchiveName("top3"));
|
||||
run(topArchiveName,
|
||||
"-Xshare:auto",
|
||||
"-XX:ArchiveClassesAtExit=" + getNewArchiveName("top3"),
|
||||
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 8279366
|
||||
* @summary Test app class paths checking with the longest common path taken into account.
|
||||
* @requires vm.cds
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @compile ../test-classes/Hello.java
|
||||
* @compile ../test-classes/HelloMore.java
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. CommonAppClasspath
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
|
||||
public class CommonAppClasspath extends DynamicArchiveTestBase {
|
||||
|
||||
private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir());
|
||||
private static final String failedMessage = "shared class paths mismatch";
|
||||
private static final String successMessage1 = "Hello source: shared objects file";
|
||||
private static final String successMessage2 = "HelloMore source: shared objects file";
|
||||
|
||||
private static void runtimeTest(String topArchiveName, String classPath,
|
||||
String mainClass, int expectedExitValue,
|
||||
String ... checkMessages) throws Exception {
|
||||
CDSTestUtils.Result result = run(topArchiveName,
|
||||
"-Xlog:class+load",
|
||||
"-Xlog:cds+dynamic=debug,cds=debug",
|
||||
"-cp", classPath, mainClass);
|
||||
if (expectedExitValue == 0) {
|
||||
result.assertNormalExit( output -> {
|
||||
for (String s : checkMessages) {
|
||||
output.shouldContain(s);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
result.assertAbnormalExit( output -> {
|
||||
for (String s : checkMessages) {
|
||||
output.shouldContain(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
runTest(CommonAppClasspath::testDefaultBase);
|
||||
}
|
||||
|
||||
static void testDefaultBase() throws Exception {
|
||||
String topArchiveName = getNewArchiveName("top");
|
||||
doTest(topArchiveName);
|
||||
}
|
||||
|
||||
private static void doTest(String topArchiveName) throws Exception {
|
||||
String mainClass = "HelloMore";
|
||||
String appJar = JarBuilder.getOrCreateHelloJar();
|
||||
String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", mainClass);
|
||||
|
||||
// dump an archive with only appJar in the original location
|
||||
String jars = appJar;
|
||||
dump(topArchiveName,
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+dynamic=debug",
|
||||
"-cp", jars, "Hello")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Written dynamic archive 0x");
|
||||
});
|
||||
|
||||
// copy hello.jar to tmp dir
|
||||
Path destPath = CDSTestUtils.copyFile(appJar, System.getProperty("java.io.tmpdir"));
|
||||
|
||||
// Run with appJar relocated to tmp dir - should PASS
|
||||
jars = destPath.toString();
|
||||
runtimeTest(topArchiveName, jars, "Hello", 0, successMessage1);
|
||||
|
||||
// dump an archive with both jars in the original location
|
||||
jars = appJar + File.pathSeparator + appJar2;
|
||||
dump(topArchiveName,
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+dynamic=debug",
|
||||
"-cp", jars, mainClass)
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Written dynamic archive 0x");
|
||||
});
|
||||
|
||||
// copy hello.jar to USER_DIR/deploy
|
||||
String newDir = USER_DIR.toString() + File.separator + "deploy";
|
||||
destPath = CDSTestUtils.copyFile(appJar, newDir);
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/deploy
|
||||
Path destPath2 = CDSTestUtils.copyFile(appJar2, newDir);
|
||||
|
||||
// Run with both jars relocated to USER_DIR/dpeloy - should PASS
|
||||
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
|
||||
runtimeTest(topArchiveName, jars, mainClass, 0, successMessage1, successMessage2);
|
||||
|
||||
// Run with relocation of only the second jar - should FAIL
|
||||
jars = appJar + File.pathSeparator + destPath2.toString();
|
||||
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
|
||||
|
||||
// Run with relocation of only the first jar - should FAIL
|
||||
jars = destPath.toString() + File.pathSeparator + appJar2;
|
||||
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
|
||||
|
||||
// Dump CDS archive with the first jar relocated.
|
||||
jars = destPath.toString() + File.pathSeparator + appJar2;
|
||||
dump(topArchiveName,
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+dynamic=debug",
|
||||
"-cp", jars, mainClass)
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Written dynamic archive 0x");
|
||||
});
|
||||
|
||||
// Run with first jar relocated - should PASS
|
||||
jars = destPath.toString() + File.pathSeparator + appJar2;
|
||||
runtimeTest(topArchiveName, jars, mainClass, 0, successMessage1, successMessage2);
|
||||
|
||||
// Run with both jars relocated - should FAIL
|
||||
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
|
||||
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
|
||||
|
||||
// Copy hello.jar to USER_DIR/a
|
||||
destPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "a");
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/aa
|
||||
destPath2 = CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "aa");
|
||||
|
||||
// Dump CDS archive with the both jar files relocated
|
||||
// appJar to USER_DIR/a
|
||||
// appJar2 to USER_DIR/aa
|
||||
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
|
||||
dump(topArchiveName,
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+dynamic=debug",
|
||||
"-cp", jars, mainClass)
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Written dynamic archive 0x");
|
||||
});
|
||||
|
||||
// Copy hello.jar to USER_DIR/x/a
|
||||
Path runPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/aa
|
||||
Path runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "aa");
|
||||
|
||||
// Run with both jars relocated to USER_DIR/x/a and USER_DIR/x/aa dirs - should PASS
|
||||
jars = runPath.toString() + File.pathSeparator + runPath2.toString();
|
||||
runtimeTest(topArchiveName, jars, mainClass, 0, successMessage1, successMessage2);
|
||||
|
||||
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/a
|
||||
runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
|
||||
|
||||
// Run with both jars relocated to USER_DIR/x/a dir - should FAIL
|
||||
jars = runPath.toString() + File.pathSeparator + runPath2.toString();
|
||||
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
|
||||
}
|
||||
}
|
@ -53,6 +53,7 @@ public class CDSArchiveUtils {
|
||||
private static int offsetCrc; // offset of GenericCDSFileMapHeader::_crc
|
||||
private static int offsetVersion; // offset of GenericCDSFileMapHeader::_version
|
||||
private static int offsetHeaderSize; // offset of GenericCDSFileMapHeader::_header_size
|
||||
private static int offsetCommonAppClasspathPrefixSize;// offset of GenericCDSFileMapHeader::_common_app_classpath_size
|
||||
private static int offsetBaseArchiveNameOffset;// offset of GenericCDSFileMapHeader::_base_archive_name_offset
|
||||
private static int offsetBaseArchiveNameSize; // offset of GenericCDSFileMapHeader::_base_archive_name_size
|
||||
private static int offsetJvmIdent; // offset of FileMapHeader::_jvm_ident
|
||||
@ -93,6 +94,7 @@ public class CDSArchiveUtils {
|
||||
offsetCrc = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_crc");
|
||||
offsetVersion = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_version");
|
||||
offsetHeaderSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_header_size");
|
||||
offsetCommonAppClasspathPrefixSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_common_app_classpath_prefix_size");
|
||||
offsetBaseArchiveNameOffset = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_base_archive_name_offset");
|
||||
offsetBaseArchiveNameSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_base_archive_name_size");
|
||||
offsetJvmIdent = wb.getCDSOffsetForName("FileMapHeader::_jvm_ident");
|
||||
@ -131,6 +133,7 @@ public class CDSArchiveUtils {
|
||||
public static int offsetCrc() { return offsetCrc; }
|
||||
public static int offsetVersion() { return offsetVersion; }
|
||||
public static int offsetHeaderSize() { return offsetHeaderSize; }
|
||||
public static int offsetCommonAppClasspathPrefixSize() { return offsetCommonAppClasspathPrefixSize; }
|
||||
public static int offsetBaseArchiveNameOffset() { return offsetBaseArchiveNameOffset; }
|
||||
public static int offsetBaseArchiveNameSize() { return offsetBaseArchiveNameSize; }
|
||||
public static int offsetJvmIdent() { return offsetJvmIdent; }
|
||||
@ -158,6 +161,10 @@ public class CDSArchiveUtils {
|
||||
return alignUpWithAlignment(size);
|
||||
}
|
||||
|
||||
public static int commonAppClasspathPrefixSize(File jsaFile) throws Exception {
|
||||
return (int)readInt(jsaFile, offsetCommonAppClasspathPrefixSize, 4);
|
||||
}
|
||||
|
||||
public static int baseArchiveNameOffset(File jsaFile) throws Exception {
|
||||
return (int)readInt(jsaFile, offsetBaseArchiveNameOffset, 4);
|
||||
}
|
||||
|
@ -28,7 +28,11 @@ import java.io.FileOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@ -761,4 +765,20 @@ public class CDSTestUtils {
|
||||
System.out.println(" ]");
|
||||
return new ProcessBuilder(args);
|
||||
}
|
||||
|
||||
public static Path copyFile(String srcFile, String destDir) throws Exception {
|
||||
int idx = srcFile.lastIndexOf(File.separator);
|
||||
String jarName = srcFile.substring(idx + 1);
|
||||
Path srcPath = Paths.get(jarName);
|
||||
Path newPath = Paths.get(destDir);
|
||||
Path newDir;
|
||||
if (!Files.exists(newPath)) {
|
||||
newDir = Files.createDirectories(newPath);
|
||||
} else {
|
||||
newDir = newPath;
|
||||
}
|
||||
Path destPath = newDir.resolve(jarName);
|
||||
Files.copy(srcPath, destPath, REPLACE_EXISTING, COPY_ATTRIBUTES);
|
||||
return destPath;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user