1 /*
   2  * Copyright (c) 2015, 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.Storage
  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.Pair;
  56 import jdk.test.lib.ProcessTools;
  57 
  58 /**
  59  * Test for PLAB resizing.
  60  */
  61 public class TestPLABResize {
  62 
  63     private static final int OBJECT_SIZE_SMALL = 10;
  64     private static final int OBJECT_SIZE_MEDIUM = 70;
  65     private static final int OBJECT_SIZE_HIGH = 150;
  66     private static final int GC_NUM_SMALL = 1;
  67     private static final int GC_NUM_MEDIUM = 3;
  68     private static final int GC_NUM_HIGH = 7;
  69     private static final int WASTE_PCT_SMALL = 10;
  70     private static final int WASTE_PCT_MEDIUM = 20;
  71     private static final int WASTE_PCT_HIGH = 30;
  72 
  73     private static final int ITERATIONS_SMALL = 3;
  74     private static final int ITERATIONS_MEDIUM = 5;
  75     private static final int ITERATIONS_HIGH = 8;
  76 
  77     private final static TestCase[] TEST_CASES = {
  78         new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_SMALL, GC_NUM_SMALL, ITERATIONS_MEDIUM),
  79         new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_HIGH, ITERATIONS_SMALL),
  80         new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH),
  81         new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM),
  82         new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL),
  83         new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH),
  84         new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM),
  85         new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL),
  86         new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH)
  87     };
  88 
  89     private static final String[] PLAB_OPTIONS = {
  90         "-XX:+ResizePLAB"
  91     };
  92 
  93     public static void main(String[] args) throws Throwable {
  94         for (TestCase testCase : TEST_CASES) {
  95             testCase.print(System.out);
  96             List<String> options = PLABUtils.prepareOptions(PLAB_OPTIONS);
  97             options.addAll(testCase.toOptions());
  98             options.add(AppPLABResize.class.getName());
  99             OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()]));
 100             if (out.getExitValue() != 0) {
 101                 System.out.println(out.getOutput());
 102                 throw new RuntimeException("Exit code is not 0");
 103             }
 104             checkResults(out.getOutput(), testCase);
 105         }
 106     }
 107 
 108     /**
 109      * Checks testing results.
 110      * Expected results - desired PLAB size is decreased and increased during promotion to Survivor.
 111      *
 112      * @param output - VM output
 113      * @param testCase
 114      */
 115     private static void checkResults(String output, TestCase testCase) {
 116         final LogParser log = new LogParser(output);
 117         final List<Pair<LogParser.ReportType, ? extends Map<String, Long>>> entries = log.getEntries();
 118         final ArrayList<Long> plabSizes = entries.stream()
 119                 .filter(p -> p.first == LogParser.ReportType.SURVIVOR_STATS)
 120                 .map(p -> p.second)
 121                 // We do not need first iterations - it is 'warm' phase of allocation.
 122                 .filter(mapEntries -> mapEntries.get(LogParser.GC_ID) > testCase.iterations)
 123                 .map(items -> items.get("desired_plab_sz"))
 124                 .collect(Collectors.toCollection(ArrayList::new));
 125 
 126         if (plabSizes.isEmpty()) {
 127             System.out.println(output);
 128             throw new RuntimeException("Cannot find desired_plab_sz information for gc_id>" + testCase.iterations);
 129         }
 130 
 131         Long prev = plabSizes.get(0);
 132         // Check that desired plab size is changed during iterations.
 133         // It should decrease during first half of iterations
 134         // and increase after.
 135         for (int index = 1; index < plabSizes.size(); ++index) {
 136             Long current = plabSizes.get(index);
 137             if (index < testCase.getIterations()) {
 138                 if (prev < current) {
 139                     System.out.println(output);
 140                     throw new RuntimeException("Test failed! Expect that previout PLAB size should be less than current. Prev.size: " + prev + " Current size:" + current);
 141                 }
 142             } else if (prev > current) {
 143                 System.out.println(output);
 144                 throw new RuntimeException("Test failed! Expect that previout PLAB size should be greater than current. Prev.size: " + prev + " Current size:" + current);
 145             }
 146             prev = current;
 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                     "-Dthreads=" + parGCThreads,
 196                     "-Dchunk.size=" + chunkSize,
 197                     "-Diterations=" + iterations,
 198                     "-XX:NewSize=16m",
 199                     "-XX:MaxNewSize=16m"
 200             );
 201         }
 202 
 203         /**
 204          * Print details about test case.
 205          */
 206         public void print(PrintStream out) {
 207             out.println("Test case details:");
 208             out.println("  Parallel GC buffer waste pct : " + wastePct);
 209             out.println("  Chunk size : " + chunkSize);
 210             out.println("  Parallel GC threads : " + parGCThreads);
 211             out.println("  Iterations per one allocation part : " + iterations);
 212             out.println("  All iteration of allocations : " + iterations * 3);
 213         }
 214 
 215         /**
 216          * @return iterations
 217          */
 218         public int getIterations() {
 219             return iterations;
 220         }
 221     }
 222 }