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