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 TestIHOPErgo
  26  * @bug 8148397
  27  * @key stress
  28  * @summary Test checks that behavior of Adaptive and Static IHOP at concurrent cycle initiation
  29  * @requires vm.gc.G1
  30  * @requires !vm.flightRecorder
  31  * @requires vm.opt.ExplicitGCInvokesConcurrent != true
  32  * @requires vm.opt.MaxGCPauseMillis == "null"
  33  * @library /test/lib /
  34  * @modules java.base/jdk.internal.misc
  35  * @modules java.management
  36  * @run driver/timeout=480 gc.g1.ihop.TestIHOPErgo
  37  */
  38 package gc.g1.ihop;
  39 
  40 import java.util.ArrayList;
  41 import java.util.Collections;
  42 import java.util.LinkedList;
  43 import java.util.List;
  44 
  45 import jdk.test.lib.process.OutputAnalyzer;
  46 import jdk.test.lib.process.ProcessTools;
  47 
  48 import gc.g1.ihop.lib.IhopUtils;
  49 
  50 /**
  51  * The test starts the AppIHOP multiple times varying settings of MaxHeapSize.
  52  * The test parses GC log from AppIHOP to check:
  53  * - occupancy is not less than threshold for Adaptive and Static IHOP at
  54  * concurrent cycle initiation
  55  * - Adaptive IHOP prediction was started during AppIHOP executing
  56  * - log contains ergonomic messages in log
  57  */
  58 public class TestIHOPErgo {
  59 
  60     // Common GC tune and logging options for test.
  61     private final static String[] COMMON_OPTIONS = {
  62         "-XX:+UnlockExperimentalVMOptions",
  63         "-XX:G1MixedGCLiveThresholdPercent=100",
  64         "-XX:G1HeapWastePercent=0",
  65         "-XX:MaxGCPauseMillis=30000",
  66         "-XX:G1MixedGCCountTarget=1",
  67         "-XX:+UseG1GC",
  68         "-XX:G1HeapRegionSize=1m",
  69         "-XX:+G1UseAdaptiveIHOP",
  70         "-Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug",
  71         "-XX:+AlwaysTenure",
  72         "-XX:G1AdaptiveIHOPNumInitialSamples=1",
  73         "-XX:InitiatingHeapOccupancyPercent=30"
  74     };
  75 
  76     public static void main(String[] args) throws Throwable {
  77 
  78         // heap size MB, sleep time for allocator, true/false for adaptive/static
  79         runTest(64, 0, false);
  80         runTest(64, 100, false);
  81         runTest(128, 100, false);
  82         runTest(256, 50, false);
  83         runTest(512, 30, false);
  84         runTest(64, 50, true);
  85         runTest(128, 200, true);
  86         runTest(256, 100, true);
  87         runTest(512, 50, true);
  88     }
  89 
  90     /**
  91      * Runs AppIHOP in separate VM and checks GC log.
  92      *
  93      * @param heapSize       heap size
  94      * @param sleepTime      sleep time between memory allocations.
  95      * @param isIhopAdaptive true forAdaptive IHOP, false for Static
  96      *
  97      * @throws Throwable
  98      */
  99     private static void runTest(int heapSize, int sleepTime, boolean isIhopAdaptive) throws Throwable {
 100         System.out.println("IHOP test:");
 101         System.out.println("  MaxHeapSize : " + heapSize);
 102 
 103         List<String> options = new ArrayList<>();
 104         Collections.addAll(options,
 105                 "-Dheap.size=" + heapSize,
 106                 "-Dsleep.time=" + sleepTime,
 107                 "-XX:MaxHeapSize=" + heapSize + "M",
 108                 "-XX:NewSize=" + heapSize / 8 + "M",
 109                 "-XX:MaxNewSize=" + heapSize / 8 + "M",
 110                 "-XX:InitialHeapSize=" + heapSize + "M",
 111                 "-XX:" + (isIhopAdaptive ? "+" : "-") + "G1UseAdaptiveIHOP"
 112         );
 113 
 114         Collections.addAll(options, COMMON_OPTIONS);
 115         options.add(AppIHOP.class.getName());
 116         OutputAnalyzer out = executeTest(options);
 117 
 118         // Checks that log contains message which indicates that IHOP prediction is active
 119         if (isIhopAdaptive) {
 120             IhopUtils.checkAdaptiveIHOPWasActivated(out);
 121         }
 122         // Checks that log contains messages which indicates that VM initiates/checks heap occupancy
 123         // and tries to start concurrent cycle.
 124         IhopUtils.checkErgoMessagesExist(out);
 125 
 126         // Checks threshold and occupancy values
 127         IhopUtils.checkIhopLogValues(out);
 128     }
 129 
 130     private static OutputAnalyzer executeTest(List<String> options) throws Throwable, RuntimeException {
 131         OutputAnalyzer out;
 132         out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()]));
 133         if (out.getExitValue() != 0) {
 134             System.out.println(out.getOutput());
 135             throw new RuntimeException("AppIHOP failed with exit code" + out.getExitValue());
 136         }
 137         return out;
 138     }
 139 
 140     /**
 141      * The AppIHOP fills 60% of heap and allocates and frees 30% of existing
 142      * heap 'iterations' times to achieve IHOP activation. To be executed in
 143      * separate VM. Expected properties:
 144      * heap.size - heap size which is used to calculate amount of memory
 145      *             to be allocated and freed
 146      * sleep.time - short pause between filling each MB
 147      */
 148     public static class AppIHOP {
 149 
 150         public final static LinkedList<Object> GARBAGE = new LinkedList<>();
 151 
 152         private final int ITERATIONS = 10;
 153         private final int OBJECT_SIZE = 100000;
 154         // 60% of the heap will be filled before test cycles.
 155         // 30% of the heap will be filled and freed during test cycle.
 156         private final long HEAP_PREALLOC_PCT = 60;
 157         private final long HEAP_ALLOC_PCT = 30;
 158         private final long HEAP_SIZE;
 159         // Amount of memory to be allocated before iterations start
 160         private final long HEAP_PREALLOC_SIZE;
 161         // Amount of memory to be allocated and freed during iterations
 162         private final long HEAP_ALLOC_SIZE;
 163         private final int SLEEP_TIME;
 164 
 165         public static void main(String[] args) throws InterruptedException {
 166             new AppIHOP().start();
 167         }
 168 
 169         AppIHOP() {
 170             HEAP_SIZE = Integer.getInteger("heap.size") * 1024 * 1024;
 171             SLEEP_TIME = Integer.getInteger("sleep.time");
 172 
 173             HEAP_PREALLOC_SIZE = HEAP_SIZE * HEAP_PREALLOC_PCT / 100;
 174             HEAP_ALLOC_SIZE = HEAP_SIZE * HEAP_ALLOC_PCT / 100;
 175         }
 176 
 177         public void start() throws InterruptedException {
 178             fill(HEAP_PREALLOC_SIZE);
 179             fillAndFree(HEAP_ALLOC_SIZE, ITERATIONS);
 180         }
 181 
 182         /**
 183          * Fills allocationSize bytes of garbage.
 184          *
 185          * @param allocationSize amount of garbage
 186          */
 187         private void fill(long allocationSize) {
 188             long allocated = 0;
 189             while (allocated < allocationSize) {
 190                 GARBAGE.addFirst(new byte[OBJECT_SIZE]);
 191                 allocated += OBJECT_SIZE;
 192             }
 193         }
 194 
 195         /**
 196          * Allocates allocationSize bytes of garbage. Performs a short pauses
 197          * during allocation. Frees allocated garbage.
 198          *
 199          * @param allocationSize amount of garbage per iteration
 200          * @param iterations     iteration count
 201          *
 202          * @throws InterruptedException
 203          */
 204         private void fillAndFree(long allocationSize, int iterations) throws InterruptedException {
 205 
 206             for (int i = 0; i < iterations; ++i) {
 207                 System.out.println("Iteration:" + i);
 208                 long allocated = 0;
 209                 long counter = 0;
 210                 while (allocated < allocationSize) {
 211                     GARBAGE.addFirst(new byte[OBJECT_SIZE]);
 212                     allocated += OBJECT_SIZE;
 213                     counter += OBJECT_SIZE;
 214                     if (counter > 1024 * 1024) {
 215                         counter = 0;
 216                         if (SLEEP_TIME != 0) {
 217                             Thread.sleep(SLEEP_TIME);
 218                         }
 219                     }
 220                 }
 221                 long removed = 0;
 222                 while (removed < allocationSize) {
 223                     GARBAGE.removeLast();
 224                     removed += OBJECT_SIZE;
 225                 }
 226             }
 227         }
 228     }
 229 }