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