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 "utilities/globalDefinitions.hpp" 34 #include "utilities/ostream.hpp" 35 36 37 ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap) 38 : DCmdWithParser(output, heap) 39 , _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false") 40 , _verbose("verbose", "Print detailed information.", "BOOLEAN", false, "false") { 41 _dcmdparser.add_dcmd_option(&_show_classes); 42 _dcmdparser.add_dcmd_option(&_verbose); 43 } 44 45 46 int ClassLoaderHierarchyDCmd::num_arguments() { 47 ResourceMark rm; 48 ClassLoaderHierarchyDCmd* dcmd = new ClassLoaderHierarchyDCmd(NULL, false); 49 if (dcmd != NULL) { 50 DCmdMark mark(dcmd); 51 return dcmd->_dcmdparser.num_arguments(); 52 } else { 53 return 0; 54 } 55 } 56 57 // Helper class for drawing the branches to the left of a node. 58 class BranchTracker : public StackObj { 59 // "<x>" 60 // " |---<y>" 61 // " | | 62 // " | <z>" 63 // " | |---<z1> 64 // " | |---<z2> 65 // ^^^^^^^ ^^^ 66 // A B 67 68 // Some terms for the graphics: 69 // - branch: vertical connection between a node's ancestor to a later sibling. 70 // - branchwork: (A) the string to print as a prefix at the start of each line, contains all branches. 71 // - twig (B): Length of the dashed line connecting a node to its branch. 72 // - branch spacing: how many spaces between branches are printed. 73 74 public: 75 76 enum { max_depth = 64, twig_len = 2, branch_spacing = 5 }; 77 78 private: 79 80 char _branches[max_depth]; 81 int _pos; 82 83 public: 84 BranchTracker() 85 : _pos(0) {} 86 87 void push(bool has_branch) { 88 if (_pos < max_depth) { 89 _branches[_pos] = has_branch ? '|' : ' '; 90 } 91 _pos ++; // beyond max depth, omit branch drawing but do count on. 92 } 93 94 void pop() { 95 assert(_pos > 0, "must be"); 96 _pos --; 97 } 98 99 void print(outputStream* st) { 100 for (int i = 0; i < _pos; i ++) { 101 st->print("%c%.*s", _branches[i], branch_spacing, " "); 102 } 103 } 104 105 class Mark { 106 BranchTracker& _tr; 107 public: 108 Mark(BranchTracker& tr, bool has_branch_here) 109 : _tr(tr) { _tr.push(has_branch_here); } 110 ~Mark() { _tr.pop(); } 111 }; 112 113 }; // end: BranchTracker 114 115 struct LoadedClassInfo : public ResourceObj { 116 public: 117 LoadedClassInfo* _next; 118 Klass* const _klass; 119 const ClassLoaderData* const _cld; 120 121 LoadedClassInfo(Klass* klass, const ClassLoaderData* cld) 122 : _klass(klass), _cld(cld) {} 123 124 }; 125 126 class LoaderTreeNode : public ResourceObj { 127 128 // We walk the CLDG and, for each CLD which is non-anonymous, add 129 // a tree node. To add a node we need its parent node; if it itself 130 // does not exist yet, we add a preliminary node for it. This preliminary 131 // node just contains its loader oop; later, when encountering its CLD in 132 // our CLDG walk, we complete the missing information in this node. 133 134 const oop _loader_oop; 135 const ClassLoaderData* _cld; 136 137 LoaderTreeNode* _child; 138 LoaderTreeNode* _next; 139 140 LoadedClassInfo* _classes; 141 int _num_classes; 142 143 LoadedClassInfo* _anon_classes; 144 int _num_anon_classes; 145 146 void print_with_childs(outputStream* st, BranchTracker& branchtracker, 147 bool print_classes, bool verbose) const { 148 149 ResourceMark rm; 150 151 if (_cld == NULL) { 152 // Not sure how this could happen: we added a preliminary node for a parent but then never encountered 153 // its CLD? 154 return; 155 } 156 157 // Retrieve information. 158 const Klass* const loader_klass = _cld->class_loader_klass(); 159 const Symbol* const loader_name = _cld->class_loader_name(); 160 161 branchtracker.print(st); 162 163 // e.g. "+--- jdk.internal.reflect.DelegatingClassLoader" 164 st->print("+%.*s", BranchTracker::twig_len, "----------"); 165 if (_cld->is_the_null_class_loader_data()) { 166 st->print(" <bootstrap>"); 167 } else { 168 if (loader_name != NULL) { 169 st->print(" \"%s\",", loader_name->as_C_string()); 170 } 171 st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??"); 172 st->print(" {" PTR_FORMAT "}", p2i(_loader_oop)); 173 } 174 st->cr(); 175 176 // Output following this node (node details and child nodes) - up to the next sibling node 177 // needs to be prefixed with "|" if there is a follow up sibling. 178 const bool have_sibling = _next != NULL; 179 BranchTracker::Mark trm(branchtracker, have_sibling); 180 181 { 182 // optional node details following this node needs to be prefixed with "|" 183 // if there are follow up child nodes. 184 const bool have_child = _child != NULL; 185 BranchTracker::Mark trm(branchtracker, have_child); 186 187 // Empty line 188 branchtracker.print(st); 189 st->cr(); 190 191 const int indentation = 18; 192 193 if (verbose) { 194 branchtracker.print(st); 195 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld)); 196 branchtracker.print(st); 197 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass)); 198 199 // Empty line 200 branchtracker.print(st); 201 st->cr(); 202 } 203 204 if (print_classes) { 205 206 if (_classes != NULL) { 207 for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) { 208 branchtracker.print(st); 209 if (lci == _classes) { // first iteration 210 st->print("%*s ", indentation, "Classes:"); 211 } else { 212 st->print("%*s ", indentation, ""); 213 } 214 st->print("%s", lci->_klass->external_name()); 215 st->cr(); 216 // Non-anonymous classes should live in the primary CLD of its loader 217 assert(lci->_cld == _cld, "must be"); 218 } 219 branchtracker.print(st); 220 st->print("%*s ", indentation, ""); 221 st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es"); 222 223 // Empty line 224 branchtracker.print(st); 225 st->cr(); 226 } 227 228 if (_anon_classes != NULL) { 229 for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) { 230 branchtracker.print(st); 231 if (lci == _anon_classes) { // first iteration 232 st->print("%*s ", indentation, "Anonymous Classes:"); 233 } else { 234 st->print("%*s ", indentation, ""); 235 } 236 st->print("%s", lci->_klass->external_name()); 237 // For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD. 238 assert(lci->_cld != _cld, "must be"); 239 if (verbose) { 240 st->print(" (CLD: " PTR_FORMAT ")", p2i(lci->_cld)); 241 } 242 st->cr(); 243 } 244 branchtracker.print(st); 245 st->print("%*s ", indentation, ""); 246 st->print_cr("(%u anonymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es"); 247 248 // Empty line 249 branchtracker.print(st); 250 st->cr(); 251 } 252 253 } // end: print_classes 254 255 } // Pop branchtracker mark 256 257 // Print children, recursively 258 LoaderTreeNode* c = _child; 259 while (c != NULL) { 260 c->print_with_childs(st, branchtracker, print_classes, verbose); 261 c = c->_next; 262 } 263 264 } 265 266 public: 267 268 LoaderTreeNode(const oop loader_oop) 269 : _loader_oop(loader_oop), _cld(NULL) 270 , _child(NULL), _next(NULL) 271 , _classes(NULL), _anon_classes(NULL) 272 , _num_classes(0), _num_anon_classes(0) {} 273 274 void set_cld(const ClassLoaderData* cld) { 275 _cld = cld; 276 } 277 278 void add_child(LoaderTreeNode* info) { 279 info->_next = _child; 280 _child = info; 281 } 282 283 void add_sibling(LoaderTreeNode* info) { 284 assert(info->_next == NULL, "must be"); 285 info->_next = _next; 286 _next = info; 287 } 288 289 void add_classes(LoadedClassInfo* first_class, int num_classes, bool anonymous) { 290 LoadedClassInfo** p_list_to_add_to = anonymous ? &_anon_classes : &_classes; 291 // Search tail. 292 while ((*p_list_to_add_to) != NULL) { 293 p_list_to_add_to = &(*p_list_to_add_to)->_next; 294 } 295 *p_list_to_add_to = first_class; 296 if (anonymous) { 297 _num_anon_classes += num_classes; 298 } else { 299 _num_classes += num_classes; 300 } 301 } 302 303 const ClassLoaderData* cld() const { 304 return _cld; 305 } 306 307 const oop loader_oop() const { 308 return _loader_oop; 309 } 310 311 LoaderTreeNode* find(const oop loader_oop) { 312 LoaderTreeNode* result = NULL; 313 if (_loader_oop == loader_oop) { 314 result = this; 315 } else { 316 LoaderTreeNode* c = _child; 317 while (c != NULL && result == NULL) { 318 result = c->find(loader_oop); 319 c = c->_next; 320 } 321 } 322 return result; 323 } 324 325 void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const { 326 BranchTracker bwt; 327 print_with_childs(st, bwt, print_classes, print_add_info); 328 } 329 330 }; 331 332 class LoadedClassCollectClosure : public KlassClosure { 333 public: 334 LoadedClassInfo* _list; 335 const ClassLoaderData* _cld; 336 int _num_classes; 337 LoadedClassCollectClosure(const ClassLoaderData* cld) 338 : _list(NULL), _cld(cld), _num_classes(0) {} 339 void do_klass(Klass* k) { 340 LoadedClassInfo* lki = new LoadedClassInfo(k, _cld); 341 lki->_next = _list; 342 _list = lki; 343 _num_classes ++; 344 } 345 }; 346 347 class LoaderInfoScanClosure : public CLDClosure { 348 349 const bool _print_classes; 350 const bool _verbose; 351 LoaderTreeNode* _root; 352 353 static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) { 354 assert(info != NULL && cld != NULL, "must be"); 355 LoadedClassCollectClosure lccc(cld); 356 const_cast<ClassLoaderData*>(cld)->classes_do(&lccc); 357 if (lccc._num_classes > 0) { 358 info->add_classes(lccc._list, lccc._num_classes, cld->is_anonymous()); 359 } 360 } 361 362 LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) { 363 364 assert(_root != NULL, "root node must exist"); 365 366 if (loader_oop == NULL) { 367 return _root; 368 } 369 370 // Check if a node for this oop already exists. 371 LoaderTreeNode* info = _root->find(loader_oop); 372 373 if (info == NULL) { 374 // It does not. Create a node. 375 info = new LoaderTreeNode(loader_oop); 376 377 // Add it to tree. 378 LoaderTreeNode* parent_info = NULL; 379 380 // Recursively add parent nodes if needed. 381 const oop parent_oop = java_lang_ClassLoader::parent(loader_oop); 382 if (parent_oop == NULL) { 383 parent_info = _root; 384 } else { 385 parent_info = find_node_or_add_empty_node(parent_oop); 386 } 387 assert(parent_info != NULL, "must be"); 388 389 parent_info->add_child(info); 390 } 391 return info; 392 } 393 394 395 public: 396 LoaderInfoScanClosure(bool print_classes, bool verbose) 397 : _print_classes(print_classes), _verbose(verbose), _root(NULL) { 398 _root = new LoaderTreeNode(NULL); 399 } 400 401 void print_results(outputStream* st) const { 402 _root->print_with_childs(st, _print_classes, _verbose); 403 } 404 405 void do_cld (ClassLoaderData* cld) { 406 407 // We do not display unloading loaders, for now. 408 if (cld->is_unloading()) { 409 return; 410 } 411 412 const oop loader_oop = cld->class_loader(); 413 414 LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop); 415 assert(info != NULL, "must be"); 416 417 // Update CLD in node, but only if this is the primary CLD for this loader. 418 if (cld->is_anonymous() == false) { 419 assert(info->cld() == NULL, "there should be only one primary CLD per loader"); 420 info->set_cld(cld); 421 } 422 423 // Add classes. 424 fill_in_classes(info, cld); 425 } 426 427 }; 428 429 430 class ClassLoaderHierarchyVMOperation : public VM_Operation { 431 outputStream* const _out; 432 const bool _show_classes; 433 const bool _verbose; 434 public: 435 ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose) : 436 _out(out), _show_classes(show_classes), _verbose(verbose) 437 {} 438 439 VMOp_Type type() const { 440 return VMOp_ClassLoaderHierarchyOperation; 441 } 442 443 void doit() { 444 assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint"); 445 ResourceMark rm; 446 LoaderInfoScanClosure cl (_show_classes, _verbose); 447 ClassLoaderDataGraph::cld_do(&cl); 448 cl.print_results(_out); 449 } 450 }; 451 452 // This command needs to be executed at a safepoint. 453 void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) { 454 ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value()); 455 VMThread::execute(&op); 456 }