mirror of
https://github.com/darlinghq/darling-openjdk.git
synced 2024-11-26 22:00:25 +00:00
8207812: Implement Dynamic CDS Archive
Improve the usability of AppCDS Co-authored-by: Ioi Lam <ioi.lam@oracle.com> Co-authored-by: Jiangli Zhou <jianglizhou@google.com> Reviewed-by: acorn, jiangli, mseledtsov
This commit is contained in:
parent
e6c52060dc
commit
72dc5721e0
@ -111,6 +111,7 @@ ifneq ($(call check-jvm-feature, cds), true)
|
||||
JVM_EXCLUDE_FILES += \
|
||||
classListParser.cpp \
|
||||
classLoaderExt.cpp \
|
||||
dynamicArchive.cpp \
|
||||
filemap.cpp \
|
||||
heapShared.cpp \
|
||||
metaspaceShared.cpp \
|
||||
|
@ -295,14 +295,14 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
|
||||
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 @@ InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* inter
|
||||
ShouldNotReachHere();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -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 @@ bool ClassPathImageEntry::is_modules_image() const {
|
||||
|
||||
#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 @@ void ClassLoader::setup_bootstrap_search_path() {
|
||||
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 @@ void* ClassLoader::get_shared_paths_misc_info() {
|
||||
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::setup_app_search_path(const char *class_path) {
|
||||
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 @@ void ClassLoader::add_to_module_path_entries(const char* path,
|
||||
|
||||
// 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 @@ void ClassLoader::setup_boot_search_path(const char *class_path) {
|
||||
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 @@ bool ClassLoader::update_class_path_entry_list(const char *path,
|
||||
return true;
|
||||
} else {
|
||||
#if INCLUDE_CDS
|
||||
if (DumpSharedSpaces) {
|
||||
if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
|
||||
_shared_paths_misc_info->add_nonexist_path(path);
|
||||
}
|
||||
#endif
|
||||
@ -1334,6 +1335,10 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
|
||||
// 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 @@ char* ClassLoader::skip_uri_protocol(char* source) {
|
||||
// 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 @@ void ClassLoader::record_result(InstanceKlass* ik, const ClassFileStream* stream
|
||||
// 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 @@ void ClassLoader::initialize() {
|
||||
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 @@ void ClassLoader::initialize() {
|
||||
|
||||
#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 @@ void ClassLoader::classLoader_init2(TRAPS) {
|
||||
// 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
|
||||
|
@ -398,7 +398,8 @@ class ClassLoader: AllStatic {
|
||||
// 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 @@ class ClassLoader: AllStatic {
|
||||
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);
|
||||
|
@ -62,7 +62,8 @@ inline ClassPathEntry* ClassLoader::classpath_entry(int n) {
|
||||
// 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 @@ inline ClassPathEntry* ClassLoader::get_next_boot_classpath_entry(ClassPathEntry
|
||||
// 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) {
|
||||
|
@ -62,7 +62,8 @@ void ClassLoaderExt::append_boot_classpath(ClassPathEntry* new_entry) {
|
||||
}
|
||||
|
||||
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::process_module_table(ModuleEntryTable* met, TRAPS) {
|
||||
}
|
||||
}
|
||||
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::setup_search_paths() {
|
||||
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 @@ InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 @@ CompactHashtableWriter::~CompactHashtableWriter() {
|
||||
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 @@ void CompactHashtableWriter::allocate_table() {
|
||||
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 @@ void CompactHashtableWriter::allocate_table() {
|
||||
|
||||
_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 @@ void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table
|
||||
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 @@ void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table
|
||||
// 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);
|
||||
|
@ -100,7 +100,7 @@ public:
|
||||
}; // 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 @@ private:
|
||||
|
||||
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 @@ public:
|
||||
private:
|
||||
void allocate_table();
|
||||
void dump_table(NumberSeq* summary);
|
||||
|
||||
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) {
|
||||
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 size_t estimate_size(int num_entries);
|
||||
};
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
@ -214,13 +212,7 @@ public:
|
||||
_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 @@ public:
|
||||
inline bool empty() {
|
||||
return (_entry_count == 0);
|
||||
}
|
||||
|
||||
static size_t calculate_header_size();
|
||||
};
|
||||
|
||||
template <
|
||||
|
@ -246,7 +246,7 @@ void Dictionary::all_entries_do(KlassClosure* closure) {
|
||||
|
||||
// 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(unsigned int hash, Symbol* name,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
InstanceKlass* Dictionary::find_class(int index, unsigned int hash,
|
||||
Symbol* name) {
|
||||
assert_locked_or_safepoint(SystemDictionary_lock);
|
||||
|
@ -218,7 +218,7 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
|
||||
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
|
||||
|
@ -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 @@ void SharedPathsMiscInfo::print_path(outputStream* out, int type, const char* pa
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ bool SharedPathsMiscInfo::check() {
|
||||
}
|
||||
|
||||
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 @@ bool SharedPathsMiscInfo::check() {
|
||||
}
|
||||
// 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 @@ char* skip_first_path_entry(const char* path) {
|
||||
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 @@ bool SharedPathsMiscInfo::check(jint type, const char* path) {
|
||||
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) {
|
||||
|
@ -67,7 +67,7 @@ protected:
|
||||
|
||||
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 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
bool check();
|
||||
bool check(bool is_static);
|
||||
};
|
||||
|
||||
#endif // SHARE_CLASSFILE_SHAREDPATHSMISCINFO_HPP
|
||||
|
@ -779,9 +779,7 @@ void StringTable::write_to_archive() {
|
||||
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);
|
||||
|
@ -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 @@ static OffsetCompactHashtable<
|
||||
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 @@ static uintx hash_symbol(const char* s, int len, bool useAlt) {
|
||||
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 @@ Symbol* SymbolTable::allocate_symbol(const char* name, int len, bool c_heap) {
|
||||
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 @@ void SymbolTable::symbols_do(SymbolClosure *cl) {
|
||||
// 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 @@ public:
|
||||
};
|
||||
|
||||
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 @@ Symbol* SymbolTable::lookup_dynamic(const char* name,
|
||||
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 @@ struct CopyToArchive : StackObj {
|
||||
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 @@ void SymbolTable::copy_shared_symbol_table(CompactHashtableWriter* writer) {
|
||||
_local_table->do_safepoint_scan(copy);
|
||||
}
|
||||
|
||||
void SymbolTable::write_to_archive() {
|
||||
_shared_table.reset();
|
||||
|
||||
int num_buckets = CompactHashtableWriter::default_num_buckets(
|
||||
_items_count);
|
||||
CompactHashtableWriter writer(num_buckets,
|
||||
&MetaspaceShared::stats()->symbol);
|
||||
copy_shared_symbol_table(&writer);
|
||||
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");
|
||||
size_t SymbolTable::estimate_size_for_archive() {
|
||||
return CompactHashtableWriter::estimate_size(int(_items_count));
|
||||
}
|
||||
|
||||
void SymbolTable::serialize_shared_table_header(SerializeClosure* soc) {
|
||||
_shared_table.serialize_header(soc);
|
||||
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);
|
||||
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");
|
||||
} else {
|
||||
writer.dump(&_dynamic_shared_table, "symbol");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -137,7 +137,7 @@ class SymbolTable : public AllStatic {
|
||||
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 @@ public:
|
||||
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
|
||||
|
@ -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 @@ public:
|
||||
int _id;
|
||||
int _clsfile_size;
|
||||
int _clsfile_crc32;
|
||||
bool _excluded;
|
||||
GrowableArray<DTConstraint>* _verifier_constraints;
|
||||
GrowableArray<char>* _verifier_constraint_flags;
|
||||
|
||||
@ -115,6 +116,15 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public:
|
||||
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:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ class RunTimeSharedDictionary : public OffsetCompactHashtable<
|
||||
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 @@ bool SystemDictionaryShared::is_shared_class_visible_for_classloader(
|
||||
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 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
|
||||
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 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name,
|
||||
|
||||
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 @@ InstanceKlass* SystemDictionaryShared::dump_time_resolve_super_or_fail(
|
||||
}
|
||||
|
||||
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 @@ DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(Insta
|
||||
}
|
||||
|
||||
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::init_dumptime_info(InstanceKlass* k) {
|
||||
}
|
||||
|
||||
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 @@ void SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason)
|
||||
|
||||
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 @@ bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) {
|
||||
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 @@ void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) {
|
||||
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 @@ class ExcludeDumpTimeSharedClasses : StackObj {
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) {
|
||||
|
||||
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 @@ void DumpTimeSharedClassInfo::add_verification_constraint(InstanceKlass* k, Symb
|
||||
|
||||
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 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
|
||||
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 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
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 @@ InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) {
|
||||
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 @@ void SystemDictionaryShared::print_on(outputStream* st) {
|
||||
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;
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ class RunTimeSharedClassInfo;
|
||||
class RunTimeSharedDictionary;
|
||||
|
||||
class SystemDictionaryShared: public SystemDictionary {
|
||||
friend class ExcludeDumpTimeSharedClasses;
|
||||
public:
|
||||
enum {
|
||||
FROM_FIELD_IS_PROTECTED = 1 << 0,
|
||||
@ -211,16 +212,21 @@ private:
|
||||
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 @@ public:
|
||||
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 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
|
||||
|
||||
DEBUG_ONLY(static bool checked_excluded_classes() {return _checked_excluded_classes;})
|
||||
};
|
||||
|
||||
#endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
|
||||
|
@ -94,12 +94,14 @@ bool VerificationType::is_reference_assignable_from(
|
||||
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(),
|
||||
|
@ -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
|
||||
|
||||
|
@ -168,7 +168,7 @@ static void log_jdk_jfr_module_resolution_error(TRAPS) {
|
||||
|
||||
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;
|
||||
|
@ -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) \
|
||||
|
@ -254,10 +254,9 @@ class MetaspaceObj {
|
||||
// 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 @@ class MetaspaceObj {
|
||||
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 @@ class MetaspaceObj {
|
||||
_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; }
|
||||
|
||||
|
1147
src/hotspot/share/memory/dynamicArchive.cpp
Normal file
1147
src/hotspot/share/memory/dynamicArchive.cpp
Normal file
File diff suppressed because it is too large
Load Diff
99
src/hotspot/share/memory/dynamicArchive.hpp
Normal file
99
src/hotspot/share/memory/dynamicArchive.hpp
Normal file
@ -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
|
@ -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_stop(const char *msg, ...) {
|
||||
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 @@ void FileMapInfo::fail_continue(const char *msg, ...) {
|
||||
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 @@ template <int N> static void get_header_version(char (&header_version) [N]) {
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
_file_offset = 0;
|
||||
_file_open = false;
|
||||
_header = (FileMapHeader*)os::malloc(sizeof(FileMapHeader), mtInternal);
|
||||
_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;
|
||||
}
|
||||
|
||||
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 FileMapInfo::populate_header(size_t alignment) {
|
||||
}
|
||||
|
||||
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 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
|
||||
_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 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
|
||||
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 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
|
||||
_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 @@ bool SharedClassPathEntry::validate(bool is_class_path) {
|
||||
// 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 @@ void SharedClassPathEntry::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
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 @@ void FileMapInfo::allocate_shared_path_table() {
|
||||
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 @@ void FileMapInfo::allocate_shared_path_table() {
|
||||
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 @@ void FileMapInfo::allocate_shared_path_table() {
|
||||
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 @@ bool FileMapInfo::validate_shared_path_table() {
|
||||
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 @@ bool FileMapInfo::validate_shared_path_table() {
|
||||
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 @@ bool FileMapInfo::validate_shared_path_table() {
|
||||
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 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
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 @@ bool FileMapInfo::init_from_file(int fd) {
|
||||
_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 @@ bool FileMapInfo::open_for_read() {
|
||||
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 @@ void FileMapInfo::open_for_write() {
|
||||
}
|
||||
|
||||
#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 @@ void FileMapInfo::write_header() {
|
||||
|
||||
_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 @@ void FileMapInfo::write_bytes(const void* buffer, size_t nbytes) {
|
||||
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 @@ void FileMapInfo::write_bytes(const void* buffer, size_t nbytes) {
|
||||
_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 @@ ReservedSpace FileMapInfo::reserve_shared_memory() {
|
||||
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 @@ char* FileMapInfo::map_region(int i, char** top_ret) {
|
||||
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 @@ char* FileMapInfo::map_region(int i, char** top_ret) {
|
||||
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 @@ bool FileMapInfo::map_heap_data(MemRegion **heap_mem, int first,
|
||||
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 @@ bool FileMapInfo::map_heap_data(MemRegion **heap_mem, int first,
|
||||
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 @@ void FileMapInfo::dealloc_archive_heap_regions(MemRegion* regions, int num, bool
|
||||
}
|
||||
#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 @@ bool FileMapInfo::verify_region_checksum(int i) {
|
||||
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::assert_mark(bool check) {
|
||||
}
|
||||
|
||||
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 @@ bool FileMapInfo::_validating_shared_path_table = false;
|
||||
// [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 @@ bool FileMapInfo::initialize() {
|
||||
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 @@ int FileMapHeader::compute_crc() {
|
||||
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 @@ bool FileMapHeader::validate() {
|
||||
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 @@ bool FileMapHeader::validate() {
|
||||
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 @@ bool FileMapInfo::is_in_shared_region(const void* p, int idx) {
|
||||
|
||||
// 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 @@ ClassPathEntry* FileMapInfo::get_classpath_entry_for_jvmti(int i, TRAPS) {
|
||||
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");
|
||||
|
@ -93,7 +93,32 @@ struct ArchiveHeapOopmapInfo {
|
||||
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 @@ struct FileMapHeader : public CDSFileMapHeaderBase {
|
||||
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 @@ struct FileMapHeader : public CDSFileMapHeaderBase {
|
||||
// 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 @@ struct FileMapHeader : public CDSFileMapHeaderBase {
|
||||
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 @@ private:
|
||||
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 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
// 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 @@ public:
|
||||
#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 @@ public:
|
||||
}
|
||||
|
||||
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 @@ public:
|
||||
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) {
|
||||
|
@ -418,8 +418,7 @@ void HeapShared::write_subgraph_info_table() {
|
||||
|
||||
_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(©);
|
||||
|
||||
|
@ -286,16 +286,6 @@ private:
|
||||
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);
|
||||
|
@ -118,4 +118,3 @@ void MemRegion::operator delete(void* p) {
|
||||
void MemRegion::operator delete [](void* p) {
|
||||
FreeHeap(p);
|
||||
}
|
||||
|
||||
|
@ -1220,6 +1220,10 @@ void Metaspace::global_initialize() {
|
||||
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
|
||||
{
|
||||
|
@ -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 @@ void MetaspaceClosure::Ref::update(address new_loc) const {
|
||||
*(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 @@ void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref, Writability w) {
|
||||
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 @@ bool UniqueMetaspaceClosure::do_ref(MetaspaceClosure::Ref* ref, bool read_only)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -101,9 +101,15 @@ public:
|
||||
// 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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ private:
|
||||
}
|
||||
|
||||
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 @@ private:
|
||||
}
|
||||
|
||||
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 @@ private:
|
||||
}
|
||||
|
||||
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 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
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 @@ public:
|
||||
// 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 @@ class UniqueMetaspaceClosure : public MetaspaceClosure {
|
||||
|
||||
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:
|
||||
|
@ -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 @@ bool MetaspaceShared::_remapped_readwrite = false;
|
||||
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,106 +115,98 @@ size_t MetaspaceShared::_core_spaces_size = 0;
|
||||
// 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();
|
||||
}
|
||||
uintx delta;
|
||||
if (DynamicDumpSharedSpaces) {
|
||||
delta = DynamicArchive::object_delta_uintx(newtop);
|
||||
} else {
|
||||
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.");
|
||||
}
|
||||
|
||||
public:
|
||||
DumpRegion(const char* name) : _name(name), _base(NULL), _top(NULL), _end(NULL), _is_packed(false) {}
|
||||
MetaspaceShared::commit_shared_space_to(newtop);
|
||||
_top = newtop;
|
||||
return _top;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
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 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(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 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 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(const ReservedSpace* rs) {
|
||||
_base = _top = rs->base();
|
||||
_end = rs->end();
|
||||
}
|
||||
void init(char* b, char* t, char* e) {
|
||||
_base = b;
|
||||
_top = t;
|
||||
_end = e;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
bool contains(char* p) {
|
||||
return base() <= p && p < top();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _md_region("md");
|
||||
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 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");
|
||||
}
|
||||
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* 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 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
|
||||
// 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 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() {
|
||||
#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 @@ void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() {
|
||||
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 @@ void MetaspaceShared::post_initialize(TRAPS) {
|
||||
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::read_extra_data(const char* filename, TRAPS) {
|
||||
}
|
||||
|
||||
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 @@ void MetaspaceShared::commit_shared_space_to(char* newtop) {
|
||||
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 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
|
||||
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 @@ address MetaspaceShared::cds_i2i_entry_code_buffers(size_t total_size) {
|
||||
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 @@ static void rewrite_nofast_bytecodes_and_calculate_fingerprints() {
|
||||
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* CppVtableCloner<T>::allocate(const char* name) {
|
||||
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 @@ int CppVtableCloner<T>::get_vtable_length(const char* name) {
|
||||
}
|
||||
|
||||
#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 @@ int CppVtableCloner<T>::get_vtable_length(const char* name) {
|
||||
#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 @@ bool MetaspaceShared::is_valid_shared_method(const Method* m) {
|
||||
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 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_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 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 @@ private:
|
||||
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 @@ public:
|
||||
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* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
|
||||
|
||||
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 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
// 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 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
|
||||
// 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 @@ void VM_PopulateDumpSharedSpace::dump_archive_heap_oopmaps(GrowableArray<MemRegi
|
||||
}
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||
|
||||
// Closure for serializing initialization data in from a data area
|
||||
// (ptr_array) read from the shared file.
|
||||
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;
|
||||
}
|
||||
|
||||
class ReadClosure : public SerializeClosure {
|
||||
private:
|
||||
intptr_t** _ptr_array;
|
||||
void ReadClosure::do_u4(u4* p) {
|
||||
intptr_t obj = nextPtr();
|
||||
*p = (u4)(uintx(obj));
|
||||
}
|
||||
|
||||
inline intptr_t nextPtr() {
|
||||
return *(*_ptr_array)++;
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ReadClosure(intptr_t** ptr_array) { _ptr_array = ptr_array; }
|
||||
|
||||
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 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_u4(u4* p) {
|
||||
intptr_t obj = nextPtr();
|
||||
*p = (u4)(uintx(obj));
|
||||
}
|
||||
|
||||
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 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_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 @@ bool MetaspaceShared::is_in_trampoline_frame(address addr) {
|
||||
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 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
|
||||
|
||||
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
|
||||
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 &&
|
||||
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 );
|
||||
|
||||
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 @@ void MetaspaceShared::initialize_shared_spaces() {
|
||||
// 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 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
|
||||
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;
|
||||
|
@ -47,6 +47,124 @@ public:
|
||||
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 @@ class MetaspaceShared : AllStatic {
|
||||
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 @@ class MetaspaceShared : AllStatic {
|
||||
}
|
||||
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 @@ class MetaspaceShared : AllStatic {
|
||||
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 @@ class MetaspaceShared : AllStatic {
|
||||
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 @@ class MetaspaceShared : AllStatic {
|
||||
#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 @@ class MetaspaceShared : AllStatic {
|
||||
|
||||
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;
|
||||
};
|
||||
|
@ -711,13 +711,14 @@ jint universe_init() {
|
||||
{
|
||||
SymbolTable::create_table();
|
||||
StringTable::create_table();
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
if (DumpSharedSpaces) {
|
||||
MetaspaceShared::prepare_for_dumping();
|
||||
}
|
||||
#endif
|
||||
if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
|
||||
MetaspaceShared::prepare_for_dumping();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strlen(VerifySubSet) > 0) {
|
||||
Universe::initialize_verify_flags();
|
||||
}
|
||||
|
@ -288,12 +288,16 @@ public:
|
||||
|
||||
// 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) {
|
||||
|
@ -371,7 +371,9 @@ void ConstantPool::remove_unshareable_info() {
|
||||
// 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 @@ void ConstantPool::remove_unshareable_info() {
|
||||
_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,
|
||||
|
@ -697,7 +697,7 @@ void ConstantPoolCache::remove_unshareable_info() {
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -451,7 +451,7 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, unsigned kind, Klass
|
||||
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 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
||||
}
|
||||
set_annotations(NULL);
|
||||
|
||||
if (DumpSharedSpaces) {
|
||||
if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
|
||||
SystemDictionaryShared::remove_dumptime_info(this);
|
||||
}
|
||||
}
|
||||
@ -2225,8 +2225,8 @@ bool InstanceKlass::should_store_fingerprint(bool is_unsafe_anonymous) {
|
||||
// (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 @@ void InstanceKlass::remove_unshareable_info() {
|
||||
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 @@ void InstanceKlass::unload_class(InstanceKlass* ik) {
|
||||
// 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 @@ void InstanceKlass::print_class_load_logging(ClassLoaderData* loader_data,
|
||||
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());
|
||||
|
@ -525,7 +525,8 @@ void Klass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
}
|
||||
|
||||
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_unshareable_info() {
|
||||
}
|
||||
|
||||
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());
|
||||
|
@ -958,23 +958,30 @@ void Method::clear_code(bool acquire_lock /* = true */) {
|
||||
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();
|
||||
}
|
||||
|
@ -468,9 +468,15 @@ class Method : public Metadata {
|
||||
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 @@ class Method : public Metadata {
|
||||
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;
|
||||
}
|
||||
|
@ -74,6 +74,13 @@ void Symbol::operator delete(void *p) {
|
||||
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
|
||||
//
|
||||
|
@ -169,6 +169,7 @@ class Symbol : public MetaspaceObj {
|
||||
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.
|
||||
|
@ -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 @@ CDSOffsets* CDSOffsets::_all = NULL;
|
||||
|
||||
#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 @@ CDSOffsets* CDSOffsets::_all = NULL;
|
||||
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) {
|
||||
|
@ -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 @@ WB_ENTRY(jboolean, WB_IsSharingEnabled(JNIEnv* env, jobject wb))
|
||||
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_ENTRY(jobject, WB_GetResolvedReferences(JNIEnv* env, jobject wb, jclass clazz
|
||||
}
|
||||
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 @@ static JNINativeMethod methods[] = {
|
||||
{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 },
|
||||
|
@ -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 @@ intx Arguments::_Tier4InvocationThreshold = Tier4InvocationThreshold;
|
||||
bool Arguments::_enable_preview = false;
|
||||
|
||||
char* Arguments::SharedArchivePath = NULL;
|
||||
char* Arguments::SharedDynamicArchivePath = NULL;
|
||||
|
||||
AgentLibraryList Arguments::_libraryList;
|
||||
AgentLibraryList Arguments::_agentList;
|
||||
@ -1469,7 +1471,8 @@ const char* unsupported_options[] = { "--limit-modules",
|
||||
"--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 @@ void Arguments::check_unsupported_dumping_properties() {
|
||||
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 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
||||
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 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
||||
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 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) {
|
||||
// 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 @@ void Arguments::set_shared_spaces_flags() {
|
||||
}
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
// Sharing support
|
||||
// Construct the path to the archive
|
||||
char* Arguments::get_default_shared_archive_path() {
|
||||
@ -3446,16 +3465,105 @@ char* Arguments::get_default_shared_archive_path() {
|
||||
return default_archive_path;
|
||||
}
|
||||
|
||||
static char* get_shared_archive_path() {
|
||||
char *shared_archive_path;
|
||||
if (SharedArchiveFile == NULL) {
|
||||
shared_archive_path = Arguments::get_default_shared_archive_path();
|
||||
} else {
|
||||
shared_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments);
|
||||
int Arguments::num_archives(const char* archive_path) {
|
||||
if (archive_path == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return shared_archive_path;
|
||||
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) {
|
||||
SharedArchivePath = get_default_shared_archive_path();
|
||||
} else {
|
||||
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 (SharedArchivePath != NULL);
|
||||
}
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Determine whether LogVMOutput should be implicitly turned on.
|
||||
static bool use_vm_log() {
|
||||
@ -3786,11 +3894,12 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
|
||||
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
|
||||
|
@ -484,6 +484,11 @@ class Arguments : AllStatic {
|
||||
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 @@ class Arguments : AllStatic {
|
||||
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 @@ class Arguments : AllStatic {
|
||||
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; }
|
||||
|
@ -2359,6 +2359,9 @@ define_pd_global(uint64_t,MaxRAM, 1ULL*G);
|
||||
"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 @@ define_pd_global(uint64_t,MaxRAM, 1ULL*G);
|
||||
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") \
|
||||
\
|
||||
|
@ -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 @@ void before_exit(JavaThread* thread) {
|
||||
// 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();
|
||||
|
||||
|
@ -153,9 +153,12 @@ Mutex* DCmdFactory_lock = NULL;
|
||||
#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 @@ void mutex_init() {
|
||||
#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 @@ void mutex_init() {
|
||||
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) {
|
||||
|
@ -132,9 +132,12 @@ extern Mutex* DCmdFactory_lock; // serialize access to DCmdFact
|
||||
#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
|
||||
|
@ -3964,10 +3964,8 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||
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 @@ void Threads::create_vm_init_agents() {
|
||||
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) {
|
||||
|
@ -314,6 +314,23 @@ hotspot_cds = \
|
||||
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 \
|
||||
|
@ -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 @@ public class AppendClasspath {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ public class BootClassPathMismatch {
|
||||
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 @@ public class BootClassPathMismatch {
|
||||
|
||||
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 @@ public class BootClassPathMismatch {
|
||||
|
||||
TestCommon.dump(appJar, appClasses, "-Xbootclasspath/a:" + appJar);
|
||||
TestCommon.run(
|
||||
"-Xlog:cds",
|
||||
"-cp", appJar, "-Xbootclasspath/a:" + otherJar, "Hello")
|
||||
.assertAbnormalExit(mismatchMessage);
|
||||
}
|
||||
@ -148,6 +155,7 @@ public class BootClassPathMismatch {
|
||||
String appClasses[] = {"Hello"};
|
||||
TestCommon.dump(appJar, appClasses);
|
||||
TestCommon.run(
|
||||
"-Xlog:cds",
|
||||
"-cp", appJar, "-Xbootclasspath/a:" + appJar, "Hello")
|
||||
.assertAbnormalExit(mismatchMessage);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 @@ public class CaseSensitiveClassPath {
|
||||
}
|
||||
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");
|
||||
|
@ -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 @@ public class CommandLineFlagCombo {
|
||||
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++) {
|
||||
|
@ -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 @@ public class CommandLineFlagComboNegative {
|
||||
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",
|
||||
|
@ -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 @@ import java.util.Arrays;
|
||||
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 @@ public class DirClasspathTest {
|
||||
|
||||
// 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 @@ public class DirClasspathTest {
|
||||
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 @@ public class DirClasspathTest {
|
||||
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");
|
||||
}
|
||||
|
@ -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 @@ public class JvmtiAddPath {
|
||||
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 @@ public class JvmtiAddPath {
|
||||
|
||||
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");
|
||||
|
@ -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 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
* @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 @@ public class LotsOfClasses {
|
||||
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) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ public class PackageSealing {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ public class ProhibitedPackage {
|
||||
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
|
||||
|
@ -71,7 +71,18 @@ public class SharedArchiveConsistency {
|
||||
|
||||
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 @@ public class SharedArchiveConsistency {
|
||||
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 class SharedArchiveConsistency {
|
||||
|
||||
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 class SharedArchiveConsistency {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class SharedArchiveConsistency {
|
||||
// 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 @@ public class SharedArchiveConsistency {
|
||||
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 @@ public class SharedArchiveConsistency {
|
||||
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);
|
||||
}
|
||||
|
@ -32,10 +32,28 @@ import jdk.test.lib.cds.CDSTestUtils.Result;
|
||||
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 @@ import java.util.Date;
|
||||
*/
|
||||
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 @@ public class TestCommon extends CDSTestUtils {
|
||||
// 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 @@ public class TestCommon extends CDSTestUtils {
|
||||
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 @@ public class TestCommon extends CDSTestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 @@ public class TestCommon extends CDSTestUtils {
|
||||
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 @@ public class TestCommon extends CDSTestUtils {
|
||||
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 @@ public class TestCommon extends CDSTestUtils {
|
||||
// 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 @@ public class TestCommon extends CDSTestUtils {
|
||||
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 @@ public class TestCommon extends CDSTestUtils {
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
// Convenience concatenation utils
|
||||
public static String[] list(String ...args) {
|
||||
return args;
|
||||
@ -336,6 +508,15 @@ public class TestCommon extends CDSTestUtils {
|
||||
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 @@ public class TestCommon extends CDSTestUtils {
|
||||
}
|
||||
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) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 class TraceLongClasspath {
|
||||
|
||||
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 @@ public class TraceLongClasspath {
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ public class WrongClasspath {
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
@ -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 @@ public class ClassListFormatA extends ClassListFormatBase {
|
||||
"input line too long (must be no longer than " + _max_allowed_line + " chars");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ public class ClassListFormatB extends ClassListFormatBase {
|
||||
"If source location is specified, id must be also specified");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,4 +79,3 @@ public class ClassListFormatBase {
|
||||
return TestCommon.list(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ public class ClassListFormatC extends ClassListFormatBase {
|
||||
"If source location is not specified, interface(s) must not be specified");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ public class ClassListFormatD extends ClassListFormatBase {
|
||||
"Interface id 2 is not yet loaded");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ public class ClassListFormatE extends ClassListFormatBase {
|
||||
"The specified super class CustomLoadee (id 4) does not match actual super class java.lang.Object");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ public class HelloCustom {
|
||||
|
||||
// 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 @@ public class HelloCustom {
|
||||
use_whitebox_jar,
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:+WhiteBoxAPI",
|
||||
"Hello", customJarPath));
|
||||
"HelloUnload", customJarPath, "true", "true"));
|
||||
TestCommon.checkExec(output);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ public class HelloCustom_JFR {
|
||||
HelloCustom.run("-XX:StartFlightRecording=dumponexit=true", "-Xlog:cds+jvmti=debug");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user