| /* |
| * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.jfr.event.gc.heapsummary; |
| |
| import java.time.Duration; |
| import java.util.List; |
| |
| import jdk.jfr.Recording; |
| import jdk.jfr.consumer.RecordedEvent; |
| import jdk.test.lib.Asserts; |
| import jdk.test.lib.jfr.EventNames; |
| import jdk.test.lib.jfr.Events; |
| import jdk.test.lib.jfr.GCHelper; |
| |
| public class HeapSummaryEventAllGcs { |
| |
| public static void test(String expectedYoungCollector, String expectedOldCollector) throws Exception { |
| Recording recording = new Recording(); |
| recording.enable(EventNames.GCConfiguration); |
| recording.enable(EventNames.GCHeapSummary); |
| recording.enable(EventNames.PSHeapSummary); |
| recording.enable(EventNames.MetaspaceSummary).withThreshold(Duration.ofMillis(0)); |
| |
| recording.start(); |
| // To eliminate the risk of being in the middle of a GC when the recording starts/stops, |
| // we run 5 System.gc() and ignores the first and last GC. |
| GCHelper.callSystemGc(5, true); |
| recording.stop(); |
| |
| if (!checkCollectors(recording, expectedYoungCollector, expectedOldCollector)) { |
| return; |
| } |
| List<RecordedEvent> events = GCHelper.removeFirstAndLastGC(Events.fromRecording(recording)); |
| for (RecordedEvent event : events) { |
| System.out.println("Event:" + event); |
| } |
| |
| Asserts.assertFalse(events.isEmpty(), "Expected at least one event."); |
| Asserts.assertEquals(events.size() % 2, 0, "Events should come in pairs"); |
| |
| int lastHeapGcId = -1; |
| int lastPSGcId = -1; |
| int lastMetaspaceGcId = -1; |
| |
| for (RecordedEvent event : events) { |
| final String eventName = event.getEventType().getName(); |
| switch (eventName) { |
| case EventNames.GCHeapSummary: |
| lastHeapGcId = checkGcId(event, lastHeapGcId); |
| checkHeapEventContent(event); |
| break; |
| case EventNames.PSHeapSummary: |
| lastPSGcId = checkGcId(event, lastPSGcId); |
| checkPSEventContent(event); |
| break; |
| case EventNames.MetaspaceSummary: |
| lastMetaspaceGcId = checkGcId(event, lastMetaspaceGcId); |
| checkMetaspaceEventContent(event); |
| break; |
| default: |
| System.out.println("Failed event: " + event); |
| Asserts.fail("Unknown event type: " + eventName); |
| } |
| } |
| |
| // Sanity check. Not complete. |
| Asserts.assertEquals(lastHeapGcId, lastMetaspaceGcId, "Should have gotten perm gen events for all GCs"); |
| } |
| |
| private static void checkMetaspaceEventContent(RecordedEvent event) { |
| long totalUsed = Events.assertField(event, "metaspace.used").atLeast(0L).getValue(); |
| long totalCommitted = Events.assertField(event, "metaspace.committed").atLeast(totalUsed).getValue(); |
| long totalReserved = Events.assertField(event, "metaspace.reserved").atLeast(totalCommitted).getValue(); |
| |
| long dataUsed = Events.assertField(event, "dataSpace.used").atLeast(0L).getValue(); |
| long dataCommitted = Events.assertField(event, "dataSpace.committed").atLeast(dataUsed).getValue(); |
| long dataReserved = Events.assertField(event, "dataSpace.reserved").atLeast(dataCommitted).getValue(); |
| |
| long classUsed = Events.assertField(event, "classSpace.used").atLeast(0L).getValue(); |
| long classCommitted = Events.assertField(event, "classSpace.committed").atLeast(classUsed).getValue(); |
| long classReserved = Events.assertField(event, "classSpace.reserved").atLeast(classCommitted).getValue(); |
| |
| Asserts.assertEquals(dataCommitted + classCommitted, totalCommitted, "Wrong committed memory"); |
| Asserts.assertEquals(dataUsed + classUsed, totalUsed, "Wrong used memory"); |
| Asserts.assertEquals(dataReserved + classReserved, totalReserved, "Wrong reserved memory"); |
| } |
| |
| private static int checkGcId(RecordedEvent event, int currGcId) { |
| int gcId = Events.assertField(event, "gcId").getValue(); |
| String when = Events.assertField(event, "when").notEmpty().getValue(); |
| if ("Before GC".equals(when)) { |
| Asserts.assertGreaterThan(gcId, currGcId, "gcId should be increasing"); |
| } else { |
| Asserts.assertEquals(gcId, currGcId, "After should have same gcId as last Before event"); |
| } |
| return gcId; |
| } |
| |
| private static void checkHeapEventContent(RecordedEvent event) { |
| checkVirtualSpace(event, "heapSpace"); |
| long heapUsed = Events.assertField(event, "heapUsed").atLeast(0L).getValue(); |
| long start = Events.assertField(event, "heapSpace.start").atLeast(0L).getValue(); |
| long committedEnd = Events.assertField(event, "heapSpace.committedEnd").above(start).getValue(); |
| Asserts.assertLessThanOrEqual(heapUsed, committedEnd- start, "used can not exceed size"); |
| } |
| |
| private static void checkPSEventContent(RecordedEvent event) { |
| checkVirtualSpace(event, "oldSpace"); |
| checkVirtualSpace(event, "youngSpace"); |
| checkSpace(event, "oldObjectSpace"); |
| checkSpace(event, "edenSpace"); |
| checkSpace(event, "fromSpace"); |
| checkSpace(event, "toSpace"); |
| |
| checkPSYoungSizes(event); |
| checkPSYoungStartEnd(event); |
| } |
| |
| private static void checkPSYoungSizes(RecordedEvent event) { |
| long youngSize = (long)Events.assertField(event, "youngSpace.committedEnd").getValue() - |
| (long)Events.assertField(event, "youngSpace.start").getValue(); |
| long edenSize = (long)Events.assertField(event, "edenSpace.end").getValue() - |
| (long)Events.assertField(event, "edenSpace.start").getValue(); |
| long fromSize = (long)Events.assertField(event, "fromSpace.end").getValue() - |
| (long)Events.assertField(event, "fromSpace.start").getValue(); |
| long toSize = (long)Events.assertField(event, "toSpace.end").getValue() - |
| (long)Events.assertField(event, "toSpace.start").getValue(); |
| Asserts.assertGreaterThanOrEqual(youngSize, edenSize + fromSize + toSize, "Young sizes don't match"); |
| } |
| |
| private static void checkPSYoungStartEnd(RecordedEvent event) { |
| long oldEnd = Events.assertField(event, "oldSpace.reservedEnd").getValue(); |
| long youngStart = Events.assertField(event, "youngSpace.start").getValue(); |
| long youngEnd = Events.assertField(event, "youngSpace.committedEnd").getValue(); |
| long edenStart = Events.assertField(event, "edenSpace.start").getValue(); |
| long edenEnd = Events.assertField(event, "edenSpace.end").getValue(); |
| long fromStart = Events.assertField(event, "fromSpace.start").getValue(); |
| long fromEnd = Events.assertField(event, "fromSpace.end").getValue(); |
| long toStart = Events.assertField(event, "toSpace.start").getValue(); |
| long toEnd = Events.assertField(event, "toSpace.end").getValue(); |
| Asserts.assertEquals(oldEnd, youngStart, "Young should start where old ends"); |
| Asserts.assertEquals(youngStart, edenStart, "Eden should be placed first in young"); |
| if (fromStart < toStart) { |
| // [eden][from][to] |
| Asserts.assertGreaterThanOrEqual(fromStart, edenEnd, "From should start after eden"); |
| Asserts.assertLessThanOrEqual(fromEnd, toStart, "To should start after From"); |
| Asserts.assertLessThanOrEqual(toEnd, youngEnd, "To should start after From"); |
| } else { |
| // [eden][to][from] |
| Asserts.assertGreaterThanOrEqual(toStart, edenEnd, "From should start after eden"); |
| Asserts.assertLessThanOrEqual(toEnd, fromStart, "To should start after From"); |
| Asserts.assertLessThanOrEqual(fromEnd, youngEnd, "To should start after From"); |
| } |
| } |
| |
| private static void checkVirtualSpace(RecordedEvent event, String structName) { |
| long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue(); |
| long committedEnd = Events.assertField(event, structName + ".committedEnd").above(start).getValue(); |
| Events.assertField(event, structName + ".reservedEnd").atLeast(committedEnd); |
| long committedSize = Events.assertField(event, structName + ".committedSize").atLeast(0L).getValue(); |
| Events.assertField(event, structName + ".reservedSize").atLeast(committedSize); |
| } |
| |
| private static void checkSpace(RecordedEvent event, String structName) { |
| long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue(); |
| long end = Events.assertField(event, structName + ".end").above(start).getValue(); |
| long used = Events.assertField(event, structName + ".used").atLeast(0L).getValue(); |
| long size = Events.assertField(event, structName + ".size").atLeast(used).getValue(); |
| Asserts.assertEquals(size, end - start, "Size mismatch"); |
| } |
| |
| private static boolean checkCollectors(Recording recording, String expectedYoung, String expectedOld) throws Exception { |
| for (RecordedEvent event : Events.fromRecording(recording)) { |
| if (Events.isEventType(event, EventNames.GCConfiguration)) { |
| final String young = Events.assertField(event, "youngCollector").notEmpty().getValue(); |
| final String old = Events.assertField(event, "oldCollector").notEmpty().getValue(); |
| if (young.equals(expectedYoung) && old.equals(expectedOld)) { |
| return true; |
| } |
| // TODO: We treat wrong collector types as an error. Old test only warned. Not sure what is correct. |
| Asserts.fail(String.format("Wrong collector types: got('%s','%s'), expected('%s','%s')", |
| young, old, expectedYoung, expectedOld)); |
| } |
| } |
| Asserts.fail("Missing event type " + EventNames.GCConfiguration); |
| return false; |
| } |
| } |