From 528a3b6459e34532120f468ba9afb8833d516f5a Mon Sep 17 00:00:00 2001
From: eapen <zhangyipeng7@huawei.com>
Date: Thu, 15 Dec 2022 11:38:55 +0800
Subject: [PATCH 19/33] I68TO2: 8189688: NMT: Report per-class load metadata
 information
---
 hotspot/src/share/vm/memory/metaspace.cpp      | 322 ++++++++++++++++++++++++-
 hotspot/src/share/vm/memory/metaspace.hpp      |   4 +
 hotspot/src/share/vm/runtime/vm_operations.cpp |   4 +
 hotspot/src/share/vm/runtime/vm_operations.hpp |  12 +
 hotspot/src/share/vm/services/nmtDCmd.cpp      |  16 +-
 hotspot/src/share/vm/services/nmtDCmd.hpp      |   1 +
 6 files changed, 354 insertions(+), 5 deletions(-)

diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp
index 6c4654b..cf4a112 100644
--- a/hotspot/src/share/vm/memory/metaspace.cpp
+++ b/hotspot/src/share/vm/memory/metaspace.cpp
@@ -75,6 +75,22 @@ enum ChunkIndex {
   NumberOfInUseLists = 4
 };

+// Helper, returns a descriptive name for the given index.
+static const char* chunk_size_name(ChunkIndex index) {
+  switch (index) {
+    case SpecializedIndex:
+      return "specialized";
+    case SmallIndex:
+      return "small";
+    case MediumIndex:
+      return "medium";
+    case HumongousIndex:
+      return "humongous";
+    default:
+      return "Invalid index";
+  }
+}
+
 enum ChunkSizes {    // in words.
   ClassSpecializedChunk = 128,
   SpecializedChunk = 128,
@@ -89,6 +105,18 @@ static ChunkIndex next_chunk_index(ChunkIndex i) {
   return (ChunkIndex) (i+1);
 }

+static const char* scale_unit(size_t scale) {
+  switch(scale) {
+    case 1: return "BYTES";
+    case K: return "KB";
+    case M: return "MB";
+    case G: return "GB";
+    default:
+      ShouldNotReachHere();
+      return NULL;
+  }
+}
+
 volatile intptr_t MetaspaceGC::_capacity_until_GC = 0;
 uint MetaspaceGC::_shrink_factor = 0;
 bool MetaspaceGC::_should_concurrent_collect = false;
@@ -141,6 +169,18 @@ class ChunkManager : public CHeapObj<mtInternal> {
   }
   void verify_free_chunks_count();

+  struct ChunkManagerStatistics {
+    size_t num_by_type[NumberOfFreeLists];
+    size_t single_size_by_type[NumberOfFreeLists];
+    size_t total_size_by_type[NumberOfFreeLists];
+    size_t num_humongous_chunks;
+    size_t total_size_humongous_chunks;
+  };
+
+  void locked_get_statistics(ChunkManagerStatistics* stat) const;
+  void get_statistics(ChunkManagerStatistics* stat) const;
+  static void print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale);
+
  public:

   ChunkManager(size_t specialized_size, size_t small_size, size_t medium_size)
