Projects
Mega:24.03:SP1:Everything
openjdk-1.8.0
_service:tar_scm:8203682-Add-jcmd-VM.classloade...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:8203682-Add-jcmd-VM.classloaders-command-to-print-ou.patch of Package openjdk-1.8.0
From 953fdbbfbc6512c3f04f3663fa5ad216d7547984 Mon Sep 17 00:00:00 2001 From: eapen <zhangyipeng7@huawei.com> Date: Thu, 15 Dec 2022 09:48:37 +0800 Subject: [PATCH 17/33] I68TO2: 8203682: Add jcmd "VM.classloaders" command to print out class loader hierarchy, details --- .../vm/classfile/classLoaderHierarchyDCmd.cpp | 468 +++++++++++++++++++++ .../vm/classfile/classLoaderHierarchyDCmd.hpp | 59 +++ hotspot/src/share/vm/runtime/vm_operations.hpp | 1 + .../src/share/vm/services/diagnosticCommand.cpp | 2 + .../dcmd/ClassLoaderHierarchyTest.java | 213 ++++++++++ 5 files changed, 743 insertions(+) create mode 100644 hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp create mode 100644 hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp create mode 100644 hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java diff --git a/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp new file mode 100644 index 0000000..4c25091 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/classLoaderHierarchyDCmd.hpp" +#include "memory/allocation.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/safepoint.hpp" +#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; + ClassLoaderHierarchyDCmd* dcmd = new ClassLoaderHierarchyDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } else { + return 0; + } +} + +// Helper class for drawing the branches to the left of a node. +class BranchTracker : public StackObj { + // "<x>" + // " |---<y>" + // " | | + // " | <z>" + // " | |---<z1> + // " | |---<z2> + // ^^^^^^^ ^^^ + // A B + + // Some terms for the graphics: + // - branch: vertical connection between a node's ancestor to a later sibling. + // - branchwork: (A) the string to print as a prefix at the start of each line, contains all branches. + // - twig (B): Length of the dashed line connecting a node to its branch. + // - branch spacing: how many spaces between branches are printed. + +public: + + enum { max_depth = 64, twig_len = 2, branch_spacing = 5 }; + +private: + + char _branches[max_depth]; + int _pos; + +public: + BranchTracker() + : _pos(0) {} + + void push(bool has_branch) { + if (_pos < max_depth) { + _branches[_pos] = has_branch ? '|' : ' '; + } + _pos ++; // beyond max depth, omit branch drawing but do count on. + } + + void pop() { + assert(_pos > 0, "must be"); + _pos --; + } + + void print(outputStream* st) { + for (int i = 0; i < _pos; i ++) { + st->print("%c%.*s", _branches[i], branch_spacing, " "); + } + } + + class Mark { + BranchTracker& _tr; + public: + Mark(BranchTracker& tr, bool has_branch_here) + : _tr(tr) { _tr.push(has_branch_here); } + ~Mark() { _tr.pop(); } + }; + +}; // end: BranchTracker + +struct LoadedClassInfo : public ResourceObj { +public: + LoadedClassInfo* _next; + Klass* const _klass; + const ClassLoaderData* const _cld; + + LoadedClassInfo(Klass* klass, const ClassLoaderData* cld) + : _next(NULL), _klass(klass), _cld(cld) {} + +}; + +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; // May be NULL if loader never loaded anything + + LoaderTreeNode* _child; + LoaderTreeNode* _next; + + LoadedClassInfo* _classes; + int _num_classes; + + LoadedClassInfo* _anon_classes; + int _num_anon_classes; + + // 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() : ""; + } + + bool is_bootstrap() const { + if (_loader_oop == NULL) { + assert(_cld != NULL && _cld->is_the_null_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; + + // Retrieve information. + const Klass* const the_loader_klass = loader_klass(); + const char* const the_loader_class_name = loader_class_name(); + // ClassLoader.java does not contain 'name' field. Replace it with loader_class_name(). + const char* const the_loader_name = the_loader_class_name; + + branchtracker.print(st); + + // e.g. "+--- jdk.internal.reflect.DelegatingClassLoader" + st->print("+%.*s", BranchTracker::twig_len, "----------"); + if (is_bootstrap()) { + st->print(" <bootstrap>"); + } else { + if (the_loader_name[0] != '\0') { + st->print(" \"%s\",", the_loader_name); + } + st->print(" %s", the_loader_class_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. + const bool have_sibling = _next != NULL; + BranchTracker::Mark trm(branchtracker, have_sibling); + + { + // optional node details following this node needs to be prefixed with "|" + // if there are follow up child nodes. + const bool have_child = _child != NULL; + BranchTracker::Mark trm(branchtracker, have_child); + + // Empty line + branchtracker.print(st); + st->cr(); + + const int indentation = 18; + + if (verbose) { + 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(the_loader_klass)); + + // Empty line + branchtracker.print(st); + st->cr(); + } + + 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) { + branchtracker.print(st); + if (lci == _classes) { // first iteration + st->print("%*s ", indentation, "Classes:"); + } else { + st->print("%*s ", indentation, ""); + } + st->print("%s", lci->_klass->external_name()); + st->cr(); + // Non-anonymous classes should live in the primary CLD of its loader + assert(lci->_cld == _cld, "must be"); + } + branchtracker.print(st); + st->print("%*s ", indentation, ""); + st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es"); + + // Empty line + branchtracker.print(st); + st->cr(); + } + + if (_anon_classes != NULL) { + assert(_cld != NULL, "we have classes, we should have a CLD"); + for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) { + branchtracker.print(st); + if (lci == _anon_classes) { // first iteration + st->print("%*s ", indentation, "Anonymous Classes:"); + } else { + st->print("%*s ", indentation, ""); + } + 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, ""); + st->print_cr("(%u anonymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es"); + + // Empty line + branchtracker.print(st); + st->cr(); + } + + } // end: print_classes + + } // Pop branchtracker mark + + // Print children, recursively + LoaderTreeNode* c = _child; + while (c != NULL) { + c->print_with_child_nodes(st, branchtracker, print_classes, verbose); + 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) { + assert(_cld == NULL, "there should be only one primary CLD per loader"); + _cld = cld; + } + + void add_child(LoaderTreeNode* info) { + info->_next = _child; + _child = info; + } + + void add_sibling(LoaderTreeNode* info) { + assert(info->_next == NULL, "must be"); + info->_next = _next; + _next = info; + } + + void add_classes(LoadedClassInfo* first_class, int num_classes, bool anonymous) { + LoadedClassInfo** p_list_to_add_to = anonymous ? &_anon_classes : &_classes; + // Search tail. + while ((*p_list_to_add_to) != NULL) { + p_list_to_add_to = &(*p_list_to_add_to)->_next; + } + *p_list_to_add_to = first_class; + if (anonymous) { + _num_anon_classes += num_classes; + } else { + _num_classes += num_classes; + } + } + + LoaderTreeNode* find(const oop loader_oop) { + LoaderTreeNode* result = NULL; + if (_loader_oop == loader_oop) { + result = this; + } else { + LoaderTreeNode* c = _child; + while (c != NULL && result == NULL) { + result = c->find(loader_oop); + c = c->_next; + } + } + return result; + } + + void print_with_child_nodes(outputStream* st, bool print_classes, bool print_add_info) const { + BranchTracker bwt; + print_with_child_nodes(st, bwt, print_classes, print_add_info); + } + +}; + +class LoadedClassCollectClosure : public KlassClosure { +public: + LoadedClassInfo* _list; + const ClassLoaderData* _cld; + int _num_classes; + LoadedClassCollectClosure(const ClassLoaderData* cld) + : _list(NULL), _cld(cld), _num_classes(0) {} + void do_klass(Klass* k) { + LoadedClassInfo* lki = new LoadedClassInfo(k, _cld); + lki->_next = _list; + _list = lki; + _num_classes ++; + } +}; + +class LoaderInfoScanClosure : public CLDClosure { + + const bool _print_classes; + const bool _verbose; + LoaderTreeNode* _root; + + static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) { + assert(info != NULL && cld != NULL, "must be"); + LoadedClassCollectClosure lccc(cld); + const_cast<ClassLoaderData*>(cld)->classes_do(&lccc); + if (lccc._num_classes > 0) { + info->add_classes(lccc._list, lccc._num_classes, cld->is_anonymous()); + } + } + + LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) { + + assert(_root != NULL, "root node must exist"); + + if (loader_oop == NULL) { + return _root; + } + + // Check if a node for this oop already exists. + LoaderTreeNode* info = _root->find(loader_oop); + + if (info == NULL) { + // It does not. Create a node. + info = new LoaderTreeNode(loader_oop); + + // Add it to tree. + LoaderTreeNode* parent_info = NULL; + + // Recursively add parent nodes if needed. + const oop parent_oop = java_lang_ClassLoader::parent(loader_oop); + if (parent_oop == NULL) { + parent_info = _root; + } else { + parent_info = find_node_or_add_empty_node(parent_oop); + } + assert(parent_info != NULL, "must be"); + + parent_info->add_child(info); + } + return info; + } + + +public: + LoaderInfoScanClosure(bool print_classes, bool verbose) + : _print_classes(print_classes), _verbose(verbose), _root(NULL) { + _root = new LoaderTreeNode(NULL); + } + + void print_results(outputStream* st) const { + _root->print_with_child_nodes(st, _print_classes, _verbose); + } + + void do_cld (ClassLoaderData* cld) { + + // We do not display unloading loaders, for now. + if (cld->is_unloading()) { + return; + } + + const oop loader_oop = cld->class_loader(); + + LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop); + assert(info != NULL, "must be"); + + // Update CLD in node, but only if this is the primary CLD for this loader. + if (cld->is_anonymous() == false) { + info->set_cld(cld); + } + + // 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; + } + + 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); +} \ No newline at end of file diff --git a/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp new file mode 100644 index 0000000..49027e6 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef HOTSPOT_SHARE_CLASSFILE_CLASSLOADERHIERARCHYDCMD_HPP_ +#define HOTSPOT_SHARE_CLASSFILE_CLASSLOADERHIERARCHYDCMD_HPP_ + +#include "services/diagnosticCommand.hpp" + +class ClassLoaderHierarchyDCmd: public DCmdWithParser { + DCmdArgument<bool> _show_classes; + DCmdArgument<bool> _verbose; +public: + + ClassLoaderHierarchyDCmd(outputStream* output, bool heap); + + static const char* name() { + return "VM.classloaders"; + } + + static const char* description() { + return "Prints classloader hierarchy."; + } + static const char* impact() { + return "Medium: Depends on number of class loaders and classes loaded."; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", + "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); + +}; + +#endif /* HOTSPOT_SHARE_CLASSFILE_CLASSLOADERHIERARCHYDCMD_HPP_ */ \ No newline at end of file diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp index a8ba78b..3744040 100644 --- a/hotspot/src/share/vm/runtime/vm_operations.hpp +++ b/hotspot/src/share/vm/runtime/vm_operations.hpp @@ -98,6 +98,7 @@ template(RotateGCLog) \ template(WhiteBoxOperation) \ template(ClassLoaderStatsOperation) \ + template(ClassLoaderHierarchyOperation) \ template(JFROldObject) \ template(PrintClasses) \ diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp index e4e6185..d3b91d9 100644 --- a/hotspot/src/share/vm/services/diagnosticCommand.cpp +++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "cds/dynamicArchive.hpp" +#include "classfile/classLoaderHierarchyDCmd.hpp" #include "classfile/classLoaderStats.hpp" #include "gc_implementation/shared/vmGCOperations.hpp" #include "runtime/javaCalls.hpp" @@ -70,6 +71,7 @@ void DCmdRegistrant::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RotateGCLogDCmd>(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderStatsDCmd>(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderHierarchyDCmd>(full_export, true, false)); #ifdef LINUX DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false)); #endif // LINUX diff --git a/hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java b/hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java new file mode 100644 index 0000000..378997d --- /dev/null +++ b/hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test of diagnostic command VM.classloaders + * @library /testlibrary + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @run testng ClassLoaderHierarchyTest + */ + +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.CommandExecutor; +import com.oracle.java.testlibrary.JMXExecutor; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +public class ClassLoaderHierarchyTest { + + class EmptyDelegatingLoader extends ClassLoader { + EmptyDelegatingLoader(ClassLoader parent) { + super(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> +// | +// +-- "sun.misc.Launcher$ExtClassLoader", sun.misc.Launcher$ExtClassLoader +// | | +// | +-- "sun.misc.Launcher$AppClassLoader", sun.misc.Launcher$AppClassLoader +// | +// +-- "sun.reflect.DelegatingClassLoader", sun.reflect.DelegatingClassLoader +// | +// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader +// | | +// | +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader +// | +// +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader +// | | +// | +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader +// | | +// | +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader +// | +// +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader +// | +// +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader +// | +// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader +// | +// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader +// | +// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader + + + public void run(CommandExecutor executor) throws ClassNotFoundException { + + // A) one unnamed, two named loaders + ClassLoader unnamed_cl = new TestClassLoader(null); + ClassLoader named_child_cl = new TestClassLoader(unnamed_cl); + loadTestClassInLoaderAndCheck("TestClass2", unnamed_cl); + loadTestClassInLoaderAndCheck("TestClass2", named_child_cl); + + // B) A named CL with empty loaders as parents (JDK-8293156) + EmptyDelegatingLoader emptyLoader1 = new EmptyDelegatingLoader( null); + EmptyDelegatingLoader emptyLoader2 = new EmptyDelegatingLoader(emptyLoader1); + ClassLoader named_child_2_cl = new TestClassLoader(emptyLoader2); + loadTestClassInLoaderAndCheck("TestClass2", named_child_2_cl); + + // C) Test output for several *unnamed* class loaders, same class, same parents, + // and all these should be folded by default. + EmptyDelegatingLoader emptyLoader3 = new EmptyDelegatingLoader(null); + EmptyDelegatingLoader emptyLoader4 = new EmptyDelegatingLoader(emptyLoader3); + ClassLoader named_child_3_cl = new TestClassLoader(emptyLoader4); // Same names + ClassLoader named_child_4_cl = new TestClassLoader(emptyLoader4); + ClassLoader named_child_5_cl = new TestClassLoader(emptyLoader4); + loadTestClassInLoaderAndCheck("TestClass2", named_child_3_cl); + loadTestClassInLoaderAndCheck("TestClass2", named_child_4_cl); + loadTestClassInLoaderAndCheck("TestClass2", named_child_5_cl); + + // First test: simple output, no classes displayed + OutputAnalyzer output = executor.execute("VM.classloaders"); + // (A) + output.shouldContain("+-- <bootstrap>"); + output.shouldContain(" +-- \"sun.misc.Launcher$ExtClassLoader\", sun.misc.Launcher$ExtClassLoader"); + output.shouldContain(" | +-- \"sun.misc.Launcher$AppClassLoader\", sun.misc.Launcher$AppClassLoader"); + output.shouldContain(" +-- \"sun.reflect.DelegatingClassLoader\", sun.reflect.DelegatingClassLoader"); + output.shouldContain(" +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); + output.shouldContain(" | +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); + // (B) + output.shouldContain(" +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); + output.shouldContain(" | +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); + output.shouldContain(" | +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); + // (C) + output.shouldContain(" +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); + output.shouldContain(" +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); + output.shouldContain(" +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); + + // Second test: print with classes. + output = executor.execute("VM.classloaders show-classes"); + output.shouldContain("<bootstrap>"); + output.shouldContain("java.lang.Object"); + output.shouldContain("java.lang.Enum"); + output.shouldContain("java.lang.NullPointerException"); + output.shouldContain("TestClass2"); + } + + static class TestClassLoader extends ClassLoader { + + public TestClassLoader() { + super(); + } + + public TestClassLoader(ClassLoader parent) { + super(parent); + } + + public static final String CLASS_NAME = "TestClass2"; + + static ByteBuffer readClassFile(String name) + { + File f = new File(System.getProperty("test.classes", "."), + name); + try (FileInputStream fin = new FileInputStream(f); + FileChannel fc = fin.getChannel()) + { + return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + } catch (IOException e) { + Assert.fail("Can't open file: " + name, e); + } + + /* Will not reach here as Assert.fail() throws exception */ + return null; + } + + protected Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class<?> c; + if (!CLASS_NAME.equals(name)) { + c = super.loadClass(name, resolve); + } else { + // should not delegate to the system class loader + c = findClass(name); + if (resolve) { + resolveClass(c); + } + } + return c; + } + + protected Class<?> findClass(String name) + throws ClassNotFoundException + { + if (!CLASS_NAME.equals(name)) { + throw new ClassNotFoundException("Unexpected class: " + name); + } + return defineClass(name, readClassFile(name + ".class"), null); + } + + } + + @Test + public void jmx() throws ClassNotFoundException { + run(new JMXExecutor()); + } + +} + +class TestClass2 { + static { + Runnable r = () -> System.out.println("Hello"); + r.run(); + } +} \ No newline at end of file -- 1.8.3.1
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2