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 6 import java.util.ArrayList; 7 import java.util.List; 8 import java.util.HashSet; 9 import java.util.Arrays; 10 11 import jrockit.jfr.TestRecording; 12 import oracle.jrockit.jfr.parser.FLREvent; 13 import oracle.jrockit.jfr.parser.FLRStruct; 14 15 import java.lang.management.RuntimeMXBean; 16 import java.lang.management.ManagementFactory; 17 18 import static jrockit.Asserts.*; 19 20 /* 21 * @test TestReferenceStatisticsEvent 22 * @key jfr 23 * @library ../common 24 * 25 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx50m -Xmn2m -XX:+UseParallelGC TestReferenceStatisticsEvent ParallelScavenge 26 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx50m -Xmn2m -XX:+UseParNewGC TestReferenceStatisticsEvent ParNew 27 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx50m -Xmn2m -XX:+UseSerialGC TestReferenceStatisticsEvent DefNew 28 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx50m -Xmn2m -XX:+UseG1GC TestReferenceStatisticsEvent G1New 29 * 30 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:-UseParallelOldGC TestReferenceStatisticsEvent SerialOld 31 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC TestReferenceStatisticsEvent ParallelOld 32 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent TestReferenceStatisticsEvent G1Old 33 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC TestReferenceStatisticsEvent SerialOld 34 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC TestReferenceStatisticsEvent SerialOld 35 * @run main/othervm -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent TestReferenceStatisticsEvent ConcurrentMarkSweep 36 * 37 */ 38 public class TestReferenceStatisticsEvent { 39 private static final String gcEventPath = "vm/gc/collector/garbage_collection"; 40 private static final String refStatsEventPath = "vm/gc/reference/statistics"; 41 public static byte[] garbage; 42 43 private static void enableEvent(TestRecording r, String path) throws Exception { 44 r.createJVMSetting(path, true, false, 0, 0); 45 } 46 47 private static boolean isYoungGC(String gcName) { 48 return Arrays.asList("ParallelScavenge", "ParNew", "DefNew", "G1New").contains(gcName); 49 } 50 51 private static boolean isExplicitGCConcurrent() { 52 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); 53 List<String> args = runtimeMxBean.getInputArguments(); 54 for (String arg : args) { 55 if (arg.equals("-XX:+ExplicitGCInvokesConcurrent")) { 56 return true; 57 } 58 } 59 return false; 60 } 61 62 private static boolean isRunningConcurrentCMS(String gcName) { 63 return gcName.equals("ConcurrentMarkSweep") && isExplicitGCConcurrent(); 64 } 65 66 private static void triggerGC(String gcName) { 67 if (isYoungGC(gcName)) { 68 for (int i = 0; i < 2048; i++) { 69 garbage = new byte[1024]; 70 } 71 } else if (isRunningConcurrentCMS(gcName)) { 72 // The "vm/gc/collector/garbage_collection" event is not sent at 73 // the time when "System.gc()" returns from the VM. To be sure that 74 // we get at least one GC event, we need to trigger to GCs. 75 System.gc(); 76 System.gc(); 77 }else { 78 System.gc(); 79 } 80 } 81 82 private static List<FLREvent> getEventsFromRecording(TestRecording r, String regex) throws Exception { 83 return r.parser().findJVMEvents(regex); 84 } 85 86 private static List<FLREvent> filterEventsOnPath(List<FLREvent> events, String path) 87 throws Exception { 88 List<FLREvent> result = new ArrayList<FLREvent>(); 89 for (FLREvent e : events) { 90 if (e.getPath().equals(path)) { 91 result.add(e); 92 } 93 } 94 return result; 95 } 96 97 private static String gcName(FLREvent e) throws Exception { 98 FLRStruct s = (FLRStruct) e.getResolvedValue("name"); 99 return (String) s.getValue("name"); 100 } 101 102 private static List<FLREvent> filterEventsOnCollector(List<FLREvent> events, String name) 103 throws Exception { 104 List<FLREvent> result = new ArrayList<FLREvent>(); 105 List<FLREvent> allGCEvents = filterEventsOnPath(events, gcEventPath); 106 for (FLREvent e : allGCEvents) { 107 if (gcName(e).equals(name)) { 108 result.add(e); 109 } 110 } 111 return result; 112 } 113 114 private static Long getGCIdFromEvent(FLREvent e) throws Exception { 115 return (Long) e.getValue("gcId"); 116 } 117 118 private static List<FLREvent> filterRefProcEventsByGcId(List<FLREvent> events, Long id) 119 throws Exception { 120 List<FLREvent> result = new ArrayList<FLREvent>(); 121 for (FLREvent e : events) { 122 if (getGCIdFromEvent(e).equals(id)) { 123 result.add(e); 124 } 125 } 126 return result; 127 } 128 129 private static List<Long> mapGCEventsToIds(List<FLREvent> events) throws Exception { 130 List<Long> gcIds = new ArrayList<Long>(); 131 for (FLREvent e : events) { 132 gcIds.add(getGCIdFromEvent(e)); 133 } 134 return gcIds; 135 } 136 137 private static List<List<FLREvent>> groupRefStatEventsByGCId(List<FLREvent> allEvents, List<FLREvent> gcEvents) 138 throws Exception { 139 List<Long> gcIds = mapGCEventsToIds(gcEvents); 140 List<FLREvent> refProcEvents = filterEventsOnPath(allEvents, refStatsEventPath); 141 List<List<FLREvent>> result = new ArrayList<List<FLREvent>>(); 142 for (Long l : gcIds) { 143 List<FLREvent> events = filterRefProcEventsByGcId(refProcEvents, l); 144 if (events.size() > 0) { 145 result.add(events); 146 } 147 } 148 return result; 149 } 150 151 private static String referenceType(FLREvent e) throws Exception { 152 FLRStruct s = (FLRStruct) e.getResolvedValue("type"); 153 return (String) s.getValue("type"); 154 } 155 156 private static void assertReferenceType(HashSet<String> types, String type, Long gcId) 157 throws Exception { 158 assertTrue(types.contains(type + " reference"), 159 "Expected " + refStatsEventPath + " event with type " + type + 160 " reference for gc with id " + gcId); 161 assertNE(gcId, -1, "Expected " + refStatsEventPath + " to have a gcId"); 162 } 163 164 private static void verifyRefStatEvents(List<FLREvent> events) throws Exception { 165 HashSet<String> types = new HashSet<String>(); 166 long gcId = -1; 167 for (FLREvent e : events) { 168 types.add(referenceType(e)); 169 gcId = getGCIdFromEvent(e); // all the event should have the same gcId 170 } 171 assertReferenceType(types, "Soft", gcId); 172 assertReferenceType(types, "Weak", gcId); 173 assertReferenceType(types, "Final", gcId); 174 assertReferenceType(types, "Phantom", gcId); 175 } 176 177 private static List<FLREvent> filterOutBadCMSEvents(List<FLREvent> events, String gcName) throws Exception { 178 List<FLREvent> cmsEvents = filterEventsOnCollector(events, gcName); 179 assertTrue(cmsEvents.size() == 1 || cmsEvents.size() == 2, 180 "Expected exactly one or two " + gcEventPath + " event(s) from concurrent CMS collector"); 181 182 if (cmsEvents.size() == 2) { 183 // No need to filter any events since we got GC events for both 184 // System.gc() calls. 185 return events; 186 } 187 188 List<FLREvent> gcEvents = filterEventsOnPath(events, gcEventPath); 189 190 List<Long> gcIds = mapGCEventsToIds(gcEvents); 191 Long largestGCId = 0L; 192 for (Long gcId : gcIds) { 193 if (gcId > largestGCId) { 194 largestGCId = gcId; 195 } 196 } 197 198 List<FLREvent> result = new ArrayList<FLREvent>(); 199 for (FLREvent e : events) { 200 String path = e.getPath(); 201 if (path.startsWith("vm/gc/") && !path.startsWith("vm/gc/configuration")) { 202 if (getGCIdFromEvent(e) == largestGCId + 1) { 203 // this event comes from "dangling" CMS collection 204 } else { 205 result.add(e); 206 } 207 } 208 } 209 return result; 210 } 211 212 public static void main(String[] args) throws Exception { 213 String gcName = args[args.length - 1]; 214 215 TestRecording r = new TestRecording(); 216 try { 217 enableEvent(r, refStatsEventPath); 218 enableEvent(r, gcEventPath); 219 220 r.start(); 221 triggerGC(gcName); 222 r.stop(); 223 224 List<FLREvent> allEvents = getEventsFromRecording(r, ".*"); 225 226 if (isRunningConcurrentCMS(gcName)) { 227 allEvents = filterOutBadCMSEvents(allEvents, gcName); 228 } 229 230 List<FLREvent> gcEvents = filterEventsOnCollector(allEvents, gcName); 231 assertTrue(gcEvents.size() > 0, 232 "Expected at least one " + gcEventPath + " event from gc " + gcName); 233 234 List<List<FLREvent>> refStatEvents = groupRefStatEventsByGCId(allEvents, gcEvents); 235 assertTrue(refStatEvents.size() == gcEvents.size(), 236 "Expected number of " + refStatsEventPath + " groups to be " + 237 gcEvents.size() + ", but got " + refStatEvents.size()); 238 239 for (List<FLREvent> refStats : refStatEvents) { 240 verifyRefStatEvents(refStats); 241 } 242 } catch (Throwable t) { 243 r.copyTo("TestReferenceStatisticsEvent.jfr"); 244 throw t; 245 } finally { 246 r.close(); 247 } 248 } 249 }