Skip to content

Commit 1ce8f3e

Browse files
committed
8293156: Dcmd VM.classloaders fails to print the full hierarchy
Backport-of: c6be2cd347fc07dcc0da56acf40fc7a005119f09
1 parent 7e58a42 commit 1ce8f3e

File tree

2 files changed

+136
-54
lines changed

2 files changed

+136
-54
lines changed

src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ struct LoadedClassInfo : public ResourceObj {
123123
const ClassLoaderData* const _cld;
124124

125125
LoadedClassInfo(Klass* klass, const ClassLoaderData* cld)
126-
: _klass(klass), _cld(cld) {}
126+
: _next(NULL), _klass(klass), _cld(cld) {}
127127

128128
};
129129

@@ -138,7 +138,7 @@ class LoaderTreeNode : public ResourceObj {
138138
// this parent loader, we fill in all the other details.
139139

140140
const oop _loader_oop;
141-
const ClassLoaderData* _cld;
141+
const ClassLoaderData* _cld; // May be NULL if loader never loaded anything
142142

143143
LoaderTreeNode* _child;
144144
LoaderTreeNode* _next;
@@ -155,33 +155,59 @@ class LoaderTreeNode : public ResourceObj {
155155
// one.
156156
int _num_folded;
157157

158-
void print_with_childs(outputStream* st, BranchTracker& branchtracker,
159-
bool print_classes, bool verbose) const {
158+
// Returns Klass of loader; NULL for bootstrap loader
159+
const Klass* loader_klass() const {
160+
return (_loader_oop != NULL) ? _loader_oop->klass() : NULL;
161+
}
160162

161-
ResourceMark rm;
163+
// Returns ResourceArea-allocated class name of loader class; "" if there is no klass (bootstrap loader)
164+
const char* loader_class_name() const {
165+
const Klass* klass = loader_klass();
166+
return klass != NULL ? klass->external_name() : "";
167+
}
162168

163-
if (_cld == NULL) {
164-
// Not sure how this could happen: we added a preliminary node for a parent but then never encountered
165-
// its CLD?
166-
return;
169+
// Returns oop of loader name; NULL for bootstrap; NULL if no name was set
170+
oop loader_name_oop() const {
171+
return (_loader_oop != NULL) ? java_lang_ClassLoader::name(_loader_oop) : NULL;
172+
}
173+
174+
// Returns ResourceArea-allocated name of loader, "" if none is set
175+
const char* loader_name() const {
176+
oop name_oop = loader_name_oop();
177+
return name_oop != NULL ? java_lang_String::as_utf8_string(name_oop) : "";
178+
}
179+
180+
bool is_bootstrap() const {
181+
if (_loader_oop == NULL) {
182+
assert(_cld != NULL && _cld->is_boot_class_loader_data(), "bootstrap loader must have CLD");
183+
return true;
167184
}
185+
return false;
186+
}
187+
188+
void print_with_child_nodes(outputStream* st, BranchTracker& branchtracker,
189+
bool print_classes, bool verbose) const {
190+
191+
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
192+
193+
ResourceMark rm;
168194

169195
// Retrieve information.
170-
const Klass* const loader_klass = _cld->class_loader_klass();
171-
const Symbol* const loader_name = _cld->name();
196+
const Klass* const the_loader_klass = loader_klass();
197+
const char* const the_loader_class_name = loader_class_name();
198+
const char* const the_loader_name = loader_name();
172199

173200
branchtracker.print(st);
174201

175202
// e.g. "+--- jdk.internal.reflect.DelegatingClassLoader"
176203
st->print("+%.*s", BranchTracker::twig_len, "----------");
177-
if (_cld->is_the_null_class_loader_data()) {
204+
if (is_bootstrap()) {
178205
st->print(" <bootstrap>");
179206
} else {
180-
assert(!_cld->has_class_mirror_holder(), "_cld must be the primary cld");
181-
if (loader_name != NULL) {
182-
st->print(" \"%s\",", loader_name->as_C_string());
207+
if (the_loader_name[0] != '\0') {
208+
st->print(" \"%s\",", the_loader_name);
183209
}
184-
st->print(" %s", loader_klass != NULL ? loader_klass->external_name() : "??");
210+
st->print(" %s", the_loader_class_name);
185211
if (_num_folded > 0) {
186212
st->print(" (+ %d more)", _num_folded);
187213
}
@@ -211,7 +237,7 @@ class LoaderTreeNode : public ResourceObj {
211237
branchtracker.print(st);
212238
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld));
213239
branchtracker.print(st);
214-
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(loader_klass));
240+
st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(the_loader_klass));
215241

216242
// Empty line
217243
branchtracker.print(st);
@@ -220,6 +246,7 @@ class LoaderTreeNode : public ResourceObj {
220246

221247
if (print_classes) {
222248
if (_classes != NULL) {
249+
assert(_cld != NULL, "we have classes, we should have a CLD");
223250
for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
224251
// non-strong hidden classes should not live in
225252
// the primary CLD of their loaders.
@@ -252,6 +279,7 @@ class LoaderTreeNode : public ResourceObj {
252279
}
253280

254281
if (_hidden_classes != NULL) {
282+
assert(_cld != NULL, "we have classes, we should have a CLD");
255283
for (LoadedClassInfo* lci = _hidden_classes; lci; lci = lci->_next) {
256284
branchtracker.print(st);
257285
if (lci == _hidden_classes) { // first iteration
@@ -285,7 +313,7 @@ class LoaderTreeNode : public ResourceObj {
285313
// Print children, recursively
286314
LoaderTreeNode* c = _child;
287315
while (c != NULL) {
288-
c->print_with_childs(st, branchtracker, print_classes, verbose);
316+
c->print_with_child_nodes(st, branchtracker, print_classes, verbose);
289317
c = c->_next;
290318
}
291319

@@ -294,10 +322,21 @@ class LoaderTreeNode : public ResourceObj {
294322
// Helper: Attempt to fold this node into the target node. If success, returns true.
295323
// Folding can be done if both nodes are leaf nodes and they refer to the same loader class
296324
// and they have the same name or no name (note: leaf check is done by caller).
297-
bool can_fold_into(LoaderTreeNode* target_node) const {
325+
bool can_fold_into(const LoaderTreeNode* target_node) const {
298326
assert(is_leaf() && target_node->is_leaf(), "must be leaf");
299-
return _cld->class_loader_klass() == target_node->_cld->class_loader_klass() &&
300-
_cld->name() == target_node->_cld->name();
327+
328+
// Must have the same non-null klass
329+
const Klass* k = loader_klass();
330+
if (k == NULL || k != target_node->loader_klass()) {
331+
return false;
332+
}
333+
334+
// Must have the same loader name, or none
335+
if (::strcmp(loader_name(), target_node->loader_name()) != 0) {
336+
return false;
337+
}
338+
339+
return true;
301340
}
302341

303342
public:
@@ -309,6 +348,7 @@ class LoaderTreeNode : public ResourceObj {
309348
{}
310349

311350
void set_cld(const ClassLoaderData* cld) {
351+
assert(_cld == NULL, "there should be only one primary CLD per loader");
312352
_cld = cld;
313353
}
314354

@@ -343,14 +383,6 @@ class LoaderTreeNode : public ResourceObj {
343383
}
344384
}
345385

346-
const ClassLoaderData* cld() const {
347-
return _cld;
348-
}
349-
350-
const oop loader_oop() const {
351-
return _loader_oop;
352-
}
353-
354386
LoaderTreeNode* find(const oop loader_oop) {
355387
LoaderTreeNode* result = NULL;
356388
if (_loader_oop == loader_oop) {
@@ -373,6 +405,7 @@ class LoaderTreeNode : public ResourceObj {
373405
void fold_children() {
374406
LoaderTreeNode* node = _child;
375407
LoaderTreeNode* prev = NULL;
408+
ResourceMark rm;
376409
while (node != NULL) {
377410
LoaderTreeNode* matching_node = NULL;
378411
if (node->is_leaf()) {
@@ -398,9 +431,9 @@ class LoaderTreeNode : public ResourceObj {
398431
}
399432
}
400433

401-
void print_with_childs(outputStream* st, bool print_classes, bool print_add_info) const {
434+
void print_with_child_nodes(outputStream* st, bool print_classes, bool print_add_info) const {
402435
BranchTracker bwt;
403-
print_with_childs(st, bwt, print_classes, print_add_info);
436+
print_with_child_nodes(st, bwt, print_classes, print_add_info);
404437
}
405438

406439
};
@@ -475,7 +508,7 @@ class LoaderInfoScanClosure : public CLDClosure {
475508
}
476509

477510
void print_results(outputStream* st) const {
478-
_root->print_with_childs(st, _print_classes, _verbose);
511+
_root->print_with_child_nodes(st, _print_classes, _verbose);
479512
}
480513

481514
void do_cld (ClassLoaderData* cld) {
@@ -492,7 +525,6 @@ class LoaderInfoScanClosure : public CLDClosure {
492525

493526
// Update CLD in node, but only if this is the primary CLD for this loader.
494527
if (cld->has_class_mirror_holder() == false) {
495-
assert(info->cld() == NULL, "there should be only one primary CLD per loader");
496528
info->set_cld(cld);
497529
}
498530

test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderHierarchyTest.java

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@
4747

4848
public class ClassLoaderHierarchyTest {
4949

50+
class EmptyDelegatingLoader extends ClassLoader {
51+
EmptyDelegatingLoader(String name, ClassLoader parent) {
52+
super(name, parent);
53+
}
54+
}
55+
56+
static void loadTestClassInLoaderAndCheck(String classname, ClassLoader loader) throws ClassNotFoundException {
57+
Class<?> c = Class.forName(classname, true, loader);
58+
if (c.getClassLoader() != loader) {
59+
Assert.fail(classname + " defined by wrong classloader: " + c.getClassLoader());
60+
}
61+
}
62+
5063
//+-- <bootstrap>
5164
// |
5265
// +-- "platform", jdk.internal.loader.ClassLoaders$PlatformClassLoader
@@ -63,39 +76,76 @@ public class ClassLoaderHierarchyTest {
6376

6477
public void run(CommandExecutor executor) throws ClassNotFoundException {
6578

79+
// A) one unnamed, two named loaders
6680
ClassLoader unnamed_cl = new TestClassLoader(null, null);
67-
Class<?> c1 = Class.forName("TestClass2", true, unnamed_cl);
68-
if (c1.getClassLoader() != unnamed_cl) {
69-
Assert.fail("TestClass defined by wrong classloader: " + c1.getClassLoader());
70-
}
71-
7281
ClassLoader named_cl = new TestClassLoader("Kevin", null);
73-
Class<?> c2 = Class.forName("TestClass2", true, named_cl);
74-
if (c2.getClassLoader() != named_cl) {
75-
Assert.fail("TestClass defined by wrong classloader: " + c2.getClassLoader());
76-
}
77-
7882
ClassLoader named_child_cl = new TestClassLoader("Bill", unnamed_cl);
79-
Class<?> c3 = Class.forName("TestClass2", true, named_child_cl);
80-
if (c3.getClassLoader() != named_child_cl) {
81-
Assert.fail("TestClass defined by wrong classloader: " + c3.getClassLoader());
82-
}
83+
loadTestClassInLoaderAndCheck("TestClass2", unnamed_cl);
84+
loadTestClassInLoaderAndCheck("TestClass2", named_cl);
85+
loadTestClassInLoaderAndCheck("TestClass2", named_child_cl);
86+
87+
// B) A named CL with empty loaders as parents (JDK-8293156)
88+
EmptyDelegatingLoader emptyLoader1 = new EmptyDelegatingLoader("EmptyLoader1", null);
89+
EmptyDelegatingLoader emptyLoader2 = new EmptyDelegatingLoader("EmptyLoader2", emptyLoader1);
90+
ClassLoader named_child_2_cl = new TestClassLoader("Child2", emptyLoader2);
91+
loadTestClassInLoaderAndCheck("TestClass2", named_child_2_cl);
92+
93+
// C) Test output for several class loaders, same class, same name, empty parents,
94+
// and all these should be folded by default.
95+
EmptyDelegatingLoader emptyLoader3 = new EmptyDelegatingLoader("EmptyLoader3", null);
96+
EmptyDelegatingLoader emptyLoader4 = new EmptyDelegatingLoader("EmptyLoader4", emptyLoader3);
97+
ClassLoader named_child_3_cl = new TestClassLoader("ChildX", emptyLoader4); // Same names
98+
ClassLoader named_child_4_cl = new TestClassLoader("ChildX", emptyLoader4);
99+
ClassLoader named_child_5_cl = new TestClassLoader("ChildX", emptyLoader4);
100+
ClassLoader named_child_6_cl = new TestClassLoader("ChildX", emptyLoader4);
101+
loadTestClassInLoaderAndCheck("TestClass2", named_child_3_cl);
102+
loadTestClassInLoaderAndCheck("TestClass2", named_child_4_cl);
103+
loadTestClassInLoaderAndCheck("TestClass2", named_child_5_cl);
104+
loadTestClassInLoaderAndCheck("TestClass2", named_child_6_cl);
105+
106+
// D) Test output for several *unnamed* class loaders, same class, same parents,
107+
// and all these should be folded by default too.
108+
EmptyDelegatingLoader emptyLoader5 = new EmptyDelegatingLoader(null, null);
109+
EmptyDelegatingLoader emptyLoader6 = new EmptyDelegatingLoader(null, emptyLoader5);
110+
ClassLoader named_child_7_cl = new TestClassLoader(null, emptyLoader6); // Same names
111+
ClassLoader named_child_8_cl = new TestClassLoader(null, emptyLoader6);
112+
ClassLoader named_child_9_cl = new TestClassLoader(null, emptyLoader6);
113+
ClassLoader named_child_10_cl = new TestClassLoader(null, emptyLoader6);
114+
loadTestClassInLoaderAndCheck("TestClass2", named_child_7_cl);
115+
loadTestClassInLoaderAndCheck("TestClass2", named_child_8_cl);
116+
loadTestClassInLoaderAndCheck("TestClass2", named_child_9_cl);
117+
loadTestClassInLoaderAndCheck("TestClass2", named_child_10_cl);
83118

84119
// First test: simple output, no classes displayed
85120
OutputAnalyzer output = executor.execute("VM.classloaders");
86-
output.shouldContain("<bootstrap>");
87-
output.shouldMatch(".*TestClassLoader");
88-
output.shouldMatch("Kevin.*TestClassLoader");
89-
output.shouldMatch("Bill.*TestClassLoader");
121+
// (A)
122+
output.shouldContain("+-- <bootstrap>");
123+
output.shouldContain(" +-- \"platform\", jdk.internal.loader.ClassLoaders$PlatformClassLoader");
124+
output.shouldContain(" | +-- \"app\", jdk.internal.loader.ClassLoaders$AppClassLoader");
125+
output.shouldContain(" +-- \"Kevin\", ClassLoaderHierarchyTest$TestClassLoader");
126+
output.shouldContain(" +-- ClassLoaderHierarchyTest$TestClassLoader");
127+
output.shouldContain(" | +-- \"Bill\", ClassLoaderHierarchyTest$TestClassLoader");
128+
// (B)
129+
output.shouldContain(" +-- \"EmptyLoader1\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
130+
output.shouldContain(" | +-- \"EmptyLoader2\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
131+
output.shouldContain(" | +-- \"Child2\", ClassLoaderHierarchyTest$TestClassLoader");
132+
// (C)
133+
output.shouldContain(" +-- \"EmptyLoader3\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
134+
output.shouldContain(" | +-- \"EmptyLoader4\", ClassLoaderHierarchyTest$EmptyDelegatingLoader");
135+
output.shouldContain(" | +-- \"ChildX\", ClassLoaderHierarchyTest$TestClassLoader (+ 3 more)");
136+
// (D)
137+
output.shouldContain(" +-- ClassLoaderHierarchyTest$EmptyDelegatingLoader");
138+
output.shouldContain(" +-- ClassLoaderHierarchyTest$EmptyDelegatingLoader");
139+
output.shouldContain(" +-- ClassLoaderHierarchyTest$TestClassLoader (+ 3 more)");
90140

91141
// Second test: print with classes.
92142
output = executor.execute("VM.classloaders show-classes");
93143
output.shouldContain("<bootstrap>");
94144
output.shouldContain("java.lang.Object");
95-
output.shouldMatch(".*TestClassLoader");
96-
output.shouldMatch("Kevin.*TestClassLoader");
97-
output.shouldMatch("Bill.*TestClassLoader");
145+
output.shouldContain("java.lang.Enum");
146+
output.shouldContain("java.lang.NullPointerException");
98147
output.shouldContain("TestClass2");
148+
99149
output.shouldContain("Hidden Classes:");
100150
}
101151

0 commit comments

Comments
 (0)