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 TestPLABResize
  26  * @bug 8141278
  27  * @summary Test for PLAB resizing
  28  * @requires vm.gc=="G1" | vm.gc=="null"
  29  * @library /testlibrary /../../test/lib /
  30  * @modules java.management
  31  * @build ClassFileInstaller
  32  *        sun.hotspot.WhiteBox
  33  *        gc.g1.plab.lib.LogParser
  34  *        gc.g1.plab.lib.MemoryConsumer
  35  *        gc.g1.plab.lib.PLABUtils
  36  *        gc.g1.plab.lib.AppPLABResize
  37  * @run main ClassFileInstaller sun.hotspot.WhiteBox
  38  *                              sun.hotspot.WhiteBox$WhiteBoxPermission
  39  * @run main gc.g1.plab.TestPLABResize
  40  */
  41 package gc.g1.plab;
  42 
  43 import java.util.ArrayList;
  44 import java.util.Arrays;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.stream.Collectors;
  48 import java.io.PrintStream;
  49 
  50 import gc.g1.plab.lib.LogParser;
  51 import gc.g1.plab.lib.PLABUtils;
  52 import gc.g1.plab.lib.AppPLABResize;
  53 
  54 import jdk.test.lib.OutputAnalyzer;
  55 import jdk.test.lib.ProcessTools;
  56 
  57 /**
  58  * Test for PLAB resizing.
  59  */
  60 public class TestPLABResize {
  61 
  62     private static final int OBJECT_SIZE_SMALL = 10;
  63     private static final int OBJECT_SIZE_MEDIUM = 70;
  64     private static final int OBJECT_SIZE_HIGH = 150;
  65     private static final int GC_NUM_SMALL = 1;
  66     private static final int GC_NUM_MEDIUM = 3;
  67     private static final int GC_NUM_HIGH = 7;
  68     private static final int WASTE_PCT_SMALL = 10;
  69     private static final int WASTE_PCT_MEDIUM = 20;
  70     private static final int WASTE_PCT_HIGH = 30;
  71 
  72     private static final int ITERATIONS_SMALL = 3;
  73     private static final int ITERATIONS_MEDIUM = 5;
  74     private static final int ITERATIONS_HIGH = 8;
  75 
  76     private final static TestCase[] TEST_CASES = {
  77         new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_SMALL, GC_NUM_SMALL, ITERATIONS_MEDIUM),
  78         new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_HIGH, ITERATIONS_SMALL),
  79         new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH),
  80         new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM),
  81         new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL),
  82         new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH),
  83         new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM),
  84         new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL),
  85         new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH)
  86     };
  87 
  88     public static void main(String[] args) throws Throwable {
  89         for (TestCase testCase : TEST_CASES) {
  90             testCase.print(System.out);
  91             List<String> options = PLABUtils.prepareOptions(testCase.toOptions());
  92             options.add(AppPLABResize.class.getName());
  93             OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()]));
  94             if (out.getExitValue() != 0) {
  95                 System.out.println(out.getOutput());
  96                 throw new RuntimeException("Exit code is not 0");
  97             }
  98             checkResults(out.getOutput(), testCase);
  99         }
 100     }
 101 
 102     /**
 103      * Checks testing results.
 104      * Expected results - desired PLAB size is decreased and increased during promotion to Survivor.
 105      *
 106      * @param output - VM output
 107      * @param testCase
 108      */
 109     private static void checkResults(String output, TestCase testCase) {
 110         final LogParser log = new LogParser(output);
 111         final Map<Long, Map<LogParser.ReportType, Map<String, Long>>> entries = log.getEntries();
 112 
 113         final ArrayList<Long> plabSizes = entries.entrySet()
 114                 .stream()
 115                 .map(item -> {
 116                     return item.getValue()
 117                             .get(LogParser.ReportType.SURVIVOR_STATS)
 118                             .get("desired_plab_sz");
 119                 })
 120                 .collect(Collectors.toCollection(ArrayList::new));
 121 
 122         // Check that desired plab size was changed during iterations.
 123         // It should decrease during first half of iterations
 124         // and increase after.
 125         List<Long> decreasedPlabs = plabSizes.subList(testCase.getIterations(), testCase.getIterations() * 2);
 126         List<Long> increasedPlabs = plabSizes.subList(testCase.getIterations() * 2, testCase.getIterations() * 3);
 127 
 128         Long prev = decreasedPlabs.get(0);
 129         for (int index = 1; index < decreasedPlabs.size(); ++index) {
 130             Long current = decreasedPlabs.get(index);
 131             if (prev < current) {
 132                 System.out.println(output);
 133                 throw new RuntimeException("Test failed! Expect that previous PLAB size should be greater than current. Prev.size: " + prev + " Current size:" + current);
 134             }
 135             prev = current;
 136         }
 137 
 138         prev = increasedPlabs.get(0);
 139         for (int index = 1; index < increasedPlabs.size(); ++index) {
 140             Long current = increasedPlabs.get(index);
 141             if (prev > current) {
 142                 System.out.println(output);
 143                 throw new RuntimeException("Test failed! Expect that previous PLAB size should be less than current. Prev.size: " + prev + " Current size:" + current);
 144             }
 145             prev = current;
 146         }
 147 
 148         System.out.println("Test passed!");
 149     }
 150 
 151     /**
 152      * Description of one test case.
 153      */
 154     private static class TestCase {
 155 
 156         private final int wastePct;
 157         private final int chunkSize;
 158         private final int parGCThreads;
 159         private final int iterations;
 160 
 161         /**
 162          * @param wastePct
 163          * ParallelGCBufferWastePct
 164          * @param chunkSize
 165          * requested object size for memory consumption
 166          * @param parGCThreads
 167          * -XX:ParallelGCThreads
 168          * @param iterations
 169          *
 170          */
 171         public TestCase(int wastePct,
 172                 int chunkSize,
 173                 int parGCThreads,
 174                 int iterations
 175         ) {
 176             if (wastePct == 0 || chunkSize == 0 || parGCThreads == 0 || iterations == 0) {
 177                 throw new IllegalArgumentException("Parameters should not be 0");
 178             }
 179             this.wastePct = wastePct;
 180 
 181             this.chunkSize = chunkSize;
 182             this.parGCThreads = parGCThreads;
 183             this.iterations = iterations;
 184         }
 185 
 186         /**
 187          * Convert current TestCase to List of options.
 188          *
 189          * @return
 190          * List of options
 191          */
 192         public List<String> toOptions() {
 193             return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads,
 194                     "-XX:ParallelGCBufferWastePct=" + wastePct,
 195                     "-XX:+ResizePLAB",
 196                     "-Dthreads=" + parGCThreads,
 197                     "-Dchunk.size=" + chunkSize,
 198                     "-Diterations=" + iterations,
 199                     "-XX:NewSize=16m",
 200                     "-XX:MaxNewSize=16m"
 201             );
 202         }
 203 
 204         /**
 205          * Print details about test case.
 206          */
 207         public void print(PrintStream out) {
 208             out.println("Test case details:");
 209             out.println("  Parallel GC buffer waste pct : " + wastePct);
 210             out.println("  Chunk size : " + chunkSize);
 211             out.println("  Parallel GC threads : " + parGCThreads);
 212             out.println("  Iterations: " + iterations);
 213         }
 214 
 215         /**
 216          * @return iterations
 217          */
 218         public int getIterations() {
 219             return iterations;
 220         }
 221     }
 222 }