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