1 /*
2  * Copyright (c) 2016, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24  /*
25  * @test TestPLABPromotion
26  * @bug 8141278 8141141
27  * @summary Test PLAB promotion
28  * @requires vm.gc.G1
29  * @requires !vm.flightRecorder
30  * @library /test/lib /
31  * @modules java.base/jdk.internal.misc
32  * @modules java.management
33  * @build sun.hotspot.WhiteBox
34  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
35  *                              sun.hotspot.WhiteBox$WhiteBoxPermission
36  * @run main/timeout=240 gc.g1.plab.TestPLABPromotion
37  */
38 package gc.g1.plab;
39 
40 import java.util.List;
41 import java.util.Arrays;
42 import java.io.PrintStream;
43 
44 import gc.g1.plab.lib.AppPLABPromotion;
45 import gc.g1.plab.lib.LogParser;
46 import gc.g1.plab.lib.PLABUtils;
47 import gc.g1.plab.lib.PlabInfo;
48 
49 import jdk.test.lib.process.OutputAnalyzer;
50 import jdk.test.lib.process.ProcessTools;
51 
52 /**
53  * Test checks PLAB promotion of different size objects.
54  */
55 public class TestPLABPromotion {
56 
57     // GC ID with survivor PLAB statistics
58     private final static long GC_ID_SURVIVOR_STATS = 1l;
59     // GC ID with old PLAB statistics
60     private final static long GC_ID_OLD_STATS = 2l;
61 
62     private final static String PLAB_USED_FIELD_NAME = "used";
63     private final static String PLAB_DIRECT_ALLOCATED_FIELD_NAME = "direct allocated";
64     private final static List<String> FIELDS_TO_EXTRACT = Arrays.asList(PLAB_USED_FIELD_NAME, PLAB_DIRECT_ALLOCATED_FIELD_NAME);
65 
66     private static String output;
67 
68     // Allowable difference for memory consumption (percentage)
69     private final static long MEM_DIFFERENCE_PCT = 5;
70 
71     private static final int PLAB_SIZE_SMALL = 1024;
72     private static final int PLAB_SIZE_MEDIUM = 4096;
73     private static final int PLAB_SIZE_HIGH = 65536;
74     private static final int OBJECT_SIZE_SMALL = 10;
75     private static final int OBJECT_SIZE_MEDIUM = 100;
76     private static final int OBJECT_SIZE_HIGH = 1000;
77     private static final int GC_NUM_SMALL = 1;
78     private static final int GC_NUM_MEDIUM = 3;
79     private static final int GC_NUM_HIGH = 7;
80     private static final int WASTE_PCT_SMALL = 10;
81     private static final int WASTE_PCT_MEDIUM = 20;
82     private static final int WASTE_PCT_HIGH = 30;
83     private static final int YOUNG_SIZE_LOW = 16;
84     private static final int YOUNG_SIZE_HIGH = 64;
85     private static final boolean PLAB_FIXED = true;
86     private static final boolean PLAB_DYNAMIC = false;
87 
88     private final static TestCase[] TEST_CASES = {
89         // Test cases for unreachable object, PLAB size is fixed
90         new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, false, false),
91         new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, false, false),
92         // Test cases for reachable objects, PLAB size is fixed
93         new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
94         new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
95         new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, false),
96         new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
97         new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
98         new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
99         new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
100         new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
101         new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_HIGH, PLAB_FIXED, true, false),
102         // Test cases for unreachable object, PLAB size is not fixed
103         new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, false, false),
104         // Test cases for reachable objects, PLAB size is not fixed
105         new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true),
106         new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true),
107         new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, false),
108         new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true),
109         new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true),
110         new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true)
111     };
112 
main(String[] args)113     public static void main(String[] args) throws Throwable {
114 
115         for (TestCase testCase : TEST_CASES) {
116             // What we going to check.
117             testCase.print(System.out);
118             List<String> options = PLABUtils.prepareOptions(testCase.toOptions());
119             options.add(AppPLABPromotion.class.getName());
120             OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()]));
121             PLABUtils.commonCheck(out);
122             output = out.getOutput();
123             checkResults(testCase);
124         }
125     }
126 
checkResults(TestCase testCase)127     private static void checkResults(TestCase testCase) {
128         long plabAllocatedSurvivor;
129         long directAllocatedSurvivor;
130         long plabAllocatedOld;
131         long directAllocatedOld;
132         long memAllocated = testCase.getMemToFill();
133         LogParser logParser = new LogParser(output);
134 
135         PlabInfo survivorPlabInfo = logParser.getSpecifiedStats(GC_ID_SURVIVOR_STATS, LogParser.ReportType.SURVIVOR_STATS, FIELDS_TO_EXTRACT);
136         PlabInfo oldPlabInfo = logParser.getSpecifiedStats(GC_ID_OLD_STATS, LogParser.ReportType.OLD_STATS, FIELDS_TO_EXTRACT);
137 
138         checkFields(survivorPlabInfo);
139         checkFields(oldPlabInfo);
140 
141         plabAllocatedSurvivor = survivorPlabInfo.get(PLAB_USED_FIELD_NAME);
142         directAllocatedSurvivor = survivorPlabInfo.get(PLAB_DIRECT_ALLOCATED_FIELD_NAME);
143         plabAllocatedOld = oldPlabInfo.get(PLAB_USED_FIELD_NAME);
144         directAllocatedOld = oldPlabInfo.get(PLAB_DIRECT_ALLOCATED_FIELD_NAME);
145 
146         System.out.printf("Survivor PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated);
147         System.out.printf("Old      PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedOld, directAllocatedOld, memAllocated);
148 
149         // Unreachable objects case
150         if (testCase.isDeadObjectCase()) {
151             checkDeadObjectsPromotion(plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated);
152             checkDeadObjectsPromotion(plabAllocatedOld, directAllocatedOld, memAllocated);
153 
154         } else {
155             // Live objects case
156             if (testCase.isPromotedByPLAB()) {
157                 checkLiveObjectsPromotion(plabAllocatedSurvivor, memAllocated, "Expect that Survivor PLAB allocation are similar to all mem consumed");
158                 checkLiveObjectsPromotion(plabAllocatedOld, memAllocated, "Expect that Old PLAB allocation are similar to all mem consumed");
159             } else {
160                 // All big objects should be directly allocated
161                 checkLiveObjectsPromotion(directAllocatedSurvivor, memAllocated, "Expect that Survivor direct allocation are similar to all mem consumed");
162                 checkLiveObjectsPromotion(directAllocatedOld, memAllocated, "Expect that Old direct allocation are similar to all mem consumed");
163             }
164 
165             checkTotalPromotion(plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated, "Expect that Survivor gen total allocation are similar to all mem consumed");
166             checkTotalPromotion(plabAllocatedOld, directAllocatedOld, memAllocated, "Expect that Old gen total allocation are similar to all mem consumed");
167         }
168         System.out.println("Test passed!");
169     }
170 
checkTotalPromotion(long plabAllocatedSurvivor, long directAllocatedSurvivor, long memAllocated, String exceptionMessage)171     private static void checkTotalPromotion(long plabAllocatedSurvivor, long directAllocatedSurvivor, long memAllocated, String exceptionMessage) {
172         // All promoted objects size should be similar to all consumed memory
173         if (!checkDifferenceRatio(plabAllocatedSurvivor + directAllocatedSurvivor, memAllocated)) {
174             System.out.println(output);
175             throw new RuntimeException(exceptionMessage);
176         }
177     }
178 
179     /**
180      * Checks that live objects were promoted as expected.
181      * @param plabAllocated
182      * @param totalMemAllocated
183      * @param exceptionMessage
184      */
checkLiveObjectsPromotion(long plabAllocated, long totalMemAllocated, String exceptionMessage)185     private static void checkLiveObjectsPromotion(long plabAllocated, long totalMemAllocated, String exceptionMessage) {
186         // All live small objects should be promoted using PLAB
187         if (!checkDifferenceRatio(plabAllocated, totalMemAllocated)) {
188             System.out.println(output);
189             throw new RuntimeException(exceptionMessage);
190         }
191     }
192 
193     /**
194      * Checks that dead objects are not promoted.
195      * @param plabPromoted promoted by PLAB
196      * @param directlyPromoted
197      * @param memoryAllocated total memory allocated
198      */
checkDeadObjectsPromotion(long plabPromoted, long directlyPromoted, long memoryAllocated)199     private static void checkDeadObjectsPromotion(long plabPromoted, long directlyPromoted, long memoryAllocated) {
200         // No dead objects should be promoted
201         if (!(checkRatio(plabPromoted, memoryAllocated) && checkRatio(directlyPromoted, memoryAllocated))) {
202             System.out.println(output);
203             throw new RuntimeException("Unreachable objects should not be allocated using PLAB or directly allocated to Survivor/Old");
204         }
205     }
206 
207     /**
208      * Checks that PLAB statistics contains expected fields.
209      * @param info
210      */
checkFields(PlabInfo info)211     private static void checkFields(PlabInfo info) {
212         if (!info.checkFields(FIELDS_TO_EXTRACT)) {
213             System.out.println(output);
214             throw new RuntimeException("PLAB log does not contain expected fields");
215         }
216     }
217 
218     /**
219      * Returns true if checkedValue is less than MEM_DIFFERENCE_PCT percent of controlValue.
220      *
221      * @param checkedValue - checked value
222      * @param controlValue - referent value
223      * @return true if checkedValue is less than MEM_DIFFERENCE_PCT percent of controlValue
224      */
checkRatio(long checkedValue, long controlValue)225     private static boolean checkRatio(long checkedValue, long controlValue) {
226         return (Math.abs(checkedValue) / controlValue) * 100L < MEM_DIFFERENCE_PCT;
227     }
228 
229     /**
230      * Returns true if difference of checkedValue and controlValue is less than
231      * MEM_DIFFERENCE_PCT percent of controlValue.
232      *
233      * @param checkedValue - checked value
234      * @param controlValue - referent value
235      * @return true if difference of checkedValue and controlValue is less than
236      * MEM_DIFFERENCE_PCT percent of controlValue
237      */
checkDifferenceRatio(long checkedValue, long controlValue)238     private static boolean checkDifferenceRatio(long checkedValue, long controlValue) {
239         return (Math.abs(checkedValue - controlValue) / controlValue) * 100L < MEM_DIFFERENCE_PCT;
240     }
241 
242     /**
243      * Description of one test case.
244      */
245     private static class TestCase {
246 
247         private final int wastePct;
248         private final int plabSize;
249         private final int chunkSize;
250         private final int parGCThreads;
251         private final int edenSize;
252         private final boolean plabIsFixed;
253         private final boolean objectsAreReachable;
254         private final boolean promotedByPLAB;
255 
256         /**
257          * @param wastePct
258          * ParallelGCBufferWastePct
259          * @param plabSize
260          * -XX:OldPLABSize and -XX:YoungPLABSize
261          * @param chunkSize
262          * requested object size for memory consumption
263          * @param parGCThreads
264          * -XX:ParallelGCThreads
265          * @param edenSize
266          * NewSize and MaxNewSize
267          * @param plabIsFixed
268          * Use dynamic PLAB or fixed size PLAB
269          * @param objectsAreReachable
270          * true - allocate live objects
271          * false - allocate unreachable objects
272          * @param promotedByPLAB
273          * true - we expect to see PLAB allocation during promotion
274          * false - objects will be directly allocated during promotion
275          */
TestCase(int wastePct, int plabSize, int chunkSize, int parGCThreads, int edenSize, boolean plabIsFixed, boolean objectsAreReachable, boolean promotedByPLAB )276         public TestCase(int wastePct,
277                 int plabSize,
278                 int chunkSize,
279                 int parGCThreads,
280                 int edenSize,
281                 boolean plabIsFixed,
282                 boolean objectsAreReachable,
283                 boolean promotedByPLAB
284         ) {
285             if (wastePct == 0 || plabSize == 0 || chunkSize == 0 || parGCThreads == 0 || edenSize == 0) {
286                 throw new IllegalArgumentException("Parameters should not be 0");
287             }
288             this.wastePct = wastePct;
289             this.plabSize = plabSize;
290             this.chunkSize = chunkSize;
291             this.parGCThreads = parGCThreads;
292             this.edenSize = edenSize;
293             this.plabIsFixed = plabIsFixed;
294             this.objectsAreReachable = objectsAreReachable;
295             this.promotedByPLAB = promotedByPLAB;
296         }
297 
298         /**
299          * Convert current TestCase to List of options.
300          * Assume test will fill half of existed eden.
301          *
302          * @return
303          * List of options
304          */
toOptions()305         public List<String> toOptions() {
306             return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads,
307                     "-XX:ParallelGCBufferWastePct=" + wastePct,
308                     "-XX:OldPLABSize=" + plabSize,
309                     "-XX:YoungPLABSize=" + plabSize,
310                     "-XX:" + (plabIsFixed ? "-" : "+") + "ResizePLAB",
311                     "-Dchunk.size=" + chunkSize,
312                     "-Dreachable=" + objectsAreReachable,
313                     "-XX:NewSize=" + edenSize + "m",
314                     "-XX:MaxNewSize=" + edenSize + "m",
315                     "-Dmem.to.fill=" + getMemToFill()
316             );
317         }
318 
319         /**
320          * Print details about test case.
321          */
print(PrintStream out)322         public void print(PrintStream out) {
323             boolean expectPLABAllocation = promotedByPLAB && objectsAreReachable;
324             boolean expectDirectAllocation = (!promotedByPLAB) && objectsAreReachable;
325 
326             out.println("Test case details:");
327             out.println("  Young gen size : " + edenSize + "M");
328             out.println("  Predefined PLAB size : " + plabSize);
329             out.println("  Parallel GC buffer waste pct : " + wastePct);
330             out.println("  Chunk size : " + chunkSize);
331             out.println("  Parallel GC threads : " + parGCThreads);
332             out.println("  Objects are created : " + (objectsAreReachable ? "reachable" : "unreachable"));
333             out.println("  PLAB size is fixed: " + (plabIsFixed ? "yes" : "no"));
334             out.println("Test expectations:");
335             out.println("  PLAB allocation : " + (expectPLABAllocation ? "expected" : "unexpected"));
336             out.println("  Direct allocation : " + (expectDirectAllocation ? "expected" : "unexpected"));
337         }
338 
339         /**
340          * @return
341          * true if we expect PLAB allocation
342          * false if no
343          */
isPromotedByPLAB()344         public boolean isPromotedByPLAB() {
345             return promotedByPLAB;
346         }
347 
348         /**
349          * @return
350          * true if it is test case for unreachable objects
351          * false for live objects
352          */
isDeadObjectCase()353         public boolean isDeadObjectCase() {
354             return !objectsAreReachable;
355         }
356 
357         /**
358          * Returns amount of memory to fill
359          *
360          * @return amount of memory
361          */
getMemToFill()362         public long getMemToFill() {
363             return (long) (edenSize) * 1024l * 1024l / 2;
364         }
365     }
366 }
367