rev 50725 : [mq]: 8205531-vm.classloader-tree-folding

   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 }
--- EOF ---