mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-09 09:58:43 +00:00
8293156: Dcmd VM.classloaders fails to print the full hierarchy
Reviewed-by: dholmes, cjplummer
This commit is contained in:
parent
711e2520ad
commit
c6be2cd347
@ -122,7 +122,7 @@ public:
|
||||
const ClassLoaderData* const _cld;
|
||||
|
||||
LoadedClassInfo(Klass* klass, const ClassLoaderData* cld)
|
||||
: _klass(klass), _cld(cld) {}
|
||||
: _next(NULL), _klass(klass), _cld(cld) {}
|
||||
|
||||
};
|
||||
|
||||
@ -137,7 +137,7 @@ class LoaderTreeNode : public ResourceObj {
|
||||
// this parent loader, we fill in all the other details.
|
||||
|
||||
const oop _loader_oop;
|
||||
const ClassLoaderData* _cld;
|
||||
const ClassLoaderData* _cld; // May be NULL if loader never loaded anything
|
||||
|
||||
LoaderTreeNode* _child;
|
||||
LoaderTreeNode* _next;
|
||||
@ -154,33 +154,59 @@ class LoaderTreeNode : public ResourceObj {
|
||||
// one.
|
||||
int _num_folded;
|
||||
|
||||
void print_with_childs(outputStream* st, BranchTracker& branchtracker,
|
||||
// Returns Klass of loader; NULL for bootstrap loader
|
||||
const Klass* loader_klass() const {
|
||||
return (_loader_oop != NULL) ? _loader_oop->klass() : NULL;
|
||||
}
|
||||
|
||||
// Returns ResourceArea-allocated class name of loader class; "" if there is no klass (bootstrap loader)
|
||||
const char* loader_class_name() const {
|
||||
const Klass* klass = loader_klass();
|
||||
return klass != NULL ? klass->external_name() : "";
|
||||
}
|
||||
|
||||
// Returns oop of loader name; NULL for bootstrap; NULL if no name was set
|
||||
oop loader_name_oop() const {
|
||||
return (_loader_oop != NULL) ? java_lang_ClassLoader::name(_loader_oop) : NULL;
|
||||
}
|
||||
|
||||
// Returns ResourceArea-allocated name of loader, "" if none is set
|
||||
const char* loader_name() const {
|
||||
oop name_oop = loader_name_oop();
|
||||
return name_oop != NULL ? java_lang_String::as_utf8_string(name_oop) : "";
|
||||
}
|
||||
|
||||
bool is_bootstrap() const {
|
||||
if (_loader_oop == NULL) {
|
||||
assert(_cld != NULL && _cld->is_boot_class_loader_data(), "bootstrap loader must have CLD");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void print_with_child_nodes(outputStream* st, BranchTracker& branchtracker,
|
||||
bool print_classes, bool verbose) const {
|
||||
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
|
||||
ResourceMark rm;
|
||||
|
||||
if (_cld == NULL) {
|
||||
// Not sure how this could happen: we added a preliminary node for a parent but then never encountered
|
||||
// its CLD?
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve information.
|
||||
const Klass* const loader_klass = _cld->class_loader_klass();
|
||||
const Symbol* const loader_name = _cld->name();
|
||||
const Klass* const the_loader_klass = loader_klass();
|
||||
const char* const the_loader_class_name = loader_class_name();
|
||||
const char* const the_loader_name = loader_name();
|
||||
|
||||
branchtracker.print(st);
|
||||
|
||||
// e.g. +-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
|
||||
st->print("+%.*s", BranchTracker::twig_len, "----------");
|
||||
if (_cld->is_the_null_class_loader_data()) {
|
||||
if (is_bootstrap()) {
|
||||
st->print(" <bootstrap>");
|
||||
} else {
|
||||
assert(!_cld->has_class_mirror_holder(), "_cld must be the primary cld");
|
||||
if (loader_name != NULL) {
|
||||
st->print(" \"%s\",", loader_name->as_C_string());
|
||||
if (the_loader_name[0] != '\0') {
|
||||
st->print(" \"%s\",", the_loader_name);
|
||||
}
|
||||
st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
|
||||
st->print(" %s", the_loader_class_name);
|
||||
if (_num_folded > 0) {
|
||||
st->print(" (+ %d more)", _num_folded);
|
||||
}
|
||||
@ -210,7 +236,7 @@ class LoaderTreeNode : public ResourceObj {
|
||||
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));
|
||||
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(the_loader_klass));
|
||||
|
||||
// Empty line
|
||||
branchtracker.print(st);
|
||||
@ -219,6 +245,7 @@ class LoaderTreeNode : public ResourceObj {
|
||||
|
||||
if (print_classes) {
|
||||
if (_classes != NULL) {
|
||||
assert(_cld != NULL, "we have classes, we should have a CLD");
|
||||
for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
|
||||
// non-strong hidden classes should not live in
|
||||
// the primary CLD of their loaders.
|
||||
@ -243,6 +270,7 @@ class LoaderTreeNode : public ResourceObj {
|
||||
}
|
||||
|
||||
if (_hidden_classes != NULL) {
|
||||
assert(_cld != NULL, "we have classes, we should have a CLD");
|
||||
for (LoadedClassInfo* lci = _hidden_classes; lci; lci = lci->_next) {
|
||||
branchtracker.print(st);
|
||||
if (lci == _hidden_classes) { // first iteration
|
||||
@ -276,7 +304,7 @@ class LoaderTreeNode : public ResourceObj {
|
||||
// Print children, recursively
|
||||
LoaderTreeNode* c = _child;
|
||||
while (c != NULL) {
|
||||
c->print_with_childs(st, branchtracker, print_classes, verbose);
|
||||
c->print_with_child_nodes(st, branchtracker, print_classes, verbose);
|
||||
c = c->_next;
|
||||
}
|
||||
|
||||
@ -285,10 +313,21 @@ class LoaderTreeNode : public ResourceObj {
|
||||
// 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 {
|
||||
bool can_fold_into(const 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();
|
||||
|
||||
// Must have the same non-null klass
|
||||
const Klass* k = loader_klass();
|
||||
if (k == NULL || k != target_node->loader_klass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must have the same loader name, or none
|
||||
if (::strcmp(loader_name(), target_node->loader_name()) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
@ -300,6 +339,7 @@ public:
|
||||
{}
|
||||
|
||||
void set_cld(const ClassLoaderData* cld) {
|
||||
assert(_cld == NULL, "there should be only one primary CLD per loader");
|
||||
_cld = cld;
|
||||
}
|
||||
|
||||
@ -334,14 +374,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const ClassLoaderData* cld() const {
|
||||
return _cld;
|
||||
}
|
||||
|
||||
const oop loader_oop() const {
|
||||
return _loader_oop;
|
||||
}
|
||||
|
||||
LoaderTreeNode* find(const oop loader_oop) {
|
||||
LoaderTreeNode* result = NULL;
|
||||
if (_loader_oop == loader_oop) {
|
||||
@ -364,6 +396,7 @@ public:
|
||||
void fold_children() {
|
||||
LoaderTreeNode* node = _child;
|
||||
LoaderTreeNode* prev = NULL;
|
||||
ResourceMark rm;
|
||||
while (node != NULL) {
|
||||
LoaderTreeNode* matching_node = NULL;
|
||||
if (node->is_leaf()) {
|
||||
@ -389,9 +422,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const {
|
||||
void print_with_child_nodes(outputStream* st, bool print_classes, bool print_add_info) const {
|
||||
BranchTracker bwt;
|
||||
print_with_childs(st, bwt, print_classes, print_add_info);
|
||||
print_with_child_nodes(st, bwt, print_classes, print_add_info);
|
||||
}
|
||||
|
||||
};
|
||||
@ -466,7 +499,7 @@ public:
|
||||
}
|
||||
|
||||
void print_results(outputStream* st) const {
|
||||
_root->print_with_childs(st, _print_classes, _verbose);
|
||||
_root->print_with_child_nodes(st, _print_classes, _verbose);
|
||||
}
|
||||
|
||||
void do_cld (ClassLoaderData* cld) {
|
||||
@ -483,7 +516,6 @@ public:
|
||||
|
||||
// Update CLD in node, but only if this is the primary CLD for this loader.
|
||||
if (cld->has_class_mirror_holder() == false) {
|
||||
assert(info->cld() == NULL, "there should be only one primary CLD per loader");
|
||||
info->set_cld(cld);
|
||||
}
|
||||
|
||||
|
||||
@ -48,6 +48,19 @@ import java.lang.ref.Reference;
|
||||
|
||||
public class ClassLoaderHierarchyTest {
|
||||
|
||||
class EmptyDelegatingLoader extends ClassLoader {
|
||||
EmptyDelegatingLoader(String name, ClassLoader parent) {
|
||||
super(name, parent);
|
||||
}
|
||||
}
|
||||
|
||||
static void loadTestClassInLoaderAndCheck(String classname, ClassLoader loader) throws ClassNotFoundException {
|
||||
Class<?> c = Class.forName(classname, true, loader);
|
||||
if (c.getClassLoader() != loader) {
|
||||
Assert.fail(classname + " defined by wrong classloader: " + c.getClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
//+-- <bootstrap>
|
||||
// |
|
||||
// +-- "platform", jdk.internal.loader.ClassLoaders$PlatformClassLoader
|
||||
@ -60,39 +73,76 @@ public class ClassLoaderHierarchyTest {
|
||||
|
||||
public void run(CommandExecutor executor) throws ClassNotFoundException {
|
||||
|
||||
// A) one unnamed, two named loaders
|
||||
ClassLoader unnamed_cl = new TestClassLoader(null, null);
|
||||
Class<?> c1 = Class.forName("TestClass2", true, unnamed_cl);
|
||||
if (c1.getClassLoader() != unnamed_cl) {
|
||||
Assert.fail("TestClass defined by wrong classloader: " + c1.getClassLoader());
|
||||
}
|
||||
|
||||
ClassLoader named_cl = new TestClassLoader("Kevin", null);
|
||||
Class<?> c2 = Class.forName("TestClass2", true, named_cl);
|
||||
if (c2.getClassLoader() != named_cl) {
|
||||
Assert.fail("TestClass defined by wrong classloader: " + c2.getClassLoader());
|
||||
}
|
||||
|
||||
ClassLoader named_child_cl = new TestClassLoader("Bill", unnamed_cl);
|
||||
Class<?> c3 = Class.forName("TestClass2", true, named_child_cl);
|
||||
if (c3.getClassLoader() != named_child_cl) {
|
||||
Assert.fail("TestClass defined by wrong classloader: " + c3.getClassLoader());
|
||||
}
|
||||
loadTestClassInLoaderAndCheck("TestClass2", unnamed_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_cl);
|
||||
|
||||
// B) A named CL with empty loaders as parents (JDK-8293156)
|
||||
EmptyDelegatingLoader emptyLoader1 = new EmptyDelegatingLoader("EmptyLoader1", null);
|
||||
EmptyDelegatingLoader emptyLoader2 = new EmptyDelegatingLoader("EmptyLoader2", emptyLoader1);
|
||||
ClassLoader named_child_2_cl = new TestClassLoader("Child2", emptyLoader2);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_2_cl);
|
||||
|
||||
// C) Test output for several class loaders, same class, same name, empty parents,
|
||||
// and all these should be folded by default.
|
||||
EmptyDelegatingLoader emptyLoader3 = new EmptyDelegatingLoader("EmptyLoader3", null);
|
||||
EmptyDelegatingLoader emptyLoader4 = new EmptyDelegatingLoader("EmptyLoader4", emptyLoader3);
|
||||
ClassLoader named_child_3_cl = new TestClassLoader("ChildX", emptyLoader4); // Same names
|
||||
ClassLoader named_child_4_cl = new TestClassLoader("ChildX", emptyLoader4);
|
||||
ClassLoader named_child_5_cl = new TestClassLoader("ChildX", emptyLoader4);
|
||||
ClassLoader named_child_6_cl = new TestClassLoader("ChildX", emptyLoader4);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_3_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_4_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_5_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_6_cl);
|
||||
|
||||
// D) Test output for several *unnamed* class loaders, same class, same parents,
|
||||
// and all these should be folded by default too.
|
||||
EmptyDelegatingLoader emptyLoader5 = new EmptyDelegatingLoader(null, null);
|
||||
EmptyDelegatingLoader emptyLoader6 = new EmptyDelegatingLoader(null, emptyLoader5);
|
||||
ClassLoader named_child_7_cl = new TestClassLoader(null, emptyLoader6); // Same names
|
||||
ClassLoader named_child_8_cl = new TestClassLoader(null, emptyLoader6);
|
||||
ClassLoader named_child_9_cl = new TestClassLoader(null, emptyLoader6);
|
||||
ClassLoader named_child_10_cl = new TestClassLoader(null, emptyLoader6);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_7_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_8_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_9_cl);
|
||||
loadTestClassInLoaderAndCheck("TestClass2", named_child_10_cl);
|
||||
|
||||
// First test: simple output, no classes displayed
|
||||
OutputAnalyzer output = executor.execute("VM.classloaders");
|
||||
output.shouldContain("<bootstrap>");
|
||||
output.shouldMatch(".*TestClassLoader");
|
||||
output.shouldMatch("Kevin.*TestClassLoader");
|
||||
output.shouldMatch("Bill.*TestClassLoader");
|
||||
// (A)
|
||||
output.shouldContain("+-- <bootstrap>");
|
||||
output.shouldContain(" +-- \"platform\", jdk.internal.loader.ClassLoaders$PlatformClassLoader");
|
||||
output.shouldContain(" | +-- \"app\", jdk.internal.loader.ClassLoaders$AppClassLoader");
|
||||
output.shouldContain(" +-- \"Kevin\", ClassLoaderHierarchyTest$TestClassLoader");
|
||||
output.shouldContain(" +-- ClassLoaderHierarchyTest$TestClassLoader");
|
||||
output.shouldContain(" | +-- \"Bill\", ClassLoaderHierarchyTest$TestClassLoader");
|
||||
// (B)
|
||||
output.shouldContain(" +-- \"EmptyLoader1\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
|
||||
output.shouldContain(" | +-- \"EmptyLoader2\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
|
||||
output.shouldContain(" | +-- \"Child2\", ClassLoaderHierarchyTest$TestClassLoader");
|
||||
// (C)
|
||||
output.shouldContain(" +-- \"EmptyLoader3\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
|
||||
output.shouldContain(" | +-- \"EmptyLoader4\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
|
||||
output.shouldContain(" | +-- \"ChildX\", ClassLoaderHierarchyTest$TestClassLoader (+ 3 more)");
|
||||
// (D)
|
||||
output.shouldContain(" +-- ClassLoaderHierarchyTest$EmptyDelegatingLoader");
|
||||
output.shouldContain(" +-- ClassLoaderHierarchyTest$EmptyDelegatingLoader");
|
||||
output.shouldContain(" +-- ClassLoaderHierarchyTest$TestClassLoader (+ 3 more)");
|
||||
|
||||
// Second test: print with classes.
|
||||
output = executor.execute("VM.classloaders show-classes");
|
||||
output.shouldContain("<bootstrap>");
|
||||
output.shouldContain("java.lang.Object");
|
||||
output.shouldMatch(".*TestClassLoader");
|
||||
output.shouldMatch("Kevin.*TestClassLoader");
|
||||
output.shouldMatch("Bill.*TestClassLoader");
|
||||
output.shouldContain("java.lang.Enum");
|
||||
output.shouldContain("java.lang.NullPointerException");
|
||||
output.shouldContain("TestClass2");
|
||||
|
||||
output.shouldContain("Hidden Classes:");
|
||||
|
||||
Reference.reachabilityFence(unnamed_cl);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user