@@ -157,6 +197,9 @@ class ChunkManager : public CHeapObj<mtInternal> {
   // for special, small, medium, and humongous chunks.
   ChunkIndex list_index(size_t size);

+  // Map a given index to the chunk size.
+  size_t size_by_index(ChunkIndex index) const;
+
   // Remove the chunk from its freelist.  It is
   // expected to be on one of the _free_chunks[] lists.
   void remove_chunk(Metachunk* chunk);
@@ -249,6 +292,10 @@ class ChunkManager : public CHeapObj<mtInternal> {
   void locked_print_sum_free_chunks(outputStream* st);

   void print_on(outputStream* st) const;
+
+  // Prints composition for both non-class and (if available)
+  // class chunk manager.
+  static void print_all_chunkmanagers(outputStream* out, size_t scale = 1);
 };

 // Used to manage the free list of Metablocks (a block corresponds
@@ -1707,7 +1754,6 @@ bool Metadebug::test_metadata_failure() {
 #endif

 // ChunkManager methods
-
 size_t ChunkManager::free_chunks_total_words() {
   return _free_chunks_total;
 }
@@ -1729,6 +1775,12 @@ size_t ChunkManager::free_chunks_count() {
   return _free_chunks_count;
 }

+size_t ChunkManager::size_by_index(ChunkIndex index) const {
+  index_bounds_check(index);
+  assert(index != HumongousIndex, "Do not call for humongous chunks.");
+  return _free_chunks[index].size();
+}
+
 void ChunkManager::locked_verify_free_chunks_total() {
   assert_lock_strong(SpaceManager::expand_lock());
   assert(sum_free_chunks() == _free_chunks_total,
@@ -1918,7 +1970,83 @@ Metachunk* ChunkManager::chunk_freelist_allocate(size_t word_size) {

 void ChunkManager::print_on(outputStream* out) const {
   if (PrintFLSStatistics != 0) {
-    const_cast<ChunkManager *>(this)->humongous_dictionary()->report_statistics();
+    _humongous_dictionary.report_statistics();
+  }
+}
+
+void ChunkManager::locked_get_statistics(ChunkManagerStatistics* stat) const {
+  assert_lock_strong(SpaceManager::expand_lock());
+  for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
+    stat->num_by_type[i] = num_free_chunks(i);
+    stat->single_size_by_type[i] = size_by_index(i);
+    stat->total_size_by_type[i] = size_free_chunks_in_bytes(i);
+  }
+  stat->num_humongous_chunks = num_free_chunks(HumongousIndex);
+  stat->total_size_humongous_chunks = size_free_chunks_in_bytes(HumongousIndex);
+}
+
+void ChunkManager::get_statistics(ChunkManagerStatistics* stat) const {
+  MutexLockerEx cl(SpaceManager::expand_lock(),
+                   Mutex::_no_safepoint_check_flag);
+  locked_get_statistics(stat);
+}
+
+void ChunkManager::print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale) {
+  size_t total = 0;
+  assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale");
+
+  const char* unit = scale_unit(scale);
+  for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
+    out->print("  " SIZE_FORMAT " %s (" SIZE_FORMAT " bytes) chunks, total ",
+                   stat->num_by_type[i], chunk_size_name(i),
+                   stat->single_size_by_type[i]);
+    if (scale == 1) {
+      out->print_cr(SIZE_FORMAT " bytes", stat->total_size_by_type[i]);
+    } else {
+      out->print_cr("%.2f%s", (float)stat->total_size_by_type[i] / scale, unit);
+    }
+
+    total += stat->total_size_by_type[i];
+  }
+
+  total += stat->total_size_humongous_chunks;
+
+  if (scale == 1) {
+    out->print_cr("  " SIZE_FORMAT " humongous chunks, total " SIZE_FORMAT " bytes",
+    stat->num_humongous_chunks, stat->total_size_humongous_chunks);
+
+    out->print_cr("  total size: " SIZE_FORMAT " bytes.", total);
+  } else {
+    out->print_cr("  " SIZE_FORMAT " humongous chunks, total %.2f%s",
+    stat->num_humongous_chunks,
+    (float)stat->total_size_humongous_chunks / scale, unit);
+
+    out->print_cr("  total size: %.2f%s.", (float)total / scale, unit);
+  }
+
+}
+
+void ChunkManager::print_all_chunkmanagers(outputStream* out, size_t scale) {
+  assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale");
+
+  // Note: keep lock protection only to retrieving statistics; keep printing
+  // out of lock protection
+  ChunkManagerStatistics stat;
+  out->print_cr("Chunkmanager (non-class):");
+  const ChunkManager* const non_class_cm = Metaspace::chunk_manager_metadata();
+  if (non_class_cm != NULL) {
+    non_class_cm->get_statistics(&stat);
+    ChunkManager::print_statistics(&stat, out, scale);
+  } else {
+    out->print_cr("unavailable.");
+  }
+  out->print_cr("Chunkmanager (class):");
+  const ChunkManager* const class_cm = Metaspace::chunk_manager_class();
+  if (class_cm != NULL) {
+    class_cm->get_statistics(&stat);
+    ChunkManager::print_statistics(&stat, out, scale);
+  } else {
+    out->print_cr("unavailable.");
   }
 }

@@ -2930,6 +3058,195 @@ void MetaspaceAux::print_waste(outputStream* out) {
   }
 }

+class MetadataStats VALUE_OBJ_CLASS_SPEC {
+private:
+  size_t _capacity;
+  size_t _used;
+  size_t _free;
+  size_t _waste;
+
+public:
+  MetadataStats() : _capacity(0), _used(0), _free(0), _waste(0) { }
+  MetadataStats(size_t capacity, size_t used, size_t free, size_t waste)
+  : _capacity(capacity), _used(used), _free(free), _waste(waste) { }
+
+  void add(const MetadataStats& stats) {
+    _capacity += stats.capacity();
+    _used += stats.used();
+    _free += stats.free();
+    _waste += stats.waste();
+  }
+
+  size_t capacity() const { return _capacity; }
+  size_t used() const     { return _used; }
+  size_t free() const     { return _free; }
+  size_t waste() const    { return _waste; }
+
+  void print_on(outputStream* out, size_t scale) const;
+};
+
+
+void MetadataStats::print_on(outputStream* out, size_t scale) const {
+  const char* unit = scale_unit(scale);
+  out->print_cr("capacity=%10.2f%s used=%10.2f%s free=%10.2f%s waste=%10.2f%s",
+    (float)capacity() / scale, unit,
+    (float)used() / scale, unit,
+    (float)free() / scale, unit,
+    (float)waste() / scale, unit);
+}
+
+class PrintCLDMetaspaceInfoClosure : public CLDClosure {
+private:
+  outputStream*  _out;
+  size_t         _scale;
+
+  size_t         _total_count;
+  MetadataStats  _total_metadata;
+  MetadataStats  _total_class;
+
+  size_t         _total_anon_count;
+  MetadataStats  _total_anon_metadata;
+  MetadataStats  _total_anon_class;
+
+public:
+  PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale = K)
+  : _out(out), _scale(scale), _total_count(0), _total_anon_count(0) { }
+
+  ~PrintCLDMetaspaceInfoClosure() {
+    print_summary();
+  }
+
+  void do_cld(ClassLoaderData* cld) {
+    assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
+
+    if (cld->is_unloading()) return;
+    Metaspace* msp = cld->metaspace_or_null();
+    if (msp == NULL) {
+      return;
+    }
+
+    bool anonymous = false;
+    if (cld->is_anonymous()) {
+      _out->print_cr("ClassLoader: for anonymous class");
+      anonymous = true;
+    } else {
+      ResourceMark rm;
+      _out->print_cr("ClassLoader: %s", cld->loader_name());
+    }
+
+    print_metaspace(msp, anonymous);
+    _out->cr();
+  }
+
+private:
+  void print_metaspace(Metaspace* msp, bool anonymous);
+  void print_summary() const;
+};
+
+void PrintCLDMetaspaceInfoClosure::print_metaspace(Metaspace* msp, bool anonymous){
+  assert(msp != NULL, "Sanity");
+  SpaceManager* vsm = msp->vsm();
+  const char* unit = scale_unit(_scale);
+
+  size_t capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord;
+  size_t used = vsm->sum_used_in_chunks_in_use() * BytesPerWord;
+  size_t free = vsm->sum_free_in_chunks_in_use() * BytesPerWord;
+  size_t waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord;
+
+  _total_count ++;
+  MetadataStats metadata_stats(capacity, used, free, waste);
+  _total_metadata.add(metadata_stats);
+
+  if (anonymous) {
+    _total_anon_count ++;
+    _total_anon_metadata.add(metadata_stats);
+  }
+
+  _out->print("  Metadata   ");
+  metadata_stats.print_on(_out, _scale);
+
+  if (Metaspace::using_class_space()) {
+    vsm = msp->class_vsm();
+
+    capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord;
+    used = vsm->sum_used_in_chunks_in_use() * BytesPerWord;
+    free = vsm->sum_free_in_chunks_in_use() * BytesPerWord;
+    waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord;
+
+    MetadataStats class_stats(capacity, used, free, waste);
+    _total_class.add(class_stats);
+
+    if (anonymous) {
+      _total_anon_class.add(class_stats);
+    }
+
+    _out->print("  Class data ");
+    class_stats.print_on(_out, _scale);
+  }
+}
+
+void PrintCLDMetaspaceInfoClosure::print_summary() const {
+  const char* unit = scale_unit(_scale);
+  _out->cr();
+  _out->print_cr("Summary:");
+
+  MetadataStats total;
+  total.add(_total_metadata);
+  total.add(_total_class);
+
+  _out->print("  Total class loaders=" SIZE_FORMAT_W(6) " ", _total_count);
+  total.print_on(_out, _scale);
+
+  _out->print("                    Metadata ");
+  _total_metadata.print_on(_out, _scale);
+
+  if (Metaspace::using_class_space()) {
+    _out->print("                  Class data ");
+    _total_class.print_on(_out, _scale);
+  }
+  _out->cr();
+
+  MetadataStats total_anon;
+  total_anon.add(_total_anon_metadata);
+  total_anon.add(_total_anon_class);
+
+  _out->print("For anonymous classes=" SIZE_FORMAT_W(6) " ", _total_anon_count);
+  total_anon.print_on(_out, _scale);
+
+  _out->print("                    Metadata ");
+  _total_anon_metadata.print_on(_out, _scale);
+
+  if (Metaspace::using_class_space()) {
+    _out->print("                  Class data ");
+    _total_anon_class.print_on(_out, _scale);
+  }
+}
+
+void MetaspaceAux::print_metadata_for_nmt(outputStream* out, size_t scale) {
+  const char* unit = scale_unit(scale);
+  out->print_cr("Metaspaces:");
+  out->print_cr("  Metadata space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s",
+    reserved_bytes(Metaspace::NonClassType) / scale, unit,
+    committed_bytes(Metaspace::NonClassType) / scale, unit);
+  if (Metaspace::using_class_space()) {
+    out->print_cr("  Class    space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s",
+    reserved_bytes(Metaspace::ClassType) / scale, unit,
+    committed_bytes(Metaspace::ClassType) / scale, unit);
+  }
+
+  out->cr();
+  ChunkManager::print_all_chunkmanagers(out, scale);
+
+  out->cr();
+  out->print_cr("Per-classloader metadata:");
+  out->cr();
+
+  PrintCLDMetaspaceInfoClosure cl(out, scale);
+  ClassLoaderDataGraph::cld_do(&cl);
+}
+
+
+
 // Dump global metaspace things from the end of ClassLoaderDataGraph
 void MetaspaceAux::dump(outputStream* out) {
   out->print_cr("All Metaspace:");
@@ -3743,6 +4060,7 @@ void Metaspace::report_metadata_oome(ClassLoaderData* loader_data, size_t word_s
       loader_data->dump(gclog_or_tty);
     }
     MetaspaceAux::dump(gclog_or_tty);
+    ChunkManager::print_all_chunkmanagers(gclog_or_tty);
   }

   bool out_of_compressed_class_space = false;
diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp
index 122dd4b..ff1b232 100644
--- a/hotspot/src/share/vm/memory/metaspace.hpp
+++ b/hotspot/src/share/vm/memory/metaspace.hpp
@@ -65,6 +65,7 @@ class MetaspaceTracer;
 class MetaWord;
 class Mutex;
 class outputStream;
+class PrintCLDMetaspaceInfoClosure;
 class SpaceManager;
 class VirtualSpaceList;

@@ -88,6 +89,7 @@ class Metaspace : public CHeapObj<mtClass> {
   friend class VM_CollectForMetadataAllocation;
   friend class MetaspaceGC;
   friend class MetaspaceAux;
+  friend class PrintCLDMetaspaceInfoClosure;

  public:
   enum MetadataType {
@@ -372,6 +374,8 @@ class MetaspaceAux : AllStatic {
     return min_chunk_size_words() * BytesPerWord;
   }

+  static void print_metadata_for_nmt(outputStream* out, size_t scale = K);
+
   static bool has_chunk_free_list(Metaspace::MetadataType mdtype);
   static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype);

diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp
index d401ea6..b42d18f 100644
--- a/hotspot/src/share/vm/runtime/vm_operations.cpp
+++ b/hotspot/src/share/vm/runtime/vm_operations.cpp
@@ -209,6 +209,10 @@ void VM_PrintJNI::doit() {
   JNIHandles::print_on(_out);
 }

+void VM_PrintMetadata::doit() {
+  MetaspaceAux::print_metadata_for_nmt(_out, _scale);
+}
+
 VM_FindDeadlocks::~VM_FindDeadlocks() {
   if (_deadlocks != NULL) {
     DeadlockCycle* cycle = _deadlocks;
diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp
index 3744040..19c33f8 100644
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp
@@ -101,6 +101,7 @@
   template(ClassLoaderHierarchyOperation)         \
   template(JFROldObject)                          \
   template(PrintClasses)                          \
+  template(PrintMetadata)                         \

 class VM_Operation: public CHeapObj<mtInternal> {
  public:
@@ -329,6 +330,17 @@ class VM_PrintJNI: public VM_Operation {
   void doit();
 };

+class VM_PrintMetadata : public VM_Operation {
+ private:
+  outputStream* _out;
+  size_t        _scale;
+ public:
+  VM_PrintMetadata(outputStream* out, size_t scale) : _out(out), _scale(scale) {};
+
+  VMOp_Type type() const  { return VMOp_PrintMetadata; }
+  void doit();
+};
+
 class DeadlockCycle;
 class VM_FindDeadlocks: public VM_Operation {
  private:
diff --git a/hotspot/src/share/vm/services/nmtDCmd.cpp b/hotspot/src/share/vm/services/nmtDCmd.cpp
index fcad784..659ca33 100644
--- a/hotspot/src/share/vm/services/nmtDCmd.cpp
+++ b/hotspot/src/share/vm/services/nmtDCmd.cpp
@@ -24,6 +24,8 @@
 #include "precompiled.hpp"

 #include "runtime/mutexLocker.hpp"
+#include "runtime/vmThread.hpp"
+#include "runtime/vm_operations.hpp"
 #include "services/nmtDCmd.hpp"
 #include "services/memReporter.hpp"
 #include "services/memTracker.hpp"
@@ -38,6 +40,8 @@ NMTDCmd::NMTDCmd(outputStream* output,
   _detail("detail", "request runtime to report memory allocation >= "
            "1K by each callsite.",
            "BOOLEAN", false, "false"),
+  _metadata("metadata", "request runtime to report metadata information",
+           "BOOLEAN", false, "false"),
   _baseline("baseline", "request runtime to baseline current memory usage, " \
             "so it can be compared against in later time.",
             "BOOLEAN", false, "false"),
@@ -57,6 +61,7 @@ NMTDCmd::NMTDCmd(outputStream* output,
        "STRING", false, "KB") {
   _dcmdparser.add_dcmd_option(&_summary);
   _dcmdparser.add_dcmd_option(&_detail);
+  _dcmdparser.add_dcmd_option(&_metadata);
   _dcmdparser.add_dcmd_option(&_baseline);
   _dcmdparser.add_dcmd_option(&_summary_diff);
   _dcmdparser.add_dcmd_option(&_detail_diff);
@@ -92,6 +97,7 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) {
   int nopt = 0;
   if (_summary.is_set() && _summary.value()) { ++nopt; }
   if (_detail.is_set() && _detail.value()) { ++nopt; }
+  if (_metadata.is_set() && _metadata.value()) { ++nopt; }
   if (_baseline.is_set() && _baseline.value()) { ++nopt; }
   if (_summary_diff.is_set() && _summary_diff.value()) { ++nopt; }
   if (_detail_diff.is_set() && _detail_diff.value()) { ++nopt; }
@@ -100,7 +106,7 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) {

   if (nopt > 1) {
       output()->print_cr("At most one of the following option can be specified: " \
-        "summary, detail, baseline, summary.diff, detail.diff, shutdown");
+        "summary, detail, metadata, baseline, summary.diff, detail.diff, shutdown");
       return;
   } else if (nopt == 0) {
     if (_summary.is_set()) {
@@ -118,9 +124,13 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) {
     report(true, scale_unit);
   } else if (_detail.value()) {
     if (!check_detail_tracking_level(output())) {
-    return;
-  }
+      return;
+    }
     report(false, scale_unit);
+  } else if (_metadata.value()) {
+      size_t scale = get_scale(_scale.value());
+      VM_PrintMetadata op(output(), scale);
+      VMThread::execute(&op);
   } else if (_baseline.value()) {
     MemBaseline& baseline = MemTracker::get_baseline();
     if (!baseline.baseline(MemTracker::tracking_level() != NMT_detail)) {
diff --git a/hotspot/src/share/vm/services/nmtDCmd.hpp b/hotspot/src/share/vm/services/nmtDCmd.hpp
index df1ab36..bbd1391 100644
--- a/hotspot/src/share/vm/services/nmtDCmd.hpp
+++ b/hotspot/src/share/vm/services/nmtDCmd.hpp
@@ -39,6 +39,7 @@ class NMTDCmd: public DCmdWithParser {
  protected:
   DCmdArgument<bool>  _summary;
   DCmdArgument<bool>  _detail;
+  DCmdArgument<bool>  _metadata;
   DCmdArgument<bool>  _baseline;
   DCmdArgument<bool>  _summary_diff;
   DCmdArgument<bool>  _detail_diff;
--
1.8.3.1
