Skip to content

Commit

Permalink
8265602: -XX:DumpLoadedClassList should support custom loaders
Browse files Browse the repository at this point in the history
Reviewed-by: ccheung, minqi
  • Loading branch information
iklam committed Aug 6, 2021
1 parent ea02dad commit e7b6f48
Show file tree
Hide file tree
Showing 14 changed files with 510 additions and 144 deletions.
79 changes: 58 additions & 21 deletions src/hotspot/share/cds/archiveBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 0 additions & 3 deletions src/hotspot/share/cds/archiveBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,6 @@ class ArchiveBuilder : public StackObj {
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;
Expand Down
25 changes: 13 additions & 12 deletions src/hotspot/share/cds/archiveUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
175 changes: 175 additions & 0 deletions src/hotspot/share/cds/classListWriter.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

1 comment on commit e7b6f48

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.