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
