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 TestPLABEvacuationFailure
26 * @bug 8148376
27 * @summary Checks PLAB statistics on evacuation failure
28 * @requires vm.gc.G1
29 * @library /test/lib /
30 * @modules java.base/jdk.internal.misc
31 * @modules java.management
32 * @run main gc.g1.plab.TestPLABEvacuationFailure
33 */
34package gc.g1.plab;
35
36import java.util.Arrays;
37import java.util.List;
38import java.util.Map;
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.regex.Pattern;
42import java.util.stream.Collectors;
43
44import jdk.test.lib.process.OutputAnalyzer;
45import jdk.test.lib.process.ProcessTools;
46import jdk.test.lib.Utils;
47
48import gc.g1.plab.lib.LogParser;
49import gc.g1.plab.lib.AppPLABEvacuationFailure;
50import gc.g1.plab.lib.PlabInfo;
51
52/**
53 * The test runs the AppPLABEvacuationFailure application to provoke a number of
54 * Evacuation Failures, parses GC log and analyzes PLAB statistics. The test checks
55 * that both fields 'failure_waste' and 'failure_used' for Evacuation Failure statistic
56 * are non zero, and zero for other statistics.
57 */
58public class TestPLABEvacuationFailure {
59
60    /* PLAB statistics fields which are checked.
61     * Test expects to find 0 in this fields in survivor statistics.
62     * Expects to find 0 in old statistics for GC when evacuation failure
63     * did not happen. And expects to find not 0 in old statistics in case when
64     * GC causes to evacuation failure.
65     */
66    private static final List<String> FAILURE_STAT_FIELDS = new ArrayList<>(Arrays.asList(
67            "failure used",
68            "failure wasted"));
69
70    private static final String[] COMMON_OPTIONS = {
71        "-Xlog:gc=debug,gc+plab=debug,gc+phases=trace",
72        "-XX:+UseG1GC",
73        "-XX:InitiatingHeapOccupancyPercent=100",
74        "-XX:-G1UseAdaptiveIHOP",
75        "-XX:G1HeapRegionSize=1m"};
76
77    private static final Pattern GC_ID_PATTERN = Pattern.compile("GC\\((\\d+)\\)");
78    private static List<Long> evacuationFailureIDs;
79    private static LogParser logParser;
80    private static String appPlabEvacFailureOutput;
81
82    public static void main(String[] args) throws Throwable {
83        // ParallelGCBufferWastePct, PLAB Size, ParallelGCBufferWastePct, MaxHeapSize, is plab fixed.
84        runTest(10, 1024, 3, 16, true);
85        runTest(15, 2048, 4, 256, true);
86        runTest(20, 65536, 7, 128, false);
87        runTest(25, 1024, 3, 16, true);
88        runTest(30, 16384, 7, 256, false);
89        runTest(10, 65536, 4, 32, false);
90    }
91
92    private static void runTest(int wastePct, int plabSize, int parGCThreads, int heapSize, boolean plabIsFixed) throws Throwable {
93        System.out.println("Test case details:");
94        System.out.println("  Heap size : " + heapSize + "M");
95        System.out.println("  Initial PLAB size : " + plabSize);
96        System.out.println("  Parallel GC buffer waste pct : " + wastePct);
97        System.out.println("  Parallel GC threads : " + parGCThreads);
98        System.out.println("  PLAB size is fixed: " + (plabIsFixed ? "yes" : "no"));
99        // Set up test GC and PLAB options
100        List<String> testOptions = new ArrayList<>();
101        Collections.addAll(testOptions, COMMON_OPTIONS);
102        Collections.addAll(testOptions, Utils.getTestJavaOpts());
103        Collections.addAll(testOptions,
104                "-XX:ParallelGCThreads=" + parGCThreads,
105                "-XX:ParallelGCBufferWastePct=" + wastePct,
106                "-XX:OldPLABSize=" + plabSize,
107                "-XX:YoungPLABSize=" + plabSize,
108                "-XX:" + (plabIsFixed ? "-" : "+") + "ResizePLAB",
109                "-XX:MaxHeapSize=" + heapSize + "m");
110        testOptions.add(AppPLABEvacuationFailure.class.getName());
111        OutputAnalyzer out = ProcessTools.executeTestJvm(testOptions.toArray(new String[testOptions.size()]));
112
113        appPlabEvacFailureOutput = out.getOutput();
114        if (out.getExitValue() != 0) {
115            System.out.println(appPlabEvacFailureOutput);
116            throw new RuntimeException("Expect exit code 0.");
117        }
118        // Get list of GC ID on evacuation failure
119        evacuationFailureIDs = getGcIdPlabEvacFailures(out);
120        logParser = new LogParser(appPlabEvacFailureOutput);
121        checkResults();
122    }
123
124    private static void checkResults() {
125
126        if (evacuationFailureIDs.isEmpty()) {
127            System.out.println(appPlabEvacFailureOutput);
128            throw new RuntimeException("AppPLABEvacuationFailure did not reach Evacuation Failure.");
129        }
130
131        Map<Long, PlabInfo> valuesToCheck = getNonEvacFailureSurvivorStats();
132        checkValuesIsZero(valuesToCheck, "Expect that SURVIVOR PLAB failure statistics should be 0 when no evacuation failure");
133
134        valuesToCheck = getNonEvacFailureOldStats();
135        checkValuesIsZero(valuesToCheck, "Expect that OLD PLAB failure statistics should be 0 when no evacuation failure");
136
137        valuesToCheck = getEvacFailureSurvivorStats();
138        checkValuesIsZero(valuesToCheck, "Expect that failure statistics should be 0 in SURVIVOR PLAB statistics at evacuation failure");
139
140        valuesToCheck = getEvacFailureOldStats();
141        checkValuesIsNotZero(valuesToCheck, "Expect that failure statistics should not be 0 in OLD PLAB statistics at evacuation failure");
142    }
143
144    /**
145     * Checks logItems for non-zero values. Throws RuntimeException if found.
146     *
147     * @param logItems
148     * @param errorMessage
149     */
150    private static void checkValuesIsZero(Map<Long, PlabInfo> logItems, String errorMessage) {
151        checkValues(logItems, errorMessage, true);
152    }
153
154    /**
155     * Checks logItems for zero values. Throws RuntimeException if found.
156     *
157     * @param logItems
158     * @param errorMessage
159     */
160    private static void checkValuesIsNotZero(Map<Long, PlabInfo> logItems, String errorMessage) {
161        checkValues(logItems, errorMessage, false);
162    }
163
164    private static void checkValues(Map<Long, PlabInfo> logItems, String errorMessage, boolean expectZero) {
165        logItems.entrySet()
166                .forEach(item -> item.getValue()
167                        .values()
168                        .forEach(items -> {
169                            if (expectZero != (items == 0)) {
170                                System.out.println(appPlabEvacFailureOutput);
171                                throw new RuntimeException(errorMessage);
172                            }
173                        })
174                );
175    }
176
177    /**
178     * For tracking PLAB statistics for specified PLAB type - survivor and old
179     */
180    private static Map<Long, PlabInfo> getNonEvacFailureSurvivorStats() {
181        return logParser.getExcludedSpecifiedStats(evacuationFailureIDs, LogParser.ReportType.SURVIVOR_STATS, FAILURE_STAT_FIELDS);
182    }
183
184    private static Map<Long, PlabInfo> getNonEvacFailureOldStats() {
185        return logParser.getExcludedSpecifiedStats(evacuationFailureIDs, LogParser.ReportType.OLD_STATS, FAILURE_STAT_FIELDS);
186    }
187
188    private static Map<Long, PlabInfo> getEvacFailureSurvivorStats() {
189        return logParser.getSpecifiedStats(evacuationFailureIDs, LogParser.ReportType.SURVIVOR_STATS, FAILURE_STAT_FIELDS);
190    }
191
192    private static Map<Long, PlabInfo> getEvacFailureOldStats() {
193        return logParser.getSpecifiedStats(evacuationFailureIDs, LogParser.ReportType.OLD_STATS, FAILURE_STAT_FIELDS);
194    }
195
196    private static List<Long> getGcIdPlabEvacFailures(OutputAnalyzer out) {
197        return out.asLines().stream()
198                .filter(line -> line.contains("Evacuation Failure"))
199                .map(line -> LogParser.getGcIdFromLine(line, GC_ID_PATTERN))
200                .collect(Collectors.toList());
201    }
202}
203