1 /*
2  * Copyright (c) 2017, 2018, 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 TestVerifyGCType
26  * @summary Test the VerifyGCType flag to ensure basic functionality.
27  * @key gc
28  * @requires vm.gc.G1
29  * @library /test/lib
30  * @build sun.hotspot.WhiteBox
31  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
32  * @run driver TestVerifyGCType
33  */
34 
35 import java.util.ArrayList;
36 import java.util.Collections;
37 
38 import jdk.test.lib.Asserts;
39 import jdk.test.lib.Utils;
40 import jdk.test.lib.process.OutputAnalyzer;
41 import jdk.test.lib.process.ProcessTools;
42 import sun.hotspot.WhiteBox;
43 
44 public class TestVerifyGCType {
45     public static final String VERIFY_TAG    = "[gc,verify]";
46     public static final String VERIFY_BEFORE = "Verifying Before GC";
47     public static final String VERIFY_DURING = "Verifying During GC";
48     public static final String VERIFY_AFTER  = "Verifying After GC";
49 
main(String args[])50     public static void main(String args[]) throws Exception {
51         testAllVerificationEnabled();
52         testAllExplicitlyEnabled();
53         testFullAndRemark();
54         testConcurrentMark();
55         testBadVerificationType();
56     }
57 
testAllVerificationEnabled()58     private static void testAllVerificationEnabled() throws Exception {
59         // Test with all verification enabled
60         OutputAnalyzer output = testWithVerificationType(new String[0]);
61         output.shouldHaveExitValue(0);
62 
63         verifyCollection("Pause Young (Normal)", true, false, true, output.getStdout());
64         verifyCollection("Pause Young (Concurrent Start)", true, false, true, output.getStdout());
65         verifyCollection("Pause Young (Mixed)", true, false, true, output.getStdout());
66         verifyCollection("Pause Young (Prepare Mixed)", true, false, true, output.getStdout());
67         verifyCollection("Pause Remark", false, true, false, output.getStdout());
68         verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
69         verifyCollection("Pause Full", true, true, true, output.getStdout());
70     }
71 
testAllExplicitlyEnabled()72     private static void testAllExplicitlyEnabled() throws Exception {
73         OutputAnalyzer output;
74         // Test with all explicitly enabled
75         output = testWithVerificationType(new String[] {
76                 "young-normal", "concurrent-start", "mixed", "remark", "cleanup", "full"});
77         output.shouldHaveExitValue(0);
78 
79         verifyCollection("Pause Young (Normal)", true, false, true, output.getStdout());
80         verifyCollection("Pause Young (Concurrent Start)", true, false, true, output.getStdout());
81         verifyCollection("Pause Young (Mixed)", true, false, true, output.getStdout());
82         verifyCollection("Pause Young (Prepare Mixed)", true, false, true, output.getStdout());
83         verifyCollection("Pause Remark", false, true, false, output.getStdout());
84         verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
85         verifyCollection("Pause Full", true, true, true, output.getStdout());
86     }
87 
testFullAndRemark()88     private static void testFullAndRemark() throws Exception {
89         OutputAnalyzer output;
90         // Test with full and remark
91         output = testWithVerificationType(new String[] {"remark", "full"});
92         output.shouldHaveExitValue(0);
93 
94         verifyCollection("Pause Young (Normal)", false, false, false, output.getStdout());
95         verifyCollection("Pause Young (Concurrent Start)", false, false, false, output.getStdout());
96         verifyCollection("Pause Young (Mixed)", false, false, false, output.getStdout());
97         verifyCollection("Pause Young (Prepare Mixed)", false, false, false, output.getStdout());
98         verifyCollection("Pause Remark", false, true, false, output.getStdout());
99         verifyCollection("Pause Cleanup", false, false, false, output.getStdout());
100         verifyCollection("Pause Full", true, true, true, output.getStdout());
101     }
102 
testConcurrentMark()103     private static void testConcurrentMark() throws Exception {
104         OutputAnalyzer output;
105         // Test with full and remark
106         output = testWithVerificationType(new String[] {"concurrent-start", "cleanup", "remark"});
107         output.shouldHaveExitValue(0);
108 
109         verifyCollection("Pause Young (Normal)", false, false, false, output.getStdout());
110         verifyCollection("Pause Young (Concurrent Start)", true, false, true, output.getStdout());
111         verifyCollection("Pause Young (Mixed)", false, false, false, output.getStdout());
112         verifyCollection("Pause Young (Prepare Mixed)", false, false, false, output.getStdout());
113         verifyCollection("Pause Remark", false, true, false, output.getStdout());
114         verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
115         verifyCollection("Pause Full", false, false, false, output.getStdout());
116     }
117 
testBadVerificationType()118     private static void testBadVerificationType() throws Exception {
119         OutputAnalyzer output;
120         // Test bad type
121         output = testWithVerificationType(new String[] {"old"});
122         output.shouldHaveExitValue(0);
123 
124         output.shouldMatch("VerifyGCType: '.*' is unknown. Available types are: young-normal, concurrent-start, mixed, remark, cleanup and full");
125         verifyCollection("Pause Young (Normal)", true, false, true, output.getStdout());
126         verifyCollection("Pause Young (Concurrent Start)", true, false, true, output.getStdout());
127         verifyCollection("Pause Young (Mixed)", true, false, true, output.getStdout());
128         verifyCollection("Pause Young (Prepare Mixed)", true, false, true, output.getStdout());
129         verifyCollection("Pause Remark", false, true, false, output.getStdout());
130         verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
131         verifyCollection("Pause Full", true, true, true, output.getStdout());
132     }
133 
testWithVerificationType(String[] types)134     private static OutputAnalyzer testWithVerificationType(String[] types) throws Exception {
135         ArrayList<String> basicOpts = new ArrayList<>();
136         Collections.addAll(basicOpts, new String[] {
137                                        "-Xbootclasspath/a:.",
138                                        "-XX:+UnlockDiagnosticVMOptions",
139                                        "-XX:+UseG1GC",
140                                        "-XX:+WhiteBoxAPI",
141                                        "-Xlog:gc,gc+start,gc+verify=info",
142                                        "-Xms16m",
143                                        "-Xmx16m",
144                                        "-XX:ParallelGCThreads=1",
145                                        "-XX:G1HeapWastePercent=1",
146                                        "-XX:+VerifyBeforeGC",
147                                        "-XX:+VerifyAfterGC",
148                                        "-XX:+VerifyDuringGC"});
149 
150         for(String verifyType : types) {
151             basicOpts.add("-XX:VerifyGCType="+verifyType);
152         }
153 
154         basicOpts.add(TriggerGCs.class.getName());
155 
156         ProcessBuilder procBuilder =  ProcessTools.createJavaProcessBuilder(basicOpts.toArray(
157                                                                             new String[basicOpts.size()]));
158         OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
159         return analyzer;
160     }
161 
verifyCollection(String name, boolean expectBefore, boolean expectDuring, boolean expectAfter, String data)162     private static void verifyCollection(String name, boolean expectBefore, boolean expectDuring, boolean expectAfter, String data) {
163         CollectionInfo ci = CollectionInfo.parseFirst(name, data);
164         Asserts.assertTrue(ci != null, "Expected GC not found: " + name + "\n" + data);
165 
166         // Verify Before
167         verifyType(ci, expectBefore, VERIFY_BEFORE);
168         // Verify During
169         verifyType(ci, expectDuring, VERIFY_DURING);
170         // Verify After
171         verifyType(ci, expectAfter, VERIFY_AFTER);
172     }
173 
verifyType(CollectionInfo ci, boolean shouldExist, String pattern)174     private static void verifyType(CollectionInfo ci, boolean shouldExist, String pattern) {
175         if (shouldExist) {
176             Asserts.assertTrue(ci.containsVerification(pattern), "Missing expected verification for: " + ci.getName());
177         } else {
178             Asserts.assertFalse(ci.containsVerification(pattern), "Found unexpected verification for: " + ci.getName());
179         }
180     }
181 
182     public static class CollectionInfo {
183         String name;
184         ArrayList<String> verification;
CollectionInfo(String name)185         public CollectionInfo(String name) {
186             this.name = name;
187             this.verification = new ArrayList<>();
188             System.out.println("Created CollectionInfo: " + name);
189         }
190 
getName()191         public String getName() {
192             return name;
193         }
194 
addVerification(String verify)195         public void addVerification(String verify) {
196             System.out.println("Adding: " + verify);
197             verification.add(verify);
198         }
199 
containsVerification(String contains)200         public boolean containsVerification(String contains) {
201             for (String entry : verification) {
202                 if (entry.contains(contains)) {
203                     return true;
204                 }
205             }
206             return false;
207         }
208 
parseFirst(String name, String data)209         static CollectionInfo parseFirst(String name, String data) {
210             CollectionInfo result = null;
211             int firstIndex = data.indexOf(name);
212             if (firstIndex == -1) {
213                 return result;
214             }
215             int nextIndex = data.indexOf(name, firstIndex + 1);
216             if (nextIndex == -1) {
217                 return result;
218             }
219             // Found an entry for this name
220             result = new CollectionInfo(name);
221             String collectionData = data.substring(firstIndex, nextIndex + name.length());
222             for (String line : collectionData.split(System.getProperty("line.separator"))) {
223                 if (line.contains(VERIFY_TAG)) {
224                     result.addVerification(line);
225                 }
226             }
227             return result;
228         }
229     }
230 
231     public static class TriggerGCs {
main(String args[])232         public static void main(String args[]) throws Exception {
233             WhiteBox wb = WhiteBox.getWhiteBox();
234             // Allocate some memory that can be turned into garbage.
235             Object[] used = alloc1M();
236 
237             wb.youngGC(); // young-normal
238 
239             // Trigger the different GCs using the WhiteBox API.
240             wb.fullGC();  // full
241 
242             // Memory have been promoted to old by full GC. Free
243             // some memory to be reclaimed by concurrent cycle.
244             partialFree(used);
245             wb.g1StartConcMarkCycle(); // concurrent-start, remark and cleanup
246 
247             // Sleep to make sure concurrent cycle is done
248             while (wb.g1InConcurrentMark()) {
249                 Thread.sleep(1000);
250             }
251 
252             // Trigger two young GCs, first will be young-prepare-mixed, second will be mixed.
253             wb.youngGC(); // young-prepare-mixed
254             wb.youngGC(); // mixed
255         }
256 
alloc1M()257         private static Object[] alloc1M() {
258             Object[] ret = new Object[1024];
259             // Alloc 1024 1k byte arrays (~1M)
260             for (int i = 0; i < ret.length; i++) {
261                 ret[i] = new byte[1024];
262             }
263             return ret;
264         }
265 
partialFree(Object[] array)266         private static void partialFree(Object[] array) {
267             // Free every other element
268             for (int i = 0; i < array.length; i+=2) {
269                 array[i] = null;
270             }
271         }
272     }
273 }
274