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/classLoaderHierarchyDCmd.hpp"
30 #include "memory/allocation.hpp"
31 #include "memory/resourceArea.hpp"
32 #include "runtime/safepoint.hpp"
33 #include "oops/reflectionAccessorImplKlassHelper.hpp"
34 #include "utilities/globalDefinitions.hpp"
35 #include "utilities/ostream.hpp"
36
37
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 _dcmdparser.add_dcmd_option(&_show_classes);
43 _dcmdparser.add_dcmd_option(&_verbose);
44 }
45
46
47 int ClassLoaderHierarchyDCmd::num_arguments() {
48 ResourceMark rm;
49 ClassLoaderHierarchyDCmd* dcmd = new ClassLoaderHierarchyDCmd(NULL, false);
50 if (dcmd != NULL) {
51 DCmdMark mark(dcmd);
52 return dcmd->_dcmdparser.num_arguments();
53 } else {
54 return 0;
55 }
56 }
57
58 // Helper class for drawing the branches to the left of a node.
59 class BranchTracker : public StackObj {
60 // "<x>"
61 // " |---<y>"
62 // " | |
63 // " | <z>"
110 : _tr(tr) { _tr.push(has_branch_here); }
111 ~Mark() { _tr.pop(); }
112 };
113
114 }; // end: BranchTracker
115
116 struct LoadedClassInfo : public ResourceObj {
117 public:
118 LoadedClassInfo* _next;
119 Klass* const _klass;
120 const ClassLoaderData* const _cld;
121
122 LoadedClassInfo(Klass* klass, const ClassLoaderData* cld)
123 : _klass(klass), _cld(cld) {}
124
125 };
126
127 class LoaderTreeNode : public ResourceObj {
128
129 // We walk the CLDG and, for each CLD which is non-anonymous, add
130 // a tree node. To add a node we need its parent node; if it itself
131 // does not exist yet, we add a preliminary node for it. This preliminary
132 // node just contains its loader oop; later, when encountering its CLD in
133 // our CLDG walk, we complete the missing information in this node.
134
135 const oop _loader_oop;
136 const ClassLoaderData* _cld;
137
138 LoaderTreeNode* _child;
139 LoaderTreeNode* _next;
140
141 LoadedClassInfo* _classes;
142 int _num_classes;
143
144 LoadedClassInfo* _anon_classes;
145 int _num_anon_classes;
146
147 void print_with_childs(outputStream* st, BranchTracker& branchtracker,
148 bool print_classes, bool verbose) const {
149
150 ResourceMark rm;
151
152 if (_cld == NULL) {
153 // Not sure how this could happen: we added a preliminary node for a parent but then never encountered
154 // its CLD?
155 return;
156 }
157
158 // Retrieve information.
159 const Klass* const loader_klass = _cld->class_loader_klass();
160 const Symbol* const loader_name = _cld->name();
161
162 branchtracker.print(st);
163
164 // e.g. "+--- jdk.internal.reflect.DelegatingClassLoader"
165 st->print("+%.*s", BranchTracker::twig_len, "----------");
166 if (_cld->is_the_null_class_loader_data()) {
167 st->print(" <bootstrap>");
168 } else {
169 if (loader_name != NULL) {
170 st->print(" \"%s\",", loader_name->as_C_string());
171 }
172 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
173 st->print(" {" PTR_FORMAT "}", p2i(_loader_oop));
174 }
175 st->cr();
176
177 // Output following this node (node details and child nodes) - up to the next sibling node
178 // needs to be prefixed with "|" if there is a follow up sibling.
179 const bool have_sibling = _next != NULL;
180 BranchTracker::Mark trm(branchtracker, have_sibling);
181
182 {
183 // optional node details following this node needs to be prefixed with "|"
184 // if there are follow up child nodes.
185 const bool have_child = _child != NULL;
186 BranchTracker::Mark trm(branchtracker, have_child);
187
188 // Empty line
189 branchtracker.print(st);
190 st->cr();
191
192 const int indentation = 18;
193
194 if (verbose) {
195 branchtracker.print(st);
196 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
197 branchtracker.print(st);
198 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));
199
200 // Empty line
201 branchtracker.print(st);
202 st->cr();
203 }
204
205 if (print_classes) {
206 if (_classes != NULL) {
207 for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
208 // Non-anonymous classes should live in the primary CLD of its loader
209 assert(lci->_cld == _cld, "must be");
210
211 branchtracker.print(st);
212 if (lci == _classes) { // first iteration
213 st->print("%*s ", indentation, "Classes:");
214 } else {
215 st->print("%*s ", indentation, "");
229 st->print("%*s ", indentation, "");
230 st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es");
231
232 // Empty line
233 branchtracker.print(st);
234 st->cr();
235 }
236
237 if (_anon_classes != NULL) {
238 for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) {
239 branchtracker.print(st);
240 if (lci == _anon_classes) { // first iteration
241 st->print("%*s ", indentation, "Anonymous Classes:");
242 } else {
243 st->print("%*s ", indentation, "");
244 }
245 st->print("%s", lci->_klass->external_name());
246 // For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD.
247 assert(lci->_cld != _cld, "must be");
248 if (verbose) {
249 st->print(" (CLD: " PTR_FORMAT ")", p2i(lci->_cld));
250 }
251 st->cr();
252 }
253 branchtracker.print(st);
254 st->print("%*s ", indentation, "");
255 st->print_cr("(%u anonymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es");
256
257 // Empty line
258 branchtracker.print(st);
259 st->cr();
260 }
261
262 } // end: print_classes
263
264 } // Pop branchtracker mark
265
266 // Print children, recursively
267 LoaderTreeNode* c = _child;
268 while (c != NULL) {
269 c->print_with_childs(st, branchtracker, print_classes, verbose);
270 c = c->_next;
271 }
272
273 }
274
275 public:
276
277 LoaderTreeNode(const oop loader_oop)
278 : _loader_oop(loader_oop), _cld(NULL)
279 , _child(NULL), _next(NULL)
280 , _classes(NULL), _anon_classes(NULL)
281 , _num_classes(0), _num_anon_classes(0) {}
282
283 void set_cld(const ClassLoaderData* cld) {
284 _cld = cld;
285 }
286
287 void add_child(LoaderTreeNode* info) {
288 info->_next = _child;
289 _child = info;
290 }
291
292 void add_sibling(LoaderTreeNode* info) {
293 assert(info->_next == NULL, "must be");
294 info->_next = _next;
295 _next = info;
296 }
297
298 void add_classes(LoadedClassInfo* first_class, int num_classes, bool anonymous) {
299 LoadedClassInfo** p_list_to_add_to = anonymous ? &_anon_classes : &_classes;
300 // Search tail.
301 while ((*p_list_to_add_to) != NULL) {
314 }
315
316 const oop loader_oop() const {
317 return _loader_oop;
318 }
319
320 LoaderTreeNode* find(const oop loader_oop) {
321 LoaderTreeNode* result = NULL;
322 if (_loader_oop == loader_oop) {
323 result = this;
324 } else {
325 LoaderTreeNode* c = _child;
326 while (c != NULL && result == NULL) {
327 result = c->find(loader_oop);
328 c = c->_next;
329 }
330 }
331 return result;
332 }
333
334 void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const {
335 BranchTracker bwt;
336 print_with_childs(st, bwt, print_classes, print_add_info);
337 }
338
339 };
340
341 class LoadedClassCollectClosure : public KlassClosure {
342 public:
343 LoadedClassInfo* _list;
344 const ClassLoaderData* _cld;
345 int _num_classes;
346 LoadedClassCollectClosure(const ClassLoaderData* cld)
347 : _list(NULL), _cld(cld), _num_classes(0) {}
348 void do_klass(Klass* k) {
349 LoadedClassInfo* lki = new LoadedClassInfo(k, _cld);
350 lki->_next = _list;
351 _list = lki;
352 _num_classes ++;
353 }
416 // We do not display unloading loaders, for now.
417 if (cld->is_unloading()) {
418 return;
419 }
420
421 const oop loader_oop = cld->class_loader();
422
423 LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop);
424 assert(info != NULL, "must be");
425
426 // Update CLD in node, but only if this is the primary CLD for this loader.
427 if (cld->is_anonymous() == false) {
428 assert(info->cld() == NULL, "there should be only one primary CLD per loader");
429 info->set_cld(cld);
430 }
431
432 // Add classes.
433 fill_in_classes(info, cld);
434 }
435
436 };
437
438
439 class ClassLoaderHierarchyVMOperation : public VM_Operation {
440 outputStream* const _out;
441 const bool _show_classes;
442 const bool _verbose;
443 public:
444 ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose) :
445 _out(out), _show_classes(show_classes), _verbose(verbose)
446 {}
447
448 VMOp_Type type() const {
449 return VMOp_ClassLoaderHierarchyOperation;
450 }
451
452 void doit() {
453 assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint");
454 ResourceMark rm;
455 LoaderInfoScanClosure cl (_show_classes, _verbose);
456 ClassLoaderDataGraph::cld_do(&cl);
457 cl.print_results(_out);
458 }
459 };
460
461 // This command needs to be executed at a safepoint.
462 void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {
463 ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value());
464 VMThread::execute(&op);
465 }
|
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/classLoaderHierarchyDCmd.hpp"
30 #include "memory/allocation.hpp"
31 #include "memory/resourceArea.hpp"
32 #include "runtime/safepoint.hpp"
33 #include "oops/reflectionAccessorImplKlassHelper.hpp"
34 #include "utilities/globalDefinitions.hpp"
35 #include "utilities/ostream.hpp"
36
37
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", true, "true") {
43 _dcmdparser.add_dcmd_option(&_show_classes);
44 _dcmdparser.add_dcmd_option(&_verbose);
45 _dcmdparser.add_dcmd_option(&_fold);
46 }
47
48
49 int ClassLoaderHierarchyDCmd::num_arguments() {
50 ResourceMark rm;
51 ClassLoaderHierarchyDCmd* dcmd = new ClassLoaderHierarchyDCmd(NULL, false);
52 if (dcmd != NULL) {
53 DCmdMark mark(dcmd);
54 return dcmd->_dcmdparser.num_arguments();
55 } else {
56 return 0;
57 }
58 }
59
60 // Helper class for drawing the branches to the left of a node.
61 class BranchTracker : public StackObj {
62 // "<x>"
63 // " |---<y>"
64 // " | |
65 // " | <z>"
112 : _tr(tr) { _tr.push(has_branch_here); }
113 ~Mark() { _tr.pop(); }
114 };
115
116 }; // end: BranchTracker
117
118 struct LoadedClassInfo : public ResourceObj {
119 public:
120 LoadedClassInfo* _next;
121 Klass* const _klass;
122 const ClassLoaderData* const _cld;
123
124 LoadedClassInfo(Klass* klass, const ClassLoaderData* cld)
125 : _klass(klass), _cld(cld) {}
126
127 };
128
129 class LoaderTreeNode : public ResourceObj {
130
131 // We walk the CLDG and, for each CLD which is non-anonymous, add
132 // a tree node.
133 // To add a node we need its parent node; if the parent node does not yet
134 // exist - because we have not yet encountered the CLD for the parent loader -
135 // we we add a preliminary empty LoaderTreeNode for it. This preliminary node
136 // just contains the loader oop and nothing else. Once we encounter the CLD of
137 // this parent loader, we fill in all the other details.
138
139 const oop _loader_oop;
140 const ClassLoaderData* _cld;
141
142 LoaderTreeNode* _child;
143 LoaderTreeNode* _next;
144
145 LoadedClassInfo* _classes;
146 int _num_classes;
147
148 LoadedClassInfo* _anon_classes;
149 int _num_anon_classes;
150
151 // In default view, similar tree nodes (same loader class, same or no name)
152 // are folded into each other to make the output more readable.
153 // _num_folded contains the number of nodes which have been folded into this
154 // one.
155 int _num_folded;
156
157 void print_with_childs(outputStream* st, BranchTracker& branchtracker,
158 bool print_classes, bool verbose) const {
159
160 ResourceMark rm;
161
162 if (_cld == NULL) {
163 // Not sure how this could happen: we added a preliminary node for a parent but then never encountered
164 // its CLD?
165 return;
166 }
167
168 // Retrieve information.
169 const Klass* const loader_klass = _cld->class_loader_klass();
170 const Symbol* const loader_name = _cld->name();
171
172 branchtracker.print(st);
173
174 // e.g. "+--- jdk.internal.reflect.DelegatingClassLoader"
175 st->print("+%.*s", BranchTracker::twig_len, "----------");
176 if (_cld->is_the_null_class_loader_data()) {
177 st->print(" <bootstrap>");
178 } else {
179 if (loader_name != NULL) {
180 st->print(" \"%s\",", loader_name->as_C_string());
181 }
182 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
183 if (_num_folded > 0) {
184 st->print(" (+ %d more)", _num_folded);
185 }
186 }
187 st->cr();
188
189 // Output following this node (node details and child nodes) - up to the next sibling node
190 // needs to be prefixed with "|" if there is a follow up sibling.
191 const bool have_sibling = _next != NULL;
192 BranchTracker::Mark trm(branchtracker, have_sibling);
193
194 {
195 // optional node details following this node needs to be prefixed with "|"
196 // if there are follow up child nodes.
197 const bool have_child = _child != NULL;
198 BranchTracker::Mark trm(branchtracker, have_child);
199
200 // Empty line
201 branchtracker.print(st);
202 st->cr();
203
204 const int indentation = 18;
205
206 if (verbose) {
207 branchtracker.print(st);
208 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop));
209 branchtracker.print(st);
210 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
211 branchtracker.print(st);
212 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));
213
214 // Empty line
215 branchtracker.print(st);
216 st->cr();
217 }
218
219 if (print_classes) {
220 if (_classes != NULL) {
221 for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
222 // Non-anonymous classes should live in the primary CLD of its loader
223 assert(lci->_cld == _cld, "must be");
224
225 branchtracker.print(st);
226 if (lci == _classes) { // first iteration
227 st->print("%*s ", indentation, "Classes:");
228 } else {
229 st->print("%*s ", indentation, "");
243 st->print("%*s ", indentation, "");
244 st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es");
245
246 // Empty line
247 branchtracker.print(st);
248 st->cr();
249 }
250
251 if (_anon_classes != NULL) {
252 for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) {
253 branchtracker.print(st);
254 if (lci == _anon_classes) { // first iteration
255 st->print("%*s ", indentation, "Anonymous Classes:");
256 } else {
257 st->print("%*s ", indentation, "");
258 }
259 st->print("%s", lci->_klass->external_name());
260 // For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD.
261 assert(lci->_cld != _cld, "must be");
262 if (verbose) {
263 st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
264 }
265 st->cr();
266 }
267 branchtracker.print(st);
268 st->print("%*s ", indentation, "");
269 st->print_cr("(%u anonymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es");
270
271 // Empty line
272 branchtracker.print(st);
273 st->cr();
274 }
275
276 } // end: print_classes
277
278 } // Pop branchtracker mark
279
280 // Print children, recursively
281 LoaderTreeNode* c = _child;
282 while (c != NULL) {
283 c->print_with_childs(st, branchtracker, print_classes, verbose);
284 c = c->_next;
285 }
286
287 }
288
289 // Helper: Attempt to fold this node into the target node. If success, returns true.
290 // Folding can be done if both nodes are leaf nodes and they refer to the same loader class
291 // and they have the same name or no name (note: leaf check is done by caller).
292 bool can_fold_into(LoaderTreeNode* target_node) const {
293 assert(is_leaf() && target_node->is_leaf(), "must be leaf");
294 return _cld->class_loader_klass() == target_node->_cld->class_loader_klass() &&
295 _cld->name() == target_node->_cld->name();
296 }
297
298 public:
299
300 LoaderTreeNode(const oop loader_oop)
301 : _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL),
302 _classes(NULL), _anon_classes(NULL), _num_classes(0), _num_anon_classes(0),
303 _num_folded(0)
304 {}
305
306 void set_cld(const ClassLoaderData* cld) {
307 _cld = cld;
308 }
309
310 void add_child(LoaderTreeNode* info) {
311 info->_next = _child;
312 _child = info;
313 }
314
315 void add_sibling(LoaderTreeNode* info) {
316 assert(info->_next == NULL, "must be");
317 info->_next = _next;
318 _next = info;
319 }
320
321 void add_classes(LoadedClassInfo* first_class, int num_classes, bool anonymous) {
322 LoadedClassInfo** p_list_to_add_to = anonymous ? &_anon_classes : &_classes;
323 // Search tail.
324 while ((*p_list_to_add_to) != NULL) {
337 }
338
339 const oop loader_oop() const {
340 return _loader_oop;
341 }
342
343 LoaderTreeNode* find(const oop loader_oop) {
344 LoaderTreeNode* result = NULL;
345 if (_loader_oop == loader_oop) {
346 result = this;
347 } else {
348 LoaderTreeNode* c = _child;
349 while (c != NULL && result == NULL) {
350 result = c->find(loader_oop);
351 c = c->_next;
352 }
353 }
354 return result;
355 }
356
357 bool is_leaf() const { return _child == NULL; }
358
359 // Attempt to fold similar nodes among this node's children. We only fold leaf nodes
360 // (no child class loaders).
361 // For non-leaf nodes (class loaders with child class loaders), do this recursivly.
362 void fold_children() {
363 LoaderTreeNode* node = _child;
364 LoaderTreeNode* prev = NULL;
365 while (node != NULL) {
366 LoaderTreeNode* matching_node = NULL;
367 if (node->is_leaf()) {
368 // Look among the preceeding node siblings for a match.
369 for (LoaderTreeNode* node2 = _child; node2 != node && matching_node == NULL;
370 node2 = node2->_next) {
371 if (node2->is_leaf() && node->can_fold_into(node2)) {
372 matching_node = node2;
373 }
374 }
375 } else {
376 node->fold_children();
377 }
378 if (matching_node != NULL) {
379 // Increase fold count for the matching node and remove folded node from the child list.
380 matching_node->_num_folded ++;
381 assert(prev != NULL, "Sanity"); // can never happen since we do not fold the first node.
382 prev->_next = node->_next;
383 } else {
384 prev = node;
385 }
386 node = node->_next;
387 }
388 }
389
390 void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const {
391 BranchTracker bwt;
392 print_with_childs(st, bwt, print_classes, print_add_info);
393 }
394
395 };
396
397 class LoadedClassCollectClosure : public KlassClosure {
398 public:
399 LoadedClassInfo* _list;
400 const ClassLoaderData* _cld;
401 int _num_classes;
402 LoadedClassCollectClosure(const ClassLoaderData* cld)
403 : _list(NULL), _cld(cld), _num_classes(0) {}
404 void do_klass(Klass* k) {
405 LoadedClassInfo* lki = new LoadedClassInfo(k, _cld);
406 lki->_next = _list;
407 _list = lki;
408 _num_classes ++;
409 }
472 // We do not display unloading loaders, for now.
473 if (cld->is_unloading()) {
474 return;
475 }
476
477 const oop loader_oop = cld->class_loader();
478
479 LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop);
480 assert(info != NULL, "must be");
481
482 // Update CLD in node, but only if this is the primary CLD for this loader.
483 if (cld->is_anonymous() == false) {
484 assert(info->cld() == NULL, "there should be only one primary CLD per loader");
485 info->set_cld(cld);
486 }
487
488 // Add classes.
489 fill_in_classes(info, cld);
490 }
491
492 void fold() {
493 _root->fold_children();
494 }
495
496 };
497
498
499 class ClassLoaderHierarchyVMOperation : public VM_Operation {
500 outputStream* const _out;
501 const bool _show_classes;
502 const bool _verbose;
503 const bool _fold;
504 public:
505 ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose, bool fold) :
506 _out(out), _show_classes(show_classes), _verbose(verbose), _fold(fold)
507 {}
508
509 VMOp_Type type() const {
510 return VMOp_ClassLoaderHierarchyOperation;
511 }
512
513 void doit() {
514 assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint");
515 ResourceMark rm;
516 LoaderInfoScanClosure cl (_show_classes, _verbose);
517 ClassLoaderDataGraph::cld_do(&cl);
518 // In non-verbose and non-show-classes mode, attempt to fold the tree.
519 if (_fold) {
520 if (!_verbose && !_show_classes) {
521 cl.fold();
522 }
523 }
524 cl.print_results(_out);
525 }
526 };
527
528 // This command needs to be executed at a safepoint.
529 void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {
530 ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value(), _fold.value());
531 VMThread::execute(&op);
532 }
|