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.ArrayList;
   6 import java.util.Collections;
   7 import java.util.List;
   8 import java.util.NoSuchElementException;
   9 
  10 import jrockit.jfr.JFRHelper;
  11 import jrockit.jfr.TestRecording;
  12 import oracle.jrockit.jfr.parser.FLREvent;
  13 import oracle.jrockit.jfr.parser.FLRStruct;
  14 
  15 import static jrockit.Asserts.*;
  16 
  17 public class HeapSummaryEventAllGcs {
  18 
  19     private static int lastHeapGcId = -1;
  20     private static int lastPSGcId = -1;
  21     private static int lastPermGcId = -1;
  22 
  23     public static void test(GarbageCollectionConfiguration.YoungCollector yc,
  24             GarbageCollectionConfiguration.OldCollector oc) throws Exception {
  25 
  26         if (!GarbageCollectionConfiguration.usesGCs(yc, oc)) {
  27             System.out.println("WARNING: Skip the test due to invalid GCs\n"
  28                     + "The expected young collector: " + yc.toString()
  29                     + " and old collector: " + oc.toString());
  30             return;
  31         }
  32 
  33         TestRecording r = new TestRecording();
  34         try {
  35             // activate the event we are interested in
  36             r.createJVMSetting("vm/gc/heap/summary", true, false, 0, 0);
  37             r.createJVMSetting("vm/gc/heap/ps_summary", true, false, 0, 0);
  38             r.createJVMSetting("vm/gc/heap/perm_gen_summary", true, false, 0, 0);
  39 
  40             r.start();
  41 
  42             // To eliminate the risk of being in the middle of a GC when the
  43             // recording starts/stops,
  44             // we run 3 System.gc() and ignores the first and last GC.
  45             for (int c = 0; c < 3; c++) {
  46                 System.gc();
  47             }
  48 
  49             r.stop();
  50 
  51             List<FLREvent> allEvents = r.parser().findJVMEvents("vm/gc/heap/.*summary");
  52             // Since the ordering of events are important in this test, we sort them by time stamp.
  53             Collections.sort(allEvents, new JFRHelper.EventTimestampComparator());
  54 
  55             // Remove first and last gcId to make sure we remove any "half" GCs.
  56             List<FLREvent> filteredEvents = removeFirstAndLastGC(allEvents);
  57 
  58             try {
  59                 assertFalse(filteredEvents.isEmpty(), "Expected at least one event.");
  60                 assertEquals(filteredEvents.size() % 2, 0, "Events should come in pairs");
  61 
  62                 for (FLREvent evt : filteredEvents) {
  63                     if ("vm/gc/heap/summary".equals(evt.getPath())) {
  64                         checkHeapGcId(evt);
  65                         checkHeapEventContent(evt);
  66                     } else if ("vm/gc/heap/ps_summary".equals(evt.getPath())) {
  67                         checkPSGcId(evt);
  68                         checkPSEventContent(evt);
  69                     } else {
  70                         assertEquals("vm/gc/heap/perm_gen_summary", evt.getPath(), "Unknown event");
  71                         checkPermGcId(evt);
  72                         checkPermEventContent(evt);
  73                     }
  74                 }
  75 
  76                 // Sanity check. Not complete.
  77                 assertEquals(lastHeapGcId, lastPermGcId, "Should have gotten perm gen events for all GCs");
  78             } catch (Throwable t) {
  79                 System.out.println("all events:");
  80                 for (FLREvent evt : filteredEvents) {
  81                     System.out.println(evt);
  82                 }
  83                 String filename = "HeapSummaryEventAllGcs_" + yc.name() + "_" + oc.name() + ".jfr";
  84                 r.copyTo(filename);
  85                 System.out.println("Failed recording saved as " + filename);
  86                 throw t;
  87             }
  88         } finally {
  89             r.close();
  90         }
  91     }
  92 
  93     private static void checkPermEventContent(FLREvent evt) {
  94         FLRStruct permSpace = (FLRStruct)evt.getResolvedValue("permSpace");
  95         FLRStruct objectSpace = (FLRStruct)evt.getResolvedValue("objectSpace");
  96         checkVirtualSpace(permSpace);
  97         checkSpace(objectSpace);
  98     }
  99 
 100     private static void checkHeapGcId(FLREvent evt) {
 101         int gcId = (int) evt.getValue("gcId");
 102         FLRStruct when = ((FLRStruct) evt.getResolvedValue("when"));
 103         if ("Before GC".equals(when.getValue("when"))) {
 104             assertGT(gcId, lastHeapGcId, "gcId should be increasing");
 105             lastHeapGcId = gcId;
 106         } else {
 107             assertEquals(gcId, lastHeapGcId, "After should have same gcId as last Before event");
 108         }
 109     }
 110 
 111     private static void checkPSGcId(FLREvent evt) {
 112         int gcId = (int) evt.getValue("gcId");
 113         FLRStruct when = ((FLRStruct) evt.getResolvedValue("when"));
 114         if ("Before GC".equals(when.getValue("when"))) {
 115             assertGT(gcId, lastPSGcId, "gcId should be increasing");
 116             lastPSGcId = gcId;
 117         } else {
 118             assertEquals(gcId, lastPSGcId, "After should have same gcId as last Before event");
 119         }
 120     }
 121 
 122     private static void checkPermGcId(FLREvent evt) {
 123         int gcId = (int) evt.getValue("gcId");
 124         FLRStruct when = ((FLRStruct) evt.getResolvedValue("when"));
 125         if ("Before GC".equals(when.getValue("when"))) {
 126             assertGT(gcId, lastPermGcId, "gcId should be increasing");
 127             lastPermGcId = gcId;
 128         } else {
 129             assertEquals(gcId, lastPermGcId, "After should have same gcId as last Before event");
 130         }
 131     }
 132 
 133     private static void checkHeapEventContent(FLREvent evt) {
 134         FLRStruct heapSpace = (FLRStruct) evt.getResolvedValue("heapSpace");
 135         checkVirtualSpace(heapSpace);
 136         long used = (long) evt.getValue("heapUsed");
 137         long size = (long) heapSpace.getValue("committedEnd")
 138                 - (long) heapSpace.getValue("start");
 139         assertLE(used, size, "used can not exceed size");
 140     }
 141 
 142     private static void checkPSEventContent(FLREvent evt) {
 143         FLRStruct oldSpace = (FLRStruct) evt.getResolvedValue("oldSpace");
 144         checkVirtualSpace(oldSpace);
 145         checkSpace((FLRStruct) evt.getResolvedValue("oldObjectSpace"));
 146 
 147         FLRStruct youngSpace = (FLRStruct) evt.getResolvedValue("youngSpace");
 148         FLRStruct edenSpace = (FLRStruct) evt.getResolvedValue("edenSpace");
 149         FLRStruct fromSpace = (FLRStruct) evt.getResolvedValue("fromSpace");
 150         FLRStruct toSpace = (FLRStruct) evt.getResolvedValue("toSpace");
 151         checkVirtualSpace(youngSpace);
 152         checkSpace(edenSpace);
 153         checkSpace(fromSpace);
 154         checkSpace(toSpace);
 155         checkPSYoungSizes(youngSpace, edenSpace, fromSpace, toSpace);
 156         checkPSYoungStartEnd(oldSpace, youngSpace, edenSpace, fromSpace, toSpace);
 157     }
 158 
 159     private static void checkPSYoungSizes(FLRStruct young, FLRStruct eden,
 160             FLRStruct from, FLRStruct to) {
 161         long youngSize = (long) young.getValue("committedEnd")
 162                 - (long) young.getValue("start");
 163         long edenSize = (long) eden.getValue("end")
 164                 - (long) eden.getValue("start");
 165         long fromSize = (long) from.getValue("end")
 166                 - (long) from.getValue("start");
 167         long toSize = (long) to.getValue("end") - (long) to.getValue("start");
 168         // We can't use equal here since we may over commit the young gen size
 169         // when we do resizing
 170         assertGE(youngSize, edenSize + fromSize + toSize, "Young sizes don't match");
 171     }
 172 
 173     private static void checkPSYoungStartEnd(FLRStruct old, FLRStruct young,
 174             FLRStruct eden, FLRStruct from, FLRStruct to) {
 175         long oldEnd = (long) old.getValue("reservedEnd");
 176         long youngStart = (long) young.getValue("start");
 177         long youngEnd = (long) young.getValue("committedEnd");
 178         long edenStart = (long) eden.getValue("start");
 179         long edenEnd = (long) eden.getValue("end");
 180         long fromStart = (long) from.getValue("start");
 181         long fromEnd = (long) from.getValue("end");
 182         long toStart = (long) to.getValue("start");
 183         long toEnd = (long) to.getValue("end");
 184         assertEQ(oldEnd, youngStart, "Young should start where old ends");
 185         assertEQ(youngStart, edenStart, "Eden should be placed first in young");
 186         if (fromStart < toStart) {
 187             // [eden][from][to]
 188             assertGE(fromStart, edenEnd, "From should start after eden");
 189             assertLE(fromEnd, toStart, "To should start after From");
 190             assertLE(toEnd, youngEnd, "To should start after From");
 191         } else {
 192             // [eden][to][from]
 193             assertGE(toStart, edenEnd, "From should start after eden");
 194             assertLE(toEnd, fromStart, "To should start after From");
 195             assertLE(fromEnd, youngEnd, "To should start after From");
 196         }
 197     }
 198 
 199     private static void checkVirtualSpace(FLRStruct virtualSpace) {
 200         long start = (long) virtualSpace.getValue("start");
 201         long committedEnd = (long) virtualSpace.getValue("committedEnd");
 202         long reservedEnd = (long) virtualSpace.getValue("reservedEnd");
 203         long committedSize = (long) virtualSpace.getValue("committedSize");
 204         long reservedSize = (long) virtualSpace.getValue("reservedSize");
 205 
 206         assertLT(start, committedEnd, "start must be less than committedEnd");
 207         assertLT(committedEnd, reservedEnd, "committedEnd must be less than reservedEnd");
 208         assertEQ(committedSize, committedEnd - start, "Size mismatch");
 209         assertEQ(reservedSize, reservedEnd - start, "Size mismatch");
 210     }
 211 
 212     private static void checkSpace(FLRStruct objectSpace) {
 213         long start = (long) objectSpace.getValue("start");
 214         long end = (long) objectSpace.getValue("end");
 215         long used = (long) objectSpace.getValue("used");
 216         long size = (long) objectSpace.getValue("size");
 217 
 218         assertLT(start, end, "start must be less than end");
 219         assertLE(used, size, "used can not exceed size");
 220         assertEQ(size, end - start, "Size mismatch");
 221     }
 222 
 223     /**
 224      * Removes gcEvents with lowest and highest gcID. This is used to filter out
 225      * any incomplete GCs if the recording started/stopped in the middle of a
 226      * GC.
 227      *
 228      * @param allEvents
 229      *            All events
 230      * @return The remaining events when the events with the lowest and highest
 231      *         gcId have been removed.
 232      */
 233     private static List<FLREvent> removeFirstAndLastGC(List<FLREvent> allEvents) {
 234         List<FLREvent> filteredEvents = new ArrayList<FLREvent>();
 235         int minGcId = Integer.MAX_VALUE;
 236         int maxGcId = Integer.MIN_VALUE;
 237 
 238         // Find min/max gcId
 239         for (FLREvent event : allEvents) {
 240             try {
 241                 int gcId = ((Integer) event.getValue("gcId")).intValue();
 242                 if (gcId < minGcId) {
 243                     minGcId = gcId;
 244                 }
 245                 if (gcId > maxGcId) {
 246                     maxGcId = gcId;
 247                 }
 248             } catch (NoSuchElementException t) {
 249                 // Expected error
 250             }
 251         }
 252 
 253         // Add all events execpt those with gcId = min/max gcId
 254         for (FLREvent event : allEvents) {
 255             try {
 256                 int gcId = ((Integer) event.getValue("gcId")).intValue();
 257                 if (gcId == minGcId || gcId == maxGcId) {
 258                     continue;
 259                 }
 260             } catch (NoSuchElementException t) {
 261                 // Expected error.
 262             }
 263             filteredEvents.add(event);
 264         }
 265         return filteredEvents;
 266     }
 267 
 268 }