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 */
38package gc.g1.ihop;
39
40import java.util.ArrayList;
41import java.util.Collections;
42import java.util.LinkedList;
43import java.util.List;
44
45import jdk.test.lib.process.OutputAnalyzer;
46import jdk.test.lib.process.ProcessTools;
47
48import 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 */
58public 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}
230