8275775: Add jcmd VM.classes to print details of all classes

Reviewed-by: dholmes, iklam, stuefe
This commit is contained in:
Yi Yang 2022-03-08 03:14:21 +00:00
parent cde923dd47
commit 3f0684d0b8
6 changed files with 172 additions and 1 deletions

View File

@ -2074,6 +2074,52 @@ Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name,
return NULL;
}
PrintClassClosure::PrintClassClosure(outputStream* st, bool verbose)
:_st(st), _verbose(verbose) {
ResourceMark rm;
_st->print("%-18s ", "KlassAddr");
_st->print("%-4s ", "Size");
_st->print("%-20s ", "State");
_st->print("%-7s ", "Flags");
_st->print("%-5s ", "ClassName");
_st->cr();
}
void PrintClassClosure::do_klass(Klass* k) {
ResourceMark rm;
// klass pointer
_st->print(INTPTR_FORMAT " ", p2i(k));
// klass size
_st->print("%4d ", k->size());
// initialization state
if (k->is_instance_klass()) {
_st->print("%-20s ",InstanceKlass::cast(k)->init_state_name());
} else {
_st->print("%-20s ","");
}
// misc flags(Changes should synced with ClassesDCmd::ClassesDCmd help doc)
char buf[10];
int i = 0;
if (k->has_finalizer()) buf[i++] = 'F';
if (k->has_final_method()) buf[i++] = 'f';
if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->is_rewritten()) buf[i++] = 'W';
if (ik->is_contended()) buf[i++] = 'C';
if (ik->has_been_redefined()) buf[i++] = 'R';
if (ik->is_shared()) buf[i++] = 'S';
}
buf[i++] = '\0';
_st->print("%-7s ", buf);
// klass name
_st->print("%-5s ", k->external_name());
// end
_st->cr();
if (_verbose) {
k->print_on(_st);
}
}
/* jni_id_for for jfieldIds only */
JNIid* InstanceKlass::jni_id_for(int offset) {
MutexLocker ml(JfieldIdCreation_lock);
@ -3393,6 +3439,10 @@ static void print_vtable(vtableEntry* start, int len, outputStream* st) {
return print_vtable(reinterpret_cast<intptr_t*>(start), len, st);
}
const char* InstanceKlass::init_state_name() const {
return state_names[_init_state];
}
void InstanceKlass::print_on(outputStream* st) const {
assert(is_klass(), "must be klass");
Klass::print_on(st);
@ -3400,7 +3450,7 @@ void InstanceKlass::print_on(outputStream* st) const {
st->print(BULLET"instance size: %d", size_helper()); st->cr();
st->print(BULLET"klass size: %d", size()); st->cr();
st->print(BULLET"access: "); access_flags().print_on(st); st->cr();
st->print(BULLET"state: "); st->print_cr("%s", state_names[_init_state]);
st->print(BULLET"state: "); st->print_cr("%s", init_state_name());
st->print(BULLET"name: "); name()->print_value_on(st); st->cr();
st->print(BULLET"super: "); Metadata::print_value_on_maybe_null(st, super()); st->cr();
st->print(BULLET"sub: ");

View File

@ -547,6 +547,7 @@ public:
bool is_in_error_state() const { return _init_state == initialization_error; }
bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; }
ClassState init_state() { return (ClassState)_init_state; }
const char* init_state_name() const;
bool is_rewritten() const { return (_misc_flags & _misc_rewritten) != 0; }
// is this a sealed class
@ -1277,6 +1278,15 @@ inline u2 InstanceKlass::next_method_idnum() {
}
}
class PrintClassClosure : public KlassClosure {
private:
outputStream* _st;
bool _verbose;
public:
PrintClassClosure(outputStream* st, bool verbose);
void do_klass(Klass* k);
};
/* JNIid class for jfieldIDs only */
class JNIid: public CHeapObj<mtClass> {

View File

@ -95,6 +95,7 @@
template(CleanClassLoaderDataMetaspaces) \
template(PrintCompileQueue) \
template(PrintClassHierarchy) \
template(PrintClasses) \
template(ICBufferFull) \
template(PrintMetadata) \
template(GTestExecuteAtSafepoint) \

View File

@ -26,6 +26,7 @@
#include "jvm.h"
#include "classfile/classLoaderHierarchyDCmd.hpp"
#include "classfile/classLoaderStats.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
@ -102,6 +103,7 @@ void DCmdRegistrant::register_dcmds(){
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemDictionaryDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHierarchyDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassesDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SymboltableDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<StringtableDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<metaspace::MetaspaceDCmd>(full_export, true, false));
@ -954,6 +956,41 @@ void TouchedMethodsDCmd::execute(DCmdSource source, TRAPS) {
VMThread::execute(&dumper);
}
ClassesDCmd::ClassesDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),
_verbose("-verbose",
"Dump the detailed content of a Java class. "
"Some classes are annotated with flags: "
"F = has, or inherits, a non-empty finalize method, "
"f = has final method, "
"W = methods rewritten, "
"C = marked with @Contended annotation, "
"R = has been redefined, "
"S = is shared class",
"BOOLEAN", false, "false") {
_dcmdparser.add_dcmd_option(&_verbose);
}
class VM_PrintClasses : public VM_Operation {
private:
outputStream* _out;
bool _verbose;
public:
VM_PrintClasses(outputStream* out, bool verbose) : _out(out), _verbose(verbose) {}
virtual VMOp_Type type() const { return VMOp_PrintClasses; }
virtual void doit() {
PrintClassClosure closure(_out, _verbose);
ClassLoaderDataGraph::classes_do(&closure);
}
};
void ClassesDCmd::execute(DCmdSource source, TRAPS) {
VM_PrintClasses vmop(output(), _verbose.is_set());
VMThread::execute(&vmop);
}
#if INCLUDE_CDS
DumpSharedArchiveDCmd::DumpSharedArchiveDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),

View File

@ -858,6 +858,28 @@ public:
virtual void execute(DCmdSource source, TRAPS);
};
class ClassesDCmd : public DCmdWithParser {
protected:
DCmdArgument<bool> _verbose;
public:
ClassesDCmd(outputStream* output, bool heap);
static const char* name() {
return "VM.classes";
}
static const char* description() {
return "Print all loaded classes";
}
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;
}
virtual void execute(DCmdSource source, TRAPS);
};
#if INCLUDE_JVMTI
class DebugOnCmdStartDCmd : public DCmd {
public:

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2022, Alibaba Group Holding Limited. 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
* @bug 8275775
* @summary Test jcmd VM.classes
* @library /test/lib
* @run main/othervm PrintClasses
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.JDKToolFinder;
public class PrintClasses {
public static void main(String args[]) throws Exception {
var pid = Long.toString(ProcessHandle.current().pid());
var pb = new ProcessBuilder();
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.classes"});
var output = new OutputAnalyzer(pb.start());
output.shouldNotContain("instance size");
output.shouldContain(PrintClasses.class.getSimpleName());
pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.classes", "-verbose"});
output = new OutputAnalyzer(pb.start());
output.shouldContain("instance size");
output.shouldContain(PrintClasses.class.getSimpleName());
}
}