1 /*
2 * Copyright (c) 2018, 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.
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 non-unsafe_anonymous, 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* _anon_classes;
150 int _num_anon_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 if (loader_name != NULL) {
181 st->print(" \"%s\",", loader_name->as_C_string());
182 }
183 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
184 if (_num_folded > 0) {
185 st->print(" (+ %d more)", _num_folded);
186 }
187 }
188 st->cr();
189
190 // Output following this node (node details and child nodes) - up to the next sibling node
191 // needs to be prefixed with "|" if there is a follow up sibling.
192 const bool have_sibling = _next != NULL;
193 BranchTracker::Mark trm(branchtracker, have_sibling);
194
195 {
196 // optional node details following this node needs to be prefixed with "|"
197 // if there are follow up child nodes.
198 const bool have_child = _child != NULL;
199 BranchTracker::Mark trm(branchtracker, have_child);
203 st->cr();
204
205 const int indentation = 18;
206
207 if (verbose) {
208 branchtracker.print(st);
209 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop));
210 branchtracker.print(st);
211 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
212 branchtracker.print(st);
213 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));
214
215 // Empty line
216 branchtracker.print(st);
217 st->cr();
218 }
219
220 if (print_classes) {
221 if (_classes != NULL) {
222 for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
223 // Non-unsafe anonymous classes should live in the primary CLD of its loader
224 assert(lci->_cld == _cld, "must be");
225
226 branchtracker.print(st);
227 if (lci == _classes) { // first iteration
228 st->print("%*s ", indentation, "Classes:");
229 } else {
230 st->print("%*s ", indentation, "");
231 }
232 st->print("%s", lci->_klass->external_name());
233
234 // Special treatment for generated core reflection accessor classes: print invocation target.
235 if (ReflectionAccessorImplKlassHelper::is_generated_accessor(lci->_klass)) {
236 st->print(" (invokes: ");
237 ReflectionAccessorImplKlassHelper::print_invocation_target(st, lci->_klass);
238 st->print(")");
239 }
240
241 st->cr();
242 }
243 branchtracker.print(st);
244 st->print("%*s ", indentation, "");
245 st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es");
246
247 // Empty line
248 branchtracker.print(st);
249 st->cr();
250 }
251
252 if (_anon_classes != NULL) {
253 for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) {
254 branchtracker.print(st);
255 if (lci == _anon_classes) { // first iteration
256 st->print("%*s ", indentation, "Unsafe Anonymous Classes:");
257 } else {
258 st->print("%*s ", indentation, "");
259 }
260 st->print("%s", lci->_klass->external_name());
261 // For unsafe anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD.
262 assert(lci->_cld != _cld, "must be");
263 if (verbose) {
264 st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
265 }
266 st->cr();
267 }
268 branchtracker.print(st);
269 st->print("%*s ", indentation, "");
270 st->print_cr("(%u unsafe anonymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es");
271
272 // Empty line
273 branchtracker.print(st);
274 st->cr();
275 }
276
277 } // end: print_classes
278
279 } // Pop branchtracker mark
280
281 // Print children, recursively
282 LoaderTreeNode* c = _child;
283 while (c != NULL) {
284 c->print_with_childs(st, branchtracker, print_classes, verbose);
285 c = c->_next;
286 }
287
288 }
289
290 // Helper: Attempt to fold this node into the target node. If success, returns true.
291 // Folding can be done if both nodes are leaf nodes and they refer to the same loader class
292 // and they have the same name or no name (note: leaf check is done by caller).
293 bool can_fold_into(LoaderTreeNode* target_node) const {
294 assert(is_leaf() && target_node->is_leaf(), "must be leaf");
295 return _cld->class_loader_klass() == target_node->_cld->class_loader_klass() &&
296 _cld->name() == target_node->_cld->name();
297 }
298
299 public:
300
301 LoaderTreeNode(const oop loader_oop)
302 : _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL),
303 _classes(NULL), _num_classes(0), _anon_classes(NULL), _num_anon_classes(0),
304 _num_folded(0)
305 {}
306
307 void set_cld(const ClassLoaderData* cld) {
308 _cld = cld;
309 }
310
311 void add_child(LoaderTreeNode* info) {
312 info->_next = _child;
313 _child = info;
314 }
315
316 void add_sibling(LoaderTreeNode* info) {
317 assert(info->_next == NULL, "must be");
318 info->_next = _next;
319 _next = info;
320 }
321
322 void add_classes(LoadedClassInfo* first_class, int num_classes, bool is_unsafe_anonymous) {
323 LoadedClassInfo** p_list_to_add_to = is_unsafe_anonymous ? &_anon_classes : &_classes;
324 // Search tail.
325 while ((*p_list_to_add_to) != NULL) {
326 p_list_to_add_to = &(*p_list_to_add_to)->_next;
327 }
328 *p_list_to_add_to = first_class;
329 if (is_unsafe_anonymous) {
330 _num_anon_classes += num_classes;
331 } else {
332 _num_classes += num_classes;
333 }
334 }
335
336 const ClassLoaderData* cld() const {
337 return _cld;
338 }
339
340 const oop loader_oop() const {
341 return _loader_oop;
342 }
343
344 LoaderTreeNode* find(const oop loader_oop) {
345 LoaderTreeNode* result = NULL;
346 if (_loader_oop == loader_oop) {
347 result = this;
348 } else {
349 LoaderTreeNode* c = _child;
350 while (c != NULL && result == NULL) {
404 : _list(NULL), _cld(cld), _num_classes(0) {}
405 void do_klass(Klass* k) {
406 LoadedClassInfo* lki = new LoadedClassInfo(k, _cld);
407 lki->_next = _list;
408 _list = lki;
409 _num_classes ++;
410 }
411 };
412
413 class LoaderInfoScanClosure : public CLDClosure {
414
415 const bool _print_classes;
416 const bool _verbose;
417 LoaderTreeNode* _root;
418
419 static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) {
420 assert(info != NULL && cld != NULL, "must be");
421 LoadedClassCollectClosure lccc(cld);
422 const_cast<ClassLoaderData*>(cld)->classes_do(&lccc);
423 if (lccc._num_classes > 0) {
424 info->add_classes(lccc._list, lccc._num_classes, cld->is_unsafe_anonymous());
425 }
426 }
427
428 LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) {
429
430 assert(_root != NULL, "root node must exist");
431
432 if (loader_oop == NULL) {
433 return _root;
434 }
435
436 // Check if a node for this oop already exists.
437 LoaderTreeNode* info = _root->find(loader_oop);
438
439 if (info == NULL) {
440 // It does not. Create a node.
441 info = new LoaderTreeNode(loader_oop);
442
443 // Add it to tree.
444 LoaderTreeNode* parent_info = NULL;
464 _root = new LoaderTreeNode(NULL);
465 }
466
467 void print_results(outputStream* st) const {
468 _root->print_with_childs(st, _print_classes, _verbose);
469 }
470
471 void do_cld (ClassLoaderData* cld) {
472
473 // We do not display unloading loaders, for now.
474 if (!cld->is_alive()) {
475 return;
476 }
477
478 const oop loader_oop = cld->class_loader();
479
480 LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop);
481 assert(info != NULL, "must be");
482
483 // Update CLD in node, but only if this is the primary CLD for this loader.
484 if (cld->is_unsafe_anonymous() == false) {
485 assert(info->cld() == NULL, "there should be only one primary CLD per loader");
486 info->set_cld(cld);
487 }
488
489 // Add classes.
490 fill_in_classes(info, cld);
491 }
492
493 void fold() {
494 _root->fold_children();
495 }
496
497 };
498
499
500 class ClassLoaderHierarchyVMOperation : public VM_Operation {
501 outputStream* const _out;
502 const bool _show_classes;
503 const bool _verbose;
504 const bool _fold;
|
1 /*
2 * Copyright (c) 2018, 2020, 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.
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* _anon_classes;
150 int _num_anon_classes;
151
152 LoadedClassInfo* _hidden_classes;
153 int _num_hidden_classes;
154
155 // In default view, similar tree nodes (same loader class, same name or no name)
156 // are folded into each other to make the output more readable.
157 // _num_folded contains the number of nodes which have been folded into this
158 // one.
159 int _num_folded;
160
161 void print_with_childs(outputStream* st, BranchTracker& branchtracker,
162 bool print_classes, bool verbose) const {
163
164 ResourceMark rm;
165
166 if (_cld == NULL) {
167 // Not sure how this could happen: we added a preliminary node for a parent but then never encountered
168 // its CLD?
169 return;
170 }
171
172 // Retrieve information.
173 const Klass* const loader_klass = _cld->class_loader_klass();
174 const Symbol* const loader_name = _cld->name();
175
176 branchtracker.print(st);
177
178 // e.g. "+--- jdk.internal.reflect.DelegatingClassLoader"
179 st->print("+%.*s", BranchTracker::twig_len, "----------");
180 if (_cld->is_the_null_class_loader_data()) {
181 st->print(" <bootstrap>");
182 } else {
183 assert(!_cld->has_class_mirror_holder(), "_cld must be the primary cld");
184 if (loader_name != NULL) {
185 st->print(" \"%s\",", loader_name->as_C_string());
186 }
187 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
188 if (_num_folded > 0) {
189 st->print(" (+ %d more)", _num_folded);
190 }
191 }
192 st->cr();
193
194 // Output following this node (node details and child nodes) - up to the next sibling node
195 // needs to be prefixed with "|" if there is a follow up sibling.
196 const bool have_sibling = _next != NULL;
197 BranchTracker::Mark trm(branchtracker, have_sibling);
198
199 {
200 // optional node details following this node needs to be prefixed with "|"
201 // if there are follow up child nodes.
202 const bool have_child = _child != NULL;
203 BranchTracker::Mark trm(branchtracker, have_child);
207 st->cr();
208
209 const int indentation = 18;
210
211 if (verbose) {
212 branchtracker.print(st);
213 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop));
214 branchtracker.print(st);
215 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
216 branchtracker.print(st);
217 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));
218
219 // Empty line
220 branchtracker.print(st);
221 st->cr();
222 }
223
224 if (print_classes) {
225 if (_classes != NULL) {
226 for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
227 // non-strong hidden and unsafe anonymous classes should not live in the primary CLD of their loaders.
228 assert(lci->_cld == _cld, "must be");
229
230 branchtracker.print(st);
231 if (lci == _classes) { // first iteration
232 st->print("%*s ", indentation, "Classes:");
233 } else {
234 st->print("%*s ", indentation, "");
235 }
236 st->print("%s", lci->_klass->external_name());
237
238 // Special treatment for generated core reflection accessor classes: print invocation target.
239 if (ReflectionAccessorImplKlassHelper::is_generated_accessor(lci->_klass)) {
240 st->print(" (invokes: ");
241 ReflectionAccessorImplKlassHelper::print_invocation_target(st, lci->_klass);
242 st->print(")");
243 }
244
245 st->cr();
246 }
247 branchtracker.print(st);
248 st->print("%*s ", indentation, "");
249 st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es");
250
251 // Empty line
252 branchtracker.print(st);
253 st->cr();
254 }
255
256 if (_anon_classes != NULL) {
257 for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) {
258 branchtracker.print(st);
259 if (lci == _anon_classes) { // first iteration
260 st->print("%*s ", indentation, "Unsafe Anonymous Classes:");
261 } else {
262 st->print("%*s ", indentation, "");
263 }
264 st->print("%s", lci->_klass->external_name());
265 // For unsafe anonymous classes, also print CLD if verbose. Should
266 // be a different one than the primary CLD.
267 assert(lci->_cld != _cld, "must be");
268 if (verbose) {
269 st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
270 }
271 st->cr();
272 }
273 branchtracker.print(st);
274 st->print("%*s ", indentation, "");
275 st->print_cr("(%u unsafe anonymous class%s)", _num_anon_classes,
276 (_num_anon_classes == 1) ? "" : "es");
277
278 // Empty line
279 branchtracker.print(st);
280 st->cr();
281 }
282
283 if (_hidden_classes != NULL) {
284 for (LoadedClassInfo* lci = _hidden_classes; lci; lci = lci->_next) {
285 branchtracker.print(st);
286 if (lci == _hidden_classes) { // first iteration
287 st->print("%*s ", indentation, "Hidden Classes:");
288 } else {
289 st->print("%*s ", indentation, "");
290 }
291 st->print("%s", lci->_klass->external_name());
292 // For non-strong hidden classes, also print CLD if verbose. Should be a
293 // different one than the primary CLD.
294 assert(lci->_cld != _cld, "must be");
295 if (verbose) {
296 st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
297 }
298 st->cr();
299 }
300 branchtracker.print(st);
301 st->print("%*s ", indentation, "");
302 st->print_cr("(%u hidden class%s)", _num_hidden_classes,
303 (_num_hidden_classes == 1) ? "" : "es");
304
305 // Empty line
306 branchtracker.print(st);
307 st->cr();
308 }
309
310 } // end: print_classes
311
312 } // Pop branchtracker mark
313
314 // Print children, recursively
315 LoaderTreeNode* c = _child;
316 while (c != NULL) {
317 c->print_with_childs(st, branchtracker, print_classes, verbose);
318 c = c->_next;
319 }
320
321 }
322
323 // Helper: Attempt to fold this node into the target node. If success, returns true.
324 // Folding can be done if both nodes are leaf nodes and they refer to the same loader class
325 // and they have the same name or no name (note: leaf check is done by caller).
326 bool can_fold_into(LoaderTreeNode* target_node) const {
327 assert(is_leaf() && target_node->is_leaf(), "must be leaf");
328 return _cld->class_loader_klass() == target_node->_cld->class_loader_klass() &&
329 _cld->name() == target_node->_cld->name();
330 }
331
332 public:
333
334 LoaderTreeNode(const oop loader_oop)
335 : _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL),
336 _classes(NULL), _num_classes(0), _anon_classes(NULL), _num_anon_classes(0),
337 _hidden_classes(NULL), _num_hidden_classes(0),
338 _num_folded(0)
339 {}
340
341 void set_cld(const ClassLoaderData* cld) {
342 _cld = cld;
343 }
344
345 void add_child(LoaderTreeNode* info) {
346 info->_next = _child;
347 _child = info;
348 }
349
350 void add_sibling(LoaderTreeNode* info) {
351 assert(info->_next == NULL, "must be");
352 info->_next = _next;
353 _next = info;
354 }
355
356 void add_classes(LoadedClassInfo* first_class, int num_classes, bool has_class_mirror_holder) {
357 LoadedClassInfo** p_list_to_add_to;
358 bool is_hidden = first_class->_klass->is_hidden();
359 if (has_class_mirror_holder) {
360 p_list_to_add_to = is_hidden ? &_hidden_classes : &_anon_classes;
361 } else {
362 p_list_to_add_to = &_classes;
363 }
364 // Search tail.
365 while ((*p_list_to_add_to) != NULL) {
366 p_list_to_add_to = &(*p_list_to_add_to)->_next;
367 }
368 *p_list_to_add_to = first_class;
369 if (has_class_mirror_holder) {
370 if (is_hidden) {
371 _num_hidden_classes += num_classes;
372 } else {
373 _num_anon_classes += num_classes;
374 }
375 } else {
376 _num_classes += num_classes;
377 }
378 }
379
380 const ClassLoaderData* cld() const {
381 return _cld;
382 }
383
384 const oop loader_oop() const {
385 return _loader_oop;
386 }
387
388 LoaderTreeNode* find(const oop loader_oop) {
389 LoaderTreeNode* result = NULL;
390 if (_loader_oop == loader_oop) {
391 result = this;
392 } else {
393 LoaderTreeNode* c = _child;
394 while (c != NULL && result == NULL) {
448 : _list(NULL), _cld(cld), _num_classes(0) {}
449 void do_klass(Klass* k) {
450 LoadedClassInfo* lki = new LoadedClassInfo(k, _cld);
451 lki->_next = _list;
452 _list = lki;
453 _num_classes ++;
454 }
455 };
456
457 class LoaderInfoScanClosure : public CLDClosure {
458
459 const bool _print_classes;
460 const bool _verbose;
461 LoaderTreeNode* _root;
462
463 static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) {
464 assert(info != NULL && cld != NULL, "must be");
465 LoadedClassCollectClosure lccc(cld);
466 const_cast<ClassLoaderData*>(cld)->classes_do(&lccc);
467 if (lccc._num_classes > 0) {
468 info->add_classes(lccc._list, lccc._num_classes, cld->has_class_mirror_holder());
469 }
470 }
471
472 LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) {
473
474 assert(_root != NULL, "root node must exist");
475
476 if (loader_oop == NULL) {
477 return _root;
478 }
479
480 // Check if a node for this oop already exists.
481 LoaderTreeNode* info = _root->find(loader_oop);
482
483 if (info == NULL) {
484 // It does not. Create a node.
485 info = new LoaderTreeNode(loader_oop);
486
487 // Add it to tree.
488 LoaderTreeNode* parent_info = NULL;
508 _root = new LoaderTreeNode(NULL);
509 }
510
511 void print_results(outputStream* st) const {
512 _root->print_with_childs(st, _print_classes, _verbose);
513 }
514
515 void do_cld (ClassLoaderData* cld) {
516
517 // We do not display unloading loaders, for now.
518 if (!cld->is_alive()) {
519 return;
520 }
521
522 const oop loader_oop = cld->class_loader();
523
524 LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop);
525 assert(info != NULL, "must be");
526
527 // Update CLD in node, but only if this is the primary CLD for this loader.
528 if (cld->has_class_mirror_holder() == false) {
529 assert(info->cld() == NULL, "there should be only one primary CLD per loader");
530 info->set_cld(cld);
531 }
532
533 // Add classes.
534 fill_in_classes(info, cld);
535 }
536
537 void fold() {
538 _root->fold_children();
539 }
540
541 };
542
543
544 class ClassLoaderHierarchyVMOperation : public VM_Operation {
545 outputStream* const _out;
546 const bool _show_classes;
547 const bool _verbose;
548 const bool _fold;
|