1 /*
   2  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jfr.event.gc.heapsummary;
  27 
  28 import java.time.Duration;
  29 import java.util.List;
  30 
  31 import jdk.jfr.Recording;
  32 import jdk.jfr.consumer.RecordedEvent;
  33 import jdk.test.lib.Asserts;
  34 import jdk.test.lib.jfr.EventNames;
  35 import jdk.test.lib.jfr.Events;
  36 import jdk.test.lib.jfr.GCHelper;
  37 
  38 public class HeapSummaryEventAllGcs {
  39 
  40     public static void test(String expectedYoungCollector, String expectedOldCollector) throws Exception {
  41         Recording recording = new Recording();
  42         recording.enable(EventNames.GCConfiguration);
  43         recording.enable(EventNames.GCHeapSummary);
  44         recording.enable(EventNames.G1HeapSummary);
  45         recording.enable(EventNames.PSHeapSummary);
  46         recording.enable(EventNames.MetaspaceSummary).withThreshold(Duration.ofMillis(0));
  47 
  48         recording.start();
  49         // To eliminate the risk of being in the middle of a GC when the recording starts/stops,
  50         // we run 5 System.gc() and ignores the first and last GC.
  51         GCHelper.callSystemGc(5, true);
  52         recording.stop();
  53 
  54         if (!checkCollectors(recording, expectedYoungCollector, expectedOldCollector)) {
  55             return;
  56         }
  57         List<RecordedEvent> events = GCHelper.removeFirstAndLastGC(Events.fromRecording(recording));
  58         for (RecordedEvent event : events) {
  59             System.out.println("Event:" + event);
  60         }
  61 
  62         Asserts.assertFalse(events.isEmpty(), "Expected at least one event.");
  63         Asserts.assertEquals(events.size() % 2, 0, "Events should come in pairs");
  64 
  65         int lastHeapGcId = -1;
  66         int lastG1GcId = -1;
  67         int lastPSGcId = -1;
  68         int lastMetaspaceGcId = -1;
  69 
  70         for (RecordedEvent event : events) {
  71             final String eventName = event.getEventType().getName();
  72             switch (eventName) {
  73                 case EventNames.GCHeapSummary:
  74                     lastHeapGcId = checkGcId(event, lastHeapGcId);
  75                     checkHeapEventContent(event);
  76                     break;
  77                 case EventNames.G1HeapSummary:
  78                     lastG1GcId = checkGcId(event, lastG1GcId);
  79                     checkG1EventContent(event);
  80                     break;
  81                 case EventNames.PSHeapSummary:
  82                     lastPSGcId = checkGcId(event, lastPSGcId);
  83                     checkPSEventContent(event);
  84                     break;
  85                 case EventNames.MetaspaceSummary:
  86                     lastMetaspaceGcId = checkGcId(event, lastMetaspaceGcId);
  87                     checkMetaspaceEventContent(event);
  88                     break;
  89                 default:
  90                     System.out.println("Failed event: " + event);
  91                     Asserts.fail("Unknown event type: " + eventName);
  92             }
  93         }
  94 
  95         // Sanity check. Not complete.
  96         Asserts.assertEquals(lastHeapGcId, lastMetaspaceGcId, "Should have gotten perm gen events for all GCs");
  97     }
  98 
  99     private static void checkMetaspaceEventContent(RecordedEvent event) {
 100         long totalUsed = Events.assertField(event, "metaspace.used").atLeast(0L).getValue();
 101         long totalCommitted = Events.assertField(event, "metaspace.committed").atLeast(totalUsed).getValue();
 102         long totalReserved = Events.assertField(event, "metaspace.reserved").atLeast(totalCommitted).getValue();
 103 
 104         long dataUsed = Events.assertField(event, "dataSpace.used").atLeast(0L).getValue();
 105         long dataCommitted = Events.assertField(event, "dataSpace.committed").atLeast(dataUsed).getValue();
 106         long dataReserved = Events.assertField(event, "dataSpace.reserved").atLeast(dataCommitted).getValue();
 107 
 108         long classUsed = Events.assertField(event, "classSpace.used").atLeast(0L).getValue();
 109         long classCommitted = Events.assertField(event, "classSpace.committed").atLeast(classUsed).getValue();
 110         long classReserved = Events.assertField(event, "classSpace.reserved").atLeast(classCommitted).getValue();
 111 
 112         Asserts.assertEquals(dataCommitted + classCommitted, totalCommitted, "Wrong committed memory");
 113         Asserts.assertEquals(dataUsed + classUsed, totalUsed, "Wrong used memory");
 114         Asserts.assertEquals(dataReserved + classReserved, totalReserved, "Wrong reserved memory");
 115     }
 116 
 117     private static int checkGcId(RecordedEvent event, int currGcId) {
 118         int gcId = Events.assertField(event, "gcId").getValue();
 119         String when = Events.assertField(event, "when").notEmpty().getValue();
 120         if ("Before GC".equals(when)) {
 121             Asserts.assertGreaterThan(gcId, currGcId, "gcId should be increasing");
 122         } else {
 123             Asserts.assertEquals(gcId, currGcId, "After should have same gcId as last Before event");
 124         }
 125         return gcId;
 126     }
 127 
 128     private static void checkHeapEventContent(RecordedEvent event) {
 129         checkVirtualSpace(event, "heapSpace");
 130         long heapUsed = Events.assertField(event, "heapUsed").atLeast(0L).getValue();
 131         long start = Events.assertField(event, "heapSpace.start").atLeast(0L).getValue();
 132         long committedEnd = Events.assertField(event, "heapSpace.committedEnd").above(start).getValue();
 133         Asserts.assertLessThanOrEqual(heapUsed, committedEnd- start, "used can not exceed size");
 134     }
 135 
 136     private static void checkG1EventContent(RecordedEvent event) {
 137         long edenUsedSize = Events.assertField(event, "edenUsedSize").atLeast(0L).getValue();
 138         long edenTotalSize = Events.assertField(event, "edenTotalSize").atLeast(0L).getValue();
 139         Asserts.assertLessThanOrEqual(edenUsedSize, edenTotalSize, "used can not exceed size");
 140         Events.assertField(event, "survivorUsedSize").atLeast(0L);
 141         Events.assertField(event, "numberOfRegions").atLeast(0);
 142     }
 143 
 144     private static void checkPSEventContent(RecordedEvent event) {
 145         checkVirtualSpace(event, "oldSpace");
 146         checkVirtualSpace(event, "youngSpace");
 147         checkSpace(event, "oldObjectSpace");
 148         checkSpace(event, "edenSpace");
 149         checkSpace(event, "fromSpace");
 150         checkSpace(event, "toSpace");
 151 
 152         checkPSYoungSizes(event);
 153         checkPSYoungStartEnd(event);
 154     }
 155 
 156     private static void checkPSYoungSizes(RecordedEvent event) {
 157         long youngSize = (long)Events.assertField(event, "youngSpace.committedEnd").getValue() -
 158                         (long)Events.assertField(event, "youngSpace.start").getValue();
 159         long edenSize = (long)Events.assertField(event, "edenSpace.end").getValue() -
 160                         (long)Events.assertField(event, "edenSpace.start").getValue();
 161         long fromSize = (long)Events.assertField(event, "fromSpace.end").getValue() -
 162                         (long)Events.assertField(event, "fromSpace.start").getValue();
 163         long toSize = (long)Events.assertField(event, "toSpace.end").getValue() -
 164                         (long)Events.assertField(event, "toSpace.start").getValue();
 165         Asserts.assertGreaterThanOrEqual(youngSize, edenSize + fromSize + toSize, "Young sizes don't match");
 166     }
 167 
 168     private static void checkPSYoungStartEnd(RecordedEvent event) {
 169         long oldEnd = Events.assertField(event, "oldSpace.reservedEnd").getValue();
 170         long youngStart = Events.assertField(event, "youngSpace.start").getValue();
 171         long youngEnd = Events.assertField(event, "youngSpace.committedEnd").getValue();
 172         long edenStart = Events.assertField(event, "edenSpace.start").getValue();
 173         long edenEnd = Events.assertField(event, "edenSpace.end").getValue();
 174         long fromStart = Events.assertField(event, "fromSpace.start").getValue();
 175         long fromEnd = Events.assertField(event, "fromSpace.end").getValue();
 176         long toStart = Events.assertField(event, "toSpace.start").getValue();
 177         long toEnd = Events.assertField(event, "toSpace.end").getValue();
 178         Asserts.assertEquals(oldEnd, youngStart, "Young should start where old ends");
 179         Asserts.assertEquals(youngStart, edenStart, "Eden should be placed first in young");
 180         if (fromStart < toStart) {
 181             // [eden][from][to]
 182             Asserts.assertGreaterThanOrEqual(fromStart, edenEnd, "From should start after eden");
 183             Asserts.assertLessThanOrEqual(fromEnd, toStart, "To should start after From");
 184             Asserts.assertLessThanOrEqual(toEnd, youngEnd, "To should start after From");
 185         } else {
 186             // [eden][to][from]
 187             Asserts.assertGreaterThanOrEqual(toStart, edenEnd, "From should start after eden");
 188             Asserts.assertLessThanOrEqual(toEnd, fromStart, "To should start after From");
 189             Asserts.assertLessThanOrEqual(fromEnd, youngEnd, "To should start after From");
 190         }
 191     }
 192 
 193     private static void checkVirtualSpace(RecordedEvent event, String structName) {
 194         long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue();
 195         long committedEnd = Events.assertField(event, structName + ".committedEnd").above(start).getValue();
 196         Events.assertField(event, structName + ".reservedEnd").atLeast(committedEnd);
 197         long committedSize = Events.assertField(event, structName + ".committedSize").atLeast(0L).getValue();
 198         Events.assertField(event, structName + ".reservedSize").atLeast(committedSize);
 199     }
 200 
 201     private static void checkSpace(RecordedEvent event, String structName) {
 202         long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue();
 203         long end = Events.assertField(event, structName + ".end").above(start).getValue();
 204         long used =  Events.assertField(event, structName + ".used").atLeast(0L).getValue();
 205         long size = Events.assertField(event, structName + ".size").atLeast(used).getValue();
 206         Asserts.assertEquals(size, end - start, "Size mismatch");
 207     }
 208 
 209     private static boolean checkCollectors(Recording recording, String expectedYoung, String expectedOld) throws Exception {
 210         for (RecordedEvent event : Events.fromRecording(recording)) {
 211             if (Events.isEventType(event, EventNames.GCConfiguration)) {
 212                 final String young = Events.assertField(event, "youngCollector").notEmpty().getValue();
 213                 final String old = Events.assertField(event, "oldCollector").notEmpty().getValue();
 214                 if (young.equals(expectedYoung) && old.equals(expectedOld)) {
 215                     return true;
 216                 }
 217                 // TODO: We treat wrong collector types as an error. Old test only warned. Not sure what is correct.
 218                 Asserts.fail(String.format("Wrong collector types: got('%s','%s'), expected('%s','%s')",
 219                 young, old, expectedYoung, expectedOld));
 220             }
 221         }
 222         Asserts.fail("Missing event type " + EventNames.GCConfiguration);
 223         return false;
 224     }
 225 }