< prev index next >
src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp
Print this page
rev 50725 : [mq]: 8205531-vm.classloader-tree-folding
*** 34,48 ****
#include "utilities/globalDefinitions.hpp"
#include "utilities/ostream.hpp"
ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap)
! : DCmdWithParser(output, heap)
! , _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false")
! , _verbose("verbose", "Print detailed information.", "BOOLEAN", false, "false") {
_dcmdparser.add_dcmd_option(&_show_classes);
_dcmdparser.add_dcmd_option(&_verbose);
}
int ClassLoaderHierarchyDCmd::num_arguments() {
ResourceMark rm;
--- 34,50 ----
#include "utilities/globalDefinitions.hpp"
#include "utilities/ostream.hpp"
ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap)
! : DCmdWithParser(output, heap),
! _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false"),
! _verbose("verbose", "Print detailed information.", "BOOLEAN", false, "false"),
! _fold("fold", "Show loaders of the same name and class as one.", "BOOLEAN", true, "true") {
_dcmdparser.add_dcmd_option(&_show_classes);
_dcmdparser.add_dcmd_option(&_verbose);
+ _dcmdparser.add_dcmd_option(&_fold);
}
int ClassLoaderHierarchyDCmd::num_arguments() {
ResourceMark rm;
*** 125,138 ****
};
class LoaderTreeNode : public ResourceObj {
// We walk the CLDG and, for each CLD which is non-anonymous, add
! // a tree node. To add a node we need its parent node; if it itself
! // does not exist yet, we add a preliminary node for it. This preliminary
! // node just contains its loader oop; later, when encountering its CLD in
! // our CLDG walk, we complete the missing information in this node.
const oop _loader_oop;
const ClassLoaderData* _cld;
LoaderTreeNode* _child;
--- 127,142 ----
};
class LoaderTreeNode : public ResourceObj {
// We walk the CLDG and, for each CLD which is non-anonymous, add
! // a tree node.
! // To add a node we need its parent node; if the parent node does not yet
! // exist - because we have not yet encountered the CLD for the parent loader -
! // we we add a preliminary empty LoaderTreeNode for it. This preliminary node
! // just contains the loader oop and nothing else. Once we encounter the CLD of
! // this parent loader, we fill in all the other details.
const oop _loader_oop;
const ClassLoaderData* _cld;
LoaderTreeNode* _child;
*** 142,151 ****
--- 146,161 ----
int _num_classes;
LoadedClassInfo* _anon_classes;
int _num_anon_classes;
+ // In default view, similar tree nodes (same loader class, same or no name)
+ // are folded into each other to make the output more readable.
+ // _num_folded contains the number of nodes which have been folded into this
+ // one.
+ int _num_folded;
+
void print_with_childs(outputStream* st, BranchTracker& branchtracker,
bool print_classes, bool verbose) const {
ResourceMark rm;
*** 168,178 ****
} else {
if (loader_name != NULL) {
st->print(" \"%s\",", loader_name->as_C_string());
}
st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
! st->print(" {" PTR_FORMAT "}", p2i(_loader_oop));
}
st->cr();
// Output following this node (node details and child nodes) - up to the next sibling node
// needs to be prefixed with "|" if there is a follow up sibling.
--- 178,190 ----
} else {
if (loader_name != NULL) {
st->print(" \"%s\",", loader_name->as_C_string());
}
st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
! if (_num_folded > 0) {
! st->print(" (+ %d more)", _num_folded);
! }
}
st->cr();
// Output following this node (node details and child nodes) - up to the next sibling node
// needs to be prefixed with "|" if there is a follow up sibling.
*** 191,200 ****
--- 203,214 ----
const int indentation = 18;
if (verbose) {
branchtracker.print(st);
+ st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Oop:", p2i(_loader_oop));
+ branchtracker.print(st);
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
branchtracker.print(st);
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));
// Empty line
*** 244,254 ****
}
st->print("%s", lci->_klass->external_name());
// For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD.
assert(lci->_cld != _cld, "must be");
if (verbose) {
! st->print(" (CLD: " PTR_FORMAT ")", p2i(lci->_cld));
}
st->cr();
}
branchtracker.print(st);
st->print("%*s ", indentation, "");
--- 258,268 ----
}
st->print("%s", lci->_klass->external_name());
// For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD.
assert(lci->_cld != _cld, "must be");
if (verbose) {
! st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
}
st->cr();
}
branchtracker.print(st);
st->print("%*s ", indentation, "");
*** 270,286 ****
c = c->_next;
}
}
public:
LoaderTreeNode(const oop loader_oop)
! : _loader_oop(loader_oop), _cld(NULL)
! , _child(NULL), _next(NULL)
! , _classes(NULL), _anon_classes(NULL)
! , _num_classes(0), _num_anon_classes(0) {}
void set_cld(const ClassLoaderData* cld) {
_cld = cld;
}
--- 284,309 ----
c = c->_next;
}
}
+ // Helper: Attempt to fold this node into the target node. If success, returns true.
+ // Folding can be done if both nodes are leaf nodes and they refer to the same loader class
+ // and they have the same name or no name (note: leaf check is done by caller).
+ bool can_fold_into(LoaderTreeNode* target_node) const {
+ assert(is_leaf() && target_node->is_leaf(), "must be leaf");
+ return _cld->class_loader_klass() == target_node->_cld->class_loader_klass() &&
+ _cld->name() == target_node->_cld->name();
+ }
+
public:
LoaderTreeNode(const oop loader_oop)
! : _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL),
! _classes(NULL), _anon_classes(NULL), _num_classes(0), _num_anon_classes(0),
! _num_folded(0)
! {}
void set_cld(const ClassLoaderData* cld) {
_cld = cld;
}
*** 329,338 ****
--- 352,394 ----
}
}
return result;
}
+ bool is_leaf() const { return _child == NULL; }
+
+ // Attempt to fold similar nodes among this node's children. We only fold leaf nodes
+ // (no child class loaders).
+ // For non-leaf nodes (class loaders with child class loaders), do this recursivly.
+ void fold_children() {
+ LoaderTreeNode* node = _child;
+ LoaderTreeNode* prev = NULL;
+ while (node != NULL) {
+ LoaderTreeNode* matching_node = NULL;
+ if (node->is_leaf()) {
+ // Look among the preceeding node siblings for a match.
+ for (LoaderTreeNode* node2 = _child; node2 != node && matching_node == NULL;
+ node2 = node2->_next) {
+ if (node2->is_leaf() && node->can_fold_into(node2)) {
+ matching_node = node2;
+ }
+ }
+ } else {
+ node->fold_children();
+ }
+ if (matching_node != NULL) {
+ // Increase fold count for the matching node and remove folded node from the child list.
+ matching_node->_num_folded ++;
+ assert(prev != NULL, "Sanity"); // can never happen since we do not fold the first node.
+ prev->_next = node->_next;
+ } else {
+ prev = node;
+ }
+ node = node->_next;
+ }
+ }
+
void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const {
BranchTracker bwt;
print_with_childs(st, bwt, print_classes, print_add_info);
}
*** 431,450 ****
// Add classes.
fill_in_classes(info, cld);
}
};
class ClassLoaderHierarchyVMOperation : public VM_Operation {
outputStream* const _out;
const bool _show_classes;
const bool _verbose;
public:
! ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose) :
! _out(out), _show_classes(show_classes), _verbose(verbose)
{}
VMOp_Type type() const {
return VMOp_ClassLoaderHierarchyOperation;
}
--- 487,511 ----
// Add classes.
fill_in_classes(info, cld);
}
+ void fold() {
+ _root->fold_children();
+ }
+
};
class ClassLoaderHierarchyVMOperation : public VM_Operation {
outputStream* const _out;
const bool _show_classes;
const bool _verbose;
+ const bool _fold;
public:
! ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose, bool fold) :
! _out(out), _show_classes(show_classes), _verbose(verbose), _fold(fold)
{}
VMOp_Type type() const {
return VMOp_ClassLoaderHierarchyOperation;
}
*** 452,465 ****
void doit() {
assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint");
ResourceMark rm;
LoaderInfoScanClosure cl (_show_classes, _verbose);
ClassLoaderDataGraph::cld_do(&cl);
cl.print_results(_out);
}
};
// This command needs to be executed at a safepoint.
void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {
! ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value());
VMThread::execute(&op);
}
--- 513,532 ----
void doit() {
assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint");
ResourceMark rm;
LoaderInfoScanClosure cl (_show_classes, _verbose);
ClassLoaderDataGraph::cld_do(&cl);
+ // In non-verbose and non-show-classes mode, attempt to fold the tree.
+ if (_fold) {
+ if (!_verbose && !_show_classes) {
+ cl.fold();
+ }
+ }
cl.print_results(_out);
}
};
// This command needs to be executed at a safepoint.
void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) {
! ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value(), _fold.value());
VMThread::execute(&op);
}
< prev index next >