Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8261455: Automatically generate the CDS archive if necessary #5997

Closed
wants to merge 8 commits into from
@@ -370,6 +370,7 @@ void DynamicArchive::prepare_for_dynamic_dumping() {
void DynamicArchive::dump(const char* archive_name, TRAPS) {
assert(UseSharedSpaces && RecordDynamicDumpInfo, "already checked in arguments.cpp?");
assert(ArchiveClassesAtExit == nullptr, "already checked in arguments.cpp?");
assert(!AutoCreateSharedArchive, "Should not call this function, instead call dump(TRAPS)");
ArchiveClassesAtExit = archive_name;
if (Arguments::init_shared_archive_paths()) {
prepare_for_dynamic_dumping();
@@ -173,9 +173,17 @@ FileMapInfo::FileMapInfo(bool is_static) {
if (_is_static) {
assert(_current_info == NULL, "must be singleton"); // not thread safe
_current_info = this;
_full_path = Arguments::GetSharedArchivePath();
} else {
assert(_dynamic_archive_info == NULL, "must be singleton"); // not thread safe
_dynamic_archive_info = this;
_full_path = Arguments::GetSharedDynamicArchivePath();
if (AutoCreateSharedArchive) {
if (!validate_archive()) {
// regenerate shared archive at exit
yminqi marked this conversation as resolved.
Show resolved Hide resolved
DynamicDumpSharedSpaces = true;
}
}
}
_file_offset = 0;
_file_open = false;
@@ -189,6 +197,22 @@ FileMapInfo::~FileMapInfo() {
assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe
_dynamic_archive_info = NULL;
}
if (_file_open) {
os::close(_fd);
}
}

// Do preliminary validation on archive. More checks are in initialization.
bool FileMapInfo::validate_archive() {
if (!os::file_exists(_full_path)) {
return false;
}
// validate header info
if (!check_archive(_full_path, _is_static)) {
return false;
}

return true;
}

void FileMapInfo::populate_header(size_t core_region_alignment) {
@@ -204,10 +228,16 @@ void FileMapInfo::populate_header(size_t core_region_alignment) {
// dynamic header including base archive name for non-default base archive
c_header_size = sizeof(DynamicArchiveHeader);
header_size = c_header_size;
if (!FLAG_IS_DEFAULT(SharedArchiveFile)) {
base_archive_name_size = strlen(Arguments::GetSharedArchivePath()) + 1;
header_size += base_archive_name_size;
base_archive_path_offset = c_header_size;
if (SharedArchiveFile != nullptr) {
// -XX:SharedArchiveFile=<file> or <base>:<top>
char* def_base_archive = Arguments::get_default_shared_archive_path();
if (strcmp(def_base_archive, Arguments::GetSharedArchivePath()) != 0) {
base_archive_name_size = strlen(Arguments::GetSharedArchivePath()) + 1;
header_size += base_archive_name_size;
base_archive_path_offset = c_header_size;
}
// Arguments::get_default_shared_archive_path creates buffer, needs release
FREE_C_HEAP_ARRAY(char, def_base_archive);
}
}
_header = (FileMapHeader*)os::malloc(header_size, mtInternal);
@@ -1083,27 +1113,35 @@ class FileHeaderHelper {
return &_header;
}

bool read_base_archive_name(char** target) {
// This function is call from FileMapInfo::get_base_archive_name, and could happen in very early
// vm stage and the log is not available yet. Do not use log in his function.
char* read_base_archive_name() {
assert(_fd != -1, "Archive should be open");
size_t name_size = (size_t)_header._base_archive_name_size;
assert(name_size != 0, "For non-default base archive, name size should be non-zero!");
*target = NEW_C_HEAP_ARRAY(char, name_size, mtInternal);
char* base_name = NEW_C_HEAP_ARRAY(char, name_size, mtInternal);
lseek(_fd, _header._base_archive_path_offset, SEEK_SET); // position to correct offset.
size_t n = os::read(_fd, *target, (unsigned int)name_size);
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, *target);
return false;
warning("Unable to read base archive name from archive");
FREE_C_HEAP_ARRAY(char, base_name);
return nullptr;
}
if (!os::file_exists(*target)) {
log_info(cds)("Base archive %s does not exist", *target);
FREE_C_HEAP_ARRAY(char, *target);
return false;
if (*(base_name + name_size - 1) != '\0' || strlen(base_name) != name_size - 1) {
warning("Base archive name is damaged");
FREE_C_HEAP_ARRAY(char, base_name);
return nullptr;
}
return true;
if (!os::file_exists(base_name)) {
warning("Base archive %s does not exist", base_name);
FREE_C_HEAP_ARRAY(char, base_name);
return nullptr;
}
return base_name;
}
};

// See comment for get_base_archive_name.
bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
FileHeaderHelper file_helper;
if (!file_helper.initialize(archive_name)) {
@@ -1120,34 +1158,45 @@ bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
return false;
}
if (header->_base_archive_path_offset != 0) {
log_info(cds)("_base_archive_path_offset should be 0");
log_info(cds)("_base_archive_path_offset = " UINT32_FORMAT, header->_base_archive_path_offset);
warning("_base_archive_path_offset should be 0");
warning("_base_archive_path_offset = " UINT32_FORMAT, header->_base_archive_path_offset);
return false;
}
} else {
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
vm_exit_during_initialization("Not a top shared archive", archive_name);
if (!AutoCreateSharedArchive) {
vm_exit_during_initialization("Not a top shared archive", archive_name);
}
return false;
}
unsigned int name_size = header->_base_archive_name_size;
unsigned int path_offset = header->_base_archive_path_offset;
unsigned int header_size = header->_header_size;
if (path_offset + name_size != header_size) {
log_info(cds)("_header_size should be equal to _base_archive_path_offset plus _base_archive_name_size");
log_info(cds)(" _base_archive_name_size = " UINT32_FORMAT, name_size);
log_info(cds)(" _base_archive_path_offset = " UINT32_FORMAT, path_offset);
log_info(cds)(" _header_size = " UINT32_FORMAT, header_size);
return false;
}
char* base_name = NULL;
if (!file_helper.read_base_archive_name(&base_name)) {
return false;
if (name_size != 0 && path_offset != 0) {
if (path_offset + name_size != header_size) {
warning("_header_size should be equal to _base_archive_path_offset plus _base_archive_name_size");
warning(" _base_archive_name_size = " UINT32_FORMAT, name_size);
warning(" _base_archive_path_offset = " UINT32_FORMAT, path_offset);
warning(" _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);
} else {
if (name_size != 0 || path_offset != 0) {
warning("_base_archive_name_size and _base_archive_path_offset must be 0 at same time");
return false;
}
}
FREE_C_HEAP_ARRAY(char, base_name);
}
return true;
}

// Since fail_continue calls log function and this function can be called in very early vm stage
// when log is not available yet so do not call fail_continue or do logging in this function.
bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
char** base_archive_name) {
FileHeaderHelper file_helper;
@@ -1157,21 +1206,22 @@ bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
GenericCDSFileMapHeader* header = file_helper.get_generic_file_header();
if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
// Not a dynamic header, no need to proceed further.
warning("Not a dynmaic archive");
return false;
}

if ((header->_base_archive_name_size == 0 && header->_base_archive_path_offset != 0) ||
(header->_base_archive_name_size != 0 && header->_base_archive_path_offset == 0)) {
fail_continue("Default base archive not set correct");
warning("Default base archive not set correct");
return false;
}
if (header->_base_archive_name_size == 0 &&
header->_base_archive_path_offset == 0) {
*base_archive_name = Arguments::get_default_shared_archive_path();
} else {
// read the base archive name
if (!file_helper.read_base_archive_name(base_archive_name)) {
*base_archive_name = NULL;
*base_archive_name = file_helper.read_base_archive_name();
if (*base_archive_name == nullptr) {
return false;
}
}
@@ -2297,13 +2347,16 @@ bool FileMapInfo::initialize() {
return false;
}

if (!open_for_read()) {
return false;
}
if (!init_from_file(_fd)) {
return false;
}
if (!validate_header()) {
// AutoCreateSharedArchive
if (!open_for_read() || !init_from_file(_fd) || !validate_header()) {
if (_is_static) {
FileMapInfo::fail_continue("Initialize static archive failed.");
} else {
FileMapInfo::fail_continue("Initialize dynamic archive failed.");
if (AutoCreateSharedArchive) {
DynamicDumpSharedSpaces = true;
}
}
return false;
}
return true;
@@ -444,6 +444,7 @@ class FileMapInfo : public CHeapObj<mtInternal> {
static void assert_mark(bool check);

// File manipulation.
bool validate_archive() NOT_CDS_RETURN_(false);
bool initialize() NOT_CDS_RETURN_(false);
bool open_for_read();
void open_for_write(const char* path = NULL);
@@ -3130,6 +3130,17 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) {
return JNI_ERR;
}

if (AutoCreateSharedArchive) {
if (SharedArchiveFile == NULL) {
log_info(cds)("-XX:+AutoCreateSharedArchive must work with a valid SharedArchiveFile");
return JNI_ERR;
}
if (ArchiveClassesAtExit != NULL) {
log_info(cds)("-XX:+AutoCreateSharedArchive does not work with ArchiveClassesAtExit");
return JNI_ERR;
}
}

if (ArchiveClassesAtExit == NULL && !RecordDynamicDumpInfo) {
FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, false);
} else {
@@ -3507,6 +3518,7 @@ bool Arguments::init_shared_archive_paths() {
SharedDynamicArchivePath = nullptr;
}
}

if (SharedArchiveFile == NULL) {
SharedArchivePath = get_default_shared_archive_path();
} else {
@@ -3524,6 +3536,7 @@ bool Arguments::init_shared_archive_paths() {
}
}
}

if (!is_dumping_archive()){
if (archives > 2) {
vm_exit_during_initialization(
@@ -3538,6 +3551,11 @@ bool Arguments::init_shared_archive_paths() {
} else {
SharedDynamicArchivePath = temp_archive_path;
}
// +AutoCreateSharedArchive, regenerate the dynamic archive base on default archive.
if (AutoCreateSharedArchive && !os::file_exists(SharedArchivePath)) {
SharedDynamicArchivePath = temp_archive_path;
SharedArchivePath = get_default_shared_archive_path();
}
} else {
extract_shared_archive_paths((const char*)SharedArchiveFile,
&SharedArchivePath, &SharedDynamicArchivePath);
@@ -1825,6 +1825,9 @@ const intx ObjectAlignmentInBytes = 8;
product(bool, RecordDynamicDumpInfo, false, \
"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, \
"Print shared archive file contents") \
\
@@ -139,7 +139,8 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws
runTwo(baseArchiveName, wrongBasePathOffset,
appJar, mainClass, 1,
new String[] {"An error has occurred while processing the shared archive file.",
"Header checksum verification failed",
"Base archive name is damaged",
"Error occurred during initialization of VM",
"Unable to use shared archive"});
// 5. Make base archive name not terminated with '\0'
System.out.println("\n5. Make base archive name not terminated with '\0'");
@@ -152,8 +153,8 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws

runTwo(baseArchiveName, wrongBaseName,
appJar, mainClass, 1,
new String[] {"Base archive " + baseArchiveName,
" does not exist",
"Header checksum verification failed"});
new String[] {"Base archive name is damaged",
"Error occurred during initialization of VM",
"Unable to use shared archive"});
}
}
@@ -94,6 +94,19 @@ public static String getNewArchiveName(String 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
* (given by cmdLineSuffix). At JVM exit, dump all eligible classes into the top archive