8261455: Automatically generate the CDS archive if necessary
Reviewed-by: iklam, ccheung
This commit is contained in:
parent
d70545d710
commit
1228b2f1f8
@ -187,7 +187,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void DynamicArchiveBuilder::init_header() {
|
void DynamicArchiveBuilder::init_header() {
|
||||||
FileMapInfo* mapinfo = new FileMapInfo(false);
|
FileMapInfo* mapinfo = new FileMapInfo(_archive_name, false);
|
||||||
assert(FileMapInfo::dynamic_info() == mapinfo, "must be");
|
assert(FileMapInfo::dynamic_info() == mapinfo, "must be");
|
||||||
FileMapInfo* base_info = FileMapInfo::current_info();
|
FileMapInfo* base_info = FileMapInfo::current_info();
|
||||||
// header only be available after populate_header
|
// header only be available after populate_header
|
||||||
@ -327,7 +327,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data) {
|
|||||||
FileMapInfo* dynamic_info = FileMapInfo::dynamic_info();
|
FileMapInfo* dynamic_info = FileMapInfo::dynamic_info();
|
||||||
assert(dynamic_info != NULL, "Sanity");
|
assert(dynamic_info != NULL, "Sanity");
|
||||||
|
|
||||||
dynamic_info->open_for_write(_archive_name);
|
dynamic_info->open_for_write();
|
||||||
ArchiveBuilder::write_archive(dynamic_info, NULL, NULL, NULL, NULL);
|
ArchiveBuilder::write_archive(dynamic_info, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
address base = _requested_dynamic_archive_bottom;
|
address base = _requested_dynamic_archive_bottom;
|
||||||
|
@ -166,8 +166,9 @@ template <int N> static void get_header_version(char (&header_version) [N]) {
|
|||||||
assert(header_version[JVM_IDENT_MAX-1] == 0, "must be");
|
assert(header_version[JVM_IDENT_MAX-1] == 0, "must be");
|
||||||
}
|
}
|
||||||
|
|
||||||
FileMapInfo::FileMapInfo(bool is_static) {
|
FileMapInfo::FileMapInfo(const char* full_path, bool is_static) {
|
||||||
memset((void*)this, 0, sizeof(FileMapInfo));
|
memset((void*)this, 0, sizeof(FileMapInfo));
|
||||||
|
_full_path = full_path;
|
||||||
_is_static = is_static;
|
_is_static = is_static;
|
||||||
if (_is_static) {
|
if (_is_static) {
|
||||||
assert(_current_info == NULL, "must be singleton"); // not thread safe
|
assert(_current_info == NULL, "must be singleton"); // not thread safe
|
||||||
@ -188,6 +189,9 @@ FileMapInfo::~FileMapInfo() {
|
|||||||
assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe
|
assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe
|
||||||
_dynamic_archive_info = NULL;
|
_dynamic_archive_info = NULL;
|
||||||
}
|
}
|
||||||
|
if (_file_open) {
|
||||||
|
os::close(_fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileMapInfo::populate_header(size_t core_region_alignment) {
|
void FileMapInfo::populate_header(size_t core_region_alignment) {
|
||||||
@ -1049,11 +1053,20 @@ void FileMapInfo::validate_non_existent_class_paths() {
|
|||||||
class FileHeaderHelper {
|
class FileHeaderHelper {
|
||||||
int _fd;
|
int _fd;
|
||||||
bool _is_valid;
|
bool _is_valid;
|
||||||
|
bool _is_static;
|
||||||
GenericCDSFileMapHeader* _header;
|
GenericCDSFileMapHeader* _header;
|
||||||
|
const char* _archive_name;
|
||||||
const char* _base_archive_name;
|
const char* _base_archive_name;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileHeaderHelper() : _fd(-1), _is_valid(false), _header(nullptr), _base_archive_name(nullptr) {}
|
FileHeaderHelper(const char* archive_name, bool is_static) {
|
||||||
|
_fd = -1;
|
||||||
|
_is_valid = false;
|
||||||
|
_header = nullptr;
|
||||||
|
_base_archive_name = nullptr;
|
||||||
|
_archive_name = archive_name;
|
||||||
|
_is_static = is_static;
|
||||||
|
}
|
||||||
|
|
||||||
~FileHeaderHelper() {
|
~FileHeaderHelper() {
|
||||||
if (_fd != -1) {
|
if (_fd != -1) {
|
||||||
@ -1061,11 +1074,11 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initialize(const char* archive_name) {
|
bool initialize() {
|
||||||
log_info(cds)("Opening shared archive: %s", archive_name);
|
assert(_archive_name != nullptr, "Archive name is NULL");
|
||||||
_fd = os::open(archive_name, O_RDONLY | O_BINARY, 0);
|
_fd = os::open(_archive_name, O_RDONLY | O_BINARY, 0);
|
||||||
if (_fd < 0) {
|
if (_fd < 0) {
|
||||||
FileMapInfo::fail_continue("Specified shared archive not found (%s)", archive_name);
|
FileMapInfo::fail_continue("Specified shared archive not found (%s)", _archive_name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return initialize(_fd);
|
return initialize(_fd);
|
||||||
@ -1073,9 +1086,8 @@ public:
|
|||||||
|
|
||||||
// for an already opened file, do not set _fd
|
// for an already opened file, do not set _fd
|
||||||
bool initialize(int fd) {
|
bool initialize(int fd) {
|
||||||
assert(fd != -1, "Archive should be opened");
|
assert(_archive_name != nullptr, "Archive name is NULL");
|
||||||
|
assert(fd != -1, "Archive must be opened already");
|
||||||
|
|
||||||
// First read the generic header so we know the exact size of the actual header.
|
// First read the generic header so we know the exact size of the actual header.
|
||||||
GenericCDSFileMapHeader gen_header;
|
GenericCDSFileMapHeader gen_header;
|
||||||
size_t size = sizeof(GenericCDSFileMapHeader);
|
size_t size = sizeof(GenericCDSFileMapHeader);
|
||||||
@ -1098,6 +1110,11 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gen_header._version != CURRENT_CDS_ARCHIVE_VERSION) {
|
||||||
|
FileMapInfo::fail_continue("The shared archive file version %d does not match the required version %d",
|
||||||
|
gen_header._version, CURRENT_CDS_ARCHIVE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
size_t filelen = os::lseek(fd, 0, SEEK_END);
|
size_t filelen = os::lseek(fd, 0, SEEK_END);
|
||||||
if (gen_header._header_size >= filelen) {
|
if (gen_header._header_size >= filelen) {
|
||||||
FileMapInfo::fail_continue("Archive file header larger than archive file");
|
FileMapInfo::fail_continue("Archive file header larger than archive file");
|
||||||
@ -1203,28 +1220,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
|
|
||||||
FileHeaderHelper file_helper;
|
|
||||||
if (!file_helper.initialize(archive_name)) {
|
|
||||||
// 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) {
|
|
||||||
fail_continue("Not a base shared archive: %s", archive_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
|
||||||
fail_continue("Not a top shared archive: %s", archive_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return value:
|
// Return value:
|
||||||
// false:
|
// false:
|
||||||
// <archive_name> is not a valid archive. *base_archive_name is set to null.
|
// <archive_name> is not a valid archive. *base_archive_name is set to null.
|
||||||
@ -1234,15 +1229,18 @@ bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
|
|||||||
// <archive_name> is a valid dynamic archive.
|
// <archive_name> is a valid dynamic archive.
|
||||||
bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
|
bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
|
||||||
char** base_archive_name) {
|
char** base_archive_name) {
|
||||||
FileHeaderHelper file_helper;
|
FileHeaderHelper file_helper(archive_name, false);
|
||||||
*base_archive_name = NULL;
|
*base_archive_name = NULL;
|
||||||
|
|
||||||
if (!file_helper.initialize(archive_name)) {
|
if (!file_helper.initialize()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GenericCDSFileMapHeader* header = file_helper.get_generic_file_header();
|
GenericCDSFileMapHeader* header = file_helper.get_generic_file_header();
|
||||||
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||||||
assert(header->_magic == CDS_ARCHIVE_MAGIC, "must be");
|
assert(header->_magic == CDS_ARCHIVE_MAGIC, "must be");
|
||||||
|
if (AutoCreateSharedArchive) {
|
||||||
|
log_warning(cds)("AutoCreateSharedArchive is ignored because %s is a static archive", archive_name);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,19 +1257,23 @@ bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
|
|||||||
// Read the FileMapInfo information from the file.
|
// Read the FileMapInfo information from the file.
|
||||||
|
|
||||||
bool FileMapInfo::init_from_file(int fd) {
|
bool FileMapInfo::init_from_file(int fd) {
|
||||||
FileHeaderHelper file_helper;
|
FileHeaderHelper file_helper(_full_path, _is_static);
|
||||||
if (!file_helper.initialize(fd)) {
|
if (!file_helper.initialize(fd)) {
|
||||||
fail_continue("Unable to read the file header.");
|
fail_continue("Unable to read the file header.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GenericCDSFileMapHeader* gen_header = file_helper.get_generic_file_header();
|
GenericCDSFileMapHeader* gen_header = file_helper.get_generic_file_header();
|
||||||
|
|
||||||
unsigned int expected_magic = is_static() ? CDS_ARCHIVE_MAGIC : CDS_DYNAMIC_ARCHIVE_MAGIC;
|
if (_is_static) {
|
||||||
if (gen_header->_magic != expected_magic) {
|
if (gen_header->_magic != CDS_ARCHIVE_MAGIC) {
|
||||||
log_info(cds)("_magic expected: 0x%08x", expected_magic);
|
FileMapInfo::fail_continue("Not a base shared archive: %s", _full_path);
|
||||||
log_info(cds)(" actual: 0x%08x", gen_header->_magic);
|
return false;
|
||||||
FileMapInfo::fail_continue("The shared archive file has a bad magic number.");
|
}
|
||||||
return false;
|
} else {
|
||||||
|
if (gen_header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||||||
|
FileMapInfo::fail_continue("Not a top shared archive: %s", _full_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_header = (FileMapHeader*)os::malloc(gen_header->_header_size, mtInternal);
|
_header = (FileMapHeader*)os::malloc(gen_header->_header_size, mtInternal);
|
||||||
@ -1348,11 +1350,6 @@ bool FileMapInfo::open_for_read() {
|
|||||||
if (_file_open) {
|
if (_file_open) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (is_static()) {
|
|
||||||
_full_path = Arguments::GetSharedArchivePath();
|
|
||||||
} else {
|
|
||||||
_full_path = Arguments::GetSharedDynamicArchivePath();
|
|
||||||
}
|
|
||||||
log_info(cds)("trying to map %s", _full_path);
|
log_info(cds)("trying to map %s", _full_path);
|
||||||
int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0);
|
int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
@ -1374,12 +1371,7 @@ bool FileMapInfo::open_for_read() {
|
|||||||
|
|
||||||
// Write the FileMapInfo information to the file.
|
// Write the FileMapInfo information to the file.
|
||||||
|
|
||||||
void FileMapInfo::open_for_write(const char* path) {
|
void FileMapInfo::open_for_write() {
|
||||||
if (path == NULL) {
|
|
||||||
_full_path = Arguments::GetSharedArchivePath();
|
|
||||||
} else {
|
|
||||||
_full_path = path;
|
|
||||||
}
|
|
||||||
LogMessage(cds) msg;
|
LogMessage(cds) msg;
|
||||||
if (msg.is_info()) {
|
if (msg.is_info()) {
|
||||||
msg.info("Dumping shared data to file: ");
|
msg.info("Dumping shared data to file: ");
|
||||||
@ -2364,15 +2356,20 @@ bool FileMapInfo::initialize() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!open_for_read()) {
|
if (!open_for_read() || !init_from_file(_fd) || !validate_header()) {
|
||||||
return false;
|
if (_is_static) {
|
||||||
}
|
FileMapInfo::fail_continue("Initialize static archive failed.");
|
||||||
if (!init_from_file(_fd)) {
|
return false;
|
||||||
return false;
|
} else {
|
||||||
}
|
FileMapInfo::fail_continue("Initialize dynamic archive failed.");
|
||||||
if (!validate_header()) {
|
if (AutoCreateSharedArchive) {
|
||||||
return false;
|
DynamicDumpSharedSpaces = true;
|
||||||
|
ArchiveClassesAtExit = Arguments::GetSharedDynamicArchivePath();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +356,6 @@ private:
|
|||||||
public:
|
public:
|
||||||
static bool get_base_archive_name_from_header(const char* archive_name,
|
static bool get_base_archive_name_from_header(const char* archive_name,
|
||||||
char** base_archive_name);
|
char** base_archive_name);
|
||||||
static bool check_archive(const char* archive_name, bool is_static);
|
|
||||||
static SharedPathTable shared_path_table() {
|
static SharedPathTable shared_path_table() {
|
||||||
return _shared_path_table;
|
return _shared_path_table;
|
||||||
}
|
}
|
||||||
@ -370,7 +369,7 @@ public:
|
|||||||
|
|
||||||
void log_paths(const char* msg, int start_idx, int end_idx);
|
void log_paths(const char* msg, int start_idx, int end_idx);
|
||||||
|
|
||||||
FileMapInfo(bool is_static);
|
FileMapInfo(const char* full_apth, bool is_static);
|
||||||
~FileMapInfo();
|
~FileMapInfo();
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
@ -441,7 +440,7 @@ public:
|
|||||||
// File manipulation.
|
// File manipulation.
|
||||||
bool initialize() NOT_CDS_RETURN_(false);
|
bool initialize() NOT_CDS_RETURN_(false);
|
||||||
bool open_for_read();
|
bool open_for_read();
|
||||||
void open_for_write(const char* path = NULL);
|
void open_for_write();
|
||||||
void write_header();
|
void write_header();
|
||||||
void write_region(int region, char* base, size_t size,
|
void write_region(int region, char* base, size_t size,
|
||||||
bool read_only, bool allow_exec);
|
bool read_only, bool allow_exec);
|
||||||
|
@ -556,7 +556,9 @@ void VM_PopulateDumpSharedSpace::doit() {
|
|||||||
builder.relocate_to_requested();
|
builder.relocate_to_requested();
|
||||||
|
|
||||||
// Write the archive file
|
// Write the archive file
|
||||||
FileMapInfo* mapinfo = new FileMapInfo(true);
|
const char* static_archive = Arguments::GetSharedArchivePath();
|
||||||
|
assert(static_archive != nullptr, "SharedArchiveFile not set?");
|
||||||
|
FileMapInfo* mapinfo = new FileMapInfo(static_archive, true);
|
||||||
mapinfo->populate_header(MetaspaceShared::core_region_alignment());
|
mapinfo->populate_header(MetaspaceShared::core_region_alignment());
|
||||||
mapinfo->set_serialized_data(serialized_data);
|
mapinfo->set_serialized_data(serialized_data);
|
||||||
mapinfo->set_cloned_vtables(cloned_vtables);
|
mapinfo->set_cloned_vtables(cloned_vtables);
|
||||||
@ -946,12 +948,17 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
|
|||||||
_requested_base_address = static_mapinfo->requested_base_address();
|
_requested_base_address = static_mapinfo->requested_base_address();
|
||||||
if (dynamic_mapped) {
|
if (dynamic_mapped) {
|
||||||
FileMapInfo::set_shared_path_table(dynamic_mapinfo);
|
FileMapInfo::set_shared_path_table(dynamic_mapinfo);
|
||||||
|
// turn AutoCreateSharedArchive off if successfully mapped
|
||||||
|
AutoCreateSharedArchive = false;
|
||||||
} else {
|
} else {
|
||||||
FileMapInfo::set_shared_path_table(static_mapinfo);
|
FileMapInfo::set_shared_path_table(static_mapinfo);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
set_shared_metaspace_range(NULL, NULL, NULL);
|
set_shared_metaspace_range(NULL, NULL, NULL);
|
||||||
UseSharedSpaces = false;
|
UseSharedSpaces = false;
|
||||||
|
// The base archive cannot be mapped. We cannot dump the dynamic shared archive.
|
||||||
|
AutoCreateSharedArchive = false;
|
||||||
|
DynamicDumpSharedSpaces = false;
|
||||||
FileMapInfo::fail_continue("Unable to map shared spaces");
|
FileMapInfo::fail_continue("Unable to map shared spaces");
|
||||||
if (PrintSharedArchiveAndExit) {
|
if (PrintSharedArchiveAndExit) {
|
||||||
vm_exit_during_initialization("Unable to use shared archive.");
|
vm_exit_during_initialization("Unable to use shared archive.");
|
||||||
@ -967,7 +974,9 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileMapInfo* MetaspaceShared::open_static_archive() {
|
FileMapInfo* MetaspaceShared::open_static_archive() {
|
||||||
FileMapInfo* mapinfo = new FileMapInfo(true);
|
const char* static_archive = Arguments::GetSharedArchivePath();
|
||||||
|
assert(static_archive != nullptr, "SharedArchivePath is NULL");
|
||||||
|
FileMapInfo* mapinfo = new FileMapInfo(static_archive, true);
|
||||||
if (!mapinfo->initialize()) {
|
if (!mapinfo->initialize()) {
|
||||||
delete(mapinfo);
|
delete(mapinfo);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -979,11 +988,12 @@ FileMapInfo* MetaspaceShared::open_dynamic_archive() {
|
|||||||
if (DynamicDumpSharedSpaces) {
|
if (DynamicDumpSharedSpaces) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (Arguments::GetSharedDynamicArchivePath() == NULL) {
|
const char* dynamic_archive = Arguments::GetSharedDynamicArchivePath();
|
||||||
|
if (dynamic_archive == nullptr) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileMapInfo* mapinfo = new FileMapInfo(false);
|
FileMapInfo* mapinfo = new FileMapInfo(dynamic_archive, false);
|
||||||
if (!mapinfo->initialize()) {
|
if (!mapinfo->initialize()) {
|
||||||
delete(mapinfo);
|
delete(mapinfo);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -3140,6 +3140,17 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) {
|
|||||||
DynamicDumpSharedSpaces = true;
|
DynamicDumpSharedSpaces = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AutoCreateSharedArchive) {
|
||||||
|
if (SharedArchiveFile == NULL) {
|
||||||
|
log_warning(cds)("-XX:+AutoCreateSharedArchive requires -XX:SharedArchiveFile");
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
if (ArchiveClassesAtExit != NULL) {
|
||||||
|
log_warning(cds)("-XX:+AutoCreateSharedArchive does not work with ArchiveClassesAtExit");
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (UseSharedSpaces && patch_mod_javabase) {
|
if (UseSharedSpaces && patch_mod_javabase) {
|
||||||
no_shared_spaces("CDS is disabled when " JAVA_BASE_NAME " module is patched.");
|
no_shared_spaces("CDS is disabled when " JAVA_BASE_NAME " module is patched.");
|
||||||
}
|
}
|
||||||
@ -3487,9 +3498,6 @@ void Arguments::extract_shared_archive_paths(const char* archive_path,
|
|||||||
char* cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
char* cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
||||||
strncpy(cur_path, begin_ptr, len);
|
strncpy(cur_path, begin_ptr, len);
|
||||||
cur_path[len] = '\0';
|
cur_path[len] = '\0';
|
||||||
if (!FileMapInfo::check_archive((const char*)cur_path, true /*is_static*/)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*base_archive_path = cur_path;
|
*base_archive_path = cur_path;
|
||||||
|
|
||||||
begin_ptr = ++end_ptr;
|
begin_ptr = ++end_ptr;
|
||||||
@ -3501,9 +3509,6 @@ void Arguments::extract_shared_archive_paths(const char* archive_path,
|
|||||||
len = end_ptr - begin_ptr;
|
len = end_ptr - begin_ptr;
|
||||||
cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
||||||
strncpy(cur_path, begin_ptr, len + 1);
|
strncpy(cur_path, begin_ptr, len + 1);
|
||||||
if (!FileMapInfo::check_archive((const char*)cur_path, false /*is_static*/)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*top_archive_path = cur_path;
|
*top_archive_path = cur_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3556,7 +3561,16 @@ void Arguments::init_shared_archive_paths() {
|
|||||||
bool success =
|
bool success =
|
||||||
FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path);
|
FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
no_shared_spaces("invalid archive");
|
// If +AutoCreateSharedArchive and the specified shared archive does not exist,
|
||||||
|
// regenerate the dynamic archive base on default archive.
|
||||||
|
if (AutoCreateSharedArchive && !os::file_exists(SharedArchiveFile)) {
|
||||||
|
DynamicDumpSharedSpaces = true;
|
||||||
|
ArchiveClassesAtExit = const_cast<char *>(SharedArchiveFile);
|
||||||
|
SharedArchivePath = get_default_shared_archive_path();
|
||||||
|
SharedArchiveFile = nullptr;
|
||||||
|
} else {
|
||||||
|
no_shared_spaces("invalid archive");
|
||||||
|
}
|
||||||
} else if (base_archive_path == NULL) {
|
} else if (base_archive_path == NULL) {
|
||||||
// User has specified a single archive, which is a static archive.
|
// User has specified a single archive, which is a static archive.
|
||||||
SharedArchivePath = const_cast<char *>(SharedArchiveFile);
|
SharedArchivePath = const_cast<char *>(SharedArchiveFile);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -1802,6 +1802,9 @@ const intx ObjectAlignmentInBytes = 8;
|
|||||||
product(bool, RecordDynamicDumpInfo, false, \
|
product(bool, RecordDynamicDumpInfo, false, \
|
||||||
"Record class info for jcmd VM.cds dynamic_dump") \
|
"Record class info for jcmd VM.cds dynamic_dump") \
|
||||||
\
|
\
|
||||||
|
product(bool, AutoCreateSharedArchive, false, \
|
||||||
|
"Create shared archive at exit if cds mapping failed") \
|
||||||
|
\
|
||||||
product(bool, PrintSharedArchiveAndExit, false, \
|
product(bool, PrintSharedArchiveAndExit, false, \
|
||||||
"Print shared archive file contents") \
|
"Print shared archive file contents") \
|
||||||
\
|
\
|
||||||
|
@ -47,37 +47,39 @@ public class MoveJDKTest {
|
|||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
String java_home_src = System.getProperty("java.home");
|
String java_home_src = System.getProperty("java.home");
|
||||||
String java_home_dst = CDSTestUtils.getOutputDir() + File.separator + "moved_jdk";
|
String java_home_dst = CDSTestUtils.getOutputDir() + File.separator + "moved_jdk";
|
||||||
|
String homeJava = java_home_src + File.separator + "bin" + File.separator + "java";
|
||||||
|
String dstJava = java_home_dst + File.separator + "bin" + File.separator + "java";
|
||||||
|
|
||||||
TestCommon.startNewArchiveName();
|
TestCommon.startNewArchiveName();
|
||||||
String jsaFile = TestCommon.getCurrentArchiveName();
|
String jsaFile = TestCommon.getCurrentArchiveName();
|
||||||
String jsaOpt = "-XX:SharedArchiveFile=" + jsaFile;
|
String jsaOpt = "-XX:SharedArchiveFile=" + jsaFile;
|
||||||
{
|
{
|
||||||
ProcessBuilder pb = makeBuilder(java_home_src + "/bin/java", "-Xshare:dump", jsaOpt);
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(homeJava, "-Xshare:dump", jsaOpt);
|
||||||
TestCommon.executeAndLog(pb, "dump")
|
TestCommon.executeAndLog(pb, "dump")
|
||||||
.shouldHaveExitValue(0);
|
.shouldHaveExitValue(0);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ProcessBuilder pb = makeBuilder(java_home_src + "/bin/java",
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(homeJava,
|
||||||
"-Xshare:auto",
|
"-Xshare:auto",
|
||||||
jsaOpt,
|
jsaOpt,
|
||||||
"-Xlog:class+path=info",
|
"-Xlog:class+path=info",
|
||||||
"-version");
|
"-version");
|
||||||
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-src");
|
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-src");
|
||||||
out.shouldHaveExitValue(0);
|
out.shouldHaveExitValue(0);
|
||||||
out.shouldNotContain("shared class paths mismatch");
|
out.shouldNotContain("shared class paths mismatch");
|
||||||
out.shouldNotContain("BOOT classpath mismatch");
|
out.shouldNotContain("BOOT classpath mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(new File(java_home_src), new File(java_home_dst));
|
CDSTestUtils.clone(new File(java_home_src), new File(java_home_dst));
|
||||||
System.out.println("============== Cloned JDK at " + java_home_dst);
|
System.out.println("============== Cloned JDK at " + java_home_dst);
|
||||||
|
|
||||||
// Test runtime with cloned JDK
|
// Test runtime with cloned JDK
|
||||||
{
|
{
|
||||||
ProcessBuilder pb = makeBuilder(java_home_dst + "/bin/java",
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(dstJava,
|
||||||
"-Xshare:auto",
|
"-Xshare:auto",
|
||||||
jsaOpt,
|
jsaOpt,
|
||||||
"-Xlog:class+path=info",
|
"-Xlog:class+path=info",
|
||||||
"-version");
|
"-version");
|
||||||
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-dst");
|
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-dst");
|
||||||
out.shouldHaveExitValue(0);
|
out.shouldHaveExitValue(0);
|
||||||
out.shouldNotContain("shared class paths mismatch");
|
out.shouldNotContain("shared class paths mismatch");
|
||||||
@ -89,21 +91,21 @@ public class MoveJDKTest {
|
|||||||
String fake_modules = copyFakeModulesFromHelloJar();
|
String fake_modules = copyFakeModulesFromHelloJar();
|
||||||
String dumptimeBootAppendOpt = "-Xbootclasspath/a:" + fake_modules;
|
String dumptimeBootAppendOpt = "-Xbootclasspath/a:" + fake_modules;
|
||||||
{
|
{
|
||||||
ProcessBuilder pb = makeBuilder(java_home_src + "/bin/java",
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(homeJava,
|
||||||
"-Xshare:dump",
|
"-Xshare:dump",
|
||||||
dumptimeBootAppendOpt,
|
dumptimeBootAppendOpt,
|
||||||
jsaOpt);
|
jsaOpt);
|
||||||
TestCommon.executeAndLog(pb, "dump")
|
TestCommon.executeAndLog(pb, "dump")
|
||||||
.shouldHaveExitValue(0);
|
.shouldHaveExitValue(0);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
String runtimeBootAppendOpt = dumptimeBootAppendOpt + System.getProperty("path.separator") + helloJar;
|
String runtimeBootAppendOpt = dumptimeBootAppendOpt + System.getProperty("path.separator") + helloJar;
|
||||||
ProcessBuilder pb = makeBuilder(java_home_dst + "/bin/java",
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(dstJava,
|
||||||
"-Xshare:auto",
|
"-Xshare:auto",
|
||||||
runtimeBootAppendOpt,
|
runtimeBootAppendOpt,
|
||||||
jsaOpt,
|
jsaOpt,
|
||||||
"-Xlog:class+path=info",
|
"-Xlog:class+path=info",
|
||||||
"-version");
|
"-version");
|
||||||
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-dst");
|
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-dst");
|
||||||
out.shouldHaveExitValue(0);
|
out.shouldHaveExitValue(0);
|
||||||
out.shouldNotContain("shared class paths mismatch");
|
out.shouldNotContain("shared class paths mismatch");
|
||||||
@ -111,78 +113,17 @@ public class MoveJDKTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test with no modules image in the <java home>/lib directory
|
// Test with no modules image in the <java home>/lib directory
|
||||||
renameModulesFile(java_home_dst);
|
String locDir = java_home_dst + File.separator + "lib";
|
||||||
|
CDSTestUtils.rename(new File(locDir + File.separator + "modules"),
|
||||||
|
new File(locDir + File.separator + "orig-modules"));
|
||||||
{
|
{
|
||||||
ProcessBuilder pb = makeBuilder(java_home_dst + "/bin/java",
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(dstJava, "-version");
|
||||||
"-version");
|
|
||||||
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-missing-modules");
|
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-missing-modules");
|
||||||
out.shouldHaveExitValue(1);
|
out.shouldHaveExitValue(1);
|
||||||
out.shouldContain("Failed setting boot class path.");
|
out.shouldContain("Failed setting boot class path.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a cheap clone of the JDK. Most files can be sym-linked. However, $JAVA_HOME/bin/java and $JAVA_HOME/lib/.../libjvm.so"
|
|
||||||
// must be copied, because the java.home property is derived from the canonicalized paths of these 2 files.
|
|
||||||
static void clone(File src, File dst) throws Exception {
|
|
||||||
if (dst.exists()) {
|
|
||||||
if (!dst.isDirectory()) {
|
|
||||||
throw new RuntimeException("Not a directory :" + dst);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!dst.mkdir()) {
|
|
||||||
throw new RuntimeException("Cannot create directory: " + dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final String jvmLib = System.mapLibraryName("jvm");
|
|
||||||
for (String child : src.list()) {
|
|
||||||
if (child.equals(".") || child.equals("..")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
File child_src = new File(src, child);
|
|
||||||
File child_dst = new File(dst, child);
|
|
||||||
if (child_dst.exists()) {
|
|
||||||
throw new RuntimeException("Already exists: " + child_dst);
|
|
||||||
}
|
|
||||||
if (child_src.isFile()) {
|
|
||||||
if (child.equals(jvmLib) || child.equals("java")) {
|
|
||||||
Files.copy(child_src.toPath(), /* copy data to -> */ child_dst.toPath());
|
|
||||||
} else {
|
|
||||||
Files.createSymbolicLink(child_dst.toPath(), /* link to -> */ child_src.toPath());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clone(child_src, child_dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renameModulesFile(String javaHome) throws Exception {
|
|
||||||
String modulesDir = javaHome + File.separator + "lib";
|
|
||||||
File origModules = new File(modulesDir, "modules");
|
|
||||||
if (!origModules.exists()) {
|
|
||||||
throw new RuntimeException("modules file not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
File renamedModules = new File(modulesDir, "orig_modules");
|
|
||||||
if (renamedModules.exists()) {
|
|
||||||
throw new RuntimeException("found orig_modules unexpectedly");
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean success = origModules.renameTo(renamedModules);
|
|
||||||
if (!success) {
|
|
||||||
throw new RuntimeException("rename modules file failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static ProcessBuilder makeBuilder(String... args) throws Exception {
|
|
||||||
System.out.print("[");
|
|
||||||
for (String s : args) {
|
|
||||||
System.out.print(" " + s);
|
|
||||||
}
|
|
||||||
System.out.println(" ]");
|
|
||||||
return new ProcessBuilder(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String copyFakeModulesFromHelloJar() throws Exception {
|
private static String copyFakeModulesFromHelloJar() throws Exception {
|
||||||
String outDir = CDSTestUtils.getOutputDir();
|
String outDir = CDSTestUtils.getOutputDir();
|
||||||
String newFile = "hello.modules";
|
String newFile = "hello.modules";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -181,9 +181,7 @@ public class SharedArchiveConsistency {
|
|||||||
copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, modVersion);
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, modVersion);
|
||||||
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), version);
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), version);
|
||||||
output = shareAuto ? TestCommon.execAuto(execArgs) : TestCommon.execCommon(execArgs);
|
output = shareAuto ? TestCommon.execAuto(execArgs) : TestCommon.execCommon(execArgs);
|
||||||
output.shouldContain("The shared archive file has the wrong version")
|
output.shouldContain("The shared archive file version " + version + " does not match the required version " + currentCDSArchiveVersion);
|
||||||
.shouldContain("_version expected: " + currentCDSArchiveVersion)
|
|
||||||
.shouldContain("actual: " + version);
|
|
||||||
if (shareAuto) {
|
if (shareAuto) {
|
||||||
output.shouldContain(HELLO_WORLD);
|
output.shouldContain(HELLO_WORLD);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -37,7 +37,6 @@ import sun.hotspot.WhiteBox;
|
|||||||
class DynamicArchiveTestBase {
|
class DynamicArchiveTestBase {
|
||||||
private static boolean executedIn_run = false;
|
private static boolean executedIn_run = false;
|
||||||
private static boolean autoMode = false; // -Xshare:auto
|
private static boolean autoMode = false; // -Xshare:auto
|
||||||
|
|
||||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||||
|
|
||||||
public static interface DynamicArchiveTest {
|
public static interface DynamicArchiveTest {
|
||||||
@ -96,6 +95,19 @@ class DynamicArchiveTestBase {
|
|||||||
return TestCommon.getNewArchiveName(stem);
|
return TestCommon.getNewArchiveName(stem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excute a JVM to dump a base archive by
|
||||||
|
* -Xshare:dump -XX:SharedArchiveFile=baseArchiveName
|
||||||
|
*/
|
||||||
|
public static Result dumpBaseArchive(String baseArchiveName, String... cmdLineSuffix)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
OutputAnalyzer output = TestCommon.dumpBaseArchive(baseArchiveName, cmdLineSuffix);
|
||||||
|
CDSOptions opts = new CDSOptions();
|
||||||
|
opts.setXShareMode("dump");
|
||||||
|
return new Result(opts, output);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a JVM using the base archive (given by baseArchiveName) with the command line
|
* Execute a JVM using the base archive (given by baseArchiveName) with the command line
|
||||||
* (given by cmdLineSuffix). At JVM exit, dump all eligible classes into the top archive
|
* (given by cmdLineSuffix). At JVM exit, dump all eligible classes into the top archive
|
||||||
|
@ -0,0 +1,693 @@
|
|||||||
|
/*
|
||||||
|
* 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 8261455
|
||||||
|
* @summary test -XX:+AutoCreateSharedArchive feature
|
||||||
|
* @requires vm.cds
|
||||||
|
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
||||||
|
* @build Hello
|
||||||
|
* @build sun.hotspot.WhiteBox
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
|
||||||
|
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:./WhiteBox.jar TestAutoCreateSharedArchive verifySharedSpacesOff
|
||||||
|
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:./WhiteBox.jar TestAutoCreateSharedArchive verifySharedSpacesOn
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -XX:SharedArchiveFile can be specified in two styles:
|
||||||
|
*
|
||||||
|
* (A) Test with default base archive -XX:+SharedArchiveFile=<archive>
|
||||||
|
* (B) Test with the base archive specified: -XX:SharedArchiveFile=<base>:<top>
|
||||||
|
* all the following if not explained explicitly, run with flag -XX:+AutoCreateSharedArchive
|
||||||
|
*
|
||||||
|
* Note VerifySharedSpaces will affect output so the tests run twice: one with -XX:+VerifySharedSpaces and the other with -XX:-VerifySharedSpaces
|
||||||
|
*
|
||||||
|
* 10 Case (A)
|
||||||
|
*
|
||||||
|
* 10.01 run with non-existing archive should automatically create dynamic archive.
|
||||||
|
* If the JDK's default CDS archive cannot be loaded, print out warning, run continue without shared archive and no shared archive created at exit.
|
||||||
|
* 10.02 run with the created dynamic archive should pass.
|
||||||
|
* 10.03 run with the created dynamic archive and -XX:+AutoCreateSharedArchive should pass and no shared archive created at exit.
|
||||||
|
*
|
||||||
|
* 11 run with static archive.
|
||||||
|
* run with static archive should printout warning and continue, share or no share depends on the archive validation at exit,
|
||||||
|
* no shared archive (top) will be generated.
|
||||||
|
*
|
||||||
|
* 12 run with damaged magic should not regenerate dynamic archive.
|
||||||
|
* if magic is not expected, no shared archive will be regenerated at exit.
|
||||||
|
*
|
||||||
|
* 13 run with a bad versioned archive.
|
||||||
|
* 13.01 run with a bad versioned (< CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION) archive should not create dynamic archive at exit.
|
||||||
|
* 13.02 run with a bad versioned (> CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION) archive should create dynamic archive at exit.
|
||||||
|
*
|
||||||
|
* 14 run with an archive whose base name is not matched, no shared archive at exit.
|
||||||
|
*
|
||||||
|
* 15 run with an archive whose jvm_ident is corrupted should
|
||||||
|
* create dynamic archive at exit with -XX:-VerifySharedSpaces
|
||||||
|
* not create dynamic archive at exit with -XX:+VerifySharedSpaces
|
||||||
|
*
|
||||||
|
* 16 run with an archive only containing magic in the file (size of 4 bytes)
|
||||||
|
* the archive will be created at exit.
|
||||||
|
*
|
||||||
|
* 20 (case B)
|
||||||
|
*
|
||||||
|
* 20.01 dump base archive which will be used for dumping top archive.
|
||||||
|
* 20.02 dump top archive based on base archive obtained in 20.1.
|
||||||
|
* 20.03 run -XX:SharedArchiveFile=<base>:<top> to verify the archives.
|
||||||
|
* 20.04 run with -XX:SharedArchveFile=base:top (reversed)
|
||||||
|
*
|
||||||
|
* 21 Mismatched versions
|
||||||
|
* 21.01 if version of top archive is higher than CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION, the archive cannot be shared and will be
|
||||||
|
* regenerated at exit.
|
||||||
|
* 21.02 if version of top archive is lower than CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION, the archive cannot be shared and will be
|
||||||
|
* created at exit.
|
||||||
|
*
|
||||||
|
* 22 create an archive with dynamic magic number only
|
||||||
|
* archive will be created at exit if base can be shared.
|
||||||
|
*
|
||||||
|
* 23 mismatched jvm_indent in base/top archive
|
||||||
|
* 23.01 mismatched jvm_indent in top archive
|
||||||
|
* 23.02 mismatched jvm_indent in base archive
|
||||||
|
*
|
||||||
|
* 24 run with non-existing shared archives
|
||||||
|
* 24.01 run -Xshare:auto -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=base.jsa:non-exist-top.jsa
|
||||||
|
* The top archive will be regenerated.
|
||||||
|
* 24.02 run -Xshare:auto -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=non-exist-base.jsa:top.jsa
|
||||||
|
* top archive will not be shared if base archive failed to load.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
|
import jdk.test.lib.cds.CDSArchiveUtils;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
|
|
||||||
|
public class TestAutoCreateSharedArchive extends DynamicArchiveTestBase {
|
||||||
|
private static final String BASE_NAME = CDSTestUtils.getOutputFileName("base.jsa");
|
||||||
|
private static final String TOP_NAME = CDSTestUtils.getOutputFileName("top.jsa");
|
||||||
|
private static final String mainAppClass = "Hello";
|
||||||
|
private static final String HELLO_SOURCE = "Hello source: shared objects file (top)";
|
||||||
|
private static final String HELLO_WORLD = "Hello World";
|
||||||
|
private static boolean verifyOn = false;
|
||||||
|
|
||||||
|
private static int genericHeaderMinVersion = CDSArchiveUtils.getGenericHeaderMinVersion();
|
||||||
|
private static int currentCDSVersion = CDSArchiveUtils.getCurrentCDSArchiveVersion();
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
if (args.length != 1 || (!args[0].equals("verifySharedSpacesOff") && !args[0].equals("verifySharedSpacesOn"))) {
|
||||||
|
throw new RuntimeException("Must run with verifySharedSpacesOff or verifySharedSpacesOn");
|
||||||
|
}
|
||||||
|
verifyOn = args[0].equals("verifySharedSpacesOn");
|
||||||
|
runTest(TestAutoCreateSharedArchive::testAutoCreateSharedArchive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkFileExists(String fileName) throws Exception {
|
||||||
|
File file = new File(fileName);
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new IOException("Archive " + fileName + " is not automatically created");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String startNewArchive(String testName) {
|
||||||
|
String newArchiveName = TestCommon.getNewArchiveName(testName);
|
||||||
|
TestCommon.setCurrentArchiveName(newArchiveName);
|
||||||
|
return newArchiveName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void print(String message) {
|
||||||
|
System.out.println(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testAutoCreateSharedArchive() throws Exception {
|
||||||
|
String appJar = ClassFileInstaller.getJarPath("hello.jar");
|
||||||
|
boolean fileModified = false;
|
||||||
|
|
||||||
|
String verifySharedSpaces = verifyOn ? "-XX:+VerifySharedSpaces" : "-XX:-VerifySharedSpaces";
|
||||||
|
File archiveFile = new File(TOP_NAME);
|
||||||
|
if (archiveFile.exists()) {
|
||||||
|
archiveFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump a static archive, used later.
|
||||||
|
// 0. Dump a static archive
|
||||||
|
print("0. dump a static archive " + BASE_NAME);
|
||||||
|
dumpBaseArchive(BASE_NAME,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0);
|
||||||
|
});
|
||||||
|
checkFileExists(BASE_NAME);
|
||||||
|
|
||||||
|
// The list numbers try to match JDK-8272331 (CSR for JDK-8261455) test items but not exactly matched.
|
||||||
|
|
||||||
|
// 10 non-existing archive should automatically create dynamic archive based on default shared archive
|
||||||
|
// if base archive loaded.
|
||||||
|
print("10 Test with default base shared archive");
|
||||||
|
print(" 10.01 run with non-existing archive should automatically create dynamic archive");
|
||||||
|
File fileTop = new File(TOP_NAME);
|
||||||
|
if (fileTop.exists()) {
|
||||||
|
fileTop.delete();
|
||||||
|
}
|
||||||
|
run(TOP_NAME,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Dumping shared data to file:")
|
||||||
|
.shouldContain(TOP_NAME);
|
||||||
|
});
|
||||||
|
checkFileExists(TOP_NAME);
|
||||||
|
|
||||||
|
//10.02 run with the created dynamic archive should pass
|
||||||
|
print(" 10.02 run with the created dynamic archive should pass");
|
||||||
|
run(TOP_NAME,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:class+load",
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain(HELLO_SOURCE);
|
||||||
|
});
|
||||||
|
// remember the FileTime
|
||||||
|
FileTime ft1 = Files.getLastModifiedTime(Paths.get(TOP_NAME));
|
||||||
|
|
||||||
|
// 10.03 run with the created dynamic archive with -XX:+AutoCreateSharedArchive should pass
|
||||||
|
// archive should not be created again.
|
||||||
|
print(" 10.03 run with the created dynamic archive with -XX:+AutoCreateSharedArchive should pass");
|
||||||
|
run(TOP_NAME,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:class+load",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain(HELLO_SOURCE)
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
FileTime ft2 = Files.getLastModifiedTime(Paths.get(TOP_NAME));
|
||||||
|
fileModified = !ft2.equals(ft1);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Archive file " + TOP_NAME + " should not be updated");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11 run with static archive
|
||||||
|
print("11 run with static archive");
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(BASE_NAME));
|
||||||
|
run(BASE_NAME,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("AutoCreateSharedArchive is ignored because " + BASE_NAME + " is a static archive")
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(BASE_NAME));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Run -XX:+AutoCreateSharedArchive on static archive create new archive");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12 run with damaged magic should not regenerate dynamic archive
|
||||||
|
print("12 run with damaged magic should not regenerate dynamic archive");
|
||||||
|
String modMagic = startNewArchive("modify-magic");
|
||||||
|
File copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, modMagic);
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetMagic(), 0x1234);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(modMagic));
|
||||||
|
|
||||||
|
run(modMagic,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(modMagic));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + modMagic + " should not automatically be generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 13 run with a bad versioned (< genericHeaderMinVersion) archive
|
||||||
|
print("13 run with a bad versioned archive");
|
||||||
|
print(" 13.01 run with a bad versioned (< genericHeaderMinVersion) archive should not create new archive");
|
||||||
|
String modVersion = startNewArchive("modify-version-b");
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, modVersion);
|
||||||
|
final int version1 = genericHeaderMinVersion - 1;
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), version1);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(modVersion));
|
||||||
|
|
||||||
|
run(modVersion,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Cannot handle shared archive file version " + version1 + ". Must be at least " + genericHeaderMinVersion)
|
||||||
|
.shouldContain("Unable to use shared archive: invalid archive")
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(modVersion));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Run -XX:+AutoCreateSharedArchive with lower version archive " + modVersion + " should not create new archive");
|
||||||
|
}
|
||||||
|
// 13.02 run with a bad versioned (> currentCDSVersion) archive
|
||||||
|
print(" 13.02 run with a bad versioned (> currentCDSVersion) archive");
|
||||||
|
modVersion = startNewArchive("modify-version-d");
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, modVersion);
|
||||||
|
final int version2 = currentCDSVersion + 1;
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), version2);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(modVersion));
|
||||||
|
|
||||||
|
run(modVersion,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("The shared archive file version " + version2 + " does not match the required version " + currentCDSVersion)
|
||||||
|
.shouldContain("UseSharedSpaces: The shared archive file has the wrong version")
|
||||||
|
.shouldContain("UseSharedSpaces: Initialize dynamic archive failed")
|
||||||
|
.shouldContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(modVersion));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (!fileModified) {
|
||||||
|
throw new RuntimeException("Run -XX:+AutoCreateSharedArchive with higher version archive " + modVersion + " should create new archive");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 14 run with an archive whose base name is not matched, no share
|
||||||
|
print("14 run with an archive whose base name is not matched, no share");
|
||||||
|
String baseNameMismatch= startNewArchive("basename-mismatch");
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, baseNameMismatch);
|
||||||
|
int nameSize = CDSArchiveUtils.baseArchiveNameSize(copiedJsa);
|
||||||
|
int offset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < nameSize - 4; i++) {
|
||||||
|
sb.append('Z');
|
||||||
|
}
|
||||||
|
sb.append(".jsa");
|
||||||
|
sb.append('\0');
|
||||||
|
String newName = sb.toString();
|
||||||
|
CDSArchiveUtils.writeData(copiedJsa, offset, newName.getBytes());
|
||||||
|
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(baseNameMismatch));
|
||||||
|
run(baseNameMismatch,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(baseNameMismatch));
|
||||||
|
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + baseNameMismatch+ " should not automatically be generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15 mismatched jvm_indent in archive, create (-VerifySharedSpaces) or not (-XX:+VerifySharedSpaces) create the new archive
|
||||||
|
print("15 mismatched jvm_indent in archive, " + (verifyOn ? "-XX:+VerifySharedSpaces not " : "-XX:-VerifySharedSpaces ") + "create new archive");
|
||||||
|
String modJvmIdent = startNewArchive("modify-jvmident");
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, modJvmIdent);
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetJvmIdent(), 0x65656565);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(modJvmIdent));
|
||||||
|
|
||||||
|
run(modJvmIdent,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0);
|
||||||
|
if (verifyOn) {
|
||||||
|
output.shouldContain("UseSharedSpaces: Header checksum verification failed")
|
||||||
|
.shouldContain("Unable to use shared archive: invalid archive")
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
} else {
|
||||||
|
output.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Dumping shared data to file");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(modJvmIdent));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (verifyOn) {
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + modJvmIdent + " should not be generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + modJvmIdent + " should be generated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16 run with an archive of only containing dynamic magic (size of 4) will not create new archive at exit
|
||||||
|
print("16 run with an archive of only containing dynamic magic (size of 4) will not create new archive at exit");
|
||||||
|
String magicOnly = startNewArchive("magic-only");
|
||||||
|
copiedJsa = CDSArchiveUtils.createMagicOnlyFile(magicOnly, false/*dynamic*/);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(magicOnly));
|
||||||
|
run(magicOnly,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Unable to read generic CDS file map header from shared archive")
|
||||||
|
.shouldNotContain("Dumping shared data to file:");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(magicOnly));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + magicOnly + " should not automatically be generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do some base tests for -XX:SharedArchiveFile=base:top, they should be same as default archive as base.
|
||||||
|
// delete top archive
|
||||||
|
if (archiveFile.exists()) {
|
||||||
|
archiveFile.delete();
|
||||||
|
}
|
||||||
|
// delete base archive
|
||||||
|
File baseFile = new File(BASE_NAME);
|
||||||
|
if (baseFile.exists()) {
|
||||||
|
baseFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 20 Testing with -XX:SharedArchiveFile=base:top
|
||||||
|
print("20 Testing with -XX:SharedArchiveFile=base:top");
|
||||||
|
// 20.01 dump base archive and top archive
|
||||||
|
print(" 20.01 dump base archive " + BASE_NAME);
|
||||||
|
dumpBaseArchive(BASE_NAME, "-Xlog:cds")
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0);
|
||||||
|
});
|
||||||
|
checkFileExists(BASE_NAME);
|
||||||
|
|
||||||
|
// 20.02 dump top based on base
|
||||||
|
print(" 20.02 dump top based on base");
|
||||||
|
dump2(BASE_NAME, TOP_NAME,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-cp", appJar, mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain("Dumping shared data to file:")
|
||||||
|
.shouldContain(TOP_NAME);
|
||||||
|
});
|
||||||
|
checkFileExists(TOP_NAME);
|
||||||
|
|
||||||
|
// 20.03 run with -XX:SharedArchveFile=base:top
|
||||||
|
print(" 20.03 run with -XX:SharedArchveFile=base:top");
|
||||||
|
run2(BASE_NAME, TOP_NAME,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
"-Xlog:class+load",
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_SOURCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 20.04 run with -XX:SharedArchveFile=top:base (reversed)
|
||||||
|
print(" 20.04 run with -XX:SharedArchveFile=top:base (reversed)");
|
||||||
|
run2(TOP_NAME, BASE_NAME,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
"-Xlog:class+load",
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertAbnormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(1)
|
||||||
|
.shouldContain("Not a base shared archive: " + TOP_NAME)
|
||||||
|
.shouldContain("An error has occurred while processing the shared archive file")
|
||||||
|
.shouldNotContain(HELLO_WORLD);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 21 Mismatched versions
|
||||||
|
print("21 Mismatched versions");
|
||||||
|
// 21.01 top version is lower than CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION, regenerate top
|
||||||
|
print(" 21.01 top version is lower than CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION, regenerate top");
|
||||||
|
String versionB = startNewArchive("modify-version-B");
|
||||||
|
archiveFile = new File(TOP_NAME);
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, versionB);
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), version1);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(versionB));
|
||||||
|
|
||||||
|
run2(BASE_NAME, versionB,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Cannot handle shared archive file version " + version1)
|
||||||
|
.shouldContain(versionB)
|
||||||
|
.shouldContain("Dumping shared data to file:");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(versionB));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (!fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + versionB + " should automatically be generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21.02 top version is higher than CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION, no share for top, create archive at exit
|
||||||
|
print(" 21.02 top version is higher than CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION, no share for top, create archive at exit");
|
||||||
|
String versionF = startNewArchive("versionF");
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, versionF);
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), version2);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(versionF));
|
||||||
|
run2(BASE_NAME, versionF,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldContain("The shared archive file version " + version2 + " does not match the required version " + currentCDSVersion)
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Dumping shared data to file:");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(versionB));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (!fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + versionB + " should be created at exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 22 create an archive with dynamic magic number only
|
||||||
|
// archive will be created at exit if base can be shared.
|
||||||
|
print("22 create an archive with dynamic magic number only");
|
||||||
|
copiedJsa = CDSArchiveUtils.createMagicOnlyFile(magicOnly, false /*dynamic*/);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(magicOnly));
|
||||||
|
run2(BASE_NAME, magicOnly,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Unable to read generic CDS file map header from shared archive")
|
||||||
|
.shouldContain("Dumping shared data to file:");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(magicOnly));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (!fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + magicOnly + " should be created at exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 23 mismatched jvm_indent in top or base archive
|
||||||
|
// 23.01 mismatched jvm_indent in top archive
|
||||||
|
print(" 23.01 mismatched jvm_indent in top archive");
|
||||||
|
String modJvmIdentTop = startNewArchive("modify-jvmident-top");
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(archiveFile, modJvmIdentTop);
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetJvmIdent(), 0x65656565);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(modJvmIdentTop));
|
||||||
|
|
||||||
|
run2(BASE_NAME, modJvmIdentTop,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0);
|
||||||
|
if (verifyOn) {
|
||||||
|
output.shouldContain("UseSharedSpaces: Header checksum verification failed");
|
||||||
|
}
|
||||||
|
output.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(modJvmIdentTop));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (!fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + modJvmIdentTop + " should be generated");
|
||||||
|
}
|
||||||
|
// 23.02 mismatched jvm_indent in base archive
|
||||||
|
print(" 23.02 mismatched jvm_indent in base archive");
|
||||||
|
String modJvmIdentBase = startNewArchive("modify-jvmident-base");
|
||||||
|
copiedJsa = CDSArchiveUtils.copyArchiveFile(new File(BASE_NAME), modJvmIdentBase);
|
||||||
|
CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetJvmIdent(), 0x65656565);
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(TOP_NAME));
|
||||||
|
|
||||||
|
run2(modJvmIdentBase, TOP_NAME,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldHaveExitValue(0)
|
||||||
|
.shouldContain(HELLO_WORLD);
|
||||||
|
if (verifyOn) {
|
||||||
|
output.shouldContain("UseSharedSpaces: Header checksum verification failed");
|
||||||
|
}
|
||||||
|
output.shouldContain("Unable to map shared spaces")
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(TOP_NAME));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + TOP_NAME + " should not be generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 24 run with non-existing shared archives
|
||||||
|
print("24 run with non-existing shared archives");
|
||||||
|
// 24.01 run -Xshare:auto -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=base.jsa:non-exist-top.jsa
|
||||||
|
print(" 24.01 run -Xshare:auto -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=base.jsa:non-exist-top.jsa");
|
||||||
|
String nonExistTop = "non-existing-top.jsa";
|
||||||
|
File fileNonExist = new File(nonExistTop);
|
||||||
|
if (fileNonExist.exists()) {
|
||||||
|
fileNonExist.delete();
|
||||||
|
}
|
||||||
|
run2(BASE_NAME, nonExistTop,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldContain("Specified shared archive not found (" + nonExistTop + ")")
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldContain("Dumping shared data to file:");
|
||||||
|
});
|
||||||
|
if (!fileNonExist.exists()) {
|
||||||
|
throw new RuntimeException("Shared archive " + nonExistTop + " should be created at exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 24.02 run -Xshare:auto -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=non-exist-base.jsa:top.jsa
|
||||||
|
print(" 24.02 run -Xshare:auto -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=non-exist-base.jsa:top.jsa");
|
||||||
|
String nonExistBase = "non-existing-base.jsa";
|
||||||
|
fileNonExist = new File(nonExistBase);
|
||||||
|
if (fileNonExist.exists()) {
|
||||||
|
fileNonExist.delete();
|
||||||
|
}
|
||||||
|
ft1 = Files.getLastModifiedTime(Paths.get(TOP_NAME));
|
||||||
|
run2(nonExistBase, TOP_NAME,
|
||||||
|
"-Xshare:auto",
|
||||||
|
"-XX:+AutoCreateSharedArchive",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:cds+dynamic=info",
|
||||||
|
verifySharedSpaces,
|
||||||
|
"-cp", appJar,
|
||||||
|
mainAppClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldContain("Specified shared archive not found (" + nonExistBase + ")")
|
||||||
|
.shouldContain(HELLO_WORLD)
|
||||||
|
.shouldNotContain("Dumping shared data to file:");
|
||||||
|
});
|
||||||
|
ft2 = Files.getLastModifiedTime(Paths.get(TOP_NAME));
|
||||||
|
fileModified = !ft1.equals(ft2);
|
||||||
|
if (fileModified) {
|
||||||
|
throw new RuntimeException("Shared archive " + TOP_NAME + " should not be created at exit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* @summary Test -XX:+AutoCreateSharedArchive on a copied JDK without default shared archive
|
||||||
|
* @bug 8261455
|
||||||
|
* @requires vm.cds
|
||||||
|
* @requires vm.flagless
|
||||||
|
* @comment This test doesn't work on Windows because it depends on symlinks
|
||||||
|
* @requires os.family != "windows"
|
||||||
|
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
|
||||||
|
* @compile ../test-classes/Hello.java
|
||||||
|
* @run driver TestAutoCreateSharedArchiveNoDefaultArchive
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
|
public class TestAutoCreateSharedArchiveNoDefaultArchive {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
String mainClass = "Hello";
|
||||||
|
String java_home_src = System.getProperty("java.home");
|
||||||
|
String java_home_dst = CDSTestUtils.getOutputDir() + File.separator + "moved_jdk";
|
||||||
|
CDSTestUtils.clone(new File(java_home_src), new File(java_home_dst));
|
||||||
|
System.out.println("======== Cloned JDK at " + java_home_dst);
|
||||||
|
|
||||||
|
String homeJava = java_home_src + File.separator + "bin" + File.separator + "java";
|
||||||
|
String dstJava = java_home_dst + File.separator + "bin" + File.separator + "java";
|
||||||
|
|
||||||
|
TestCommon.startNewArchiveName();
|
||||||
|
String jsaFileName = TestCommon.getCurrentArchiveName();
|
||||||
|
File jsaFile = new File(jsaFileName);
|
||||||
|
if (jsaFile.exists()) {
|
||||||
|
jsaFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
String jsaOpt = "-XX:SharedArchiveFile=" + jsaFileName;
|
||||||
|
String autoCreateArchive = "-XX:+AutoCreateSharedArchive";
|
||||||
|
{
|
||||||
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(homeJava,
|
||||||
|
"-Xshare:dump",
|
||||||
|
jsaOpt);
|
||||||
|
TestCommon.executeAndLog(pb, "dump")
|
||||||
|
.shouldHaveExitValue(0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(homeJava,
|
||||||
|
"-Xshare:auto",
|
||||||
|
jsaOpt,
|
||||||
|
"-Xlog:class+path=info",
|
||||||
|
"-version");
|
||||||
|
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-src");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
out.shouldNotContain("shared class paths mismatch");
|
||||||
|
out.shouldNotContain("BOOT classpath mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
String helloJar = JarBuilder.getOrCreateHelloJar();
|
||||||
|
|
||||||
|
if (jsaFile.exists()) {
|
||||||
|
jsaFile.delete();
|
||||||
|
}
|
||||||
|
// Test runtime with cloned JDK
|
||||||
|
System.out.println("======== run with cloned jdk to created dynamic shared archive at exit");
|
||||||
|
{
|
||||||
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(dstJava,
|
||||||
|
"-Xshare:auto",
|
||||||
|
autoCreateArchive,
|
||||||
|
jsaOpt,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:class+path=info",
|
||||||
|
"-cp", helloJar,
|
||||||
|
mainClass);
|
||||||
|
OutputAnalyzer out = TestCommon.executeAndLog(pb, "exec-dst");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
out.shouldContain("Dumping shared data to file");
|
||||||
|
if (!jsaFile.exists()) {
|
||||||
|
throw new RuntimeException("Shared archive " + jsaFileName + " should be created at exit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now rename classes.jsa to old-classes.jsa
|
||||||
|
String dstDir = java_home_dst + File.separator + "lib" + File.separator + "server";
|
||||||
|
CDSTestUtils.rename(new File(dstDir + File.separator + "classes.jsa"),
|
||||||
|
new File(dstDir + File.separator + "old-classes.jsa"));
|
||||||
|
System.out.println("======= renamed classes.jsa to old-classes.jsa");
|
||||||
|
|
||||||
|
{
|
||||||
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(dstJava,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-version");
|
||||||
|
TestCommon.executeAndLog(pb, "show-version")
|
||||||
|
.shouldHaveExitValue(0)
|
||||||
|
.shouldContain("UseSharedSpaces: Initialize static archive failed")
|
||||||
|
.shouldContain("UseSharedSpaces: Unable to map shared spaces")
|
||||||
|
.shouldContain("mixed mode")
|
||||||
|
.shouldNotContain("sharing");
|
||||||
|
}
|
||||||
|
// delete existing jsa file
|
||||||
|
if (jsaFile.exists()) {
|
||||||
|
jsaFile.delete();
|
||||||
|
}
|
||||||
|
System.out.println("======= run with no default shared archive should not create shared archive at exit");
|
||||||
|
{
|
||||||
|
ProcessBuilder pb = CDSTestUtils.makeBuilder(dstJava,
|
||||||
|
"-Xshare:auto",
|
||||||
|
autoCreateArchive,
|
||||||
|
jsaOpt,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Xlog:class+path=info",
|
||||||
|
"-cp", helloJar,
|
||||||
|
mainClass);
|
||||||
|
TestCommon.executeAndLog(pb, "no-default-archive")
|
||||||
|
.shouldHaveExitValue(0)
|
||||||
|
.shouldContain("UseSharedSpaces: Initialize static archive failed")
|
||||||
|
.shouldContain("UseSharedSpaces: Unable to map shared spaces")
|
||||||
|
.shouldNotContain("Dumping shared data to file");
|
||||||
|
if (jsaFile.exists()) {
|
||||||
|
throw new RuntimeException("Archive file " + jsaFileName + " should not be created at exit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -341,6 +341,18 @@ public class CDSArchiveUtils {
|
|||||||
return newJsaFile;
|
return newJsaFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static File createMagicOnlyFile(String fileName, boolean createStatic) throws Exception {
|
||||||
|
File file = new File(fileName);
|
||||||
|
if (file.exists()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
try (FileOutputStream out = new FileOutputStream(file)) {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(4).putInt(createStatic ? staticMagic: dynamicMagic);
|
||||||
|
out.write(buffer.array(), 0, 4);
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
private static FileChannel getFileChannel(File file, boolean write) throws Exception {
|
private static FileChannel getFileChannel(File file, boolean write) throws Exception {
|
||||||
List<StandardOpenOption> arry = new ArrayList<StandardOpenOption>();
|
List<StandardOpenOption> arry = new ArrayList<StandardOpenOption>();
|
||||||
arry.add(READ);
|
arry.add(READ);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,6 +26,9 @@ import java.io.IOException;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.CopyOption;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -684,4 +687,78 @@ public class CDSTestUtils {
|
|||||||
private static boolean isAsciiPrintable(char ch) {
|
private static boolean isAsciiPrintable(char ch) {
|
||||||
return ch >= 32 && ch < 127;
|
return ch >= 32 && ch < 127;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JDK utility
|
||||||
|
|
||||||
|
// Do a cheap clone of the JDK. Most files can be sym-linked. However, $JAVA_HOME/bin/java and $JAVA_HOME/lib/.../libjvm.so"
|
||||||
|
// must be copied, because the java.home property is derived from the canonicalized paths of these 2 files.
|
||||||
|
// Set a list of {jvm, "java"} which will be physically copied. If a file needs copied physically, add it to the list.
|
||||||
|
private static String[] phCopied = {System.mapLibraryName("jvm"), "java"};
|
||||||
|
public static void clone(File src, File dst) throws Exception {
|
||||||
|
if (dst.exists()) {
|
||||||
|
if (!dst.isDirectory()) {
|
||||||
|
throw new RuntimeException("Not a directory :" + dst);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!dst.mkdir()) {
|
||||||
|
throw new RuntimeException("Cannot create directory: " + dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// final String jvmLib = System.mapLibraryName("jvm");
|
||||||
|
for (String child : src.list()) {
|
||||||
|
if (child.equals(".") || child.equals("..")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
File child_src = new File(src, child);
|
||||||
|
File child_dst = new File(dst, child);
|
||||||
|
if (child_dst.exists()) {
|
||||||
|
throw new RuntimeException("Already exists: " + child_dst);
|
||||||
|
}
|
||||||
|
if (child_src.isFile()) {
|
||||||
|
boolean needPhCopy = false;
|
||||||
|
for (String target : phCopied) {
|
||||||
|
if (child.equals(target)) {
|
||||||
|
needPhCopy = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needPhCopy) {
|
||||||
|
Files.copy(child_src.toPath(), /* copy data to -> */ child_dst.toPath(),
|
||||||
|
new CopyOption[] { StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES});
|
||||||
|
} else {
|
||||||
|
Files.createSymbolicLink(child_dst.toPath(), /* link to -> */ child_src.toPath());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clone(child_src, child_dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// modulesDir, like $JDK/lib
|
||||||
|
// oldName, module name under modulesDir
|
||||||
|
// newName, new name for oldName
|
||||||
|
public static void rename(File fromFile, File toFile) throws Exception {
|
||||||
|
if (!fromFile.exists()) {
|
||||||
|
throw new RuntimeException(fromFile.getName() + " does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toFile.exists()) {
|
||||||
|
throw new RuntimeException(toFile.getName() + " already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = fromFile.renameTo(toFile);
|
||||||
|
if (!success) {
|
||||||
|
throw new RuntimeException("rename file " + fromFile.getName()+ " to " + toFile.getName() + " failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ProcessBuilder makeBuilder(String... args) throws Exception {
|
||||||
|
System.out.print("[");
|
||||||
|
for (String s : args) {
|
||||||
|
System.out.print(" " + s);
|
||||||
|
}
|
||||||
|
System.out.println(" ]");
|
||||||
|
return new ProcessBuilder(args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user