mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-12 06:29:37 +00:00
8276769: -Xshare:auto should tolerate problems in the CDS archive
Reviewed-by: iklam, ccheung
This commit is contained in:
parent
79165b738d
commit
3e93e0b809
@ -121,7 +121,6 @@ void FileMapInfo::fail_continue(const char *msg, ...) {
|
||||
fail_exit(msg, ap);
|
||||
} else {
|
||||
if (log_is_enabled(Info, cds)) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(Log(cds)::info());
|
||||
ls.print("UseSharedSpaces: ");
|
||||
ls.vprint_cr(msg, ap);
|
||||
@ -1042,15 +1041,20 @@ void FileMapInfo::validate_non_existent_class_paths() {
|
||||
}
|
||||
}
|
||||
|
||||
// a utility class for checking file header
|
||||
// A utility class for reading/validating the GenericCDSFileMapHeader portion of
|
||||
// a CDS archive's header. The file header of all CDS archives with versions from
|
||||
// CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION (12) are guaranteed to always start
|
||||
// with GenericCDSFileMapHeader. This makes it possible to read important information
|
||||
// from a CDS archive created by a different version of HotSpot, so that we can
|
||||
// automatically regenerate the archive as necessary (JDK-8261455).
|
||||
class FileHeaderHelper {
|
||||
int _fd;
|
||||
GenericCDSFileMapHeader _header;
|
||||
bool _is_valid;
|
||||
GenericCDSFileMapHeader* _header;
|
||||
const char* _base_archive_name;
|
||||
|
||||
public:
|
||||
FileHeaderHelper() {
|
||||
_fd = -1;
|
||||
}
|
||||
FileHeaderHelper() : _fd(-1), _is_valid(false), _header(nullptr), _base_archive_name(nullptr) {}
|
||||
|
||||
~FileHeaderHelper() {
|
||||
if (_fd != -1) {
|
||||
@ -1059,8 +1063,10 @@ public:
|
||||
}
|
||||
|
||||
bool initialize(const char* archive_name) {
|
||||
log_info(cds)("Opening shared archive: %s", archive_name);
|
||||
_fd = os::open(archive_name, O_RDONLY | O_BINARY, 0);
|
||||
if (_fd < 0) {
|
||||
FileMapInfo::fail_continue("Specified shared archive not found (%s)", archive_name);
|
||||
return false;
|
||||
}
|
||||
return initialize(_fd);
|
||||
@ -1069,117 +1075,185 @@ public:
|
||||
// for an already opened file, do not set _fd
|
||||
bool initialize(int fd) {
|
||||
assert(fd != -1, "Archive should be opened");
|
||||
|
||||
|
||||
// First read the generic header so we know the exact size of the actual header.
|
||||
GenericCDSFileMapHeader gen_header;
|
||||
size_t size = sizeof(GenericCDSFileMapHeader);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
size_t n = os::read(fd, (void*)&_header, (unsigned int)size);
|
||||
size_t n = os::read(fd, (void*)&gen_header, (unsigned int)size);
|
||||
if (n != size) {
|
||||
vm_exit_during_initialization("Unable to read generic CDS file map header from shared archive");
|
||||
FileMapInfo::fail_continue("Unable to read generic CDS file map header from shared archive");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gen_header._magic != CDS_ARCHIVE_MAGIC &&
|
||||
gen_header._magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||||
FileMapInfo::fail_continue("The shared archive file has a bad magic number: %#x", gen_header._magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gen_header._version < CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION) {
|
||||
FileMapInfo::fail_continue("Cannot handle shared archive file version %d. Must be at least %d",
|
||||
gen_header._version, CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t filelen = os::lseek(fd, 0, SEEK_END);
|
||||
if (gen_header._header_size >= filelen) {
|
||||
FileMapInfo::fail_continue("Archive file header larger than archive file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the actual header and perform more checks
|
||||
size = gen_header._header_size;
|
||||
_header = (GenericCDSFileMapHeader*)NEW_C_HEAP_ARRAY(char, size, mtInternal);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
n = os::read(fd, (void*)_header, (unsigned int)size);
|
||||
if (n != size) {
|
||||
FileMapInfo::fail_continue("Unable to read actual CDS file map header from shared archive");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!check_crc()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!check_and_init_base_archive_name()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All fields in the GenericCDSFileMapHeader has been validated.
|
||||
_is_valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
GenericCDSFileMapHeader* get_generic_file_header() {
|
||||
return &_header;
|
||||
assert(_header != nullptr && _is_valid, "must be a valid archive file");
|
||||
return _header;
|
||||
}
|
||||
|
||||
char* read_base_archive_name() {
|
||||
assert(_fd != -1, "Archive should be open");
|
||||
size_t name_size = _header._base_archive_name_size;
|
||||
assert(name_size != 0, "For non-default base archive, name size should be non-zero!");
|
||||
char* base_name = NEW_C_HEAP_ARRAY(char, name_size, mtInternal);
|
||||
lseek(_fd, _header._base_archive_name_offset, SEEK_SET); // position to correct offset.
|
||||
size_t n = os::read(_fd, base_name, (unsigned int)name_size);
|
||||
if (n != name_size) {
|
||||
log_info(cds)("Unable to read base archive name from archive");
|
||||
FREE_C_HEAP_ARRAY(char, base_name);
|
||||
return nullptr;
|
||||
const char* base_archive_name() {
|
||||
assert(_header != nullptr && _is_valid, "must be a valid archive file");
|
||||
return _base_archive_name;
|
||||
}
|
||||
|
||||
private:
|
||||
bool check_crc() {
|
||||
if (VerifySharedSpaces) {
|
||||
FileMapHeader* header = (FileMapHeader*)_header;
|
||||
int actual_crc = header->compute_crc();
|
||||
if (actual_crc != header->crc()) {
|
||||
log_info(cds)("_crc expected: %d", header->crc());
|
||||
log_info(cds)(" actual: %d", actual_crc);
|
||||
FileMapInfo::fail_continue("Header checksum verification failed.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (base_name[name_size - 1] != '\0' || strlen(base_name) != name_size - 1) {
|
||||
log_info(cds)("Base archive name is damaged");
|
||||
FREE_C_HEAP_ARRAY(char, base_name);
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_and_init_base_archive_name() {
|
||||
unsigned int name_offset = _header->_base_archive_name_offset;
|
||||
unsigned int name_size = _header->_base_archive_name_size;
|
||||
unsigned int header_size = _header->_header_size;
|
||||
|
||||
if (name_offset + name_size < name_offset) {
|
||||
FileMapInfo::fail_continue("base_archive_name offset/size overflow: " UINT32_FORMAT "/" UINT32_FORMAT,
|
||||
name_offset, name_size);
|
||||
return false;
|
||||
}
|
||||
if (!os::file_exists(base_name)) {
|
||||
log_info(cds)("Base archive %s does not exist", base_name);
|
||||
FREE_C_HEAP_ARRAY(char, base_name);
|
||||
return nullptr;
|
||||
if (_header->_magic == CDS_ARCHIVE_MAGIC) {
|
||||
if (name_offset != 0) {
|
||||
FileMapInfo::fail_continue("static shared archive must have zero _base_archive_name_offset");
|
||||
return false;
|
||||
}
|
||||
if (name_size != 0) {
|
||||
FileMapInfo::fail_continue("static shared archive must have zero _base_archive_name_size");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(_header->_magic == CDS_DYNAMIC_ARCHIVE_MAGIC, "must be");
|
||||
if ((name_size == 0 && name_offset != 0) ||
|
||||
(name_size != 0 && name_offset == 0)) {
|
||||
// If either is zero, both must be zero. This indicates that we are using the default base archive.
|
||||
FileMapInfo::fail_continue("Invalid base_archive_name offset/size: " UINT32_FORMAT "/" UINT32_FORMAT,
|
||||
name_offset, name_size);
|
||||
return false;
|
||||
}
|
||||
if (name_size > 0) {
|
||||
if (name_offset + name_size > header_size) {
|
||||
FileMapInfo::fail_continue("Invalid base_archive_name offset/size (out of range): "
|
||||
UINT32_FORMAT " + " UINT32_FORMAT " > " UINT32_FORMAT ,
|
||||
name_offset, name_size, header_size);
|
||||
return false;
|
||||
}
|
||||
const char* name = ((const char*)_header) + _header->_base_archive_name_offset;
|
||||
if (name[name_size - 1] != '\0' || strlen(name) != name_size - 1) {
|
||||
FileMapInfo::fail_continue("Base archive name is damaged");
|
||||
return false;
|
||||
}
|
||||
if (!os::file_exists(name)) {
|
||||
FileMapInfo::fail_continue("Base archive %s does not exist", name);
|
||||
return false;
|
||||
}
|
||||
_base_archive_name = name;
|
||||
}
|
||||
}
|
||||
return base_name;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
|
||||
FileHeaderHelper file_helper;
|
||||
if (!file_helper.initialize(archive_name)) {
|
||||
// do not vm_exit_during_initialization here because Arguments::init_shared_archive_paths()
|
||||
// requires a shared archive name. The open_for_read() function will log a message regarding
|
||||
// failure in opening a shared archive.
|
||||
// Any errors are reported by fail_continue().
|
||||
return false;
|
||||
}
|
||||
|
||||
GenericCDSFileMapHeader* header = file_helper.get_generic_file_header();
|
||||
if (is_static) {
|
||||
if (header->_magic != CDS_ARCHIVE_MAGIC) {
|
||||
vm_exit_during_initialization("Not a base shared archive", archive_name);
|
||||
return false;
|
||||
}
|
||||
if (header->_base_archive_name_offset != 0) {
|
||||
log_info(cds)("_base_archive_name_offset should be 0");
|
||||
log_info(cds)("_base_archive_name_offset = " UINT32_FORMAT, header->_base_archive_name_offset);
|
||||
fail_continue("Not a base shared archive: %s", archive_name);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||||
vm_exit_during_initialization("Not a top shared archive", archive_name);
|
||||
fail_continue("Not a top shared archive: %s", archive_name);
|
||||
return false;
|
||||
}
|
||||
unsigned int name_size = header->_base_archive_name_size;
|
||||
unsigned int name_offset = header->_base_archive_name_offset;
|
||||
unsigned int header_size = header->_header_size;
|
||||
if (name_offset + name_size != header_size) {
|
||||
log_info(cds)("_header_size should be equal to _base_archive_name_offset plus _base_archive_name_size");
|
||||
log_info(cds)(" _base_archive_name_size = " UINT32_FORMAT, name_size);
|
||||
log_info(cds)(" _base_archive_name_offset = " UINT32_FORMAT, name_offset);
|
||||
log_info(cds)(" _header_size = " UINT32_FORMAT, header_size);
|
||||
return false;
|
||||
}
|
||||
char* base_name = file_helper.read_base_archive_name();
|
||||
if (base_name == nullptr) {
|
||||
return false;
|
||||
}
|
||||
FREE_C_HEAP_ARRAY(char, base_name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return value:
|
||||
// false:
|
||||
// <archive_name> is not a valid archive. *base_archive_name is set to null.
|
||||
// true && (*base_archive_name) == NULL:
|
||||
// <archive_name> is a valid static archive.
|
||||
// true && (*base_archive_name) != NULL:
|
||||
// <archive_name> is a valid dynamic archive.
|
||||
bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
|
||||
char** base_archive_name) {
|
||||
FileHeaderHelper file_helper;
|
||||
*base_archive_name = NULL;
|
||||
|
||||
if (!file_helper.initialize(archive_name)) {
|
||||
return false;
|
||||
}
|
||||
GenericCDSFileMapHeader* header = file_helper.get_generic_file_header();
|
||||
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||||
// Not a dynamic header, no need to proceed further.
|
||||
return false;
|
||||
assert(header->_magic == CDS_ARCHIVE_MAGIC, "must be");
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((header->_base_archive_name_size == 0 && header->_base_archive_name_offset != 0) ||
|
||||
(header->_base_archive_name_size != 0 && header->_base_archive_name_offset == 0)) {
|
||||
fail_continue("Default base archive not set correct");
|
||||
return false;
|
||||
}
|
||||
if (header->_base_archive_name_size == 0 &&
|
||||
header->_base_archive_name_offset == 0) {
|
||||
const char* base = file_helper.base_archive_name();
|
||||
if (base == nullptr) {
|
||||
*base_archive_name = Arguments::get_default_shared_archive_path();
|
||||
} else {
|
||||
// read the base archive name
|
||||
*base_archive_name = file_helper.read_base_archive_name();
|
||||
if (*base_archive_name == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*base_archive_name = os::strdup_check_oom(base);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1247,16 +1321,6 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VerifySharedSpaces) {
|
||||
int expected_crc = header()->compute_crc();
|
||||
if (expected_crc != header()->crc()) {
|
||||
log_info(cds)("_crc expected: %d", expected_crc);
|
||||
log_info(cds)(" actual: %d", header()->crc());
|
||||
FileMapInfo::fail_continue("Header checksum verification failed.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_file_offset = header()->header_size(); // accounts for the size of _base_archive_name
|
||||
|
||||
if (is_static()) {
|
||||
@ -1294,9 +1358,9 @@ bool FileMapInfo::open_for_read() {
|
||||
int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT) {
|
||||
fail_continue("Specified shared archive not found (%s).", _full_path);
|
||||
fail_continue("Specified shared archive not found (%s)", _full_path);
|
||||
} else {
|
||||
fail_continue("Failed to open shared archive file (%s).",
|
||||
fail_continue("Failed to open shared archive file (%s)",
|
||||
os::strerror(errno));
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
#define NUM_CDS_REGIONS 7 // this must be the same as MetaspaceShared::n_regions
|
||||
#define CDS_ARCHIVE_MAGIC 0xf00baba2
|
||||
#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8
|
||||
#define CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION 12
|
||||
#define CURRENT_CDS_ARCHIVE_VERSION 12
|
||||
|
||||
typedef struct CDSFileMapRegion {
|
||||
@ -59,7 +60,8 @@ typedef struct CDSFileMapRegion {
|
||||
char* _mapped_base; // Actually mapped address (NULL if this region is not mapped).
|
||||
} CDSFileMapRegion;
|
||||
|
||||
// This portion of the archive file header must remain unchanged for _version >= 12.
|
||||
// This portion of the archive file header must remain unchanged for
|
||||
// _version >= CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION (12).
|
||||
// This makes it possible to read important information from a CDS archive created by
|
||||
// a different version of HotSpot, so that we can automatically regenerate the archive as necessary.
|
||||
typedef struct GenericCDSFileMapHeader {
|
||||
|
||||
@ -3449,7 +3449,7 @@ jint Arguments::parse_options_buffer(const char* name, char* buffer, const size_
|
||||
return vm_args->set_args(&options);
|
||||
}
|
||||
|
||||
jint Arguments::set_shared_spaces_flags_and_archive_paths() {
|
||||
void Arguments::set_shared_spaces_flags_and_archive_paths() {
|
||||
if (DumpSharedSpaces) {
|
||||
if (RequireSharedSpaces) {
|
||||
warning("Cannot dump shared archive while using shared archive");
|
||||
@ -3459,9 +3459,12 @@ jint Arguments::set_shared_spaces_flags_and_archive_paths() {
|
||||
#if INCLUDE_CDS
|
||||
// Initialize shared archive paths which could include both base and dynamic archive paths
|
||||
// This must be after set_ergonomics_flags() called so flag UseCompressedOops is set properly.
|
||||
init_shared_archive_paths();
|
||||
//
|
||||
// UseSharedSpaces may be disabled if -XX:SharedArchiveFile is invalid.
|
||||
if (DumpSharedSpaces || UseSharedSpaces) {
|
||||
init_shared_archive_paths();
|
||||
}
|
||||
#endif // INCLUDE_CDS
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
@ -3510,7 +3513,9 @@ void Arguments::extract_shared_archive_paths(const char* archive_path,
|
||||
char* cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
||||
strncpy(cur_path, begin_ptr, len);
|
||||
cur_path[len] = '\0';
|
||||
FileMapInfo::check_archive((const char*)cur_path, true /*is_static*/);
|
||||
if (!FileMapInfo::check_archive((const char*)cur_path, true /*is_static*/)) {
|
||||
return;
|
||||
}
|
||||
*base_archive_path = cur_path;
|
||||
|
||||
begin_ptr = ++end_ptr;
|
||||
@ -3522,7 +3527,9 @@ void Arguments::extract_shared_archive_paths(const char* archive_path,
|
||||
len = end_ptr - begin_ptr;
|
||||
cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
||||
strncpy(cur_path, begin_ptr, len + 1);
|
||||
FileMapInfo::check_archive((const char*)cur_path, false /*is_static*/);
|
||||
if (!FileMapInfo::check_archive((const char*)cur_path, false /*is_static*/)) {
|
||||
return;
|
||||
}
|
||||
*top_archive_path = cur_path;
|
||||
}
|
||||
|
||||
@ -3566,17 +3573,26 @@ void Arguments::init_shared_archive_paths() {
|
||||
"Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option");
|
||||
}
|
||||
if (archives == 1) {
|
||||
char* temp_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments);
|
||||
char* base_archive_path = NULL;
|
||||
bool success =
|
||||
FileMapInfo::get_base_archive_name_from_header(temp_archive_path, &SharedArchivePath);
|
||||
FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path);
|
||||
if (!success) {
|
||||
SharedArchivePath = temp_archive_path;
|
||||
no_shared_spaces("invalid archive");
|
||||
} else if (base_archive_path == NULL) {
|
||||
// User has specified a single archive, which is a static archive.
|
||||
SharedArchivePath = const_cast<char *>(SharedArchiveFile);
|
||||
} else {
|
||||
SharedDynamicArchivePath = temp_archive_path;
|
||||
// User has specified a single archive, which is a dynamic archive.
|
||||
SharedDynamicArchivePath = const_cast<char *>(SharedArchiveFile);
|
||||
SharedArchivePath = base_archive_path; // has been c-heap allocated.
|
||||
}
|
||||
} else {
|
||||
extract_shared_archive_paths((const char*)SharedArchiveFile,
|
||||
&SharedArchivePath, &SharedDynamicArchivePath);
|
||||
if (SharedArchivePath == NULL) {
|
||||
assert(SharedDynamicArchivePath == NULL, "must be");
|
||||
no_shared_spaces("invalid archive");
|
||||
}
|
||||
}
|
||||
|
||||
if (SharedDynamicArchivePath != nullptr) {
|
||||
@ -4049,8 +4065,7 @@ jint Arguments::apply_ergo() {
|
||||
|
||||
GCConfig::arguments()->initialize();
|
||||
|
||||
result = set_shared_spaces_flags_and_archive_paths();
|
||||
if (result != JNI_OK) return result;
|
||||
set_shared_spaces_flags_and_archive_paths();
|
||||
|
||||
// Initialize Metaspace flags and alignments
|
||||
Metaspace::ergo_initialize();
|
||||
|
||||
@ -366,7 +366,7 @@ class Arguments : AllStatic {
|
||||
static void set_use_compressed_oops();
|
||||
static void set_use_compressed_klass_ptrs();
|
||||
static jint set_ergonomics_flags();
|
||||
static jint set_shared_spaces_flags_and_archive_paths();
|
||||
static void set_shared_spaces_flags_and_archive_paths();
|
||||
// Limits the given heap size by the maximum amount of virtual
|
||||
// memory this process is currently allowed to use. It also takes
|
||||
// the virtual-to-physical ratio of the current GC into account.
|
||||
|
||||
@ -112,7 +112,8 @@ public class SharedArchiveConsistency {
|
||||
// test, should pass
|
||||
System.out.println("1. Normal, should pass but may fail\n");
|
||||
|
||||
String[] execArgs = {"-Xlog:cds=debug", "-cp", jarFile, "Hello"};
|
||||
// disable VerifySharedSpaces, it may be turned on by jtreg args
|
||||
String[] execArgs = {"-Xlog:cds=debug", "-XX:-VerifySharedSpaces", "-cp", jarFile, "Hello"};
|
||||
// tests that corrupt contents of the archive need to run with
|
||||
// VerifySharedSpaces enabled to detect inconsistencies
|
||||
String[] verifyExecArgs = {"-Xlog:cds", "-XX:+VerifySharedSpaces", "-cp", jarFile, "Hello"};
|
||||
@ -172,7 +173,7 @@ public class SharedArchiveConsistency {
|
||||
System.out.println("\n2d. Corrupt _version, should fail\n");
|
||||
String modVersion = startNewArchive("modify-version");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, modVersion);
|
||||
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), 0x00000000);
|
||||
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), 0x3FFFFFFF);
|
||||
output = shareAuto ? TestCommon.execAuto(execArgs) : TestCommon.execCommon(execArgs);
|
||||
output.shouldContain("The shared archive file has the wrong version");
|
||||
output.shouldNotContain("Checksum verification failed");
|
||||
@ -180,6 +181,17 @@ public class SharedArchiveConsistency {
|
||||
output.shouldContain(HELLO_WORLD);
|
||||
}
|
||||
|
||||
System.out.println("\n2e. Corrupt _version, should fail\n");
|
||||
String modVersion2 = startNewArchive("modify-version2");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, modVersion2);
|
||||
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), 0x00000000);
|
||||
output = shareAuto ? TestCommon.execAuto(execArgs) : TestCommon.execCommon(execArgs);
|
||||
output.shouldContain("Cannot handle shared archive file version 0. Must be at least 12");
|
||||
output.shouldNotContain("Checksum verification failed");
|
||||
if (shareAuto) {
|
||||
output.shouldContain(HELLO_WORLD);
|
||||
}
|
||||
|
||||
// modify content inside regions
|
||||
System.out.println("\n3. Corrupt Content, should fail\n");
|
||||
for (int i=0; i<num_regions; i++) {
|
||||
|
||||
@ -30,7 +30,8 @@
|
||||
* @build Hello sun.hotspot.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ArchiveConsistency
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ArchiveConsistency on
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ArchiveConsistency auto
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
@ -40,8 +41,15 @@ import jdk.test.lib.cds.CDSTestUtils;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
|
||||
public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
private static final String HELLO_WORLD = "Hello World";
|
||||
private static boolean isAuto;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 1 || (!args[0].equals("on") && !args[0].equals("auto"))) {
|
||||
throw new RuntimeException("Must have one arg either of \"on\" or \"auto\"");
|
||||
}
|
||||
isAuto = args[0].equals("auto");
|
||||
setAutoMode(isAuto);
|
||||
runTest(ArchiveConsistency::testCustomBase);
|
||||
}
|
||||
|
||||
@ -53,31 +61,39 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
doTest(baseArchiveName, topArchiveName);
|
||||
}
|
||||
|
||||
static boolean VERIFY_CRC = false;
|
||||
|
||||
static void runTwo(String base, String top,
|
||||
String jarName, String mainClassName, int exitValue,
|
||||
String jarName, String mainClassName, int expectedExitValue,
|
||||
String ... checkMessages) throws Exception {
|
||||
CDSTestUtils.Result result = run2(base, top,
|
||||
"-Xlog:cds",
|
||||
"-Xlog:cds+dynamic=debug",
|
||||
"-XX:+VerifySharedSpaces",
|
||||
VERIFY_CRC ? "-XX:+VerifySharedSpaces" : "-XX:-VerifySharedSpaces",
|
||||
"-cp",
|
||||
jarName,
|
||||
mainClassName);
|
||||
if (exitValue == 0) {
|
||||
if (expectedExitValue == 0) {
|
||||
result.assertNormalExit( output -> {
|
||||
for (String s : checkMessages) {
|
||||
output.shouldContain(s);
|
||||
}
|
||||
output.shouldContain(HELLO_WORLD);
|
||||
});
|
||||
} else {
|
||||
result.assertAbnormalExit( output -> {
|
||||
for (String s : checkMessages) {
|
||||
output.shouldContain(s);
|
||||
}
|
||||
output.shouldContain("Unable to use shared archive");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void startTest(String str) {
|
||||
System.out.println("\n" + str);
|
||||
}
|
||||
|
||||
private static void doTest(String baseArchiveName, String topArchiveName) throws Exception {
|
||||
String appJar = ClassFileInstaller.getJarPath("hello.jar");
|
||||
String mainClass = "Hello";
|
||||
@ -94,42 +110,36 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
throw new IOException(jsa + " does not exist!");
|
||||
}
|
||||
|
||||
// 1. Modify the CRC values in the header of the top archive.
|
||||
System.out.println("\n1. Modify the CRC values in the header of the top archive");
|
||||
startTest("1. Modify the CRC values in the header of the top archive");
|
||||
String modTop = getNewArchiveName("modTopRegionsCrc");
|
||||
File copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, modTop);
|
||||
CDSArchiveUtils.modifyAllRegionsCrc(copiedJsa);
|
||||
|
||||
VERIFY_CRC = true;
|
||||
runTwo(baseArchiveName, modTop,
|
||||
appJar, mainClass, 1,
|
||||
new String[] {"Header checksum verification failed",
|
||||
"Unable to use shared archive"});
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Header checksum verification failed");
|
||||
VERIFY_CRC = false;
|
||||
|
||||
// 2. Make header size larger than the archive size
|
||||
System.out.println("\n2. Make header size larger than the archive size");
|
||||
startTest("2. Make header size larger than the archive size");
|
||||
String largerHeaderSize = getNewArchiveName("largerHeaderSize");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, largerHeaderSize);
|
||||
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetHeaderSize(), (int)copiedJsa.length() + 1024);
|
||||
runTwo(baseArchiveName, largerHeaderSize,
|
||||
appJar, mainClass, 1,
|
||||
new String[] {"_header_size should be equal to _base_archive_name_offset plus _base_archive_name_size",
|
||||
"Unable to use shared archive"});
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Archive file header larger than archive file");
|
||||
|
||||
// 3. Make base archive path offset beyond of header size
|
||||
System.out.println("\n3. Make base archive path offset beyond of header size.");
|
||||
startTest("3. Make base archive name offset beyond of header size.");
|
||||
String wrongBaseArchiveNameOffset = getNewArchiveName("wrongBaseArchiveNameOffset");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseArchiveNameOffset);
|
||||
int fileHeaderSize = (int)CDSArchiveUtils.fileHeaderSize(copiedJsa);
|
||||
int baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
|
||||
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetBaseArchiveNameOffset(), baseArchiveNameOffset + 1024);
|
||||
runTwo(baseArchiveName, wrongBaseArchiveNameOffset,
|
||||
appJar, mainClass, 1,
|
||||
new String[] {"_header_size should be equal to _base_archive_name_offset plus _base_archive_name_size",
|
||||
"The shared archive file has an incorrect header size",
|
||||
"Unable to use shared archive"});
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Invalid base_archive_name offset/size (out of range)");
|
||||
|
||||
// 4. Make base archive path offset points to middle of name size
|
||||
System.out.println("\n4. Make base archive path offset points to middle of name size");
|
||||
startTest("4. Make base archive name offset points to middle of the base archive name");
|
||||
String wrongBaseNameOffset = getNewArchiveName("wrongBaseNameOffset");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseNameOffset);
|
||||
int baseArchiveNameSize = CDSArchiveUtils.baseArchiveNameSize(copiedJsa);
|
||||
@ -137,13 +147,10 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, baseArchiveNameOffset,
|
||||
baseArchiveNameOffset + baseArchiveNameSize/2);
|
||||
runTwo(baseArchiveName, wrongBaseNameOffset,
|
||||
appJar, mainClass, 1,
|
||||
new String[] {"An error has occurred while processing the shared archive file.",
|
||||
"Header checksum verification failed",
|
||||
"Unable to use shared archive"});
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Base archive name is damaged");
|
||||
|
||||
// 5. Make base archive name not terminated with '\0'
|
||||
System.out.println("\n5. Make base archive name not terminated with '\0'");
|
||||
startTest("5. Make base archive name not terminated with '\0'");
|
||||
String wrongBaseName = getNewArchiveName("wrongBaseName");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseName);
|
||||
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
|
||||
@ -152,12 +159,10 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
CDSArchiveUtils.writeData(copiedJsa, offset, new byte[] {(byte)'X'});
|
||||
|
||||
runTwo(baseArchiveName, wrongBaseName,
|
||||
appJar, mainClass, 1,
|
||||
new String[] {"Base archive name is damaged",
|
||||
"Header checksum verification failed"});
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Base archive name is damaged");
|
||||
|
||||
// 6. Modify base archive name to a file that doesn't exist.
|
||||
System.out.println("\n6. Modify base archive name to a file that doesn't exist");
|
||||
startTest("6. Modify base archive name to a file that doesn't exist");
|
||||
String wrongBaseName2 = getNewArchiveName("wrongBaseName2");
|
||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseName2);
|
||||
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
|
||||
@ -170,8 +175,32 @@ public class ArchiveConsistency extends DynamicArchiveTestBase {
|
||||
(new File(badName)).delete();
|
||||
|
||||
runTwo(baseArchiveName, wrongBaseName2,
|
||||
appJar, mainClass, 1,
|
||||
new String[] {"Base archive " + badName + " does not exist",
|
||||
"Header checksum verification failed"});
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Base archive " + badName + " does not exist");
|
||||
|
||||
// Following three tests:
|
||||
// -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");
|
||||
String nonExistBase = "non-exist-base.jsa";
|
||||
File nonExistBaseFile = new File(nonExistBase);
|
||||
nonExistBaseFile.delete();
|
||||
runTwo(nonExistBase, topArchiveName,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistBase + ")");
|
||||
|
||||
startTest("8. Non-exist top archive");
|
||||
String nonExistTop = "non-exist-top.jsa";
|
||||
File nonExistTopFile = new File(nonExistTop);
|
||||
nonExistTopFile.delete();
|
||||
runTwo(baseArchiveName, nonExistTop,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistTop + ")");
|
||||
|
||||
startTest("9. nost-exist-base and non-exist-top");
|
||||
runTwo(nonExistBase, nonExistTop,
|
||||
appJar, mainClass, isAuto ? 0 : 1,
|
||||
"Specified shared archive not found (" + nonExistBase + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ import sun.hotspot.WhiteBox;
|
||||
*/
|
||||
class DynamicArchiveTestBase {
|
||||
private static boolean executedIn_run = false;
|
||||
private static boolean autoMode = false; // -Xshare:auto
|
||||
|
||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
@ -47,6 +48,7 @@ class DynamicArchiveTestBase {
|
||||
public void run(String args[]) throws Exception;
|
||||
}
|
||||
|
||||
public static void setAutoMode(boolean val) { autoMode = val; }
|
||||
|
||||
/*
|
||||
* Tests for dynamic archives should be written using this pattern:
|
||||
@ -183,7 +185,7 @@ class DynamicArchiveTestBase {
|
||||
(topArchiveName == null) ? baseArchiveName :
|
||||
baseArchiveName + File.pathSeparator + topArchiveName;
|
||||
String[] cmdLine = TestCommon.concat(
|
||||
"-Xshare:on",
|
||||
autoMode ? "-Xshare:auto" : "-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + archiveFiles);
|
||||
cmdLine = TestCommon.concat(cmdLine, cmdLineSuffix);
|
||||
return execProcess("exec", null, cmdLine);
|
||||
@ -202,7 +204,7 @@ class DynamicArchiveTestBase {
|
||||
(topArchiveName == null) ? baseArchiveName :
|
||||
baseArchiveName + File.pathSeparator + topArchiveName;
|
||||
String[] cmdLine = TestCommon.concat(
|
||||
"-Xshare:on",
|
||||
autoMode ? "-Xshare:auto" : "-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + archiveFiles);
|
||||
cmdLine = TestCommon.concat(cmdLine, cmdLineSuffix);
|
||||
return execProcess("exec", jarDir, cmdLine);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user