From bc77a647797b98cae34ce01d93b1dcbce881a373 Mon Sep 17 00:00:00 2001
From: Chris Larsen <clarsen@yahoo-inc.com>
Date: Fri, 29 Apr 2016 15:56:22 -0700
Subject: [PATCH] [core] Add Utils.getGCStats() to return a map of stats for
 each garbage collector. This will allow greater insight into the generations.
 Now the client will emmit the stats for each collector at the end of run
 along with the totals.

---
 core/src/main/java/com/yahoo/ycsb/Client.java | 19 +++++++++---
 core/src/main/java/com/yahoo/ycsb/Utils.java  | 30 +++++++++++++++++++
 .../test/java/com/yahoo/ycsb/TestUtils.java   |  4 ++-
 3 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/core/src/main/java/com/yahoo/ycsb/Client.java b/core/src/main/java/com/yahoo/ycsb/Client.java
index 0e099118..de87be7c 100644
--- a/core/src/main/java/com/yahoo/ycsb/Client.java
+++ b/core/src/main/java/com/yahoo/ycsb/Client.java
@@ -30,6 +30,7 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -696,10 +697,20 @@ public class Client
       double throughput = 1000.0 * (opcount) / (runtime);
       exporter.write("OVERALL", "Throughput(ops/sec)", throughput);
       
-      exporter.write("TOTAL_GCs", "Count", Utils.getGCTotalCollectionCount());
-      final long gcTime = Utils.getGCTotalTime();
-      exporter.write("TOTAL_GC_TIME", "Time(ms)", gcTime);
-      exporter.write("TOTAL_GC_TIME_%", "Time(%)", ((double)gcTime / runtime) * (double)100);
+      final Map<String, Long[]> gcs = Utils.getGCStatst();
+      long totalGCCount = 0;
+      long totalGCTime = 0;
+      for (final Entry<String, Long[]> entry : gcs.entrySet()) {
+        exporter.write("TOTAL_GCS_" + entry.getKey(), "Count", entry.getValue()[0]);
+        exporter.write("TOTAL_GC_TIME_" + entry.getKey(), "Time(ms)", entry.getValue()[1]);
+        exporter.write("TOTAL_GC_TIME_%_" + entry.getKey(), "Time(%)",((double)entry.getValue()[1] / runtime) * (double)100);
+        totalGCCount += entry.getValue()[0];
+        totalGCTime += entry.getValue()[1];
+      }
+      exporter.write("TOTAL_GCs", "Count", totalGCCount);
+      
+      exporter.write("TOTAL_GC_TIME", "Time(ms)", totalGCTime);
+      exporter.write("TOTAL_GC_TIME_%", "Time(%)", ((double)totalGCTime / runtime) * (double)100);
       if (statusthread != null && statusthread.trackJVMStats()) {
         exporter.write("MAX_MEM_USED", "MBs", statusthread.getMaxUsedMem());
         exporter.write("MIN_MEM_USED", "MBs", statusthread.getMinUsedMem());
diff --git a/core/src/main/java/com/yahoo/ycsb/Utils.java b/core/src/main/java/com/yahoo/ycsb/Utils.java
index a6af3e17..05199fd7 100644
--- a/core/src/main/java/com/yahoo/ycsb/Utils.java
+++ b/core/src/main/java/com/yahoo/ycsb/Utils.java
@@ -20,7 +20,9 @@ package com.yahoo.ycsb;
 import java.lang.management.GarbageCollectorMXBean;
 import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 
 /**
@@ -240,4 +242,32 @@ public class Utils
         }
         return time;
       }
+
+      /**
+       * Returns a map of garbage collectors and their stats.
+       * The first object in the array is the total count since JVM start and the
+       * second is the total time (ms) since JVM start. 
+       * If a garbage collectors does not support the collector MXBean, then it
+       * will not be represented in the map.
+       * @return A non-null map of garbage collectors and their metrics. The map
+       * may be empty.
+       */ 
+      public static Map<String, Long[]> getGCStatst() {
+        final List<GarbageCollectorMXBean> gcBeans = 
+            ManagementFactory.getGarbageCollectorMXBeans();
+        final Map<String, Long[]> map = new HashMap<String, Long[]>(gcBeans.size());
+        for (final GarbageCollectorMXBean bean : gcBeans) {
+          if (!bean.isValid() || bean.getCollectionCount() < 0 || 
+              bean.getCollectionTime() < 0) {
+            continue;
+          }
+          
+          final Long[] measurements = new Long[] {
+              bean.getCollectionCount(),
+              bean.getCollectionTime()
+          };
+          map.put(bean.getName().replace(" ", "_"), measurements);
+        }
+        return map;
+      }
 }
diff --git a/core/src/test/java/com/yahoo/ycsb/TestUtils.java b/core/src/test/java/com/yahoo/ycsb/TestUtils.java
index f9fddf76..a84eca86 100644
--- a/core/src/test/java/com/yahoo/ycsb/TestUtils.java
+++ b/core/src/test/java/com/yahoo/ycsb/TestUtils.java
@@ -112,8 +112,10 @@ public class TestUtils {
     assertTrue(Utils.getGCTotalCollectionCount() >= 0);
     // Could be zero similar to GC total collection count
     assertTrue(Utils.getGCTotalTime() >= 0);
+    // Could be empty
+    assertTrue(Utils.getGCStatst().size() >= 0);
   }
-  
+   
   /**
    * Since this version of TestNG doesn't appear to have an assertArrayEquals,
    * this will compare the two to make sure they're the same. 
-- 
GitLab