2
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
3
* Copyright (c) 2018 SAP SE. All rights reserved.
4
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6
* This code is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License version 2 only, as
8
* published by the Free Software Foundation.
10
* This code is distributed in the hope that it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* version 2 for more details (a copy is included in the LICENSE file that
14
* accompanied this code).
16
* You should have received a copy of the GNU General Public License version
17
* 2 along with this work; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
* or visit www.oracle.com if you need additional information or have any
26
#include "precompiled.hpp"
28
#include "classfile/classLoaderData.inline.hpp"
29
#include "classfile/classLoaderDataGraph.hpp"
30
#include "classfile/classLoaderHierarchyDCmd.hpp"
31
#include "memory/allocation.hpp"
32
#include "memory/resourceArea.hpp"
33
#include "runtime/safepoint.hpp"
34
#include "utilities/globalDefinitions.hpp"
35
#include "utilities/ostream.hpp"
38
ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap)
39
: DCmdWithParser(output, heap),
40
_show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false"),
41
_verbose("verbose", "Print detailed information.", "BOOLEAN", false, "false"),
42
_fold("fold", "Show loaders of the same name and class as one.", "BOOLEAN", false, "true") {
43
_dcmdparser.add_dcmd_option(&_show_classes);
44
_dcmdparser.add_dcmd_option(&_verbose);
45
_dcmdparser.add_dcmd_option(&_fold);
48
// Helper class for drawing the branches to the left of a node.
49
class BranchTracker : public StackObj {
59
// Some terms for the graphics:
60
// - branch: vertical connection between a node's ancestor to a later sibling.
61
// - branchwork: (A) the string to print as a prefix at the start of each line, contains all branches.
62
// - twig (B): Length of the dashed line connecting a node to its branch.
63
// - branch spacing: how many spaces between branches are printed.
67
enum { max_depth = 64, twig_len = 2, branch_spacing = 5 };
71
char _branches[max_depth];
78
void push(bool has_branch) {
79
if (_pos < max_depth) {
80
_branches[_pos] = has_branch ? '|' : ' ';
82
_pos ++; // beyond max depth, omit branch drawing but do count on.
86
assert(_pos > 0, "must be");
90
void print(outputStream* st) {
91
for (int i = 0; i < _pos; i ++) {
92
st->print("%c%.*s", _branches[i], branch_spacing, " ");
99
Mark(BranchTracker& tr, bool has_branch_here)
100
: _tr(tr) { _tr.push(has_branch_here); }
101
~Mark() { _tr.pop(); }
104
}; // end: BranchTracker
106
struct LoadedClassInfo : public ResourceObj {
108
LoadedClassInfo* _next;
110
const ClassLoaderData* const _cld;
112
LoadedClassInfo(Klass* klass, const ClassLoaderData* cld)
113
: _next(nullptr), _klass(klass), _cld(cld) {}
117
class LoaderTreeNode : public ResourceObj {
119
// We walk the CLDG and, for each CLD which is findable, add
121
// To add a node we need its parent node; if the parent node does not yet
122
// exist - because we have not yet encountered the CLD for the parent loader -
123
// we add a preliminary empty LoaderTreeNode for it. This preliminary node
124
// just contains the loader oop and nothing else. Once we encounter the CLD of
125
// this parent loader, we fill in all the other details.
127
const oop _loader_oop;
128
const ClassLoaderData* _cld; // May be null if loader never loaded anything
130
LoaderTreeNode* _child;
131
LoaderTreeNode* _next;
133
LoadedClassInfo* _classes;
136
LoadedClassInfo* _hidden_classes;
137
int _num_hidden_classes;
139
// In default view, similar tree nodes (same loader class, same name or no name)
140
// are folded into each other to make the output more readable.
141
// _num_folded contains the number of nodes which have been folded into this
145
// Returns Klass of loader; null for bootstrap loader
146
const Klass* loader_klass() const {
147
return (_loader_oop != nullptr) ? _loader_oop->klass() : nullptr;
150
// Returns ResourceArea-allocated class name of loader class; "" if there is no klass (bootstrap loader)
151
const char* loader_class_name() const {
152
const Klass* klass = loader_klass();
153
return klass != nullptr ? klass->external_name() : "";
156
// Returns oop of loader name; null for bootstrap; null if no name was set
157
oop loader_name_oop() const {
158
return (_loader_oop != nullptr) ? java_lang_ClassLoader::name(_loader_oop) : nullptr;
161
// Returns ResourceArea-allocated name of loader, "" if none is set
162
const char* loader_name() const {
163
oop name_oop = loader_name_oop();
164
return name_oop != nullptr ? java_lang_String::as_utf8_string(name_oop) : "";
167
bool is_bootstrap() const {
168
if (_loader_oop == nullptr) {
169
assert(_cld != nullptr && _cld->is_boot_class_loader_data(), "bootstrap loader must have CLD");
175
void print_with_child_nodes(outputStream* st, BranchTracker& branchtracker,
176
bool print_classes, bool verbose) const {
178
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
182
// Retrieve information.
183
const Klass* const the_loader_klass = loader_klass();
184
const char* const the_loader_class_name = loader_class_name();
185
const char* const the_loader_name = loader_name();
187
branchtracker.print(st);
189
// e.g. +-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
190
st->print("+%.*s", BranchTracker::twig_len, "----------");
191
if (is_bootstrap()) {
192
st->print(" <bootstrap>");
194
if (the_loader_name[0] != '\0') {
195
st->print(" \"%s\",", the_loader_name);
197
st->print(" %s", the_loader_class_name);
198
if (_num_folded > 0) {
199
st->print(" (+ %d more)", _num_folded);
204
// Output following this node (node details and child nodes) - up to the next sibling node
205
// needs to be prefixed with "|" if there is a follow up sibling.
206
const bool have_sibling = _next != nullptr;
207
BranchTracker::Mark trm(branchtracker, have_sibling);
210
// optional node details following this node needs to be prefixed with "|"
211
// if there are follow up child nodes.
212
const bool have_child = _child != nullptr;
213
BranchTracker::Mark trm(branchtracker, have_child);
216
branchtracker.print(st);
219
const int indentation = 18;
222
branchtracker.print(st);
223
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop));
224
branchtracker.print(st);
225
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
226
branchtracker.print(st);
227
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(the_loader_klass));
230
branchtracker.print(st);
235
if (_classes != nullptr) {
236
assert(_cld != nullptr, "we have classes, we should have a CLD");
237
for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
238
// non-strong hidden classes should not live in
239
// the primary CLD of their loaders.
240
assert(lci->_cld == _cld, "must be");
242
branchtracker.print(st);
243
if (lci == _classes) { // first iteration
244
st->print("%*s ", indentation, "Classes:");
246
st->print("%*s ", indentation, "");
248
st->print("%s", lci->_klass->external_name());
251
branchtracker.print(st);
252
st->print("%*s ", indentation, "");
253
st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es");
256
branchtracker.print(st);
260
if (_hidden_classes != nullptr) {
261
assert(_cld != nullptr, "we have classes, we should have a CLD");
262
for (LoadedClassInfo* lci = _hidden_classes; lci; lci = lci->_next) {
263
branchtracker.print(st);
264
if (lci == _hidden_classes) { // first iteration
265
st->print("%*s ", indentation, "Hidden Classes:");
267
st->print("%*s ", indentation, "");
269
st->print("%s", lci->_klass->external_name());
270
// For non-strong hidden classes, also print CLD if verbose. Should be a
271
// different one than the primary CLD.
272
assert(lci->_cld != _cld, "must be");
274
st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
278
branchtracker.print(st);
279
st->print("%*s ", indentation, "");
280
st->print_cr("(%u hidden class%s)", _num_hidden_classes,
281
(_num_hidden_classes == 1) ? "" : "es");
284
branchtracker.print(st);
288
} // end: print_classes
290
} // Pop branchtracker mark
292
// Print children, recursively
293
LoaderTreeNode* c = _child;
294
while (c != nullptr) {
295
c->print_with_child_nodes(st, branchtracker, print_classes, verbose);
301
// Helper: Attempt to fold this node into the target node. If success, returns true.
302
// Folding can be done if both nodes are leaf nodes and they refer to the same loader class
303
// and they have the same name or no name (note: leaf check is done by caller).
304
bool can_fold_into(const LoaderTreeNode* target_node) const {
305
assert(is_leaf() && target_node->is_leaf(), "must be leaf");
307
// Must have the same non-null klass
308
const Klass* k = loader_klass();
309
if (k == nullptr || k != target_node->loader_klass()) {
313
// Must have the same loader name, or none
314
if (::strcmp(loader_name(), target_node->loader_name()) != 0) {
323
LoaderTreeNode(const oop loader_oop)
324
: _loader_oop(loader_oop), _cld(nullptr), _child(nullptr), _next(nullptr),
325
_classes(nullptr), _num_classes(0), _hidden_classes(nullptr),
326
_num_hidden_classes(0), _num_folded(0)
329
void set_cld(const ClassLoaderData* cld) {
330
assert(_cld == nullptr, "there should be only one primary CLD per loader");
334
void add_child(LoaderTreeNode* info) {
335
info->_next = _child;
339
void add_sibling(LoaderTreeNode* info) {
340
assert(info->_next == nullptr, "must be");
345
void add_classes(LoadedClassInfo* first_class, int num_classes, bool has_class_mirror_holder) {
346
LoadedClassInfo** p_list_to_add_to;
347
bool is_hidden = first_class->_klass->is_hidden();
348
if (has_class_mirror_holder) {
349
p_list_to_add_to = &_hidden_classes;
351
p_list_to_add_to = &_classes;
354
while ((*p_list_to_add_to) != nullptr) {
355
p_list_to_add_to = &(*p_list_to_add_to)->_next;
357
*p_list_to_add_to = first_class;
358
if (has_class_mirror_holder) {
359
_num_hidden_classes += num_classes;
361
_num_classes += num_classes;
365
LoaderTreeNode* find(const oop loader_oop) {
366
LoaderTreeNode* result = nullptr;
367
if (_loader_oop == loader_oop) {
370
LoaderTreeNode* c = _child;
371
while (c != nullptr && result == nullptr) {
372
result = c->find(loader_oop);
379
bool is_leaf() const { return _child == nullptr; }
381
// Attempt to fold similar nodes among this node's children. We only fold leaf nodes
382
// (no child class loaders).
383
// For non-leaf nodes (class loaders with child class loaders), do this recursively.
384
void fold_children() {
385
LoaderTreeNode* node = _child;
386
LoaderTreeNode* prev = nullptr;
388
while (node != nullptr) {
389
LoaderTreeNode* matching_node = nullptr;
390
if (node->is_leaf()) {
391
// Look among the preceding node siblings for a match.
392
for (LoaderTreeNode* node2 = _child; node2 != node && matching_node == nullptr;
393
node2 = node2->_next) {
394
if (node2->is_leaf() && node->can_fold_into(node2)) {
395
matching_node = node2;
399
node->fold_children();
401
if (matching_node != nullptr) {
402
// Increase fold count for the matching node and remove folded node from the child list.
403
matching_node->_num_folded ++;
404
assert(prev != nullptr, "Sanity"); // can never happen since we do not fold the first node.
405
prev->_next = node->_next;
413
void print_with_child_nodes(outputStream* st, bool print_classes, bool print_add_info) const {
415
print_with_child_nodes(st, bwt, print_classes, print_add_info);
420
class LoadedClassCollectClosure : public KlassClosure {
422
LoadedClassInfo* _list;
423
const ClassLoaderData* _cld;
425
LoadedClassCollectClosure(const ClassLoaderData* cld)
426
: _list(nullptr), _cld(cld), _num_classes(0) {}
427
void do_klass(Klass* k) {
428
LoadedClassInfo* lki = new LoadedClassInfo(k, _cld);
435
class LoaderInfoScanClosure : public CLDClosure {
437
const bool _print_classes;
439
LoaderTreeNode* _root;
441
static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) {
442
assert(info != nullptr && cld != nullptr, "must be");
443
LoadedClassCollectClosure lccc(cld);
444
const_cast<ClassLoaderData*>(cld)->classes_do(&lccc);
445
if (lccc._num_classes > 0) {
446
info->add_classes(lccc._list, lccc._num_classes, cld->has_class_mirror_holder());
450
LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) {
452
assert(_root != nullptr, "root node must exist");
454
if (loader_oop == nullptr) {
458
// Check if a node for this oop already exists.
459
LoaderTreeNode* info = _root->find(loader_oop);
461
if (info == nullptr) {
462
// It does not. Create a node.
463
info = new LoaderTreeNode(loader_oop);
466
LoaderTreeNode* parent_info = nullptr;
468
// Recursively add parent nodes if needed.
469
const oop parent_oop = java_lang_ClassLoader::parent(loader_oop);
470
if (parent_oop == nullptr) {
473
parent_info = find_node_or_add_empty_node(parent_oop);
475
assert(parent_info != nullptr, "must be");
477
parent_info->add_child(info);
484
LoaderInfoScanClosure(bool print_classes, bool verbose)
485
: _print_classes(print_classes), _verbose(verbose), _root(nullptr) {
486
_root = new LoaderTreeNode(nullptr);
489
void print_results(outputStream* st) const {
490
_root->print_with_child_nodes(st, _print_classes, _verbose);
493
void do_cld (ClassLoaderData* cld) {
495
// We do not display unloading loaders, for now.
496
if (!cld->is_alive()) {
500
const oop loader_oop = cld->class_loader();
502
LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop);
503
assert(info != nullptr, "must be");
505
// Update CLD in node, but only if this is the primary CLD for this loader.
506
if (cld->has_class_mirror_holder() == false) {
511
fill_in_classes(info, cld);
515
_root->fold_children();
521
class ClassLoaderHierarchyVMOperation : public VM_Operation {
522
outputStream* const _out;
523
const bool _show_classes;
527
ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose, bool fold) :
528
_out(out), _show_classes(show_classes), _verbose(verbose), _fold(fold)
531
VMOp_Type type() const {
532
return VMOp_ClassLoaderHierarchyOperation;
536
assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint");
538
LoaderInfoScanClosure cl (_show_classes, _verbose);
539
ClassLoaderDataGraph::loaded_cld_do(&cl);
540
// In non-verbose and non-show-classes mode, attempt to fold the tree.
542
if (!_verbose && !_show_classes) {
546
cl.print_results(_out);
550
// This command needs to be executed at a safepoint.
551
void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {
552
ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value(), _fold.value());
553
VMThread::execute(&op);