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. 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>" 66 // " | |---<z1> 67 // " | |---<z2> 68 // ^^^^^^^ ^^^ 69 // A B 70 71 // Some terms for the graphics: 72 // - branch: vertical connection between a node's ancestor to a later sibling. 73 // - branchwork: (A) the string to print as a prefix at the start of each line, contains all branches. 74 // - twig (B): Length of the dashed line connecting a node to its branch. 75 // - branch spacing: how many spaces between branches are printed. 76 77 public: 78 79 enum { max_depth = 64, twig_len = 2, branch_spacing = 5 }; 80 81 private: 82 83 char _branches[max_depth]; 84 int _pos; 85 86 public: 87 BranchTracker() 88 : _pos(0) {} 89 90 void push(bool has_branch) { 91 if (_pos < max_depth) { 92 _branches[_pos] = has_branch ? '|' : ' '; 93 } 94 _pos ++; // beyond max depth, omit branch drawing but do count on. 95 } 96 97 void pop() { 98 assert(_pos > 0, "must be"); 99 _pos --; 100 } 101 102 void print(outputStream* st) { 103 for (int i = 0; i < _pos; i ++) { 104 st->print("%c%.*s", _branches[i], branch_spacing, " "); 105 } 106 } 107 108 class Mark { 109 BranchTracker& _tr; 110 public: 111 Mark(BranchTracker& tr, bool has_branch_here) 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, ""); 230 } 231 st->print("%s", lci->_klass->external_name()); 232 233 // Special treatment for generated core reflection accessor classes: print invocation target. 234 if (ReflectionAccessorImplKlassHelper::is_generated_accessor(lci->_klass)) { 235 st->print(" (invokes: "); 236 ReflectionAccessorImplKlassHelper::print_invocation_target(st, lci->_klass); 237 st->print(")"); 238 } 239 240 st->cr(); 241 } 242 branchtracker.print(st); 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) { 325 p_list_to_add_to = &(*p_list_to_add_to)->_next; 326 } 327 *p_list_to_add_to = first_class; 328 if (anonymous) { 329 _num_anon_classes += num_classes; 330 } else { 331 _num_classes += num_classes; 332 } 333 } 334 335 const ClassLoaderData* cld() const { 336 return _cld; 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 } 410 }; 411 412 class LoaderInfoScanClosure : public CLDClosure { 413 414 const bool _print_classes; 415 const bool _verbose; 416 LoaderTreeNode* _root; 417 418 static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) { 419 assert(info != NULL && cld != NULL, "must be"); 420 LoadedClassCollectClosure lccc(cld); 421 const_cast<ClassLoaderData*>(cld)->classes_do(&lccc); 422 if (lccc._num_classes > 0) { 423 info->add_classes(lccc._list, lccc._num_classes, cld->is_anonymous()); 424 } 425 } 426 427 LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) { 428 429 assert(_root != NULL, "root node must exist"); 430 431 if (loader_oop == NULL) { 432 return _root; 433 } 434 435 // Check if a node for this oop already exists. 436 LoaderTreeNode* info = _root->find(loader_oop); 437 438 if (info == NULL) { 439 // It does not. Create a node. 440 info = new LoaderTreeNode(loader_oop); 441 442 // Add it to tree. 443 LoaderTreeNode* parent_info = NULL; 444 445 // Recursively add parent nodes if needed. 446 const oop parent_oop = java_lang_ClassLoader::parent(loader_oop); 447 if (parent_oop == NULL) { 448 parent_info = _root; 449 } else { 450 parent_info = find_node_or_add_empty_node(parent_oop); 451 } 452 assert(parent_info != NULL, "must be"); 453 454 parent_info->add_child(info); 455 } 456 return info; 457 } 458 459 460 public: 461 LoaderInfoScanClosure(bool print_classes, bool verbose) 462 : _print_classes(print_classes), _verbose(verbose), _root(NULL) { 463 _root = new LoaderTreeNode(NULL); 464 } 465 466 void print_results(outputStream* st) const { 467 _root->print_with_childs(st, _print_classes, _verbose); 468 } 469 470 void do_cld (ClassLoaderData* cld) { 471 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 }