8207812: Implement Dynamic CDS Archive
Summary: Improve the usability of AppCDS
Reviewed-by: acorn, jiangli, mseledtsov
Contributed-by: ioi.lam@oracle.com, jianglizhou@google.com, calvin.cheung@oracle.com
--- a/make/hotspot/lib/JvmFeatures.gmk Fri May 17 10:48:02 2019 -0400
+++ b/make/hotspot/lib/JvmFeatures.gmk Fri May 17 08:29:55 2019 -0700
@@ -111,6 +111,7 @@
JVM_EXCLUDE_FILES += \
classListParser.cpp \
classLoaderExt.cpp \
+ dynamicArchive.cpp \
filemap.cpp \
heapShared.cpp \
metaspaceShared.cpp \
--- a/src/hotspot/share/classfile/classListParser.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/classListParser.cpp Fri May 17 08:29:55 2019 -0700
@@ -295,14 +295,14 @@
if (!is_id_specified()) {
error("If source location is specified, id must be also specified");
}
- InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, CHECK_NULL);
-
if (strncmp(_class_name, "java/", 5) == 0) {
log_info(cds)("Prohibited package for non-bootstrap classes: %s.class from %s",
_class_name, _source);
return NULL;
}
+ InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, CHECK_NULL);
+
if (k != NULL) {
if (k->local_interfaces()->length() != _interfaces->length()) {
print_specified_interfaces();
@@ -461,4 +461,3 @@
ShouldNotReachHere();
return NULL;
}
-
--- a/src/hotspot/share/classfile/classLoader.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/classLoader.cpp Fri May 17 08:29:55 2019 -0700
@@ -36,6 +36,7 @@
#include "classfile/klassFactory.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmSymbols.hpp"
#include "compiler/compileBroker.hpp"
#include "interpreter/bytecodeStream.hpp"
@@ -468,7 +469,7 @@
#if INCLUDE_CDS
void ClassLoader::exit_with_path_failure(const char* error, const char* message) {
- assert(DumpSharedSpaces, "only called at dump time");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only called at dump time");
tty->print_cr("Hint: enable -Xlog:class+path=info to diagnose the failure");
vm_exit_during_initialization(error, message);
}
@@ -534,7 +535,7 @@
trace_class_path("bootstrap loader class path=", sys_class_path);
}
#if INCLUDE_CDS
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
_shared_paths_misc_info->add_boot_classpath(sys_class_path);
}
#endif
@@ -550,16 +551,16 @@
return _shared_paths_misc_info->buffer();
}
-bool ClassLoader::check_shared_paths_misc_info(void *buf, int size) {
+bool ClassLoader::check_shared_paths_misc_info(void *buf, int size, bool is_static) {
SharedPathsMiscInfo* checker = new SharedPathsMiscInfo((char*)buf, size);
- bool result = checker->check();
+ bool result = checker->check(is_static);
delete checker;
return result;
}
void ClassLoader::setup_app_search_path(const char *class_path) {
- assert(DumpSharedSpaces, "Sanity");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity");
Thread* THREAD = Thread::current();
int len = (int)strlen(class_path);
@@ -587,7 +588,7 @@
void ClassLoader::add_to_module_path_entries(const char* path,
ClassPathEntry* entry) {
assert(entry != NULL, "ClassPathEntry should not be NULL");
- assert(DumpSharedSpaces, "dump time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only");
// The entry does not exist, add to the list
if (_module_path_entries == NULL) {
@@ -601,7 +602,7 @@
// Add a module path to the _module_path_entries list.
void ClassLoader::update_module_path_entry_list(const char *path, TRAPS) {
- assert(DumpSharedSpaces, "dump time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only");
struct stat st;
if (os::stat(path, &st) != 0) {
tty->print_cr("os::stat error %d (%s). CDS dump aborted (path was \"%s\").",
@@ -709,7 +710,7 @@
bool set_base_piece = true;
#if INCLUDE_CDS
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
if (!Arguments::has_jimage()) {
vm_exit_during_initialization("CDS is not supported in exploded JDK build", NULL);
}
@@ -976,7 +977,7 @@
return true;
} else {
#if INCLUDE_CDS
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
_shared_paths_misc_info->add_nonexist_path(path);
}
#endif
@@ -1334,6 +1335,10 @@
// appear in the _patch_mod_entries. The runtime shared class visibility
// check will determine if a shared class is visible based on the runtime
// environemnt, including the runtime --patch-module setting.
+ //
+ // DynamicDumpSharedSpaces requires UseSharedSpaces to be enabled. Since --patch-module
+ // is not supported with UseSharedSpaces, it is not supported with DynamicDumpSharedSpaces.
+ assert(!DynamicDumpSharedSpaces, "sanity");
if (!DumpSharedSpaces) {
stream = search_module_entries(_patch_mod_entries, class_name, file_name, CHECK_NULL);
}
@@ -1423,7 +1428,7 @@
// Record the shared classpath index and loader type for classes loaded
// by the builtin loaders at dump time.
void ClassLoader::record_result(InstanceKlass* ik, const ClassFileStream* stream, TRAPS) {
- assert(DumpSharedSpaces, "sanity");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "sanity");
assert(stream != NULL, "sanity");
if (ik->is_unsafe_anonymous()) {
@@ -1513,6 +1518,8 @@
// user defined classloader.
if (classpath_index < 0) {
assert(ik->shared_classpath_index() < 0, "Sanity");
+ ik->set_shared_classpath_index(UNREGISTERED_INDEX);
+ SystemDictionaryShared::set_shared_class_misc_info(ik, (ClassFileStream*)stream);
return;
}
} else {
@@ -1595,7 +1602,7 @@
load_jimage_library();
#if INCLUDE_CDS
// initialize search path
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
_shared_paths_misc_info = new SharedPathsMiscInfo();
}
#endif
@@ -1604,14 +1611,14 @@
#if INCLUDE_CDS
void ClassLoader::initialize_shared_path() {
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
ClassLoaderExt::setup_search_paths();
_shared_paths_misc_info->write_jint(0); // see comments in SharedPathsMiscInfo::check()
}
}
void ClassLoader::initialize_module_path(TRAPS) {
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
ClassLoaderExt::setup_module_paths(THREAD);
FileMapInfo::allocate_shared_path_table();
}
@@ -1677,6 +1684,7 @@
// entries will be added to the exploded build array.
if (!has_jrt_entry()) {
assert(!DumpSharedSpaces, "DumpSharedSpaces not supported with exploded module builds");
+ assert(!DynamicDumpSharedSpaces, "DynamicDumpSharedSpaces not supported with exploded module builds");
assert(!UseSharedSpaces, "UsedSharedSpaces not supported with exploded module builds");
// Set up the boot loader's _exploded_entries list. Note that this gets
// done before loading any classes, by the same thread that will
--- a/src/hotspot/share/classfile/classLoader.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/classLoader.hpp Fri May 17 08:29:55 2019 -0700
@@ -398,7 +398,8 @@
// Helper function used by CDS code to get the number of module path
// entries during shared classpath setup time.
static int num_module_path_entries() {
- assert(DumpSharedSpaces, "Should only be called at CDS dump time");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "Should only be called at CDS dump time");
int num_entries = 0;
ClassPathEntry* e= ClassLoader::_module_path_entries;
while (e != NULL) {
@@ -410,7 +411,7 @@
static void finalize_shared_paths_misc_info();
static int get_shared_paths_misc_info_size();
static void* get_shared_paths_misc_info();
- static bool check_shared_paths_misc_info(void* info, int size);
+ static bool check_shared_paths_misc_info(void* info, int size, bool is_static);
static void exit_with_path_failure(const char* error, const char* message);
static char* skip_uri_protocol(char* source);
static void record_result(InstanceKlass* ik, const ClassFileStream* stream, TRAPS);
--- a/src/hotspot/share/classfile/classLoader.inline.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/classLoader.inline.hpp Fri May 17 08:29:55 2019 -0700
@@ -62,7 +62,8 @@
// entries during shared classpath setup time.
inline int ClassLoader::num_boot_classpath_entries() {
- assert(DumpSharedSpaces, "Should only be called at CDS dump time");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "Should only be called at CDS dump time");
assert(has_jrt_entry(), "must have a java runtime image");
int num_entries = 1; // count the runtime image
ClassPathEntry* e = ClassLoader::_first_append_entry;
@@ -84,7 +85,8 @@
// Helper function used by CDS code to get the number of app classpath
// entries during shared classpath setup time.
inline int ClassLoader::num_app_classpath_entries() {
- assert(DumpSharedSpaces, "Should only be called at CDS dump time");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "Should only be called at CDS dump time");
int num_entries = 0;
ClassPathEntry* e= ClassLoader::_app_classpath_entries;
while (e != NULL) {
--- a/src/hotspot/share/classfile/classLoaderExt.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/classLoaderExt.cpp Fri May 17 08:29:55 2019 -0700
@@ -62,7 +62,8 @@
}
void ClassLoaderExt::setup_app_search_path() {
- assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "this function is only used at CDS dump time");
_app_class_paths_start_index = ClassLoader::num_boot_classpath_entries();
char* app_class_path = os::strdup(Arguments::get_appclasspath());
@@ -92,7 +93,8 @@
}
}
void ClassLoaderExt::setup_module_paths(TRAPS) {
- assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "this function is only used with CDS dump time");
_app_module_paths_start_index = ClassLoader::num_boot_classpath_entries() +
ClassLoader::num_app_classpath_entries();
Handle system_class_loader (THREAD, SystemDictionary::java_system_loader());
@@ -227,7 +229,7 @@
void ClassLoaderExt::record_result(const s2 classpath_index,
InstanceKlass* result,
TRAPS) {
- assert(DumpSharedSpaces, "Sanity");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity");
// We need to remember where the class comes from during dumping.
oop loader = result->class_loader();
@@ -301,8 +303,6 @@
tty->print_cr("Preload Error: Failed to load %s", class_name);
return NULL;
}
- result->set_shared_classpath_index(UNREGISTERED_INDEX);
- SystemDictionaryShared::set_shared_class_misc_info(result, stream);
return result;
}
--- a/src/hotspot/share/classfile/compactHashtable.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/compactHashtable.cpp Fri May 17 08:29:55 2019 -0700
@@ -27,6 +27,7 @@
#include "classfile/compactHashtable.hpp"
#include "classfile/javaClasses.hpp"
#include "logging/logMessage.hpp"
+#include "memory/dynamicArchive.hpp"
#include "memory/heapShared.inline.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceShared.hpp"
@@ -39,12 +40,14 @@
//
// The compact hash table writer implementations
//
-CompactHashtableWriter::CompactHashtableWriter(int num_buckets,
+CompactHashtableWriter::CompactHashtableWriter(int num_entries,
CompactHashtableStats* stats) {
- assert(DumpSharedSpaces, "dump-time only");
- assert(num_buckets > 0, "no buckets");
- _num_buckets = num_buckets;
- _num_entries = 0;
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump-time only");
+ assert(num_entries >= 0, "sanity");
+ _num_buckets = calculate_num_buckets(num_entries);
+ assert(_num_buckets > 0, "no buckets");
+
+ _num_entries_written = 0;
_buckets = NEW_C_HEAP_ARRAY(GrowableArray<Entry>*, _num_buckets, mtSymbol);
for (int i=0; i<_num_buckets; i++) {
_buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray<Entry>(0, true, mtSymbol);
@@ -67,11 +70,24 @@
FREE_C_HEAP_ARRAY(GrowableArray<Entry>*, _buckets);
}
+size_t CompactHashtableWriter::estimate_size(int num_entries) {
+ int num_buckets = calculate_num_buckets(num_entries);
+ size_t bucket_bytes = MetaspaceShared::ro_array_bytesize<u4>(num_buckets + 1);
+
+ // In worst case, we have no VALUE_ONLY_BUCKET_TYPE, so each entry takes 2 slots
+ int entries_space = 2 * num_entries;
+ size_t entry_bytes = MetaspaceShared::ro_array_bytesize<u4>(entries_space);
+
+ return bucket_bytes
+ + entry_bytes
+ + SimpleCompactHashtable::calculate_header_size();
+}
+
// Add a symbol entry to the temporary hash table
void CompactHashtableWriter::add(unsigned int hash, u4 value) {
int index = hash % _num_buckets;
_buckets[index]->append_if_missing(Entry(hash, value));
- _num_entries++;
+ _num_entries_written++;
}
void CompactHashtableWriter::allocate_table() {
@@ -81,7 +97,7 @@
int bucket_size = bucket->length();
if (bucket_size == 1) {
entries_space++;
- } else {
+ } else if (bucket_size > 1) {
entries_space += 2 * bucket_size;
}
}
@@ -96,7 +112,7 @@
_stats->bucket_count = _num_buckets;
_stats->bucket_bytes = _compact_buckets->size() * BytesPerWord;
- _stats->hashentry_count = _num_entries;
+ _stats->hashentry_count = _num_entries_written;
_stats->hashentry_bytes = _compact_entries->size() * BytesPerWord;
}
@@ -144,19 +160,19 @@
dump_table(&summary);
int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes;
- address base_address = address(MetaspaceShared::shared_rs()->base());
- cht->init(base_address, _num_entries, _num_buckets,
+ address base_address = address(SharedBaseAddress);
+ cht->init(base_address, _num_entries_written, _num_buckets,
_compact_buckets->data(), _compact_entries->data());
LogMessage(cds, hashtables) msg;
if (msg.is_info()) {
double avg_cost = 0.0;
- if (_num_entries > 0) {
- avg_cost = double(table_bytes)/double(_num_entries);
+ if (_num_entries_written > 0) {
+ avg_cost = double(table_bytes)/double(_num_entries_written);
}
msg.info("Shared %s table stats -------- base: " PTR_FORMAT,
table_name, (intptr_t)base_address);
- msg.info("Number of entries : %9d", _num_entries);
+ msg.info("Number of entries : %9d", _num_entries_written);
msg.info("Total bytes used : %9d", table_bytes);
msg.info("Average bytes per entry : %9.3f", avg_cost);
msg.info("Average bucket size : %9.3f", summary.avg());
@@ -174,7 +190,28 @@
// The CompactHashtable implementation
//
+void SimpleCompactHashtable::init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) {
+ _bucket_count = bucket_count;
+ _entry_count = entry_count;
+ _base_address = base_address;
+ if (DynamicDumpSharedSpaces) {
+ _buckets = DynamicArchive::buffer_to_target(buckets);
+ _entries = DynamicArchive::buffer_to_target(entries);
+ } else {
+ _buckets = buckets;
+ _entries = entries;
+ }
+}
+
+size_t SimpleCompactHashtable::calculate_header_size() {
+ // We have 5 fields. Each takes up sizeof(intptr_t). See WriteClosure::do_u4
+ size_t bytes = sizeof(intptr_t) * 5;
+ return bytes;
+}
+
void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) {
+ // NOTE: if you change this function, you MUST change the number 5 in
+ // calculate_header_size() accordingly.
soc->do_ptr((void**)&_base_address);
soc->do_u4(&_entry_count);
soc->do_u4(&_bucket_count);
--- a/src/hotspot/share/classfile/compactHashtable.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/compactHashtable.hpp Fri May 17 08:29:55 2019 -0700
@@ -100,7 +100,7 @@
}; // class CompactHashtableWriter::Entry
private:
- int _num_entries;
+ int _num_entries_written;
int _num_buckets;
int _num_empty_buckets;
int _num_value_only_buckets;
@@ -112,7 +112,7 @@
public:
// This is called at dump-time only
- CompactHashtableWriter(int num_buckets, CompactHashtableStats* stats);
+ CompactHashtableWriter(int num_entries, CompactHashtableStats* stats);
~CompactHashtableWriter();
void add(unsigned int hash, u4 value);
@@ -120,18 +120,16 @@
private:
void allocate_table();
void dump_table(NumberSeq* summary);
+ static int calculate_num_buckets(int num_entries) {
+ int num_buckets = num_entries / SharedSymbolTableBucketSize;
+ // calculation of num_buckets can result in zero buckets, we need at least one
+ return (num_buckets < 1) ? 1 : num_buckets;
+ }
public:
void dump(SimpleCompactHashtable *cht, const char* table_name);
- static int default_num_buckets(size_t num_entries) {
- return default_num_buckets((int)num_entries);
- }
- static int default_num_buckets(int num_entries) {
- int num_buckets = num_entries / SharedSymbolTableBucketSize;
- // calculation of num_buckets can result in zero buckets, we need at least one
- return (num_buckets < 1) ? 1 : num_buckets;
- }
+ static size_t estimate_size(int num_entries);
};
#endif // INCLUDE_CDS
@@ -214,13 +212,7 @@
_entries = 0;
}
- void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) {
- _base_address = base_address;
- _bucket_count = bucket_count;
- _entry_count = entry_count;
- _buckets = buckets;
- _entries = entries;
- }
+ void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries);
// Read/Write the table's header from/to the CDS archive
void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN;
@@ -228,6 +220,8 @@
inline bool empty() {
return (_entry_count == 0);
}
+
+ static size_t calculate_header_size();
};
template <
--- a/src/hotspot/share/classfile/dictionary.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/dictionary.cpp Fri May 17 08:29:55 2019 -0700
@@ -246,7 +246,7 @@
// Used to scan and relocate the classes during CDS archive dump.
void Dictionary::classes_do(MetaspaceClosure* it) {
- assert(DumpSharedSpaces, "dump-time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump-time only");
for (int index = 0; index < table_size(); index++) {
for (DictionaryEntry* probe = bucket(index);
probe != NULL;
@@ -312,7 +312,6 @@
}
}
-
InstanceKlass* Dictionary::find_class(int index, unsigned int hash,
Symbol* name) {
assert_locked_or_safepoint(SystemDictionary_lock);
--- a/src/hotspot/share/classfile/klassFactory.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/klassFactory.cpp Fri May 17 08:29:55 2019 -0700
@@ -218,7 +218,7 @@
JFR_ONLY(ON_KLASS_CREATION(result, parser, THREAD);)
#if INCLUDE_CDS
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
ClassLoader::record_result(result, stream, THREAD);
}
#endif // INCLUDE_CDS
--- a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -104,7 +104,7 @@
}
}
-bool SharedPathsMiscInfo::check() {
+bool SharedPathsMiscInfo::check(bool is_static) {
// The whole buffer must be 0 terminated so that we can use strlen and strcmp
// without fear.
_end_ptr -= sizeof(jint);
@@ -116,9 +116,10 @@
}
jshort cur_index = 0;
- jshort max_cp_index = FileMapInfo::current_info()->header()->max_used_path_index();
- jshort module_paths_start_index =
- FileMapInfo::current_info()->header()->app_module_paths_start_index();
+ FileMapHeader* header = is_static ? FileMapInfo::current_info()->header() :
+ FileMapInfo::dynamic_info()->header();
+ jshort max_cp_index = header->max_used_path_index();
+ jshort module_paths_start_index = header->app_module_paths_start_index();
while (_cur_ptr < _end_ptr) {
jint type;
const char* path = _cur_ptr;
@@ -136,7 +137,7 @@
}
// skip checking the class path(s) which was not referenced during CDS dump
if ((cur_index <= max_cp_index) || (cur_index >= module_paths_start_index)) {
- if (!check(type, path)) {
+ if (!check(type, path, is_static)) {
if (!PrintSharedArchiveAndExit) {
return false;
}
@@ -171,7 +172,7 @@
return p;
}
-bool SharedPathsMiscInfo::check(jint type, const char* path) {
+bool SharedPathsMiscInfo::check(jint type, const char* path, bool is_static) {
assert(UseSharedSpaces, "runtime only");
switch (type) {
case BOOT_PATH:
@@ -196,7 +197,9 @@
char* rp = skip_first_path_entry(runtime_boot_path);
char* dp = skip_first_path_entry(path);
- bool relaxed_check = !FileMapInfo::current_info()->header()->has_platform_or_app_classes();
+ bool relaxed_check = is_static ?
+ !FileMapInfo::current_info()->header()->has_platform_or_app_classes() :
+ !FileMapInfo::dynamic_info()->header()->has_platform_or_app_classes();
if (dp == NULL && rp == NULL) {
break; // ok, both runtime and dump time boot paths have modules_images only
} else if (dp == NULL && rp != NULL && relaxed_check) {
--- a/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp Fri May 17 08:29:55 2019 -0700
@@ -67,7 +67,7 @@
protected:
static bool fail(const char* msg, const char* name = NULL);
- bool check(jint type, const char* path);
+ bool check(jint type, const char* path, bool is_static);
public:
enum {
@@ -162,7 +162,7 @@
}
public:
- bool check();
+ bool check(bool is_static);
};
#endif // SHARE_CLASSFILE_SHAREDPATHSMISCINFO_HPP
--- a/src/hotspot/share/classfile/stringTable.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/stringTable.cpp Fri May 17 08:29:55 2019 -0700
@@ -779,9 +779,7 @@
assert(HeapShared::is_heap_object_archiving_allowed(), "must be");
_shared_table.reset();
- int num_buckets = CompactHashtableWriter::default_num_buckets(_items_count);
- CompactHashtableWriter writer(num_buckets,
- &MetaspaceShared::stats()->string);
+ CompactHashtableWriter writer(_items_count, &MetaspaceShared::stats()->string);
// Copy the interned strings into the "string space" within the java heap
copy_shared_string_table(&writer);
--- a/src/hotspot/share/classfile/symbolTable.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/symbolTable.cpp Fri May 17 08:29:55 2019 -0700
@@ -28,6 +28,7 @@
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "memory/allocation.inline.hpp"
+#include "memory/dynamicArchive.hpp"
#include "memory/metaspaceClosure.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
@@ -69,6 +70,11 @@
symbol_equals_compact_hashtable_entry
> _shared_table;
+static OffsetCompactHashtable<
+ const char*, Symbol*,
+ symbol_equals_compact_hashtable_entry
+> _dynamic_shared_table;
+
// --------------------------------------------------------------------------
typedef ConcurrentHashTable<Symbol*,
@@ -109,9 +115,11 @@
java_lang_String::hash_code((const jbyte*)s, len);
}
+#if INCLUDE_CDS
static uintx hash_shared_symbol(const char* s, int len) {
return java_lang_String::hash_code((const jbyte*)s, len);
}
+#endif
class SymbolTableConfig : public SymbolTableHash::BaseConfig {
private:
@@ -213,7 +221,7 @@
assert (len <= Symbol::max_length(), "should be checked by caller");
Symbol* sym;
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
c_heap = false;
}
if (c_heap) {
@@ -254,6 +262,7 @@
// all symbols from shared table
SharedSymbolIterator iter(cl);
_shared_table.iterate(&iter);
+ _dynamic_shared_table.iterate(&iter);
// all symbols from the dynamic table
SymbolsDo sd(cl);
@@ -275,7 +284,7 @@
};
void SymbolTable::metaspace_pointers_do(MetaspaceClosure* it) {
- assert(DumpSharedSpaces, "called only during dump time");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "called only during dump time");
MetaspacePointersDo mpd(it);
_local_table->do_safepoint_scan(mpd);
}
@@ -287,19 +296,24 @@
return sym;
}
+#if INCLUDE_CDS
Symbol* SymbolTable::lookup_shared(const char* name,
int len, unsigned int hash) {
+ Symbol* sym = NULL;
if (!_shared_table.empty()) {
if (_alt_hash) {
// hash_code parameter may use alternate hashing algorithm but the shared table
// always uses the same original hash code.
hash = hash_shared_symbol(name, len);
}
- return _shared_table.lookup(name, hash, len);
- } else {
- return NULL;
+ sym = _shared_table.lookup(name, hash, len);
+ if (sym == NULL && DynamicArchive::is_mapped()) {
+ sym = _dynamic_shared_table.lookup(name, hash, len);
+ }
}
+ return sym;
}
+#endif
Symbol* SymbolTable::lookup_common(const char* name,
int len, unsigned int hash) {
@@ -588,6 +602,9 @@
unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length());
assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false),
"must not rehash during dumping");
+ if (DynamicDumpSharedSpaces) {
+ sym = DynamicArchive::original_to_target(sym);
+ }
_writer->add(fixed_hash, MetaspaceShared::object_delta_u4(sym));
return true;
}
@@ -598,30 +615,43 @@
_local_table->do_safepoint_scan(copy);
}
-void SymbolTable::write_to_archive() {
- _shared_table.reset();
+size_t SymbolTable::estimate_size_for_archive() {
+ return CompactHashtableWriter::estimate_size(int(_items_count));
+}
- int num_buckets = CompactHashtableWriter::default_num_buckets(
- _items_count);
- CompactHashtableWriter writer(num_buckets,
+void SymbolTable::write_to_archive(bool is_static_archive) {
+ _shared_table.reset();
+ _dynamic_shared_table.reset();
+
+ CompactHashtableWriter writer(int(_items_count),
&MetaspaceShared::stats()->symbol);
copy_shared_symbol_table(&writer);
- writer.dump(&_shared_table, "symbol");
+ if (is_static_archive) {
+ writer.dump(&_shared_table, "symbol");
- // Verify table is correct
- Symbol* sym = vmSymbols::java_lang_Object();
- const char* name = (const char*)sym->bytes();
- int len = sym->utf8_length();
- unsigned int hash = hash_symbol(name, len, _alt_hash);
- assert(sym == _shared_table.lookup(name, hash, len), "sanity");
+ // Verify table is correct
+ Symbol* sym = vmSymbols::java_lang_Object();
+ const char* name = (const char*)sym->bytes();
+ int len = sym->utf8_length();
+ unsigned int hash = hash_symbol(name, len, _alt_hash);
+ assert(sym == _shared_table.lookup(name, hash, len), "sanity");
+ } else {
+ writer.dump(&_dynamic_shared_table, "symbol");
+ }
}
-void SymbolTable::serialize_shared_table_header(SerializeClosure* soc) {
- _shared_table.serialize_header(soc);
-
+void SymbolTable::serialize_shared_table_header(SerializeClosure* soc,
+ bool is_static_archive) {
+ OffsetCompactHashtable<const char*, Symbol*, symbol_equals_compact_hashtable_entry> * table;
+ if (is_static_archive) {
+ table = &_shared_table;
+ } else {
+ table = &_dynamic_shared_table;
+ }
+ table->serialize_header(soc);
if (soc->writing()) {
// Sanity. Make sure we don't use the shared table at dump time
- _shared_table.reset();
+ table->reset();
}
}
#endif //INCLUDE_CDS
--- a/src/hotspot/share/classfile/symbolTable.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/symbolTable.hpp Fri May 17 08:29:55 2019 -0700
@@ -137,7 +137,7 @@
const char** name, int* lengths,
int* cp_indices, unsigned int* hashValues);
- static Symbol* lookup_shared(const char* name, int len, unsigned int hash);
+ static Symbol* lookup_shared(const char* name, int len, unsigned int hash) NOT_CDS_RETURN_(NULL);
static Symbol* lookup_dynamic(const char* name, int len, unsigned int hash);
static Symbol* lookup_common(const char* name, int len, unsigned int hash);
@@ -209,8 +209,10 @@
private:
static void copy_shared_symbol_table(CompactHashtableWriter* ch_table);
public:
- static void write_to_archive() NOT_CDS_RETURN;
- static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_RETURN;
+ static size_t estimate_size_for_archive() NOT_CDS_RETURN_(0);
+ static void write_to_archive(bool is_static_archive = true) NOT_CDS_RETURN;
+ static void serialize_shared_table_header(SerializeClosure* soc,
+ bool is_static_archive = true) NOT_CDS_RETURN;
static void metaspace_pointers_do(MetaspaceClosure* it);
// Jcmd
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp Fri May 17 08:29:55 2019 -0700
@@ -44,6 +44,7 @@
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
+#include "memory/dynamicArchive.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/klass.inline.hpp"
#include "oops/objArrayOop.inline.hpp"
@@ -61,9 +62,10 @@
objArrayOop SystemDictionaryShared::_shared_protection_domains = NULL;
objArrayOop SystemDictionaryShared::_shared_jar_urls = NULL;
objArrayOop SystemDictionaryShared::_shared_jar_manifests = NULL;
-DEBUG_ONLY(bool SystemDictionaryShared::_checked_excluded_classes = false;)
+DEBUG_ONLY(bool SystemDictionaryShared::_no_class_loading_should_happen = false;)
class DumpTimeSharedClassInfo: public CHeapObj<mtClass> {
+ bool _excluded;
public:
struct DTConstraint {
Symbol* _name;
@@ -76,7 +78,6 @@
int _id;
int _clsfile_size;
int _clsfile_crc32;
- bool _excluded;
GrowableArray<DTConstraint>* _verifier_constraints;
GrowableArray<char>* _verifier_constraint_flags;
@@ -115,6 +116,15 @@
}
}
}
+
+ void set_excluded() {
+ _excluded = true;
+ }
+
+ bool is_excluded() {
+ // _klass may become NULL due to DynamicArchiveBuilder::set_to_null
+ return _excluded || _klass == NULL;
+ }
};
class DumpTimeSharedClassTable: public ResourceHashtable<
@@ -131,8 +141,8 @@
DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k) {
DumpTimeSharedClassInfo* p = get(k);
if (p == NULL) {
- assert(!SystemDictionaryShared::checked_excluded_classes(),
- "no new classes can be added after check_excluded_classes");
+ assert(!SystemDictionaryShared::no_class_loading_should_happen(),
+ "no new classes can be loaded while dumping archive");
put(k, DumpTimeSharedClassInfo());
p = get(k);
assert(p != NULL, "sanity");
@@ -146,16 +156,20 @@
public:
CountClassByCategory(DumpTimeSharedClassTable* table) : _table(table) {}
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
- if (SystemDictionaryShared::is_builtin(k)) {
- ++ _table->_builtin_count;
- } else {
- ++ _table->_unregistered_count;
+ if (!info.is_excluded()) {
+ if (info.is_builtin()) {
+ ++ _table->_builtin_count;
+ } else {
+ ++ _table->_unregistered_count;
+ }
}
return true; // keep on iterating
}
};
void update_counts() {
+ _builtin_count = 0;
+ _unregistered_count = 0;
CountClassByCategory counter(this);
iterate(&counter);
}
@@ -250,26 +264,36 @@
return (char*)(address(this) + verifier_constraint_flags_offset());
}
+ static u4 object_delta_u4(Symbol* sym) {
+ if (DynamicDumpSharedSpaces) {
+ sym = DynamicArchive::original_to_target(sym);
+ }
+ return MetaspaceShared::object_delta_u4(sym);
+ }
+
void init(DumpTimeSharedClassInfo& info) {
_klass = info._klass;
- _num_constraints = info.num_constraints();
if (!SystemDictionaryShared::is_builtin(_klass)) {
CrcInfo* c = crc();
c->_clsfile_size = info._clsfile_size;
c->_clsfile_crc32 = info._clsfile_crc32;
}
+ _num_constraints = info.num_constraints();
if (_num_constraints > 0) {
RTConstraint* constraints = verifier_constraints();
char* flags = verifier_constraint_flags();
int i;
for (i = 0; i < _num_constraints; i++) {
- constraints[i]._name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._name);
- constraints[i]._from_name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._from_name);
+ constraints[i]._name = object_delta_u4(info._verifier_constraints->at(i)._name);
+ constraints[i]._from_name = object_delta_u4(info._verifier_constraints->at(i)._from_name);
}
for (i = 0; i < _num_constraints; i++) {
flags[i] = info._verifier_constraint_flags->at(i);
}
}
+ if (DynamicDumpSharedSpaces) {
+ _klass = DynamicArchive::original_to_target(info._klass);
+ }
}
bool matches(int clsfile_size, int clsfile_crc32) const {
@@ -307,7 +331,12 @@
return *info_pointer_addr(klass);
}
static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) {
- *info_pointer_addr(klass) = record;
+ if (DynamicDumpSharedSpaces) {
+ klass = DynamicArchive::original_to_buffer(klass);
+ *info_pointer_addr(klass) = DynamicArchive::buffer_to_target(record);
+ } else {
+ *info_pointer_addr(klass) = record;
+ }
}
// Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS
@@ -323,8 +352,12 @@
RunTimeSharedClassInfo::EQUALS> {};
static DumpTimeSharedClassTable* _dumptime_table = NULL;
+// SystemDictionaries in the base layer static archive
static RunTimeSharedDictionary _builtin_dictionary;
static RunTimeSharedDictionary _unregistered_dictionary;
+// SystemDictionaries in the top layer dynamic archive
+static RunTimeSharedDictionary _dynamic_builtin_dictionary;
+static RunTimeSharedDictionary _dynamic_unregistered_dictionary;
oop SystemDictionaryShared::shared_protection_domain(int index) {
return _shared_protection_domains->obj_at(index);
@@ -710,6 +743,17 @@
return false;
}
+bool SystemDictionaryShared::has_platform_or_app_classes() {
+ if (FileMapInfo::current_info()->header()->has_platform_or_app_classes()) {
+ return true;
+ }
+ if (DynamicArchive::is_mapped() &&
+ FileMapInfo::dynamic_info()->header()->has_platform_or_app_classes()) {
+ return true;
+ }
+ return false;
+}
+
// The following stack shows how this code is reached:
//
// [0] SystemDictionaryShared::find_or_load_shared_class()
@@ -747,7 +791,7 @@
Symbol* name, Handle class_loader, TRAPS) {
InstanceKlass* k = NULL;
if (UseSharedSpaces) {
- if (!FileMapInfo::current_info()->header()->has_platform_or_app_classes()) {
+ if (!has_platform_or_app_classes()) {
return NULL;
}
@@ -864,11 +908,17 @@
const RunTimeSharedClassInfo* record = find_record(&_unregistered_dictionary, class_name);
if (record == NULL) {
- return NULL;
+ if (DynamicArchive::is_mapped()) {
+ record = find_record(&_dynamic_unregistered_dictionary, class_name);
+ }
+ if (record == NULL) {
+ return NULL;
+ }
}
int clsfile_size = cfs->length();
int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
+
if (!record->matches(clsfile_size, clsfile_crc32)) {
return NULL;
}
@@ -971,6 +1021,7 @@
}
DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) {
+ MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
if (_dumptime_table == NULL) {
_dumptime_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeSharedClassTable();
}
@@ -978,7 +1029,7 @@
}
void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
- assert(DumpSharedSpaces, "only when dumping");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only when dumping");
assert(!is_builtin(k), "must be unregistered class");
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
info->_clsfile_size = cfs->length();
@@ -990,6 +1041,28 @@
}
void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
+ MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
+ DumpTimeSharedClassInfo* p = _dumptime_table->get(k);
+ if (p == NULL) {
+ return;
+ }
+ if (p->_verifier_constraints != NULL) {
+ for (int i = 0; i < p->_verifier_constraints->length(); i++) {
+ DumpTimeSharedClassInfo::DTConstraint constraint = p->_verifier_constraints->at(i);
+ if (constraint._name != NULL ) {
+ constraint._name->decrement_refcount();
+ }
+ if (constraint._from_name != NULL ) {
+ constraint._from_name->decrement_refcount();
+ }
+ }
+ FREE_C_HEAP_ARRAY(DTConstraint, p->_verifier_constraints);
+ p->_verifier_constraints = NULL;
+ }
+ if (p->_verifier_constraint_flags != NULL) {
+ FREE_C_HEAP_ARRAY(char, p->_verifier_constraint_flags);
+ p->_verifier_constraint_flags = NULL;
+ }
_dumptime_table->remove(k);
}
@@ -1010,9 +1083,11 @@
bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) {
if (k->class_loader_data()->is_unsafe_anonymous()) {
+ warn_excluded(k, "Unsafe anonymous class");
return true; // unsafe anonymous classes are not archived, skip
}
if (k->is_in_error_state()) {
+ warn_excluded(k, "In error state");
return true;
}
if (k->shared_classpath_index() < 0 && is_builtin(k)) {
@@ -1036,6 +1111,44 @@
warn_excluded(k, "JFR event class");
return true;
}
+ if (k->init_state() < InstanceKlass::linked) {
+ // In static dumping, we will attempt to link all classes. Those that fail to link will
+ // be marked as in error state.
+ assert(DynamicDumpSharedSpaces, "must be");
+
+ // TODO -- rethink how this can be handled.
+ // We should try to link ik, however, we can't do it here because
+ // 1. We are at VM exit
+ // 2. linking a class may cause other classes to be loaded, which means
+ // a custom ClassLoader.loadClass() may be called, at a point where the
+ // class loader doesn't expect it.
+ warn_excluded(k, "Not linked");
+ return true;
+ }
+ if (k->major_version() < 50 /*JAVA_6_VERSION*/) {
+ ResourceMark rm;
+ log_warning(cds)("Pre JDK 6 class not supported by CDS: %u.%u %s",
+ k->major_version(), k->minor_version(), k->name()->as_C_string());
+ return true;
+ }
+
+ InstanceKlass* super = k->java_super();
+ if (super != NULL && should_be_excluded(super)) {
+ ResourceMark rm;
+ log_warning(cds)("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string());
+ return true;
+ }
+
+ Array<InstanceKlass*>* interfaces = k->local_interfaces();
+ int len = interfaces->length();
+ for (int i = 0; i < len; i++) {
+ InstanceKlass* intf = interfaces->at(i);
+ if (should_be_excluded(intf)) {
+ log_warning(cds)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
+ return true;
+ }
+ }
+
return false;
}
@@ -1044,8 +1157,9 @@
ResourceMark rm;
const char* name = k->name()->as_C_string();
DumpTimeSharedClassInfo* info = _dumptime_table->get(k);
+ assert(_no_class_loading_should_happen, "class loading must be disabled");
guarantee(info != NULL, "Class %s must be entered into _dumptime_table", name);
- guarantee(!info->_excluded, "Should not attempt to archive excluded class %s", name);
+ guarantee(!info->is_excluded(), "Should not attempt to archive excluded class %s", name);
if (is_builtin(k)) {
guarantee(k->loader_type() != 0,
"Class loader type must be set for BUILTIN class %s", name);
@@ -1059,7 +1173,7 @@
public:
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
if (SystemDictionaryShared::should_be_excluded(k)) {
- info._excluded = true;
+ info.set_excluded();
}
return true; // keep on iterating
}
@@ -1068,13 +1182,13 @@
void SystemDictionaryShared::check_excluded_classes() {
ExcludeDumpTimeSharedClasses excl;
_dumptime_table->iterate(&excl);
- DEBUG_ONLY(_checked_excluded_classes = true;)
+ _dumptime_table->update_counts();
}
bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
- assert(_checked_excluded_classes, "sanity");
- assert(DumpSharedSpaces, "only when dumping");
- return find_or_allocate_info_for(k)->_excluded;
+ assert(_no_class_loading_should_happen, "sanity");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only when dumping");
+ return find_or_allocate_info_for(k)->is_excluded();
}
class IterateDumpTimeSharedClassTable : StackObj {
@@ -1083,7 +1197,7 @@
IterateDumpTimeSharedClassTable(MetaspaceClosure* it) : _it(it) {}
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
- if (!info._excluded) {
+ if (!info.is_excluded()) {
info.metaspace_pointers_do(_it);
}
return true; // keep on iterating
@@ -1097,18 +1211,27 @@
bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
- assert(DumpSharedSpaces, "called at dump time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "called at dump time only");
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
info->add_verification_constraint(k, name, from_name, from_field_is_protected,
from_is_array, from_is_object);
- if (is_builtin(k)) {
- // For builtin class loaders, we can try to complete the verification check at dump time,
- // because we can resolve all the constraint classes.
+
+ if (DynamicDumpSharedSpaces) {
+ // For dynamic dumping, we can resolve all the constraint classes for all class loaders during
+ // the initial run prior to creating the archive before vm exit. We will also perform verification
+ // check when running with the archive.
return false;
} else {
- // For non-builtin class loaders, we cannot complete the verification check at dump time,
- // because at dump time we don't know how to resolve classes for such loaders.
- return true;
+ if (is_builtin(k)) {
+ // For builtin class loaders, we can try to complete the verification check at dump time,
+ // because we can resolve all the constraint classes. We will also perform verification check
+ // when running with the archive.
+ return false;
+ } else {
+ // For non-builtin class loaders, we cannot complete the verification check at dump time,
+ // because at dump time we don't know how to resolve classes for such loaders.
+ return true;
+ }
}
}
@@ -1139,9 +1262,9 @@
if (log_is_enabled(Trace, cds, verification)) {
ResourceMark rm;
- log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s",
+ log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s [0x%x]",
k->external_name(), from_name->as_klass_external_name(),
- name->as_klass_external_name());
+ name->as_klass_external_name(), c);
}
}
@@ -1157,6 +1280,13 @@
Symbol* from_name = record->get_constraint_from_name(i);
char c = record->get_constraint_flag(i);
+ if (log_is_enabled(Trace, cds, verification)) {
+ ResourceMark rm(THREAD);
+ log_trace(cds, verification)("check_verification_constraint: %s: %s must be subclass of %s [0x%x]",
+ klass->external_name(), from_name->as_klass_external_name(),
+ name->as_klass_external_name(), c);
+ }
+
bool from_field_is_protected = (c & SystemDictionaryShared::FROM_FIELD_IS_PROTECTED) ? true : false;
bool from_is_array = (c & SystemDictionaryShared::FROM_IS_ARRAY) ? true : false;
bool from_is_object = (c & SystemDictionaryShared::FROM_IS_OBJECT) ? true : false;
@@ -1178,22 +1308,72 @@
}
}
+class EstimateSizeForArchive : StackObj {
+ size_t _shared_class_info_size;
+ int _num_builtin_klasses;
+ int _num_unregistered_klasses;
+
+public:
+ EstimateSizeForArchive() {
+ _shared_class_info_size = 0;
+ _num_builtin_klasses = 0;
+ _num_unregistered_klasses = 0;
+ }
+
+ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
+ if (!info.is_excluded()) {
+ size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints());
+ _shared_class_info_size += align_up(byte_size, BytesPerWord);
+ }
+ return true; // keep on iterating
+ }
+
+ size_t total() {
+ return _shared_class_info_size;
+ }
+};
+
+size_t SystemDictionaryShared::estimate_size_for_archive() {
+ EstimateSizeForArchive est;
+ _dumptime_table->iterate(&est);
+ return est.total() +
+ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(true)) +
+ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(false));
+}
+
class CopySharedClassInfoToArchive : StackObj {
CompactHashtableWriter* _writer;
bool _is_builtin;
public:
- CopySharedClassInfoToArchive(CompactHashtableWriter* writer, bool is_builtin)
+ CopySharedClassInfoToArchive(CompactHashtableWriter* writer,
+ bool is_builtin,
+ bool is_static_archive)
: _writer(writer), _is_builtin(is_builtin) {}
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
- if (!info._excluded && info.is_builtin() == _is_builtin) {
+ if (!info.is_excluded() && info.is_builtin() == _is_builtin) {
size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints());
- RunTimeSharedClassInfo* record =
- (RunTimeSharedClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size);
+ RunTimeSharedClassInfo* record;
+ record = (RunTimeSharedClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size);
record->init(info);
- unsigned int hash = primitive_hash<Symbol*>(info._klass->name());
- _writer->add(hash, MetaspaceShared::object_delta_u4(record));
+ unsigned int hash;
+ Symbol* name = info._klass->name();
+ if (DynamicDumpSharedSpaces) {
+ name = DynamicArchive::original_to_target(name);
+ }
+ hash = primitive_hash<Symbol*>(name);
+ u4 delta;
+ if (DynamicDumpSharedSpaces) {
+ delta = MetaspaceShared::object_delta_u4(DynamicArchive::buffer_to_target(record));
+ } else {
+ delta = MetaspaceShared::object_delta_u4(record);
+ }
+ _writer->add(hash, delta);
+ if (log_is_enabled(Trace, cds, hashtables)) {
+ ResourceMark rm;
+ log_trace(cds,hashtables)("%s dictionary: %s", (_is_builtin ? "builtin" : "unregistered"), info._klass->external_name());
+ }
// Save this for quick runtime lookup of InstanceKlass* -> RunTimeSharedClassInfo*
RunTimeSharedClassInfo::set_for(info._klass, record);
@@ -1202,25 +1382,36 @@
}
};
-void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin) {
+void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary,
+ bool is_builtin,
+ bool is_static_archive) {
CompactHashtableStats stats;
dictionary->reset();
- int num_buckets = CompactHashtableWriter::default_num_buckets(_dumptime_table->count_of(is_builtin));
- CompactHashtableWriter writer(num_buckets, &stats);
- CopySharedClassInfoToArchive copy(&writer, is_builtin);
+ CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats);
+ CopySharedClassInfoToArchive copy(&writer, is_builtin, is_static_archive);
_dumptime_table->iterate(©);
writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary");
}
-void SystemDictionaryShared::write_to_archive() {
- _dumptime_table->update_counts();
- write_dictionary(&_builtin_dictionary, true);
- write_dictionary(&_unregistered_dictionary, false);
+void SystemDictionaryShared::write_to_archive(bool is_static_archive) {
+ if (is_static_archive) {
+ write_dictionary(&_builtin_dictionary, true);
+ write_dictionary(&_unregistered_dictionary, false);
+ } else {
+ write_dictionary(&_dynamic_builtin_dictionary, true);
+ write_dictionary(&_dynamic_unregistered_dictionary, false);
+ }
}
-void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc) {
- _builtin_dictionary.serialize_header(soc);
- _unregistered_dictionary.serialize_header(soc);
+void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc,
+ bool is_static_archive) {
+ if (is_static_archive) {
+ _builtin_dictionary.serialize_header(soc);
+ _unregistered_dictionary.serialize_header(soc);
+ } else {
+ _dynamic_builtin_dictionary.serialize_header(soc);
+ _dynamic_unregistered_dictionary.serialize_header(soc);
+ }
}
const RunTimeSharedClassInfo*
@@ -1237,9 +1428,16 @@
const RunTimeSharedClassInfo* record = find_record(&_builtin_dictionary, name);
if (record) {
return record->_klass;
- } else {
- return NULL;
}
+
+ if (DynamicArchive::is_mapped()) {
+ record = find_record(&_dynamic_builtin_dictionary, name);
+ if (record) {
+ return record->_klass;
+ }
+ }
+
+ return NULL;
}
void SystemDictionaryShared::update_shared_entry(InstanceKlass* k, int id) {
@@ -1266,14 +1464,31 @@
SharedDictionaryPrinter p(st);
_builtin_dictionary.iterate(&p);
_unregistered_dictionary.iterate(&p);
+ if (DynamicArchive::is_mapped()) {
+ _dynamic_builtin_dictionary.iterate(&p);
+ _unregistered_dictionary.iterate(&p);
+ }
}
}
-void SystemDictionaryShared::print() { print_on(tty); }
-
void SystemDictionaryShared::print_table_statistics(outputStream* st) {
if (UseSharedSpaces) {
_builtin_dictionary.print_table_statistics(st, "Builtin Shared Dictionary");
_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary");
+ if (DynamicArchive::is_mapped()) {
+ _dynamic_builtin_dictionary.print_table_statistics(st, "Dynamic Builtin Shared Dictionary");
+ _dynamic_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary");
+ }
}
}
+
+bool SystemDictionaryShared::empty_dumptime_table() {
+ if (_dumptime_table == NULL) {
+ return true;
+ }
+ _dumptime_table->update_counts();
+ if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0){
+ return true;
+ }
+ return false;
+}
--- a/src/hotspot/share/classfile/systemDictionaryShared.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp Fri May 17 08:29:55 2019 -0700
@@ -109,6 +109,7 @@
class RunTimeSharedDictionary;
class SystemDictionaryShared: public SystemDictionary {
+ friend class ExcludeDumpTimeSharedClasses;
public:
enum {
FROM_FIELD_IS_PROTECTED = 1 << 0,
@@ -211,16 +212,21 @@
const ClassFileStream* cfs,
TRAPS);
static DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k);
- static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin);
+ static void write_dictionary(RunTimeSharedDictionary* dictionary,
+ bool is_builtin,
+ bool is_static_archive = true);
static bool is_jfr_event_class(InstanceKlass *k);
static void warn_excluded(InstanceKlass* k, const char* reason);
+ static bool should_be_excluded(InstanceKlass* k);
- DEBUG_ONLY(static bool _checked_excluded_classes;)
+ DEBUG_ONLY(static bool _no_class_loading_should_happen;)
public:
static InstanceKlass* find_builtin_class(Symbol* class_name);
static const RunTimeSharedClassInfo* find_record(RunTimeSharedDictionary* dict, Symbol* name);
+ static bool has_platform_or_app_classes();
+
// Called by PLATFORM/APP loader only
static InstanceKlass* find_or_load_shared_class(Symbol* class_name,
Handle class_loader,
@@ -288,18 +294,34 @@
static bool is_builtin(InstanceKlass* k) {
return (k->shared_classpath_index() != UNREGISTERED_INDEX);
}
- static bool should_be_excluded(InstanceKlass* k);
static void check_excluded_classes();
static void validate_before_archiving(InstanceKlass* k);
static bool is_excluded_class(InstanceKlass* k);
static void dumptime_classes_do(class MetaspaceClosure* it);
- static void write_to_archive();
- static void serialize_dictionary_headers(class SerializeClosure* soc);
- static void print();
+ static size_t estimate_size_for_archive();
+ static void write_to_archive(bool is_static_archive = true);
+ static void serialize_dictionary_headers(class SerializeClosure* soc,
+ bool is_static_archive = true);
+ static void print() { return print_on(tty); }
static void print_on(outputStream* st) NOT_CDS_RETURN;
static void print_table_statistics(outputStream* st) NOT_CDS_RETURN;
+ static bool empty_dumptime_table() NOT_CDS_RETURN_(true);
- DEBUG_ONLY(static bool checked_excluded_classes() {return _checked_excluded_classes;})
+ DEBUG_ONLY(static bool no_class_loading_should_happen() {return _no_class_loading_should_happen;})
+
+#ifdef ASSERT
+ class NoClassLoadingMark: public StackObj {
+ public:
+ NoClassLoadingMark() {
+ assert(!_no_class_loading_should_happen, "must not be nested");
+ _no_class_loading_should_happen = true;
+ }
+ ~NoClassLoadingMark() {
+ _no_class_loading_should_happen = false;
+ }
+ };
+#endif
+
};
#endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
--- a/src/hotspot/share/classfile/verificationType.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/classfile/verificationType.cpp Fri May 17 08:29:55 2019 -0700
@@ -94,12 +94,14 @@
return true;
}
- if (DumpSharedSpaces && SystemDictionaryShared::add_verification_constraint(klass,
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
+ if (SystemDictionaryShared::add_verification_constraint(klass,
name(), from.name(), from_field_is_protected, from.is_array(),
from.is_object())) {
- // If add_verification_constraint() returns true, the resolution/check should be
- // delayed until runtime.
- return true;
+ // If add_verification_constraint() returns true, the resolution/check should be
+ // delayed until runtime.
+ return true;
+ }
}
return resolve_and_check_assignability(klass, name(), from.name(),
--- a/src/hotspot/share/include/cds.h Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/include/cds.h Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -35,6 +35,7 @@
#define NUM_CDS_REGIONS 9
#define CDS_ARCHIVE_MAGIC 0xf00baba2
+#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8
#define CURRENT_CDS_ARCHIVE_VERSION 5
#define INVALID_CDS_ARCHIVE_VERSION -1
--- a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp Fri May 17 08:29:55 2019 -0700
@@ -168,7 +168,7 @@
static bool is_cds_dump_requested() {
// we will not be able to launch recordings if a cds dump is being requested
- if (DumpSharedSpaces && (JfrOptionSet::startup_recording_options() != NULL)) {
+ if ((DumpSharedSpaces || DynamicDumpSharedSpaces) && (JfrOptionSet::startup_recording_options() != NULL)) {
warning("JFR will be disabled during CDS dumping");
teardown_startup_support();
return true;
--- a/src/hotspot/share/logging/logTag.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/logging/logTag.hpp Fri May 17 08:29:55 2019 -0700
@@ -66,6 +66,7 @@
LOG_TAG(defaultmethods) \
LOG_TAG(director) \
LOG_TAG(dump) \
+ LOG_TAG(dynamic) \
LOG_TAG(ergo) \
LOG_TAG(event) \
LOG_TAG(exceptions) \
--- a/src/hotspot/share/memory/allocation.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/allocation.hpp Fri May 17 08:29:55 2019 -0700
@@ -254,10 +254,9 @@
// into a single contiguous memory block, so we can use these
// two pointers to quickly determine if something is in the
// shared metaspace.
- //
// When CDS is not enabled, both pointers are set to NULL.
- static void* _shared_metaspace_base; // (inclusive) low address
- static void* _shared_metaspace_top; // (exclusive) high address
+ static void* _shared_metaspace_base; // (inclusive) low address
+ static void* _shared_metaspace_top; // (exclusive) high address
public:
@@ -269,7 +268,8 @@
static bool is_shared(const MetaspaceObj* p) {
// If no shared metaspace regions are mapped, _shared_metaspace_{base,top} will
// both be NULL and all values of p will be rejected quickly.
- return (((void*)p) < _shared_metaspace_top && ((void*)p) >= _shared_metaspace_base);
+ return (((void*)p) < _shared_metaspace_top &&
+ ((void*)p) >= _shared_metaspace_base);
}
bool is_shared() const { return MetaspaceObj::is_shared(this); }
@@ -279,6 +279,12 @@
_shared_metaspace_base = base;
_shared_metaspace_top = top;
}
+
+ static void expand_shared_metaspace_range(void* top) {
+ assert(top >= _shared_metaspace_top, "must be");
+ _shared_metaspace_top = top;
+ }
+
static void* shared_metaspace_base() { return _shared_metaspace_base; }
static void* shared_metaspace_top() { return _shared_metaspace_top; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/dynamicArchive.cpp Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jvm.h"
+#include "classfile/classLoaderData.inline.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "logging/log.hpp"
+#include "memory/metadataFactory.hpp"
+#include "memory/metaspace.hpp"
+#include "memory/metaspaceClosure.hpp"
+#include "memory/metaspaceShared.hpp"
+#include "memory/resourceArea.hpp"
+#include "memory/dynamicArchive.hpp"
+#include "oops/compressedOops.hpp"
+#include "oops/objArrayKlass.hpp"
+#include "prims/jvmtiRedefineClasses.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/os.inline.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/vmThread.hpp"
+#include "runtime/vmOperations.hpp"
+#include "utilities/bitMap.inline.hpp"
+
+#ifndef O_BINARY // if defined (Win32) use binary files.
+#define O_BINARY 0 // otherwise do nothing.
+#endif
+
+class DynamicArchiveBuilder : ResourceObj {
+ CHeapBitMap _ptrmap;
+ static unsigned my_hash(const address& a) {
+ return primitive_hash<address>(a);
+ }
+ static bool my_equals(const address& a0, const address& a1) {
+ return primitive_equals<address>(a0, a1);
+ }
+ typedef ResourceHashtable<
+ address, address,
+ DynamicArchiveBuilder::my_hash, // solaris compiler doesn't like: primitive_hash<address>
+ DynamicArchiveBuilder::my_equals, // solaris compiler doesn't like: primitive_equals<address>
+ 16384, ResourceObj::C_HEAP> RelocationTable;
+ RelocationTable _new_loc_table;
+
+ intx _buffer_to_target_delta;
+
+ DumpRegion* _current_dump_space;
+
+ static size_t reserve_alignment() {
+ return Metaspace::reserve_alignment();
+ }
+
+ static const int _total_dump_regions = 3;
+ int _num_dump_regions_used;
+
+public:
+ void mark_pointer(address* ptr_loc) {
+ if (is_in_buffer_space(ptr_loc)) {
+ size_t idx = pointer_delta(ptr_loc, _alloc_bottom, sizeof(address));
+ _ptrmap.set_bit(idx);
+ }
+ }
+
+ DumpRegion* current_dump_space() const {
+ return _current_dump_space;
+ }
+
+ bool is_in_buffer_space(address p) const {
+ return (_alloc_bottom <= p && p < (address)current_dump_space()->top());
+ }
+
+ template <typename T> bool is_in_target_space(T target_obj) const {
+ address buff_obj = address(target_obj) - _buffer_to_target_delta;
+ return is_in_buffer_space(buff_obj);
+ }
+
+ template <typename T> bool is_in_buffer_space(T obj) const {
+ return is_in_buffer_space(address(obj));
+ }
+
+ template <typename T> T to_target_no_check(T obj) const {
+ return (T)(address(obj) + _buffer_to_target_delta);
+ }
+
+ template <typename T> T to_target(T obj) const {
+ assert(is_in_buffer_space(obj), "must be");
+ return (T)(address(obj) + _buffer_to_target_delta);
+ }
+
+ template <typename T> T get_new_loc(T obj) {
+ address* pp = _new_loc_table.get((address)obj);
+ if (pp == NULL) {
+ // Excluded klasses are not copied
+ return NULL;
+ } else {
+ return (T)*pp;
+ }
+ }
+
+ address get_new_loc(MetaspaceClosure::Ref* ref) {
+ return get_new_loc(ref->obj());
+ }
+
+ template <typename T> bool has_new_loc(T obj) {
+ address* pp = _new_loc_table.get((address)obj);
+ return pp != NULL;
+ }
+
+protected:
+ enum FollowMode {
+ make_a_copy, point_to_it, set_to_null
+ };
+
+public:
+ void copy(MetaspaceClosure::Ref* ref, bool read_only) {
+ int bytes = ref->size() * BytesPerWord;
+ address old_obj = ref->obj();
+ address new_obj = copy_impl(ref, read_only, bytes);
+
+ assert(new_obj != NULL, "must be");
+ assert(new_obj != old_obj, "must be");
+ bool isnew = _new_loc_table.put(old_obj, new_obj);
+ assert(isnew, "must be");
+ }
+
+ // Make a shallow copy of each eligible MetaspaceObj into the buffer.
+ class ShallowCopier: public UniqueMetaspaceClosure {
+ DynamicArchiveBuilder* _builder;
+ bool _read_only;
+ public:
+ ShallowCopier(DynamicArchiveBuilder* shuffler, bool read_only)
+ : _builder(shuffler), _read_only(read_only) {}
+
+ virtual bool do_unique_ref(Ref* orig_obj, bool read_only) {
+ // This method gets called on each *original* object
+ // reachable from _builder->iterate_roots(). Each orig_obj is
+ // called exactly once.
+ FollowMode mode = _builder->follow_ref(orig_obj);
+
+ if (mode == point_to_it) {
+ if (read_only == _read_only) {
+ log_debug(cds, dynamic)("ptr : " PTR_FORMAT " %s", p2i(orig_obj->obj()),
+ MetaspaceObj::type_name(orig_obj->msotype()));
+ address p = orig_obj->obj();
+ bool isnew = _builder->_new_loc_table.put(p, p);
+ assert(isnew, "must be");
+ }
+ return false;
+ }
+
+ if (mode == set_to_null) {
+ log_debug(cds, dynamic)("nul : " PTR_FORMAT " %s", p2i(orig_obj->obj()),
+ MetaspaceObj::type_name(orig_obj->msotype()));
+ return false;
+ }
+
+ if (read_only == _read_only) {
+ // Make a shallow copy of orig_obj in a buffer (maintained
+ // by copy_impl in a subclass of DynamicArchiveBuilder).
+ _builder->copy(orig_obj, read_only);
+ }
+ return true;
+ }
+ };
+
+ // Relocate all embedded pointer fields within a MetaspaceObj's shallow copy
+ class ShallowCopyEmbeddedRefRelocator: public UniqueMetaspaceClosure {
+ DynamicArchiveBuilder* _builder;
+ public:
+ ShallowCopyEmbeddedRefRelocator(DynamicArchiveBuilder* shuffler)
+ : _builder(shuffler) {}
+
+ // This method gets called on each *original* object reachable
+ // from _builder->iterate_roots(). Each orig_obj is
+ // called exactly once.
+ virtual bool do_unique_ref(Ref* orig_ref, bool read_only) {
+ FollowMode mode = _builder->follow_ref(orig_ref);
+
+ if (mode == point_to_it) {
+ // We did not make a copy of this object
+ // and we have nothing to update
+ assert(_builder->get_new_loc(orig_ref) == NULL ||
+ _builder->get_new_loc(orig_ref) == orig_ref->obj(), "must be");
+ return false;
+ }
+
+ if (mode == set_to_null) {
+ // We did not make a copy of this object
+ // and we have nothing to update
+ assert(!_builder->has_new_loc(orig_ref->obj()), "must not be copied or pointed to");
+ return false;
+ }
+
+ // - orig_obj points to the original object.
+ // - new_obj points to the shallow copy (created by ShallowCopier)
+ // of orig_obj. new_obj is NULL if the orig_obj is excluded
+ address orig_obj = orig_ref->obj();
+ address new_obj = _builder->get_new_loc(orig_ref);
+
+ assert(new_obj != orig_obj, "must be");
+#ifdef ASSERT
+ if (new_obj == NULL) {
+ if (orig_ref->msotype() == MetaspaceObj::ClassType) {
+ Klass* k = (Klass*)orig_obj;
+ assert(k->is_instance_klass() &&
+ SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(k)),
+ "orig_obj must be excluded Class");
+ }
+ }
+#endif
+
+ log_debug(cds, dynamic)("Relocating " PTR_FORMAT " %s", p2i(new_obj),
+ MetaspaceObj::type_name(orig_ref->msotype()));
+ if (new_obj != NULL) {
+ EmbeddedRefUpdater updater(_builder, orig_obj, new_obj);
+ orig_ref->metaspace_pointers_do(&updater);
+ }
+
+ return true; // keep recursing until every object is visited exactly once.
+ }
+ };
+
+ class EmbeddedRefUpdater: public MetaspaceClosure {
+ DynamicArchiveBuilder* _builder;
+ address _orig_obj;
+ address _new_obj;
+ public:
+ EmbeddedRefUpdater(DynamicArchiveBuilder* shuffler, address orig_obj, address new_obj) :
+ _builder(shuffler), _orig_obj(orig_obj), _new_obj(new_obj) {}
+
+ // This method gets called once for each pointer field F of orig_obj.
+ // We update new_obj->F to point to the new location of orig_obj->F.
+ //
+ // Example: Klass* 0x100 is copied to 0x400
+ // Symbol* 0x200 is copied to 0x500
+ //
+ // Let orig_obj == 0x100; and
+ // new_obj == 0x400; and
+ // ((Klass*)orig_obj)->_name == 0x200;
+ // Then this function effectively assigns
+ // ((Klass*)new_obj)->_name = 0x500;
+ virtual bool do_ref(Ref* ref, bool read_only) {
+ address new_pointee = NULL;
+
+ if (ref->not_null()) {
+ address old_pointee = ref->obj();
+
+ FollowMode mode = _builder->follow_ref(ref);
+ if (mode == point_to_it) {
+ new_pointee = old_pointee;
+ } else if (mode == set_to_null) {
+ new_pointee = NULL;
+ } else {
+ new_pointee = _builder->get_new_loc(old_pointee);
+ }
+ }
+
+ const char* kind = MetaspaceObj::type_name(ref->msotype());
+ // offset of this field inside the original object
+ intx offset = (address)ref->addr() - _orig_obj;
+ _builder->update_pointer((address*)(_new_obj + offset), new_pointee, kind, offset);
+
+ // We can't mark the pointer here, because DynamicArchiveBuilder::sort_methods
+ // may re-layout the [iv]tables, which would change the offset(s) in an InstanceKlass
+ // that would contain pointers. Therefore, we must mark the pointers after
+ // sort_methods(), using PointerMarker.
+ return false; // Do not recurse.
+ }
+ };
+
+ class ExternalRefUpdater: public MetaspaceClosure {
+ DynamicArchiveBuilder* _builder;
+
+ public:
+ ExternalRefUpdater(DynamicArchiveBuilder* shuffler) : _builder(shuffler) {}
+
+ virtual bool do_ref(Ref* ref, bool read_only) {
+ // ref is a pointer that lives OUTSIDE of the buffer, but points to an object inside the buffer
+ if (ref->not_null()) {
+ address new_loc = _builder->get_new_loc(ref);
+ const char* kind = MetaspaceObj::type_name(ref->msotype());
+ _builder->update_pointer(ref->addr(), new_loc, kind, 0);
+ _builder->mark_pointer(ref->addr());
+ }
+ return false; // Do not recurse.
+ }
+ };
+
+ class PointerMarker: public UniqueMetaspaceClosure {
+ DynamicArchiveBuilder* _builder;
+
+ public:
+ PointerMarker(DynamicArchiveBuilder* shuffler) : _builder(shuffler) {}
+
+ virtual bool do_unique_ref(Ref* ref, bool read_only) {
+ if (_builder->is_in_buffer_space(ref->obj())) {
+ EmbeddedRefMarker ref_marker(_builder);
+ ref->metaspace_pointers_do(&ref_marker);
+ return true; // keep recursing until every buffered object is visited exactly once.
+ } else {
+ return false;
+ }
+ }
+ };
+
+ class EmbeddedRefMarker: public MetaspaceClosure {
+ DynamicArchiveBuilder* _builder;
+
+ public:
+ EmbeddedRefMarker(DynamicArchiveBuilder* shuffler) : _builder(shuffler) {}
+ virtual bool do_ref(Ref* ref, bool read_only) {
+ if (ref->not_null() && _builder->is_in_buffer_space(ref->obj())) {
+ _builder->mark_pointer(ref->addr());
+ }
+ return false; // Do not recurse.
+ }
+ };
+
+ void update_pointer(address* addr, address value, const char* kind, uintx offset, bool is_mso_pointer=true) {
+ // Propagate the the mask bits to the new value -- see comments above MetaspaceClosure::obj()
+ if (is_mso_pointer) {
+ const uintx FLAG_MASK = 0x03;
+ uintx mask_bits = uintx(*addr) & FLAG_MASK;
+ value = (address)(uintx(value) | mask_bits);
+ }
+
+ if (*addr != value) {
+ log_debug(cds, dynamic)("Update (%18s*) %3d [" PTR_FORMAT "] " PTR_FORMAT " -> " PTR_FORMAT,
+ kind, int(offset), p2i(addr), p2i(*addr), p2i(value));
+ *addr = value;
+ }
+ }
+
+private:
+ GrowableArray<Symbol*>* _symbols; // symbols to dump
+ GrowableArray<InstanceKlass*>* _klasses; // klasses to dump
+
+ void append(InstanceKlass* k) { _klasses->append(k); }
+ void append(Symbol* s) { _symbols->append(s); }
+
+ class GatherKlassesAndSymbols : public UniqueMetaspaceClosure {
+ DynamicArchiveBuilder* _builder;
+ bool _read_only;
+
+ public:
+ GatherKlassesAndSymbols(DynamicArchiveBuilder* builder)
+ : _builder(builder) {}
+
+ virtual bool do_unique_ref(Ref* ref, bool read_only) {
+ if (_builder->follow_ref(ref) != make_a_copy) {
+ return false;
+ }
+ if (ref->msotype() == MetaspaceObj::ClassType) {
+ Klass* klass = (Klass*)ref->obj();
+ assert(klass->is_klass(), "must be");
+ if (klass->is_instance_klass()) {
+ InstanceKlass* ik = InstanceKlass::cast(klass);
+ assert(!SystemDictionaryShared::is_excluded_class(ik), "must be");
+ _builder->append(ik);
+ _builder->_estimated_metsapceobj_bytes += BytesPerWord; // See RunTimeSharedClassInfo::get_for()
+ }
+ } else if (ref->msotype() == MetaspaceObj::SymbolType) {
+ _builder->append((Symbol*)ref->obj());
+ }
+
+ int bytes = ref->size() * BytesPerWord;
+ _builder->_estimated_metsapceobj_bytes += bytes;
+
+ return true;
+ }
+ };
+
+ FollowMode follow_ref(MetaspaceClosure::Ref *ref) {
+ address obj = ref->obj();
+ if (MetaspaceShared::is_in_shared_metaspace(obj)) {
+ // Don't dump existing shared metadata again.
+ return point_to_it;
+ } else if (ref->msotype() == MetaspaceObj::MethodDataType) {
+ return set_to_null;
+ } else {
+ if (ref->msotype() == MetaspaceObj::ClassType) {
+ Klass* klass = (Klass*)ref->obj();
+ assert(klass->is_klass(), "must be");
+ if (klass->is_instance_klass()) {
+ InstanceKlass* ik = InstanceKlass::cast(klass);
+ if (SystemDictionaryShared::is_excluded_class(ik)) {
+ ResourceMark rm;
+ log_debug(cds, dynamic)("Skipping class (excluded): %s", klass->external_name());
+ return set_to_null;
+ }
+ } else if (klass->is_array_klass()) {
+ // Don't support archiving of array klasses for now.
+ ResourceMark rm;
+ log_debug(cds, dynamic)("Skipping class (array): %s", klass->external_name());
+ return set_to_null;
+ }
+ }
+
+ return make_a_copy;
+ }
+ }
+
+ address copy_impl(MetaspaceClosure::Ref* ref, bool read_only, int bytes) {
+ if (ref->msotype() == MetaspaceObj::ClassType) {
+ // Save a pointer immediate in front of an InstanceKlass, so
+ // we can do a quick lookup from InstanceKlass* -> RunTimeSharedClassInfo*
+ // without building another hashtable. See RunTimeSharedClassInfo::get_for()
+ // in systemDictionaryShared.cpp.
+ address obj = ref->obj();
+ Klass* klass = (Klass*)obj;
+ if (klass->is_instance_klass()) {
+ SystemDictionaryShared::validate_before_archiving(InstanceKlass::cast(klass));
+ current_dump_space()->allocate(sizeof(address), BytesPerWord);
+ }
+ }
+ address p = (address)current_dump_space()->allocate(bytes);
+ address obj = ref->obj();
+ log_debug(cds, dynamic)("COPY: " PTR_FORMAT " ==> " PTR_FORMAT " %5d %s",
+ p2i(obj), p2i(p), bytes,
+ MetaspaceObj::type_name(ref->msotype()));
+ memcpy(p, obj, bytes);
+
+ intptr_t* cloned_vtable = MetaspaceShared::fix_cpp_vtable_for_dynamic_archive(ref->msotype(), p);
+ if (cloned_vtable != NULL) {
+ update_pointer((address*)p, (address)cloned_vtable, "vtb", 0, /*is_mso_pointer*/false);
+ }
+
+ return (address)p;
+ }
+
+ DynamicArchiveHeader *_header;
+ address _alloc_bottom;
+ address _last_verified_top;
+ size_t _other_region_used_bytes;
+
+ // Conservative estimate for number of bytes needed for:
+ size_t _estimated_metsapceobj_bytes; // all archived MetsapceObj's.
+ size_t _estimated_hashtable_bytes; // symbol table and dictionaries
+ size_t _estimated_trampoline_bytes; // method entry trampolines
+
+ size_t estimate_archive_size();
+ size_t estimate_trampoline_size();
+ size_t estimate_class_file_size();
+ address reserve_space_and_init_buffer_to_target_delta();
+ void init_header(address addr);
+ void make_trampolines();
+ void make_klasses_shareable();
+ void sort_methods(InstanceKlass* ik) const;
+ void set_symbols_permanent();
+ void relocate_buffer_to_target();
+ void write_archive(char* read_only_tables_start);
+
+ void init_first_dump_space(address reserved_bottom) {
+ address first_space_base = reserved_bottom;
+ DumpRegion* rw_space = MetaspaceShared::read_write_dump_space();
+ MetaspaceShared::init_shared_dump_space(rw_space, first_space_base);
+ _current_dump_space = rw_space;
+ _last_verified_top = first_space_base;
+ _num_dump_regions_used = 1;
+ }
+
+public:
+ DynamicArchiveBuilder() {
+ _klasses = new (ResourceObj::C_HEAP, mtClass) GrowableArray<InstanceKlass*>(100, true, mtInternal);
+ _symbols = new (ResourceObj::C_HEAP, mtClass) GrowableArray<Symbol*>(1000, true, mtInternal);
+
+ _estimated_metsapceobj_bytes = 0;
+ _estimated_hashtable_bytes = 0;
+ _estimated_trampoline_bytes = 0;
+
+ _num_dump_regions_used = 0;
+ }
+
+ void start_dump_space(DumpRegion* next) {
+ address bottom = _last_verified_top;
+ address top = (address)(current_dump_space()->top());
+ _other_region_used_bytes += size_t(top - bottom);
+
+ MetaspaceShared::pack_dump_space(current_dump_space(), next, MetaspaceShared::shared_rs());
+ _current_dump_space = next;
+ _num_dump_regions_used ++;
+
+ _last_verified_top = (address)(current_dump_space()->top());
+ }
+
+ void verify_estimate_size(size_t estimate, const char* which) {
+ address bottom = _last_verified_top;
+ address top = (address)(current_dump_space()->top());
+ size_t used = size_t(top - bottom) + _other_region_used_bytes;
+ int diff = int(estimate) - int(used);
+
+ log_info(cds)("%s estimate = %lu used = %lu; diff = %d bytes", which, estimate, used, diff);
+ assert(diff >= 0, "Estimate is too small");
+
+ _last_verified_top = top;
+ _other_region_used_bytes = 0;
+ }
+
+ // Do this before and after the archive dump to see if any corruption
+ // is caused by dynamic dumping.
+ void verify_universe(const char* info) {
+ if (VerifyBeforeExit) {
+ log_info(cds)("Verify %s", info);
+ HandleMark hm;
+ // Among other things, this ensures that Eden top is correct.
+ Universe::heap()->prepare_for_verify();
+ Universe::verify(info);
+ }
+ }
+
+ void doit() {
+ verify_universe("Before CDS dynamic dump");
+ DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
+ SystemDictionaryShared::check_excluded_classes();
+
+ {
+ ResourceMark rm;
+ GatherKlassesAndSymbols gatherer(this);
+
+ SystemDictionaryShared::dumptime_classes_do(&gatherer);
+ SymbolTable::metaspace_pointers_do(&gatherer);
+ FileMapInfo::metaspace_pointers_do(&gatherer);
+
+ gatherer.finish();
+ }
+
+ // rw space starts ...
+ address reserved_bottom = reserve_space_and_init_buffer_to_target_delta();
+ init_header(reserved_bottom);
+
+ verify_estimate_size(sizeof(DynamicArchiveHeader), "header");
+
+ log_info(cds, dynamic)("Copying %d klasses and %d symbols",
+ _klasses->length(), _symbols->length());
+
+ {
+ assert(current_dump_space() == MetaspaceShared::read_write_dump_space(),
+ "Current dump space is not rw space");
+ // shallow-copy RW objects, if necessary
+ ResourceMark rm;
+ ShallowCopier rw_copier(this, false);
+ iterate_roots(&rw_copier);
+ }
+
+ // ro space starts ...
+ DumpRegion* ro_space = MetaspaceShared::read_only_dump_space();
+ {
+ start_dump_space(ro_space);
+
+ // shallow-copy RO objects, if necessary
+ ResourceMark rm;
+ ShallowCopier ro_copier(this, true);
+ iterate_roots(&ro_copier);
+ }
+
+ size_t bitmap_size = pointer_delta(current_dump_space()->top(),
+ _alloc_bottom, sizeof(address));
+ _ptrmap.initialize(bitmap_size);
+
+ {
+ log_info(cds)("Relocating embedded pointers ... ");
+ ResourceMark rm;
+ ShallowCopyEmbeddedRefRelocator emb_reloc(this);
+ iterate_roots(&emb_reloc);
+ }
+
+ {
+ log_info(cds)("Relocating external roots ... ");
+ ResourceMark rm;
+ ExternalRefUpdater ext_reloc(this);
+ iterate_roots(&ext_reloc);
+ }
+
+ verify_estimate_size(_estimated_metsapceobj_bytes, "MetaspaceObjs");
+
+ char* read_only_tables_start;
+ {
+ set_symbols_permanent();
+
+ // Write the symbol table and system dictionaries to the RO space.
+ // Note that these tables still point to the *original* objects
+ // (because they were not processed by ExternalRefUpdater), so
+ // they would need to call DynamicArchive::original_to_target() to
+ // get the correct addresses.
+ assert(current_dump_space() == ro_space, "Must be RO space");
+ SymbolTable::write_to_archive(false);
+ SystemDictionaryShared::write_to_archive(false);
+
+ read_only_tables_start = ro_space->top();
+ WriteClosure wc(ro_space);
+ SymbolTable::serialize_shared_table_header(&wc, false);
+ SystemDictionaryShared::serialize_dictionary_headers(&wc, false);
+ }
+
+ verify_estimate_size(_estimated_hashtable_bytes, "Hashtables");
+
+ // mc space starts ...
+ {
+ start_dump_space(MetaspaceShared::misc_code_dump_space());
+ make_trampolines();
+ }
+
+ verify_estimate_size(_estimated_trampoline_bytes, "Trampolines");
+
+ make_klasses_shareable();
+
+ {
+ log_info(cds)("Final relocation of pointers ... ");
+ ResourceMark rm;
+ PointerMarker marker(this);
+ iterate_roots(&marker);
+ relocate_buffer_to_target();
+ }
+
+ write_archive(read_only_tables_start);
+
+ assert(_num_dump_regions_used == _total_dump_regions, "must be");
+ verify_universe("After CDS dynamic dump");
+ }
+
+ void iterate_roots(MetaspaceClosure* it) {
+ int i;
+ int num_klasses = _klasses->length();
+ for (i = 0; i < num_klasses; i++) {
+ it->push(&_klasses->at(i));
+ }
+
+ int num_symbols = _symbols->length();
+ for (i = 0; i < num_symbols; i++) {
+ it->push(&_symbols->at(i));
+ }
+
+ _header->_shared_path_table.metaspace_pointers_do(it);
+
+ // Do not call these again, as we have already collected all the classes and symbols
+ // that we want to archive. Also, these calls would corrupt the tables when
+ // ExternalRefUpdater is used.
+ //
+ // SystemDictionaryShared::dumptime_classes_do(it);
+ // SymbolTable::metaspace_pointers_do(it);
+
+ it->finish();
+ }
+};
+
+size_t DynamicArchiveBuilder::estimate_archive_size() {
+ // size of the symbol table and two dictionaries, plus the RunTimeSharedClassInfo's
+ _estimated_hashtable_bytes = 0;
+ _estimated_hashtable_bytes += SymbolTable::estimate_size_for_archive();
+ _estimated_hashtable_bytes += SystemDictionaryShared::estimate_size_for_archive();
+
+ _estimated_trampoline_bytes = estimate_trampoline_size();
+
+ size_t total = 0;
+
+ total += _estimated_metsapceobj_bytes;
+ total += _estimated_hashtable_bytes;
+ total += _estimated_trampoline_bytes;
+
+ // allow fragmentation at the end of each dump region
+ total += _total_dump_regions * reserve_alignment();
+
+ return align_up(total, reserve_alignment());
+}
+
+address DynamicArchiveBuilder::reserve_space_and_init_buffer_to_target_delta() {
+ size_t total = estimate_archive_size();
+ bool large_pages = false; // No large pages when dumping the CDS archive.
+ size_t increment = align_up(1*G, reserve_alignment());
+ char* addr = (char*)align_up(CompressedKlassPointers::base() + MetaspaceSize + increment,
+ reserve_alignment());
+
+ ReservedSpace* rs = MetaspaceShared::reserve_shared_rs(
+ total, reserve_alignment(), large_pages, addr);
+ while (!rs->is_reserved() && (addr + increment > addr)) {
+ addr += increment;
+ rs = MetaspaceShared::reserve_shared_rs(
+ total, reserve_alignment(), large_pages, addr);
+ }
+ if (!rs->is_reserved()) {
+ log_error(cds, dynamic)("Failed to reserve %d bytes of output buffer.", (int)total);
+ vm_direct_exit(0);
+ }
+
+ address buffer_base = (address)rs->base();
+ log_info(cds, dynamic)("Reserved output buffer space at : " PTR_FORMAT " [%d bytes]",
+ p2i(buffer_base), (int)total);
+
+ // At run time, we will mmap the dynamic archive at target_space_bottom.
+ // However, at dump time, we may not be able to write into the target_space,
+ // as it's occupied by dynamically loaded Klasses. So we allocate a buffer
+ // at an arbitrary location chosen by the OS. We will write all the dynamically
+ // archived classes into this buffer. At the final stage of dumping, we relocate
+ // all pointers that are inside the buffer_space to point to their (runtime)
+ // target location inside thetarget_space.
+ address target_space_bottom =
+ (address)align_up(MetaspaceShared::shared_metaspace_top(), reserve_alignment());
+ _buffer_to_target_delta = intx(target_space_bottom) - intx(buffer_base);
+
+ log_info(cds, dynamic)("Target archive space at : " PTR_FORMAT, p2i(target_space_bottom));
+ log_info(cds, dynamic)("Buffer-space to target-space delta : " PTR_FORMAT, p2i((address)_buffer_to_target_delta));
+
+ return buffer_base;
+}
+
+void DynamicArchiveBuilder::init_header(address reserved_bottom) {
+ _alloc_bottom = reserved_bottom;
+ _last_verified_top = reserved_bottom;
+ _other_region_used_bytes = 0;
+
+ init_first_dump_space(reserved_bottom);
+
+ FileMapInfo* mapinfo = new FileMapInfo(false);
+ _header = (DynamicArchiveHeader*)mapinfo->_header;
+
+ Thread* THREAD = Thread::current();
+ FileMapInfo* base_info = FileMapInfo::current_info();
+ int* crc = _header->_base_archive_crc;
+ *crc++ = base_info->crc(); // base archive header crc
+ for (int i = 0; i < MetaspaceShared::n_regions; i++) {
+ *crc++ = base_info->space_crc(i);
+ }
+ _header->populate(base_info, os::vm_allocation_granularity());
+}
+
+size_t DynamicArchiveBuilder::estimate_trampoline_size() {
+ size_t total = 0;
+ size_t each_method_bytes =
+ align_up(SharedRuntime::trampoline_size(), BytesPerWord) +
+ align_up(sizeof(AdapterHandlerEntry*), BytesPerWord);
+
+ for (int i = 0; i < _klasses->length(); i++) {
+ InstanceKlass* ik = _klasses->at(i);
+ Array<Method*>* methods = ik->methods();
+ total += each_method_bytes * methods->length();
+ }
+ return total;
+}
+
+void DynamicArchiveBuilder::make_trampolines() {
+ for (int i = 0; i < _klasses->length(); i++) {
+ InstanceKlass* ik = _klasses->at(i);
+ Array<Method*>* methods = ik->methods();
+ for (int j = 0; j < methods->length(); j++) {
+ Method* m = methods->at(j);
+ address c2i_entry_trampoline =
+ (address)MetaspaceShared::misc_code_space_alloc(SharedRuntime::trampoline_size());
+ m->set_from_compiled_entry(to_target(c2i_entry_trampoline));
+ AdapterHandlerEntry** adapter_trampoline =
+ (AdapterHandlerEntry**)MetaspaceShared::misc_code_space_alloc(sizeof(AdapterHandlerEntry*));
+ *adapter_trampoline = NULL;
+ m->set_adapter_trampoline(to_target(adapter_trampoline));
+ }
+ }
+}
+
+void DynamicArchiveBuilder::make_klasses_shareable() {
+ int i, count = _klasses->length();
+
+ for (i = 0; i < count; i++) {
+ InstanceKlass* ik = _klasses->at(i);
+ sort_methods(ik);
+ }
+
+ for (i = 0; i < count; i++) {
+ InstanceKlass* ik = _klasses->at(i);
+ ClassLoaderData *cld = ik->class_loader_data();
+ if (cld->is_boot_class_loader_data()) {
+ ik->set_class_loader_type(ClassLoader::BOOT_LOADER);
+ }
+ else if (cld->is_platform_class_loader_data()) {
+ ik->set_class_loader_type(ClassLoader::PLATFORM_LOADER);
+ }
+ else if (cld->is_system_class_loader_data()) {
+ ik->set_class_loader_type(ClassLoader::APP_LOADER);
+ }
+
+ MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(ik);
+ ik->remove_unshareable_info();
+
+ assert(ik->array_klasses() == NULL, "sanity");
+
+ if (log_is_enabled(Debug, cds, dynamic)) {
+ ResourceMark rm;
+ log_debug(cds, dynamic)("klasses[%4i] = " PTR_FORMAT " %s", i, p2i(to_target(ik)), ik->external_name());
+ }
+ }
+}
+
+// The address order of the copied Symbols may be different than when the original
+// klasses were created. Re-sort all the tables. See Method::sort_methods().
+void DynamicArchiveBuilder::sort_methods(InstanceKlass* ik) const {
+ assert(ik != NULL, "DynamicArchiveBuilder currently doesn't support dumping the base archive");
+ if (MetaspaceShared::is_in_shared_metaspace(ik)) {
+ // We have reached a supertype that's already in the base archive
+ return;
+ }
+
+ if (ik->java_mirror() == NULL) {
+ // NULL mirror means this class has already been visited and methods are already sorted
+ return;
+ }
+ ik->remove_java_mirror();
+
+ if (log_is_enabled(Debug, cds, dynamic)) {
+ ResourceMark rm;
+ log_debug(cds, dynamic)("sorting methods for " PTR_FORMAT " %s", p2i(to_target(ik)), ik->external_name());
+ }
+
+ // Make sure all supertypes have been sorted
+ sort_methods(ik->java_super());
+ Array<InstanceKlass*>* interfaces = ik->local_interfaces();
+ int len = interfaces->length();
+ for (int i = 0; i < len; i++) {
+ sort_methods(interfaces->at(i));
+ }
+
+#ifdef ASSERT
+ {
+ for (int m = 0; m < ik->methods()->length(); m++) {
+ Symbol* name = ik->methods()->at(m)->name();
+ assert(MetaspaceShared::is_in_shared_metaspace(name) || is_in_buffer_space(name), "must be");
+ }
+ }
+#endif
+
+ Thread* THREAD = Thread::current();
+ Method::sort_methods(ik->methods());
+ if (ik->default_methods() != NULL) {
+ Method::sort_methods(ik->default_methods(), /*set_idnums=*/false);
+ }
+ ik->vtable().initialize_vtable(true, THREAD); assert(!HAS_PENDING_EXCEPTION, "cannot fail");
+ ik->itable().initialize_itable(true, THREAD); assert(!HAS_PENDING_EXCEPTION, "cannot fail");
+}
+
+void DynamicArchiveBuilder::set_symbols_permanent() {
+ int count = _symbols->length();
+ for (int i=0; i<count; i++) {
+ Symbol* s = _symbols->at(i);
+ s->set_permanent();
+
+ if (log_is_enabled(Trace, cds, dynamic)) {
+ ResourceMark rm;
+ log_trace(cds, dynamic)("symbols[%4i] = " PTR_FORMAT " %s", i, p2i(to_target(s)), s->as_quoted_ascii());
+ }
+ }
+}
+
+class RelocateBufferToTarget: public BitMapClosure {
+ DynamicArchiveBuilder *_builder;
+ address* _buffer_bottom;
+ intx _buffer_to_target_delta;
+ public:
+ RelocateBufferToTarget(DynamicArchiveBuilder* builder, address* bottom, intx delta) :
+ _builder(builder), _buffer_bottom(bottom), _buffer_to_target_delta(delta) {}
+
+ bool do_bit(size_t offset) {
+ address* p = _buffer_bottom + offset;
+ assert(_builder->is_in_buffer_space(p), "pointer must live in buffer space");
+
+ address old_ptr = *p;
+ if (_builder->is_in_buffer_space(old_ptr)) {
+ address new_ptr = old_ptr + _buffer_to_target_delta;
+ log_trace(cds, dynamic)("Final patch: @%6d [" PTR_FORMAT " -> " PTR_FORMAT "] " PTR_FORMAT " => " PTR_FORMAT,
+ (int)offset, p2i(p), p2i(_builder->to_target(p)),
+ p2i(old_ptr), p2i(new_ptr));
+ *p = new_ptr;
+ }
+
+ return true; // keep iterating
+ }
+};
+
+
+void DynamicArchiveBuilder::relocate_buffer_to_target() {
+ RelocateBufferToTarget patcher(this, (address*)_alloc_bottom, _buffer_to_target_delta);
+ _ptrmap.iterate(&patcher);
+
+ Array<u8>* table = _header->_shared_path_table.table();
+ table = to_target(table);
+ _header->_shared_path_table.set_table(table);
+}
+
+static void write_archive_info(FileMapInfo* dynamic_info, DynamicArchiveHeader *header) {
+ dynamic_info->write_header();
+ dynamic_info->align_file_position();
+ dynamic_info->write_region(MetaspaceShared::rw,
+ MetaspaceShared::read_write_dump_space()->base(),
+ MetaspaceShared::read_write_dump_space()->used(),
+ /*read_only=*/false,/*allow_exec=*/false);
+ dynamic_info->write_region(MetaspaceShared::ro,
+ MetaspaceShared::read_only_dump_space()->base(),
+ MetaspaceShared::read_only_dump_space()->used(),
+ /*read_only=*/true, /*allow_exec=*/false);
+ dynamic_info->write_region(MetaspaceShared::mc,
+ MetaspaceShared::misc_code_dump_space()->base(),
+ MetaspaceShared::misc_code_dump_space()->used(),
+ /*read_only=*/false,/*allow_exec=*/true);
+}
+
+void DynamicArchiveBuilder::write_archive(char* read_only_tables_start) {
+ int num_klasses = _klasses->length();
+ int num_symbols = _symbols->length();
+
+ _header->_read_only_tables_start = to_target(read_only_tables_start);
+
+ FileMapInfo* dynamic_info = FileMapInfo::dynamic_info();
+ assert(dynamic_info != NULL, "Sanity");
+
+ // Populate the file offsets, region crcs, etc. No data is written out.
+ write_archive_info(dynamic_info, _header);
+
+ // the header will no longer change. Compute its crc.
+ dynamic_info->set_header_crc(dynamic_info->compute_header_crc());
+
+ // Now write the archived data including the file offsets.
+ const char* archive_name = Arguments::GetSharedDynamicArchivePath();
+ dynamic_info->open_for_write(archive_name);
+ write_archive_info(dynamic_info, _header);
+ dynamic_info->close();
+
+
+ address base = to_target(_alloc_bottom);
+ address top = address(current_dump_space()->top()) + _buffer_to_target_delta;
+ int file_size = int(top - base);
+
+ log_info(cds, dynamic)("Written dynamic archive " PTR_FORMAT " - " PTR_FORMAT " [%d bytes header, %d bytes total]",
+ p2i(base), p2i(top), (int)_header->_header_size, file_size);
+ log_info(cds, dynamic)("%d klasses; %d symbols", num_klasses, num_symbols);
+}
+
+
+class VM_PopulateDynamicDumpSharedSpace: public VM_Operation {
+ DynamicArchiveBuilder* _builder;
+public:
+ VM_PopulateDynamicDumpSharedSpace(DynamicArchiveBuilder* builder) : _builder(builder) {}
+ VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
+ void doit() {
+ ResourceMark rm;
+ if (SystemDictionaryShared::empty_dumptime_table()) {
+ log_warning(cds, dynamic)("There is no class to be included in the dynamic archive.");
+ return;
+ }
+ if (AllowArchivingWithJavaAgent) {
+ warning("This archive was created with AllowArchivingWithJavaAgent. It should be used "
+ "for testing purposes only and should not be used in a production environment");
+ }
+ FileMapInfo::check_nonempty_dir_in_shared_path_table();
+
+ _builder->doit();
+ }
+};
+
+
+void DynamicArchive::dump() {
+ if (Arguments::GetSharedDynamicArchivePath() == NULL) {
+ log_warning(cds, dynamic)("SharedDynamicArchivePath is not specified");
+ return;
+ }
+
+ DynamicArchiveBuilder builder;
+ _builder = &builder;
+ VM_PopulateDynamicDumpSharedSpace op(&builder);
+ VMThread::execute(&op);
+ _builder = NULL;
+}
+
+address DynamicArchive::original_to_buffer_impl(address orig_obj) {
+ assert(DynamicDumpSharedSpaces, "must be");
+ address buff_obj = _builder->get_new_loc(orig_obj);
+ assert(buff_obj != NULL, "orig_obj must be used by the dynamic archive");
+ assert(buff_obj != orig_obj, "call this only when you know orig_obj must be copied and not just referenced");
+ assert(_builder->is_in_buffer_space(buff_obj), "must be");
+ return buff_obj;
+}
+
+address DynamicArchive::buffer_to_target_impl(address buff_obj) {
+ assert(DynamicDumpSharedSpaces, "must be");
+ assert(_builder->is_in_buffer_space(buff_obj), "must be");
+ return _builder->to_target(buff_obj);
+}
+
+address DynamicArchive::original_to_target_impl(address orig_obj) {
+ assert(DynamicDumpSharedSpaces, "must be");
+ if (MetaspaceShared::is_in_shared_metaspace(orig_obj)) {
+ // This happens when the top archive points to a Symbol* in the base archive.
+ return orig_obj;
+ }
+ address buff_obj = _builder->get_new_loc(orig_obj);
+ assert(buff_obj != NULL, "orig_obj must be used by the dynamic archive");
+ if (buff_obj == orig_obj) {
+ // We are storing a pointer to an original object into the dynamic buffer. E.g.,
+ // a Symbol* that used by both the base and top archives.
+ assert(MetaspaceShared::is_in_shared_metaspace(orig_obj), "must be");
+ return orig_obj;
+ } else {
+ return _builder->to_target(buff_obj);
+ }
+}
+
+uintx DynamicArchive::object_delta_uintx(void* buff_obj) {
+ assert(DynamicDumpSharedSpaces, "must be");
+ address target_obj = _builder->to_target_no_check(address(buff_obj));
+ assert(uintx(target_obj) >= SharedBaseAddress, "must be");
+ return uintx(target_obj) - SharedBaseAddress;
+}
+
+bool DynamicArchive::is_in_target_space(void *obj) {
+ assert(DynamicDumpSharedSpaces, "must be");
+ return _builder->is_in_target_space(obj);
+}
+
+
+static DynamicArchiveHeader *_dynamic_header = NULL;
+DynamicArchiveBuilder* DynamicArchive::_builder = NULL;
+
+void DynamicArchive::map_failed(FileMapInfo* mapinfo) {
+ if (mapinfo->_header != NULL) {
+ os::free(mapinfo->_header);
+ }
+ delete mapinfo;
+}
+
+// Returns the top of the mapped address space
+address DynamicArchive::map() {
+ assert(UseSharedSpaces, "Sanity");
+
+ // Create the dynamic archive map info
+ FileMapInfo* mapinfo;
+ const char* filename = Arguments::GetSharedDynamicArchivePath();
+ struct stat st;
+ address result;
+ if ((filename != NULL) && (os::stat(filename, &st) == 0)) {
+ mapinfo = new FileMapInfo(false);
+ if (!mapinfo->open_for_read(filename)) {
+ result = NULL;
+ }
+ result = map_impl(mapinfo);
+ if (result == NULL) {
+ map_failed(mapinfo);
+ mapinfo->restore_shared_path_table();
+ }
+ } else {
+ if (filename != NULL) {
+ log_warning(cds, dynamic)("specified dynamic archive doesn't exist: %s", filename);
+ }
+ result = NULL;
+ }
+ return result;
+}
+
+address DynamicArchive::map_impl(FileMapInfo* mapinfo) {
+
+
+ // Read header
+ if (!mapinfo->initialize(false)) {
+ return NULL;
+ }
+
+ _dynamic_header = (DynamicArchiveHeader*)mapinfo->header();
+
+ int regions[] = {MetaspaceShared::rw,
+ MetaspaceShared::ro,
+ MetaspaceShared::mc};
+
+ size_t len = sizeof(regions)/sizeof(int);
+ char* saved_base[] = {NULL, NULL, NULL};
+ char* top = mapinfo->map_regions(regions, saved_base, len);
+ if (top == NULL) {
+ mapinfo->unmap_regions(regions, saved_base, len);
+ FileMapInfo::fail_continue("Unable to use dynamic archive. Failed map_region for using -Xshare:on.");
+ return NULL;
+ }
+
+ if (!validate(mapinfo)) {
+ return NULL;
+ }
+
+ if (_dynamic_header == NULL) {
+ return NULL;
+ }
+
+ intptr_t* buffer = (intptr_t*)_dynamic_header->_read_only_tables_start;
+ ReadClosure rc(&buffer);
+ SymbolTable::serialize_shared_table_header(&rc, false);
+ SystemDictionaryShared::serialize_dictionary_headers(&rc, false);
+
+ return (address)top;
+}
+
+bool DynamicArchive::validate(FileMapInfo* dynamic_info) {
+ // Check if the recorded base archive matches with the current one
+ FileMapInfo* base_info = FileMapInfo::current_info();
+ DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)dynamic_info->header();
+ int* crc = dynamic_header->_base_archive_crc;
+
+ // Check the header crc
+ if (*crc++ != base_info->crc()) {
+ FileMapInfo::fail_continue("Archive header checksum verification failed.");
+ return false;
+ }
+
+ // Check each space's crc
+ for (int i = 0; i < MetaspaceShared::n_regions; i++) {
+ if (*crc++ != base_info->space_crc(i)) {
+ FileMapInfo::fail_continue("Archive region #%d checksum verification failed.", i);
+ return false;
+ }
+ }
+
+ // Validate the dynamic archived shared path table, and set the global
+ // _shared_path_table to that.
+ if (!dynamic_info->validate_shared_path_table()) {
+ return false;
+ }
+ return true;
+}
+
+bool DynamicArchive::is_mapped() {
+ return (_dynamic_header != NULL);
+}
+
+void DynamicArchive::disable() {
+ _dynamic_header = NULL;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/dynamicArchive.hpp Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+#ifndef SHARE_VM_MEMORY_DYNAMICARCHIVE_HPP
+#define SHARE_VM_MEMORY_DYNAMICARCHIVE_HPP
+
+#if INCLUDE_CDS
+
+#include "classfile/compactHashtable.hpp"
+#include "memory/allocation.hpp"
+#include "memory/filemap.hpp"
+#include "memory/memRegion.hpp"
+#include "memory/virtualspace.hpp"
+#include "oops/oop.hpp"
+#include "utilities/exceptions.hpp"
+#include "utilities/macros.hpp"
+#include "utilities/resourceHash.hpp"
+
+// We want to include all archive header information in the dynamic archive.
+// This helps simplify the process if the base layer archive is missing at
+// dynamic archiving time.
+struct DynamicArchiveHeader : public FileMapHeader {
+ // crc for the base archive header and regions
+ int _base_archive_crc[MetaspaceShared::n_regions+1];
+};
+
+class DynamicArchive : AllStatic {
+ static class DynamicArchiveBuilder* _builder;
+ static address original_to_target_impl(address orig_obj);
+ static address original_to_buffer_impl(address orig_obj);
+ static address buffer_to_target_impl(address buff_obj);
+
+public:
+ static void dump();
+
+ // obj is a copy of a MetaspaceObj, stored in the dumping buffer.
+ //
+ // The return value is the runtime targeted location of this object as
+ // mapped from the dynamic archive.
+ template <typename T> static T buffer_to_target(T buff_obj) {
+ return (T)buffer_to_target_impl(address(buff_obj));
+ }
+
+ // obj is an original MetaspaceObj used by the JVM (e.g., a valid Symbol* in the
+ // SymbolTable).
+ //
+ // The return value is the runtime targeted location of this object as
+ // mapped from the dynamic archive.
+ template <typename T> static T original_to_target(T obj) {
+ return (T)original_to_target_impl(address(obj));
+ }
+
+ // obj is an original MetaspaceObj use by the JVM (e.g., a valid Symbol* in the
+ // SymbolTable).
+ //
+ // The return value is the location of this object in the dump time
+ // buffer space
+ template <typename T> static T original_to_buffer(T obj) {
+ return (T)original_to_buffer_impl(address(obj));
+ }
+
+ // Delta of this object from SharedBaseAddress
+ static uintx object_delta_uintx(void* buff_obj);
+
+ // Does obj point to an address inside the runtime target space of the dynamic
+ // archive?
+ static bool is_in_target_space(void *obj);
+
+ static address map();
+ static bool is_mapped();
+ static bool validate(FileMapInfo* dynamic_info);
+ static void disable();
+private:
+ static address map_impl(FileMapInfo* mapinfo);
+ static void map_failed(FileMapInfo* mapinfo);
+};
+#endif // INCLUDE_CDS
+#endif // SHARE_VM_MEMORY_DYNAMICARCHIVE_HPP
--- a/src/hotspot/share/memory/filemap.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/filemap.cpp Fri May 17 08:29:55 2019 -0700
@@ -34,6 +34,7 @@
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "logging/logMessage.hpp"
+#include "memory/dynamicArchive.hpp"
#include "memory/filemap.hpp"
#include "memory/heapShared.inline.hpp"
#include "memory/iterator.inline.hpp"
@@ -42,6 +43,7 @@
#include "memory/metaspaceShared.hpp"
#include "memory/oopFactory.hpp"
#include "memory/universe.hpp"
+#include "oops/compressedOops.hpp"
#include "oops/compressedOops.inline.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
@@ -101,7 +103,12 @@
void FileMapInfo::fail_continue(const char *msg, ...) {
va_list ap;
va_start(ap, msg);
- MetaspaceShared::set_archive_loading_failed();
+ if (_dynamic_archive_info == NULL) {
+ MetaspaceShared::set_archive_loading_failed();
+ } else {
+ // _dynamic_archive_info has been setup after mapping the base archive
+ DynamicArchive::disable();
+ }
if (PrintSharedArchiveAndExit && _validating_shared_path_table) {
// If we are doing PrintSharedArchiveAndExit and some of the classpath entries
// do not validate, we can still continue "limping" to validate the remaining
@@ -120,9 +127,15 @@
ls.vprint_cr(msg, ap);
}
}
- UseSharedSpaces = false;
- assert(current_info() != NULL, "singleton must be registered");
- current_info()->close();
+ if (_dynamic_archive_info == NULL) {
+ UseSharedSpaces = false;
+ assert(current_info() != NULL, "singleton must be registered");
+ current_info()->close();
+ } else {
+ // We are failing when loading the top archive, but the base archive should
+ // continue to work.
+ log_warning(cds, dynamic)("Unable to use shared archive. The top archive failed to load: %s", _dynamic_archive_info->_full_path);
+ }
}
va_end(ap);
}
@@ -159,20 +172,36 @@
}
}
-FileMapInfo::FileMapInfo() {
- assert(_current_info == NULL, "must be singleton"); // not thread safe
- _current_info = this;
+FileMapInfo::FileMapInfo(bool is_static) {
memset((void*)this, 0, sizeof(FileMapInfo));
+ _is_static = is_static;
+ size_t header_size;
+ if (is_static) {
+ assert(_current_info == NULL, "must be singleton"); // not thread safe
+ _current_info = this;
+ header_size = sizeof(FileMapHeader);
+ } else {
+ assert(_dynamic_archive_info == NULL, "must be singleton"); // not thread safe
+ _dynamic_archive_info = this;
+ header_size = sizeof(DynamicArchiveHeader);
+ }
+ _header = (FileMapHeader*)os::malloc(header_size, mtInternal);
+ memset((void*)_header, 0, header_size);
+ _header->_header_size = header_size;
+ _header->_version = INVALID_CDS_ARCHIVE_VERSION;
+ _header->_has_platform_or_app_classes = true;
_file_offset = 0;
_file_open = false;
- _header = (FileMapHeader*)os::malloc(sizeof(FileMapHeader), mtInternal);
- _header->_version = INVALID_CDS_ARCHIVE_VERSION;
- _header->_has_platform_or_app_classes = true;
}
FileMapInfo::~FileMapInfo() {
- assert(_current_info == this, "must be singleton"); // not thread safe
- _current_info = NULL;
+ if (_is_static) {
+ assert(_current_info == this, "must be singleton"); // not thread safe
+ _current_info = NULL;
+ } else {
+ assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe
+ _dynamic_archive_info = NULL;
+ }
}
void FileMapInfo::populate_header(size_t alignment) {
@@ -180,7 +209,11 @@
}
void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
- _magic = CDS_ARCHIVE_MAGIC;
+ if (DynamicDumpSharedSpaces) {
+ _magic = CDS_DYNAMIC_ARCHIVE_MAGIC;
+ } else {
+ _magic = CDS_ARCHIVE_MAGIC;
+ }
_version = CURRENT_CDS_ARCHIVE_VERSION;
_alignment = alignment;
_obj_alignment = ObjectAlignmentInBytes;
@@ -191,9 +224,7 @@
_max_heap_size = MaxHeapSize;
_narrow_klass_base = CompressedKlassPointers::base();
_narrow_klass_shift = CompressedKlassPointers::shift();
- _shared_path_table_size = mapinfo->_shared_path_table_size;
_shared_path_table = mapinfo->_shared_path_table;
- _shared_path_entry_size = mapinfo->_shared_path_entry_size;
if (HeapShared::is_heap_object_archiving_allowed()) {
_heap_reserved = Universe::heap()->reserved_region();
}
@@ -208,6 +239,7 @@
ClassLoaderExt::finalize_shared_paths_misc_info();
_app_class_paths_start_index = ClassLoaderExt::app_class_paths_start_index();
_app_module_paths_start_index = ClassLoaderExt::app_module_paths_start_index();
+ _num_module_paths = ClassLoader::num_module_path_entries();
_max_used_path_index = ClassLoaderExt::max_used_path_index();
_verify_local = BytecodeVerificationLocal;
@@ -215,10 +247,13 @@
_has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes();
_shared_base_address = SharedBaseAddress;
_allow_archiving_with_java_agent = AllowArchivingWithJavaAgent;
+ // the following 2 fields will be set in write_header for dynamic archive header
+ _base_archive_name_size = 0;
+ _base_archive_is_default = false;
}
void SharedClassPathEntry::init(const char* name, bool is_modules_image, TRAPS) {
- assert(DumpSharedSpaces, "dump time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only");
_timestamp = 0;
_filesize = 0;
@@ -297,6 +332,7 @@
// If PrintSharedArchiveAndExit is enabled, don't report failure to the
// caller. Please see above comments for more details.
ok = true;
+ MetaspaceShared::set_archive_loading_failed();
}
return ok;
}
@@ -306,8 +342,27 @@
it->push(&_manifest);
}
+void SharedPathTable::metaspace_pointers_do(MetaspaceClosure* it) {
+ it->push(&_table);
+ for (int i=0; i<_size; i++) {
+ path_at(i)->metaspace_pointers_do(it);
+ }
+}
+
+void SharedPathTable::dumptime_init(ClassLoaderData* loader_data, Thread* THREAD) {
+ size_t entry_size = sizeof(SharedClassPathEntry);
+ int num_boot_classpath_entries = ClassLoader::num_boot_classpath_entries();
+ int num_app_classpath_entries = ClassLoader::num_app_classpath_entries();
+ int num_module_path_entries = ClassLoader::num_module_path_entries();
+ int num_entries = num_boot_classpath_entries + num_app_classpath_entries + num_module_path_entries;
+ size_t bytes = entry_size * num_entries;
+
+ _table = MetadataFactory::new_array<u8>(loader_data, (int)(bytes + 7 / 8), THREAD);
+ _size = num_entries;
+}
+
void FileMapInfo::allocate_shared_path_table() {
- assert(DumpSharedSpaces, "Sanity");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity");
Thread* THREAD = Thread::current();
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
@@ -316,16 +371,7 @@
assert(jrt != NULL,
"No modular java runtime image present when allocating the CDS classpath entry table");
- size_t entry_size = sizeof(SharedClassPathEntry); // assert ( should be 8 byte aligned??)
- int num_boot_classpath_entries = ClassLoader::num_boot_classpath_entries();
- int num_app_classpath_entries = ClassLoader::num_app_classpath_entries();
- int num_module_path_entries = ClassLoader::num_module_path_entries();
- int num_entries = num_boot_classpath_entries + num_app_classpath_entries + num_module_path_entries;
- size_t bytes = entry_size * num_entries;
-
- _shared_path_table = MetadataFactory::new_array<u8>(loader_data, (int)(bytes + 7 / 8), THREAD);
- _shared_path_table_size = num_entries;
- _shared_path_entry_size = entry_size;
+ _shared_path_table.dumptime_init(loader_data, THREAD);
// 1. boot class path
int i = 0;
@@ -343,7 +389,7 @@
cpe = ClassLoader::get_next_boot_classpath_entry(cpe);
i++;
}
- assert(i == num_boot_classpath_entries,
+ assert(i == ClassLoader::num_boot_classpath_entries(),
"number of boot class path entry mismatch");
// 2. app class path
@@ -369,15 +415,15 @@
mpe = mpe->next();
i++;
}
- assert(i == num_entries, "number of shared path entry mismatch");
+ assert(i == _shared_path_table.size(), "number of shared path entry mismatch");
}
void FileMapInfo::check_nonempty_dir_in_shared_path_table() {
- assert(DumpSharedSpaces, "dump time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only");
bool has_nonempty_dir = false;
- int last = _shared_path_table_size - 1;
+ int last = _shared_path_table.size() - 1;
if (last > ClassLoaderExt::max_used_path_index()) {
// no need to check any path beyond max_used_path_index
last = ClassLoaderExt::max_used_path_index();
@@ -479,9 +525,29 @@
assert(UseSharedSpaces, "runtime only");
_validating_shared_path_table = true;
+
+ // Load the shared path table info from the archive header
_shared_path_table = _header->_shared_path_table;
- _shared_path_entry_size = _header->_shared_path_entry_size;
- _shared_path_table_size = _header->_shared_path_table_size;
+ if (DynamicDumpSharedSpaces) {
+ // Only support dynamic dumping with the usage of the default CDS archive
+ // or a simple base archive.
+ // If the base layer archive contains additional path component besides
+ // the runtime image and the -cp, dynamic dumping is disabled.
+ //
+ // When dynamic archiving is enabled, the _shared_path_table is overwritten
+ // to include the application path and stored in the top layer archive.
+ assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image");
+ if (_header->_app_class_paths_start_index > 1) {
+ DynamicDumpSharedSpaces = false;
+ warning(
+ "Dynamic archiving is disabled because base layer archive has appended boot classpath");
+ }
+ if (_header->_num_module_paths > 0) {
+ DynamicDumpSharedSpaces = false;
+ warning(
+ "Dynamic archiving is disabled because base layer archive has module path");
+ }
+ }
int module_paths_start_index = _header->_app_module_paths_start_index;
@@ -491,14 +557,18 @@
if (shared_path(i)->validate()) {
log_info(class, path)("ok");
} else {
- assert(!UseSharedSpaces, "UseSharedSpaces should be disabled");
+ if (_dynamic_archive_info != NULL && _dynamic_archive_info->_is_static) {
+ assert(!UseSharedSpaces, "UseSharedSpaces should be disabled");
+ }
return false;
}
} else if (i >= module_paths_start_index) {
if (shared_path(i)->validate(false /* not a class path entry */)) {
log_info(class, path)("ok");
} else {
- assert(!UseSharedSpaces, "UseSharedSpaces should be disabled");
+ if (_dynamic_archive_info != NULL && _dynamic_archive_info->_is_static) {
+ assert(!UseSharedSpaces, "UseSharedSpaces should be disabled");
+ }
return false;
}
}
@@ -510,18 +580,151 @@
if (_classpath_entries_for_jvmti != NULL) {
os::free(_classpath_entries_for_jvmti);
}
- size_t sz = sizeof(ClassPathEntry*) * _shared_path_table_size;
+ size_t sz = sizeof(ClassPathEntry*) * get_number_of_shared_paths();
_classpath_entries_for_jvmti = (ClassPathEntry**)os::malloc(sz, mtClass);
- memset(_classpath_entries_for_jvmti, 0, sz);
+ memset((void*)_classpath_entries_for_jvmti, 0, sz);
#endif
return true;
}
+bool FileMapInfo::same_files(const char* file1, const char* file2) {
+ if (strcmp(file1, file2) == 0) {
+ return true;
+ }
+
+ bool is_same = false;
+ // if the two paths diff only in case
+ struct stat st1;
+ struct stat st2;
+ int ret1;
+ int ret2;
+ ret1 = os::stat(file1, &st1);
+ ret2 = os::stat(file2, &st2);
+ if (ret1 < 0 || ret2 < 0) {
+ // one of the files is invalid. So they are not the same.
+ is_same = false;
+ } else if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
+ // different files
+ is_same = false;
+#ifndef _WINDOWS
+ } else if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) {
+ // same files
+ is_same = true;
+#else
+ } else if ((st1.st_size == st2.st_size) && (st1.st_ctime == st2.st_ctime) &&
+ (st1.st_mtime == st2.st_mtime)) {
+ // same files
+ is_same = true;
+#endif
+ }
+ return is_same;
+}
+
+bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
+ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0);
+ if (fd < 0) {
+ // do not vm_exit_during_initialization here because Arguments::init_shared_archive_paths()
+ // requires a shared archive name. The open_for_read() function will log a message regarding
+ // failure in opening a shared archive.
+ return false;
+ }
+
+ size_t sz = is_static ? sizeof(FileMapHeader) : sizeof(DynamicArchiveHeader);
+ void* header = os::malloc(sz, mtInternal);
+ memset(header, 0, sz);
+ size_t n = os::read(fd, header, (unsigned int)sz);
+ if (n != sz) {
+ os::free(header);
+ os::close(fd);
+ vm_exit_during_initialization("Unable to read header from shared archive", archive_name);
+ return false;
+ }
+ if (is_static) {
+ FileMapHeader* static_header = (FileMapHeader*)header;
+ if (static_header->_magic != CDS_ARCHIVE_MAGIC) {
+ os::free(header);
+ os::close(fd);
+ vm_exit_during_initialization("Not a base shared archive", archive_name);
+ return false;
+ }
+ } else {
+ DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)header;
+ if (dynamic_header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
+ os::free(header);
+ os::close(fd);
+ vm_exit_during_initialization("Not a top shared archive", archive_name);
+ return false;
+ }
+ }
+ os::free(header);
+ os::close(fd);
+ return true;
+}
+
+bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
+ int* size, char** base_archive_name) {
+ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0);
+ if (fd < 0) {
+ *size = 0;
+ return false;
+ }
+
+ // read the header as a dynamic archive header
+ size_t sz = sizeof(DynamicArchiveHeader);
+ DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)os::malloc(sz, mtInternal);
+ size_t n = os::read(fd, dynamic_header, (unsigned int)sz);
+ if (n != sz) {
+ fail_continue("Unable to read the file header.");
+ os::free(dynamic_header);
+ os::close(fd);
+ return false;
+ }
+ if (dynamic_header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
+ // Not a dynamic header, no need to proceed further.
+ *size = 0;
+ os::free(dynamic_header);
+ os::close(fd);
+ return false;
+ }
+ if (dynamic_header->_base_archive_is_default) {
+ *base_archive_name = Arguments::get_default_shared_archive_path();
+ } else {
+ // skip over the _paths_misc_info
+ sz = dynamic_header->_paths_misc_info_size;
+ lseek(fd, (long)sz, SEEK_CUR);
+ // read the base archive name
+ size_t name_size = dynamic_header->_base_archive_name_size;
+ if (name_size == 0) {
+ os::free(dynamic_header);
+ os::close(fd);
+ return false;
+ }
+ *base_archive_name = NEW_C_HEAP_ARRAY(char, name_size, mtInternal);
+ n = os::read(fd, *base_archive_name, (unsigned int)name_size);
+ if (n != name_size) {
+ fail_continue("Unable to read the base archive name from the header.");
+ FREE_C_HEAP_ARRAY(char, *base_archive_name);
+ *base_archive_name = NULL;
+ os::free(dynamic_header);
+ os::close(fd);
+ return false;
+ }
+ }
+
+ os::free(dynamic_header);
+ os::close(fd);
+ return true;
+}
+
+void FileMapInfo::restore_shared_path_table() {
+ _shared_path_table = _current_info->_header->_shared_path_table;
+}
+
// Read the FileMapInfo information from the file.
-bool FileMapInfo::init_from_file(int fd) {
- size_t sz = sizeof(FileMapHeader);
+bool FileMapInfo::init_from_file(int fd, bool is_static) {
+ size_t sz = is_static ? sizeof(FileMapHeader) : sizeof(DynamicArchiveHeader);
size_t n = os::read(fd, _header, (unsigned int)sz);
if (n != sz) {
fail_continue("Unable to read the file header.");
@@ -531,14 +734,10 @@
fail_continue("The shared archive file has the wrong version.");
return false;
}
- _file_offset = (long)n;
+ _file_offset = n;
size_t info_size = _header->_paths_misc_info_size;
- _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass);
- if (_paths_misc_info == NULL) {
- fail_continue("Unable to read the file header.");
- return false;
- }
+ _paths_misc_info = NEW_C_HEAP_ARRAY(char, info_size, mtClass);
n = os::read(fd, _paths_misc_info, (unsigned int)info_size);
if (n != info_size) {
fail_continue("Unable to read the shared path info header.");
@@ -546,29 +745,45 @@
_paths_misc_info = NULL;
return false;
}
+ _file_offset += n + _header->_base_archive_name_size; // accounts for the size of _base_archive_name
- size_t len = lseek(fd, 0, SEEK_END);
- CDSFileMapRegion* si = space_at(MetaspaceShared::last_valid_region);
- // The last space might be empty
- if (si->_file_offset > len || len - si->_file_offset < si->_used) {
- fail_continue("The shared archive file has been truncated.");
- return false;
+ if (is_static) {
+ if (_header->_magic != CDS_ARCHIVE_MAGIC) {
+ fail_continue("Incorrect static archive magic number");
+ return false;
+ }
+ // just checking the last region is sufficient since the archive is written
+ // in sequential order
+ size_t len = lseek(fd, 0, SEEK_END);
+ CDSFileMapRegion* si = space_at(MetaspaceShared::last_valid_region);
+ // The last space might be empty
+ if (si->_file_offset > len || len - si->_file_offset < si->_used) {
+ fail_continue("The shared archive file has been truncated.");
+ return false;
+ }
+
+ SharedBaseAddress = _header->_shared_base_address;
}
- _file_offset += (long)n;
- SharedBaseAddress = _header->_shared_base_address;
return true;
}
// Read the FileMapInfo information from the file.
-bool FileMapInfo::open_for_read() {
- _full_path = Arguments::GetSharedArchivePath();
+bool FileMapInfo::open_for_read(const char* path) {
+ if (_file_open) {
+ return true;
+ }
+ if (path == NULL) {
+ _full_path = Arguments::GetSharedArchivePath();
+ } else {
+ _full_path = path;
+ }
int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0);
if (fd < 0) {
if (errno == ENOENT) {
// Not locating the shared archive is ok.
- fail_continue("Specified shared archive not found.");
+ fail_continue("Specified shared archive not found (%s).", _full_path);
} else {
fail_continue("Failed to open shared archive file (%s).",
os::strerror(errno));
@@ -581,11 +796,14 @@
return true;
}
-
// Write the FileMapInfo information to the file.
-void FileMapInfo::open_for_write() {
- _full_path = Arguments::GetSharedArchivePath();
+void FileMapInfo::open_for_write(const char* path) {
+ if (path == NULL) {
+ _full_path = Arguments::GetSharedArchivePath();
+ } else {
+ _full_path = path;
+ }
LogMessage(cds) msg;
if (msg.is_info()) {
msg.info("Dumping shared data to file: ");
@@ -593,7 +811,7 @@
}
#ifdef _WINDOWS // On Windows, need WRITE permission to remove the file.
- chmod(_full_path, _S_IREAD | _S_IWRITE);
+ chmod(_full_path, _S_IREAD | _S_IWRITE);
#endif
// Use remove() to delete the existing file because, on Unix, this will
@@ -617,40 +835,59 @@
_header->_paths_misc_info_size = info_size;
- align_file_position();
- write_bytes(_header, sizeof(FileMapHeader));
+ char* base_archive_name = NULL;
+ if (_header->_magic == CDS_DYNAMIC_ARCHIVE_MAGIC) {
+ base_archive_name = (char*)Arguments::GetSharedArchivePath();
+ _header->_base_archive_name_size = (int)strlen(base_archive_name) + 1;
+ _header->_base_archive_is_default = FLAG_IS_DEFAULT(SharedArchiveFile);
+ }
+
+ assert(is_file_position_aligned(), "must be");
+ write_bytes(_header, _header->_header_size);
write_bytes(ClassLoader::get_shared_paths_misc_info(), (size_t)info_size);
+ if (base_archive_name != NULL) {
+ write_bytes(base_archive_name, (size_t)_header->_base_archive_name_size);
+ }
align_file_position();
}
-
// Dump region to file.
-
+// This is called twice for each region during archiving, once before
+// the archive file is open (_file_open is false) and once after.
void FileMapInfo::write_region(int region, char* base, size_t size,
bool read_only, bool allow_exec) {
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Dump time only");
+
CDSFileMapRegion* si = space_at(region);
+ char* target_base = base;
+ if (DynamicDumpSharedSpaces) {
+ target_base = DynamicArchive::buffer_to_target(base);
+ }
if (_file_open) {
guarantee(si->_file_offset == _file_offset, "file offset mismatch.");
log_info(cds)("Shared file region %d: " SIZE_FORMAT_HEX_W(08)
" bytes, addr " INTPTR_FORMAT " file offset " SIZE_FORMAT_HEX_W(08),
- region, size, p2i(base), _file_offset);
+ region, size, p2i(target_base), _file_offset);
} else {
si->_file_offset = _file_offset;
}
+
if (HeapShared::is_heap_region(region)) {
- assert((base - (char*)CompressedOops::base()) % HeapWordSize == 0, "Sanity");
- if (base != NULL) {
- si->_addr._offset = (intx)CompressedOops::encode_not_null((oop)base);
+ assert((target_base - (char*)CompressedKlassPointers::base()) % HeapWordSize == 0, "Sanity");
+ if (target_base != NULL) {
+ si->_addr._offset = (intx)CompressedOops::encode_not_null((oop)target_base);
} else {
si->_addr._offset = 0;
}
} else {
- si->_addr._base = base;
+ si->_addr._base = target_base;
}
si->_used = size;
si->_read_only = read_only;
si->_allow_exec = allow_exec;
+
+ // Use the current 'base' when computing the CRC value and writing out data
si->_crc = ClassLoader::crc32(0, base, (jint)size);
if (base != NULL) {
write_bytes_aligned(base, size);
@@ -734,8 +971,7 @@
if (_file_open) {
size_t n = os::write(_fd, buffer, (unsigned int)nbytes);
if (n != nbytes) {
- // It is dangerous to leave the corrupted shared archive file around,
- // close and remove the file. See bug 6372906.
+ // If the shared archive is corrupted, close it and remove it.
close();
remove(_full_path);
fail_stop("Unable to write to shared archive file.");
@@ -744,6 +980,10 @@
_file_offset += nbytes;
}
+bool FileMapInfo::is_file_position_aligned() const {
+ return _file_offset == align_up(_file_offset,
+ os::vm_allocation_granularity());
+}
// Align file position to an allocation unit boundary.
@@ -843,6 +1083,30 @@
static const char* shared_region_name[] = { "MiscData", "ReadWrite", "ReadOnly", "MiscCode", "OptionalData",
"String1", "String2", "OpenArchive1", "OpenArchive2" };
+char* FileMapInfo::map_regions(int regions[], char* saved_base[], size_t len) {
+ char* prev_top = NULL;
+ char* curr_base;
+ char* curr_top;
+ int i = 0;
+ for (i = 0; i < (int)len; i++) {
+ curr_base = map_region(regions[i], &curr_top);
+ if (curr_base == NULL) {
+ return NULL;
+ }
+ if (i > 0) {
+ // We require that mc->rw->ro->md->od to be laid out consecutively, with no
+ // gaps between them. That way, we can ensure that the OS won't be able to
+ // allocate any new memory spaces inside _shared_metaspace_{base,top}, which
+ // would mess up the simple comparision in MetaspaceShared::is_in_shared_metaspace().
+ assert(curr_base == prev_top, "must be");
+ }
+ log_info(cds)("Mapped region #%d at base %p top %p", regions[i], curr_base, curr_top);
+ saved_base[i] = curr_base;
+ prev_top = curr_top;
+ }
+ return curr_top;
+}
+
char* FileMapInfo::map_region(int i, char** top_ret) {
assert(!HeapShared::is_heap_region(i), "sanity");
CDSFileMapRegion* si = space_at(i);
@@ -869,6 +1133,7 @@
si->_allow_exec);
if (base == NULL || base != requested_addr) {
fail_continue("Unable to map %s shared space at required address.", shared_region_name[i]);
+ _memory_mapping_failed = true;
return NULL;
}
#ifdef _WINDOWS
@@ -885,6 +1150,18 @@
return base;
}
+size_t FileMapInfo::read_bytes(void* buffer, size_t count) {
+ assert(_file_open, "Archive file is not open");
+ size_t n = os::read(_fd, buffer, (unsigned int)count);
+ if (n != count) {
+ // Close the file if there's a problem reading it.
+ close();
+ return 0;
+ }
+ _file_offset += count;
+ return count;
+}
+
address FileMapInfo::decode_start_address(CDSFileMapRegion* spc, bool with_current_oop_encoding_mode) {
if (with_current_oop_encoding_mode) {
return (address)CompressedOops::decode_not_null(offset_of_space(spc));
@@ -1126,13 +1403,13 @@
p2i(addr), regions[i].byte_size());
return false;
}
- }
- if (!verify_mapped_heap_regions(first, region_num)) {
- // dealloc the regions from java heap
- dealloc_archive_heap_regions(regions, region_num, is_open_archive);
- log_info(cds)("UseSharedSpaces: mapped heap regions are corrupt");
- return false;
+ if (VerifySharedSpaces && !region_crc_check(addr, regions[i].byte_size(), si->_crc)) {
+ // dealloc the regions from java heap
+ dealloc_archive_heap_regions(regions, region_num, is_open_archive);
+ log_info(cds)("UseSharedSpaces: mapped heap regions are corrupt");
+ return false;
+ }
}
// the shared heap data is mapped successfully
@@ -1141,18 +1418,6 @@
return true;
}
-bool FileMapInfo::verify_mapped_heap_regions(int first, int num) {
- assert(num > 0, "sanity");
- if (VerifySharedSpaces) {
- for (int i = first; i < first + num; i++) {
- if (!verify_region_checksum(i)) {
- return false;
- }
- }
- }
- return true;
-}
-
void FileMapInfo::patch_archived_heap_embedded_pointers() {
if (!_heap_pointers_need_patching) {
return;
@@ -1205,6 +1470,15 @@
}
#endif // INCLUDE_CDS_JAVA_HEAP
+bool FileMapInfo::region_crc_check(char* buf, size_t size, int expected_crc) {
+ int crc = ClassLoader::crc32(0, buf, (jint)size);
+ if (crc != expected_crc) {
+ fail_continue("Checksum verification failed.");
+ return false;
+ }
+ return true;
+}
+
bool FileMapInfo::verify_region_checksum(int i) {
assert(VerifySharedSpaces, "sanity");
@@ -1213,19 +1487,16 @@
if (sz == 0) {
return true; // no data
}
- if ((HeapShared::is_closed_archive_heap_region(i) &&
- !HeapShared::closed_archive_heap_region_mapped()) ||
- (HeapShared::is_open_archive_heap_region(i) &&
- !HeapShared::open_archive_heap_region_mapped())) {
- return true; // archived heap data is not mapped
+
+ return region_crc_check(region_addr(i), sz, space_at(i)->_crc);
+}
+
+void FileMapInfo::unmap_regions(int regions[], char* saved_base[], size_t len) {
+ for (int i = 0; i < (int)len; i++) {
+ if (saved_base[i] != NULL) {
+ unmap_region(regions[i]);
+ }
}
- const char* buf = region_addr(i);
- int crc = ClassLoader::crc32(0, buf, (jint)sz);
- if (crc != space_at(i)->_crc) {
- fail_continue("Checksum verification failed.");
- return false;
- }
- return true;
}
// Unmap a memory region in the address space.
@@ -1253,19 +1524,15 @@
}
void FileMapInfo::metaspace_pointers_do(MetaspaceClosure* it) {
- it->push(&_shared_path_table);
- for (int i=0; i<_shared_path_table_size; i++) {
- shared_path(i)->metaspace_pointers_do(it);
- }
+ _shared_path_table.metaspace_pointers_do(it);
}
-
FileMapInfo* FileMapInfo::_current_info = NULL;
+FileMapInfo* FileMapInfo::_dynamic_archive_info = NULL;
bool FileMapInfo::_heap_pointers_need_patching = false;
-Array<u8>* FileMapInfo::_shared_path_table = NULL;
-int FileMapInfo::_shared_path_table_size = 0;
-size_t FileMapInfo::_shared_path_entry_size = 0x1234baad;
+SharedPathTable FileMapInfo::_shared_path_table;
bool FileMapInfo::_validating_shared_path_table = false;
+bool FileMapInfo::_memory_mapping_failed = false;
// Open the shared archive file, read and validate the header
// information (version, boot classpath, etc.). If initialization
@@ -1277,7 +1544,7 @@
// [1] validate_header() - done here. This checks the header, including _paths_misc_info.
// [2] validate_shared_path_table - this is done later, because the table is in the RW
// region of the archive, which is not mapped yet.
-bool FileMapInfo::initialize() {
+bool FileMapInfo::initialize(bool is_static) {
assert(UseSharedSpaces, "UseSharedSpaces expected.");
if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) {
@@ -1293,8 +1560,8 @@
return false;
}
- init_from_file(_fd);
- if (!validate_header()) {
+ init_from_file(_fd, is_static);
+ if (!validate_header(is_static)) {
return false;
}
return true;
@@ -1315,7 +1582,7 @@
char* start = (char*)this;
// start computing from the field after _crc
char* buf = (char*)&_crc + sizeof(_crc);
- size_t sz = sizeof(FileMapHeader) - (buf - start);
+ size_t sz = _header_size - (buf - start);
int crc = ClassLoader::crc32(0, buf, (jint)sz);
return crc;
}
@@ -1336,7 +1603,7 @@
FileMapInfo::fail_continue("The shared archive file is the wrong version.");
return false;
}
- if (_magic != CDS_ARCHIVE_MAGIC) {
+ if (_magic != CDS_ARCHIVE_MAGIC && _magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
FileMapInfo::fail_continue("The shared archive file has a bad magic number.");
return false;
}
@@ -1401,11 +1668,11 @@
return true;
}
-bool FileMapInfo::validate_header() {
+bool FileMapInfo::validate_header(bool is_static) {
bool status = _header->validate();
if (status) {
- if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size)) {
+ if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size, is_static)) {
if (!PrintSharedArchiveAndExit) {
fail_continue("shared class paths mismatch (hint: enable -Xlog:class+path=info to diagnose the failure)");
status = false;
@@ -1435,7 +1702,7 @@
// Unmap mapped regions of shared space.
void FileMapInfo::stop_sharing_and_unmap(const char* msg) {
- MetaspaceObj::set_shared_metaspace_range(NULL, NULL);
+ MetaspaceShared::set_shared_metaspace_range(NULL, NULL);
FileMapInfo *map_info = FileMapInfo::current_info();
if (map_info) {
@@ -1502,7 +1769,7 @@
ClassFileStream* FileMapInfo::open_stream_for_jvmti(InstanceKlass* ik, Handle class_loader, TRAPS) {
int path_index = ik->shared_classpath_index();
assert(path_index >= 0, "should be called for shared built-in classes only");
- assert(path_index < (int)_shared_path_table_size, "sanity");
+ assert(path_index < (int)get_number_of_shared_paths(), "sanity");
ClassPathEntry* cpe = get_classpath_entry_for_jvmti(path_index, CHECK_NULL);
assert(cpe != NULL, "must be");
--- a/src/hotspot/share/memory/filemap.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/filemap.hpp Fri May 17 08:29:55 2019 -0700
@@ -93,7 +93,32 @@
size_t _oopmap_size_in_bits;
};
+class SharedPathTable {
+ Array<u8>* _table;
+ int _size;
+public:
+ void dumptime_init(ClassLoaderData* loader_data, Thread* THREAD);
+ void metaspace_pointers_do(MetaspaceClosure* it);
+
+ int size() {
+ return _size;
+ }
+ SharedClassPathEntry* path_at(int index) {
+ if (index < 0) {
+ return NULL;
+ }
+ assert(index < _size, "sanity");
+ char* p = (char*)_table->data();
+ p += sizeof(SharedClassPathEntry) * index;
+ return (SharedClassPathEntry*)p;
+ }
+ Array<u8>* table() {return _table;}
+ void set_table(Array<u8>* table) {_table = table;}
+
+};
+
struct FileMapHeader : public CDSFileMapHeaderBase {
+ size_t _header_size;
size_t _alignment; // how shared archive should be aligned
int _obj_alignment; // value of ObjectAlignmentInBytes
address _narrow_oop_base; // compressed oop encoding base
@@ -110,12 +135,16 @@
size_t _core_spaces_size; // number of bytes allocated by the core spaces
// (mc, md, ro, rw and od).
MemRegion _heap_reserved; // reserved region for the entire heap at dump time.
+ bool _base_archive_is_default; // indicates if the base archive is the system default one
// The following fields are all sanity checks for whether this archive
// will function correctly with this JVM and the bootclasspath it's
// invoked with.
char _jvm_ident[JVM_IDENT_MAX]; // identifier for jvm
+ // size of the base archive name including NULL terminator
+ int _base_archive_name_size;
+
// The _paths_misc_info is a variable-size structure that records "miscellaneous"
// information during dumping. It is generated and validated by the
// SharedPathsMiscInfo class. See SharedPathsMiscInfo.hpp for
@@ -140,12 +169,11 @@
// FIXME -- if JAR files in the tail of the list were specified but not used during dumping,
// they should be removed from this table, to save space and to avoid spurious
// loading failures during runtime.
- int _shared_path_table_size;
- size_t _shared_path_entry_size;
- Array<u8>* _shared_path_table;
+ SharedPathTable _shared_path_table;
jshort _app_class_paths_start_index; // Index of first app classpath entry
jshort _app_module_paths_start_index; // Index of first module path entry
+ jshort _num_module_paths; // number of module path entries
jshort _max_used_path_index; // max path index referenced during CDS dump
bool _verify_local; // BytecodeVerificationLocal setting
bool _verify_remote; // BytecodeVerificationRemote setting
@@ -161,13 +189,14 @@
jshort app_module_paths_start_index() { return _app_module_paths_start_index; }
bool validate();
- void populate(FileMapInfo* info, size_t alignment);
int compute_crc();
CDSFileMapRegion* space_at(int i) {
assert(i >= 0 && i < NUM_CDS_REGIONS, "invalid region");
return &_space[i];
}
+public:
+ void populate(FileMapInfo* info, size_t alignment);
};
class FileMapInfo : public CHeapObj<mtInternal> {
@@ -176,14 +205,14 @@
friend class VMStructs;
friend struct FileMapHeader;
+ bool _is_static;
bool _file_open;
int _fd;
size_t _file_offset;
private:
- static Array<u8>* _shared_path_table;
- static int _shared_path_table_size;
- static size_t _shared_path_entry_size;
+ // TODO: Probably change the following to be non-static
+ static SharedPathTable _shared_path_table;
static bool _validating_shared_path_table;
// FileMapHeader describes the shared space data in the file to be
@@ -202,24 +231,31 @@
const char* _full_path;
char* _paths_misc_info;
+ char* _base_archive_name;
static FileMapInfo* _current_info;
+ static FileMapInfo* _dynamic_archive_info;
static bool _heap_pointers_need_patching;
-
- bool init_from_file(int fd);
- void align_file_position();
- bool validate_header_impl();
+ static bool _memory_mapping_failed;
+ static bool get_base_archive_name_from_header(const char* archive_name,
+ int* size, char** base_archive_name);
+ static bool check_archive(const char* archive_name, bool is_static);
+ static bool same_files(const char* file1, const char* file2);
+ void restore_shared_path_table();
+ bool init_from_file(int fd, bool is_static);
static void metaspace_pointers_do(MetaspaceClosure* it);
public:
- FileMapInfo();
+ FileMapInfo(bool is_static);
~FileMapInfo();
int compute_header_crc() { return _header->compute_crc(); }
void set_header_crc(int crc) { _header->_crc = crc; }
+ int space_crc(int i) { return space_at(i)->_crc; }
void populate_header(size_t alignment);
- bool validate_header();
+ bool validate_header(bool is_static);
void invalidate();
+ int crc() { return _header->_crc; }
int version() { return _header->_version; }
size_t alignment() { return _header->_alignment; }
CompressedOops::Mode narrow_oop_mode() { return _header->_narrow_oop_mode; }
@@ -234,6 +270,9 @@
char* read_only_tables_start() { return _header->_read_only_tables_start; }
void set_read_only_tables_start(char* p) { _header->_read_only_tables_start = p; }
+ bool is_file_position_aligned() const;
+ void align_file_position();
+
address cds_i2i_entry_code_buffers() {
return _header->_cds_i2i_entry_code_buffers;
}
@@ -254,12 +293,21 @@
NOT_CDS(return NULL;)
}
+ static void set_current_info(FileMapInfo* info) {
+ CDS_ONLY(_current_info = info;)
+ }
+
+ static FileMapInfo* dynamic_info() {
+ CDS_ONLY(return _dynamic_archive_info;)
+ NOT_CDS(return NULL;)
+ }
+
static void assert_mark(bool check);
// File manipulation.
- bool initialize() NOT_CDS_RETURN_(false);
- bool open_for_read();
- void open_for_write();
+ bool initialize(bool is_static) NOT_CDS_RETURN_(false);
+ bool open_for_read(const char* path = NULL);
+ void open_for_write(const char* path = NULL);
void write_header();
void write_region(int region, char* base, size_t size,
bool read_only, bool allow_exec);
@@ -269,6 +317,8 @@
bool print_log);
void write_bytes(const void* buffer, size_t count);
void write_bytes_aligned(const void* buffer, size_t count);
+ size_t read_bytes(void* buffer, size_t count);
+ char* map_regions(int regions[], char* saved_base[], size_t len);
char* map_region(int i, char** top_ret);
void map_heap_regions_impl() NOT_CDS_JAVA_HEAP_RETURN;
void map_heap_regions() NOT_CDS_JAVA_HEAP_RETURN;
@@ -278,6 +328,7 @@
int first_region_idx) NOT_CDS_JAVA_HEAP_RETURN;
bool has_heap_regions() NOT_CDS_JAVA_HEAP_RETURN_(false);
MemRegion get_heap_regions_range_with_current_oop_encoding_mode() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion());
+ void unmap_regions(int regions[], char* saved_base[], size_t len);
void unmap_region(int i);
bool verify_region_checksum(int i);
void close();
@@ -291,7 +342,10 @@
// Errors.
static void fail_stop(const char *msg, ...) ATTRIBUTE_PRINTF(1, 2);
static void fail_continue(const char *msg, ...) ATTRIBUTE_PRINTF(1, 2);
-
+ static bool memory_mapping_failed() {
+ CDS_ONLY(return _memory_mapping_failed;)
+ NOT_CDS(return false;)
+ }
bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false);
// Stop CDS sharing and unmap CDS regions.
@@ -307,13 +361,7 @@
#endif
static SharedClassPathEntry* shared_path(int index) {
- if (index < 0) {
- return NULL;
- }
- assert(index < _shared_path_table_size, "sanity");
- char* p = (char*)_shared_path_table->data();
- p += _shared_path_entry_size * index;
- return (SharedClassPathEntry*)p;
+ return _shared_path_table.path_at(index);
}
static const char* shared_path_name(int index) {
@@ -322,7 +370,7 @@
}
static int get_number_of_shared_paths() {
- return _shared_path_table_size;
+ return _shared_path_table.size();
}
char* region_addr(int idx);
@@ -330,7 +378,7 @@
private:
bool map_heap_data(MemRegion **heap_mem, int first, int max, int* num,
bool is_open = false) NOT_CDS_JAVA_HEAP_RETURN_(false);
- bool verify_mapped_heap_regions(int first, int num) NOT_CDS_JAVA_HEAP_RETURN_(false);
+ bool region_crc_check(char* buf, size_t size, int expected_crc) NOT_CDS_RETURN_(false);
void dealloc_archive_heap_regions(MemRegion* regions, int num, bool is_open) NOT_CDS_JAVA_HEAP_RETURN;
CDSFileMapRegion* space_at(int i) {
--- a/src/hotspot/share/memory/heapShared.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/heapShared.cpp Fri May 17 08:29:55 2019 -0700
@@ -418,8 +418,7 @@
_run_time_subgraph_info_table.reset();
- int num_buckets = CompactHashtableWriter::default_num_buckets(d_table->_count);
- CompactHashtableWriter writer(num_buckets, &stats);
+ CompactHashtableWriter writer(d_table->_count, &stats);
CopyKlassSubGraphInfoToArchive copy(&writer);
d_table->iterate(©);
--- a/src/hotspot/share/memory/heapShared.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/heapShared.hpp Fri May 17 08:29:55 2019 -0700
@@ -286,16 +286,6 @@
idx <= MetaspaceShared::last_open_archive_heap_region));
NOT_CDS_JAVA_HEAP_RETURN_(false);
}
- static bool is_closed_archive_heap_region(int idx) {
- CDS_JAVA_HEAP_ONLY(return (idx >= MetaspaceShared::first_closed_archive_heap_region &&
- idx <= MetaspaceShared::last_closed_archive_heap_region));
- NOT_CDS_JAVA_HEAP_RETURN_(false);
- }
- static bool is_open_archive_heap_region(int idx) {
- CDS_JAVA_HEAP_ONLY(return (idx >= MetaspaceShared::first_open_archive_heap_region &&
- idx <= MetaspaceShared::last_open_archive_heap_region));
- NOT_CDS_JAVA_HEAP_RETURN_(false);
- }
static void set_closed_archive_heap_region_mapped() {
CDS_JAVA_HEAP_ONLY(_closed_archive_heap_region_mapped = true);
--- a/src/hotspot/share/memory/memRegion.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/memRegion.cpp Fri May 17 08:29:55 2019 -0700
@@ -118,4 +118,3 @@
void MemRegion::operator delete [](void* p) {
FreeHeap(p);
}
-
--- a/src/hotspot/share/memory/metaspace.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/metaspace.cpp Fri May 17 08:29:55 2019 -0700
@@ -1220,6 +1220,10 @@
MetaspaceShared::initialize_runtime_shared_and_meta_spaces();
}
+ if (DynamicDumpSharedSpaces && !UseSharedSpaces) {
+ vm_exit_during_initialization("DynamicDumpSharedSpaces not supported when base CDS archive is not loaded", NULL);
+ }
+
if (!DumpSharedSpaces && !UseSharedSpaces)
#endif // INCLUDE_CDS
{
--- a/src/hotspot/share/memory/metaspaceClosure.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/metaspaceClosure.cpp Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -34,9 +34,20 @@
*(address*)mpp() = (address)p;
}
-void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref, Writability w) {
- if (ref->not_null()) {
+void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) {
+ if (_nest_level < MAX_NEST_LEVEL) {
+ do_push(ref);
+ delete ref;
+ } else {
+ ref->set_next(_pending_refs);
+ _pending_refs = ref;
+ }
+}
+
+void MetaspaceClosure::do_push(MetaspaceClosure::Ref* ref) {
+ if (ref->not_null()) { // FIXME: make this configurable, so DynamicArchiveBuilder mark all pointers
bool read_only;
+ Writability w = ref->writability();
switch (w) {
case _writable:
read_only = false;
@@ -48,12 +59,29 @@
assert(w == _default, "must be");
read_only = ref->is_read_only_by_default();
}
+ _nest_level ++;
if (do_ref(ref, read_only)) { // true means we want to iterate the embedded pointer in <ref>
ref->metaspace_pointers_do(this);
}
+ _nest_level --;
}
}
+void MetaspaceClosure::finish() {
+ assert(_nest_level == 0, "must be");
+ while (_pending_refs != NULL) {
+ Ref* ref = _pending_refs;
+ _pending_refs = _pending_refs->next();
+ do_push(ref);
+ delete ref;
+ }
+}
+
+MetaspaceClosure::~MetaspaceClosure() {
+ assert(_pending_refs == NULL,
+ "you must explicitly call MetaspaceClosure::finish() to process all refs!");
+}
+
bool UniqueMetaspaceClosure::do_ref(MetaspaceClosure::Ref* ref, bool read_only) {
bool* found = _has_been_visited.lookup(ref->obj());
if (found != NULL) {
@@ -64,7 +92,6 @@
if (_has_been_visited.maybe_grow(MAX_TABLE_SIZE)) {
log_info(cds, hashtables)("Expanded _has_been_visited table to %d", _has_been_visited.table_size());
}
- do_unique_ref(ref, read_only);
- return true; // Saw this for the first time: iterate the embedded pointers.
+ return do_unique_ref(ref, read_only);
}
}
--- a/src/hotspot/share/memory/metaspaceClosure.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/metaspaceClosure.hpp Fri May 17 08:29:55 2019 -0700
@@ -101,9 +101,15 @@
// Symbol* bar() { return (Symbol*) _obj; }
//
// [2] All Array<T> dimensions are statically declared.
- class Ref {
+ class Ref : public CHeapObj<mtInternal> {
+ Writability _writability;
+ Ref* _next;
+ // Noncopyable.
+ Ref(const Ref&);
+ Ref& operator=(const Ref&);
protected:
virtual void** mpp() const = 0;
+ Ref(Writability w) : _writability(w), _next(NULL) {}
public:
virtual bool not_null() const = 0;
virtual int size() const = 0;
@@ -111,6 +117,7 @@
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const = 0;
virtual MetaspaceObj::Type msotype() const = 0;
virtual bool is_read_only_by_default() const = 0;
+ virtual ~Ref() {}
address obj() const {
// In some rare cases (see CPSlot in constantPool.hpp) we store some flags in the lowest
@@ -119,8 +126,16 @@
return (address)(p & (~FLAG_MASK));
}
+ address* addr() const {
+ return (address*)mpp();
+ }
+
void update(address new_loc) const;
+ Writability writability() const { return _writability; };
+ void set_next(Ref* n) { _next = n; }
+ Ref* next() const { return _next; }
+
private:
static const uintx FLAG_MASK = 0x03;
@@ -143,7 +158,7 @@
}
public:
- ObjectRef(T** mpp) : _mpp(mpp) {}
+ ObjectRef(T** mpp, Writability w) : Ref(w), _mpp(mpp) {}
virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); }
virtual bool not_null() const { return dereference() != NULL; }
@@ -170,7 +185,7 @@
}
public:
- PrimitiveArrayRef(Array<T>** mpp) : _mpp(mpp) {}
+ PrimitiveArrayRef(Array<T>** mpp, Writability w) : Ref(w), _mpp(mpp) {}
// all Arrays are read-only by default
virtual bool is_read_only_by_default() const { return true; }
@@ -200,7 +215,7 @@
}
public:
- PointerArrayRef(Array<T*>** mpp) : _mpp(mpp) {}
+ PointerArrayRef(Array<T*>** mpp, Writability w) : Ref(w), _mpp(mpp) {}
// all Arrays are read-only by default
virtual bool is_read_only_by_default() const { return true; }
@@ -224,9 +239,21 @@
}
};
- void push_impl(Ref* ref, Writability w);
+ // If recursion is too deep, save the Refs in _pending_refs, and push them later using
+ // MetaspaceClosure::finish()
+ static const int MAX_NEST_LEVEL = 5;
+ Ref* _pending_refs;
+ int _nest_level;
+
+ void push_impl(Ref* ref);
+ void do_push(Ref* ref);
public:
+ MetaspaceClosure(): _pending_refs(NULL), _nest_level(0) {}
+ ~MetaspaceClosure();
+
+ void finish();
+
// returns true if we want to keep iterating the pointers embedded inside <ref>
virtual bool do_ref(Ref* ref, bool read_only) = 0;
@@ -237,22 +264,19 @@
// C++ will try to match the "most specific" template function. This one will
// will be matched if possible (if mpp is an Array<> of any pointer type).
template <typename T> void push(Array<T*>** mpp, Writability w = _default) {
- PointerArrayRef<T> ref(mpp);
- push_impl(&ref, w);
+ push_impl(new PointerArrayRef<T>(mpp, w));
}
// If the above function doesn't match (mpp is an Array<>, but T is not a pointer type), then
// this is the second choice.
template <typename T> void push(Array<T>** mpp, Writability w = _default) {
- PrimitiveArrayRef<T> ref(mpp);
- push_impl(&ref, w);
+ push_impl(new PrimitiveArrayRef<T>(mpp, w));
}
// If the above function doesn't match (mpp is not an Array<> type), then
// this will be matched by default.
template <class T> void push(T** mpp, Writability w = _default) {
- ObjectRef<T> ref(mpp);
- push_impl(&ref, w);
+ push_impl(new ObjectRef<T>(mpp, w));
}
};
@@ -266,7 +290,7 @@
public:
// Gets called the first time we discover an object.
- virtual void do_unique_ref(Ref* ref, bool read_only) = 0;
+ virtual bool do_unique_ref(Ref* ref, bool read_only) = 0;
UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE) {}
private:
--- a/src/hotspot/share/memory/metaspaceShared.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/metaspaceShared.cpp Fri May 17 08:29:55 2019 -0700
@@ -48,10 +48,12 @@
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
+#include "memory/dynamicArchive.hpp"
#include "oops/compressedOops.inline.hpp"
#include "oops/instanceClassLoaderKlass.hpp"
#include "oops/instanceMirrorKlass.hpp"
#include "oops/instanceRefKlass.hpp"
+#include "oops/methodData.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
@@ -81,6 +83,7 @@
address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
size_t MetaspaceShared::_core_spaces_size = 0;
+void* MetaspaceShared::_shared_metaspace_static_top = NULL;
// The CDS archive is divided into the following regions:
// mc - misc code (the method entry trampolines)
@@ -112,105 +115,97 @@
// The s0/s1 and oa0/oa1 regions are populated inside HeapShared::archive_java_heap_objects.
// Their layout is independent of the other 5 regions.
-class DumpRegion {
-private:
- const char* _name;
- char* _base;
- char* _top;
- char* _end;
- bool _is_packed;
-
- char* expand_top_to(char* newtop) {
- assert(is_allocatable(), "must be initialized and not packed");
- assert(newtop >= _top, "must not grow backwards");
- if (newtop > _end) {
- MetaspaceShared::report_out_of_space(_name, newtop - _top);
- ShouldNotReachHere();
- }
- uintx delta = MetaspaceShared::object_delta_uintx(newtop);
- if (delta > MAX_SHARED_DELTA) {
- // This is just a sanity check and should not appear in any real world usage. This
- // happens only if you allocate more than 2GB of shared objects and would require
- // millions of shared classes.
- vm_exit_during_initialization("Out of memory in the CDS archive",
- "Please reduce the number of shared classes.");
- }
-
- MetaspaceShared::commit_shared_space_to(newtop);
- _top = newtop;
- return _top;
+char* DumpRegion::expand_top_to(char* newtop) {
+ assert(is_allocatable(), "must be initialized and not packed");
+ assert(newtop >= _top, "must not grow backwards");
+ if (newtop > _end) {
+ MetaspaceShared::report_out_of_space(_name, newtop - _top);
+ ShouldNotReachHere();
}
-
-public:
- DumpRegion(const char* name) : _name(name), _base(NULL), _top(NULL), _end(NULL), _is_packed(false) {}
-
- char* allocate(size_t num_bytes, size_t alignment=BytesPerWord) {
- char* p = (char*)align_up(_top, alignment);
- char* newtop = p + align_up(num_bytes, alignment);
- expand_top_to(newtop);
- memset(p, 0, newtop - p);
- return p;
+ uintx delta;
+ if (DynamicDumpSharedSpaces) {
+ delta = DynamicArchive::object_delta_uintx(newtop);
+ } else {
+ delta = MetaspaceShared::object_delta_uintx(newtop);
}
-
- void append_intptr_t(intptr_t n) {
- assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment");
- intptr_t *p = (intptr_t*)_top;
- char* newtop = _top + sizeof(intptr_t);
- expand_top_to(newtop);
- *p = n;
+ if (delta > MAX_SHARED_DELTA) {
+ // This is just a sanity check and should not appear in any real world usage. This
+ // happens only if you allocate more than 2GB of shared objects and would require
+ // millions of shared classes.
+ vm_exit_during_initialization("Out of memory in the CDS archive",
+ "Please reduce the number of shared classes.");
}
- char* base() const { return _base; }
- char* top() const { return _top; }
- char* end() const { return _end; }
- size_t reserved() const { return _end - _base; }
- size_t used() const { return _top - _base; }
- bool is_packed() const { return _is_packed; }
- bool is_allocatable() const {
- return !is_packed() && _base != NULL;
- }
+ MetaspaceShared::commit_shared_space_to(newtop);
+ _top = newtop;
+ return _top;
+}
+
+char* DumpRegion::allocate(size_t num_bytes, size_t alignment) {
+ char* p = (char*)align_up(_top, alignment);
+ char* newtop = p + align_up(num_bytes, alignment);
+ expand_top_to(newtop);
+ memset(p, 0, newtop - p);
+ return p;
+}
- void print(size_t total_bytes) const {
- tty->print_cr("%-3s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT,
- _name, used(), percent_of(used(), total_bytes), reserved(), percent_of(used(), reserved()), p2i(_base));
+void DumpRegion::print(size_t total_bytes) const {
+ tty->print_cr("%-3s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT,
+ _name, used(), percent_of(used(), total_bytes), reserved(), percent_of(used(), reserved()), p2i(_base));
+}
+
+void DumpRegion::print_out_of_space_msg(const char* failing_region, size_t needed_bytes) {
+ tty->print("[%-8s] " PTR_FORMAT " - " PTR_FORMAT " capacity =%9d, allocated =%9d",
+ _name, p2i(_base), p2i(_top), int(_end - _base), int(_top - _base));
+ if (strcmp(_name, failing_region) == 0) {
+ tty->print_cr(" required = %d", int(needed_bytes));
+ } else {
+ tty->cr();
}
- void print_out_of_space_msg(const char* failing_region, size_t needed_bytes) {
- tty->print("[%-8s] " PTR_FORMAT " - " PTR_FORMAT " capacity =%9d, allocated =%9d",
- _name, p2i(_base), p2i(_top), int(_end - _base), int(_top - _base));
- if (strcmp(_name, failing_region) == 0) {
- tty->print_cr(" required = %d", int(needed_bytes));
- } else {
- tty->cr();
- }
- }
+}
- void init(const ReservedSpace* rs) {
- _base = _top = rs->base();
- _end = rs->end();
+void DumpRegion::pack(DumpRegion* next) {
+ assert(!is_packed(), "sanity");
+ _end = (char*)align_up(_top, Metaspace::reserve_alignment());
+ _is_packed = true;
+ if (next != NULL) {
+ next->_base = next->_top = this->_end;
+ next->_end = MetaspaceShared::shared_rs()->end();
}
- void init(char* b, char* t, char* e) {
- _base = b;
- _top = t;
- _end = e;
- }
+}
+
+DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _md_region("md"), _od_region("od");
+size_t _total_closed_archive_region_size = 0, _total_open_archive_region_size = 0;
- void pack(DumpRegion* next = NULL) {
- assert(!is_packed(), "sanity");
- _end = (char*)align_up(_top, Metaspace::reserve_alignment());
- _is_packed = true;
- if (next != NULL) {
- next->_base = next->_top = this->_end;
- next->_end = MetaspaceShared::shared_rs()->end();
- }
+void MetaspaceShared::init_shared_dump_space(DumpRegion* first_space, address first_space_bottom) {
+ // Start with 0 committed bytes. The memory will be committed as needed by
+ // MetaspaceShared::commit_shared_space_to().
+ if (!_shared_vs.initialize(_shared_rs, 0)) {
+ vm_exit_during_initialization("Unable to allocate memory for shared space");
}
- bool contains(char* p) {
- return base() <= p && p < top();
- }
-};
+ first_space->init(&_shared_rs, (char*)first_space_bottom);
+}
+
+DumpRegion* MetaspaceShared::misc_code_dump_space() {
+ return &_mc_region;
+}
+
+DumpRegion* MetaspaceShared::read_write_dump_space() {
+ return &_rw_region;
+}
+DumpRegion* MetaspaceShared::read_only_dump_space() {
+ return &_ro_region;
+}
-DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _md_region("md");
-size_t _total_closed_archive_region_size = 0, _total_open_archive_region_size = 0;
+DumpRegion* MetaspaceShared::optional_data_dump_space() {
+ return &_od_region;
+}
+
+void MetaspaceShared::pack_dump_space(DumpRegion* current, DumpRegion* next,
+ ReservedSpace* rs) {
+ current->pack(next);
+}
char* MetaspaceShared::misc_code_space_alloc(size_t num_bytes) {
return _mc_region.allocate(num_bytes);
@@ -226,20 +221,23 @@
// If using shared space, open the file that contains the shared space
// and map in the memory before initializing the rest of metaspace (so
// the addresses don't conflict)
- address cds_address = NULL;
- FileMapInfo* mapinfo = new FileMapInfo();
+ FileMapInfo* mapinfo = new FileMapInfo(true);
// Open the shared archive file, read and validate the header. If
// initialization fails, shared spaces [UseSharedSpaces] are
// disabled and the file is closed.
// Map in spaces now also
- if (mapinfo->initialize() && map_shared_spaces(mapinfo)) {
+ if (mapinfo->initialize(true) && map_shared_spaces(mapinfo)) {
size_t cds_total = core_spaces_size();
- cds_address = (address)mapinfo->region_addr(0);
+ address cds_address = (address)mapinfo->region_addr(0);
+ char* cds_end = (char *)align_up(cds_address + cds_total,
+ Metaspace::reserve_alignment());
+
+ // Mapping the dynamic archive before allocating the class space
+ cds_end = initialize_dynamic_runtime_shared_spaces((char*)cds_address, cds_end);
+
#ifdef _LP64
if (Metaspace::using_class_space()) {
- char* cds_end = (char*)(cds_address + cds_total);
- cds_end = (char *)align_up(cds_end, Metaspace::reserve_alignment());
// If UseCompressedClassPointers is set then allocate the metaspace area
// above the heap and above the CDS area (if it exists).
Metaspace::allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address);
@@ -255,6 +253,31 @@
}
}
+char* MetaspaceShared::initialize_dynamic_runtime_shared_spaces(
+ char* static_start, char* static_end) {
+ assert(UseSharedSpaces, "must be runtime");
+ char* cds_end = static_end;
+ if (!DynamicDumpSharedSpaces) {
+ address dynamic_top = DynamicArchive::map();
+ if (dynamic_top != NULL) {
+ assert(dynamic_top > (address)static_start, "Unexpected layout");
+ MetaspaceObj::expand_shared_metaspace_range(dynamic_top);
+ cds_end = (char *)align_up(dynamic_top, Metaspace::reserve_alignment());
+ }
+ }
+ return cds_end;
+}
+
+ReservedSpace* MetaspaceShared::reserve_shared_rs(size_t size, size_t alignment,
+ bool large, char* requested_address) {
+ if (requested_address != NULL) {
+ _shared_rs = ReservedSpace(size, alignment, large, requested_address);
+ } else {
+ _shared_rs = ReservedSpace(size, alignment, large);
+ }
+ return &_shared_rs;
+}
+
void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() {
assert(DumpSharedSpaces, "should be called for dump time only");
const size_t reserve_alignment = Metaspace::reserve_alignment();
@@ -280,12 +303,14 @@
#endif
// First try to reserve the space at the specified SharedBaseAddress.
- _shared_rs = ReservedSpace(cds_total, reserve_alignment, large_pages, shared_base);
+ //_shared_rs = ReservedSpace(cds_total, reserve_alignment, large_pages, shared_base);
+ reserve_shared_rs(cds_total, reserve_alignment, large_pages, shared_base);
if (_shared_rs.is_reserved()) {
assert(shared_base == 0 || _shared_rs.base() == shared_base, "should match");
} else {
// Get a mmap region anywhere if the SharedBaseAddress fails.
- _shared_rs = ReservedSpace(cds_total, reserve_alignment, large_pages);
+ //_shared_rs = ReservedSpace(cds_total, reserve_alignment, large_pages);
+ reserve_shared_rs(cds_total, reserve_alignment, large_pages, NULL);
}
if (!_shared_rs.is_reserved()) {
vm_exit_during_initialization("Unable to reserve memory for shared space",
@@ -324,13 +349,7 @@
CompressedClassSpaceSize, p2i(tmp_class_space.base()));
#endif
- // Start with 0 committed bytes. The memory will be committed as needed by
- // MetaspaceShared::commit_shared_space_to().
- if (!_shared_vs.initialize(_shared_rs, 0)) {
- vm_exit_during_initialization("Unable to allocate memory for shared space");
- }
-
- _mc_region.init(&_shared_rs);
+ init_shared_dump_space(&_mc_region);
SharedBaseAddress = (size_t)_shared_rs.base();
tty->print_cr("Allocated shared space: " SIZE_FORMAT " bytes at " PTR_FORMAT,
_shared_rs.size(), p2i(_shared_rs.base()));
@@ -342,9 +361,16 @@
int size = FileMapInfo::get_number_of_shared_paths();
if (size > 0) {
SystemDictionaryShared::allocate_shared_data_arrays(size, THREAD);
- FileMapHeader* header = FileMapInfo::current_info()->header();
- ClassLoaderExt::init_paths_start_index(header->_app_class_paths_start_index);
- ClassLoaderExt::init_app_module_paths_start_index(header->_app_module_paths_start_index);
+ if (!DynamicDumpSharedSpaces) {
+ FileMapHeader* header;
+ if (FileMapInfo::dynamic_info() == NULL) {
+ header = FileMapInfo::current_info()->header();
+ } else {
+ header = FileMapInfo::dynamic_info()->header();
+ }
+ ClassLoaderExt::init_paths_start_index(header->_app_class_paths_start_index);
+ ClassLoaderExt::init_app_module_paths_start_index(header->_app_module_paths_start_index);
+ }
}
}
}
@@ -405,7 +431,7 @@
}
void MetaspaceShared::commit_shared_space_to(char* newtop) {
- assert(DumpSharedSpaces, "dump-time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump-time only");
char* base = _shared_rs.base();
size_t need_committed_size = newtop - base;
size_t has_committed_size = _shared_vs.committed_size();
@@ -417,7 +443,8 @@
size_t preferred_bytes = 1 * M;
size_t uncommitted = _shared_vs.reserved_size() - has_committed_size;
- size_t commit = MAX2(min_bytes, preferred_bytes);
+ size_t commit =MAX2(min_bytes, preferred_bytes);
+ commit = MIN2(commit, uncommitted);
assert(commit <= uncommitted, "sanity");
bool result = _shared_vs.expand_by(commit, false);
@@ -465,6 +492,9 @@
InstanceMirrorKlass::serialize_offsets(soc);
soc->do_tag(--tag);
+ serialize_cloned_cpp_vtptrs(soc);
+ soc->do_tag(--tag);
+
soc->do_tag(666);
}
@@ -484,6 +514,19 @@
return _cds_i2i_entry_code_buffers;
}
+uintx MetaspaceShared::object_delta_uintx(void* obj) {
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "supported only for dumping");
+ if (DumpSharedSpaces) {
+ assert(shared_rs()->contains(obj), "must be");
+ } else {
+ assert(is_in_shared_metaspace(obj) || DynamicArchive::is_in_target_space(obj), "must be");
+ }
+ address base_address = address(SharedBaseAddress);
+ uintx deltax = address(obj) - base_address;
+ return deltax;
+}
+
// Global object for holding classes that have been loaded. Since this
// is run at a safepoint just before exit, this is the entire set of classes.
static GrowableArray<Klass*>* _global_klass_objects;
@@ -589,17 +632,21 @@
Klass* k = _global_klass_objects->at(i);
if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
- for (int i = 0; i < ik->methods()->length(); i++) {
- Method* m = ik->methods()->at(i);
- rewrite_nofast_bytecode(m);
- Fingerprinter fp(m);
- // The side effect of this call sets method's fingerprint field.
- fp.fingerprint();
- }
+ MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(ik);
}
}
}
+void MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(InstanceKlass* ik) {
+ for (int i = 0; i < ik->methods()->length(); i++) {
+ Method* m = ik->methods()->at(i);
+ rewrite_nofast_bytecode(m);
+ Fingerprinter fp(m);
+ // The side effect of this call sets method's fingerprint field.
+ fp.fingerprint();
+ }
+}
+
// Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables.
// (In GCC this is the field <Type>::_vptr, i.e., first word in the object.)
//
@@ -686,7 +733,7 @@
intptr_t* p = clone_vtable(name, _info);
assert((char*)p == _md_region.top(), "must be");
- return p;
+ return _info->cloned_vtable();
}
template <class T>
@@ -759,7 +806,7 @@
}
#define ALLOC_CPP_VTABLE_CLONE(c) \
- CppVtableCloner<c>::allocate(#c);
+ _cloned_cpp_vtptrs[c##_Kind] = CppVtableCloner<c>::allocate(#c);
#define CLONE_CPP_VTABLE(c) \
p = CppVtableCloner<c>::clone_vtable(#c, (CppVtableInfo*)p);
@@ -767,6 +814,85 @@
#define ZERO_CPP_VTABLE(c) \
CppVtableCloner<c>::zero_vtable_clone();
+//------------------------------ for DynamicDumpSharedSpaces - start
+#define DECLARE_CLONED_VTABLE_KIND(c) c ## _Kind,
+
+enum {
+ CPP_VTABLE_PATCH_TYPES_DO(DECLARE_CLONED_VTABLE_KIND)
+ _num_cloned_vtable_kinds
+};
+
+static intptr_t** _cloned_cpp_vtptrs = NULL;
+
+void MetaspaceShared::serialize_cloned_cpp_vtptrs(SerializeClosure* soc) {
+ soc->do_ptr((void**)&_cloned_cpp_vtptrs);
+}
+
+intptr_t* MetaspaceShared::fix_cpp_vtable_for_dynamic_archive(MetaspaceObj::Type msotype, address obj) {
+ assert(DynamicDumpSharedSpaces, "must");
+ int kind = -1;
+ switch (msotype) {
+ case MetaspaceObj::SymbolType:
+ case MetaspaceObj::TypeArrayU1Type:
+ case MetaspaceObj::TypeArrayU2Type:
+ case MetaspaceObj::TypeArrayU4Type:
+ case MetaspaceObj::TypeArrayU8Type:
+ case MetaspaceObj::TypeArrayOtherType:
+ case MetaspaceObj::ConstMethodType:
+ case MetaspaceObj::ConstantPoolCacheType:
+ case MetaspaceObj::AnnotationsType:
+ case MetaspaceObj::MethodCountersType:
+ // These have no vtables.
+ break;
+ case MetaspaceObj::ClassType:
+ {
+ Klass* k = (Klass*)obj;
+ assert(k->is_klass(), "must be");
+ if (k->is_instance_klass()) {
+ kind = InstanceKlass_Kind;
+ } else {
+ assert(k->is_objArray_klass(),
+ "We shouldn't archive any other klasses in DynamicDumpSharedSpaces");
+ kind = ObjArrayKlass_Kind;
+ }
+ }
+ break;
+
+ case MetaspaceObj::MethodType:
+ {
+ Method* m = (Method*)obj;
+ assert(m->is_method(), "must be");
+ kind = Method_Kind;
+ }
+ break;
+
+ case MetaspaceObj::MethodDataType:
+ // We don't archive MethodData <-- should have been removed in removed_unsharable_info
+ ShouldNotReachHere();
+ break;
+
+ case MetaspaceObj::ConstantPoolType:
+ {
+ ConstantPool *cp = (ConstantPool*)obj;
+ assert(cp->is_constantPool(), "must be");
+ kind = ConstantPool_Kind;
+ }
+ break;
+
+ default:
+ ShouldNotReachHere();
+ }
+
+ if (kind >= 0) {
+ assert(kind < _num_cloned_vtable_kinds, "must be");
+ return _cloned_cpp_vtptrs[kind];
+ } else {
+ return NULL;
+ }
+}
+
+//------------------------------ for DynamicDumpSharedSpaces - end
+
// This can be called at both dump time and run time.
intptr_t* MetaspaceShared::clone_cpp_vtables(intptr_t* p) {
assert(DumpSharedSpaces || UseSharedSpaces, "sanity");
@@ -830,55 +956,27 @@
return CppVtableCloner<Method>::is_valid_shared_object(m);
}
-// Closure for serializing initialization data out to a data area to be
-// written to the shared file.
-
-class WriteClosure : public SerializeClosure {
-private:
- DumpRegion* _dump_region;
-
-public:
- WriteClosure(DumpRegion* r) {
- _dump_region = r;
+void WriteClosure::do_oop(oop* o) {
+ if (*o == NULL) {
+ _dump_region->append_intptr_t(0);
+ } else {
+ assert(HeapShared::is_heap_object_archiving_allowed(),
+ "Archiving heap object is not allowed");
+ _dump_region->append_intptr_t(
+ (intptr_t)CompressedOops::encode_not_null(*o));
}
-
- void do_ptr(void** p) {
- _dump_region->append_intptr_t((intptr_t)*p);
- }
-
- void do_u4(u4* p) {
- void* ptr = (void*)(uintx(*p));
- do_ptr(&ptr);
- }
-
- void do_tag(int tag) {
- _dump_region->append_intptr_t((intptr_t)tag);
- }
+}
- void do_oop(oop* o) {
- if (*o == NULL) {
- _dump_region->append_intptr_t(0);
- } else {
- assert(HeapShared::is_heap_object_archiving_allowed(),
- "Archiving heap object is not allowed");
- _dump_region->append_intptr_t(
- (intptr_t)CompressedOops::encode_not_null(*o));
- }
+void WriteClosure::do_region(u_char* start, size_t size) {
+ assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment");
+ assert(size % sizeof(intptr_t) == 0, "bad size");
+ do_tag((int)size);
+ while (size > 0) {
+ _dump_region->append_intptr_t(*(intptr_t*)start);
+ start += sizeof(intptr_t);
+ size -= sizeof(intptr_t);
}
-
- void do_region(u_char* start, size_t size) {
- assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment");
- assert(size % sizeof(intptr_t) == 0, "bad size");
- do_tag((int)size);
- while (size > 0) {
- _dump_region->append_intptr_t(*(intptr_t*)start);
- start += sizeof(intptr_t);
- size -= sizeof(intptr_t);
- }
- }
-
- bool reading() const { return false; }
-};
+}
// This is for dumping detailed statistics for the allocations
// in the shared spaces.
@@ -1166,20 +1264,22 @@
public:
ShallowCopier(bool read_only) : _read_only(read_only) {}
- virtual void do_unique_ref(Ref* ref, bool read_only) {
+ virtual bool do_unique_ref(Ref* ref, bool read_only) {
if (read_only == _read_only) {
allocate(ref, read_only);
}
+ return true; // recurse into ref.obj()
}
};
// Relocate embedded pointers within a MetaspaceObj's shallow copy
class ShallowCopyEmbeddedRefRelocator: public UniqueMetaspaceClosure {
public:
- virtual void do_unique_ref(Ref* ref, bool read_only) {
+ virtual bool do_unique_ref(Ref* ref, bool read_only) {
address new_loc = get_new_loc(ref);
RefRelocator refer;
ref->metaspace_pointers_do_at(&refer, new_loc);
+ return true; // recurse into ref.obj()
}
};
@@ -1294,6 +1394,8 @@
Universe::metaspace_pointers_do(it);
SymbolTable::metaspace_pointers_do(it);
vmSymbols::metaspace_pointers_do(it);
+
+ it->finish();
}
static Klass* get_relocated_klass(Klass* orig_klass) {
@@ -1336,6 +1438,9 @@
char* start = _ro_region.top();
+ size_t vtptrs_bytes = _num_cloned_vtable_kinds * sizeof(intptr_t*);
+ _cloned_cpp_vtptrs = (intptr_t**)_ro_region.allocate(vtptrs_bytes, sizeof(intptr_t*));
+
// Write the other data to the output array.
WriteClosure wc(&_ro_region);
MetaspaceShared::serialize(&wc);
@@ -1354,6 +1459,7 @@
// in the VM thread.
// (2) ArchiveCompactor needs to work with a stable set of MetaspaceObjs.
Metaspace::freeze();
+ DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
Thread* THREAD = VMThread::vm_thread();
@@ -1441,7 +1547,7 @@
// Create and write the archive file that maps the shared spaces.
- FileMapInfo* mapinfo = new FileMapInfo();
+ FileMapInfo* mapinfo = new FileMapInfo(true);
mapinfo->populate_header(os::vm_allocation_granularity());
mapinfo->set_read_only_tables_start(read_only_tables_start);
mapinfo->set_misc_data_patching_start(vtbl_list);
@@ -1816,67 +1922,55 @@
}
#endif // INCLUDE_CDS_JAVA_HEAP
-// Closure for serializing initialization data in from a data area
-// (ptr_array) read from the shared file.
-
-class ReadClosure : public SerializeClosure {
-private:
- intptr_t** _ptr_array;
-
- inline intptr_t nextPtr() {
- return *(*_ptr_array)++;
- }
+void ReadClosure::do_ptr(void** p) {
+ assert(*p == NULL, "initializing previous initialized pointer.");
+ intptr_t obj = nextPtr();
+ assert((intptr_t)obj >= 0 || (intptr_t)obj < -100,
+ "hit tag while initializing ptrs.");
+ *p = (void*)obj;
+}
-public:
- ReadClosure(intptr_t** ptr_array) { _ptr_array = ptr_array; }
+void ReadClosure::do_u4(u4* p) {
+ intptr_t obj = nextPtr();
+ *p = (u4)(uintx(obj));
+}
- void do_ptr(void** p) {
- assert(*p == NULL, "initializing previous initialized pointer.");
- intptr_t obj = nextPtr();
- assert((intptr_t)obj >= 0 || (intptr_t)obj < -100,
- "hit tag while initializing ptrs.");
- *p = (void*)obj;
- }
-
- void do_u4(u4* p) {
- intptr_t obj = nextPtr();
- *p = (u4)(uintx(obj));
- }
+void ReadClosure::do_tag(int tag) {
+ int old_tag;
+ old_tag = (int)(intptr_t)nextPtr();
+ // do_int(&old_tag);
+ assert(tag == old_tag, "old tag doesn't match");
+ FileMapInfo::assert_mark(tag == old_tag);
+}
- void do_tag(int tag) {
- int old_tag;
- old_tag = (int)(intptr_t)nextPtr();
- // do_int(&old_tag);
- assert(tag == old_tag, "old tag doesn't match");
- FileMapInfo::assert_mark(tag == old_tag);
+void ReadClosure::do_oop(oop *p) {
+ narrowOop o = (narrowOop)nextPtr();
+ if (o == 0 || !HeapShared::open_archive_heap_region_mapped()) {
+ p = NULL;
+ } else {
+ assert(HeapShared::is_heap_object_archiving_allowed(),
+ "Archived heap object is not allowed");
+ assert(HeapShared::open_archive_heap_region_mapped(),
+ "Open archive heap region is not mapped");
+ *p = HeapShared::decode_from_archive(o);
}
+}
- void do_oop(oop *p) {
- narrowOop o = (narrowOop)nextPtr();
- if (o == 0 || !HeapShared::open_archive_heap_region_mapped()) {
- p = NULL;
- } else {
- assert(HeapShared::is_heap_object_archiving_allowed(),
- "Archived heap object is not allowed");
- assert(HeapShared::open_archive_heap_region_mapped(),
- "Open archive heap region is not mapped");
- *p = HeapShared::decode_from_archive(o);
- }
+void ReadClosure::do_region(u_char* start, size_t size) {
+ assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment");
+ assert(size % sizeof(intptr_t) == 0, "bad size");
+ do_tag((int)size);
+ while (size > 0) {
+ *(intptr_t*)start = nextPtr();
+ start += sizeof(intptr_t);
+ size -= sizeof(intptr_t);
}
+}
- void do_region(u_char* start, size_t size) {
- assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment");
- assert(size % sizeof(intptr_t) == 0, "bad size");
- do_tag((int)size);
- while (size > 0) {
- *(intptr_t*)start = nextPtr();
- start += sizeof(intptr_t);
- size -= sizeof(intptr_t);
- }
- }
-
- bool reading() const { return true; }
-};
+void MetaspaceShared::set_shared_metaspace_range(void* base, void* top) {
+ _shared_metaspace_static_top = top;
+ MetaspaceObj::set_shared_metaspace_range(base, top);
+}
// Return true if given address is in the misc data region
bool MetaspaceShared::is_in_shared_region(const void* p, int idx) {
@@ -1890,6 +1984,15 @@
return false;
}
+bool MetaspaceShared::is_shared_dynamic(void* p) {
+ if ((p < MetaspaceObj::shared_metaspace_top()) &&
+ (p >= _shared_metaspace_static_top)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
// Map shared spaces at requested addresses and return if succeeded.
bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
size_t image_alignment = mapinfo->alignment();
@@ -1904,42 +2007,23 @@
assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces");
- char* ro_base = NULL; char* ro_top;
- char* rw_base = NULL; char* rw_top;
- char* mc_base = NULL; char* mc_top;
- char* md_base = NULL; char* md_top;
+ // Map each shared region
+ int regions[] = {mc, rw, ro, md};
+ size_t len = sizeof(regions)/sizeof(int);
+ char* saved_base[] = {NULL, NULL, NULL, NULL};
+ char* top = mapinfo->map_regions(regions, saved_base, len );
- // Map each shared region
- if ((mc_base = mapinfo->map_region(mc, &mc_top)) != NULL &&
- (rw_base = mapinfo->map_region(rw, &rw_top)) != NULL &&
- (ro_base = mapinfo->map_region(ro, &ro_top)) != NULL &&
- (md_base = mapinfo->map_region(md, &md_top)) != NULL &&
+ if (top != NULL &&
(image_alignment == (size_t)os::vm_allocation_granularity()) &&
mapinfo->validate_shared_path_table()) {
// Success -- set up MetaspaceObj::_shared_metaspace_{base,top} for
// fast checking in MetaspaceShared::is_in_shared_metaspace() and
// MetaspaceObj::is_shared().
- //
- // We require that mc->rw->ro->md to be laid out consecutively, with no
- // gaps between them. That way, we can ensure that the OS won't be able to
- // allocate any new memory spaces inside _shared_metaspace_{base,top}, which
- // would mess up the simple comparision in MetaspaceShared::is_in_shared_metaspace().
- assert(mc_base < ro_base && mc_base < rw_base && mc_base < md_base, "must be");
- assert(md_top > ro_top && md_top > rw_top && md_top > mc_top , "must be");
- assert(mc_top == rw_base, "must be");
- assert(rw_top == ro_base, "must be");
- assert(ro_top == md_base, "must be");
-
_core_spaces_size = mapinfo->core_spaces_size();
- MetaspaceObj::set_shared_metaspace_range((void*)mc_base, (void*)md_top);
+ set_shared_metaspace_range((void*)saved_base[0], (void*)top);
return true;
} else {
- // If there was a failure in mapping any of the spaces, unmap the ones
- // that succeeded
- if (ro_base != NULL) mapinfo->unmap_region(ro);
- if (rw_base != NULL) mapinfo->unmap_region(rw);
- if (mc_base != NULL) mapinfo->unmap_region(mc);
- if (md_base != NULL) mapinfo->unmap_region(md);
+ mapinfo->unmap_regions(regions, saved_base, len);
#ifndef _WINDOWS
// Release the entire mapped region
shared_rs.release();
@@ -1970,6 +2054,9 @@
// The rest of the data is now stored in the RW region
buffer = mapinfo->read_only_tables_start();
+ // Skip over _cloned_cpp_vtptrs;
+ buffer += _num_cloned_vtable_kinds * sizeof(intptr_t*);
+
// Verify various attributes of the archive, plus initialize the
// shared string/symbol tables
intptr_t* array = (intptr_t*)buffer;
@@ -2009,6 +2096,12 @@
if (!mapinfo->remap_shared_readonly_as_readwrite()) {
return false;
}
+ if (FileMapInfo::dynamic_info() != NULL) {
+ mapinfo = FileMapInfo::dynamic_info();
+ if (!mapinfo->remap_shared_readonly_as_readwrite()) {
+ return false;
+ }
+ }
_remapped_readwrite = true;
}
return true;
--- a/src/hotspot/share/memory/metaspaceShared.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/metaspaceShared.hpp Fri May 17 08:29:55 2019 -0700
@@ -47,6 +47,124 @@
CompactHashtableStats string;
};
+#if INCLUDE_CDS
+class DumpRegion {
+private:
+ const char* _name;
+ char* _base;
+ char* _top;
+ char* _end;
+ bool _is_packed;
+
+public:
+ DumpRegion(const char* name) : _name(name), _base(NULL), _top(NULL), _end(NULL), _is_packed(false) {}
+
+ char* expand_top_to(char* newtop);
+ char* allocate(size_t num_bytes, size_t alignment=BytesPerWord);
+
+ void append_intptr_t(intptr_t n) {
+ assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment");
+ intptr_t *p = (intptr_t*)_top;
+ char* newtop = _top + sizeof(intptr_t);
+ expand_top_to(newtop);
+ *p = n;
+ }
+
+ char* base() const { return _base; }
+ char* top() const { return _top; }
+ char* end() const { return _end; }
+ size_t reserved() const { return _end - _base; }
+ size_t used() const { return _top - _base; }
+ bool is_packed() const { return _is_packed; }
+ bool is_allocatable() const {
+ return !is_packed() && _base != NULL;
+ }
+
+ void print(size_t total_bytes) const;
+ void print_out_of_space_msg(const char* failing_region, size_t needed_bytes);
+
+ void init(const ReservedSpace* rs, char* base) {
+ if (base == NULL) {
+ base = rs->base();
+ }
+ assert(rs->contains(base), "must be");
+ _base = _top = base;
+ _end = rs->end();
+ }
+ void init(char* b, char* t, char* e) {
+ _base = b;
+ _top = t;
+ _end = e;
+ }
+
+ void pack(DumpRegion* next = NULL);
+
+ bool contains(char* p) {
+ return base() <= p && p < top();
+ }
+};
+
+// Closure for serializing initialization data out to a data area to be
+// written to the shared file.
+
+class WriteClosure : public SerializeClosure {
+private:
+ DumpRegion* _dump_region;
+
+public:
+ WriteClosure(DumpRegion* r) {
+ _dump_region = r;
+ }
+
+ void do_ptr(void** p) {
+ _dump_region->append_intptr_t((intptr_t)*p);
+ }
+
+ void do_u4(u4* p) {
+ void* ptr = (void*)(uintx(*p));
+ do_ptr(&ptr);
+ }
+
+ void do_tag(int tag) {
+ _dump_region->append_intptr_t((intptr_t)tag);
+ }
+
+ void do_oop(oop* o);
+
+ void do_region(u_char* start, size_t size);
+
+ bool reading() const { return false; }
+};
+
+// Closure for serializing initialization data in from a data area
+// (ptr_array) read from the shared file.
+
+class ReadClosure : public SerializeClosure {
+private:
+ intptr_t** _ptr_array;
+
+ inline intptr_t nextPtr() {
+ return *(*_ptr_array)++;
+ }
+
+public:
+ ReadClosure(intptr_t** ptr_array) { _ptr_array = ptr_array; }
+
+ void do_ptr(void** p);
+
+ void do_u4(u4* p);
+
+ void do_tag(int tag);
+
+ void do_oop(oop *p);
+
+ void do_region(u_char* start, size_t size);
+
+ bool reading() const { return true; }
+};
+
+#endif
+
// Class Data Sharing Support
class MetaspaceShared : AllStatic {
@@ -61,6 +179,7 @@
static address _cds_i2i_entry_code_buffers;
static size_t _cds_i2i_entry_code_buffers_size;
static size_t _core_spaces_size;
+ static void* _shared_metaspace_static_top;
public:
enum {
// core archive spaces
@@ -102,16 +221,12 @@
}
static void initialize_dumptime_shared_and_meta_spaces() NOT_CDS_RETURN;
static void initialize_runtime_shared_and_meta_spaces() NOT_CDS_RETURN;
+ static char* initialize_dynamic_runtime_shared_spaces(
+ char* static_start, char* static_end) NOT_CDS_RETURN_(NULL);
static void post_initialize(TRAPS) NOT_CDS_RETURN;
- // Delta of this object from the bottom of the archive.
- static uintx object_delta_uintx(void* obj) {
- assert(DumpSharedSpaces, "supported only for dumping");
- assert(shared_rs()->contains(obj), "must be");
- address base_address = address(shared_rs()->base());
- uintx deltax = address(obj) - base_address;
- return deltax;
- }
+ // Delta of this object from SharedBaseAddress
+ static uintx object_delta_uintx(void* obj);
static u4 object_delta_u4(void* obj) {
// offset is guaranteed to be less than MAX_SHARED_DELTA in DumpRegion::expand_top_to()
@@ -134,15 +249,25 @@
return (p < MetaspaceObj::shared_metaspace_top() && p >= MetaspaceObj::shared_metaspace_base());
}
+ static address shared_metaspace_top() {
+ return (address)MetaspaceObj::shared_metaspace_top();
+ }
+
+ static void set_shared_metaspace_range(void* base, void* top) NOT_CDS_RETURN;
+
// Return true if given address is in the shared region corresponding to the idx
static bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false);
static bool is_in_trampoline_frame(address addr) NOT_CDS_RETURN_(false);
+ static bool is_shared_dynamic(void* p) NOT_CDS_RETURN_(false);
+
static void allocate_cpp_vtable_clones();
static intptr_t* clone_cpp_vtables(intptr_t* p);
static void zero_cpp_vtable_clones_for_writing();
static void patch_cpp_vtable_pointers();
+ static void serialize_cloned_cpp_vtptrs(SerializeClosure* sc);
+
static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false);
static void serialize(SerializeClosure* sc) NOT_CDS_RETURN;
@@ -165,6 +290,20 @@
static bool try_link_class(InstanceKlass* ik, TRAPS);
static void link_and_cleanup_shared_classes(TRAPS);
+#if INCLUDE_CDS
+ static ReservedSpace* reserve_shared_rs(size_t size, size_t alignment,
+ bool large, char* requested_address);
+ static void init_shared_dump_space(DumpRegion* first_space, address first_space_bottom = NULL);
+ static DumpRegion* misc_code_dump_space();
+ static DumpRegion* read_write_dump_space();
+ static DumpRegion* read_only_dump_space();
+ static DumpRegion* optional_data_dump_space();
+ static void pack_dump_space(DumpRegion* current, DumpRegion* next,
+ ReservedSpace* rs);
+
+ static void rewrite_nofast_bytecodes_and_calculate_fingerprints(InstanceKlass* ik);
+#endif
+
// Allocate a block of memory from the "mc", "ro", or "rw" regions.
static char* misc_code_space_alloc(size_t num_bytes);
static char* read_only_space_alloc(size_t num_bytes);
@@ -181,6 +320,12 @@
#endif
}
+ template <typename T>
+ static size_t ro_array_bytesize(int length) {
+ size_t byte_size = Array<T>::byte_sizeof(length, sizeof(T));
+ return align_up(byte_size, BytesPerWord);
+ }
+
static address cds_i2i_entry_code_buffers(size_t total_size);
static address cds_i2i_entry_code_buffers() {
@@ -193,6 +338,8 @@
static Klass* get_relocated_klass(Klass *k);
+ static intptr_t* fix_cpp_vtable_for_dynamic_archive(MetaspaceObj::Type msotype, address obj);
+
private:
static void read_extra_data(const char* filename, TRAPS) NOT_CDS_RETURN;
};
--- a/src/hotspot/share/memory/universe.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/memory/universe.cpp Fri May 17 08:29:55 2019 -0700
@@ -711,13 +711,14 @@
{
SymbolTable::create_table();
StringTable::create_table();
+ }
#if INCLUDE_CDS
- if (DumpSharedSpaces) {
- MetaspaceShared::prepare_for_dumping();
- }
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
+ MetaspaceShared::prepare_for_dumping();
+ }
#endif
- }
+
if (strlen(VerifySubSet) > 0) {
Universe::initialize_verify_flags();
}
--- a/src/hotspot/share/oops/constMethod.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/constMethod.hpp Fri May 17 08:29:55 2019 -0700
@@ -288,12 +288,16 @@
// adapter
void set_adapter_entry(AdapterHandlerEntry* adapter) {
- assert(!is_shared(), "shared methods have fixed adapter_trampoline");
+ assert(!is_shared(),
+ "shared methods in archive have fixed adapter_trampoline");
_adapter = adapter;
}
void set_adapter_trampoline(AdapterHandlerEntry** trampoline) {
- assert(DumpSharedSpaces, "must be");
- assert(*trampoline == NULL, "must be NULL during dump time, to be initialized at run time");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "must be");
+ if (DumpSharedSpaces) {
+ assert(*trampoline == NULL,
+ "must be NULL during dump time, to be initialized at run time");
+ }
_adapter_trampoline = trampoline;
}
void update_adapter_trampoline(AdapterHandlerEntry* adapter) {
--- a/src/hotspot/share/oops/constantPool.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/constantPool.cpp Fri May 17 08:29:55 2019 -0700
@@ -371,7 +371,9 @@
// If archiving heap objects is not allowed, clear the resolved references.
// Otherwise, it is cleared after the resolved references array is cached
// (see archive_resolved_references()).
- if (!HeapShared::is_heap_object_archiving_allowed()) {
+ // If DynamicDumpSharedSpaces is enabled, clear the resolved references also
+ // as java objects are not archived in the top layer.
+ if (!HeapShared::is_heap_object_archiving_allowed() || DynamicDumpSharedSpaces) {
set_resolved_references(NULL);
}
@@ -382,7 +384,16 @@
_flags |= (_on_stack | _is_shared);
int num_klasses = 0;
for (int index = 1; index < length(); index++) { // Index 0 is unused
- assert(!tag_at(index).is_unresolved_klass_in_error(), "This must not happen during dump time");
+ if (!DynamicDumpSharedSpaces) {
+ assert(!tag_at(index).is_unresolved_klass_in_error(), "This must not happen during static dump time");
+ } else {
+ if (tag_at(index).is_unresolved_klass_in_error() ||
+ tag_at(index).is_method_handle_in_error() ||
+ tag_at(index).is_method_type_in_error() ||
+ tag_at(index).is_dynamic_constant_in_error()) {
+ tag_at_put(index, JVM_CONSTANT_UnresolvedClass);
+ }
+ }
if (tag_at(index).is_klass()) {
// This class was resolved as a side effect of executing Java code
// during dump time. We need to restore it back to an UnresolvedClass,
--- a/src/hotspot/share/oops/cpCache.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/cpCache.cpp Fri May 17 08:29:55 2019 -0700
@@ -697,7 +697,7 @@
}
void ConstantPoolCache::walk_entries_for_initialization(bool check_only) {
- assert(DumpSharedSpaces, "sanity");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "sanity");
// When dumping the archive, we want to clean up the ConstantPoolCache
// to remove any effect of linking due to the execution of Java code --
// each ConstantPoolCacheEntry will have the same contents as if
--- a/src/hotspot/share/oops/instanceKlass.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/instanceKlass.cpp Fri May 17 08:29:55 2019 -0700
@@ -451,7 +451,7 @@
assert(is_instance_klass(), "is layout incorrect?");
assert(size_helper() == parser.layout_size(), "incorrect size_helper?");
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
SystemDictionaryShared::init_dumptime_info(this);
}
}
@@ -601,7 +601,7 @@
}
set_annotations(NULL);
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
SystemDictionaryShared::remove_dumptime_info(this);
}
}
@@ -2225,8 +2225,8 @@
// (1) We are running AOT to generate a shared library.
return true;
}
- if (DumpSharedSpaces) {
- // (2) We are running -Xshare:dump to create a shared archive
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
+ // (2) We are running -Xshare:dump or -XX:ArchiveClassesAtExit to create a shared archive
return true;
}
if (UseAOT && is_unsafe_anonymous) {
@@ -2346,15 +2346,13 @@
array_klasses()->remove_unshareable_info();
}
- // These are not allocated from metaspace, but they should should all be empty
- // during dump time, so we don't need to worry about them in InstanceKlass::iterate().
- guarantee(_source_debug_extension == NULL, "must be");
- guarantee(_dep_context == NULL, "must be");
- guarantee(_osr_nmethods_head == NULL, "must be");
-
+ // These are not allocated from metaspace. They are safe to set to NULL.
+ _source_debug_extension = NULL;
+ _dep_context = NULL;
+ _osr_nmethods_head = NULL;
#if INCLUDE_JVMTI
- guarantee(_breakpoints == NULL, "must be");
- guarantee(_previous_versions == NULL, "must be");
+ _breakpoints = NULL;
+ _previous_versions = NULL;
_cached_class_file = NULL;
#endif
@@ -2475,6 +2473,10 @@
// notify ClassLoadingService of class unload
ClassLoadingService::notify_class_unloaded(ik);
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
+ SystemDictionaryShared::remove_dumptime_info(ik);
+ }
+
if (log_is_enabled(Info, class, unload)) {
ResourceMark rm;
log_info(class, unload)("unloading class %s " INTPTR_FORMAT, ik->external_name(), p2i(ik));
@@ -3422,7 +3424,12 @@
info_stream.print(" source: %s", class_loader->klass()->external_name());
}
} else {
- info_stream.print(" source: shared objects file");
+ assert(this->is_shared(), "must be");
+ if (MetaspaceShared::is_shared_dynamic((void*)this)) {
+ info_stream.print(" source: shared objects file (top)");
+ } else {
+ info_stream.print(" source: shared objects file");
+ }
}
msg.info("%s", info_stream.as_string());
--- a/src/hotspot/share/oops/klass.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/klass.cpp Fri May 17 08:29:55 2019 -0700
@@ -525,7 +525,8 @@
}
void Klass::remove_unshareable_info() {
- assert (DumpSharedSpaces, "only called for DumpSharedSpaces");
+ assert (DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "only called during CDS dump time");
JFR_ONLY(REMOVE_ID(this);)
if (log_is_enabled(Trace, cds, unshareable)) {
ResourceMark rm;
@@ -542,7 +543,7 @@
}
void Klass::remove_java_mirror() {
- assert (DumpSharedSpaces, "only called for DumpSharedSpaces");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only called during CDS dump time");
if (log_is_enabled(Trace, cds, unshareable)) {
ResourceMark rm;
log_trace(cds, unshareable)("remove java_mirror: %s", external_name());
--- a/src/hotspot/share/oops/method.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/method.cpp Fri May 17 08:29:55 2019 -0700
@@ -958,23 +958,30 @@
void Method::unlink_method() {
_code = NULL;
- assert(DumpSharedSpaces, "dump time only");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only");
// Set the values to what they should be at run time. Note that
// this Method can no longer be executed during dump time.
_i2i_entry = Interpreter::entry_for_cds_method(this);
_from_interpreted_entry = _i2i_entry;
+ if (DynamicDumpSharedSpaces) {
+ assert(_from_compiled_entry != NULL, "sanity");
+ } else {
+ // TODO: Simplify the adapter trampoline allocation for static archiving.
+ // Remove the use of CDSAdapterHandlerEntry.
+ CDSAdapterHandlerEntry* cds_adapter = (CDSAdapterHandlerEntry*)adapter();
+ constMethod()->set_adapter_trampoline(cds_adapter->get_adapter_trampoline());
+ _from_compiled_entry = cds_adapter->get_c2i_entry_trampoline();
+ assert(*((int*)_from_compiled_entry) == 0,
+ "must be NULL during dump time, to be initialized at run time");
+ }
+
if (is_native()) {
*native_function_addr() = NULL;
set_signature_handler(NULL);
}
NOT_PRODUCT(set_compiled_invocation_count(0);)
- CDSAdapterHandlerEntry* cds_adapter = (CDSAdapterHandlerEntry*)adapter();
- constMethod()->set_adapter_trampoline(cds_adapter->get_adapter_trampoline());
- _from_compiled_entry = cds_adapter->get_c2i_entry_trampoline();
- assert(*((int*)_from_compiled_entry) == 0, "must be NULL during dump time, to be initialized at run time");
-
set_method_data(NULL);
clear_method_counters();
}
--- a/src/hotspot/share/oops/method.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/method.hpp Fri May 17 08:29:55 2019 -0700
@@ -468,9 +468,15 @@
void set_adapter_entry(AdapterHandlerEntry* adapter) {
constMethod()->set_adapter_entry(adapter);
}
+ void set_adapter_trampoline(AdapterHandlerEntry** trampoline) {
+ constMethod()->set_adapter_trampoline(trampoline);
+ }
void update_adapter_trampoline(AdapterHandlerEntry* adapter) {
constMethod()->update_adapter_trampoline(adapter);
}
+ void set_from_compiled_entry(address entry) {
+ _from_compiled_entry = entry;
+ }
address get_i2c_entry();
address get_c2i_entry();
@@ -511,7 +517,8 @@
address interpreter_entry() const { return _i2i_entry; }
// Only used when first initialize so we can set _i2i_entry and _from_interpreted_entry
void set_interpreter_entry(address entry) {
- assert(!is_shared(), "shared method's interpreter entry should not be changed at run time");
+ assert(!is_shared(),
+ "shared method's interpreter entry should not be changed at run time");
if (_i2i_entry != entry) {
_i2i_entry = entry;
}
--- a/src/hotspot/share/oops/symbol.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/symbol.cpp Fri May 17 08:29:55 2019 -0700
@@ -74,6 +74,13 @@
FreeHeap(p);
}
+void Symbol::set_permanent() {
+ // This is called at a safepoint during dumping of a dynamic CDS archive.
+ assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
+ _length_and_refcount = pack_length_and_refcount(length(), PERM_REFCOUNT);
+}
+
+
// ------------------------------------------------------------------
// Symbol::starts_with
//
--- a/src/hotspot/share/oops/symbol.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/oops/symbol.hpp Fri May 17 08:29:55 2019 -0700
@@ -169,6 +169,7 @@
bool is_permanent() {
return (refcount() == PERM_REFCOUNT);
}
+ void set_permanent();
void make_permanent();
// Function char_at() returns the Symbol's selected u1 byte as a char type.
--- a/src/hotspot/share/prims/cdsoffsets.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/prims/cdsoffsets.cpp Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
#include "utilities/macros.hpp"
#if INCLUDE_CDS
#include "runtime/os.hpp"
+#include "memory/dynamicArchive.hpp"
#include "memory/filemap.hpp"
#include "memory/allocation.hpp"
#include "memory/allocation.inline.hpp"
@@ -44,6 +45,7 @@
#define CREATE_OFFSET_MAPS \
_all = new CDSOffsets("size_t_size", sizeof(size_t), NULL); \
+ ADD_NEXT(_all, "int_size", sizeof(int)); \
ADD_NEXT(_all, "FileMapHeader::_magic", offset_of(FileMapHeader, _magic)); \
ADD_NEXT(_all, "FileMapHeader::_crc", offset_of(FileMapHeader, _crc)); \
ADD_NEXT(_all, "FileMapHeader::_version", offset_of(FileMapHeader, _version)); \
@@ -52,6 +54,7 @@
ADD_NEXT(_all, "CDSFileMapRegion::_used", offset_of(CDSFileMapRegion, _used)); \
ADD_NEXT(_all, "FileMapHeader::_paths_misc_info_size", offset_of(FileMapHeader, _paths_misc_info_size)); \
ADD_NEXT(_all, "file_header_size", sizeof(FileMapHeader)); \
+ ADD_NEXT(_all, "DynamicArchiveHeader::_base_archive_crc", offset_of(DynamicArchiveHeader, _base_archive_crc)); \
ADD_NEXT(_all, "CDSFileMapRegion_size", sizeof(CDSFileMapRegion));
int CDSOffsets::find_offset(const char* name) {
--- a/src/hotspot/share/prims/whitebox.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/prims/whitebox.cpp Fri May 17 08:29:55 2019 -0700
@@ -38,6 +38,7 @@
#include "gc/shared/genArguments.hpp"
#include "gc/shared/genCollectedHeap.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
+#include "memory/filemap.hpp"
#include "memory/heapShared.inline.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/metadataFactory.hpp"
@@ -1883,6 +1884,10 @@
return UseSharedSpaces;
WB_END
+WB_ENTRY(jboolean, WB_CDSMemoryMappingFailed(JNIEnv* env, jobject wb))
+ return FileMapInfo::memory_mapping_failed();
+WB_END
+
WB_ENTRY(jboolean, WB_IsShared(JNIEnv* env, jobject wb, jobject obj))
oop obj_oop = JNIHandles::resolve(obj);
return HeapShared::is_archived_object(obj_oop);
@@ -1908,6 +1913,15 @@
}
WB_END
+WB_ENTRY(void, WB_LinkClass(JNIEnv* env, jobject wb, jclass clazz))
+ Klass *k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz));
+ if (!k->is_instance_klass()) {
+ return;
+ }
+ InstanceKlass *ik = InstanceKlass::cast(k);
+ ik->link_class(THREAD); // may throw verification error
+WB_END
+
WB_ENTRY(jboolean, WB_AreOpenArchiveHeapObjectsMapped(JNIEnv* env))
return HeapShared::open_archive_heap_region_mapped();
WB_END
@@ -2342,10 +2356,12 @@
{CC"isSharedClass", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsSharedClass },
{CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored },
{CC"getResolvedReferences", CC"(Ljava/lang/Class;)Ljava/lang/Object;", (void*)&WB_GetResolvedReferences},
+ {CC"linkClass", CC"(Ljava/lang/Class;)V", (void*)&WB_LinkClass},
{CC"areOpenArchiveHeapObjectsMapped", CC"()Z", (void*)&WB_AreOpenArchiveHeapObjectsMapped},
{CC"isCDSIncludedInVmBuild", CC"()Z", (void*)&WB_IsCDSIncludedInVmBuild },
{CC"isJFRIncludedInVmBuild", CC"()Z", (void*)&WB_IsJFRIncludedInVmBuild },
- {CC"isJavaHeapArchiveSupported", CC"()Z", (void*)&WB_IsJavaHeapArchiveSupported },
+ {CC"isJavaHeapArchiveSupported", CC"()Z", (void*)&WB_IsJavaHeapArchiveSupported },
+ {CC"cdsMemoryMappingFailed", CC"()Z", (void*)&WB_CDSMemoryMappingFailed },
{CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches },
{CC"handshakeWalkStack", CC"(Ljava/lang/Thread;Z)I", (void*)&WB_HandshakeWalkStack },
--- a/src/hotspot/share/runtime/arguments.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/runtime/arguments.cpp Fri May 17 08:29:55 2019 -0700
@@ -36,6 +36,7 @@
#include "logging/logStream.hpp"
#include "logging/logTag.hpp"
#include "memory/allocation.inline.hpp"
+#include "memory/filemap.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/arguments.hpp"
@@ -95,6 +96,7 @@
bool Arguments::_enable_preview = false;
char* Arguments::SharedArchivePath = NULL;
+char* Arguments::SharedDynamicArchivePath = NULL;
AgentLibraryList Arguments::_libraryList;
AgentLibraryList Arguments::_agentList;
@@ -1469,7 +1471,8 @@
"--patch-module"
};
void Arguments::check_unsupported_dumping_properties() {
- assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces,
+ "this function is only used with CDS dump time");
assert(ARRAY_SIZE(unsupported_properties) == ARRAY_SIZE(unsupported_options), "must be");
// If a vm option is found in the unsupported_options array, vm will exit with an error message.
SystemProperty* sp = system_properties();
@@ -1492,6 +1495,13 @@
bool Arguments::check_unsupported_cds_runtime_properties() {
assert(UseSharedSpaces, "this function is only used with -Xshare:{on,auto}");
assert(ARRAY_SIZE(unsupported_properties) == ARRAY_SIZE(unsupported_options), "must be");
+ if (ArchiveClassesAtExit != NULL) {
+ // dynamic dumping, just return false for now.
+ // check_unsupported_dumping_properties() will be called later to check the same set of
+ // properties, and will exit the VM with the correct error message if the unsupported properties
+ // are used.
+ return false;
+ }
for (uint i = 0; i < ARRAY_SIZE(unsupported_properties); i++) {
if (get_property(unsupported_properties[i]) != NULL) {
if (RequireSharedSpaces) {
@@ -2713,7 +2723,6 @@
if (FLAG_SET_CMDLINE(bool, DumpSharedSpaces, true) != JVMFlag::SUCCESS) {
return JNI_EINVAL;
}
- set_mode_flags(_int); // Prevent compilation, which creates objects
// -Xshare:on
} else if (match_option(option, "-Xshare:on")) {
if (FLAG_SET_CMDLINE(bool, UseSharedSpaces, true) != JVMFlag::SUCCESS) {
@@ -2722,7 +2731,7 @@
if (FLAG_SET_CMDLINE(bool, RequireSharedSpaces, true) != JVMFlag::SUCCESS) {
return JNI_EINVAL;
}
- // -Xshare:auto
+ // -Xshare:auto || -XX:ArchiveClassesAtExit=<archive file>
} else if (match_option(option, "-Xshare:auto")) {
if (FLAG_SET_CMDLINE(bool, UseSharedSpaces, true) != JVMFlag::SUCCESS) {
return JNI_EINVAL;
@@ -3110,15 +3119,24 @@
// the archived Klasses and Java string objects (at dump time only).
UseBiasedLocking = false;
+ // Compiler threads may concurrently update the class metadata (such as method entries), so it's
+ // unsafe with DumpSharedSpaces (which modifies the class metadata in place). Let's disable
+ // compiler just to be safe.
+ //
+ // Note: this is not a concern for DynamicDumpSharedSpaces, which makes a copy of the class metadata
+ // instead of modifying them in place. The copy is inaccessible to the compiler.
+ // TODO: revisit the following for the static archive case.
+ set_mode_flags(_int);
+ }
+ if (DumpSharedSpaces || ArchiveClassesAtExit != NULL) {
// Always verify non-system classes during CDS dump
if (!BytecodeVerificationRemote) {
BytecodeVerificationRemote = true;
log_info(cds)("All non-system classes will be verified (-Xverify:remote) during CDS dump time.");
}
-
- // Compilation is already disabled if the user specifies -Xshare:dump.
- // Disable compilation in case user specifies -XX:+DumpSharedSpaces instead of -Xshare:dump.
- set_mode_flags(_int);
+ }
+ if (ArchiveClassesAtExit == NULL) {
+ FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, false);
}
if (UseSharedSpaces && patch_mod_javabase) {
no_shared_spaces("CDS is disabled when " JAVA_BASE_NAME " module is patched.");
@@ -3427,6 +3445,7 @@
}
}
+#if INCLUDE_CDS
// Sharing support
// Construct the path to the archive
char* Arguments::get_default_shared_archive_path() {
@@ -3446,15 +3465,104 @@
return default_archive_path;
}
-static char* get_shared_archive_path() {
- char *shared_archive_path;
+int Arguments::num_archives(const char* archive_path) {
+ if (archive_path == NULL) {
+ return 0;
+ }
+ int npaths = 1;
+ char* p = (char*)archive_path;
+ while (*p != '\0') {
+ if (*p == os::path_separator()[0]) {
+ npaths++;
+ }
+ p++;
+ }
+ return npaths;
+}
+
+void Arguments::extract_shared_archive_paths(const char* archive_path,
+ char** base_archive_path,
+ char** top_archive_path) {
+ char* begin_ptr = (char*)archive_path;
+ char* end_ptr = strchr((char*)archive_path, os::path_separator()[0]);
+ if (end_ptr == NULL || end_ptr == begin_ptr) {
+ vm_exit_during_initialization("Base archive was not specified", archive_path);
+ }
+ size_t len = end_ptr - begin_ptr;
+ char* cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
+ strncpy(cur_path, begin_ptr, len);
+ cur_path[len] = '\0';
+ FileMapInfo::check_archive((const char*)cur_path, true /*is_static*/);
+ *base_archive_path = cur_path;
+
+ begin_ptr = ++end_ptr;
+ if (*begin_ptr == '\0') {
+ vm_exit_during_initialization("Top archive was not specified", archive_path);
+ }
+ end_ptr = strchr(begin_ptr, '\0');
+ assert(end_ptr != NULL, "sanity");
+ len = end_ptr - begin_ptr;
+ cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
+ strncpy(cur_path, begin_ptr, len + 1);
+ //cur_path[len] = '\0';
+ FileMapInfo::check_archive((const char*)cur_path, false /*is_static*/);
+ *top_archive_path = cur_path;
+}
+
+bool Arguments::init_shared_archive_paths() {
+ if (ArchiveClassesAtExit != NULL) {
+ if (DumpSharedSpaces) {
+ vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump");
+ }
+ if (FLAG_SET_CMDLINE(bool, DynamicDumpSharedSpaces, true) != JVMFlag::SUCCESS) {
+ return false;
+ }
+ check_unsupported_dumping_properties();
+ SharedDynamicArchivePath = os::strdup_check_oom(ArchiveClassesAtExit, mtArguments);
+ }
if (SharedArchiveFile == NULL) {
- shared_archive_path = Arguments::get_default_shared_archive_path();
+ SharedArchivePath = get_default_shared_archive_path();
} else {
- shared_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments);
+ int archives = num_archives(SharedArchiveFile);
+ if (DynamicDumpSharedSpaces || DumpSharedSpaces) {
+ if (archives > 1) {
+ vm_exit_during_initialization(
+ "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping");
+ }
+ if (DynamicDumpSharedSpaces) {
+ if (FileMapInfo::same_files(SharedArchiveFile, ArchiveClassesAtExit)) {
+ vm_exit_during_initialization(
+ "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit",
+ SharedArchiveFile);
+ }
+ }
+ }
+ if (!DynamicDumpSharedSpaces && !DumpSharedSpaces){
+ if (archives > 2) {
+ vm_exit_during_initialization(
+ "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option");
+ }
+ if (archives == 1) {
+ char* temp_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments);
+ int name_size;
+ bool success =
+ FileMapInfo::get_base_archive_name_from_header(temp_archive_path, &name_size, &SharedArchivePath);
+ if (!success) {
+ SharedArchivePath = temp_archive_path;
+ } else {
+ SharedDynamicArchivePath = temp_archive_path;
+ }
+ } else {
+ extract_shared_archive_paths((const char*)SharedArchiveFile,
+ &SharedArchivePath, &SharedDynamicArchivePath);
+ }
+ } else { // CDS dumping
+ SharedArchivePath = os::strdup_check_oom(SharedArchiveFile, mtArguments);
+ }
}
- return shared_archive_path;
+ return (SharedArchivePath != NULL);
}
+#endif // INCLUDE_CDS
#ifndef PRODUCT
// Determine whether LogVMOutput should be implicitly turned on.
@@ -3786,11 +3894,12 @@
return result;
}
- // Call get_shared_archive_path() here, after possible SharedArchiveFile option got parsed.
- SharedArchivePath = get_shared_archive_path();
- if (SharedArchivePath == NULL) {
+#if INCLUDE_CDS
+ // Initialize shared archive paths which could include both base and dynamic archive paths
+ if (!init_shared_archive_paths()) {
return JNI_ENOMEM;
}
+#endif
// Delay warning until here so that we've had a chance to process
// the -XX:-PrintWarnings flag
--- a/src/hotspot/share/runtime/arguments.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/runtime/arguments.hpp Fri May 17 08:29:55 2019 -0700
@@ -484,6 +484,11 @@
static AliasedLoggingFlag catch_logging_aliases(const char* name, bool on);
static char* SharedArchivePath;
+ static char* SharedDynamicArchivePath;
+ static int num_archives(const char* archive_path) NOT_CDS_RETURN_(0);
+ static void extract_shared_archive_paths(const char* archive_path,
+ char** base_archive_path,
+ char** top_archive_path) NOT_CDS_RETURN;
public:
// Parses the arguments, first phase
@@ -563,6 +568,7 @@
static vfprintf_hook_t vfprintf_hook() { return _vfprintf_hook; }
static const char* GetSharedArchivePath() { return SharedArchivePath; }
+ static const char* GetSharedDynamicArchivePath() { return SharedDynamicArchivePath; }
// Java launcher properties
static void process_sun_java_launcher_properties(JavaVMInitArgs* args);
@@ -625,7 +631,8 @@
static char* get_appclasspath() { return _java_class_path->value(); }
static void fix_appclasspath();
- static char* get_default_shared_archive_path();
+ static char* get_default_shared_archive_path() NOT_CDS_RETURN_(NULL);
+ static bool init_shared_archive_paths() NOT_CDS_RETURN_(false);
// Operation modi
static Mode mode() { return _mode; }
--- a/src/hotspot/share/runtime/globals.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/runtime/globals.hpp Fri May 17 08:29:55 2019 -0700
@@ -2359,6 +2359,9 @@
"shared spaces, and dumps the shared spaces to a file to be " \
"used in future JVM runs") \
\
+ product(bool, DynamicDumpSharedSpaces, false, \
+ "Dynamic archive") \
+ \
product(bool, PrintSharedArchiveAndExit, false, \
"Print shared archive file contents") \
\
@@ -2476,6 +2479,9 @@
product(ccstr, SharedArchiveFile, NULL, \
"Override the default location of the CDS archive file") \
\
+ product(ccstr, ArchiveClassesAtExit, NULL, \
+ "The path and name of the dynamic archive file") \
+ \
product(ccstr, ExtraSharedClassListFile, NULL, \
"Extra classlist for building the CDS archive file") \
\
--- a/src/hotspot/share/runtime/java.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/runtime/java.cpp Fri May 17 08:29:55 2019 -0700
@@ -43,6 +43,7 @@
#include "logging/logStream.hpp"
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
+#include "memory/dynamicArchive.hpp"
#include "memory/universe.hpp"
#include "oops/constantPool.hpp"
#include "oops/generateOopMap.hpp"
@@ -498,6 +499,12 @@
// Note: we don't wait until it actually dies.
os::terminate_signal_thread();
+#if INCLUDE_CDS
+ if (DynamicDumpSharedSpaces) {
+ DynamicArchive::dump();
+ }
+#endif
+
print_statistics();
Universe::heap()->print_tracing_info();
--- a/src/hotspot/share/runtime/mutexLocker.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/runtime/mutexLocker.cpp Fri May 17 08:29:55 2019 -0700
@@ -153,9 +153,12 @@
#if INCLUDE_NMT
Mutex* NMTQuery_lock = NULL;
#endif
-#if INCLUDE_CDS && INCLUDE_JVMTI
+#if INCLUDE_CDS
+#if INCLUDE_JVMTI
Mutex* CDSClassFileStream_lock = NULL;
#endif
+Mutex* DumpTimeTable_lock = NULL;
+#endif // INCLUDE_CDS
#if INCLUDE_JVMCI
Monitor* JVMCI_lock = NULL;
@@ -351,7 +354,8 @@
#if INCLUDE_NMT
def(NMTQuery_lock , PaddedMutex , max_nonleaf, false, Monitor::_safepoint_check_always);
#endif
-#if INCLUDE_CDS && INCLUDE_JVMTI
+#if INCLUDE_CDS
+#if INCLUDE_JVMTI
def(CDSClassFileStream_lock , PaddedMutex , max_nonleaf, false, Monitor::_safepoint_check_always);
#endif
@@ -360,6 +364,8 @@
def(JVMCIGlobalAlloc_lock , PaddedMutex , nonleaf, true, Monitor::_safepoint_check_never);
def(JVMCIGlobalActive_lock , PaddedMutex , nonleaf-1, true, Monitor::_safepoint_check_never);
#endif
+ def(DumpTimeTable_lock , PaddedMutex , leaf, true, Monitor::_safepoint_check_never);
+#endif // INCLUDE_CDS
}
GCMutexLocker::GCMutexLocker(Monitor * mutex) {
--- a/src/hotspot/share/runtime/mutexLocker.hpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/runtime/mutexLocker.hpp Fri May 17 08:29:55 2019 -0700
@@ -132,9 +132,12 @@
#if INCLUDE_NMT
extern Mutex* NMTQuery_lock; // serialize NMT Dcmd queries
#endif
-#if INCLUDE_CDS && INCLUDE_JVMTI
+#if INCLUDE_CDS
+#if INCLUDE_JVMTI
extern Mutex* CDSClassFileStream_lock; // FileMapInfo::open_stream_for_jvmti
#endif
+extern Mutex* DumpTimeTable_lock; // SystemDictionaryShared::find_or_allocate_info_for
+#endif // INCLUDE_CDS
#if INCLUDE_JFR
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table
extern Monitor* JfrMsg_lock; // protects JFR messaging
--- a/src/hotspot/share/runtime/thread.cpp Fri May 17 10:48:02 2019 -0400
+++ b/src/hotspot/share/runtime/thread.cpp Fri May 17 08:29:55 2019 -0700
@@ -3964,10 +3964,8 @@
SystemDictionary::compute_java_loaders(CHECK_JNI_ERR);
#if INCLUDE_CDS
- if (DumpSharedSpaces) {
- // capture the module path info from the ModuleEntryTable
- ClassLoader::initialize_module_path(THREAD);
- }
+ // capture the module path info from the ModuleEntryTable
+ ClassLoader::initialize_module_path(THREAD);
#endif
#if INCLUDE_JVMCI
@@ -4169,7 +4167,7 @@
for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) {
// CDS dumping does not support native JVMTI agent.
// CDS dumping supports Java agent if the AllowArchivingWithJavaAgent diagnostic option is specified.
- if (DumpSharedSpaces) {
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
if(!agent->is_instrument_lib()) {
vm_exit_during_cds_dumping("CDS dumping does not support native JVMTI agent, name", agent->name());
} else if (!AllowArchivingWithJavaAgent) {
--- a/test/hotspot/jtreg/TEST.groups Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/TEST.groups Fri May 17 08:29:55 2019 -0700
@@ -314,6 +314,23 @@
hotspot_appcds = \
runtime/appcds/
+hotspot_appcds_dynamic = \
+ runtime/appcds/ \
+ -runtime/appcds/cacheObject \
+ -runtime/appcds/customLoader \
+ -runtime/appcds/dynamicArchive \
+ -runtime/appcds/javaldr/ArrayTest.java \
+ -runtime/appcds/javaldr/GCSharedStringsDuringDump.java \
+ -runtime/appcds/javaldr/HumongousDuringDump.java \
+ -runtime/appcds/sharedStrings \
+ -runtime/appcds/DumpClassList.java \
+ -runtime/appcds/ExtraSymbols.java \
+ -runtime/appcds/LongClassListPath.java \
+ -runtime/appcds/LotsOfClasses.java \
+ -runtime/appcds/SharedArchiveConsistency.java \
+ -runtime/appcds/UnusedCPDuringDump.java \
+ -runtime/appcds/VerifierTest_1B.java
+
# A subset of AppCDS tests to be run in tier1
tier1_runtime_appcds = \
runtime/appcds/HelloTest.java \
--- a/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -58,23 +58,26 @@
// FAIL: 2) runtime with classpath different from the one used in dump time
// (runtime has an extra jar file prepended to the class path)
TestCommon.run(
+ "-Xlog:cds",
"-cp", appJar2 + File.pathSeparator + appJar,
"HelloMore")
- .assertAbnormalExit(errorMessage1, errorMessage2);
+ .assertAbnormalExit(errorMessage1, errorMessage2);
// FAIL: 3) runtime with classpath part of the one used in dump time
TestCommon.testDump(appJar + File.pathSeparator + appJar2,
TestCommon.list("Hello"));
TestCommon.run(
+ "-Xlog:cds",
"-cp", appJar2,
"Hello")
- .assertAbnormalExit(errorMessage1, errorMessage2);
+ .assertAbnormalExit(errorMessage1, errorMessage2);
// FAIL: 4) runtime with same set of jar files in the classpath but
// with different order
TestCommon.run(
+ "-Xlog:cds",
"-cp", appJar2 + File.pathSeparator + appJar,
"HelloMore")
- .assertAbnormalExit(errorMessage1, errorMessage2);
+ .assertAbnormalExit(errorMessage1, errorMessage2);
}
}
--- a/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -55,7 +55,11 @@
test.testBootClassPathMismatch();
test.testBootClassPathMismatchWithAppClass();
test.testBootClassPathMismatchWithBadPath();
- test.testBootClassPathMatchWithAppend();
+ if (!TestCommon.isDynamicArchive()) {
+ // this test is not applicable to dynamic archive since
+ // there is no class to be archived in the top archive
+ test.testBootClassPathMatchWithAppend();
+ }
test.testBootClassPathMatch();
}
@@ -77,11 +81,13 @@
TestCommon.dump(appJar, appClasses, "-Xbootclasspath/a:" + appJar);
TestCommon.run(
+ "-Xlog:cds",
"-cp", appJar, "-Xbootclasspath/a:" + otherJar, "Hello")
.assertAbnormalExit(mismatchMessage);
TestCommon.dump(appJar, appClasses, "-Xbootclasspath/a:" + otherJar);
TestCommon.run(
+ "-Xlog:cds",
"-cp", appJar, "-Xbootclasspath/a:" + appJar, "Hello")
.assertAbnormalExit(mismatchMessage);
}
@@ -100,6 +106,7 @@
TestCommon.dump(appJar, appClasses, "-Xbootclasspath/a:" + appJar);
TestCommon.run(
+ "-Xlog:cds",
"-cp", appJar, "-Xbootclasspath/a:" + otherJar, "Hello")
.assertAbnormalExit(mismatchMessage);
}
@@ -148,6 +155,7 @@
String appClasses[] = {"Hello"};
TestCommon.dump(appJar, appClasses);
TestCommon.run(
+ "-Xlog:cds",
"-cp", appJar, "-Xbootclasspath/a:" + appJar, "Hello")
.assertAbnormalExit(mismatchMessage);
}
--- a/test/hotspot/jtreg/runtime/appcds/CDSandJFR.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/CDSandJFR.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -30,7 +30,7 @@
* @modules jdk.jfr
* @build Hello GetFlightRecorder
* @run driver ClassFileInstaller -jar CDSandJFR.jar Hello GetFlightRecorder GetFlightRecorder$TestEvent GetFlightRecorder$SimpleEvent
- * @run driver/timeout=500 CDSandJFR
+ * @run main/othervm/timeout=500 CDSandJFR
*/
import jdk.test.lib.BuildHelper;
--- a/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -78,8 +78,7 @@
}
boolean isSameFile = Files.isSameFile(jarPath, jarPathUpper);
- TestCommon.run("-cp", appJarUpper, "Hello", "-Xlog:class+path=info",
- "-Xlog:cds")
+ TestCommon.run("-Xlog:class+path=info,cds", "-cp", appJarUpper, "Hello")
.ifNoMappingFailure(output -> {
if (isSameFile) {
output.shouldContain("Hello World");
--- a/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -72,10 +72,22 @@
continue;
OutputAnalyzer dumpOutput = TestCommon.dump(appJar, classList, testEntry);
- TestCommon.checkDump(dumpOutput, "Loading classes to share");
+ if (!TestCommon.isDynamicArchive()) {
+ TestCommon.checkDump(dumpOutput, "Loading classes to share");
+ } else {
+ if (testEntry.contains("ObjectAlignmentInBytes")) {
+ dumpOutput.shouldHaveExitValue(1)
+ .shouldMatch("The shared archive file's ObjectAlignmentInBytes of .* does not equal the current ObjectAlignmentInBytes of");
+ } else {
+ TestCommon.checkDump(dumpOutput, "Loading classes to share");
+ }
+ }
- OutputAnalyzer execOutput = TestCommon.exec(appJar, testEntry, "Hello");
- TestCommon.checkExec(execOutput, "Hello World");
+ if ((TestCommon.isDynamicArchive() && !testEntry.contains("ObjectAlignmentInBytes")) ||
+ !TestCommon.isDynamicArchive()) {
+ OutputAnalyzer execOutput = TestCommon.exec(appJar, testEntry, "Hello");
+ TestCommon.checkExec(execOutput, "Hello World");
+ }
}
for (int i=0; i<2; i++) {
--- a/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -65,8 +65,10 @@
if (Platform.is64bit()) {
testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=8", "-XX:ObjectAlignmentInBytes=16",
"An error has occurred while processing the shared archive file", 1) );
- testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=64", "-XX:ObjectAlignmentInBytes=32",
- "An error has occurred while processing the shared archive file", 1) );
+ if (!TestCommon.isDynamicArchive()) {
+ testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=64", "-XX:ObjectAlignmentInBytes=32",
+ "An error has occurred while processing the shared archive file", 1) );
+ }
testTable.add( new TestVector("-XX:+UseCompressedOops", "-XX:-UseCompressedOops",
"Class data sharing is inconsistent with other specified options", 1) );
testTable.add( new TestVector("-XX:+UseCompressedClassPointers", "-XX:-UseCompressedClassPointers",
--- a/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -27,7 +27,9 @@
* @summary Handling of directories in -cp is based on the classlist
* @requires vm.cds
* @library /test/lib
+ * @modules jdk.jartool/sun.tools.jar
* @compile test-classes/Hello.java
+ * @compile test-classes/Super.java
* @run driver DirClasspathTest
*/
@@ -42,11 +44,19 @@
public class DirClasspathTest {
private static final int MAX_PATH = 260;
+ // We add helloJar into the classpath to be compatible with TestCommon.DYNAMIC_DUMP
+ static OutputAnalyzer doDump(String path, String classList[],
+ String... suffix) throws Exception {
+ String helloJar = JarBuilder.getOrCreateHelloJar();
+ return TestCommon.dump(helloJar + File.pathSeparator + path, classList, suffix);
+ }
+
public static void main(String[] args) throws Exception {
File dir = new File(System.getProperty("user.dir"));
File emptydir = new File(dir, "emptydir");
emptydir.mkdir();
+
/////////////////////////////////////////////////////////////////
// The classlist only contains boot class in following test cases
/////////////////////////////////////////////////////////////////
@@ -54,7 +64,7 @@
// Empty dir in -cp: should be OK
OutputAnalyzer output;
- output = TestCommon.dump(emptydir.getPath(), bootClassList, "-Xlog:class+path=info");
+ output = doDump(emptydir.getPath(), bootClassList, "-Xlog:class+path=info");
TestCommon.checkDump(output);
// Long path to empty dir in -cp: should be OK
@@ -71,17 +81,17 @@
longDir.mkdir();
File subDir = new File(longDir, "subdir");
subDir.mkdir();
- output = TestCommon.dump(subDir.getPath(), bootClassList, "-Xlog:class+path=info");
+ output = doDump(subDir.getPath(), bootClassList, "-Xlog:class+path=info");
TestCommon.checkDump(output);
// Non-empty dir in -cp: should be OK
// <dir> is not empty because it has at least one subdirectory, i.e., <emptydir>
- output = TestCommon.dump(dir.getPath(), bootClassList, "-Xlog:class+path=info");
+ output = doDump(dir.getPath(), bootClassList, "-Xlog:class+path=info");
TestCommon.checkDump(output);
// Long path to non-empty dir in -cp: should be OK
// <dir> is not empty because it has at least one subdirectory, i.e., <emptydir>
- output = TestCommon.dump(longDir.getPath(), bootClassList, "-Xlog:class+path=info");
+ output = doDump(longDir.getPath(), bootClassList, "-Xlog:class+path=info");
TestCommon.checkDump(output);
/////////////////////////////////////////////////////////////////
@@ -90,27 +100,27 @@
String appClassList[] = {"java/lang/Object", "com/sun/tools/javac/Main"};
// Non-empty dir in -cp: should be OK (as long as no classes were loaded from there)
- output = TestCommon.dump(dir.getPath(), appClassList, "-Xlog:class+path=info");
+ output = doDump(dir.getPath(), appClassList, "-Xlog:class+path=info");
TestCommon.checkDump(output);
// Long path to non-empty dir in -cp: should be OK (as long as no classes were loaded from there)
- output = TestCommon.dump(longDir.getPath(), appClassList, "-Xlog:class+path=info");
+ output = doDump(longDir.getPath(), appClassList, "-Xlog:class+path=info");
TestCommon.checkDump(output);
/////////////////////////////////////////////////////////////////
// Loading an app class from a directory
/////////////////////////////////////////////////////////////////
- String appClassList2[] = {"Hello", "java/lang/Object", "com/sun/tools/javac/Main"};
+ String appClassList2[] = {"Super", "java/lang/Object", "com/sun/tools/javac/Main"};
// Non-empty dir in -cp: should report error if a class is loaded from it
- output = TestCommon.dump(classDir.toString(), appClassList2, "-Xlog:class+path=info");
+ output = doDump(classDir.toString(), appClassList2, "-Xlog:class+path=info,class+load=trace");
output.shouldNotHaveExitValue(0);
output.shouldContain("Cannot have non-empty directory in paths");
// Long path to non-empty dir in -cp: should report error if a class is loaded from it
- File srcClass = new File(classDir.toFile(), "Hello.class");
- File destClass = new File(longDir, "Hello.class");
+ File srcClass = new File(classDir.toFile(), "Super.class");
+ File destClass = new File(longDir, "Super.class");
Files.copy(srcClass.toPath(), destClass.toPath());
- output = TestCommon.dump(longDir.getPath(), appClassList2, "-Xlog:class+path=info");
+ output = doDump(longDir.getPath(), appClassList2, "-Xlog:class+path=info");
output.shouldNotHaveExitValue(0);
output.shouldContain("Cannot have non-empty directory in paths");
}
--- a/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -84,7 +84,9 @@
run(check_appcds_enabled, appJar, "-Xlog:class+load", "JvmtiApp", "noadd"); // appcds should be enabled
System.out.println("Test case 2: add to boot classpath only - should find Hello.class in boot loader");
- run(check_appcds_disabled, appJar, "-Xlog:class+load", "JvmtiApp", "bootonly", addbootJar); // appcds should be disabled
+ String[] toCheck = (TestCommon.isDynamicArchive()) ? check_appcds_enabled
+ : check_appcds_disabled;
+ run(toCheck, appJar, "-Xlog:class+load", "JvmtiApp", "bootonly", addbootJar); // appcds should be disabled
System.out.println("Test case 3: add to app classpath only - should find Hello.class in app loader");
run(appJar, "JvmtiApp", "apponly", addappJar);
@@ -97,7 +99,11 @@
System.out.println("Test case 6: add to app using AppCDS, but add to boot using JVMTI - should find Hello.class in boot loader");
TestCommon.testDump(twoAppJars, TestCommon.list("JvmtiApp", "ExtraClass", "Hello"), use_whitebox_jar);
- run(twoAppJars, "JvmtiApp", "bootonly", addappJar);
+ if (!TestCommon.isDynamicArchive()) {
+ // skip for dynamic archive, the Hello class will be loaded from
+ // the dynamic archive
+ run(twoAppJars, "JvmtiApp", "bootonly", addappJar);
+ }
System.out.println("Test case 7: add to app using AppCDS, no JVMTI calls - should find Hello.class in app loader");
run(twoAppJars, "JvmtiApp", "noadd-appcds");
--- a/test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -22,15 +22,7 @@
*
*/
-import java.net.URI;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.cds.CDSOptions;
@@ -41,16 +33,15 @@
* @summary Try to archive lots of classes by searching for classes from the jrt:/ file system. With JDK 12
* this will produce an archive with over 30,000 classes.
* @requires vm.cds
- * @library /test/lib
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
* @run driver/timeout=500 LotsOfClasses
*/
public class LotsOfClasses {
- static Pattern pattern;
public static void main(String[] args) throws Throwable {
ArrayList<String> list = new ArrayList<>();
- findAllClasses(list);
+ TestCommon.findAllClasses(list);
CDSOptions opts = new CDSOptions();
opts.setClassList(list);
@@ -64,28 +55,4 @@
OutputAnalyzer out = CDSTestUtils.createArchive(opts);
CDSTestUtils.checkDump(out);
}
-
- static void findAllClasses(ArrayList<String> list) throws Throwable {
- // Find all the classes in the jrt file system
- pattern = Pattern.compile("/modules/[a-z.]*[a-z]+/([^-]*)[.]class");
- FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
- Path base = fs.getPath("/modules/");
- find(base, list);
- }
-
- static void find(Path p, ArrayList<String> list) throws Throwable {
- try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
- for (Path entry: stream) {
- Matcher matcher = pattern.matcher(entry.toString());
- if (matcher.find()) {
- String className = matcher.group(1);
- list.add(className);
- //System.out.println(className);
- }
- try {
- find(entry, list);
- } catch (Throwable t) {}
- }
- }
- }
}
--- a/test/hotspot/jtreg/runtime/appcds/PackageSealing.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/PackageSealing.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -27,14 +27,15 @@
* @summary AppCDS handling of package.
* @requires vm.cds
* @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
+ * @modules jdk.jartool/sun.tools.jar
* @compile test-classes/C1.java
* @compile test-classes/C2.java
* @compile test-classes/PackageSealingTest.java
+ * @compile test-classes/Hello.java
* @run driver PackageSealing
*/
+import java.io.File;
import jdk.test.lib.process.OutputAnalyzer;
public class PackageSealing {
@@ -44,16 +45,19 @@
ClassFileInstaller.Manifest.fromSourceFile("test-classes/package_seal.mf"),
"PackageSealingTest", "sealed/pkg/C1", "pkg/C2");
+ String helloJar = JarBuilder.getOrCreateHelloJar();
+ String jars = helloJar + File.pathSeparator + appJar;
+
// test shared package from -cp path
- TestCommon.testDump(appJar, TestCommon.list(classList));
+ TestCommon.testDump(jars, TestCommon.list(classList));
OutputAnalyzer output;
- output = TestCommon.exec(appJar, "PackageSealingTest");
+ output = TestCommon.exec(jars, "PackageSealingTest");
TestCommon.checkExec(output, "OK");
// test shared package from -Xbootclasspath/a
- TestCommon.dump(appJar, TestCommon.list(classList),
+ TestCommon.dump(helloJar, TestCommon.list(classList),
"-Xbootclasspath/a:" + appJar);
- output = TestCommon.exec(appJar, "-Xbootclasspath/a:" + appJar, "PackageSealingTest");
+ output = TestCommon.exec(helloJar, "-Xbootclasspath/a:" + appJar, "PackageSealingTest");
TestCommon.checkExec(output, "OK");
}
}
--- a/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -27,9 +27,7 @@
* @summary AppCDS handling of prohibited package.
* @requires vm.cds
* @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool/sun.tools.jar
* @compile test-classes/ProhibitedHelper.java test-classes/Prohibited.jasm
* @run driver ProhibitedPackage
*/
@@ -46,7 +44,8 @@
String appJar = TestCommon.getTestJar("prohibited_pkg.jar");
// Test support for customer loaders
- if (Platform.areCustomLoadersSupportedForCDS()) {
+ if (Platform.areCustomLoadersSupportedForCDS() &&
+ !TestCommon.isDynamicArchive()) {
String classlist[] = new String[] {
"java/lang/Object id: 1",
"java/lang/Prohibited id: 2 super: 1 source: " + appJar
--- a/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java Fri May 17 08:29:55 2019 -0700
@@ -71,7 +71,18 @@
public static File jsa; // will be updated during test
public static File orgJsaFile; // kept the original file not touched.
- public static String[] shared_region_name = {"MiscCode", "ReadWrite", "ReadOnly", "MiscData"};
+ // The following should be consistent with the enum in the C++ MetaspaceShared class
+ public static String[] shared_region_name = {
+ "mc", // MiscCode
+ "rw", // ReadWrite
+ "ro", // ReadOnly
+ "md", // MiscData
+ "first_closed_archive",
+ "last_closed_archive",
+ "first_open_archive",
+ "last_open_archive"
+ };
+
public static int num_regions = shared_region_name.length;
public static String[] matchMessages = {
"Unable to use shared archive",
@@ -102,10 +113,11 @@
return file_header_size;
}
// this is not real header size, it is struct size
+ int int_size = wb.getOffsetForName("int_size");
file_header_size = wb.getOffsetForName("file_header_size");
int offset_path_misc_info = wb.getOffsetForName("FileMapHeader::_paths_misc_info_size") -
offset_magic;
- int path_misc_info_size = (int)readInt(fc, offset_path_misc_info, size_t_size);
+ int path_misc_info_size = (int)readInt(fc, offset_path_misc_info, int_size);
file_header_size += path_misc_info_size; //readInt(fc, offset_path_misc_info, size_t_size);
System.out.println("offset_path_misc_info = " + offset_path_misc_info);
System.out.println("path_misc_info_size = " + path_misc_info_size);
@@ -157,25 +169,26 @@
public static void modifyJsaContentRandomly() throws Exception {
FileChannel fc = getFileChannel();
- // corrupt random area in the data areas (MiscCode, ReadWrite, ReadOnly, MiscData)
+ // corrupt random area in the data areas
long[] used = new long[num_regions]; // record used bytes
long start0, start, end, off;
int used_offset, path_info_size;
int bufSize;
- System.out.printf("%-12s%-12s%-12s%-12s%-12s\n", "Space Name", "Offset", "Used bytes", "Reg Start", "Random Offset");
+ System.out.printf("%-24s%12s%12s%16s\n", "Space Name", "Used bytes", "Reg Start", "Random Offset");
start0 = getFileHeaderSize(fc);
for (int i = 0; i < num_regions; i++) {
- used_offset = sp_offset + CDSFileMapRegion_size * i + sp_used_offset;
- // read 'used'
- used[i] = readInt(fc, used_offset, size_t_size);
+ used[i] = get_region_used_size_aligned(fc, i);
start = start0;
for (int j = 0; j < i; j++) {
start += align_up_page(used[j]);
}
end = start + used[i];
+ if (start == end) {
+ continue; // Ignore empty regions
+ }
off = getRandomBetween(start, end);
- System.out.printf("%-12s%-12d%-12d%-12d%-12d\n", shared_region_name[i], used_offset, used[i], start, off);
+ System.out.printf("%-24s%12d%12d%16d\n", shared_region_name[i], used[i], start, off);
if (end - off < 1024) {
bufSize = (int)(end - off + 1);
} else {
@@ -189,34 +202,50 @@
}
}
- public static void modifyJsaContent() throws Exception {
+ static long get_region_used_size_aligned(FileChannel fc, int region) throws Exception {
+ long n = sp_offset + CDSFileMapRegion_size * region + sp_used_offset;
+ long alignment = WhiteBox.getWhiteBox().metaspaceReserveAlignment();
+ long used = readInt(fc, n, size_t_size);
+ used = (used + alignment - 1) & ~(alignment - 1);
+ return used;
+ }
+
+ public static boolean modifyJsaContent(int region) throws Exception {
FileChannel fc = getFileChannel();
byte[] buf = new byte[4096];
ByteBuffer bbuf = ByteBuffer.wrap(buf);
long total = 0L;
- long used_offset = 0L;
long[] used = new long[num_regions];
- System.out.printf("%-12s%-12s\n", "Space name", "Used bytes");
+ System.out.printf("%-24s%12s\n", "Space name", "Used bytes");
for (int i = 0; i < num_regions; i++) {
- used_offset = sp_offset + CDSFileMapRegion_size* i + sp_used_offset;
- // read 'used'
- used[i] = readInt(fc, used_offset, size_t_size);
- System.out.printf("%-12s%-12d\n", shared_region_name[i], used[i]);
+ used[i] = get_region_used_size_aligned(fc, i);
+ System.out.printf("%-24s%12d\n", shared_region_name[i], used[i]);
total += used[i];
}
- System.out.printf("%-12s%-12d\n", "Total: ", total);
- long corrupt_used_offset = getFileHeaderSize(fc);
- System.out.println("Corrupt RO section, offset = " + corrupt_used_offset);
- while (used_offset < used[0]) {
- writeData(fc, corrupt_used_offset, bbuf);
+ System.out.printf("%-24s%12d\n", "Total: ", total);
+ long header_size = getFileHeaderSize(fc);
+ long region_start_offset = header_size;
+ for (int i=0; i<region; i++) {
+ region_start_offset += used[i];
+ }
+ if (used[region] == 0) {
+ System.out.println("Region " + shared_region_name[region] + " is empty. Nothing to corrupt.");
+ return false;
+ }
+ System.out.println("Corrupt " + shared_region_name[region] + " section, start = " + region_start_offset
+ + " (header_size + 0x" + Long.toHexString(region_start_offset-header_size) + ")");
+ long bytes_written = 0L;
+ while (bytes_written < used[region]) {
+ writeData(fc, region_start_offset + bytes_written, bbuf);
bbuf.clear();
- used_offset += 4096;
+ bytes_written += 4096;
}
fc.force(true);
if (fc.isOpen()) {
fc.close();
}
+ return true;
}
public static void modifyJsaHeader() throws Exception {
@@ -299,11 +328,11 @@
// read the jsa file
// 1) run normal
// 2) modify header
- // 3) keep header correct but modify content
+ // 3) keep header correct but modify content in each region specified by shared_region_name[]
// 4) update both header and content, test
// 5) delete bytes in data begining
// 6) insert bytes in data begining
- // 7) randomly corrupt data in four areas: RO, RW. MISC DATA, MISC CODE
+ // 7) randomly corrupt data in each region specified by shared_region_name[]
public static void main(String... args) throws Exception {
// must call to get offset info first!!!
getFileOffsetInfo();
@@ -352,18 +381,23 @@
output.shouldContain("The shared archive file has the wrong version");
output.shouldNotContain("Checksum verification failed");
+ File newJsaFile = null;
// modify content
System.out.println("\n3. Corrupt Content, should fail\n");
-
- copyFile(orgJsaFile, jsa);
- modifyJsaContent();
- testAndCheck(verifyExecArgs);
+ for (int i=0; i<num_regions; i++) {
+ newJsaFile = new File(TestCommon.getNewArchiveName(shared_region_name[i]));
+ copyFile(orgJsaFile, newJsaFile);
+ if (modifyJsaContent(i)) {
+ testAndCheck(execArgs);
+ }
+ }
// modify both header and content, test should fail
System.out.println("\n4. Corrupt Header and Content, should fail\n");
- copyFile(orgJsaFile, jsa);
+ newJsaFile = new File(TestCommon.getNewArchiveName("header-and-content"));
+ copyFile(orgJsaFile, newJsaFile);
modifyJsaHeader();
- modifyJsaContent(); // this will not be reached since failed on header change first
+ modifyJsaContent(0); // this will not be reached since failed on header change first
output = TestCommon.execCommon(execArgs);
output.shouldContain("The shared archive file has the wrong version");
output.shouldNotContain("Checksum verification failed");
@@ -379,7 +413,8 @@
testAndCheck(verifyExecArgs);
System.out.println("\n7. modify Content in random areas, should fail\n");
- copyFile(orgJsaFile, jsa);
+ newJsaFile = new File(TestCommon.getNewArchiveName("random-areas"));
+ copyFile(orgJsaFile, newJsaFile);
modifyJsaContentRandomly();
testAndCheck(verifyExecArgs);
}
--- a/test/hotspot/jtreg/runtime/appcds/TestCommon.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/TestCommon.java Fri May 17 08:29:55 2019 -0700
@@ -32,10 +32,28 @@
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Date;
+import java.util.Enumeration;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+import jtreg.SkippedException;
+import cdsutils.DynamicDumpHelper;
+
/**
* This is a test utility class for common AppCDS test functionality.
@@ -51,7 +69,7 @@
*/
public class TestCommon extends CDSTestUtils {
private static final String JSA_FILE_PREFIX = System.getProperty("user.dir") +
- File.separator + "appcds-";
+ File.separator;
private static final SimpleDateFormat timeStampFormat =
new SimpleDateFormat("HH'h'mm'm'ss's'SSS");
@@ -64,8 +82,7 @@
// Call this method to start new archive with new unique name
public static void startNewArchiveName() {
deletePriorArchives();
- currentArchiveName = JSA_FILE_PREFIX +
- timeStampFormat.format(new Date()) + ".jsa";
+ currentArchiveName = getNewArchiveName();
}
// Call this method to get current archive name
@@ -73,6 +90,18 @@
return currentArchiveName;
}
+ public static String getNewArchiveName() {
+ return getNewArchiveName(null);
+ }
+
+ public static String getNewArchiveName(String stem) {
+ if (stem == null) {
+ stem = "appcds";
+ }
+ return JSA_FILE_PREFIX + stem + "-" +
+ timeStampFormat.format(new Date()) + ".jsa";
+ }
+
// Attempt to clean old archives to preserve space
// Archives are large artifacts (20Mb or more), and much larger than
// most other artifacts created in jtreg testing.
@@ -92,7 +121,6 @@
}
}
-
// Create AppCDS archive using most common args - convenience method
// Legacy name preserved for compatibility
public static OutputAnalyzer dump(String appJar, String classList[],
@@ -110,10 +138,12 @@
return createArchive(opts);
}
+ // Simulate -Xshare:dump with -XX:ArchiveClassesAtExit. See comments around patchJarForDynamicDump()
+ private static final Class tmp = DynamicDumpHelper.class;
+
// Create AppCDS archive using appcds options
public static OutputAnalyzer createArchive(AppCDSOptions opts)
throws Exception {
-
ArrayList<String> cmd = new ArrayList<String>();
startNewArchiveName();
@@ -122,23 +152,73 @@
if (opts.appJar != null) {
cmd.add("-cp");
cmd.add(opts.appJar);
+ File jf = new File(opts.appJar);
+ if (DYNAMIC_DUMP && !jf.isDirectory()) {
+ patchJarForDynamicDump(opts.appJar);
+ }
} else {
cmd.add("-Djava.class.path=");
}
- cmd.add("-Xshare:dump");
-
- if (opts.archiveName == null)
+ if (opts.archiveName == null) {
opts.archiveName = getCurrentArchiveName();
-
- cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
-
- if (opts.classList != null) {
- File classListFile = makeClassList(opts.classList);
- cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath());
}
- for (String s : opts.suffix) cmd.add(s);
+ if (DYNAMIC_DUMP) {
+ cmd.add("-Xshare:on");
+ cmd.add("-XX:ArchiveClassesAtExit=" + opts.archiveName);
+
+ cmd.add("-Xlog:cds");
+ cmd.add("-Xlog:cds+dynamic");
+ boolean mainModuleSpecified = false;
+ boolean patchModuleSpecified = false;
+ for (String s : opts.suffix) {
+ if (s.length() == 0) {
+ continue;
+ }
+ if (s.equals("-m")) {
+ mainModuleSpecified = true;
+ }
+ if (s.startsWith("--patch-module=")) {
+ patchModuleSpecified = true;
+ }
+ cmd.add(s);
+ }
+
+ if (opts.appJar != null) {
+ // classlist is supported only when we have a Jar file to patch (to insert
+ // cdsutils.DynamicDumpHelper)
+ if (opts.classList == null) {
+ throw new RuntimeException("test.dynamic.dump requires classList file");
+ }
+
+ if (!mainModuleSpecified && !patchModuleSpecified) {
+ cmd.add("cdsutils.DynamicDumpHelper");
+ File classListFile = makeClassList(opts.classList);
+ cmd.add(classListFile.getPath());
+ }
+ } else {
+ if (!mainModuleSpecified && !patchModuleSpecified) {
+ // If you have an empty classpath, you cannot specify a classlist!
+ if (opts.classList != null && opts.classList.length > 0) {
+ throw new RuntimeException("test.dynamic.dump not supported empty classpath with non-empty classlist");
+ }
+ cmd.add("-version");
+ }
+ }
+ } else {
+ // static dump
+ cmd.add("-Xshare:dump");
+ cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
+
+ if (opts.classList != null) {
+ File classListFile = makeClassList(opts.classList);
+ cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath());
+ }
+ for (String s : opts.suffix) {
+ cmd.add(s);
+ }
+ }
String[] cmdLine = cmd.toArray(new String[cmd.size()]);
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
@@ -154,6 +234,93 @@
// Some AppCDS tests are not compatible with this mode. See the group
// hotspot_appcds_with_jfr in ../../TEST.ROOT for details.
private static final boolean RUN_WITH_JFR = Boolean.getBoolean("test.cds.run.with.jfr");
+ // This method simulates -Xshare:dump with -XX:ArchiveClassesAtExit. This way, we
+ // can re-use many tests (outside of the ./dynamicArchive directory) for testing
+ // general features of JDK-8215311 (JEP 350: Dynamic CDS Archives).
+ //
+ // We insert the cdsutils/DynamicDumpHelper.class into the first Jar file in
+ // the classpath. We use this class to load all the classes specified in the classlist.
+ //
+ // There's no need to change the run-time command-line: in this special mode, two
+ // archives are involved. The command-line specifies only the top archive. However,
+ // the location of the base archive is recorded in the top archive, so it can be
+ // determined by the JVM at runtime start-up.
+ //
+ // To run in this special mode, specify the following in your jtreg command-line
+ // -Dtest.dynamic.cds.archive=true
+ //
+ // Note that some tests are not compatible with this special mode, including
+ // + Tests in ./dynamicArchive: these tests are specifically written for
+ // dynamic archive, and do not use TestCommon.createArchive(), which works
+ // together with patchJarForDynamicDump().
+ // + Tests related to cached objects and shared strings: dynamic dumping
+ // does not support these.
+ // + Custom loader tests: DynamicDumpHelper doesn't support the required
+ // classlist syntax. (FIXME).
+ // + Extra symbols and extra strings.
+ // See the hotspot_appcds_dynamic in ../../TEST.ROOT for details.
+ //
+ // To run all tests that are compatible with this mode:
+ // cd test/hotspot/jtreg
+ // jtreg -Dtest.dynamic.cds.archive=true :hotspot_appcds_dynamic
+ //
+ private static void patchJarForDynamicDump(String cp) throws Exception {
+ System.out.println("patchJarForDynamicDump: classpath = " + cp);
+ String firstJar = cp;
+ int n = firstJar.indexOf(File.pathSeparator);
+ if (n > 0) {
+ firstJar = firstJar.substring(0, n);
+ }
+ String classDir = System.getProperty("test.classes");
+ String expected1 = classDir + File.separator;
+ String expected2 = System.getProperty("user.dir") + File.separator;
+
+ if (!firstJar.startsWith(expected1) && !firstJar.startsWith(expected2)) {
+ throw new RuntimeException("FIXME: jar file not at a supported location ('"
+ + expected1 + "', or '" + expected2 + "'): " + firstJar);
+ }
+
+ String replaceJar = firstJar + ".tmp";
+ String patchClass = "cdsutils/DynamicDumpHelper.class";
+ ZipFile zipFile = new ZipFile(firstJar);
+ byte[] buf = new byte[1024];
+ int len;
+ if (zipFile.getEntry(patchClass) == null) {
+ FileOutputStream fout = new FileOutputStream(replaceJar);
+ final ZipOutputStream zos = new ZipOutputStream(fout);
+
+ zos.putNextEntry(new ZipEntry(patchClass));
+ InputStream is = new FileInputStream(classDir + File.separator + patchClass);
+ while ((len = (is.read(buf))) > 0) {
+ zos.write(buf, 0, len);
+ }
+ zos.closeEntry();
+ is.close();
+
+ for (Enumeration e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry entryIn = (ZipEntry) e.nextElement();
+ zos.putNextEntry(entryIn);
+ is = zipFile.getInputStream(entryIn);
+ while ((len = is.read(buf)) > 0) {
+ zos.write(buf, 0, len);
+ }
+ zos.closeEntry();
+ is.close();
+ }
+
+ zos.close();
+ fout.close();
+ zipFile.close();
+
+ File oldFile = new File(firstJar);
+ File newFile = new File(replaceJar);
+ oldFile.delete();
+ newFile.renameTo(oldFile);
+ System.out.println("firstJar = " + firstJar + " Modified");
+ } else {
+ System.out.println("firstJar = " + firstJar);
+ }
+ }
// Execute JVM using AppCDS archive with specified AppCDSOptions
public static OutputAnalyzer runWithArchive(AppCDSOptions opts)
@@ -260,12 +427,18 @@
return runWithArchive(opts);
}
-
// A common operation: dump, then check results
public static OutputAnalyzer testDump(String appJar, String classList[],
String... suffix) throws Exception {
OutputAnalyzer output = dump(appJar, classList, suffix);
- output.shouldContain("Loading classes to share");
+ if (DYNAMIC_DUMP) {
+ if (isUnableToMap(output)) {
+ throw new SkippedException(UnableToMapMsg);
+ }
+ output.shouldContain("Written dynamic archive");
+ } else {
+ output.shouldContain("Loading classes to share");
+ }
output.shouldHaveExitValue(0);
return output;
}
@@ -301,7 +474,6 @@
return output;
}
-
// Convenience concatenation utils
public static String[] list(String ...args) {
return args;
@@ -336,6 +508,15 @@
return list.toArray(new String[list.size()]);
}
+ public static String[] concat(String prefix, String[] extra) {
+ ArrayList<String> list = new ArrayList<String>();
+ list.add(prefix);
+ for (String s : extra) {
+ list.add(s);
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
// ===================== Concatenate paths
public static String concatPaths(String... paths) {
@@ -384,4 +565,29 @@
}
return true;
}
+
+ static Pattern pattern;
+
+ static void findAllClasses(ArrayList<String> list) throws Throwable {
+ // Find all the classes in the jrt file system
+ pattern = Pattern.compile("/modules/[a-z.]*[a-z]+/([^-]*)[.]class");
+ FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+ Path base = fs.getPath("/modules/");
+ findAllClassesAtPath(base, list);
+ }
+
+ private static void findAllClassesAtPath(Path p, ArrayList<String> list) throws Throwable {
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
+ for (Path entry: stream) {
+ Matcher matcher = pattern.matcher(entry.toString());
+ if (matcher.find()) {
+ String className = matcher.group(1);
+ list.add(className);
+ }
+ try {
+ findAllClassesAtPath(entry, list);
+ } catch (Throwable t) {}
+ }
+ }
+ }
}
--- a/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -31,6 +31,7 @@
* java.management
* jdk.jartool/sun.tools.jar
* @compile test-classes/Hello.java
+ * @compile test-classes/Super.java
* @run driver TraceLongClasspath
*/
@@ -43,8 +44,10 @@
public static void main(String[] args) throws Exception {
String appJar = JarBuilder.getOrCreateHelloJar();
+ String dummyJar = JarBuilder.build("dummy", "Super");
String longClassPath =
+ dummyJar + ps +
"/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/user-patch.jar" + ps +
"/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/abc-startup.jar" + ps +
"/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/features/com.foobar.db.jdbc7-dms.jar" + ps +
@@ -91,15 +94,15 @@
// Then try to execute the archive with a different classpath and with -XX:+TraceClassPaths.
// The diagnosis "expecting" app classpath trace should show the entire classpath.
TestCommon.run(
- "-XX:+TraceClassPaths",
+ "-XX:+TraceClassPaths", "-Xlog:cds",
"-cp", appJar,
"Hello")
- .assertAbnormalExit(output -> {
- output.shouldContain("Unable to use shared archive");
- output.shouldContain("shared class paths mismatch");
- // the "expecting" app classpath from -XX:+TraceClassPaths should not
- // be truncated
- output.shouldContain(myCP);
- });
+ .assertAbnormalExit(output -> {
+ output.shouldContain("Unable to use shared archive");
+ output.shouldContain("shared class paths mismatch");
+ // the "expecting" app classpath from -XX:+TraceClassPaths should not
+ // be truncated
+ output.shouldContain(myCP);
+ });
}
}
--- a/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -47,8 +47,9 @@
// Then try to execute the archive without -classpath -- it should fail
TestCommon.run(
/* "-cp", appJar, */ // <- uncomment this and the execution should succeed
+ "-Xlog:cds",
"Hello")
- .assertAbnormalExit("Unable to use shared archive",
- "shared class paths mismatch");
+ .assertAbnormalExit("Unable to use shared archive",
+ "shared class paths mismatch");
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/cdsutils/DynamicDumpHelper.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+package cdsutils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+/**
+ * This class is used to simulate -Xshare:dump with -XX:ArchiveClassesAtExit.
+ * It loads all classes specified in a classlist file. See patchJarForDynamicDump()
+ * in ../TestCommon.java for details.
+ */
+public class DynamicDumpHelper {
+ public static void main(String args[]) throws Throwable {
+ File file = new File(args[0]);
+
+ System.out.println("Loading classes to share...");
+ try (BufferedReader br = new BufferedReader(new FileReader(file))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ //System.out.println("Loading class: " + line);
+ line = line.replace('/', '.');
+ try {
+ Class.forName(line);
+ } catch (java.lang.ClassNotFoundException ex) {
+ try {
+ Class.forName(line, true, null);
+ } catch (java.lang.ClassNotFoundException cnfe) {
+ System.out.println("Preload Warning: Cannot find " + line.replace('.', '/'));
+ }
+ } catch (Throwable t) {
+ System.out.println("Error: failed to load \"" + line + "\": " + t);
+ }
+ }
+ }
+ System.out.println("Loading classes to share: done.");
+ }
+}
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -32,7 +32,7 @@
* @modules java.base/jdk.internal.misc
* java.management
* jdk.jartool/sun.tools.jar
- * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ * @compile ../test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
* test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
* @run driver ClassListFormatA
*/
@@ -133,4 +133,3 @@
"input line too long (must be no longer than " + _max_allowed_line + " chars");
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -32,7 +32,7 @@
* @modules java.base/jdk.internal.misc
* java.management
* jdk.jartool/sun.tools.jar
- * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ * @compile ../test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
* test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
* @run driver ClassListFormatB
*/
@@ -69,4 +69,3 @@
"If source location is specified, id must be also specified");
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java Fri May 17 08:29:55 2019 -0700
@@ -79,4 +79,3 @@
return TestCommon.list(args);
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -32,7 +32,7 @@
* @modules java.base/jdk.internal.misc
* java.management
* jdk.jartool/sun.tools.jar
- * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ * @compile ../test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
* test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
* @run driver ClassListFormatC
*/
@@ -71,4 +71,3 @@
"If source location is not specified, interface(s) must not be specified");
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -32,7 +32,7 @@
* @modules java.base/jdk.internal.misc
* java.management
* jdk.jartool/sun.tools.jar
- * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ * @compile ../test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
* test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
* @run driver ClassListFormatD
*/
@@ -80,4 +80,3 @@
"Interface id 2 is not yet loaded");
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -32,7 +32,7 @@
* @modules java.base/jdk.internal.misc
* java.management
* jdk.jartool/sun.tools.jar
- * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ * @compile ../test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
* test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
* @run driver ClassListFormatE
*/
@@ -106,4 +106,3 @@
"The specified super class CustomLoadee (id 4) does not match actual super class java.lang.Object");
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java Fri May 17 08:29:55 2019 -0700
@@ -27,10 +27,12 @@
* @summary Hello World test for AppCDS custom loader support
* @requires vm.cds
* @requires vm.cds.custom.loaders
- * @library /test/lib /test/hotspot/jtreg/runtime/appcds
- * @compile test-classes/Hello.java test-classes/CustomLoadee.java
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller -jar hello.jar Hello
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /runtime/testlibrary
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * @compile test-classes/HelloUnload.java test-classes/CustomLoadee.java
+ * @build sun.hotspot.WhiteBox ClassUnloadCommon
+ * @run driver ClassFileInstaller -jar hello.jar HelloUnload ClassUnloadCommon ClassUnloadCommon$1 ClassUnloadCommon$TestFailure
* @run driver ClassFileInstaller -jar hello_custom.jar CustomLoadee
* @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
* @run driver HelloCustom
@@ -52,7 +54,7 @@
// Dump the archive
String classlist[] = new String[] {
- "Hello",
+ "HelloUnload",
"java/lang/Object id: 1",
"CustomLoadee id: 2 super: 1 source: " + customJarPath
};
@@ -68,8 +70,7 @@
use_whitebox_jar,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
- "Hello", customJarPath));
+ "HelloUnload", customJarPath, "true", "true"));
TestCommon.checkExec(output);
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom_JFR.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom_JFR.java Fri May 17 08:29:55 2019 -0700
@@ -30,10 +30,10 @@
* @requires vm.hasJFR
* @requires vm.cds
* @requires vm.cds.custom.loaders
- * @library /test/lib /test/hotspot/jtreg/runtime/appcds
- * @compile test-classes/Hello.java test-classes/CustomLoadee.java
- * @build sun.hotspot.WhiteBox
- * @run driver ClassFileInstaller -jar hello.jar Hello
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /runtime/testlibrary
+ * @compile test-classes/HelloUnload.java test-classes/CustomLoadee.java
+ * @build sun.hotspot.WhiteBox ClassUnloadCommon
+ * @run driver ClassFileInstaller -jar hello.jar HelloUnload ClassUnloadCommon ClassUnloadCommon$1 ClassUnloadCommon$TestFailure
* @run driver ClassFileInstaller -jar hello_custom.jar CustomLoadee
* @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
* @run driver HelloCustom_JFR
@@ -47,4 +47,3 @@
HelloCustom.run("-XX:StartFlightRecording=dumponexit=true", "-Xlog:cds+jvmti=debug");
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -31,7 +31,7 @@
* @modules java.base/jdk.internal.misc
* java.management
* jdk.jartool/sun.tools.jar
- * @compile ClassListFormatBase.java test-classes/Hello.java test-classes/InProhibitedPkg.java
+ * @compile ClassListFormatBase.java ../test-classes/Hello.java test-classes/InProhibitedPkg.java
* @run driver ProhibitedPackageNamesTest
*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/HelloUnload.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import sun.hotspot.WhiteBox;
+
+public class HelloUnload {
+ private static String className = "CustomLoadee";
+
+ public static void main(String args[]) throws Exception {
+ if (args.length != 3) {
+ throw new RuntimeException("Unexpected number of arguments: expected 3, actual " + args.length);
+ }
+
+ String path = args[0];
+ URL url = new File(path).toURI().toURL();
+ URL[] urls = new URL[] {url};
+ System.out.println(path);
+ System.out.println(url);
+
+ // unload the custom class loader
+ boolean doUnload = false;
+ if (args[1].equals("true")) {
+ doUnload = true;
+ } else if (args[1].equals("false")) {
+ doUnload = false;
+ } else {
+ throw new RuntimeException("args[1] can only be either \"true\" or \"false\", actual " + args[1]);
+ }
+
+ // should the CustomLoadee class be in the shared archive
+ boolean inArchive = false;
+ if (args[2].equals("true")) {
+ inArchive = true;
+ } else if (args[2].equals("false")) {
+ inArchive = false;
+ } else {
+ throw new RuntimeException("args[2] can only be either \"true\" or \"false\", actual " + args[1]);
+ }
+
+ URLClassLoader urlClassLoader =
+ new URLClassLoader("HelloClassLoader", urls, null);
+ Class c = Class.forName(className, true, urlClassLoader);
+ System.out.println(c);
+ System.out.println(c.getClassLoader());
+ Object o = c.newInstance();
+
+ // [1] Check that CustomLoadee is defined by the correct loader
+ if (c.getClassLoader() != urlClassLoader) {
+ throw new RuntimeException("c.getClassLoader() == " + c.getClassLoader() +
+ ", expected == " + urlClassLoader);
+ }
+
+ // [2] Check that CustomLoadee is loaded from shared archive.
+ WhiteBox wb = WhiteBox.getWhiteBox();
+ if(wb.isSharedClass(HelloUnload.class)) {
+ if (inArchive && !wb.isSharedClass(c)) {
+ throw new RuntimeException("wb.isSharedClass(c) should be true");
+ }
+ }
+
+ ClassUnloadCommon.failIf(!wb.isClassAlive(className), "should be live here");
+
+ if (doUnload) {
+ String loaderName = urlClassLoader.getName();
+ int loadedRefcount = wb.getSymbolRefcount(loaderName);
+ System.out.println("Refcount of symbol " + loaderName + " is " + loadedRefcount);
+
+ urlClassLoader = null; c = null; o = null;
+ ClassUnloadCommon.triggerUnloading();
+ System.out.println("Is CustomLoadee alive? " + wb.isClassAlive(className));
+ ClassUnloadCommon.failIf(wb.isClassAlive(className), "should have been unloaded");
+
+ int unloadedRefcount = wb.getSymbolRefcount(loaderName);
+ System.out.println("Refcount of symbol " + loaderName + " is " + unloadedRefcount);
+
+ // refcount of a permanent symbol will not be decremented
+ if (loadedRefcount != 65535) {
+ ClassUnloadCommon.failIf(unloadedRefcount != (loadedRefcount - 1), "Refcount must be decremented");
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/AppendClasspath.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019, 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 At run time, it is OK to append new elements to the classpath that was used at dump time.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/Hello.java
+ * @compile ../test-classes/HelloMore.java
+ * @run driver AppendClasspath
+ */
+
+import java.io.File;
+
+public class AppendClasspath extends DynamicArchiveTestBase {
+
+ public static void main(String[] args) throws Exception {
+ runTest(AppendClasspath::testDefaultBase);
+ }
+
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ doTest(topArchiveName);
+ }
+
+ private static void doTest(String topArchiveName) throws Exception {
+ String appJar = JarBuilder.getOrCreateHelloJar();
+ String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", "HelloMore");
+
+ // Dump an archive with a specified JAR file in -classpath
+ dump(topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, "Hello")
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+
+ // runtime with classpath containing the one used in dump time,
+ // i.e. the dump time classpath is a prefix of the runtime classpath.
+ run(topArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar + File.pathSeparator + appJar2,
+ "HelloMore")
+ .assertNormalExit(output -> {
+ output.shouldContain("Hello source: shared objects file")
+ .shouldContain("Hello World ... More")
+ .shouldHaveExitValue(0);
+ });
+
+ // reverse the order of the 2 jar files so that the dump time classpath
+ // is no longer a prefix of the runtime classpath. The Hello class
+ // should be loaded from the jar file.
+ run(topArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar2 + File.pathSeparator + appJar,
+ "HelloMore")
+ .assertAbnormalExit(output -> {
+ output.shouldContain("shared class paths mismatch")
+ .shouldHaveExitValue(1);
+ });
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/ArchiveConsistency.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2019, 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 Corrupt the header CRC fields of the top archive. VM should exit with an error.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @build Hello sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar hello.jar Hello
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ArchiveConsistency
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.nio.file.StandardOpenOption;
+import static java.nio.file.StandardOpenOption.READ;
+import static java.nio.file.StandardOpenOption.WRITE;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import sun.hotspot.WhiteBox;
+
+public class ArchiveConsistency extends DynamicArchiveTestBase {
+ public static WhiteBox wb;
+ public static int int_size; // size of int
+ public static String[] shared_region_name = {"MiscCode", "ReadWrite", "ReadOnly", "MiscData"};
+ public static int num_regions = shared_region_name.length;
+
+ public static void main(String[] args) throws Exception {
+ runTest(ArchiveConsistency::testCustomBase);
+ }
+
+ // Test with custom base archive + top archive
+ static void testCustomBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top2");
+ String baseArchiveName = getNewArchiveName("base");
+ dumpBaseArchive(baseArchiveName);
+ doTest(baseArchiveName, topArchiveName);
+ }
+
+ public static void setReadWritePermission(File file) throws Exception {
+ if (!file.canRead()) {
+ if (!file.setReadable(true)) {
+ throw new IOException("Cannot modify file " + file + " as readable");
+ }
+ }
+ if (!file.canWrite()) {
+ if (!file.setWritable(true)) {
+ throw new IOException("Cannot modify file " + file + " as writable");
+ }
+ }
+ }
+
+ public static long readInt(FileChannel fc, long offset, int nbytes) throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(nbytes);
+ bb.order(ByteOrder.nativeOrder());
+ fc.position(offset);
+ fc.read(bb);
+ return (nbytes > 4 ? bb.getLong(0) : bb.getInt(0));
+ }
+
+ public static long align_up_page(long l) throws Exception {
+ // wb is obtained in getFileOffsetInfo() which is called first in main() else we should call
+ // WhiteBox.getWhiteBox() here first.
+ int pageSize = wb.getVMPageSize();
+ return (l + pageSize -1) & (~ (pageSize - 1));
+ }
+
+ public static void writeData(FileChannel fc, long offset, ByteBuffer bb) throws Exception {
+ fc.position(offset);
+ fc.write(bb);
+ fc.force(true);
+ }
+
+ public static FileChannel getFileChannel(File jsa) throws Exception {
+ List<StandardOpenOption> arry = new ArrayList<StandardOpenOption>();
+ arry.add(READ);
+ arry.add(WRITE);
+ return FileChannel.open(jsa.toPath(), new HashSet<StandardOpenOption>(arry));
+ }
+
+ public static void modifyJsaHeaderCRC(File jsa) throws Exception {
+ FileChannel fc = getFileChannel(jsa);
+ int_size = wb.getOffsetForName("int_size");
+ System.out.println(" int_size " + int_size);
+ ByteBuffer bbuf = ByteBuffer.allocateDirect(int_size);
+ for (int i = 0; i < int_size; i++) {
+ bbuf.put((byte)0);
+ }
+
+ int baseArchiveCRCOffset = wb.getOffsetForName("DynamicArchiveHeader::_base_archive_crc");
+ int crc = 0;
+ System.out.printf("%-12s%-12s\n", "Space name", "CRC");
+ for (int i = 0; i < 4; i++) {
+ baseArchiveCRCOffset += int_size * i;
+ System.out.println(" baseArchiveCRCOffset " + baseArchiveCRCOffset);
+ crc = (int)readInt(fc, baseArchiveCRCOffset, int_size );
+ System.out.printf("%-11s%-12d\n", shared_region_name[i], crc);
+ bbuf.rewind();
+ writeData(fc, baseArchiveCRCOffset, bbuf);
+ }
+ fc.force(true);
+ if (fc.isOpen()) {
+ fc.close();
+ }
+ }
+
+ private static void doTest(String baseArchiveName, String topArchiveName) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("hello.jar");
+ String mainClass = "Hello";
+ dump2(baseArchiveName, topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+
+ File jsa = new File(topArchiveName);
+ if (!jsa.exists()) {
+ throw new IOException(jsa + " does not exist!");
+ }
+
+ // Modify the CRC values in the header of the top archive.
+ wb = WhiteBox.getWhiteBox();
+ setReadWritePermission(jsa);
+ modifyJsaHeaderCRC(jsa);
+
+ run2(baseArchiveName, topArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-XX:+VerifySharedSpaces",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldContain("Header checksum verification failed")
+ .shouldContain("Unable to use shared archive")
+ .shouldHaveExitValue(1);
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/ArrayKlasses.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019, 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 handling of the existence of InstanceKlass::array_klasses()
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * /test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes
+ * @build ArrayKlassesApp
+ * @run driver ClassFileInstaller -jar ArrayKlasses.jar
+ * ArrayKlassesApp
+ * @run driver ArrayKlasses
+ */
+
+public class ArrayKlasses extends DynamicArchiveTestBase {
+ public static void main(String[] args) throws Exception {
+ runTest(ArrayKlasses::test);
+ }
+
+ static void test() throws Exception {
+ String topArchiveName = getNewArchiveName();
+ String appJar = ClassFileInstaller.getJarPath("ArrayKlasses.jar");
+ String mainClass = "ArrayKlassesApp";
+
+ dumpAndRun(topArchiveName, "-Xlog:cds+dynamic=debug", "-cp", appJar, mainClass);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/ClassResolutionFailure.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019, 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 with a jar file which contains only the main class but not the dependent class.
+ * The main class should be archived. During run time, the main class
+ * should be loaded from the archive.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes
+ * @build StrConcatApp
+ * @build MissingDependent
+ * @run driver ClassFileInstaller -jar missingDependent.jar MissingDependent
+ * @run driver ClassResolutionFailure
+ */
+
+public class ClassResolutionFailure extends DynamicArchiveTestBase {
+
+ public static void main(String[] args) throws Exception {
+ runTest(ClassResolutionFailure::testDefaultBase);
+ }
+
+ // Test with default base archive + top archive
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ doTest(topArchiveName);
+ }
+
+ private static void doTest(String topArchiveName) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("missingDependent.jar");
+ String mainClass = "MissingDependent";
+
+ dump(topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-Xlog:class+load=trace",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+
+ run(topArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("MissingDependent source: shared objects file")
+ .shouldHaveExitValue(0);
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/DynamicArchiveTestBase.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.cds.CDSTestUtils.Result;
+
+/**
+ * Base class for test cases in test/hotspot/jtreg/runtime/appcds/dynamicArchive/
+ */
+class DynamicArchiveTestBase {
+ private static boolean executedIn_run = false;
+
+ public static interface DynamicArchiveTest {
+ public void run() throws Exception;
+ }
+
+ public static interface DynamicArchiveTestWithArgs {
+ public void run(String args[]) throws Exception;
+ }
+
+
+ /*
+ * Tests for dynamic archives should be written using this pattern:
+ *
+ * public class HelloDynamic extends DynamicArchiveTestBase {
+ * public static void main(String[] args) throws Exception {
+ * runTest(HelloDynamic::testDefaultBase); // launch one test case
+ * }
+ *
+ * // the body of a test case
+ * static void testDefaultBase() throws Exception {
+ * String topArchiveName = getNewArchiveName("top");
+ * doTest(null, topArchiveName);
+ * }
+ * }
+ *
+ * The reason for this is so that we can clean up the archive files
+ * created by prior test cases. Otherwise tests with lots of
+ * test cases may fill up the scratch directory.
+ */
+ public static void runTest(DynamicArchiveTest t) throws Exception {
+ executedIn_run = true;
+ try {
+ TestCommon.deletePriorArchives();
+ t.run();
+ } finally {
+ executedIn_run = false;
+ }
+ }
+
+ public static void runTest(DynamicArchiveTestWithArgs t, String... args) throws Exception {
+ executedIn_run = true;
+ try {
+ TestCommon.deletePriorArchives();
+ t.run(args);
+ } finally {
+ executedIn_run = false;
+ }
+ }
+
+ public static String getNewArchiveName() {
+ return TestCommon.getNewArchiveName();
+ }
+ public static String getNewArchiveName(String stem) {
+ return TestCommon.getNewArchiveName(stem);
+ }
+
+ /**
+ * 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
+ * (give by topArchiveName).
+ *
+ * If baseArchiveName is null, use the JDK's default archive as the base archive.
+ */
+ public static Result dump2(String baseArchiveName, String topArchiveName, String ... cmdLineSuffix)
+ throws Exception
+ {
+ String[] cmdLine = TestCommon.concat(
+ "-XX:ArchiveClassesAtExit=" + topArchiveName);
+ // to allow dynamic archive tests to be run in the "rt-non-cds-mode"
+ cmdLine = TestCommon.concat(cmdLine, "-Xshare:auto");
+ if (baseArchiveName != null) {
+ cmdLine = TestCommon.concat(cmdLine, "-XX:SharedArchiveFile=" + baseArchiveName);
+ }
+ cmdLine = TestCommon.concat(cmdLine, cmdLineSuffix);
+ return execProcess("dump", cmdLine);
+ }
+
+ public static Result dump2_WB(String baseArchiveName, String topArchiveName, String ... cmdLineSuffix)
+ throws Exception
+ {
+ return dump2(baseArchiveName, topArchiveName,
+ TestCommon.concat(wbRuntimeArgs(), cmdLineSuffix));
+ }
+
+ /**
+ * A convenience method similar to dump2, but always use the JDK's default archive
+ * as the base archive.
+ *
+ * Most dynamicArchive/*.java test cases should be using this method instead of run2.
+ */
+ public static Result dump(String topArchiveName, String ... cmdLineSuffix)
+ throws Exception
+ {
+ return dump2(null, topArchiveName, cmdLineSuffix);
+ }
+
+ /**
+ * Dump the base archive. The JDK's default class list is used (unless otherwise specified
+ * in cmdLineSuffix).
+ */
+ public static void dumpBaseArchive(String baseArchiveName, String ... cmdLineSuffix)
+ throws Exception
+ {
+ CDSOptions opts = new CDSOptions();
+ opts.setArchiveName(baseArchiveName);
+ opts.addSuffix(cmdLineSuffix);
+ opts.addSuffix("-Djava.class.path=");
+ OutputAnalyzer out = CDSTestUtils.createArchive(opts);
+ CDSTestUtils.checkDump(out);
+ }
+
+ /**
+ * Same as dumpBaseArchive, but also add WhiteBox to the bootcp
+ */
+ public static void dumpBaseArchive_WB(String baseArchiveName, String ... cmdLineSuffix)
+ throws Exception
+ {
+ dumpBaseArchive(baseArchiveName,
+ TestCommon.concat("-Xbootclasspath/a:" + getWhiteBoxJar(), cmdLineSuffix));
+ }
+
+ private static String getWhiteBoxJar() {
+ String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
+ if (!(new File(wbJar)).exists()) {
+ throw new RuntimeException("Test error: your test must have " +
+ "'@run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox'");
+ }
+ return wbJar;
+ }
+
+ private static String[] wbRuntimeArgs() {
+ return TestCommon.concat("-Xbootclasspath/a:" + getWhiteBoxJar(),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI");
+ }
+
+ /**
+ * Execute a JVM using the base archive (given by baseArchiveName) and the top archive
+ * (give by topArchiveName), using the command line (given by cmdLineSuffix).
+ *
+ * If baseArchiveName is null, use the JDK's default archive as the base archive.
+ */
+ public static Result run2(String baseArchiveName, String topArchiveName, String ... cmdLineSuffix)
+ throws Exception {
+ if (baseArchiveName == null && topArchiveName == null) {
+ throw new RuntimeException("Both baseArchiveName and topArchiveName cannot be null at the same time.");
+ }
+ String archiveFiles = (baseArchiveName == null) ? topArchiveName :
+ (topArchiveName == null) ? baseArchiveName :
+ baseArchiveName + File.pathSeparator + topArchiveName;
+ String[] cmdLine = TestCommon.concat(
+ "-Xshare:on",
+ "-XX:SharedArchiveFile=" + archiveFiles);
+ cmdLine = TestCommon.concat(cmdLine, cmdLineSuffix);
+ return execProcess("exec", cmdLine);
+ }
+
+ public static Result run2_WB(String baseArchiveName, String topArchiveName, String ... cmdLineSuffix)
+ throws Exception
+ {
+ return run2(baseArchiveName, topArchiveName,
+ TestCommon.concat(wbRuntimeArgs(), cmdLineSuffix));
+ }
+
+ /**
+ * A convenience method similar to run2, but always use the JDK's default archive
+ * as the base archive.
+ *
+ * Most dynamicArchive/*.java test cases should be using this method instead of run2.
+ */
+ public static Result run(String topArchiveName, String ... cmdLineSuffix)
+ throws Exception
+ {
+ return run2(null, topArchiveName, cmdLineSuffix);
+ }
+
+ private static String getXshareMode(String[] cmdLine) {
+ for (int i = 0; i <= cmdLine.length - 1; i++) {
+ int j = cmdLine[i].indexOf("-Xshare:");
+ if (j != -1) {
+ return (cmdLine[i].substring(j));
+ }
+ }
+ return null;
+ }
+
+
+ private static Result execProcess(String mode, String[] cmdLine) throws Exception {
+ if (!executedIn_run) {
+ throw new Exception("Test error: dynamic archive tests must be executed via DynamicArchiveTestBase.run()");
+ }
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
+ OutputAnalyzer output = TestCommon.executeAndLog(pb, mode);
+ CDSOptions opts = new CDSOptions();
+ String xShareMode = getXshareMode(cmdLine);
+ if (xShareMode != null) {
+ opts.setXShareMode(xShareMode);
+ }
+ return new Result(opts, output);
+ }
+
+ /**
+ * A convenience method for dumping and running, using the default CDS archive from the
+ * JDK. Both dumping and running should exit normally.
+ */
+ public static void dumpAndRun(String topArchiveName, String ... cmdLineSuffix) throws Exception {
+ dump(topArchiveName, cmdLineSuffix).assertNormalExit();
+ run(topArchiveName, cmdLineSuffix).assertNormalExit();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/DynamicFlag.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019, 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 The DynamicDumpShareSpaces flag is internal, setting it at the command line should have no effect.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/Hello.java
+ * @run driver DynamicFlag
+ */
+
+public class DynamicFlag {
+ public static void main(String[] args) throws Exception {
+ TestCommon.test(JarBuilder.getOrCreateHelloJar(),
+ TestCommon.list("Hello"), "-XX:+DynamicDumpSharedSpaces", "Hello");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/DynamicLotsOfClasses.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.ArrayList;
+
+/*
+ * @test
+ * @summary Try to archive lots of classes by searching for classes from the jrt:/ file system. With JDK 12
+ * this will produce an archive with over 30,000 classes.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes
+ * @build LoadClasses
+ * @build sun.hotspot.WhiteBox
+ * @modules jdk.jartool/sun.tools.jar
+ * @run driver ClassFileInstaller -jar loadclasses.jar LoadClasses
+ * @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox
+ * @run driver/timeout=500 DynamicLotsOfClasses
+ */
+
+public class DynamicLotsOfClasses extends DynamicArchiveTestBase {
+
+ public static void main(String[] args) throws Throwable {
+ runTest(DynamicLotsOfClasses::testDefaultBase);
+ }
+
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ try {
+ doTest(topArchiveName);
+ } catch (Throwable th) {
+ System.out.println(th.toString());
+ Exception ex = new Exception(th);
+ throw ex;
+ }
+ }
+
+ private static void doTest(String topArchiveName) throws Throwable {
+ ArrayList<String> list = new ArrayList<>();
+ TestCommon.findAllClasses(list);
+
+ String classList = System.getProperty("user.dir") + File.separator +
+ "LotsOfClasses.list";
+ List<String> lines = list;
+ Path file = Paths.get(classList);
+ Files.write(file, lines, Charset.forName("UTF-8"));
+
+ String appJar = ClassFileInstaller.getJarPath("loadclasses.jar");
+ String mainClass = "LoadClasses";
+
+ String whiteBoxJar = ClassFileInstaller.getJarPath("whitebox.jar");
+ String bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar;
+ dump(topArchiveName,
+ "--add-modules",
+ "ALL-SYSTEM",
+ "-Xlog:hashtables",
+ "-Xmx500m",
+ "-Xlog:cds,cds+dynamic",
+ bootClassPath,
+ "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+ "-cp", appJar, mainClass, classList)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/ExcludedClasses.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019, 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 Unreferenced app classes during dump time should not be included in the archive.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * /test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes
+ * @build ExcludedClassesApp
+ * @run driver ClassFileInstaller -jar ExcludedClasses.jar
+ * ExcludedClassesApp
+ * ExcludedClassesApp$NotLinkedSuper
+ * ExcludedClassesApp$NotLinkedChild
+ * ExcludedClassesApp$NotLinkedInterface
+ * @run driver ExcludedClasses
+ */
+
+public class ExcludedClasses extends DynamicArchiveTestBase {
+ public static void main(String[] args) throws Exception {
+ runTest(ExcludedClasses::test);
+ }
+
+ static void test() throws Exception {
+ String topArchiveName = getNewArchiveName();
+ String appJar = ClassFileInstaller.getJarPath("ExcludedClasses.jar");
+ String mainClass = "ExcludedClassesApp";
+
+ dumpAndRun(topArchiveName, "-Xlog:cds+dynamic=debug", "-cp", appJar, mainClass);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/HelloDynamic.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019, 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 Hello World test for dynamic archive
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @build Hello
+ * @run driver ClassFileInstaller -jar hello.jar Hello
+ * @run driver HelloDynamic
+ */
+
+public class HelloDynamic extends DynamicArchiveTestBase {
+ public static void main(String[] args) throws Exception {
+ runTest(HelloDynamic::testDefaultBase);
+ runTest(HelloDynamic::testCustomBase);
+ }
+
+ // (1) Test with default base archive + top archive
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ doTest(null, topArchiveName);
+ }
+
+ // (2) Test with custom base archive + top archive
+ static void testCustomBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top2");
+ String baseArchiveName = getNewArchiveName("base");
+ dumpBaseArchive(baseArchiveName);
+ doTest(baseArchiveName, topArchiveName);
+ }
+
+ private static void doTest(String baseArchiveName, String topArchiveName) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("hello.jar");
+ String mainClass = "Hello";
+ dump2(baseArchiveName, topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+ run2(baseArchiveName, topArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("Hello source: shared objects file")
+ .shouldHaveExitValue(0);
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/HelloDynamicCustom.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019, 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 Hello World test for dynamic archive with custom loader
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/customLoader/test-classes /runtime/testlibrary
+ * @build HelloUnload CustomLoadee ClassUnloadCommon
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar hello.jar HelloUnload ClassUnloadCommon ClassUnloadCommon$1 ClassUnloadCommon$TestFailure
+ * @run driver ClassFileInstaller -jar hello_custom.jar CustomLoadee
+ * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run driver HelloDynamicCustom
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class HelloDynamicCustom {
+ private static final String ARCHIVE_NAME =
+ System.getProperty("test.classes") + File.separator + "HelloDynamicCustom.jsa";
+
+ public static void main(String[] args) throws Exception {
+ String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
+ String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+ String appJar = ClassFileInstaller.getJarPath("hello.jar");
+ String customJarPath = ClassFileInstaller.getJarPath("hello_custom.jar");
+ String mainAppClass = "HelloUnload";
+
+ ProcessBuilder dumpPb = ProcessTools.createJavaProcessBuilder(true,
+ use_whitebox_jar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-Xshare:auto",
+ "-XX:ArchiveClassesAtExit=" + ARCHIVE_NAME,
+ "-cp", appJar,
+ mainAppClass, customJarPath, "false", "false");
+ TestCommon.executeAndLog(dumpPb, "dump")
+ .shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x")
+ .shouldNotContain("klasses.*=.*CustomLoadee") // Fixme -- use a better way to decide if a class has been archived
+ .shouldHaveExitValue(0);
+
+ ProcessBuilder execPb = ProcessTools.createJavaProcessBuilder(true,
+ use_whitebox_jar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-Xlog:class+load",
+ "-Xlog:cds=debug",
+ "-Xlog:cds+dynamic=info",
+ "-Xshare:auto",
+ "-XX:SharedArchiveFile=" + ARCHIVE_NAME,
+ "-cp", appJar,
+ mainAppClass, customJarPath, "false", "true");
+ TestCommon.executeAndLog(execPb, "exec")
+ .shouldContain("HelloUnload source: shared objects file")
+ .shouldContain("CustomLoadee source: shared objects file")
+ .shouldHaveExitValue(0);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/HelloDynamicCustomUnload.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019, 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 Hello World test for dynamic archive with custom loader.
+ * Attempt will be made to unload the custom loader during
+ * dump time and run time. The custom loader will be unloaded
+ * during dump time.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/customLoader/test-classes /runtime/testlibrary
+ * @build HelloUnload CustomLoadee ClassUnloadCommon
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar hello.jar HelloUnload ClassUnloadCommon ClassUnloadCommon$1 ClassUnloadCommon$TestFailure
+ * @run driver ClassFileInstaller -jar hello_custom.jar CustomLoadee
+ * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run driver HelloDynamicCustomUnload
+ */
+
+import java.io.File;
+
+public class HelloDynamicCustomUnload extends DynamicArchiveTestBase {
+ private static final String ARCHIVE_NAME =
+ System.getProperty("test.classes") + File.separator + "HelloDynamicCustomUnload.jsa";
+
+ public static void main(String[] args) throws Exception {
+ runTest(HelloDynamicCustomUnload::testDefaultBase);
+ }
+
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("HelloDynamicCustomUnload-top");
+ doTest(topArchiveName);
+ }
+
+ private static void doTest(String topArchiveName) throws Exception {
+ String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
+ String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+ String appJar = ClassFileInstaller.getJarPath("hello.jar");
+ String customJarPath = ClassFileInstaller.getJarPath("hello_custom.jar");
+ String mainAppClass = "HelloUnload";
+
+ dump(topArchiveName,
+ use_whitebox_jar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-Xmn8m",
+ "-Xlog:cds,cds+dynamic=debug,class+unload=debug",
+ "-XX:ArchiveClassesAtExit=" + ARCHIVE_NAME,
+ "-cp", appJar,
+ mainAppClass, customJarPath, "true", "false")
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x")
+ .shouldNotContain("klasses.*=.*CustomLoadee") // Fixme -- use a better way to decide if a class has been archived
+ .shouldHaveExitValue(0);
+ });
+
+ run(topArchiveName,
+ use_whitebox_jar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-Xlog:class+load,cds=debug,class+unload=debug",
+ "-XX:SharedArchiveFile=" + ARCHIVE_NAME,
+ "-cp", appJar,
+ mainAppClass, customJarPath, "true", "false")
+ .assertNormalExit(output -> {
+ output.shouldContain("HelloUnload source: shared objects file")
+ .shouldMatch(".class.load. CustomLoadee source:.*hello_custom.jar")
+ .shouldHaveExitValue(0);
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/JITInteraction.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019, 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 interaction with JIT threads during vm exit.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes
+ * @build TestJIT
+ * @modules jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar testjit.jar TestJIT
+ * @run driver JITInteraction
+ */
+
+public class JITInteraction extends DynamicArchiveTestBase {
+
+ public static void main(String[] args) throws Exception {
+ runTest(JITInteraction::testDefaultBase);
+ }
+
+ // Test with default base archive + top archive
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ doTest(topArchiveName);
+ }
+
+ private static void doTest(String topArchiveName) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("testjit.jar");
+ String mainClass = "TestJIT";
+
+ dump2_WB(null, topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic",
+ "-XX:-UseOnStackReplacement",
+ "-XX:+PrintCompilation",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/MainModuleOnly.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2019, 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
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules jdk.compiler
+ * jdk.jartool/sun.tools.jar
+ * jdk.jlink
+ * @run driver MainModuleOnly
+ * @summary Test some scenarios with a main modular jar specified in the --module-path and -cp options in the command line.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Platform;
+
+public class MainModuleOnly extends DynamicArchiveTestBase {
+
+ private static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
+
+ private static final String FS = File.separator;
+ private static final String TEST_SRC = System.getProperty("test.src") +
+ FS + ".." + FS + "jigsaw" + FS + "modulepath";
+
+ private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+ private static final Path MODS_DIR = Paths.get("mods");
+
+ // the module name of the test module
+ private static final String TEST_MODULE1 = "com.simple";
+
+ // the module main class
+ private static final String MAIN_CLASS = "com.simple.Main";
+
+ private static Path moduleDir = null;
+ private static Path moduleDir2 = null;
+ private static Path destJar = null;
+
+ public static void buildTestModule() throws Exception {
+
+ // javac -d mods/$TESTMODULE --module-path MOD_DIR src/$TESTMODULE/**
+ JarBuilder.compileModule(SRC_DIR.resolve(TEST_MODULE1),
+ MODS_DIR.resolve(TEST_MODULE1),
+ MODS_DIR.toString());
+
+
+ moduleDir = Files.createTempDirectory(USER_DIR, "mlib");
+ moduleDir2 = Files.createTempDirectory(USER_DIR, "mlib2");
+
+ Path srcJar = moduleDir.resolve(TEST_MODULE1 + ".jar");
+ destJar = moduleDir2.resolve(TEST_MODULE1 + ".jar");
+ String classes = MODS_DIR.resolve(TEST_MODULE1).toString();
+ JarBuilder.createModularJar(srcJar.toString(), classes, MAIN_CLASS);
+ Files.copy(srcJar, destJar);
+
+ }
+
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ doTest(topArchiveName);
+ }
+
+ public static void main(String... args) throws Exception {
+ runTest(MainModuleOnly::testDefaultBase);
+ }
+
+ public static void doTest(String topArchiveName) throws Exception {
+ // compile the modules and create the modular jar files
+ buildTestModule();
+ // create an archive with both -cp and --module-path in the command line.
+ // Only the class in the modular jar in the --module-path will be archived;
+ // the class in the modular jar in the -cp won't be archived.
+ dump2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", destJar.toString(),
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE1)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+
+ // run with the archive using the same command line as in dump time.
+ // The main class should be loaded from the archive.
+ run2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug,class+load=trace",
+ "-cp", destJar.toString(),
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE1)
+ .assertNormalExit(output -> {
+ output.shouldContain("[class,load] com.simple.Main source: shared objects file")
+ .shouldHaveExitValue(0);
+ });
+
+ // run with the archive with the main class name inserted before the -m.
+ // The main class name will be picked up before the module name. So the
+ // main class should be loaded from the jar in the -cp.
+ run2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug,class+load=trace",
+ "-cp", destJar.toString(),
+ "--module-path", moduleDir.toString(),
+ MAIN_CLASS, "-m", TEST_MODULE1)
+ .assertNormalExit(out ->
+ out.shouldMatch(".class.load. com.simple.Main source:.*com.simple.jar"));
+
+ // run with the archive with exploded module. Since during dump time, we
+ // only archive classes from the modular jar in the --module-path, the
+ // main class should be loaded from the exploded module directory.
+ run2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug,class+load=trace",
+ "-cp", destJar.toString(),
+ "--module-path", MODS_DIR.toString(),
+ "-m", TEST_MODULE1 + "/" + MAIN_CLASS)
+ .assertNormalExit(out -> {
+ out.shouldMatch(".class.load. com.simple.Main source:.*com.simple")
+ .shouldContain(MODS_DIR.toString());
+ });
+
+ // run with the archive with the --upgrade-module-path option.
+ // CDS will be disabled with this options and the main class will be
+ // loaded from the modular jar.
+ run2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug,class+load=trace",
+ "-cp", destJar.toString(),
+ "--upgrade-module-path", moduleDir.toString(),
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE1)
+ .assertSilentlyDisabledCDS(out -> {
+ out.shouldHaveExitValue(0)
+ .shouldMatch("CDS is disabled when the.*option is specified")
+ .shouldMatch(".class.load. com.simple.Main source:.*com.simple.jar");
+ });
+ // run with the archive with the --limit-modules option.
+ // CDS will be disabled with this options and the main class will be
+ // loaded from the modular jar.
+ run2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug,class+load=trace",
+ "-cp", destJar.toString(),
+ "--limit-modules", "java.base," + TEST_MODULE1,
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE1)
+ .assertSilentlyDisabledCDS(out -> {
+ out.shouldHaveExitValue(0)
+ .shouldMatch("CDS is disabled when the.*option is specified")
+ .shouldMatch(".class.load. com.simple.Main source:.*com.simple.jar");
+ });
+ // run with the archive with the --patch-module option.
+ // CDS will be disabled with this options and the main class will be
+ // loaded from the modular jar.
+ run2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug,class+load=trace",
+ "-cp", destJar.toString(),
+ "--patch-module", TEST_MODULE1 + "=" + MODS_DIR.toString(),
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE1)
+ .assertSilentlyDisabledCDS(out -> {
+ out.shouldHaveExitValue(0)
+ .shouldMatch("CDS is disabled when the.*option is specified")
+ .shouldMatch(".class.load. com.simple.Main source:.*com.simple.jar");
+ });
+ // modify the timestamp of the jar file
+ (new File(destJar.toString())).setLastModified(System.currentTimeMillis() + 2000);
+ // run with the archive and the jar with modified timestamp.
+ // It should fail due to timestamp of the jar doesn't match the one
+ // used during dump time.
+ run2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug,class+load=trace",
+ "-cp", destJar.toString(),
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE1)
+ .assertAbnormalExit(
+ "A jar file is not the one used while building the shared archive file:");
+ // create an archive with a non-empty directory in the --module-path.
+ // The dumping process will exit with an error due to non-empty directory
+ // in the --module-path.
+ dump2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", destJar.toString(),
+ "--module-path", MODS_DIR.toString(),
+ "-m", TEST_MODULE1 + "/" + MAIN_CLASS)
+ .assertAbnormalExit(output -> {
+ output.shouldMatch("Error: non-empty directory.*com.simple");
+ });
+
+ // test module path with very long length
+ //
+ // This test can't be run on the windows platform due to an existing
+ // issue in ClassLoader::get_canonical_path() (JDK-8190737).
+ if (Platform.isWindows()) {
+ System.out.println("Long module path test cannot be tested on the Windows platform.");
+ return;
+ }
+ Path longDir = USER_DIR;
+ int pathLen = longDir.toString().length();
+ int PATH_LEN = 2034;
+ int MAX_DIR_LEN = 250;
+ while (pathLen < PATH_LEN) {
+ int remaining = PATH_LEN - pathLen;
+ int subPathLen = remaining > MAX_DIR_LEN ? MAX_DIR_LEN : remaining;
+ char[] chars = new char[subPathLen];
+ Arrays.fill(chars, 'x');
+ String subPath = new String(chars);
+ longDir = Paths.get(longDir.toString(), subPath);
+ pathLen = longDir.toString().length();
+ }
+ File longDirFile = new File(longDir.toString());
+ try {
+ longDirFile.mkdirs();
+ } catch (Exception e) {
+ throw e;
+ }
+ Path longDirJar = longDir.resolve(TEST_MODULE1 + ".jar");
+ // IOException results from the Files.copy() call on platform
+ // such as MacOS X. Test can't be proceeded further with the
+ // exception.
+ try {
+ Files.copy(destJar, longDirJar);
+ } catch (java.io.IOException ioe) {
+ System.out.println("Caught IOException from Files.copy(). Cannot continue.");
+ return;
+ }
+ dump2(null, topArchiveName,
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", destJar.toString(),
+ "-Xlog:exceptions=trace",
+ "--module-path", longDirJar.toString(),
+ "-m", TEST_MODULE1)
+ .ifAbnormalExit(output -> {
+ output.shouldMatch("os::stat error.*CDS dump aborted");
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/MethodSorting.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, 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 When HelloA and HelloB are copied into the dynamic archive, the Symbols
+ * for their method's names will have a different sorting order. This requires
+ * that the dumped InstanceKlass to re-sort their "methods" array and re-layout the vtables/itables.
+ * A regression test for an earlier bug in DynamicArchiveBuilder::relocate_buffer_to_target().
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * /test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes
+ * @build MethodSortingApp
+ * @run driver ClassFileInstaller -jar method_sorting.jar
+ * MethodSortingApp
+ * MethodSortingApp$HelloA
+ * MethodSortingApp$HelloA1
+ * MethodSortingApp$HelloB
+ * MethodSortingApp$HelloB1
+ * MethodSortingApp$InterfaceA
+ * MethodSortingApp$InterfaceB
+ * MethodSortingApp$ImplementorA
+ * MethodSortingApp$ImplementorA1
+ * MethodSortingApp$ImplementorB
+ * MethodSortingApp$ImplementorB1
+ * @run driver MethodSorting
+ */
+
+public class MethodSorting extends DynamicArchiveTestBase {
+ public static void main(String[] args) throws Exception {
+ runTest(MethodSorting::test);
+ }
+
+ static void test() throws Exception {
+ String topArchiveName = getNewArchiveName();
+ String appJar = ClassFileInstaller.getJarPath("method_sorting.jar");
+ String mainClass = "MethodSortingApp";
+
+ dumpAndRun(topArchiveName, "-Xlog:cds+dynamic=debug", "-cp", appJar, mainClass);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/MissingArchive.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import java.io.File;
+
+/*
+ * @test
+ * @summary error handling when either (or both) of the base/top archives are missing.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @build GenericTestApp sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar GenericTestApp.jar GenericTestApp
+ * @run driver MissingArchive
+ */
+
+public class MissingArchive extends DynamicArchiveTestBase {
+ private static final String TOP = "top";
+ private static final String BASE = "base";
+ private static final String BOTH = "base/top";
+ private static final String NONE = "none";
+
+ public static void main(String[] args) throws Exception {
+ runTest(MissingArchive::test, TOP);
+ runTest(MissingArchive::test, BASE);
+ runTest(MissingArchive::test, BOTH);
+ runTest(MissingArchive::test, NONE);
+ }
+
+ static void delete(String fileName) {
+ File f = new File(fileName);
+ f.delete();
+ }
+
+ static void test(String args[]) throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ String baseArchiveName = getNewArchiveName("base");
+ dumpBaseArchive(baseArchiveName);
+
+ String appJar = ClassFileInstaller.getJarPath("GenericTestApp.jar");
+ String mainClass = "GenericTestApp";
+ dump2_WB(baseArchiveName, topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+
+ // Use -Xshare:auto so top archive can fail after base archive has succeeded,
+ // but the app will continue to run.
+ String[] cmdline = TestCommon.concat(
+ "-Xlog:cds*",
+ "-Xshare:auto",
+ "-cp", appJar, mainClass);
+
+
+ String mode = args[0];
+
+ if (mode.contains(BASE)) {
+ delete(baseArchiveName);
+ cmdline = TestCommon.concat(cmdline, "assertNotShared:java.lang.Object");
+ } else {
+ cmdline = TestCommon.concat(cmdline, "assertShared:java.lang.Object");
+ }
+
+ if (mode.contains(TOP)) {
+ delete(topArchiveName);
+ }
+
+ if (mode.equals(NONE)) {
+ cmdline = TestCommon.concat(cmdline, "assertShared:GenericTestApp");
+ } else {
+ cmdline = TestCommon.concat(cmdline, "assertNotShared:GenericTestApp");
+ }
+
+ run2_WB(baseArchiveName, topArchiveName, cmdline).assertNormalExit();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/NoClassToArchive.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2019, 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 A few edge cases where there's no class to be included in the dynamic archive.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes
+ * @build StrConcatApp
+ * @run driver ClassFileInstaller -jar strConcatApp.jar StrConcatApp
+ * @run driver NoClassToArchive
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class NoClassToArchive extends DynamicArchiveTestBase {
+ static final String warningMessage =
+ "There is no class to be included in the dynamic archive";
+ static final String classList = System.getProperty("test.classes") +
+ File.separator + "NoClassToArchive.list";
+ static final String appClass = "StrConcatApp";
+
+ public static void main(String[] args) throws Exception {
+ runTest(NoClassToArchive::testDefaultBase);
+ runTest(NoClassToArchive::testCustomBase);
+ }
+
+ // (1) Test with default base archive + top archive
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ doTest(null, topArchiveName);
+ }
+
+ // (2) Test with custom base archive + top archive
+ static void testCustomBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top2");
+ String baseArchiveName = getNewArchiveName("base");
+ doTestCustomBase(baseArchiveName, topArchiveName);
+ }
+
+ private static void doTest(String baseArchiveName, String topArchiveName) throws Exception {
+ dump2(baseArchiveName, topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-Xlog:class+load=trace",
+ "-version")
+ .assertNormalExit(output -> {
+ if (output.getStdout().contains("jrt:/")) {
+ System.out.println("test skipped: this platform uses non-archived classes when running -version");
+ } else {
+ output.shouldContain(warningMessage);
+ }
+ });
+
+ dump2(baseArchiveName, topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-Xlog:class+load=trace",
+ "-help")
+ .assertNormalExit(output -> {
+ // some classes will be loaded from the java.base module
+ output.shouldContain("java.text.MessageFormat source: jrt:/java.base");
+ });
+ }
+
+ private static void doTestCustomBase(String baseArchiveName, String topArchiveName) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("strConcatApp.jar");
+ // dump class list by running the StrConcatApp
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ true,
+ "-XX:DumpLoadedClassList=" + classList,
+ "-cp",
+ appJar,
+ appClass);
+ OutputAnalyzer output = TestCommon.executeAndLog(pb, "dumpClassList");
+ TestCommon.checkExecReturn(output, 0, true, "length = 0");
+
+ // create a custom base archive based on the class list
+ dumpBaseArchive(baseArchiveName, "-XX:SharedClassListFile=" + classList);
+
+ // create a dynamic archive with the custom base archive
+ // no class should be included in the dynamic archive
+ dump2(baseArchiveName, topArchiveName, "-version")
+ .assertNormalExit(out -> {
+ out.shouldMatch(warningMessage);
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/SharedArchiveFileOption.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2019, 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 Some negative tests for the SharedArchiveFile option
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @build Hello
+ * @run driver ClassFileInstaller -jar hello.jar Hello
+ * @run driver SharedArchiveFileOption
+ */
+
+import java.io.File;
+
+public class SharedArchiveFileOption extends DynamicArchiveTestBase {
+ public static void main(String[] args) throws Exception {
+ runTest(SharedArchiveFileOption::testCustomBase);
+ }
+
+ static String baseArchiveName2;
+ static void testCustomBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ String baseArchiveName = getNewArchiveName("base");
+ baseArchiveName2 = getNewArchiveName("base2");
+ dumpBaseArchive(baseArchiveName);
+ dumpBaseArchive(baseArchiveName2);
+ doTest(baseArchiveName, topArchiveName);
+ }
+
+ private static void doTest(String baseArchiveName, String topArchiveName) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("hello.jar");
+ String mainClass = "Hello";
+ String dummyArchiveName = getNewArchiveName("dummy");
+
+ // -Xshare:dump specified with -XX:ArchiveClassesAtExit
+ dump2(dummyArchiveName, dummyArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-Xshare:dump",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldContain("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump");
+ });
+
+ // more than 1 archive file specified in -XX:SharedArchiveFile during
+ // dynamic dumpgin
+ String dummyArchives = dummyArchiveName + File.pathSeparator + dummyArchiveName;
+ dump2(dummyArchives, dummyArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldContain("Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping");
+ });
+
+ // normal dynamic archive dumping
+ dump2(baseArchiveName, topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+
+ // same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit
+ dump2(baseArchiveName, baseArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldContain("Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit: "
+ + baseArchiveName);
+ });
+
+
+ // a top archive specified in the base archive position
+ run2(topArchiveName, baseArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldMatch("Not a base shared archive:.*top.*.jsa");
+ });
+
+ // a base archive specified in the top archive position
+ run2(baseArchiveName, baseArchiveName2,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldMatch("Not a top shared archive:.*base.*.jsa");
+ });
+
+ // more than 2 archives specified in the -XX:ShareArchiveFile option
+ String baseArchives = baseArchiveName + File.pathSeparator + baseArchiveName2;
+ run2(baseArchives, topArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldContain(
+ "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option");
+ });
+
+ // base archive not specified
+ final String topArchive = File.pathSeparator + topArchiveName;
+ run2(topArchive, null,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldContain(
+ "Base archive was not specified: " + topArchive);
+ });
+
+ // top archive not specified
+ final String baseArchive = baseArchiveName + File.pathSeparator;
+ run2(baseArchive, null,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ "-cp", appJar, mainClass)
+ .assertAbnormalExit(output -> {
+ output.shouldContain(
+ "Top archive was not specified: " + baseArchive);
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/UnsupportedBaseArchive.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/*
+ * @test
+ * @summary unsupported base archive tests
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @modules jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/Hello.java
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run driver UnsupportedBaseArchive
+ */
+
+public class UnsupportedBaseArchive extends DynamicArchiveTestBase {
+ private static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
+
+ private static final String FS = File.separator;
+ private static final String TEST_SRC = System.getProperty("test.src") +
+ FS + ".." + FS + "jigsaw" + FS + "modulepath";
+
+ private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+ private static final Path MODS_DIR = Paths.get("mods");
+
+ // the module name of the test module
+ private static final String TEST_MODULE = "com.simple";
+
+ // the module main class
+ private static final String MAIN_CLASS = "com.simple.Main";
+
+ private static Path moduleDir = null;
+ private static Path srcJar = null;
+
+ private static final String warningBCP =
+ "Dynamic archiving is disabled because base layer archive has appended boot classpath";
+
+ private static final String warningModulePath =
+ "Dynamic archiving is disabled because base layer archive has module path";
+
+ public static void buildTestModule() throws Exception {
+
+ // javac -d mods/$TESTMODULE --module-path MOD_DIR src/$TESTMODULE/**
+ JarBuilder.compileModule(SRC_DIR.resolve(TEST_MODULE),
+ MODS_DIR.resolve(TEST_MODULE),
+ MODS_DIR.toString());
+
+
+ moduleDir = Files.createTempDirectory(USER_DIR, "mlib");
+ srcJar = moduleDir.resolve(TEST_MODULE + ".jar");
+ String classes = MODS_DIR.resolve(TEST_MODULE).toString();
+ JarBuilder.createModularJar(srcJar.toString(), classes, MAIN_CLASS);
+ }
+
+ public static void main(String[] args) throws Exception {
+ runTest(UnsupportedBaseArchive::test);
+ }
+
+ static void test(String args[]) throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ String baseArchiveName = getNewArchiveName("base");
+
+ // create a base archive with -Xbootclasspath/a:whitebox.jar
+ dumpBaseArchive_WB(baseArchiveName);
+
+ String appJar = JarBuilder.getOrCreateHelloJar();
+ String mainClass = "Hello";
+
+ // dumping of dynamic archive should be disabled with a warning message
+ // if the base archive contains -Xbootclasspath/a entries.
+ dump2_WB(baseArchiveName, topArchiveName,
+ "-Xlog:cds*",
+ "-Xlog:cds+dynamic=debug",
+ "-Xlog:class+path=info",
+ "-cp", appJar, mainClass)
+ .assertNormalExit(warningBCP);
+
+ // create a base archive with the --module-path option
+ buildTestModule();
+ baseArchiveName = getNewArchiveName("base-with-module");
+ dumpBaseArchive(baseArchiveName,
+ "-cp", srcJar.toString(),
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE);
+
+ // dumping of dynamic archive should be disabled with a warning message
+ // if the base archive contains --module-path entries.
+ topArchiveName = getNewArchiveName("top-with-module");
+ dump2(baseArchiveName, topArchiveName,
+ "-Xlog:cds*",
+ "-Xlog:cds+dynamic=debug",
+ "-Xlog:class+path=info",
+ "-cp", srcJar.toString(),
+ "--module-path", moduleDir.toString(),
+ "-m", TEST_MODULE)
+ .assertNormalExit(warningModulePath);
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/UnusedCPDuringDump.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019, 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 non-empty dir in -cp should be fine during dump time if only classes
+ * from the system modules are being loaded even though some are
+ * defined to the PlatformClassLoader and AppClassLoader.
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/Hello.java
+ * @run main/othervm -Dtest.cds.copy.child.stdout=false UnusedCPDuringDump
+ */
+
+import java.io.File;
+
+public class UnusedCPDuringDump extends DynamicArchiveTestBase {
+
+ public static void main(String[] args) throws Exception {
+ runTest(UnusedCPDuringDump::testDefaultBase);
+ }
+
+ static void testDefaultBase() throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ doTest(topArchiveName);
+ }
+
+ private static void doTest(String topArchiveName) throws Exception {
+ File dir = new File(System.getProperty("user.dir"));
+ File emptydir = new File(dir, "emptydir");
+ emptydir.mkdir();
+ String appJar = JarBuilder.getOrCreateHelloJar();
+ String bootClassPath = "-Xbootclasspath/a:" + appJar;
+
+ // Dumping with a non-empty directory in the -cp.
+ // It should be fine because the app class won't be loaded from the
+ // -cp, it is being loaded from the bootclasspath.
+ dump(topArchiveName,
+ "-Xlog:cds",
+ "-Xlog:cds+dynamic=debug",
+ bootClassPath,
+ "-cp", dir.getPath(),
+ "Hello")
+ .assertNormalExit(output -> {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ });
+
+ // Running with -cp different from dumping. It should be fine because
+ // the runtime classpath won't be checked against unused classpath
+ // during dump time.
+ run(topArchiveName,
+ "-Xlog:class+load",
+ "-Xlog:cds+dynamic=debug,cds=debug",
+ bootClassPath,
+ "-cp", appJar, "Hello")
+ .assertNormalExit(output -> {
+ output.shouldContain("Hello World")
+ .shouldHaveExitValue(0);
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/WrongTopClasspath.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import java.io.File;
+
+/*
+ * @test
+ * @summary correct classpath for bottom archive, but bad classpath for top archive
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @build GenericTestApp sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller -jar GenericTestApp.jar GenericTestApp
+ * @run driver ClassFileInstaller -jar WrongJar.jar GenericTestApp
+ * @run driver WrongTopClasspath
+ */
+
+public class WrongTopClasspath extends DynamicArchiveTestBase {
+
+ public static void main(String[] args) throws Exception {
+ runTest(WrongTopClasspath::test);
+ }
+
+ static void test(String args[]) throws Exception {
+ String topArchiveName = getNewArchiveName("top");
+ String baseArchiveName = getNewArchiveName("base");
+ dumpBaseArchive(baseArchiveName);
+
+ String appJar = ClassFileInstaller.getJarPath("GenericTestApp.jar");
+ String wrongJar = ClassFileInstaller.getJarPath("WrongJar.jar");
+ String mainClass = "GenericTestApp";
+
+ // Dump the top archive using "-cp GenericTestApp.jar" ...
+ dump2_WB(baseArchiveName, topArchiveName,
+ "-Xlog:cds*",
+ "-Xlog:cds+dynamic=debug",
+ "-cp", appJar, mainClass)
+ .assertNormalExit();
+
+ // ... but try to load the top archive using "-cp WrongJar.jar".
+ // Use -Xshare:auto so top archive can fail after base archive has succeeded,
+ // but the app will continue to run.
+ run2_WB(baseArchiveName, topArchiveName,
+ "-Xlog:cds*",
+ "-Xlog:cds+dynamic=debug",
+ "-Xlog:class+path=info",
+ "-Xshare:auto",
+ "-cp", wrongJar, mainClass,
+ "assertShared:java.lang.Object", // base archive still useable
+ "assertNotShared:GenericTestApp") // but top archive is not useable
+ .assertNormalExit("The top archive failed to load");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+public class ArrayKlassesApp {
+ public static void main(String args[]) {
+ ArrayKlassesApp[][] array = new ArrayKlassesApp[1][2];
+ for (int i=0; i<1; i++) {
+ for (int j=0; j<2; j++) {
+ array[i][j] = new ArrayKlassesApp();
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes/ExcludedClassesApp.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+public class ExcludedClassesApp {
+ interface NotLinkedInterface {}
+ static class NotLinkedSuper {
+
+ }
+
+ static class NotLinkedChild extends NotLinkedSuper implements NotLinkedInterface {
+
+ }
+
+ public static NotLinkedSuper notUsedMethod() {
+ return new NotLinkedChild();
+ }
+
+ public static void main(String args[]) {
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes/LoadClasses.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+import java.io.File;
+import java.util.List;
+import java.util.Scanner;
+import sun.hotspot.WhiteBox;
+
+public class LoadClasses {
+
+ public static void main (String[] args) throws Throwable {
+ String classList = args[0];
+ Scanner sc = new Scanner(new File(classList));
+ WhiteBox wb = WhiteBox.getWhiteBox();
+ int count = 0;
+ while (sc.hasNextLine()) {
+ String cn = sc.nextLine().replace('/', '.');
+ try {
+ Class<?> cls = Class.forName(cn, false, LoadClasses.class.getClassLoader());
+ wb.linkClass(cls);
+ count++;
+ } catch (Throwable ex) {
+ System.out.println("Loading failed: " + cn);
+ System.out.println(ex.toString());
+ }
+ }
+ System.out.println("Loaded classes = " + count);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes/MethodSortingApp.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+
+public class MethodSortingApp {
+ static class HelloA {
+ String aaaa() { return "aaaa"; }
+ String bbbb() { return "bbbb"; }
+ String dddd() { return "dddd"; }
+ String eeee() { return "eeee"; }
+ String gggg() { return "gggg"; }
+ }
+
+ static class HelloA1 extends HelloA {
+ String aaaa() { return "aaa-"; }
+ String dddd() { return "ddd-"; }
+ String gggg() { return "ggg-"; }
+ }
+
+ static class HelloB {
+ String aaaa() { return "aaaa"; }
+ String cccc() { return "cccc"; }
+ String dddd() { return "dddd"; }
+ String ffff() { return "ffff"; }
+ String gggg() { return "gggg"; }
+ }
+
+ static class HelloB1 extends HelloB {
+ String aaaa() { return "aaa-"; }
+ String dddd() { return "ddd-"; }
+ String gggg() { return "ggg-"; }
+ }
+
+ // Default methods in interfaces must be sorted
+ static interface InterfaceA {
+ default public String aaa() { return "aaa";}
+ default public String bbb() { return "bbb";}
+ default public String ddd() { return "ddd";}
+ default public String eee() { return "eee";}
+ default public String ggg() { return "ggg";}
+ }
+
+ static class ImplementorA implements InterfaceA {
+ public String aaa() { return "aa-";}
+ }
+
+ static class ImplementorA1 extends ImplementorA {
+ public String bbb() { return "bb-";}
+ }
+
+ static interface InterfaceB {
+ default public String aaa() { return "aaa"; }
+ default public String ccc() { return "ccc"; }
+ default public String ddd() { return "ddd"; }
+ default public String fff() { return "fff"; }
+ default public String ggg() { return "ggg"; }
+ }
+
+ static class ImplementorB implements InterfaceB {
+ public String ggg() { return "gg-";}
+ }
+
+ static class ImplementorB1 extends ImplementorB {
+ public String fff() { return "ff-";}
+ }
+
+
+ public static void main(String args[]) {
+ testSimpleMethods();
+ testDefaultMethods();
+ testMixedInterfaces();
+ }
+
+ static void testSimpleMethods() {
+ // When HelloA and HelloB are copied into the dynamic archive, the Symbols
+ // for their method's names will have a different sorting order. This requires
+ // that the dumped InstanceKlass to re-sort their "methods" array and re-layout
+ // the vtables/itables.
+ HelloA1 a1 = new HelloA1();
+ HelloA a = new HelloA();
+ assertEqual(a.aaaa(), "aaaa");
+ assertEqual(a.bbbb(), "bbbb");
+ assertEqual(a.dddd(), "dddd");
+ assertEqual(a.eeee(), "eeee");
+ assertEqual(a.gggg(), "gggg");
+
+ assertEqual(a1.aaaa(), "aaa-");
+ assertEqual(a1.bbbb(), "bbbb");
+ assertEqual(a1.dddd(), "ddd-");
+ assertEqual(a1.eeee(), "eeee");
+ assertEqual(a1.gggg(), "ggg-");
+
+ HelloB b = new HelloB();
+ assertEqual(b.aaaa(), "aaaa");
+ assertEqual(b.cccc(), "cccc");
+ assertEqual(b.dddd(), "dddd");
+ assertEqual(b.ffff(), "ffff");
+ assertEqual(b.gggg(), "gggg");
+
+ HelloB b1 = new HelloB1();
+ assertEqual(b1.aaaa(), "aaa-");
+ assertEqual(b1.cccc(), "cccc");
+ assertEqual(b1.dddd(), "ddd-");
+ assertEqual(b1.ffff(), "ffff");
+ assertEqual(b1.gggg(), "ggg-");
+ }
+
+ static void testDefaultMethods() {
+ InterfaceA a1 = new ImplementorA1();
+ InterfaceA a = new ImplementorA();
+
+ assertEqual(a.aaa(), "aa-");
+ assertEqual(a.bbb(), "bbb");
+ assertEqual(a.ddd(), "ddd");
+ assertEqual(a.eee(), "eee");
+ assertEqual(a.ggg(), "ggg");
+
+ assertEqual(a1.aaa(), "aa-");
+ assertEqual(a1.bbb(), "bb-");
+ assertEqual(a1.ddd(), "ddd");
+ assertEqual(a1.eee(), "eee");
+ assertEqual(a1.ggg(), "ggg");
+
+ InterfaceB b = new ImplementorB();
+ InterfaceB b1 = new ImplementorB1();
+
+ assertEqual(b.aaa(), "aaa");
+ assertEqual(b.ccc(), "ccc");
+ assertEqual(b.ddd(), "ddd");
+ assertEqual(b.fff(), "fff");
+ assertEqual(b.ggg(), "gg-");
+
+ assertEqual(b1.aaa(), "aaa");
+ assertEqual(b1.ccc(), "ccc");
+ assertEqual(b1.ddd(), "ddd");
+ assertEqual(b1.fff(), "ff-");
+ assertEqual(b1.ggg(), "gg-");
+ }
+
+ // This is a regression test for an earlier bug in
+ // DynamicArchiveBuilder::relocate_buffer_to_target()
+ static void testMixedInterfaces() {
+ Object xx = new SynchronousQueue();
+ BlockingQueue yy = (BlockingQueue)xx;
+ }
+
+ static private void assertEqual(String a, String b) {
+ if (!a.equals(b)) {
+ throw new RuntimeException(a + " is not equal to " + b);
+ } else {
+ System.out.println("Expected: " + a + ", got: " + b);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes/MissingDependent.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+public class MissingDependent {
+ public static void main(String args[]) throws Exception {
+ try {
+ Object obj = new StrConcatApp();
+ } catch (Throwable e) {
+ String cause = e.getCause().toString();
+ if (cause.equals("java.lang.ClassNotFoundException: StrConcatApp")) {
+ e.printStackTrace();
+ } else {
+ throw e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes/StrConcatApp.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+public class StrConcatApp {
+ public static void main(String args[]) {
+ System.out.println("length = " + args.length);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/dynamicArchive/test-classes/TestJIT.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import sun.hotspot.WhiteBox;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+public class TestJIT {
+
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ static void doSomething() {
+ HashMap<String,String> map = new HashMap<>();
+ for (int i=0; i<400; i++) {
+ // Call these methods so that the class/field/method references used
+ // by these methods are resolved. This allows C2 to compile more code.
+ String x = "Hello" + i;
+ map.put(x, x);
+ map.get(x);
+ }
+ }
+
+ static public void main(String[] args) throws NoSuchMethodException {
+ Method put_method = HashMap.class.getDeclaredMethod("put", Object.class, Object.class);
+ Method get_method = HashMap.class.getDeclaredMethod("get", Object.class);
+ Method test_method = TestJIT.class.getDeclaredMethod("doSomething");
+
+ doSomething();
+
+ // 4 == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION => C2
+ WHITE_BOX.enqueueMethodForCompilation(get_method, 4);
+ WHITE_BOX.enqueueMethodForCompilation(put_method, 4);
+ WHITE_BOX.enqueueMethodForCompilation(test_method, 4);
+
+ // Try to start dynamic dumping while the above compilations are still in progesss
+ System.exit(0);
+ }
+}
--- a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -74,11 +74,12 @@
// Case 2: --patch-module specified for run time but not for dump time
System.out.println("Case 2: --patch-module specified for run time but not for dump time");
+ String appJar = JarBuilder.build("PatchMain-app", "PatchMain");
output =
- TestCommon.dump(null,
+ TestCommon.dump(appJar,
TestCommon.list("javax/naming/spi/NamingManager"),
"PatchMain", "javax.naming.spi.NamingManager");
- TestCommon.checkDump(output, "Loading classes to share");
+ TestCommon.checkDump(output);
// javax.naming.spi.NamingManager is patched at runtime
TestCommon.run(
--- a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -58,18 +58,25 @@
moduleJar = TestCommon.getTestJar("javabase.jar");
System.out.println("Test dumping with --patch-module");
+ String runError = "Unable to use shared archive: CDS is disabled when java.base module is patched";
+ String dumpingError = "Cannot use the following option when dumping the shared archive: --patch-module";
+ String errMsg;
+ if (TestCommon.isDynamicArchive()) {
+ errMsg = runError;
+ } else {
+ errMsg = dumpingError;
+ }
OutputAnalyzer output =
TestCommon.dump(null, null,
"--patch-module=java.base=" + moduleJar,
"PatchMain", "java.lang.NewClass");
output.shouldHaveExitValue(1)
- .shouldContain("Cannot use the following option when dumping the shared archive: --patch-module");
+ .shouldContain(errMsg);
TestCommon.run(
"-XX:+UnlockDiagnosticVMOptions",
"--patch-module=java.base=" + moduleJar,
"PatchMain", "java.lang.NewClass")
- .assertAbnormalExit("Unable to use shared archive",
- "CDS is disabled when java.base module is patched");
+ .assertAbnormalExit(runError);
}
}
--- a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -27,6 +27,9 @@
public class PatchMain {
public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("cdsutils.DynamicDumpHelper")) {
+ break;
+ }
Class.forName(args[i]);
}
}
--- a/test/hotspot/jtreg/runtime/appcds/jigsaw/modulepath/AddModules.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/modulepath/AddModules.java Fri May 17 08:29:55 2019 -0700
@@ -29,6 +29,7 @@
* @modules jdk.compiler
* jdk.jartool/sun.tools.jar
* jdk.jlink
+ * @compile ../../test-classes/Hello.java
* @run driver AddModules
* @summary sanity test the --add-modules option
*/
@@ -63,6 +64,7 @@
private static Path subJar = null;
private static Path mainJar1 = null;
private static Path mainJar2 = null;
+ private static String appJar;
public static void buildTestModule() throws Exception {
@@ -96,18 +98,19 @@
}
public static void main(String... args) throws Exception {
+ appJar = JarBuilder.getOrCreateHelloJar();
// compile the modules and create the modular jar files
buildTestModule();
String appClasses[] = {MAIN_CLASS1, MAIN_CLASS2, APP_CLASS};
// create an archive with the classes in the modules built in the
// previous step
OutputAnalyzer output = TestCommon.createArchive(
- null, appClasses,
+ appJar, appClasses,
"--module-path", moduleDir.toString(),
"--add-modules",
MAIN_MODULE1 + "," + MAIN_MODULE2);
TestCommon.checkDump(output);
- String prefix[] = {"-Djava.class.path=", "-Xlog:class+load=trace"};
+ String prefix[] = {"-Djava.class.path=" + appJar, "-Xlog:class+load=trace"};
// run the com.greetings module with the archive with the --module-path
// the same as the one during dump time.
--- a/test/hotspot/jtreg/runtime/appcds/jigsaw/modulepath/JvmtiAddPath.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/modulepath/JvmtiAddPath.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -121,7 +121,9 @@
"-Xlog:class+load", "JvmtiApp", "noadd", MAIN_CLASS); // appcds should be enabled
System.out.println("Test case 2: add to boot classpath only - should find Hello.class in boot loader");
- run(check_appcds_disabled, appJar,
+ String[] toCheck = TestCommon.isDynamicArchive() ? check_appcds_enabled :
+ check_appcds_disabled;
+ run(toCheck, appJar,
"-Xlog:class+load=trace",
modulePath,
"JvmtiApp", "bootonly", addbootJar, MAIN_CLASS); // appcds should be disabled
--- a/test/hotspot/jtreg/runtime/appcds/jigsaw/modulepath/MainModuleOnly.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/modulepath/MainModuleOnly.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -29,7 +29,7 @@
* @modules jdk.compiler
* jdk.jartool/sun.tools.jar
* jdk.jlink
- * @run driver MainModuleOnly
+ * @run main/othervm MainModuleOnly
* @summary Test some scenarios with a main modular jar specified in the --module-path and -cp options in the command line.
*/
@@ -39,6 +39,7 @@
import java.nio.file.Paths;
import java.util.Arrays;
+import jdk.test.lib.cds.CDSTestUtils.Result;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.Platform;
@@ -61,6 +62,8 @@
private static Path moduleDir2 = null;
private static Path destJar = null;
+ private static final String jarFileError = "A jar file is not the one used while building the shared archive file:";
+
public static void buildTestModule() throws Exception {
// javac -d mods/$TESTMODULE --module-path MOD_DIR src/$TESTMODULE/**
@@ -167,17 +170,21 @@
// run with the archive and the jar with modified timestamp.
// It should fail due to timestamp of the jar doesn't match the one
// used during dump time.
- TestCommon.run("-cp", destJar.toString(),
+ Result res = TestCommon.run("-cp", destJar.toString(),
+ "-Xlog:cds",
"--module-path", moduleDir.toString(),
- "-m", TEST_MODULE1)
- .assertAbnormalExit(
- "A jar file is not the one used while building the shared archive file:");
+ "-m", TEST_MODULE1);
+ res.assertAbnormalExit(jarFileError);
// create an archive with a non-empty directory in the --module-path.
// The dumping process will exit with an error due to non-empty directory
// in the --module-path.
+ String mainModule = TEST_MODULE1;
+ if (TestCommon.isDynamicArchive()) {
+ mainModule += "/" + MAIN_CLASS;
+ }
output = TestCommon.createArchive(destJar.toString(), appClasses,
"--module-path", MODS_DIR.toString(),
- "-m", TEST_MODULE1);
+ "-m", mainModule);
output.shouldHaveExitValue(1)
.shouldMatch("Error: non-empty directory.*com.simple");
--- a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java Fri May 17 08:29:55 2019 -0700
@@ -26,8 +26,10 @@
* @test
* @requires vm.cds
* @modules java.base/jdk.internal.misc
+ * jdk.jartool/sun.tools.jar
* @library ../..
* @library /test/lib
+ * @compile ../../test-classes/Hello.java
* @run driver OverrideTests
* @summary AppCDS tests for overriding archived classes with -p and --upgrade-module-path
*/
@@ -65,7 +67,7 @@
private static final String TEST_SRC = System.getProperty("test.src");
private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
private static final Path MODS_DIR = Paths.get("mods");
-
+ private static String appJar;
// the module that is upgraded
private static final String[] UPGRADED_MODULES = {"jdk.compiler", "java.net.http"};
private static final Path[] UPGRADEDMODS_DIR = {Paths.get("upgradedmod1"), Paths.get("upgradedmod2")};
@@ -82,6 +84,7 @@
public static void main(String[] args) throws Exception {
+ appJar = JarBuilder.getOrCreateHelloJar();
OverrideTests tests = new OverrideTests();
tests.compileModulesAndDumpArchive();
tests.testAppClassOverriding();
@@ -111,7 +114,7 @@
Asserts.assertTrue(compiled, TEST_MODULE + " did not compile");
// dump the archive with jdk.compiler and java.net.http classes in the class list
- OutputAnalyzer output = TestCommon.dump(null /* appJar*/, TestCommon.list(ARCHIVE_CLASSES));
+ OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list(ARCHIVE_CLASSES));
TestCommon.checkDump(output);
// Make sure all the classes where successfully archived.
for (String archiveClass : ARCHIVE_CLASSES) {
@@ -160,7 +163,7 @@
int upgradeModIdx = isAppLoader ? 0 : 1;
String expectedException = "java.lang.module.FindException: Unable to compute the hash";
String prefix[] = new String[3];
- prefix[0] = "-Djava.class.path=";
+ prefix[0] = "-Djava.class.path=" + appJar;
prefix[1] = "--add-modules";
prefix[2] = "java.net.http";
--- a/test/hotspot/jtreg/runtime/appcds/jvmti/dumpingWithAgent/DumpingWithJavaAgent.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/dumpingWithAgent/DumpingWithJavaAgent.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -105,7 +105,7 @@
if (!TestCommon.isUnableToMap(output)) {
output.shouldHaveExitValue(0);
output.shouldContain(errorMessage);
- output.shouldMatch(".class.load. Hello source:.*DumpingWithJavaAgent.jar");
+ output.shouldMatch(".class.load.* Hello source:.*DumpingWithJavaAgent.jar");
// CDS dumping with a java agent without the AllowArchvingWithJavaAgent diagnostic option.
// VM will exit with an error message.
@@ -116,4 +116,3 @@
.shouldHaveExitValue(1);
}
}
-
--- a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -154,6 +154,12 @@
return;
}
+ if (TestCommon.isDynamicArchive()) {
+ log("custom loader class list not applicable to dynamic archive" +
+ " - skipping test case for custom loader");
+ return;
+ }
+
String appClasses[] = {
"CustomLoaderApp",
};
--- a/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java Fri May 17 10:48:02 2019 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -24,6 +24,7 @@
import java.lang.*;
import java.lang.reflect.*;
+import jdk.test.lib.cds.CDSTestUtils;
import sun.hotspot.WhiteBox;
public class DummyClassHelper {
@@ -50,9 +51,14 @@
cls = Class.forName(classNames[i]);
checkDummyMethod(cls, classNames[i]);
if (doWBCheck) {
- if (!wb.isSharedClass(cls)) {
- throw new java.lang.RuntimeException(classNames[i] +
- ".class should be in shared space.");
+ // FIXME: for dynamic archive, the class loaded from the
+ // bootclasspath jar during dump time is not loaded from the
+ // archive during run time.
+ if (!CDSTestUtils.isDynamicArchive()) {
+ if (!wb.isSharedClass(cls)) {
+ throw new java.lang.RuntimeException(classNames[i] +
+ ".class should be in shared space.");
+ }
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/GenericTestApp.java Fri May 17 08:29:55 2019 -0700
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import sun.hotspot.WhiteBox;
+
+/**
+ * This is a generic test app for testing if classes are loaded from the CDS archive
+ * or not (without having to parse -Xlog:class+load, or writing your own WhiteBox apps).
+ * Usage:
+ * [1] Create an archive with WhiteBox enabled.
+ * [2] Run this app with arguments such as
+ * "assertShared:java.lang.Object"
+ * "assertNotShared:NotSharedClassName"
+ *
+ * We can probably add other kinds of simple tests as well ....
+ *
+ * FIXME: enhance WB API to check if a particular archive has failed. So you can say
+ * assertShared:0,java.lang.Object
+ * to assert that java.lang.Object is shared from archive #0 (i.e., base archive).
+ */
+public class GenericTestApp {
+ private static final WhiteBox wb = WhiteBox.getWhiteBox();
+
+ public static void main(String args[]) throws Exception {
+ System.out.println("GenericTestApp started. WhiteBox = " + wb);
+ System.out.println("cdsMemoryMappingFailed() = " + cdsMemoryMappingFailed());
+
+ for (String s : args) {
+ Class c;
+ if ((c = getClass(s, "assertShared:")) != null) {
+ assertShared(c);
+ }
+ else if ((c = getClass(s, "assertNotShared:")) != null) {
+ assertNotShared(c);
+ }
+ else {
+ throw new RuntimeException("Unknown option: " + s);
+ }
+ System.out.println("passed: " + s);
+ }
+ }
+
+ private static Class getClass(String s, String prefix) throws Exception {
+ if (s.startsWith(prefix)) {
+ return Class.forName(s.substring(prefix.length()));
+ } else {
+ return null;
+ }
+ }
+
+ private static boolean cdsMemoryMappingFailed() {
+ return wb.cdsMemoryMappingFailed();
+ }
+
+ private static void assertShared(Class klass) {
+ if (!cdsMemoryMappingFailed()) {
+ if (!wb.isSharedClass(klass)) {
+ throw new RuntimeException("Class should be shared but is not: " + klass);
+ }
+ } else {
+ // FIXME -- need to throw jtreg.SkippedException
+ System.out.println("Cannot test for wb.isSharedClass(" + klass + ") because CDS mapping has failed");
+ }
+ }
+
+ private static void assertNotShared(Class klass) {
+ if (wb.isSharedClass(klass)) {
+ throw new RuntimeException("Class should be shared but is not: " + klass);
+ }
+ }
+}
--- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java Fri May 17 10:48:02 2019 -0400
+++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java Fri May 17 08:29:55 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -43,6 +43,8 @@
public static final String MSG_COMPRESSION_MUST_BE_USED =
"Unable to use shared archive: UseCompressedOops and UseCompressedClassPointers must be on for UseSharedSpaces.";
+ public static final boolean DYNAMIC_DUMP = Boolean.getBoolean("test.dynamic.cds.archive");
+
public interface Checker {
public void check(OutputAnalyzer output) throws Exception;
}
@@ -246,7 +248,7 @@
cmd.add("-Xlog:cds,cds+hashtables");
if (opts.archiveName == null)
opts.archiveName = getDefaultArchiveName();
- cmd.add("-XX:SharedArchiveFile=./" + opts.archiveName);
+ cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
if (opts.classList != null) {
File classListFile = makeClassList(opts.classList);
@@ -260,12 +262,20 @@
return executeAndLog(pb, "dump");
}
+ public static boolean isDynamicArchive() {
+ return DYNAMIC_DUMP;
+ }
// check result of 'dump-the-archive' operation, that is "-Xshare:dump"
public static OutputAnalyzer checkDump(OutputAnalyzer output, String... extraMatches)
throws Exception {
- output.shouldContain("Loading classes to share");
+ if (!DYNAMIC_DUMP) {
+ output.shouldContain("Loading classes to share");
+ } else {
+ output.shouldContain("Buffer-space to target-space delta")
+ .shouldContain("Written dynamic archive 0x");
+ }
output.shouldHaveExitValue(0);
for (String match : extraMatches) {
--- a/test/lib/sun/hotspot/WhiteBox.java Fri May 17 10:48:02 2019 -0400
+++ b/test/lib/sun/hotspot/WhiteBox.java Fri May 17 08:29:55 2019 -0700
@@ -514,6 +514,7 @@
// Sharing & archiving
public native String getDefaultArchivePath();
+ public native boolean cdsMemoryMappingFailed();
public native boolean isSharingEnabled();
public native boolean isShared(Object o);
public native boolean isSharedClass(Class<?> c);
@@ -522,6 +523,7 @@
public native boolean isJFRIncludedInVmBuild();
public native boolean isJavaHeapArchiveSupported();
public native Object getResolvedReferences(Class<?> c);
+ public native void linkClass(Class<?> c);
public native boolean areOpenArchiveHeapObjectsMapped();
// Compiler Directive