34 #include "utilities/globalDefinitions.hpp" |
34 #include "utilities/globalDefinitions.hpp" |
35 #include "utilities/ostream.hpp" |
35 #include "utilities/ostream.hpp" |
36 |
36 |
37 |
37 |
38 ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap) |
38 ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap) |
39 : DCmdWithParser(output, heap) |
39 : DCmdWithParser(output, heap), |
40 , _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false") |
40 _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false"), |
41 , _verbose("verbose", "Print detailed information.", "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") { |
42 _dcmdparser.add_dcmd_option(&_show_classes); |
43 _dcmdparser.add_dcmd_option(&_show_classes); |
43 _dcmdparser.add_dcmd_option(&_verbose); |
44 _dcmdparser.add_dcmd_option(&_verbose); |
|
45 _dcmdparser.add_dcmd_option(&_fold); |
44 } |
46 } |
45 |
47 |
46 |
48 |
47 int ClassLoaderHierarchyDCmd::num_arguments() { |
49 int ClassLoaderHierarchyDCmd::num_arguments() { |
48 ResourceMark rm; |
50 ResourceMark rm; |
125 }; |
127 }; |
126 |
128 |
127 class LoaderTreeNode : public ResourceObj { |
129 class LoaderTreeNode : public ResourceObj { |
128 |
130 |
129 // We walk the CLDG and, for each CLD which is non-anonymous, add |
131 // 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 |
132 // a tree node. |
131 // does not exist yet, we add a preliminary node for it. This preliminary |
133 // To add a node we need its parent node; if the parent node does not yet |
132 // node just contains its loader oop; later, when encountering its CLD in |
134 // exist - because we have not yet encountered the CLD for the parent loader - |
133 // our CLDG walk, we complete the missing information in this node. |
135 // 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. |
134 |
138 |
135 const oop _loader_oop; |
139 const oop _loader_oop; |
136 const ClassLoaderData* _cld; |
140 const ClassLoaderData* _cld; |
137 |
141 |
138 LoaderTreeNode* _child; |
142 LoaderTreeNode* _child; |
141 LoadedClassInfo* _classes; |
145 LoadedClassInfo* _classes; |
142 int _num_classes; |
146 int _num_classes; |
143 |
147 |
144 LoadedClassInfo* _anon_classes; |
148 LoadedClassInfo* _anon_classes; |
145 int _num_anon_classes; |
149 int _num_anon_classes; |
|
150 |
|
151 // In default view, similar tree nodes (same loader class, same name 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; |
146 |
156 |
147 void print_with_childs(outputStream* st, BranchTracker& branchtracker, |
157 void print_with_childs(outputStream* st, BranchTracker& branchtracker, |
148 bool print_classes, bool verbose) const { |
158 bool print_classes, bool verbose) const { |
149 |
159 |
150 ResourceMark rm; |
160 ResourceMark rm; |
168 } else { |
178 } else { |
169 if (loader_name != NULL) { |
179 if (loader_name != NULL) { |
170 st->print(" \"%s\",", loader_name->as_C_string()); |
180 st->print(" \"%s\",", loader_name->as_C_string()); |
171 } |
181 } |
172 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??"); |
182 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??"); |
173 st->print(" {" PTR_FORMAT "}", p2i(_loader_oop)); |
183 if (_num_folded > 0) { |
|
184 st->print(" (+ %d more)", _num_folded); |
|
185 } |
174 } |
186 } |
175 st->cr(); |
187 st->cr(); |
176 |
188 |
177 // Output following this node (node details and child nodes) - up to the next sibling node |
189 // 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. |
190 // needs to be prefixed with "|" if there is a follow up sibling. |
190 st->cr(); |
202 st->cr(); |
191 |
203 |
192 const int indentation = 18; |
204 const int indentation = 18; |
193 |
205 |
194 if (verbose) { |
206 if (verbose) { |
|
207 branchtracker.print(st); |
|
208 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop)); |
195 branchtracker.print(st); |
209 branchtracker.print(st); |
196 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld)); |
210 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld)); |
197 branchtracker.print(st); |
211 branchtracker.print(st); |
198 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass)); |
212 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass)); |
199 |
213 |
244 } |
258 } |
245 st->print("%s", lci->_klass->external_name()); |
259 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. |
260 // For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD. |
247 assert(lci->_cld != _cld, "must be"); |
261 assert(lci->_cld != _cld, "must be"); |
248 if (verbose) { |
262 if (verbose) { |
249 st->print(" (CLD: " PTR_FORMAT ")", p2i(lci->_cld)); |
263 st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld)); |
250 } |
264 } |
251 st->cr(); |
265 st->cr(); |
252 } |
266 } |
253 branchtracker.print(st); |
267 branchtracker.print(st); |
254 st->print("%*s ", indentation, ""); |
268 st->print("%*s ", indentation, ""); |
270 c = c->_next; |
284 c = c->_next; |
271 } |
285 } |
272 |
286 |
273 } |
287 } |
274 |
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 |
275 public: |
298 public: |
276 |
299 |
277 LoaderTreeNode(const oop loader_oop) |
300 LoaderTreeNode(const oop loader_oop) |
278 : _loader_oop(loader_oop), _cld(NULL) |
301 : _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL), |
279 , _child(NULL), _next(NULL) |
302 _classes(NULL), _anon_classes(NULL), _num_classes(0), _num_anon_classes(0), |
280 , _classes(NULL), _anon_classes(NULL) |
303 _num_folded(0) |
281 , _num_classes(0), _num_anon_classes(0) {} |
304 {} |
282 |
305 |
283 void set_cld(const ClassLoaderData* cld) { |
306 void set_cld(const ClassLoaderData* cld) { |
284 _cld = cld; |
307 _cld = cld; |
285 } |
308 } |
286 |
309 |
327 result = c->find(loader_oop); |
350 result = c->find(loader_oop); |
328 c = c->_next; |
351 c = c->_next; |
329 } |
352 } |
330 } |
353 } |
331 return result; |
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 } |
332 } |
388 } |
333 |
389 |
334 void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const { |
390 void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const { |
335 BranchTracker bwt; |
391 BranchTracker bwt; |
336 print_with_childs(st, bwt, print_classes, print_add_info); |
392 print_with_childs(st, bwt, print_classes, print_add_info); |
431 |
487 |
432 // Add classes. |
488 // Add classes. |
433 fill_in_classes(info, cld); |
489 fill_in_classes(info, cld); |
434 } |
490 } |
435 |
491 |
|
492 void fold() { |
|
493 _root->fold_children(); |
|
494 } |
|
495 |
436 }; |
496 }; |
437 |
497 |
438 |
498 |
439 class ClassLoaderHierarchyVMOperation : public VM_Operation { |
499 class ClassLoaderHierarchyVMOperation : public VM_Operation { |
440 outputStream* const _out; |
500 outputStream* const _out; |
441 const bool _show_classes; |
501 const bool _show_classes; |
442 const bool _verbose; |
502 const bool _verbose; |
443 public: |
503 const bool _fold; |
444 ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose) : |
504 public: |
445 _out(out), _show_classes(show_classes), _verbose(verbose) |
505 ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose, bool fold) : |
|
506 _out(out), _show_classes(show_classes), _verbose(verbose), _fold(fold) |
446 {} |
507 {} |
447 |
508 |
448 VMOp_Type type() const { |
509 VMOp_Type type() const { |
449 return VMOp_ClassLoaderHierarchyOperation; |
510 return VMOp_ClassLoaderHierarchyOperation; |
450 } |
511 } |
452 void doit() { |
513 void doit() { |
453 assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint"); |
514 assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint"); |
454 ResourceMark rm; |
515 ResourceMark rm; |
455 LoaderInfoScanClosure cl (_show_classes, _verbose); |
516 LoaderInfoScanClosure cl (_show_classes, _verbose); |
456 ClassLoaderDataGraph::cld_do(&cl); |
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 } |
457 cl.print_results(_out); |
524 cl.print_results(_out); |
458 } |
525 } |
459 }; |
526 }; |
460 |
527 |
461 // This command needs to be executed at a safepoint. |
528 // This command needs to be executed at a safepoint. |
462 void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) { |
529 void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) { |
463 ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value()); |
530 ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value(), _fold.value()); |
464 VMThread::execute(&op); |
531 VMThread::execute(&op); |
465 } |
532 } |