--- /dev/null 2017-11-09 09:38:01.297999907 +0100 +++ new/test/jdk/jdk/jfr/event/gc/detailed/PromotionEvent.java 2018-04-09 17:46:13.445451760 +0200 @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2014, 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.detailed; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNotEquals; +import static jdk.test.lib.Asserts.assertNotNull; +import static jdk.test.lib.Asserts.assertTrue; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * This is a base class for testing Promotion Events + * + * See TestPromotionEventWith* for actual test classes. Tests must set + * -XX:MaxTenuringThreshold=5 -XX:InitialTenuringThreshold=5 + * + * @author Staffan Friberg + */ +public class PromotionEvent { + + private final static String PROMOTION_IN_NEW_PLAB_NAME = EventNames.PromoteObjectInNewPLAB; + private final static String PROMOTION_OUTSIDE_PLAB_NAME = EventNames.PromoteObjectOutsidePLAB; + + // This value needs to match the command line option set above + private final static int MAX_TENURING_THRESHOLD = 5; + + // Keep track of the collection count just before and after JFR recording + private static int startGCCount = 0; + + // Dummy objects to keep things alive and assure allocation happens + public static Object dummy; + public static Object[] keepAlive = new Object[128]; + public static Object[] age = new Object[128]; + + public static void test() throws Exception { + GarbageCollectorMXBean ycBean = null; + + List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean gcBean : gcBeans) { + if ("PS Scavenge".equals(gcBean.getName()) + || "G1 Young Generation".equals(gcBean.getName()) + || ("ParNew".equals(gcBean.getName()))) { + ycBean = gcBean; + } + + if (ycBean != null) { + break; + } + } + + if (ycBean == null) { + assertNotNull(ycBean, "Test failed since the MXBean for the Young Collector could not be found."); + return; // To remove IDE warning + } + + System.gc(); // Clear nusery before recording + + // Get total GC count before recording + for (GarbageCollectorMXBean gcBean : gcBeans) { + startGCCount += gcBean.getCollectionCount(); + } + + Recording recording = new Recording(); + recording.enable(PROMOTION_IN_NEW_PLAB_NAME); + recording.enable(PROMOTION_OUTSIDE_PLAB_NAME); + recording.start(); + + byte[] largeBytes = new byte[1024 * 10]; + byte[] smallBytes = new byte[64]; + + // Some large strings to keep alive for tenuring + for (int i = 0; i < keepAlive.length / 2; i++) { + ThreadLocalRandom.current().nextBytes(largeBytes); + keepAlive[i] = new String(largeBytes); + } + + // Some small strings to keep alive for tenuring + for (int i = keepAlive.length / 2; i < keepAlive.length; i++) { + ThreadLocalRandom.current().nextBytes(smallBytes); + keepAlive[i] = new String(smallBytes); + } + + // Allocate temp data to force GCs until we have promoted the live data + for (int gcCount = 0; gcCount < MAX_TENURING_THRESHOLD * 2; gcCount++) { + long currentGCCount = ycBean.getCollectionCount(); + + // some large strings to age + for (int i = 0; i < age.length / 2; i++) { + ThreadLocalRandom.current().nextBytes(largeBytes); + age[i] = new String(largeBytes); + } + + // Some small strings to age + for (int i = age.length / 2; i < age.length; i++) { + ThreadLocalRandom.current().nextBytes(smallBytes); + age[i] = new String(smallBytes); + } + + while (ycBean.getCollectionCount() <= currentGCCount + 3) { + ThreadLocalRandom.current().nextBytes(smallBytes); + dummy = new String(smallBytes); + } + } + + recording.stop(); + + List events = Events.fromRecording(recording); + + verifyPromotionSampleEvents(events); + + recording.close(); + } + + private static void verifyPromotionSampleEvents(List events) + throws Exception { + + boolean objectWasPromotedInNewPLAB = false; + boolean objectPromotedInNewPLABWasAged = false; + boolean objectPromotedInNewPLABWasTenured = false; + boolean objectWasPromotedOutsidePLAB = false; + boolean objectPromotedOutsidePLABWasAged = false; + boolean objectPromotedOutsidePLABWasTenured = false; + + Events.hasEvents(events); + + for (RecordedEvent event : events) { + // Read all common fields + Events.assertField(event, "gcId").atLeast(startGCCount).getValue(); + String className = (event.getEventType()).getName().toString(); + Events.assertField(event, "tenuringAge").atLeast(0).atMost(MAX_TENURING_THRESHOLD).getValue(); + Boolean tenured = Events.assertField(event, "tenured").getValue(); + Long objectSize = Events.assertField(event, "objectSize").above(0L).getValue(); + + // Verify Class Name + assertNotNull(className, "Class name is null. Event: " + event); + assertNotEquals(className.length(), 0, "Class name is of zero length. Event: " + event); + + // Verify PLAB size and direct allocation + if (PROMOTION_IN_NEW_PLAB_NAME.equals(event.getEventType().getName())) { + // Read event specific fields + Long plabSize = Events.assertField(event, "plabSize").above(0L).getValue(); + assertTrue(plabSize >= objectSize, "PLAB size is smaller than object size. Event: " + event); + objectWasPromotedInNewPLAB = true; + // Verify tenured is hard to do as objects might be tenured earlier than the max threshold + // but at least verify that we got the field set at least once during the test + if (tenured) { + objectPromotedInNewPLABWasTenured = true; + } else { + objectPromotedInNewPLABWasAged = true; + } + } else if (PROMOTION_OUTSIDE_PLAB_NAME.equals(event.getEventType().getName())) { + objectWasPromotedOutsidePLAB = true; + // Verify tenured is hard to do as objects might be tenured earlier than the max threshold + // but at least verify that we got the field set at least once during the test + if (tenured) { + objectPromotedOutsidePLABWasTenured = true; + } else { + objectPromotedOutsidePLABWasAged = true; + } + } else { + assertEquals(event.getEventType().getName(), "Unreachable...", "Got wrong type of event " + event); + } + + } + + // Verify that at least one event of these types occured during test + assertTrue(objectWasPromotedInNewPLAB, "No object in new plab was promoted in test"); + assertTrue(objectPromotedInNewPLABWasAged, "No object in new plab was aged in test"); + assertTrue(objectPromotedInNewPLABWasTenured, "No object in new plab was tenured in test"); + assertTrue(objectWasPromotedOutsidePLAB, "No object outside plab was promoted in test"); + assertTrue(objectPromotedOutsidePLABWasAged, "No object outside plab was aged in test"); + assertTrue(objectPromotedOutsidePLABWasTenured, "No object outside plab was tenured in test"); + } +}