1 /*
2  * Copyright (c) 2014, 2019, 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 package jdk.jfr.event.gc.detailed;
26 
27 import static jdk.test.lib.Asserts.assertEquals;
28 import static jdk.test.lib.Asserts.assertNotEquals;
29 import static jdk.test.lib.Asserts.assertNotNull;
30 import static jdk.test.lib.Asserts.assertTrue;
31 
32 import java.lang.management.GarbageCollectorMXBean;
33 import java.lang.management.ManagementFactory;
34 import java.util.List;
35 import java.util.concurrent.ThreadLocalRandom;
36 
37 import jdk.jfr.Recording;
38 import jdk.jfr.consumer.RecordedEvent;
39 import jdk.test.lib.jfr.EventNames;
40 import jdk.test.lib.jfr.Events;
41 
42 /**
43  * This is a base class for testing Promotion Events
44  *
45  * See TestPromotionEventWith* for actual test classes. Tests must set
46  * -XX:MaxTenuringThreshold=5 -XX:InitialTenuringThreshold=5
47  *
48  * @author Staffan Friberg
49  */
50 public class PromotionEvent {
51 
52     private final static String PROMOTION_IN_NEW_PLAB_NAME = EventNames.PromoteObjectInNewPLAB;
53     private final static String PROMOTION_OUTSIDE_PLAB_NAME = EventNames.PromoteObjectOutsidePLAB;
54 
55     // This value needs to match the command line option set above
56     private final static int MAX_TENURING_THRESHOLD = 5;
57 
58     // Keep track of the collection count just before and after JFR recording
59     private static int startGCCount = 0;
60 
61     // Dummy objects to keep things alive and assure allocation happens
62     public static Object dummy;
63     public static Object[] keepAlive = new Object[128];
64     public static Object[] age = new Object[128];
65 
test()66     public static void test() throws Exception {
67         GarbageCollectorMXBean ycBean = null;
68 
69         List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
70         for (GarbageCollectorMXBean gcBean : gcBeans) {
71             if ("PS Scavenge".equals(gcBean.getName())
72                     || "G1 Young Generation".equals(gcBean.getName())) {
73                 ycBean = gcBean;
74             }
75 
76             if (ycBean != null) {
77                 break;
78             }
79         }
80 
81         if (ycBean == null) {
82             assertNotNull(ycBean, "Test failed since the MXBean for the Young Collector could not be found.");
83             return; // To remove IDE warning
84         }
85 
86         System.gc(); // Clear nusery before recording
87 
88         // Get total GC count before recording
89         for (GarbageCollectorMXBean gcBean : gcBeans) {
90             startGCCount += gcBean.getCollectionCount();
91         }
92 
93         Recording recording = new Recording();
94         recording.enable(PROMOTION_IN_NEW_PLAB_NAME);
95         recording.enable(PROMOTION_OUTSIDE_PLAB_NAME);
96         recording.start();
97 
98         byte[] largeBytes = new byte[1024 * 10];
99         byte[] smallBytes = new byte[64];
100 
101         // Some large strings to keep alive for tenuring
102         for (int i = 0; i < keepAlive.length / 2; i++) {
103             ThreadLocalRandom.current().nextBytes(largeBytes);
104             keepAlive[i] = new String(largeBytes);
105         }
106 
107         // Some small strings to keep alive for tenuring
108         for (int i = keepAlive.length / 2; i < keepAlive.length; i++) {
109             ThreadLocalRandom.current().nextBytes(smallBytes);
110             keepAlive[i] = new String(smallBytes);
111         }
112 
113         // Allocate temp data to force GCs until we have promoted the live data
114         for (int gcCount = 0; gcCount < MAX_TENURING_THRESHOLD * 2; gcCount++) {
115             long currentGCCount = ycBean.getCollectionCount();
116 
117             // some large strings to age
118             for (int i = 0; i < age.length / 2; i++) {
119                 ThreadLocalRandom.current().nextBytes(largeBytes);
120                 age[i] = new String(largeBytes);
121             }
122 
123             // Some small strings to age
124             for (int i = age.length / 2; i < age.length; i++) {
125                 ThreadLocalRandom.current().nextBytes(smallBytes);
126                 age[i] = new String(smallBytes);
127             }
128 
129             while (ycBean.getCollectionCount() <= currentGCCount + 3) {
130                 ThreadLocalRandom.current().nextBytes(smallBytes);
131                 dummy = new String(smallBytes);
132             }
133         }
134 
135         recording.stop();
136 
137         List<RecordedEvent> events = Events.fromRecording(recording);
138 
139         verifyPromotionSampleEvents(events);
140 
141         recording.close();
142     }
143 
verifyPromotionSampleEvents(List<RecordedEvent> events)144     private static void verifyPromotionSampleEvents(List<RecordedEvent> events)
145             throws Exception {
146 
147         boolean objectWasPromotedInNewPLAB = false;
148         boolean objectPromotedInNewPLABWasAged = false;
149         boolean objectPromotedInNewPLABWasTenured = false;
150         boolean objectWasPromotedOutsidePLAB = false;
151         boolean objectPromotedOutsidePLABWasAged = false;
152         boolean objectPromotedOutsidePLABWasTenured = false;
153 
154         Events.hasEvents(events);
155 
156         for (RecordedEvent event : events) {
157             // Read all common fields
158             Events.assertField(event, "gcId").atLeast(startGCCount).getValue();
159             String className = (event.getEventType()).getName().toString();
160             Events.assertField(event, "tenuringAge").atLeast(0).atMost(MAX_TENURING_THRESHOLD).getValue();
161             Boolean tenured = Events.assertField(event, "tenured").getValue();
162             Long objectSize = Events.assertField(event, "objectSize").above(0L).getValue();
163 
164             // Verify Class Name
165             assertNotNull(className, "Class name is null. Event: " + event);
166             assertNotEquals(className.length(), 0, "Class name is of zero length. Event: " + event);
167 
168             // Verify PLAB size and direct allocation
169             if (PROMOTION_IN_NEW_PLAB_NAME.equals(event.getEventType().getName())) {
170                 // Read event specific fields
171                 Long plabSize = Events.assertField(event, "plabSize").above(0L).getValue();
172                 assertTrue(plabSize >= objectSize, "PLAB size is smaller than object size. Event: " + event);
173                 objectWasPromotedInNewPLAB = true;
174                 // Verify tenured is hard to do as objects might be tenured earlier than the max threshold
175                 // but at least verify that we got the field set at least once during the test
176                 if (tenured) {
177                     objectPromotedInNewPLABWasTenured = true;
178                 } else {
179                     objectPromotedInNewPLABWasAged = true;
180                 }
181             } else if (PROMOTION_OUTSIDE_PLAB_NAME.equals(event.getEventType().getName())) {
182                 objectWasPromotedOutsidePLAB = true;
183                 // Verify tenured is hard to do as objects might be tenured earlier than the max threshold
184                 // but at least verify that we got the field set at least once during the test
185                 if (tenured) {
186                     objectPromotedOutsidePLABWasTenured = true;
187                 } else {
188                     objectPromotedOutsidePLABWasAged = true;
189                 }
190             } else {
191                 assertEquals(event.getEventType().getName(), "Unreachable...", "Got wrong type of event " + event);
192             }
193 
194         }
195 
196         // Verify that at least one event of these types occured during test
197         assertTrue(objectWasPromotedInNewPLAB, "No object in new plab was promoted in test");
198         assertTrue(objectPromotedInNewPLABWasAged, "No object in new plab was aged in test");
199         assertTrue(objectPromotedInNewPLABWasTenured, "No object in new plab was tenured in test");
200         assertTrue(objectWasPromotedOutsidePLAB, "No object outside plab was promoted in test");
201         assertTrue(objectPromotedOutsidePLABWasAged, "No object outside plab was aged in test");
202         assertTrue(objectPromotedOutsidePLABWasTenured, "No object outside plab was tenured in test");
203     }
204 }
205