Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp
40949 views
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
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
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:
88
BranchTracker()
89
: _pos(0) {}
90
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
98
void pop() {
99
assert(_pos > 0, "must be");
100
_pos --;
101
}
102
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:
112
Mark(BranchTracker& tr, bool has_branch_here)
113
: _tr(tr) { _tr.push(has_branch_here); }
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
125
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
158
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).
297
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
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
311
void set_cld(const ClassLoaderData* cld) {
312
_cld = cld;
313
}
314
315
void add_child(LoaderTreeNode* info) {
316
info->_next = _child;
317
_child = info;
318
}
319
320
void add_sibling(LoaderTreeNode* info) {
321
assert(info->_next == NULL, "must be");
322
info->_next = _next;
323
_next = info;
324
}
325
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
346
const ClassLoaderData* cld() const {
347
return _cld;
348
}
349
350
const oop loader_oop() const {
351
return _loader_oop;
352
}
353
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
368
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.
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
401
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;
413
LoadedClassCollectClosure(const ClassLoaderData* cld)
414
: _list(NULL), _cld(cld), _num_classes(0) {}
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
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
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:
472
LoaderInfoScanClosure(bool print_classes, bool verbose)
473
: _print_classes(print_classes), _verbose(verbose), _root(NULL) {
474
_root = new LoaderTreeNode(NULL);
475
}
476
477
void print_results(outputStream* st) const {
478
_root->print_with_childs(st, _print_classes, _verbose);
479
}
480
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
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:
516
ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose, bool fold) :
517
_out(out), _show_classes(show_classes), _verbose(verbose), _fold(fold)
518
{}
519
520
VMOp_Type type() const {
521
return VMOp_ClassLoaderHierarchyOperation;
522
}
523
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.
540
void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {
541
ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value(), _fold.value());
542
VMThread::execute(&op);
543
}
544
545