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:
parent
3d3f6553c0
commit
0b5dffb337
@ -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();
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
199
hotspot/test/serviceability/dcmd/vm/ClassHierarchyTest.java
Normal file
199
hotspot/test/serviceability/dcmd/vm/ClassHierarchyTest.java
Normal 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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user