Path: blob/master/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp
40949 views
/*1* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2018 SAP SE. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#include "precompiled.hpp"2627#include "classfile/classLoaderData.inline.hpp"28#include "classfile/classLoaderDataGraph.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"363738ClassLoaderHierarchyDCmd::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}474849int ClassLoaderHierarchyDCmd::num_arguments() {50ResourceMark rm;51ClassLoaderHierarchyDCmd* dcmd = new ClassLoaderHierarchyDCmd(NULL, false);52if (dcmd != NULL) {53DCmdMark mark(dcmd);54return dcmd->_dcmdparser.num_arguments();55} else {56return 0;57}58}5960// Helper class for drawing the branches to the left of a node.61class BranchTracker : public StackObj {62// "<x>"63// " |---<y>"64// " | |65// " | <z>"66// " | |---<z1>67// " | |---<z2>68// ^^^^^^^ ^^^69// A B7071// 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.7677public:7879enum { max_depth = 64, twig_len = 2, branch_spacing = 5 };8081private:8283char _branches[max_depth];84int _pos;8586public:87BranchTracker()88: _pos(0) {}8990void push(bool has_branch) {91if (_pos < max_depth) {92_branches[_pos] = has_branch ? '|' : ' ';93}94_pos ++; // beyond max depth, omit branch drawing but do count on.95}9697void pop() {98assert(_pos > 0, "must be");99_pos --;100}101102void print(outputStream* st) {103for (int i = 0; i < _pos; i ++) {104st->print("%c%.*s", _branches[i], branch_spacing, " ");105}106}107108class Mark {109BranchTracker& _tr;110public:111Mark(BranchTracker& tr, bool has_branch_here)112: _tr(tr) { _tr.push(has_branch_here); }113~Mark() { _tr.pop(); }114};115116}; // end: BranchTracker117118struct LoadedClassInfo : public ResourceObj {119public:120LoadedClassInfo* _next;121Klass* const _klass;122const ClassLoaderData* const _cld;123124LoadedClassInfo(Klass* klass, const ClassLoaderData* cld)125: _klass(klass), _cld(cld) {}126127};128129class LoaderTreeNode : public ResourceObj {130131// We walk the CLDG and, for each CLD which is findable, add132// a tree node.133// To add a node we need its parent node; if the parent node does not yet134// exist - because we have not yet encountered the CLD for the parent loader -135// we add a preliminary empty LoaderTreeNode for it. This preliminary node136// just contains the loader oop and nothing else. Once we encounter the CLD of137// this parent loader, we fill in all the other details.138139const oop _loader_oop;140const ClassLoaderData* _cld;141142LoaderTreeNode* _child;143LoaderTreeNode* _next;144145LoadedClassInfo* _classes;146int _num_classes;147148LoadedClassInfo* _hidden_classes;149int _num_hidden_classes;150151// In default view, similar tree nodes (same loader class, same name 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 this154// one.155int _num_folded;156157void print_with_childs(outputStream* st, BranchTracker& branchtracker,158bool print_classes, bool verbose) const {159160ResourceMark rm;161162if (_cld == NULL) {163// Not sure how this could happen: we added a preliminary node for a parent but then never encountered164// its CLD?165return;166}167168// Retrieve information.169const Klass* const loader_klass = _cld->class_loader_klass();170const Symbol* const loader_name = _cld->name();171172branchtracker.print(st);173174// e.g. "+--- jdk.internal.reflect.DelegatingClassLoader"175st->print("+%.*s", BranchTracker::twig_len, "----------");176if (_cld->is_the_null_class_loader_data()) {177st->print(" <bootstrap>");178} else {179assert(!_cld->has_class_mirror_holder(), "_cld must be the primary cld");180if (loader_name != NULL) {181st->print(" \"%s\",", loader_name->as_C_string());182}183st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");184if (_num_folded > 0) {185st->print(" (+ %d more)", _num_folded);186}187}188st->cr();189190// Output following this node (node details and child nodes) - up to the next sibling node191// needs to be prefixed with "|" if there is a follow up sibling.192const bool have_sibling = _next != NULL;193BranchTracker::Mark trm(branchtracker, have_sibling);194195{196// optional node details following this node needs to be prefixed with "|"197// if there are follow up child nodes.198const bool have_child = _child != NULL;199BranchTracker::Mark trm(branchtracker, have_child);200201// Empty line202branchtracker.print(st);203st->cr();204205const int indentation = 18;206207if (verbose) {208branchtracker.print(st);209st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop));210branchtracker.print(st);211st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));212branchtracker.print(st);213st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));214215// Empty line216branchtracker.print(st);217st->cr();218}219220if (print_classes) {221if (_classes != NULL) {222for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {223// non-strong hidden classes should not live in224// the primary CLD of their loaders.225assert(lci->_cld == _cld, "must be");226227branchtracker.print(st);228if (lci == _classes) { // first iteration229st->print("%*s ", indentation, "Classes:");230} else {231st->print("%*s ", indentation, "");232}233st->print("%s", lci->_klass->external_name());234235// Special treatment for generated core reflection accessor classes: print invocation target.236if (ReflectionAccessorImplKlassHelper::is_generated_accessor(lci->_klass)) {237st->print(" (invokes: ");238ReflectionAccessorImplKlassHelper::print_invocation_target(st, lci->_klass);239st->print(")");240}241242st->cr();243}244branchtracker.print(st);245st->print("%*s ", indentation, "");246st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es");247248// Empty line249branchtracker.print(st);250st->cr();251}252253if (_hidden_classes != NULL) {254for (LoadedClassInfo* lci = _hidden_classes; lci; lci = lci->_next) {255branchtracker.print(st);256if (lci == _hidden_classes) { // first iteration257st->print("%*s ", indentation, "Hidden Classes:");258} else {259st->print("%*s ", indentation, "");260}261st->print("%s", lci->_klass->external_name());262// For non-strong hidden classes, also print CLD if verbose. Should be a263// different one than the primary CLD.264assert(lci->_cld != _cld, "must be");265if (verbose) {266st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));267}268st->cr();269}270branchtracker.print(st);271st->print("%*s ", indentation, "");272st->print_cr("(%u hidden class%s)", _num_hidden_classes,273(_num_hidden_classes == 1) ? "" : "es");274275// Empty line276branchtracker.print(st);277st->cr();278}279280} // end: print_classes281282} // Pop branchtracker mark283284// Print children, recursively285LoaderTreeNode* c = _child;286while (c != NULL) {287c->print_with_childs(st, branchtracker, print_classes, verbose);288c = c->_next;289}290291}292293// Helper: Attempt to fold this node into the target node. If success, returns true.294// Folding can be done if both nodes are leaf nodes and they refer to the same loader class295// and they have the same name or no name (note: leaf check is done by caller).296bool can_fold_into(LoaderTreeNode* target_node) const {297assert(is_leaf() && target_node->is_leaf(), "must be leaf");298return _cld->class_loader_klass() == target_node->_cld->class_loader_klass() &&299_cld->name() == target_node->_cld->name();300}301302public:303304LoaderTreeNode(const oop loader_oop)305: _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL),306_classes(NULL), _num_classes(0), _hidden_classes(NULL),307_num_hidden_classes(0), _num_folded(0)308{}309310void set_cld(const ClassLoaderData* cld) {311_cld = cld;312}313314void add_child(LoaderTreeNode* info) {315info->_next = _child;316_child = info;317}318319void add_sibling(LoaderTreeNode* info) {320assert(info->_next == NULL, "must be");321info->_next = _next;322_next = info;323}324325void add_classes(LoadedClassInfo* first_class, int num_classes, bool has_class_mirror_holder) {326LoadedClassInfo** p_list_to_add_to;327bool is_hidden = first_class->_klass->is_hidden();328if (has_class_mirror_holder) {329p_list_to_add_to = &_hidden_classes;330} else {331p_list_to_add_to = &_classes;332}333// Search tail.334while ((*p_list_to_add_to) != NULL) {335p_list_to_add_to = &(*p_list_to_add_to)->_next;336}337*p_list_to_add_to = first_class;338if (has_class_mirror_holder) {339_num_hidden_classes += num_classes;340} else {341_num_classes += num_classes;342}343}344345const ClassLoaderData* cld() const {346return _cld;347}348349const oop loader_oop() const {350return _loader_oop;351}352353LoaderTreeNode* find(const oop loader_oop) {354LoaderTreeNode* result = NULL;355if (_loader_oop == loader_oop) {356result = this;357} else {358LoaderTreeNode* c = _child;359while (c != NULL && result == NULL) {360result = c->find(loader_oop);361c = c->_next;362}363}364return result;365}366367bool is_leaf() const { return _child == NULL; }368369// Attempt to fold similar nodes among this node's children. We only fold leaf nodes370// (no child class loaders).371// For non-leaf nodes (class loaders with child class loaders), do this recursivly.372void fold_children() {373LoaderTreeNode* node = _child;374LoaderTreeNode* prev = NULL;375while (node != NULL) {376LoaderTreeNode* matching_node = NULL;377if (node->is_leaf()) {378// Look among the preceeding node siblings for a match.379for (LoaderTreeNode* node2 = _child; node2 != node && matching_node == NULL;380node2 = node2->_next) {381if (node2->is_leaf() && node->can_fold_into(node2)) {382matching_node = node2;383}384}385} else {386node->fold_children();387}388if (matching_node != NULL) {389// Increase fold count for the matching node and remove folded node from the child list.390matching_node->_num_folded ++;391assert(prev != NULL, "Sanity"); // can never happen since we do not fold the first node.392prev->_next = node->_next;393} else {394prev = node;395}396node = node->_next;397}398}399400void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const {401BranchTracker bwt;402print_with_childs(st, bwt, print_classes, print_add_info);403}404405};406407class LoadedClassCollectClosure : public KlassClosure {408public:409LoadedClassInfo* _list;410const ClassLoaderData* _cld;411int _num_classes;412LoadedClassCollectClosure(const ClassLoaderData* cld)413: _list(NULL), _cld(cld), _num_classes(0) {}414void do_klass(Klass* k) {415LoadedClassInfo* lki = new LoadedClassInfo(k, _cld);416lki->_next = _list;417_list = lki;418_num_classes ++;419}420};421422class LoaderInfoScanClosure : public CLDClosure {423424const bool _print_classes;425const bool _verbose;426LoaderTreeNode* _root;427428static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) {429assert(info != NULL && cld != NULL, "must be");430LoadedClassCollectClosure lccc(cld);431const_cast<ClassLoaderData*>(cld)->classes_do(&lccc);432if (lccc._num_classes > 0) {433info->add_classes(lccc._list, lccc._num_classes, cld->has_class_mirror_holder());434}435}436437LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) {438439assert(_root != NULL, "root node must exist");440441if (loader_oop == NULL) {442return _root;443}444445// Check if a node for this oop already exists.446LoaderTreeNode* info = _root->find(loader_oop);447448if (info == NULL) {449// It does not. Create a node.450info = new LoaderTreeNode(loader_oop);451452// Add it to tree.453LoaderTreeNode* parent_info = NULL;454455// Recursively add parent nodes if needed.456const oop parent_oop = java_lang_ClassLoader::parent(loader_oop);457if (parent_oop == NULL) {458parent_info = _root;459} else {460parent_info = find_node_or_add_empty_node(parent_oop);461}462assert(parent_info != NULL, "must be");463464parent_info->add_child(info);465}466return info;467}468469470public:471LoaderInfoScanClosure(bool print_classes, bool verbose)472: _print_classes(print_classes), _verbose(verbose), _root(NULL) {473_root = new LoaderTreeNode(NULL);474}475476void print_results(outputStream* st) const {477_root->print_with_childs(st, _print_classes, _verbose);478}479480void do_cld (ClassLoaderData* cld) {481482// We do not display unloading loaders, for now.483if (!cld->is_alive()) {484return;485}486487const oop loader_oop = cld->class_loader();488489LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop);490assert(info != NULL, "must be");491492// Update CLD in node, but only if this is the primary CLD for this loader.493if (cld->has_class_mirror_holder() == false) {494assert(info->cld() == NULL, "there should be only one primary CLD per loader");495info->set_cld(cld);496}497498// Add classes.499fill_in_classes(info, cld);500}501502void fold() {503_root->fold_children();504}505506};507508509class ClassLoaderHierarchyVMOperation : public VM_Operation {510outputStream* const _out;511const bool _show_classes;512const bool _verbose;513const bool _fold;514public:515ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose, bool fold) :516_out(out), _show_classes(show_classes), _verbose(verbose), _fold(fold)517{}518519VMOp_Type type() const {520return VMOp_ClassLoaderHierarchyOperation;521}522523void doit() {524assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint");525ResourceMark rm;526LoaderInfoScanClosure cl (_show_classes, _verbose);527ClassLoaderDataGraph::loaded_cld_do(&cl);528// In non-verbose and non-show-classes mode, attempt to fold the tree.529if (_fold) {530if (!_verbose && !_show_classes) {531cl.fold();532}533}534cl.print_results(_out);535}536};537538// This command needs to be executed at a safepoint.539void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {540ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value(), _fold.value());541VMThread::execute(&op);542}543544545