8054888: Runtime: Add Diagnostic Command that prints the class hierarchy

Added the VM.class_hierarhcy DCMD

Reviewed-by: stefank, miauno
This commit is contained in:
Chris Plummer 2015-02-11 15:22:43 -08:00
parent 3d3f6553c0
commit 0b5dffb337
8 changed files with 524 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. 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
@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/systemDictionary.hpp"
#include "gc_interface/collectedHeap.hpp"
#include "memory/genCollectedHeap.hpp"
#include "memory/heapInspection.hpp"
@ -39,6 +40,19 @@ PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
// HeapInspection
inline KlassInfoEntry::~KlassInfoEntry() {
if (_subclasses != NULL) {
delete _subclasses;
}
}
inline void KlassInfoEntry::add_subclass(KlassInfoEntry* cie) {
if (_subclasses == NULL) {
_subclasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<KlassInfoEntry*>(4, true);
}
_subclasses->append(cie);
}
int KlassInfoEntry::compare(KlassInfoEntry* e1, KlassInfoEntry* e2) {
if(e1->_instance_words > e2->_instance_words) {
return -1;
@ -129,7 +143,7 @@ void KlassInfoTable::AllClassesFinder::do_klass(Klass* k) {
_table->lookup(k);
}
KlassInfoTable::KlassInfoTable(bool need_class_stats) {
KlassInfoTable::KlassInfoTable(bool add_all_classes) {
_size_of_instances_in_words = 0;
_size = 0;
_ref = (HeapWord*) Universe::boolArrayKlassObj();
@ -141,7 +155,7 @@ KlassInfoTable::KlassInfoTable(bool need_class_stats) {
for (int index = 0; index < _size; index++) {
_buckets[index].initialize();
}
if (need_class_stats) {
if (add_all_classes) {
AllClassesFinder finder(this);
ClassLoaderDataGraph::classes_do(&finder);
}
@ -299,6 +313,191 @@ PRAGMA_DIAG_POP
st->cr();
}
class HierarchyClosure : public KlassInfoClosure {
private:
GrowableArray<KlassInfoEntry*> *_elements;
public:
HierarchyClosure(GrowableArray<KlassInfoEntry*> *_elements) : _elements(_elements) {}
void do_cinfo(KlassInfoEntry* cie) {
// ignore array classes
if (cie->klass()->oop_is_instance()) {
_elements->append(cie);
}
}
};
void KlassHierarchy::print_class_hierarchy(outputStream* st, bool print_interfaces,
bool print_subclasses, char* classname) {
ResourceMark rm;
Stack <KlassInfoEntry*, mtClass> class_stack;
GrowableArray<KlassInfoEntry*> elements;
// Add all classes to the KlassInfoTable, which allows for quick lookup.
// A KlassInfoEntry will be created for each class.
KlassInfoTable cit(true);
if (cit.allocation_failed()) {
st->print_cr("ERROR: Ran out of C-heap; hierarchy not generated");
return;
}
// Add all created KlassInfoEntry instances to the elements array for easy
// iteration, and to allow each KlassInfoEntry instance to have a unique index.
HierarchyClosure hc(&elements);
cit.iterate(&hc);
for(int i = 0; i < elements.length(); i++) {
KlassInfoEntry* cie = elements.at(i);
const InstanceKlass* k = (InstanceKlass*)cie->klass();
Klass* super = ((InstanceKlass*)k)->java_super();
// Set the index for the class.
cie->set_index(i + 1);
// Add the class to the subclass array of its superclass.
if (super != NULL) {
KlassInfoEntry* super_cie = cit.lookup(super);
assert(super_cie != NULL, "could not lookup superclass");
super_cie->add_subclass(cie);
}
}
// Set the do_print flag for each class that should be printed.
for(int i = 0; i < elements.length(); i++) {
KlassInfoEntry* cie = elements.at(i);
if (classname == NULL) {
// We are printing all classes.
cie->set_do_print(true);
} else {
// We are only printing the hierarchy of a specific class.
if (strcmp(classname, cie->klass()->external_name()) == 0) {
KlassHierarchy::set_do_print_for_class_hierarchy(cie, &cit, print_subclasses);
}
}
}
// Now we do a depth first traversal of the class hierachry. The class_stack will
// maintain the list of classes we still need to process. Start things off
// by priming it with java.lang.Object.
KlassInfoEntry* jlo_cie = cit.lookup(SystemDictionary::Object_klass());
assert(jlo_cie != NULL, "could not lookup java.lang.Object");
class_stack.push(jlo_cie);
// Repeatedly pop the top item off the stack, print its class info,
// and push all of its subclasses on to the stack. Do this until there
// are no classes left on the stack.
while (!class_stack.is_empty()) {
KlassInfoEntry* curr_cie = class_stack.pop();
if (curr_cie->do_print()) {
print_class(st, curr_cie, print_interfaces);
if (curr_cie->subclasses() != NULL) {
// Current class has subclasses, so push all of them onto the stack.
for (int i = 0; i < curr_cie->subclasses()->length(); i++) {
KlassInfoEntry* cie = curr_cie->subclasses()->at(i);
if (cie->do_print()) {
class_stack.push(cie);
}
}
}
}
}
st->flush();
}
// Sets the do_print flag for every superclass and subclass of the specified class.
void KlassHierarchy::set_do_print_for_class_hierarchy(KlassInfoEntry* cie, KlassInfoTable* cit,
bool print_subclasses) {
// Set do_print for all superclasses of this class.
Klass* super = ((InstanceKlass*)cie->klass())->java_super();
while (super != NULL) {
KlassInfoEntry* super_cie = cit->lookup(super);
super_cie->set_do_print(true);
super = super->super();
}
// Set do_print for this class and all of its subclasses.
Stack <KlassInfoEntry*, mtClass> class_stack;
class_stack.push(cie);
while (!class_stack.is_empty()) {
KlassInfoEntry* curr_cie = class_stack.pop();
curr_cie->set_do_print(true);
if (print_subclasses && curr_cie->subclasses() != NULL) {
// Current class has subclasses, so push all of them onto the stack.
for (int i = 0; i < curr_cie->subclasses()->length(); i++) {
KlassInfoEntry* cie = curr_cie->subclasses()->at(i);
class_stack.push(cie);
}
}
}
}
static void print_indent(outputStream* st, int indent) {
while (indent != 0) {
st->print("|");
indent--;
if (indent != 0) {
st->print(" ");
}
}
}
// Print the class name and its unique ClassLoader identifer.
static void print_classname(outputStream* st, Klass* klass) {
oop loader_oop = klass->class_loader_data()->class_loader();
st->print("%s/", klass->external_name());
if (loader_oop == NULL) {
st->print("null");
} else {
st->print(INTPTR_FORMAT, klass->class_loader_data());
}
}
static void print_interface(outputStream* st, Klass* intf_klass, const char* intf_type, int indent) {
print_indent(st, indent);
st->print(" implements ");
print_classname(st, intf_klass);
st->print(" (%s intf)\n", intf_type);
}
void KlassHierarchy::print_class(outputStream* st, KlassInfoEntry* cie, bool print_interfaces) {
ResourceMark rm;
InstanceKlass* klass = (InstanceKlass*)cie->klass();
int indent = 0;
// Print indentation with proper indicators of superclass.
Klass* super = klass->super();
while (super != NULL) {
super = super->super();
indent++;
}
print_indent(st, indent);
if (indent != 0) st->print("--");
// Print the class name, its unique ClassLoader identifer, and if it is an interface.
print_classname(st, klass);
if (klass->is_interface()) {
st->print(" (intf)");
}
st->print("\n");
// Print any interfaces the class has.
if (print_interfaces) {
Array<Klass*>* local_intfs = klass->local_interfaces();
Array<Klass*>* trans_intfs = klass->transitive_interfaces();
for (int i = 0; i < local_intfs->length(); i++) {
print_interface(st, local_intfs->at(i), "declared", indent);
}
for (int i = 0; i < trans_intfs->length(); i++) {
Klass* trans_interface = trans_intfs->at(i);
// Only print transitive interfaces if they are not also declared.
if (!local_intfs->contains(trans_interface)) {
print_interface(st, trans_interface, "inherited", indent);
}
}
}
}
void KlassInfoHisto::print_class_stats(outputStream* st,
bool csv_format, const char *columns) {
ResourceMark rm;
@ -320,6 +519,8 @@ void KlassInfoHisto::print_class_stats(outputStream* st,
elements()->at(i)->set_index(i+1);
}
// First iteration is for accumulating stats totals in colsum_table[].
// Second iteration is for printing stats for each class.
for (int pass=1; pass<=2; pass++) {
if (pass == 2) {
print_title(st, csv_format, selected, width_table, name_table);
@ -328,6 +529,7 @@ void KlassInfoHisto::print_class_stats(outputStream* st,
KlassInfoEntry* e = (KlassInfoEntry*)elements()->at(i);
const Klass* k = e->klass();
// Get the stats for this class.
memset(&sz, 0, sizeof(sz));
sz._inst_count = e->count();
sz._inst_bytes = HeapWordSize * e->words();
@ -335,11 +537,13 @@ void KlassInfoHisto::print_class_stats(outputStream* st,
sz._total_bytes = sz._ro_bytes + sz._rw_bytes;
if (pass == 1) {
// Add the stats for this class to the overall totals.
for (int c=0; c<KlassSizeStats::_num_columns; c++) {
colsum_table[c] += col_table[c];
}
} else {
int super_index = -1;
// Print the stats for this class.
if (k->oop_is_instance()) {
Klass* super = ((InstanceKlass*)k)->java_super();
if (super) {
@ -373,6 +577,8 @@ void KlassInfoHisto::print_class_stats(outputStream* st,
}
if (pass == 1) {
// Calculate the minimum width needed for the column by accounting for the
// column header width and the width of the largest value in the column.
for (int c=0; c<KlassSizeStats::_num_columns; c++) {
width_table[c] = col_width(colsum_table[c], name_table[c]);
}
@ -381,6 +587,7 @@ void KlassInfoHisto::print_class_stats(outputStream* st,
sz_sum._inst_size = 0;
// Print the column totals.
if (csv_format) {
st->print(",");
for (int c=0; c<KlassSizeStats::_num_columns; c++) {
@ -514,6 +721,7 @@ void HeapInspection::heap_inspection(outputStream* st) {
KlassInfoTable cit(_print_class_stats);
if (!cit.allocation_failed()) {
// populate table with object allocation info
size_t missed_count = populate_table(&cit);
if (missed_count != 0) {
st->print_cr("WARNING: Ran out of C-heap; undercounted " SIZE_FORMAT
@ -533,7 +741,7 @@ void HeapInspection::heap_inspection(outputStream* st) {
histo.sort();
histo.print_histo_on(st, _print_class_stats, _csv_format, _columns);
} else {
st->print_cr("WARNING: Ran out of C-heap; histogram not generated");
st->print_cr("ERROR: Ran out of C-heap; histogram not generated");
}
st->flush();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. 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
@ -188,11 +188,15 @@ class KlassInfoEntry: public CHeapObj<mtInternal> {
long _instance_count;
size_t _instance_words;
long _index;
bool _do_print; // True if we should print this class when printing the class hierarchy.
GrowableArray<KlassInfoEntry*>* _subclasses;
public:
KlassInfoEntry(Klass* k, KlassInfoEntry* next) :
_klass(k), _instance_count(0), _instance_words(0), _next(next), _index(-1)
_klass(k), _instance_count(0), _instance_words(0), _next(next), _index(-1),
_do_print(false), _subclasses(NULL)
{}
~KlassInfoEntry();
KlassInfoEntry* next() const { return _next; }
bool is_equal(const Klass* k) { return k == _klass; }
Klass* klass() const { return _klass; }
@ -202,6 +206,10 @@ class KlassInfoEntry: public CHeapObj<mtInternal> {
void set_words(size_t wds) { _instance_words = wds; }
void set_index(long index) { _index = index; }
long index() const { return _index; }
GrowableArray<KlassInfoEntry*>* subclasses() const { return _subclasses; }
void add_subclass(KlassInfoEntry* cie);
void set_do_print(bool do_print) { _do_print = do_print; }
bool do_print() const { return _do_print; }
int compare(KlassInfoEntry* e1, KlassInfoEntry* e2);
void print_on(outputStream* st) const;
const char* name() const;
@ -248,7 +256,7 @@ class KlassInfoTable: public StackObj {
};
public:
KlassInfoTable(bool need_class_stats);
KlassInfoTable(bool add_all_classes);
~KlassInfoTable();
bool record_instance(const oop obj);
void iterate(KlassInfoClosure* cic);
@ -256,6 +264,18 @@ class KlassInfoTable: public StackObj {
size_t size_of_instances_in_words() const;
friend class KlassInfoHisto;
friend class KlassHierarchy;
};
class KlassHierarchy : AllStatic {
public:
static void print_class_hierarchy(outputStream* st, bool print_interfaces, bool print_subclasses,
char* classname);
private:
static void set_do_print_for_class_hierarchy(KlassInfoEntry* cie, KlassInfoTable* cit,
bool print_subclasse);
static void print_class(outputStream* st, KlassInfoEntry* cie, bool print_subclasses);
};
class KlassInfoHisto : public StackObj {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. 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
@ -29,6 +29,7 @@
#include "compiler/compileBroker.hpp"
#include "compiler/compilerOracle.hpp"
#include "gc_implementation/shared/isGCActiveMark.hpp"
#include "memory/heapInspection.hpp"
#include "memory/resourceArea.hpp"
#include "oops/symbol.hpp"
#include "runtime/arguments.hpp"
@ -486,3 +487,9 @@ void VM_PrintCodeList::doit() {
void VM_PrintCodeCache::doit() {
CodeCache::print_layout(_out);
}
#if INCLUDE_SERVICES
void VM_PrintClassHierarchy::doit() {
KlassHierarchy::print_class_hierarchy(_out, _print_interfaces, _print_subclasses, _classname);
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. 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
@ -105,6 +105,7 @@
template(PrintCompileQueue) \
template(PrintCodeList) \
template(PrintCodeCache) \
template(PrintClassHierarchy) \
class VM_Operation: public CHeapObj<mtInternal> {
public:
@ -457,5 +458,21 @@ class VM_PrintCodeCache: public VM_Operation {
void doit();
};
#if INCLUDE_SERVICES
class VM_PrintClassHierarchy: public VM_Operation {
private:
outputStream* _out;
bool _print_interfaces;
bool _print_subclasses;
char* _classname;
public:
VM_PrintClassHierarchy(outputStream* st, bool print_interfaces, bool print_subclasses, char* classname) :
_out(st), _print_interfaces(print_interfaces), _print_subclasses(print_subclasses),
_classname(classname) {}
VMOp_Type type() const { return VMOp_PrintClassHierarchy; }
void doit();
};
#endif // INCLUDE_SERVICES
#endif // SHARE_VM_RUNTIME_VM_OPERATIONS_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. 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
@ -57,6 +57,7 @@ void DCmdRegistrant::register_dcmds(){
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHierarchyDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SymboltableDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<StringtableDCmd>(full_export, true, false));
#endif // INCLUDE_SERVICES
@ -695,3 +696,35 @@ void CodeCacheDCmd::execute(DCmdSource source, TRAPS) {
VMThread::execute(&printCodeCacheOp);
}
#if INCLUDE_SERVICES
ClassHierarchyDCmd::ClassHierarchyDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),
_print_interfaces("-i", "Inherited interfaces should be printed.", "BOOLEAN", false, "false"),
_print_subclasses("-s", "If a classname is specified, print its subclasses. "
"Otherwise only its superclasses are printed.", "BOOLEAN", false, "false"),
_classname("classname", "Name of class whose hierarchy should be printed. "
"If not specified, all class hierarchies are printed.",
"STRING", false) {
_dcmdparser.add_dcmd_option(&_print_interfaces);
_dcmdparser.add_dcmd_option(&_print_subclasses);
_dcmdparser.add_dcmd_argument(&_classname);
}
void ClassHierarchyDCmd::execute(DCmdSource source, TRAPS) {
VM_PrintClassHierarchy printClassHierarchyOp(output(), _print_interfaces.value(),
_print_subclasses.value(), _classname.value());
VMThread::execute(&printClassHierarchyOp);
}
int ClassHierarchyDCmd::num_arguments() {
ResourceMark rm;
ClassHierarchyDCmd* dcmd = new ClassHierarchyDCmd(NULL, false);
if (dcmd != NULL) {
DCmdMark mark(dcmd);
return dcmd->_dcmdparser.num_arguments();
} else {
return 0;
}
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. 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
@ -271,6 +271,34 @@ public:
virtual void execute(DCmdSource source, TRAPS);
};
class ClassHierarchyDCmd : public DCmdWithParser {
protected:
DCmdArgument<bool> _print_interfaces; // true if inherited interfaces should be printed.
DCmdArgument<bool> _print_subclasses; // true if subclasses of the specified classname should be printed.
DCmdArgument<char*> _classname; // Optional single class name whose hierarchy should be printed.
public:
ClassHierarchyDCmd(outputStream* output, bool heap);
static const char* name() {
return "VM.class_hierarchy";
}
static const char* description() {
return "Print a list of all loaded classes, indented to show the class hiearchy. "
"The name of each class is followed by the ClassLoaderData* of its ClassLoader, "
"or \"null\" if loaded by the bootstrap class loader.";
}
static const char* impact() {
return "Medium: Depends on number of loaded classes.";
}
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission",
"monitor", NULL};
return p;
}
static int num_arguments();
virtual void execute(DCmdSource source, TRAPS);
};
// See also: thread_dump in attachListener.cpp
class ThreadDumpDCmd : public DCmdWithParser {
protected:

View File

@ -271,7 +271,7 @@ JTREG_BASIC_OPTIONS += -a
# Report details on all failed or error tests, times too
JTREG_BASIC_OPTIONS += -v:fail,error,time
# Retain all files for failing tests
JTREG_BASIC_OPTIONS += -retain:fail,error
JTREG_BASIC_OPTIONS += -retain:all
# Ignore tests are not run and completely silent about it
JTREG_IGNORE_OPTION = -ignore:quiet
JTREG_BASIC_OPTIONS += $(JTREG_IGNORE_OPTION)

View File

@ -0,0 +1,199 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. 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.class_hierarchy
* @library /testlibrary
* @build com.oracle.java.testlibrary.*
* @build com.oracle.java.testlibrary.dcmd.*
* @run testng ClassHierarchyTest
*/
import org.testng.annotations.Test;
import org.testng.Assert;
import com.oracle.java.testlibrary.OutputAnalyzer;
import com.oracle.java.testlibrary.dcmd.CommandExecutor;
import com.oracle.java.testlibrary.dcmd.JMXExecutor;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ClassHierarchyTest {
// $> jcmd DcmdTestClass VM.class_hierarchy DcmdTestClass | grep DcmdTestClass\$\$Lambda
// |--DcmdTestClass$$Lambda$1/4081552/0xa529fbb0
// > VM.class_hierarchy DcmdBaseClass
// java.lang.Object/null
// |--DcmdBaseClass/0xa4abcd48
// > VM.class_hierarchy DcmdBaseClass -s
// java.lang.Object/null
// |--DcmdBaseClass/0xa4abcd48
// | |--DcmdTestClass/0xa4abcd48
// > VM.class_hierarchy DcmdBaseClass -i -s
// java.lang.Object/null
// |--DcmdBaseClass/0xa4abcd48
// | implements Intf2/0xa4abcd48 (declared intf)
// | implements Intf1/0xa4abcd48 (inherited intf)
// | |--DcmdTestClass/0xa4abcd48
// | | implements Intf1/0xa4abcd48 (inherited intf)
// | | implements Intf2/0xa4abcd48 (inherited intf)
static Pattern expected_lambda_line =
Pattern.compile("\\|--DcmdTestClass\\$\\$Lambda.*");
static Pattern expected_lines[] = {
Pattern.compile("java.lang.Object/null"),
Pattern.compile("\\|--DcmdBaseClass/0x(\\p{XDigit}*)"),
Pattern.compile("\\| implements Intf2/0x(\\p{XDigit}*) \\(declared intf\\)"),
Pattern.compile("\\| implements Intf1/0x(\\p{XDigit}*) \\(inherited intf\\)"),
Pattern.compile("\\| \\|--DcmdTestClass/0x(\\p{XDigit}*)"),
Pattern.compile("\\| \\| implements Intf1/0x(\\p{XDigit}*) \\(inherited intf\\)"),
Pattern.compile("\\| \\| implements Intf2/0x(\\p{XDigit}*) \\(inherited intf\\)")
};
public void run(CommandExecutor executor) throws ClassNotFoundException {
OutputAnalyzer output;
Iterator<String> lines;
int i;
// Load our test class whose hierarchy we will print.
Class<?> c = Class.forName("DcmdTestClass");
// Verify the presence of the lamba anonymous class
output = executor.execute("VM.class_hierarchy");
lines = output.asLines().iterator();
Boolean foundMatch = false;
while (lines.hasNext()) {
String line = lines.next();
Matcher m = expected_lambda_line.matcher(line);
if (m.matches()) {
foundMatch = true;
break;
}
}
if (!foundMatch) {
Assert.fail("Failed to find lamda class");
}
// Verify the output for the simple hierachry of just DcmdBaseClass.
output = executor.execute("VM.class_hierarchy DcmdBaseClass");
lines = output.asLines().iterator();
i = 0;
while (lines.hasNext()) {
String line = lines.next();
Matcher m = expected_lines[i].matcher(line);
i++;
if (!m.matches()) {
Assert.fail("Failed to match line #" + i + ": " + line);
}
// Should only be two lines of output in this form.
if (i == 2) break;
}
if (lines.hasNext()) {
String line = lines.next();
Assert.fail("Unexpected dcmd output: " + line);
}
// Verify the output for the full hierarchy of DcmdBaseClass, but without interfaces.
output = executor.execute("VM.class_hierarchy DcmdBaseClass -s");
lines = output.asLines().iterator();
i = 0;
while (lines.hasNext()) {
String line = lines.next();
Matcher m = expected_lines[i].matcher(line);
i++;
if (!m.matches()) {
Assert.fail("Failed to match line #" + i + ": " + line);
}
// "implements" lines should not be in this output.
if (i == 2 || i == 4) i += 2;
}
if (lines.hasNext()) {
String line = lines.next();
Assert.fail("Unexpected dcmd output: " + line);
}
// Verify the output for the full hierarchy of DcmdBaseClass, including interfaces.
output = executor.execute("VM.class_hierarchy DcmdBaseClass -i -s");
lines = output.asLines().iterator();
i = 0;
String classLoaderAddr = null;
while (lines.hasNext()) {
String line = lines.next();
Matcher m = expected_lines[i].matcher(line);
i++;
if (!m.matches()) {
Assert.fail("Failed to match line #" + i + ": " + line);
}
if (i == 2) {
// Fetch the ClassLoader address, which should be the same in
// subsequent lines.
classLoaderAddr = m.group(1);
System.out.println(classLoaderAddr);
} else if (i > 2) {
if (!classLoaderAddr.equals(m.group(1))) {
Assert.fail("Classloader address didn't match on line #"
+ i + ": " + line);
}
}
if (i == expected_lines.length) break;
}
if (lines.hasNext()) {
String line = lines.next();
Assert.fail("Unexpected dcmd output: " + line);
}
}
@Test
public void jmx() throws ClassNotFoundException {
run(new JMXExecutor());
}
}
interface Intf1 {
}
interface Intf2 extends Intf1 {
}
class DcmdBaseClass implements Intf2 {
}
class DcmdTestClass extends DcmdBaseClass {
static {
// Force creation of anonymous class (for the lambdaform).
Runnable r = () -> System.out.println("Hello");
r.run();
}
}