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 "utilities/globalDefinitions.hpp" 33 #include "utilities/ostream.hpp" 34 35 36 ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap) 37 : DCmdWithParser(output, heap) 38 , _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false") 39 , _verbose("verbose", "Print detailed information.", "BOOLEAN", false, "false") 40 { 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 { 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 { maxdepth = 64, twiglen = 2, branch_spacing = 5 }; 77 78 private: 79 80 char _branches[maxdepth]; 81 int _pos; 82 83 public: 84 BranchTracker() 85 : _pos(0) 86 {} 87 88 void push(bool has_branch) { 89 if (_pos < maxdepth) { 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 CHeapObj<mtInternal> { 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 128 class LoaderTreeNode : public CHeapObj<mtInternal> { 129 130 const oop _loader_oop; 131 const ClassLoaderData* _cld; 132 133 LoaderTreeNode* _child; 134 LoaderTreeNode* _next; 135 136 LoadedClassInfo* _classes; 137 int _num_classes; 138 139 LoadedClassInfo* _anon_classes; 140 int _num_anon_classes; 141 142 void print_with_childs(outputStream* st, BranchTracker& branchtracker, 143 bool print_classes, bool verbose) const 144 { 145 ResourceMark rm; 146 147 // Retrieve information. 148 const Klass* loader_klass = NULL; 149 const char* loader_name = NULL; 150 const char* loader_class_name = NULL; 151 152 if (_loader_oop != NULL) { 153 loader_klass = _loader_oop->klass(); 154 if (loader_klass != NULL) { 155 loader_class_name = loader_klass->external_name(); 156 } 157 oop nameOop = java_lang_ClassLoader::name(_loader_oop); 158 if (nameOop != NULL) { 159 const char* s = java_lang_String::as_utf8_string(nameOop); 160 if (s != NULL) { 161 loader_name = s; 162 } 163 } 164 } else { 165 // Boot loader 166 loader_name = "<bootstrap>"; 167 } 168 169 branchtracker.print(st); 170 171 // e.g. "+--- ABC (instance of DEF)" 172 st->print("+%.*s ", BranchTracker::twiglen, "----------"); 173 st->print("%s", loader_name != NULL ? loader_name : "<unnamed>"); 174 if (loader_class_name != NULL) { 175 st->print(" (instance of %s)", loader_class_name); 176 } 177 st->cr(); 178 179 const bool have_sibling = _next != NULL; 180 BranchTracker::Mark trm(branchtracker, have_sibling); 181 182 // Area below class loader name, but not belonging to child nodes. Here 183 // we print additional infos about the loader (e..g. loaded classes). 184 185 { 186 const bool have_child = _child != NULL; 187 BranchTracker::Mark trm(branchtracker, have_child); 188 189 // Empty line 190 branchtracker.print(st); 191 st->cr(); 192 193 const int indentation = 22; 194 195 if (verbose) { 196 branchtracker.print(st); 197 st->print_cr("%*s " PTR_FORMAT, indentation, "CLD:", p2i(_cld)); 198 branchtracker.print(st); 199 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass)); 200 branchtracker.print(st); 201 st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop)); 202 203 // Empty line 204 branchtracker.print(st); 205 st->cr(); 206 } 207 208 if (print_classes) { 209 210 if (_classes != NULL) { 211 for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) { 212 branchtracker.print(st); 213 if (lci == _classes) { // first iteration 214 st->print("%*s ", indentation, "Classes:"); 215 } else { 216 st->print("%*s ", indentation, ""); 217 } 218 st->print("%s", lci->_klass->external_name()); 219 st->cr(); 220 // Non-anonymous classes should live in the primary CLD of its loader 221 assert(lci->_cld == _cld, "must be"); 222 } 223 branchtracker.print(st); 224 st->print("%*s ", indentation, ""); 225 st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es"); 226 227 // Empty line 228 branchtracker.print(st); 229 st->cr(); 230 } 231 232 if (_anon_classes != NULL) { 233 for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) { 234 branchtracker.print(st); 235 if (lci == _anon_classes) { // first iteration 236 st->print("%*s ", indentation, "Anonymous Classes:"); 237 } else { 238 st->print("%*s ", indentation, ""); 239 } 240 st->print("%s", lci->_klass->external_name()); 241 // For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD. 242 assert(lci->_cld != _cld, "must be"); 243 if (verbose) { 244 st->print(" (CLD: " PTR_FORMAT ")", p2i(lci->_cld)); 245 } 246 st->cr(); 247 } 248 branchtracker.print(st); 249 st->print("%*s ", indentation, ""); 250 st->print_cr("(%u anononymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es"); 251 252 // Empty line 253 branchtracker.print(st); 254 st->cr(); 255 } 256 257 } // end: print_classes 258 259 } // Pop branchtracker mark 260 261 // Print children, recursively 262 LoaderTreeNode* c = _child; 263 while (c != NULL) { 264 c->print_with_childs(st, branchtracker, print_classes, verbose); 265 c = c->_next; 266 } 267 268 } 269 270 void free_child_nodes() { 271 LoaderTreeNode* c = _child; 272 while (c) { 273 LoaderTreeNode* next = c->_next; 274 delete c; 275 c = next; 276 } 277 } 278 279 void free_class_info_list(LoadedClassInfo* first) { 280 LoadedClassInfo* p = first; 281 while (p) { 282 LoadedClassInfo* next = p->_next; 283 delete p; 284 p = next; 285 } 286 } 287 288 public: 289 290 LoaderTreeNode(const oop loader_oop) 291 : _loader_oop(loader_oop), _cld(NULL) 292 , _child(NULL), _next(NULL) 293 , _classes(NULL), _anon_classes(NULL) 294 , _num_classes(0), _num_anon_classes(0) 295 {} 296 297 ~LoaderTreeNode() { 298 free_child_nodes(); 299 free_class_info_list(_classes); 300 free_class_info_list(_anon_classes); 301 } 302 303 void set_cld(const ClassLoaderData* cld) { _cld = cld; } 304 305 void add_child(LoaderTreeNode* info) { 306 info->_next = _child; 307 _child = info; 308 } 309 310 void add_sibling(LoaderTreeNode* info) { 311 assert(info->_next == NULL, "must be"); 312 info->_next = _next; 313 _next = info; 314 } 315 316 void add_classes(LoadedClassInfo* first_class, int num_classes, bool anonymous) { 317 LoadedClassInfo** p_list_to_add_to = anonymous ? &_anon_classes : &_classes; 318 // Search tail. 319 while ((*p_list_to_add_to) != NULL) { 320 p_list_to_add_to = &(*p_list_to_add_to)->_next; 321 } 322 *p_list_to_add_to = first_class; 323 if (anonymous) { 324 _num_anon_classes += num_classes; 325 } else { 326 _num_classes += num_classes; 327 } 328 } 329 330 const ClassLoaderData* cld() const { return _cld; } 331 const oop loader_oop() const { return _loader_oop; } 332 333 LoaderTreeNode* find(const oop loader_oop) { 334 LoaderTreeNode* result = NULL; 335 if (_loader_oop == loader_oop) { 336 result = this; 337 } else { 338 LoaderTreeNode* c = _child; 339 while (c != NULL && result == NULL) { 340 result = c->find(loader_oop); 341 c = c->_next; 342 } 343 } 344 return result; 345 } 346 347 void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const { 348 BranchTracker bwt; 349 print_with_childs(st, bwt, print_classes, print_add_info); 350 } 351 352 }; 353 354 class LoadedClassCollectClosure : public KlassClosure { 355 public: 356 LoadedClassInfo* _list; 357 const ClassLoaderData* _cld; 358 int _num_classes; 359 LoadedClassCollectClosure(const ClassLoaderData* cld) 360 : _list(NULL), _cld(cld), _num_classes(0) {} 361 void do_klass(Klass* k) { 362 ResourceMark rm; 363 LoadedClassInfo* lki = new LoadedClassInfo(k, _cld); 364 lki->_next = _list; 365 _list = lki; 366 _num_classes ++; 367 } 368 }; 369 370 class LoaderInfoScanClosure : public CLDClosure { 371 372 const bool _print_classes; 373 const bool _verbose; 374 LoaderTreeNode* _root; 375 376 static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) { 377 assert(info != NULL && cld != NULL, "must be"); 378 LoadedClassCollectClosure lccc(cld); 379 const_cast<ClassLoaderData*>(cld)->classes_do(&lccc); 380 if (lccc._num_classes > 0) { 381 info->add_classes(lccc._list, lccc._num_classes, cld->is_anonymous()); 382 } 383 } 384 385 LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) { 386 387 assert(_root != NULL, "root node must exist"); 388 389 if (loader_oop == NULL) { 390 return _root; 391 } 392 393 // Check if a node for this oop already exists. 394 LoaderTreeNode* info = _root->find(loader_oop); 395 396 if (info == NULL) { 397 // It does not. Create a node. 398 info = new LoaderTreeNode(loader_oop); 399 400 // Add it to tree. 401 LoaderTreeNode* parent_info = _root->find(loader_oop); 402 403 // Recursively add parent nodes if needed. 404 const oop parent_oop = java_lang_ClassLoader::parent(loader_oop); 405 if (parent_oop == NULL) { 406 parent_info = _root; 407 } else { 408 parent_info = find_node_or_add_empty_node(parent_oop); 409 } 410 assert(parent_info != NULL, "must be"); 411 412 parent_info->add_child(info); 413 } 414 return info; 415 } 416 417 418 public: 419 LoaderInfoScanClosure(bool print_classes, bool verbose) 420 : _print_classes(print_classes), _verbose(verbose), _root(NULL) 421 { 422 _root = new LoaderTreeNode(NULL); 423 } 424 425 ~LoaderInfoScanClosure() { 426 delete _root; 427 } 428 429 void print_results(outputStream* st) const { 430 _root->print_with_childs(st, _print_classes, _verbose); 431 } 432 433 void do_cld (ClassLoaderData* cld) { 434 435 // We do not display unloading loaders, for now. 436 if (cld->is_unloading()) { 437 return; 438 } 439 440 const oop loader_oop = cld->class_loader(); 441 442 LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop); 443 assert(info != NULL, "must be"); 444 445 // Update CLD in node, but only if this is the primary CLD for this loader. 446 if (cld->is_anonymous() == false) { 447 assert(info->cld() == NULL, "there should be only one primary CLD per loader"); 448 info->set_cld(cld); 449 } 450 451 // Add classes. 452 fill_in_classes(info, cld); 453 } 454 455 }; 456 457 458 class ClassLoaderHierarchyVMOperation : public VM_Operation { 459 outputStream* const _out; 460 const bool _show_classes; 461 const bool _verbose; 462 public: 463 ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose) : 464 _out(out), _show_classes(show_classes), _verbose(verbose) 465 {} 466 467 VMOp_Type type() const { 468 return VMOp_ClassLoaderHierarchyOperation; 469 } 470 471 void doit() { 472 LoaderInfoScanClosure cl (_show_classes, _verbose); 473 ClassLoaderDataGraph::cld_do(&cl); 474 cl.print_results(_out); 475 } 476 477 }; 478 479 480 481 void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) { 482 ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value()); 483 VMThread::execute(&op); 484 }