8265602: -XX:DumpLoadedClassList should support custom loaders

Reviewed-by: ccheung, minqi
This commit is contained in:
Ioi Lam 2021-08-06 05:58:48 +00:00
parent ea02dade43
commit e7b6f48182
14 changed files with 512 additions and 146 deletions

View File

@ -161,9 +161,6 @@ ArchiveBuilder::ArchiveBuilder() :
_rw_src_objs(),
_ro_src_objs(),
_src_obj_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE),
_num_instance_klasses(0),
_num_obj_array_klasses(0),
_num_type_array_klasses(0),
_total_closed_heap_region_size(0),
_total_open_heap_region_size(0),
_estimated_metaspaceobj_bytes(0),
@ -222,14 +219,6 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re
assert(klass->is_klass(), "must be");
if (!is_excluded(klass)) {
_klasses->append(klass);
if (klass->is_instance_klass()) {
_num_instance_klasses ++;
} else if (klass->is_objArray_klass()) {
_num_obj_array_klasses ++;
} else {
assert(klass->is_typeArray_klass(), "sanity");
_num_type_array_klasses ++;
}
}
// See RunTimeClassInfo::get_for()
_estimated_metaspaceobj_bytes += align_up(BytesPerWord, SharedSpaceObjectAlignment);
@ -258,12 +247,6 @@ void ArchiveBuilder::gather_klasses_and_symbols() {
#endif
doit.finish();
log_info(cds)("Number of classes %d", _num_instance_klasses + _num_obj_array_klasses + _num_type_array_klasses);
log_info(cds)(" instance classes = %5d", _num_instance_klasses);
log_info(cds)(" obj array classes = %5d", _num_obj_array_klasses);
log_info(cds)(" type array classes = %5d", _num_type_array_klasses);
log_info(cds)(" symbols = %5d", _symbols->length());
if (DumpSharedSpaces) {
// To ensure deterministic contents in the static archive, we need to ensure that
// we iterate the MetaspaceObjs in a deterministic order. It doesn't matter where
@ -731,31 +714,85 @@ void ArchiveBuilder::relocate_vm_classes() {
}
void ArchiveBuilder::make_klasses_shareable() {
int num_instance_klasses = 0;
int num_boot_klasses = 0;
int num_platform_klasses = 0;
int num_app_klasses = 0;
int num_hidden_klasses = 0;
int num_unlinked_klasses = 0;
int num_unregistered_klasses = 0;
int num_obj_array_klasses = 0;
int num_type_array_klasses = 0;
for (int i = 0; i < klasses()->length(); i++) {
const char* type;
const char* unlinked = "";
const char* hidden = "";
Klass* k = klasses()->at(i);
k->remove_java_mirror();
if (k->is_objArray_klass()) {
// InstanceKlass and TypeArrayKlass will in turn call remove_unshareable_info
// on their array classes.
num_obj_array_klasses ++;
type = "array";
} else if (k->is_typeArray_klass()) {
num_type_array_klasses ++;
type = "array";
k->remove_unshareable_info();
} else {
assert(k->is_instance_klass(), " must be");
num_instance_klasses ++;
InstanceKlass* ik = InstanceKlass::cast(k);
if (DynamicDumpSharedSpaces) {
// For static dump, class loader type are already set.
ik->assign_class_loader_type();
}
if (ik->is_shared_boot_class()) {
type = "boot";
num_boot_klasses ++;
} else if (ik->is_shared_platform_class()) {
type = "plat";
num_platform_klasses ++;
} else if (ik->is_shared_app_class()) {
type = "app";
num_app_klasses ++;
} else {
assert(ik->is_shared_unregistered_class(), "must be");
type = "unreg";
num_unregistered_klasses ++;
}
if (!ik->is_linked()) {
num_unlinked_klasses ++;
unlinked = " ** unlinked";
}
if (ik->is_hidden()) {
num_hidden_klasses ++;
hidden = " ** hidden";
}
MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik);
ik->remove_unshareable_info();
}
if (log_is_enabled(Debug, cds, class)) {
ResourceMark rm;
log_debug(cds, class)("klasses[%4d] = " PTR_FORMAT " %s", i, p2i(to_requested(ik)), ik->external_name());
}
if (log_is_enabled(Debug, cds, class)) {
ResourceMark rm;
log_debug(cds, class)("klasses[%5d] = " PTR_FORMAT " %-5s %s%s%s", i, p2i(to_requested(k)), type, k->external_name(), hidden, unlinked);
}
}
log_info(cds)("Number of classes %d", num_instance_klasses + num_obj_array_klasses + num_type_array_klasses);
log_info(cds)(" instance classes = %5d", num_instance_klasses);
log_info(cds)(" boot = %5d", num_boot_klasses);
log_info(cds)(" app = %5d", num_app_klasses);
log_info(cds)(" platform = %5d", num_platform_klasses);
log_info(cds)(" unregistered = %5d", num_unregistered_klasses);
log_info(cds)(" (hidden) = %5d", num_hidden_klasses);
log_info(cds)(" (unlinked) = %5d", num_unlinked_klasses);
log_info(cds)(" obj array classes = %5d", num_obj_array_klasses);
log_info(cds)(" type array classes = %5d", num_type_array_klasses);
log_info(cds)(" symbols = %5d", _symbols->length());
}
uintx ArchiveBuilder::buffer_to_offset(address p) const {

View File

@ -205,9 +205,6 @@ private:
GrowableArray<SpecialRefInfo>* _special_refs;
// statistics
int _num_instance_klasses;
int _num_obj_array_klasses;
int _num_type_array_klasses;
DumpAllocStats _alloc_stats;
size_t _total_closed_heap_region_size;
size_t _total_open_heap_region_size;

View File

@ -330,23 +330,24 @@ void ReadClosure::do_region(u_char* start, size_t size) {
}
}
fileStream* ClassListWriter::_classlist_file = NULL;
void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) {
if (ClassListWriter::is_enabled()) {
if (SystemDictionaryShared::is_supported_invokedynamic(bootstrap_specifier)) {
ResourceMark rm(THREAD);
const constantPoolHandle& pool = bootstrap_specifier->pool();
int pool_index = bootstrap_specifier->bss_index();
ClassListWriter w;
w.stream()->print("%s %s", LAMBDA_PROXY_TAG, pool->pool_holder()->name()->as_C_string());
CDSIndyInfo cii;
ClassListParser::populate_cds_indy_info(pool, pool_index, &cii, CHECK);
GrowableArray<const char*>* indy_items = cii.items();
for (int i = 0; i < indy_items->length(); i++) {
w.stream()->print(" %s", indy_items->at(i));
if (SystemDictionaryShared::is_builtin_loader(pool->pool_holder()->class_loader_data())) {
// Currently lambda proxy classes are supported only for the built-in loaders.
ResourceMark rm(THREAD);
int pool_index = bootstrap_specifier->bss_index();
ClassListWriter w;
w.stream()->print("%s %s", LAMBDA_PROXY_TAG, pool->pool_holder()->name()->as_C_string());
CDSIndyInfo cii;
ClassListParser::populate_cds_indy_info(pool, pool_index, &cii, CHECK);
GrowableArray<const char*>* indy_items = cii.items();
for (int i = 0; i < indy_items->length(); i++) {
w.stream()->print(" %s", indy_items->at(i));
}
w.stream()->cr();
}
w.stream()->cr();
}
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "cds/classListWriter.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "runtime/mutexLocker.hpp"
fileStream* ClassListWriter::_classlist_file = NULL;
void ClassListWriter::init() {
// For -XX:DumpLoadedClassList=<file> option
if (DumpLoadedClassList != NULL) {
const char* list_name = make_log_name(DumpLoadedClassList, NULL);
_classlist_file = new(ResourceObj::C_HEAP, mtInternal)
fileStream(list_name);
_classlist_file->print_cr("# NOTE: Do not modify this file.");
_classlist_file->print_cr("#");
_classlist_file->print_cr("# This file is generated via the -XX:DumpLoadedClassList=<class_list_file> option");
_classlist_file->print_cr("# and is used at CDS archive dump time (see -Xshare:dump).");
_classlist_file->print_cr("#");
FREE_C_HEAP_ARRAY(char, list_name);
}
}
void ClassListWriter::write(const InstanceKlass* k, const ClassFileStream* cfs) {
assert(is_enabled(), "must be");
if (!ClassLoader::has_jrt_entry()) {
warning("DumpLoadedClassList and CDS are not supported in exploded build");
DumpLoadedClassList = NULL;
return;
}
ClassListWriter w;
write_to_stream(k, w.stream(), cfs);
}
class ClassListWriter::IDTable : public ResourceHashtable<
const InstanceKlass*, int,
15889, // prime number
ResourceObj::C_HEAP> {};
ClassListWriter::IDTable* ClassListWriter::_id_table = NULL;
int ClassListWriter::_total_ids = 0;
int ClassListWriter::get_id(const InstanceKlass* k) {
assert_locked();
if (_id_table == NULL) {
_id_table = new (ResourceObj::C_HEAP, mtClass)IDTable();
}
bool created;
int* v = _id_table->put_if_absent(k, &created);
if (created) {
*v = _total_ids++;
}
return *v;
}
bool ClassListWriter::has_id(const InstanceKlass* k) {
assert_locked();
if (_id_table != NULL) {
return _id_table->get(k) != NULL;
} else {
return false;
}
}
void ClassListWriter::handle_class_unloading(const InstanceKlass* klass) {
assert_locked();
if (_id_table != NULL) {
_id_table->remove(klass);
}
}
void ClassListWriter::write_to_stream(const InstanceKlass* k, outputStream* stream, const ClassFileStream* cfs) {
assert_locked();
ClassLoaderData* loader_data = k->class_loader_data();
if (!SystemDictionaryShared::is_builtin_loader(loader_data)) {
if (cfs == NULL || strncmp(cfs->source(), "file:", 5) != 0) {
return;
}
if (!SystemDictionaryShared::add_unregistered_class(Thread::current(), (InstanceKlass*)k)) {
return;
}
}
{
InstanceKlass* super = k->java_super();
if (super != NULL && !has_id(super)) {
return;
}
Array<InstanceKlass*>* interfaces = k->local_interfaces();
int len = interfaces->length();
for (int i = 0; i < len; i++) {
InstanceKlass* intf = interfaces->at(i);
if (!has_id(intf)) {
return;
}
}
}
if (k->is_hidden()) {
return;
}
if (k->module()->is_patched()) {
return;
}
ResourceMark rm;
stream->print("%s id: %d", k->name()->as_C_string(), get_id(k));
if (!SystemDictionaryShared::is_builtin_loader(loader_data)) {
InstanceKlass* super = k->java_super();
assert(super != NULL, "must be");
stream->print(" super: %d", get_id(super));
Array<InstanceKlass*>* interfaces = k->local_interfaces();
int len = interfaces->length();
if (len > 0) {
stream->print(" interfaces:");
for (int i = 0; i < len; i++) {
InstanceKlass* intf = interfaces->at(i);
stream->print(" %d", get_id(intf));
}
}
#ifdef _WINDOWS
// "file:/C:/dir/foo.jar" -> "C:/dir/foo.jar"
stream->print(" source: %s", cfs->source() + 6);
#else
// "file:/dir/foo.jar" -> "/dir/foo.jar"
stream->print(" source: %s", cfs->source() + 5);
#endif
}
stream->cr();
stream->flush();
}
void ClassListWriter::delete_classlist() {
if (_classlist_file != NULL) {
delete _classlist_file;
}
}

View File

@ -29,54 +29,44 @@
#include "runtime/thread.hpp"
#include "utilities/ostream.hpp"
class ClassListWriter {
friend const char* make_log_name(const char* log_name, const char* force_directory);
class ClassFileStream;
static fileStream* _classlist_file;
MutexLocker _locker;
public:
class ClassListWriter {
#if INCLUDE_CDS
class IDTable;
static fileStream* _classlist_file;
static IDTable* _id_table;
static int _total_ids;
MutexLocker _locker;
static int get_id(const InstanceKlass* k);
static bool has_id(const InstanceKlass* k);
static void assert_locked() { assert_lock_strong(ClassListFile_lock); }
public:
ClassListWriter() : _locker(Thread::current(), ClassListFile_lock, Mutex::_no_safepoint_check_flag) {}
#else
ClassListWriter() : _locker(Thread::current(), NULL, Mutex::_no_safepoint_check_flag) {}
#endif
outputStream* stream() {
return _classlist_file;
}
void handle_class_unloading(const InstanceKlass* klass);
static bool is_enabled() {
#if INCLUDE_CDS
return _classlist_file != NULL && _classlist_file->is_open();
}
#else
public:
static bool is_enabled() {
return false;
#endif
}
#endif // INCLUDE_CDS
static void init() {
#if INCLUDE_CDS
// For -XX:DumpLoadedClassList=<file> option
if (DumpLoadedClassList != NULL) {
const char* list_name = make_log_name(DumpLoadedClassList, NULL);
_classlist_file = new(ResourceObj::C_HEAP, mtInternal)
fileStream(list_name);
_classlist_file->print_cr("# NOTE: Do not modify this file.");
_classlist_file->print_cr("#");
_classlist_file->print_cr("# This file is generated via the -XX:DumpLoadedClassList=<class_list_file> option");
_classlist_file->print_cr("# and is used at CDS archive dump time (see -Xshare:dump).");
_classlist_file->print_cr("#");
FREE_C_HEAP_ARRAY(char, list_name);
}
#endif
}
static void delete_classlist() {
#if INCLUDE_CDS
if (_classlist_file != NULL) {
delete _classlist_file;
}
#endif
}
static void init() NOT_CDS_RETURN;
static void write(const InstanceKlass* k, const ClassFileStream* cfs) NOT_CDS_RETURN;
static void write_to_stream(const InstanceKlass* k, outputStream* stream, const ClassFileStream* cfs = NULL) NOT_CDS_RETURN;
static void delete_classlist() NOT_CDS_RETURN;
};
#endif // SHARE_CDS_CLASSLISTWRITER_HPP

View File

@ -26,6 +26,7 @@
#include "jvm_io.h"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsProtectionDomain.hpp"
#include "cds/classListWriter.hpp"
#include "cds/classListParser.hpp"
#include "cds/cppVtables.hpp"
#include "cds/dumpAllocStats.hpp"
@ -75,6 +76,7 @@
#include "utilities/bitMap.inline.hpp"
#include "utilities/ostream.hpp"
#include "utilities/defaultStream.hpp"
#include "utilities/resourceHash.hpp"
#if INCLUDE_G1GC
#include "gc/g1/g1CollectedHeap.inline.hpp"
#endif
@ -148,16 +150,43 @@ static bool shared_base_valid(char* shared_base) {
}
class DumpClassListCLDClosure : public CLDClosure {
static const int INITIAL_TABLE_SIZE = 1987;
static const int MAX_TABLE_SIZE = 61333;
fileStream *_stream;
ResizeableResourceHashtable<InstanceKlass*, bool,
ResourceObj::C_HEAP, mtClassShared> _dumped_classes;
void dump(InstanceKlass* ik) {
bool created;
_dumped_classes.put_if_absent(ik, &created);
if (!created) {
return;
}
if (_dumped_classes.maybe_grow(MAX_TABLE_SIZE)) {
log_info(cds, hashtables)("Expanded _dumped_classes table to %d", _dumped_classes.table_size());
}
if (ik->java_super()) {
dump(ik->java_super());
}
Array<InstanceKlass*>* interfaces = ik->local_interfaces();
int len = interfaces->length();
for (int i = 0; i < len; i++) {
dump(interfaces->at(i));
}
ClassListWriter::write_to_stream(ik, _stream);
}
public:
DumpClassListCLDClosure(fileStream* f) : CLDClosure() { _stream = f; }
DumpClassListCLDClosure(fileStream* f)
: CLDClosure(), _dumped_classes(INITIAL_TABLE_SIZE) {
_stream = f;
}
void do_cld(ClassLoaderData* cld) {
for (Klass* klass = cld->klasses(); klass != NULL; klass = klass->next_link()) {
if (klass->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(klass);
if (ik->is_shareable()) {
_stream->print_cr("%s", ik->name()->as_C_string());
}
dump(InstanceKlass::cast(klass));
}
}
}
@ -167,6 +196,7 @@ void MetaspaceShared::dump_loaded_classes(const char* file_name, TRAPS) {
fileStream stream(file_name, "w");
if (stream.is_open()) {
MutexLocker lock(ClassLoaderDataGraph_lock);
MutexLocker lock2(ClassListFile_lock, Mutex::_no_safepoint_check_flag);
DumpClassListCLDClosure collect_classes(&stream);
ClassLoaderDataGraph::loaded_cld_do(&collect_classes);
} else {

View File

@ -26,6 +26,7 @@
#include "cds/archiveUtils.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/classListParser.hpp"
#include "cds/classListWriter.hpp"
#include "cds/dynamicArchive.hpp"
#include "cds/filemap.hpp"
#include "cds/heapShared.hpp"
@ -332,7 +333,7 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
return false; // false == k should NOT be excluded
}
bool SystemDictionaryShared::is_sharing_possible(ClassLoaderData* loader_data) {
bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) {
oop class_loader = loader_data->class_loader();
return (class_loader == NULL ||
SystemDictionary::is_system_class_loader(class_loader) ||
@ -433,7 +434,7 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla
// We don't allow duplicated unregistered classes with the same name.
// We only archive the first class with that name that succeeds putting
// itself into the table.
Arguments::assert_is_dumping_archive();
assert(Arguments::is_dumping_archive() || ClassListWriter::is_enabled(), "sanity");
MutexLocker ml(current, UnregisteredClassesTable_lock);
Symbol* name = klass->name();
if (_unregistered_classes_table == NULL) {
@ -547,7 +548,9 @@ void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
}
void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
remove_dumptime_info(klass);
if (Arguments::is_dumping_archive()) {
remove_dumptime_info(klass);
}
if (_unregistered_classes_table != NULL) {
// Remove the class from _unregistered_classes_table: keep the entry but
@ -559,6 +562,11 @@ void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
*v = NULL;
}
}
if (ClassListWriter::is_enabled()) {
ClassListWriter cw;
cw.handle_class_unloading((const InstanceKlass*)klass);
}
}
// Check if a class or any of its supertypes has been redefined.

View File

@ -206,8 +206,7 @@ public:
static void allocate_shared_data_arrays(int size, TRAPS);
// Check if sharing is supported for the class loader.
static bool is_sharing_possible(ClassLoaderData* loader_data);
static bool is_builtin_loader(ClassLoaderData* loader_data);
static bool add_unregistered_class_for_static_archive(Thread* current, InstanceKlass* k);
static InstanceKlass* lookup_super_for_unregistered_class(Symbol* class_name,

View File

@ -676,9 +676,7 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
}
set_annotations(NULL);
if (Arguments::is_dumping_archive()) {
SystemDictionaryShared::handle_class_unloading(this);
}
SystemDictionaryShared::handle_class_unloading(this);
}
bool InstanceKlass::is_record() const {
@ -2620,9 +2618,7 @@ void InstanceKlass::unload_class(InstanceKlass* ik) {
// notify ClassLoadingService of class unload
ClassLoadingService::notify_class_unloaded(ik);
if (Arguments::is_dumping_archive()) {
SystemDictionaryShared::handle_class_unloading(ik);
}
SystemDictionaryShared::handle_class_unloading(ik);
if (log_is_enabled(Info, class, unload)) {
ResourceMark rm;
@ -3591,7 +3587,9 @@ const char* InstanceKlass::internal_name() const {
void InstanceKlass::print_class_load_logging(ClassLoaderData* loader_data,
const ModuleEntry* module_entry,
const ClassFileStream* cfs) const {
log_to_classlist();
if (ClassListWriter::is_enabled()) {
ClassListWriter::write(this, cfs);
}
if (!log_is_enabled(Info, class, load)) {
return;
@ -4132,45 +4130,6 @@ unsigned char * InstanceKlass::get_cached_class_file_bytes() {
}
#endif
bool InstanceKlass::is_shareable() const {
#if INCLUDE_CDS
ClassLoaderData* loader_data = class_loader_data();
if (!SystemDictionaryShared::is_sharing_possible(loader_data)) {
return false;
}
if (is_hidden()) {
return false;
}
if (module()->is_patched()) {
return false;
}
return true;
#else
return false;
#endif
}
void InstanceKlass::log_to_classlist() const {
#if INCLUDE_CDS
ResourceMark rm;
if (ClassListWriter::is_enabled()) {
if (!ClassLoader::has_jrt_entry()) {
warning("DumpLoadedClassList and CDS are not supported in exploded build");
DumpLoadedClassList = NULL;
return;
}
if (is_shareable()) {
ClassListWriter w;
w.stream()->print_cr("%s", name()->as_C_string());
w.stream()->flush();
}
}
#endif // INCLUDE_CDS
}
// Make a step iterating over the class hierarchy under the root class.
// Skips subclasses if requested.
void ClassHierarchyIterator::next() {

View File

@ -27,8 +27,9 @@
* @summary DumpLoadedClassList should exclude generated classes, classes in bootclasspath/a and
* --patch-module.
* @requires vm.cds
* @modules jdk.jfr
* @library /test/lib
* @compile test-classes/ArrayListTest.java
* @compile test-classes/DumpClassListApp.java
* @run driver DumpClassList
*/
@ -40,25 +41,25 @@ import jdk.test.lib.helpers.ClassFileInstaller;
public class DumpClassList {
public static void main(String[] args) throws Exception {
// build The app
String[] appClass = new String[] {"ArrayListTest"};
String[] appClass = new String[] {"DumpClassListApp"};
String classList = "app.list";
JarBuilder.build("app", appClass[0]);
String appJar = TestCommon.getTestJar("app.jar");
// build patch-module
String source = "package java.lang; " +
String source = "package jdk.jfr; " +
"public class NewClass { " +
" static { " +
" System.out.println(\"NewClass\"); "+
" } " +
"}";
ClassFileInstaller.writeClassToDisk("java/lang/NewClass",
InMemoryJavaCompiler.compile("java.lang.NewClass", source, "--patch-module=java.base"),
ClassFileInstaller.writeClassToDisk("jdk/jfr/NewClass",
InMemoryJavaCompiler.compile("jdk.jfr.NewClass", source, "--patch-module=jdk.jfr"),
System.getProperty("test.classes"));
String patchJar = JarBuilder.build("javabase", "java/lang/NewClass");
String patchJar = JarBuilder.build("jdk_jfr", "jdk/jfr/NewClass");
// build bootclasspath/a
String source2 = "package boot.append; " +
@ -76,7 +77,7 @@ public class DumpClassList {
// dump class list
CDSTestUtils.dumpClassList(classList,
"--patch-module=java.base=" + patchJar,
"--patch-module=jdk.jfr=" + patchJar,
"-Xbootclasspath/a:" + appendJar,
"-cp",
appJar,
@ -90,10 +91,14 @@ public class DumpClassList {
.addPrefix("-cp", appJar,
"-Xbootclasspath/a:" + appendJar,
"-Xlog:class+load",
"-Xlog:cds+class=debug",
"-XX:SharedClassListFile=" + classList);
CDSTestUtils.createArchiveAndCheck(opts)
.shouldNotContain("Preload Warning: Cannot find java/lang/invoke/LambdaForm")
.shouldNotContain("Preload Warning: Cannot find boot/append/Foo")
.shouldContain("[info][class,load] boot.append.Foo");
.shouldNotContain("Preload Warning: Cannot find jdk/jfr/NewClass")
.shouldMatch(".info..class,load *. boot.append.Foo") // from -Xlog:class+load
.shouldMatch("cds,class.*boot boot.append.Foo") // from -Xlog:cds+class
.shouldNotMatch(".info..class,load *. jdk.jfr.NewClass"); // from -Xlog:class+load
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2021, 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 -XX:DumpLoadedClassList should support custom loaders
* @bug 8265602
* @requires vm.cds
* @requires vm.cds.custom.loaders
* @library /test/lib
* /test/hotspot/jtreg/runtime/cds/appcds
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* test-classes
* @build CustomLoaderApp OldClass CustomLoadee CustomLoadee2
* CustomLoadee3Child CustomLoadee4WithLambda
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar CustomLoaderApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar custom.jar
* OldClass CustomLoadee
* CustomLoadee2 CustomInterface2_ia CustomInterface2_ib
* CustomLoadee3 CustomLoadee3Child
* CustomLoadee4WithLambda
* @run driver CustomClassListDump
*/
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.helpers.ClassFileInstaller;
public class CustomClassListDump {
private static String appJar = ClassFileInstaller.getJarPath("app.jar");
private static String customJar = ClassFileInstaller.getJarPath("custom.jar");
private static String classList = "app.list";
private static String commandLine[] = {
"-cp", appJar,
"CustomLoaderApp",
customJar,
"unregistered",
"CustomLoadee",
"CustomLoadee2",
"CustomLoadee3Child",
"CustomLoadee4WithLambda",
"OldClass",
};
public static void main(String[] args) throws Exception {
// Dump the classlist and check that custom-loader classes are in there.
CDSTestUtils.dumpClassList(classList, commandLine)
.assertNormalExit();
String listData = new String(Files.readAllBytes(Paths.get(classList)));
check(listData, true, "CustomLoaderApp id: [0-9]+");
check(listData, true, "CustomLoadee id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
check(listData, true, "CustomInterface2_ia id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
check(listData, true, "CustomInterface2_ib id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
check(listData, true, "CustomLoadee2 id: [0-9]+ super: [0-9]+ interfaces: [0-9]+ [0-9]+ source: .*/custom.jar");
check(listData, true, "CustomLoadee3 id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
check(listData, true, "CustomLoadee3Child id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
check(listData, true, "CustomLoadee4WithLambda id: [0-9]+ super: [0-9]+ source: .*/custom.jar");
// We don't support archiving of Lambda proxies for custom loaders.
check(listData, false, "@lambda-proxy.*CustomLoadee4WithLambda");
// Dump the static archive
CDSOptions opts = (new CDSOptions())
.addPrefix("-cp", appJar,
"-Xlog:cds+class=debug",
"-XX:SharedClassListFile=" + classList);
CDSTestUtils.createArchiveAndCheck(opts)
.shouldContain("unreg CustomLoadee")
.shouldContain("unreg CustomLoadee2")
.shouldContain("unreg CustomLoadee3Child")
.shouldContain("unreg OldClass ** unlinked");
// Use the dumped static archive
opts = (new CDSOptions())
.setUseVersion(false)
.addPrefix("-cp", appJar)
.addSuffix("-Xlog:class+load,verification")
.addSuffix(commandLine);
CDSTestUtils.run(opts)
.assertNormalExit("CustomLoadee source: shared objects file",
"CustomLoadee2 source: shared objects file",
"CustomLoadee3Child source: shared objects file",
"OldClass source: shared objects file",
"Verifying class OldClass with old format");
}
static void check(String listData, boolean mustMatch, String regexp) throws Exception {
Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(listData);
boolean found = matcher.find();
if (mustMatch && !found) {
System.out.println(listData);
throw new RuntimeException("Pattern \"" + regexp + "\" not found in classlist");
}
if (!mustMatch && found) {
throw new RuntimeException("Pattern \"" + regexp + "\" found in in classlist: \""
+ matcher.group() + "\"");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2021, 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
@ -63,15 +63,18 @@ public class CustomLoaderApp {
String loaderType = args[1];
log("loaderType = " + loaderType);
String testClass = args[2];
log("testClass = " + testClass);
switch(loaderType) {
case "unregistered":
loadAndUseWithUnregisteredLoader(urls, testClass);
break;
default:
throw new IllegalArgumentException("loader type is wrong: " + loaderType);
for (int i = 2; i < args.length; i++) {
String testClass = args[i];
log("testClass = " + testClass);
switch(loaderType) {
case "unregistered":
loadAndUseWithUnregisteredLoader(urls, testClass);
break;
default:
throw new IllegalArgumentException("loader type is wrong: " + loaderType);
}
}
}
@ -87,7 +90,7 @@ public class CustomLoaderApp {
private static Class loadAndCheck(ClassLoader loader, String className)
throws ClassNotFoundException {
Class c = loader.loadClass(className);
log("class =" + c);
log("class = " + c);
log("loader = " + c.getClassLoader());
// Check that c is defined by the correct loader

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
public class CustomLoadee4WithLambda {
public static void test() {
doit(() -> {
System.out.println("Hello inside a Lambda expression");
});
}
static void doit(Runnable r) {
r.run();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 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,7 +26,7 @@ import java.util.*;
// This is a test case executed by DumpClassList.java to load classes
// from various places to ensure that they are not written to the class list.
public class ArrayListTest {
public class DumpClassListApp {
public static void main(String args[]) throws Exception {
// The following lambda usage should generate various classes like
// java.lang.invoke.LambdaForm$MH/1146743572. All of them should be excluded from
@ -35,7 +35,7 @@ public class ArrayListTest {
a.add("hello world.");
a.forEach(str -> System.out.println(str));
System.out.println(Class.forName("java.lang.NewClass")); // should be excluded from the class list.
System.out.println(Class.forName("jdk.jfr.NewClass")); // should be excluded from the class list.
System.out.println(Class.forName("boot.append.Foo")); // should be excluded from the class list.
}
}