1 /*
2 * Copyright (c) 2018, 2021, 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.
5 *
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.
9 *
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).
15 *
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.
19 *
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
22 * questions.
23 *
24 */
25
26 #include "precompiled.hpp"
27
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 "oops/reflectionAccessorImplKlassHelper.hpp"
35 #include "utilities/globalDefinitions.hpp"
36 #include "utilities/ostream.hpp"
37
38
ClassLoaderHierarchyDCmd(outputStream * output,bool heap)39 ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap)
40 : DCmdWithParser(output, heap),
41 _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false"),
42 _verbose("verbose", "Print detailed information.", "BOOLEAN", false, "false"),
43 _fold("fold", "Show loaders of the same name and class as one.", "BOOLEAN", true, "true") {
44 _dcmdparser.add_dcmd_option(&_show_classes);
45 _dcmdparser.add_dcmd_option(&_verbose);
46 _dcmdparser.add_dcmd_option(&_fold);
47 }
48
49
num_arguments()50 int ClassLoaderHierarchyDCmd::num_arguments() {
51 ResourceMark rm;
52 ClassLoaderHierarchyDCmd* dcmd = new ClassLoaderHierarchyDCmd(NULL, false);
53 if (dcmd != NULL) {
54 DCmdMark mark(dcmd);
55 return dcmd->_dcmdparser.num_arguments();
56 } else {
57 return 0;
58 }
59 }
60
61 // Helper class for drawing the branches to the left of a node.
62 class BranchTracker : public StackObj {
63 // "<x>"
64 // " |---<y>"
65 // " | |
66 // " | <z>"
67 // " | |---<z1>
68 // " | |---<z2>
69 // ^^^^^^^ ^^^
70 // A B
71
72 // Some terms for the graphics:
73 // - branch: vertical connection between a node's ancestor to a later sibling.
74 // - branchwork: (A) the string to print as a prefix at the start of each line, contains all branches.
75 // - twig (B): Length of the dashed line connecting a node to its branch.
76 // - branch spacing: how many spaces between branches are printed.
77
78 public:
79
80 enum { max_depth = 64, twig_len = 2, branch_spacing = 5 };
81
82 private:
83
84 char _branches[max_depth];
85 int _pos;
86
87 public:
BranchTracker()88 BranchTracker()
89 : _pos(0) {}
90
push(bool has_branch)91 void push(bool has_branch) {
92 if (_pos < max_depth) {
93 _branches[_pos] = has_branch ? '|' : ' ';
94 }
95 _pos ++; // beyond max depth, omit branch drawing but do count on.
96 }
97
pop()98 void pop() {
99 assert(_pos > 0, "must be");
100 _pos --;
101 }
102
print(outputStream * st)103 void print(outputStream* st) {
104 for (int i = 0; i < _pos; i ++) {
105 st->print("%c%.*s", _branches[i], branch_spacing, " ");
106 }
107 }
108
109 class Mark {
110 BranchTracker& _tr;
111 public:
Mark(BranchTracker & tr,bool has_branch_here)112 Mark(BranchTracker& tr, bool has_branch_here)
113 : _tr(tr) { _tr.push(has_branch_here); }
~Mark()114 ~Mark() { _tr.pop(); }
115 };
116
117 }; // end: BranchTracker
118
119 struct LoadedClassInfo : public ResourceObj {
120 public:
121 LoadedClassInfo* _next;
122 Klass* const _klass;
123 const ClassLoaderData* const _cld;
124
LoadedClassInfoLoadedClassInfo125 LoadedClassInfo(Klass* klass, const ClassLoaderData* cld)
126 : _klass(klass), _cld(cld) {}
127
128 };
129
130 class LoaderTreeNode : public ResourceObj {
131
132 // We walk the CLDG and, for each CLD which is findable, add
133 // a tree node.
134 // To add a node we need its parent node; if the parent node does not yet
135 // exist - because we have not yet encountered the CLD for the parent loader -
136 // we add a preliminary empty LoaderTreeNode for it. This preliminary node
137 // just contains the loader oop and nothing else. Once we encounter the CLD of
138 // this parent loader, we fill in all the other details.
139
140 const oop _loader_oop;
141 const ClassLoaderData* _cld;
142
143 LoaderTreeNode* _child;
144 LoaderTreeNode* _next;
145
146 LoadedClassInfo* _classes;
147 int _num_classes;
148
149 LoadedClassInfo* _hidden_classes;
150 int _num_hidden_classes;
151
152 // In default view, similar tree nodes (same loader class, same name or no name)
153 // are folded into each other to make the output more readable.
154 // _num_folded contains the number of nodes which have been folded into this
155 // one.
156 int _num_folded;
157
print_with_childs(outputStream * st,BranchTracker & branchtracker,bool print_classes,bool verbose) const158 void print_with_childs(outputStream* st, BranchTracker& branchtracker,
159 bool print_classes, bool verbose) const {
160
161 ResourceMark rm;
162
163 if (_cld == NULL) {
164 // Not sure how this could happen: we added a preliminary node for a parent but then never encountered
165 // its CLD?
166 return;
167 }
168
169 // Retrieve information.
170 const Klass* const loader_klass = _cld->class_loader_klass();
171 const Symbol* const loader_name = _cld->name();
172
173 branchtracker.print(st);
174
175 // e.g. "+--- jdk.internal.reflect.DelegatingClassLoader"
176 st->print("+%.*s", BranchTracker::twig_len, "----------");
177 if (_cld->is_the_null_class_loader_data()) {
178 st->print(" <bootstrap>");
179 } else {
180 assert(!_cld->has_class_mirror_holder(), "_cld must be the primary cld");
181 if (loader_name != NULL) {
182 st->print(" \"%s\",", loader_name->as_C_string());
183 }
184 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
185 if (_num_folded > 0) {
186 st->print(" (+ %d more)", _num_folded);
187 }
188 }
189 st->cr();
190
191 // Output following this node (node details and child nodes) - up to the next sibling node
192 // needs to be prefixed with "|" if there is a follow up sibling.
193 const bool have_sibling = _next != NULL;
194 BranchTracker::Mark trm(branchtracker, have_sibling);
195
196 {
197 // optional node details following this node needs to be prefixed with "|"
198 // if there are follow up child nodes.
199 const bool have_child = _child != NULL;
200 BranchTracker::Mark trm(branchtracker, have_child);
201
202 // Empty line
203 branchtracker.print(st);
204 st->cr();
205
206 const int indentation = 18;
207
208 if (verbose) {
209 branchtracker.print(st);
210 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop));
211 branchtracker.print(st);
212 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
213 branchtracker.print(st);
214 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));
215
216 // Empty line
217 branchtracker.print(st);
218 st->cr();
219 }
220
221 if (print_classes) {
222 if (_classes != NULL) {
223 for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
224 // non-strong hidden classes should not live in
225 // the primary CLD of their loaders.
226 assert(lci->_cld == _cld, "must be");
227
228 branchtracker.print(st);
229 if (lci == _classes) { // first iteration
230 st->print("%*s ", indentation, "Classes:");
231 } else {
232 st->print("%*s ", indentation, "");
233 }
234 st->print("%s", lci->_klass->external_name());
235
236 // Special treatment for generated core reflection accessor classes: print invocation target.
237 if (ReflectionAccessorImplKlassHelper::is_generated_accessor(lci->_klass)) {
238 st->print(" (invokes: ");
239 ReflectionAccessorImplKlassHelper::print_invocation_target(st, lci->_klass);
240 st->print(")");
241 }
242
243 st->cr();
244 }
245 branchtracker.print(st);
246 st->print("%*s ", indentation, "");
247 st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es");
248
249 // Empty line
250 branchtracker.print(st);
251 st->cr();
252 }
253
254 if (_hidden_classes != NULL) {
255 for (LoadedClassInfo* lci = _hidden_classes; lci; lci = lci->_next) {
256 branchtracker.print(st);
257 if (lci == _hidden_classes) { // first iteration
258 st->print("%*s ", indentation, "Hidden Classes:");
259 } else {
260 st->print("%*s ", indentation, "");
261 }
262 st->print("%s", lci->_klass->external_name());
263 // For non-strong hidden classes, also print CLD if verbose. Should be a
264 // different one than the primary CLD.
265 assert(lci->_cld != _cld, "must be");
266 if (verbose) {
267 st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
268 }
269 st->cr();
270 }
271 branchtracker.print(st);
272 st->print("%*s ", indentation, "");
273 st->print_cr("(%u hidden class%s)", _num_hidden_classes,
274 (_num_hidden_classes == 1) ? "" : "es");
275
276 // Empty line
277 branchtracker.print(st);
278 st->cr();
279 }
280
281 } // end: print_classes
282
283 } // Pop branchtracker mark
284
285 // Print children, recursively
286 LoaderTreeNode* c = _child;
287 while (c != NULL) {
288 c->print_with_childs(st, branchtracker, print_classes, verbose);
289 c = c->_next;
290 }
291
292 }
293
294 // Helper: Attempt to fold this node into the target node. If success, returns true.
295 // Folding can be done if both nodes are leaf nodes and they refer to the same loader class
296 // and they have the same name or no name (note: leaf check is done by caller).
can_fold_into(LoaderTreeNode * target_node) const297 bool can_fold_into(LoaderTreeNode* target_node) const {
298 assert(is_leaf() && target_node->is_leaf(), "must be leaf");
299 return _cld->class_loader_klass() == target_node->_cld->class_loader_klass() &&
300 _cld->name() == target_node->_cld->name();
301 }
302
303 public:
304
LoaderTreeNode(const oop loader_oop)305 LoaderTreeNode(const oop loader_oop)
306 : _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL),
307 _classes(NULL), _num_classes(0), _hidden_classes(NULL),
308 _num_hidden_classes(0), _num_folded(0)
309 {}
310
set_cld(const ClassLoaderData * cld)311 void set_cld(const ClassLoaderData* cld) {
312 _cld = cld;
313 }
314
add_child(LoaderTreeNode * info)315 void add_child(LoaderTreeNode* info) {
316 info->_next = _child;
317 _child = info;
318 }
319
add_sibling(LoaderTreeNode * info)320 void add_sibling(LoaderTreeNode* info) {
321 assert(info->_next == NULL, "must be");
322 info->_next = _next;
323 _next = info;
324 }
325
add_classes(LoadedClassInfo * first_class,int num_classes,bool has_class_mirror_holder)326 void add_classes(LoadedClassInfo* first_class, int num_classes, bool has_class_mirror_holder) {
327 LoadedClassInfo** p_list_to_add_to;
328 bool is_hidden = first_class->_klass->is_hidden();
329 if (has_class_mirror_holder) {
330 p_list_to_add_to = &_hidden_classes;
331 } else {
332 p_list_to_add_to = &_classes;
333 }
334 // Search tail.
335 while ((*p_list_to_add_to) != NULL) {
336 p_list_to_add_to = &(*p_list_to_add_to)->_next;
337 }
338 *p_list_to_add_to = first_class;
339 if (has_class_mirror_holder) {
340 _num_hidden_classes += num_classes;
341 } else {
342 _num_classes += num_classes;
343 }
344 }
345
cld() const346 const ClassLoaderData* cld() const {
347 return _cld;
348 }
349
loader_oop() const350 const oop loader_oop() const {
351 return _loader_oop;
352 }
353
find(const oop loader_oop)354 LoaderTreeNode* find(const oop loader_oop) {
355 LoaderTreeNode* result = NULL;
356 if (_loader_oop == loader_oop) {
357 result = this;
358 } else {
359 LoaderTreeNode* c = _child;
360 while (c != NULL && result == NULL) {
361 result = c->find(loader_oop);
362 c = c->_next;
363 }
364 }
365 return result;
366 }
367
is_leaf() const368 bool is_leaf() const { return _child == NULL; }
369
370 // Attempt to fold similar nodes among this node's children. We only fold leaf nodes
371 // (no child class loaders).
372 // For non-leaf nodes (class loaders with child class loaders), do this recursivly.
fold_children()373 void fold_children() {
374 LoaderTreeNode* node = _child;
375 LoaderTreeNode* prev = NULL;
376 while (node != NULL) {
377 LoaderTreeNode* matching_node = NULL;
378 if (node->is_leaf()) {
379 // Look among the preceeding node siblings for a match.
380 for (LoaderTreeNode* node2 = _child; node2 != node && matching_node == NULL;
381 node2 = node2->_next) {
382 if (node2->is_leaf() && node->can_fold_into(node2)) {
383 matching_node = node2;
384 }
385 }
386 } else {
387 node->fold_children();
388 }
389 if (matching_node != NULL) {
390 // Increase fold count for the matching node and remove folded node from the child list.
391 matching_node->_num_folded ++;
392 assert(prev != NULL, "Sanity"); // can never happen since we do not fold the first node.
393 prev->_next = node->_next;
394 } else {
395 prev = node;
396 }
397 node = node->_next;
398 }
399 }
400
print_with_childs(outputStream * st,bool print_classes,bool print_add_info) const401 void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const {
402 BranchTracker bwt;
403 print_with_childs(st, bwt, print_classes, print_add_info);
404 }
405
406 };
407
408 class LoadedClassCollectClosure : public KlassClosure {
409 public:
410 LoadedClassInfo* _list;
411 const ClassLoaderData* _cld;
412 int _num_classes;
LoadedClassCollectClosure(const ClassLoaderData * cld)413 LoadedClassCollectClosure(const ClassLoaderData* cld)
414 : _list(NULL), _cld(cld), _num_classes(0) {}
do_klass(Klass * k)415 void do_klass(Klass* k) {
416 LoadedClassInfo* lki = new LoadedClassInfo(k, _cld);
417 lki->_next = _list;
418 _list = lki;
419 _num_classes ++;
420 }
421 };
422
423 class LoaderInfoScanClosure : public CLDClosure {
424
425 const bool _print_classes;
426 const bool _verbose;
427 LoaderTreeNode* _root;
428
fill_in_classes(LoaderTreeNode * info,const ClassLoaderData * cld)429 static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) {
430 assert(info != NULL && cld != NULL, "must be");
431 LoadedClassCollectClosure lccc(cld);
432 const_cast<ClassLoaderData*>(cld)->classes_do(&lccc);
433 if (lccc._num_classes > 0) {
434 info->add_classes(lccc._list, lccc._num_classes, cld->has_class_mirror_holder());
435 }
436 }
437
find_node_or_add_empty_node(oop loader_oop)438 LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) {
439
440 assert(_root != NULL, "root node must exist");
441
442 if (loader_oop == NULL) {
443 return _root;
444 }
445
446 // Check if a node for this oop already exists.
447 LoaderTreeNode* info = _root->find(loader_oop);
448
449 if (info == NULL) {
450 // It does not. Create a node.
451 info = new LoaderTreeNode(loader_oop);
452
453 // Add it to tree.
454 LoaderTreeNode* parent_info = NULL;
455
456 // Recursively add parent nodes if needed.
457 const oop parent_oop = java_lang_ClassLoader::parent(loader_oop);
458 if (parent_oop == NULL) {
459 parent_info = _root;
460 } else {
461 parent_info = find_node_or_add_empty_node(parent_oop);
462 }
463 assert(parent_info != NULL, "must be");
464
465 parent_info->add_child(info);
466 }
467 return info;
468 }
469
470
471 public:
LoaderInfoScanClosure(bool print_classes,bool verbose)472 LoaderInfoScanClosure(bool print_classes, bool verbose)
473 : _print_classes(print_classes), _verbose(verbose), _root(NULL) {
474 _root = new LoaderTreeNode(NULL);
475 }
476
print_results(outputStream * st) const477 void print_results(outputStream* st) const {
478 _root->print_with_childs(st, _print_classes, _verbose);
479 }
480
do_cld(ClassLoaderData * cld)481 void do_cld (ClassLoaderData* cld) {
482
483 // We do not display unloading loaders, for now.
484 if (!cld->is_alive()) {
485 return;
486 }
487
488 const oop loader_oop = cld->class_loader();
489
490 LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop);
491 assert(info != NULL, "must be");
492
493 // Update CLD in node, but only if this is the primary CLD for this loader.
494 if (cld->has_class_mirror_holder() == false) {
495 assert(info->cld() == NULL, "there should be only one primary CLD per loader");
496 info->set_cld(cld);
497 }
498
499 // Add classes.
500 fill_in_classes(info, cld);
501 }
502
fold()503 void fold() {
504 _root->fold_children();
505 }
506
507 };
508
509
510 class ClassLoaderHierarchyVMOperation : public VM_Operation {
511 outputStream* const _out;
512 const bool _show_classes;
513 const bool _verbose;
514 const bool _fold;
515 public:
ClassLoaderHierarchyVMOperation(outputStream * out,bool show_classes,bool verbose,bool fold)516 ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose, bool fold) :
517 _out(out), _show_classes(show_classes), _verbose(verbose), _fold(fold)
518 {}
519
type() const520 VMOp_Type type() const {
521 return VMOp_ClassLoaderHierarchyOperation;
522 }
523
doit()524 void doit() {
525 assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint");
526 ResourceMark rm;
527 LoaderInfoScanClosure cl (_show_classes, _verbose);
528 ClassLoaderDataGraph::loaded_cld_do(&cl);
529 // In non-verbose and non-show-classes mode, attempt to fold the tree.
530 if (_fold) {
531 if (!_verbose && !_show_classes) {
532 cl.fold();
533 }
534 }
535 cl.print_results(_out);
536 }
537 };
538
539 // This command needs to be executed at a safepoint.
execute(DCmdSource source,TRAPS)540 void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {
541 ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value(), _fold.value());
542 VMThread::execute(&op);
543 }
544