1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
   4  */
   5 import java.util.List;
   6 import java.util.ArrayList;
   7 import java.util.Arrays;
   8 
   9 import java.util.NoSuchElementException;
  10 
  11 import java.util.Set;
  12 import java.util.HashSet;
  13 
  14 import static jrockit.Asserts.*;
  15 
  16 import java.lang.management.RuntimeMXBean;
  17 import java.lang.management.ManagementFactory;
  18 
  19 import oracle.jrockit.jfr.parser.FLREvent;
  20 import oracle.jrockit.jfr.parser.FLRStruct;
  21 import jrockit.jfr.TestRecording;
  22 
  23 /*
  24  * @test TestObjectCountAfterGCEvent
  25  * @key jfr
  26  * @library ../common
  27  *
  28  * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestObjectCountAfterGCEvent SerialOld
  29  * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UseParallelGC -XX:-UseParallelOldGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestObjectCountAfterGCEvent SerialOld
  30  * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestObjectCountAfterGCEvent ParallelOld
  31  * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestObjectCountAfterGCEvent SerialOld
  32  * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestObjectCountAfterGCEvent G1Old
  33  * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UseConcMarkSweepGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestObjectCountAfterGCEvent SerialOld
  34  * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestObjectCountAfterGCEvent ConcurrentMarkSweep
  35  */
  36 public class TestObjectCountAfterGCEvent {
  37     private static final String objectCountEventPath = "vm/gc/detailed/object_count_after_gc";
  38     private static final String gcEventPath = "vm/gc/collector/garbage_collection";
  39 
  40     public static void main(String[] args) throws Exception {
  41         String gcName = args[0];
  42 
  43         TestRecording r = new TestRecording();
  44         try {
  45             enableEvent(r, objectCountEventPath);
  46             enableEvent(r, gcEventPath);
  47 
  48             ObjectCountEventVerifier.createTestData();
  49 
  50             r.start();
  51 
  52             System.gc();
  53             if (gcName.equals("ConcurrentMarkSweep") && isExplicitGCConcurrent()) {
  54                 System.gc();
  55             }
  56 
  57             r.stop();
  58 
  59             List<FLREvent> allEvents = getRecordedEvents(r);
  60 
  61             if (gcName.equals("ConcurrentMarkSweep") && isExplicitGCConcurrent()) {
  62                 allEvents = removeEventsFromUnfinishedCMS(allEvents);
  63             }
  64 
  65             List<FLREvent> gcEvents = selectGCEventsWithName(allEvents, gcName);
  66             assertNotEmpty(gcEvents,
  67                            "Expected at least one " + gcEventPath + " event");
  68 
  69             Set<Integer> gcIds = new HashSet<>();
  70             gcIds.addAll(gcIdsFromEvents(gcEvents));
  71 
  72             List<FLREvent> objectCountEvents = selectEventsWithPath(allEvents, objectCountEventPath);
  73             assertNotEmpty(gcEvents,
  74                            "Expected at least one " + objectCountEventPath + " event");
  75 
  76             List<FLREvent> targetObjectCountEvents =
  77                 selectObjectCountEventsWithIds(objectCountEvents, gcIds);
  78             assertNotEmpty(targetObjectCountEvents,
  79                            "Expected at least one " + objectCountEventPath +
  80                            " event for gc " + gcName);
  81 
  82             ObjectCountEventVerifier.verify(objectCountEvents);
  83         } catch (Throwable t) {
  84             r.copyTo("TestObjectCountAfterGCEvent.jfr");
  85             throw t;
  86         } finally {
  87             r.close();
  88         }
  89     }
  90 
  91     private static List<FLREvent> selectObjectCountEventsWithIds(List<FLREvent> events, Set<Integer> gcIds)
  92         throws Exception {
  93         List<FLREvent> selected = new ArrayList<FLREvent>();
  94         for (FLREvent e : events) {
  95             if (gcIds.contains(getGCIdFromEvent(e))) {
  96                 selected.add(e);
  97             }
  98         }
  99         return selected;
 100     }
 101 
 102     private static List<FLREvent> getRecordedEvents(TestRecording r) throws Exception {
 103         return r.parser().findJVMEvents(".*");
 104     }
 105 
 106     private static void enableEvent(TestRecording r, String path) throws Exception {
 107         r.createJVMSetting(path, true, false, 0, 0);
 108     }
 109 
 110     private static List<FLREvent> selectEventsWithPath(List<FLREvent> events, String path)
 111         throws Exception {
 112         List<FLREvent> selection = new ArrayList<FLREvent>();
 113         for (FLREvent e : events) {
 114             if (e.getPath().equals(path)) {
 115                 selection.add(e);
 116             }
 117         }
 118         return selection;
 119     }
 120 
 121     private static List<FLREvent> selectEventsWithPathPrefix(List<FLREvent> events, String path)
 122         throws Exception {
 123         List<FLREvent> selection = new ArrayList<FLREvent>();
 124         for (FLREvent e : events) {
 125             if (e.getPath().startsWith(path)) {
 126                 selection.add(e);
 127             }
 128         }
 129         return selection;
 130     }
 131 
 132     private static List<FLREvent> removeEventsFromUnfinishedCMS(List<FLREvent> events)
 133         throws Exception {
 134         List<Integer> additionalGCIds = gcIdsNotPresentInCollectionEvents(events);
 135 
 136         StringBuilder msg = new StringBuilder();
 137         msg.append('[');
 138         for (Integer gcId : additionalGCIds) {
 139             msg.append(" " + gcId);
 140         }
 141         msg.append(" ]");
 142         assertLE(additionalGCIds.size(), 1,
 143                  "Expected at most one additional gcId from a concurrent CMS, but got " + msg.toString());
 144 
 145         if (additionalGCIds.isEmpty()) {
 146             return events;
 147         }
 148 
 149         return removeEventsWithGCId(events, additionalGCIds.get(0));
 150     }
 151 
 152     private static List<Integer> gcIdsNotPresentInCollectionEvents(List<FLREvent> events)
 153         throws Exception {
 154         List<FLREvent> gcEvents = selectEventsWithPath(events, "vm/gc/collector/garbage_collection");
 155         List<Integer> gcIds = gcIdsFromEvents(gcEvents);
 156         List<FLREvent> gcPhases = selectEventsWithPathPrefix(events, "vm/gc/phases");
 157         Set<Integer> phaseIds = new HashSet<Integer>();
 158         phaseIds.addAll(gcIdsFromEvents(gcPhases));
 159 
 160         for (Integer gcId : gcIds) {
 161             phaseIds.remove(gcId);
 162         }
 163 
 164         return Arrays.asList(phaseIds.toArray(new Integer[phaseIds.size()]));
 165     }
 166 
 167     public static List<Integer> gcIdsFromEvents(List<FLREvent> events)
 168         throws Exception {
 169         List<Integer> gcIds = new ArrayList<>();
 170         for (FLREvent e : events) {
 171             if (hasGCId(e)) {
 172                 gcIds.add(getGCIdFromEvent(e));
 173             }
 174         }
 175         return gcIds;
 176     }
 177 
 178     private static List<FLREvent> removeEventsWithGCId(List<FLREvent> events, Integer gcId)
 179         throws Exception {
 180         List<FLREvent> selected = new ArrayList<FLREvent>();
 181         for (FLREvent e : events) {
 182             if (hasGCId(e)) {
 183                 if (getGCIdFromEvent(e) != gcId) {
 184                     selected.add(e);
 185                 }
 186             }
 187         }
 188         return selected;
 189     }
 190 
 191     private static Integer getGCIdFromEvent(FLREvent e) {
 192         return (Integer) e.getValue("gcId");
 193     }
 194 
 195     private static boolean hasGCId(FLREvent e) {
 196         try {
 197             e.getValue("gcId");
 198             return true;
 199         } catch (NoSuchElementException exc) {
 200             return false;
 201         }
 202     }
 203 
 204     private static boolean isExplicitGCConcurrent() {
 205         RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
 206         List<String> args = runtimeMxBean.getInputArguments();
 207         for (String arg : args) {
 208             if (arg.equals("-XX:+ExplicitGCInvokesConcurrent")) {
 209                 return true;
 210             }
 211         }
 212         return false;
 213     }
 214 
 215     public static List<FLREvent> selectGCEventsWithName(List<FLREvent> events, String name)
 216         throws Exception {
 217         List<FLREvent> gcEvents = selectEventsWithPath(events, gcEventPath);
 218 
 219         List<FLREvent> selected = new ArrayList<>();
 220         for (FLREvent e : gcEvents) {
 221             FLRStruct resolved = (FLRStruct) e.getResolvedValue("name");
 222             String gcName = (String) resolved.getValue("name");
 223             if (gcName.equals(name)) {
 224                 selected.add(e);
 225             }
 226         }
 227         return selected;
 228     }
 229 }
 230