--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/gc/detailed/PromotionEvent.java Tue May 15 20:24:34 2018 +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<GarbageCollectorMXBean> 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<RecordedEvent> events = Events.fromRecording(recording);
+
+ verifyPromotionSampleEvents(events);
+
+ recording.close();
+ }
+
+ private static void verifyPromotionSampleEvents(List<RecordedEvent> 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");
+ }
+}