blob: c3fa2e9ae9d13fdf9b363bbdce27db23e7472f81 [file] [log] [blame]
/*
* 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");
}
}