431 lines
16 KiB
Diff
431 lines
16 KiB
Diff
From c427ef7ceeea1fb8f8ebd035e59b6f06b5ec34c1 Mon Sep 17 00:00:00 2001
|
|
From: eapen <zhangyipeng7@huawei.com>
|
|
Date: Tue, 13 Dec 2022 21:06:41 +0800
|
|
Subject: [PATCH 15/33] I68TO2: 8275775: Add jcmd VM.classes to print details of all
|
|
classes
|
|
---
|
|
hotspot/src/share/vm/oops/instanceKlass.cpp | 56 ++++++++++++++++++++--
|
|
hotspot/src/share/vm/oops/instanceKlass.hpp | 17 ++++---
|
|
hotspot/src/share/vm/runtime/fieldDescriptor.cpp | 4 +-
|
|
hotspot/src/share/vm/runtime/fieldDescriptor.hpp | 4 +-
|
|
hotspot/src/share/vm/runtime/globals.hpp | 2 +-
|
|
hotspot/src/share/vm/runtime/vm_operations.hpp | 1 +
|
|
.../src/share/vm/services/diagnosticCommand.cpp | 53 ++++++++++++++++++++
|
|
.../src/share/vm/services/diagnosticCommand.hpp | 23 +++++++++
|
|
hotspot/test/runtime/CommandLine/PrintClasses.java | 51 ++++++++++++++++++++
|
|
9 files changed, 195 insertions(+), 16 deletions(-)
|
|
create mode 100644 hotspot/test/runtime/CommandLine/PrintClasses.java
|
|
|
|
diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp
|
|
index 2a9cd92..538645b 100644
|
|
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp
|
|
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp
|
|
@@ -1799,6 +1799,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->oop_is_instance()) {
|
|
+ _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->oop_is_instance()) {
|
|
+ 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_impl for jfieldIds only */
|
|
JNIid* InstanceKlass::jni_id_for_impl(instanceKlassHandle this_oop, int offset) {
|
|
MutexLocker ml(JfieldIdCreation_lock);
|
|
@@ -3244,7 +3290,6 @@ oop InstanceKlass::add_member_name(Handle mem_name, bool intern) {
|
|
// -----------------------------------------------------------------------------------------------------
|
|
// Printing
|
|
|
|
-#ifndef PRODUCT
|
|
|
|
#define BULLET " - "
|
|
|
|
@@ -3264,6 +3309,10 @@ static void print_vtable(intptr_t* start, int len, outputStream* 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);
|
|
@@ -3271,7 +3320,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: "); super()->print_value_on_maybe_null(st); st->cr();
|
|
st->print(BULLET"sub: ");
|
|
@@ -3380,7 +3429,6 @@ void InstanceKlass::print_on(outputStream* st) const {
|
|
st->cr();
|
|
}
|
|
|
|
-#endif //PRODUCT
|
|
|
|
void InstanceKlass::print_value_on(outputStream* st) const {
|
|
assert(is_klass(), "must be klass");
|
|
@@ -3388,7 +3436,6 @@ void InstanceKlass::print_value_on(outputStream* st) const {
|
|
name()->print_value_on(st);
|
|
}
|
|
|
|
-#ifndef PRODUCT
|
|
|
|
void FieldPrinter::do_field(fieldDescriptor* fd) {
|
|
_st->print(BULLET);
|
|
@@ -3449,7 +3496,6 @@ void InstanceKlass::oop_print_on(oop obj, outputStream* st) {
|
|
}
|
|
}
|
|
|
|
-#endif //PRODUCT
|
|
|
|
void InstanceKlass::oop_print_value_on(oop obj, outputStream* st) {
|
|
st->print("a ");
|
|
diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp
|
|
index 43919e8..6e36fa4 100644
|
|
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp
|
|
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp
|
|
@@ -99,7 +99,6 @@ public:
|
|
virtual void do_field(fieldDescriptor* fd) = 0;
|
|
};
|
|
|
|
-#ifndef PRODUCT
|
|
// Print fields.
|
|
// If "obj" argument to constructor is NULL, prints static fields, otherwise prints non-static fields.
|
|
class FieldPrinter: public FieldClosure {
|
|
@@ -109,7 +108,6 @@ class FieldPrinter: public FieldClosure {
|
|
FieldPrinter(outputStream* st, oop obj = NULL) : _obj(obj), _st(st) {}
|
|
void do_field(fieldDescriptor* fd);
|
|
};
|
|
-#endif // !PRODUCT
|
|
|
|
// ValueObjs embedded in klass. Describes where oops are located in instances of
|
|
// this klass.
|
|
@@ -462,6 +460,7 @@ class InstanceKlass: public Klass {
|
|
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; }
|
|
|
|
// defineClass specified verification
|
|
@@ -1174,16 +1173,13 @@ public:
|
|
|
|
public:
|
|
// Printing
|
|
-#ifndef PRODUCT
|
|
void print_on(outputStream* st) const;
|
|
-#endif
|
|
void print_value_on(outputStream* st) const;
|
|
|
|
void oop_print_value_on(oop obj, outputStream* st);
|
|
|
|
-#ifndef PRODUCT
|
|
void oop_print_on (oop obj, outputStream* st);
|
|
-
|
|
+#ifndef PRODUCT
|
|
void print_dependent_nmethods(bool verbose = false);
|
|
bool is_dependent_nmethod(nmethod* nm);
|
|
#endif
|
|
@@ -1217,6 +1213,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> {
|
|
diff --git a/hotspot/src/share/vm/runtime/fieldDescriptor.cpp b/hotspot/src/share/vm/runtime/fieldDescriptor.cpp
|
|
index 610402d..288e82d 100644
|
|
--- a/hotspot/src/share/vm/runtime/fieldDescriptor.cpp
|
|
+++ b/hotspot/src/share/vm/runtime/fieldDescriptor.cpp
|
|
@@ -123,6 +123,8 @@ void fieldDescriptor::verify() const {
|
|
}
|
|
}
|
|
|
|
+#endif /* PRODUCT */
|
|
+
|
|
void fieldDescriptor::print_on(outputStream* st) const {
|
|
access_flags().print_on(st);
|
|
name()->print_value_on(st);
|
|
@@ -206,5 +208,3 @@ void fieldDescriptor::print_on_for(outputStream* st, oop obj) {
|
|
st->print(" (%x)", as_int);
|
|
}
|
|
}
|
|
-
|
|
-#endif /* PRODUCT */
|
|
diff --git a/hotspot/src/share/vm/runtime/fieldDescriptor.hpp b/hotspot/src/share/vm/runtime/fieldDescriptor.hpp
|
|
index 1810a16..f7e9a26 100644
|
|
--- a/hotspot/src/share/vm/runtime/fieldDescriptor.hpp
|
|
+++ b/hotspot/src/share/vm/runtime/fieldDescriptor.hpp
|
|
@@ -129,8 +129,8 @@ class fieldDescriptor VALUE_OBJ_CLASS_SPEC {
|
|
|
|
// Print
|
|
void print() { print_on(tty); }
|
|
- void print_on(outputStream* st) const PRODUCT_RETURN;
|
|
- void print_on_for(outputStream* st, oop obj) PRODUCT_RETURN;
|
|
+ void print_on(outputStream* st) const;
|
|
+ void print_on_for(outputStream* st, oop obj);
|
|
void verify() const PRODUCT_RETURN;
|
|
};
|
|
|
|
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
|
|
index ec48c48..41b1392 100644
|
|
--- a/hotspot/src/share/vm/runtime/globals.hpp
|
|
+++ b/hotspot/src/share/vm/runtime/globals.hpp
|
|
@@ -3097,7 +3097,7 @@ class CommandLineFlags {
|
|
notproduct(intx, MaxElementPrintSize, 256, \
|
|
"maximum number of elements to print") \
|
|
\
|
|
- notproduct(intx, MaxSubklassPrintSize, 4, \
|
|
+ product(intx, MaxSubklassPrintSize, 4, \
|
|
"maximum number of subklasses to print when printing klass") \
|
|
\
|
|
product(intx, MaxInlineLevel, 9, \
|
|
diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp
|
|
index 8c6795a..a8ba78b 100644
|
|
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp
|
|
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp
|
|
@@ -99,6 +99,7 @@
|
|
template(WhiteBoxOperation) \
|
|
template(ClassLoaderStatsOperation) \
|
|
template(JFROldObject) \
|
|
+ template(PrintClasses) \
|
|
|
|
class VM_Operation: public CHeapObj<mtInternal> {
|
|
public:
|
|
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
|
index 60417b5..e4e6185 100644
|
|
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
|
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
|
@@ -64,6 +64,7 @@ void DCmdRegistrant::register_dcmds(){
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<DynamicCDSDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
|
|
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassesDCmd>(full_export, true, false));
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false));
|
|
#endif // INCLUDE_SERVICES
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false));
|
|
@@ -98,9 +99,14 @@ HelpDCmd::HelpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, hea
|
|
_dcmdparser.add_dcmd_argument(&_cmd);
|
|
};
|
|
|
|
+static int compare_strings(const char** s1, const char** s2) {
|
|
+ return ::strcmp(*s1, *s2);
|
|
+}
|
|
+
|
|
void HelpDCmd::execute(DCmdSource source, TRAPS) {
|
|
if (_all.value()) {
|
|
GrowableArray<const char*>* cmd_list = DCmdFactory::DCmd_list(source);
|
|
+ cmd_list->sort(compare_strings);
|
|
for (int i = 0; i < cmd_list->length(); i++) {
|
|
DCmdFactory* factory = DCmdFactory::factory(source, cmd_list->at(i),
|
|
strlen(cmd_list->at(i)));
|
|
@@ -141,6 +147,7 @@ void HelpDCmd::execute(DCmdSource source, TRAPS) {
|
|
} else {
|
|
output()->print_cr("The following commands are available:");
|
|
GrowableArray<const char *>* cmd_list = DCmdFactory::DCmd_list(source);
|
|
+ cmd_list->sort(compare_strings);
|
|
for (int i = 0; i < cmd_list->length(); i++) {
|
|
DCmdFactory* factory = DCmdFactory::factory(source, cmd_list->at(i),
|
|
strlen(cmd_list->at(i)));
|
|
@@ -419,6 +426,52 @@ int ClassHistogramDCmd::num_arguments() {
|
|
}
|
|
}
|
|
|
|
+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);
|
|
+}
|
|
+
|
|
+int ClassesDCmd::num_arguments() {
|
|
+ ResourceMark rm;
|
|
+ ClassesDCmd* dcmd = new ClassesDCmd(NULL, false);
|
|
+ if (dcmd != NULL) {
|
|
+ DCmdMark mark(dcmd);
|
|
+ return dcmd->_dcmdparser.num_arguments();
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
#define DEFAULT_COLUMNS "InstBytes,KlassBytes,CpAll,annotations,MethodCount,Bytecodes,MethodAll,ROAll,RWAll,Total"
|
|
ClassStatsDCmd::ClassStatsDCmd(outputStream* output, bool heap) :
|
|
DCmdWithParser(output, heap),
|
|
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
|
index e28011f..f86ab5f 100644
|
|
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
|
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
|
@@ -314,6 +314,29 @@ 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;
|
|
+ }
|
|
+ static int num_arguments();
|
|
+ virtual void execute(DCmdSource source, TRAPS);
|
|
+};
|
|
+
|
|
class ClassStatsDCmd : public DCmdWithParser {
|
|
protected:
|
|
DCmdArgument<bool> _all;
|
|
diff --git a/hotspot/test/runtime/CommandLine/PrintClasses.java b/hotspot/test/runtime/CommandLine/PrintClasses.java
|
|
new file mode 100644
|
|
index 0000000..7c1d4db
|
|
--- /dev/null
|
|
+++ b/hotspot/test/runtime/CommandLine/PrintClasses.java
|
|
@@ -0,0 +1,51 @@
|
|
+/*
|
|
+ * Copyright (c) 2022, Alibaba Group Holding Limited. All rights reserved.
|
|
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 /testlibrary
|
|
+ * @run main/othervm PrintClasses
|
|
+ */
|
|
+
|
|
+import com.oracle.java.testlibrary.*;
|
|
+
|
|
+public class PrintClasses {
|
|
+ public static void main(String args[]) throws Exception {
|
|
+ String pid = Integer.toString(ProcessTools.getProcessId());
|
|
+ ProcessBuilder pb = new ProcessBuilder();
|
|
+
|
|
+ pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.classes"});
|
|
+ OutputAnalyzer 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());
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
--
|
|
1.8.3.1
|