changeset 48054:34fe70d22e9c

8191821: Finer granularity for GC verification Reviewed-by: tschatzl, poonam, sangheki
author sjohanss
date Fri, 01 Dec 2017 08:56:22 +0100
parents 88ec5fca7726
children e277fdf5e631
files src/hotspot/share/gc/g1/g1Arguments.cpp src/hotspot/share/gc/g1/g1Arguments.hpp src/hotspot/share/gc/g1/g1CollectedHeap.cpp src/hotspot/share/gc/g1/g1ConcurrentMark.cpp src/hotspot/share/gc/g1/g1FullCollector.cpp src/hotspot/share/gc/g1/g1HeapVerifier.cpp src/hotspot/share/gc/g1/g1HeapVerifier.hpp src/hotspot/share/gc/shared/gcArguments.cpp src/hotspot/share/gc/shared/gcArguments.hpp src/hotspot/share/memory/universe.cpp src/hotspot/share/runtime/globals.hpp test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp test/hotspot/jtreg/gc/g1/TestVerifyGCType.java
diffstat 13 files changed, 457 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/gc/g1/g1Arguments.cpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/g1/g1Arguments.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -26,6 +26,7 @@
 #include "gc/g1/g1Arguments.hpp"
 #include "gc/g1/g1CollectedHeap.inline.hpp"
 #include "gc/g1/g1CollectorPolicy.hpp"
+#include "gc/g1/g1HeapVerifier.hpp"
 #include "gc/g1/heapRegion.hpp"
 #include "gc/shared/gcArguments.inline.hpp"
 #include "runtime/globals.hpp"
@@ -104,6 +105,12 @@
 #endif
 }
 
+bool G1Arguments::parse_verification_type(const char* type) {
+  G1CollectedHeap::heap()->verifier()->parse_verification_type(type);
+  // Always return true because we want to parse all values.
+  return true;
+}
+
 CollectedHeap* G1Arguments::create_heap() {
   return create_heap_with_policy<G1CollectedHeap, G1CollectorPolicy>();
 }
--- a/src/hotspot/share/gc/g1/g1Arguments.hpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/g1/g1Arguments.hpp	Fri Dec 01 08:56:22 2017 +0100
@@ -32,6 +32,7 @@
 class G1Arguments : public GCArguments {
 public:
   virtual void initialize_flags();
+  virtual bool parse_verification_type(const char* type);
   virtual size_t conservative_max_heap_alignment();
   virtual CollectedHeap* create_heap();
 };
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -1084,7 +1084,6 @@
     PostCompactionPrinterClosure cl(hr_printer());
     heap_region_iterate(&cl);
   }
-
 }
 
 void G1CollectedHeap::abort_concurrent_cycle() {
@@ -1133,7 +1132,7 @@
   assert(!GCCause::is_user_requested_gc(gc_cause()) || explicit_gc, "invariant");
   assert(used() == recalculate_used(), "Should be equal");
   _verifier->verify_region_sets_optional();
-  _verifier->verify_before_gc();
+  _verifier->verify_before_gc(G1HeapVerifier::G1VerifyFull);
   _verifier->check_bitmaps("Full GC Start");
 }
 
@@ -1174,7 +1173,7 @@
   check_gc_time_stamps();
   _hrm.verify_optional();
   _verifier->verify_region_sets_optional();
-  _verifier->verify_after_gc();
+  _verifier->verify_after_gc(G1HeapVerifier::G1VerifyFull);
   // Clear the previous marking bitmap, if needed for bitmap verification.
   // Note we cannot do this when we clear the next marking bitmap in
   // G1ConcurrentMark::abort() above since VerifyDuringGC verifies the
@@ -2958,13 +2957,17 @@
 
     GCTraceCPUTime tcpu;
 
+    G1HeapVerifier::G1VerifyType verify_type;
     FormatBuffer<> gc_string("Pause ");
     if (collector_state()->during_initial_mark_pause()) {
       gc_string.append("Initial Mark");
+      verify_type = G1HeapVerifier::G1VerifyInitialMark;
     } else if (collector_state()->gcs_are_young()) {
       gc_string.append("Young");
+      verify_type = G1HeapVerifier::G1VerifyYoungOnly;
     } else {
       gc_string.append("Mixed");
+      verify_type = G1HeapVerifier::G1VerifyMixed;
     }
     GCTraceTime(Info, gc) tm(gc_string, NULL, gc_cause(), true);
 
@@ -3005,7 +3008,7 @@
         heap_region_iterate(&v_cl);
       }
 
-      _verifier->verify_before_gc();
+      _verifier->verify_before_gc(verify_type);
 
       _verifier->check_bitmaps("GC Start");
 
@@ -3165,7 +3168,7 @@
           heap_region_iterate(&v_cl);
         }
 
-        _verifier->verify_after_gc();
+        _verifier->verify_after_gc(verify_type);
         _verifier->check_bitmaps("GC End");
 
         assert(!ref_processor_stw()->discovery_enabled(), "Postcondition");
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -1015,9 +1015,7 @@
   SvcGCMarker sgcm(SvcGCMarker::OTHER);
 
   if (VerifyDuringGC) {
-    HandleMark hm;  // handle scope
-    g1h->prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (before)");
+    g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "During GC (before)");
   }
   g1h->verifier()->check_bitmaps("Remark Start");
 
@@ -1038,9 +1036,7 @@
 
     // Verify the heap w.r.t. the previous marking bitmap.
     if (VerifyDuringGC) {
-      HandleMark hm;  // handle scope
-      g1h->prepare_for_verify();
-      Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (overflow)");
+      g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "During GC (overflow)");
     }
 
     // Clear the marking state because we will be restarting
@@ -1055,9 +1051,7 @@
                                        true /* expected_active */);
 
     if (VerifyDuringGC) {
-      HandleMark hm;  // handle scope
-      g1h->prepare_for_verify();
-      Universe::verify(VerifyOption_G1UseNextMarking, "During GC (after)");
+      g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UseNextMarking, "During GC (after)");
     }
     g1h->verifier()->check_bitmaps("Remark End");
     assert(!restart_for_overflow(), "sanity");
@@ -1189,9 +1183,7 @@
   g1h->verifier()->verify_region_sets_optional();
 
   if (VerifyDuringGC) {
-    HandleMark hm;  // handle scope
-    g1h->prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (before)");
+    g1h->verifier()->verify(G1HeapVerifier::G1VerifyCleanup, VerifyOption_G1UsePrevMarking, "During GC (before)");
   }
   g1h->verifier()->check_bitmaps("Cleanup Start");
 
@@ -1263,9 +1255,7 @@
   Universe::update_heap_info_at_gc();
 
   if (VerifyDuringGC) {
-    HandleMark hm;  // handle scope
-    g1h->prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (after)");
+    g1h->verifier()->verify(G1HeapVerifier::G1VerifyCleanup, VerifyOption_G1UsePrevMarking, "During GC (after)");
   }
 
   g1h->verifier()->check_bitmaps("Cleanup End");
--- a/src/hotspot/share/gc/g1/g1FullCollector.cpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -245,8 +245,8 @@
 }
 
 void G1FullCollector::verify_after_marking() {
-  if (!VerifyDuringGC) {
-    //Only do verification if VerifyDuringGC is set.
+  if (!VerifyDuringGC || !_heap->verifier()->should_verify(G1HeapVerifier::G1VerifyFull)) {
+    // Only do verification if VerifyDuringGC and G1VerifyFull is set.
     return;
   }
 
@@ -265,6 +265,6 @@
   // fail. At the end of the GC, the original mark word values
   // (including hash values) are restored to the appropriate
   // objects.
-  GCTraceTime(Info, gc, verify)("During GC (full)");
+  GCTraceTime(Info, gc, verify)("Verifying During GC (full)");
   _heap->verify(VerifyOption_G1UseFullMarking);
 }
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -376,6 +376,37 @@
   }
 };
 
+void G1HeapVerifier::parse_verification_type(const char* type) {
+  if (strcmp(type, "young-only") == 0) {
+    enable_verification_type(G1VerifyYoungOnly);
+  } else if (strcmp(type, "initial-mark") == 0) {
+    enable_verification_type(G1VerifyInitialMark);
+  } else if (strcmp(type, "mixed") == 0) {
+    enable_verification_type(G1VerifyMixed);
+  } else if (strcmp(type, "remark") == 0) {
+    enable_verification_type(G1VerifyRemark);
+  } else if (strcmp(type, "cleanup") == 0) {
+    enable_verification_type(G1VerifyCleanup);
+  } else if (strcmp(type, "full") == 0) {
+    enable_verification_type(G1VerifyFull);
+  } else {
+    log_warning(gc, verify)("VerifyGCType: '%s' is unknown. Available types are: "
+                            "young-only, initial-mark, mixed, remark, cleanup and full", type);
+  }
+}
+
+void G1HeapVerifier::enable_verification_type(G1VerifyType type) {
+  // First enable will clear _enabled_verification_types.
+  if (_enabled_verification_types == G1VerifyAll) {
+    _enabled_verification_types = type;
+  } else {
+    _enabled_verification_types |= type;
+  }
+}
+
+bool G1HeapVerifier::should_verify(G1VerifyType type) {
+  return (_enabled_verification_types & type) == type;
+}
 
 void G1HeapVerifier::verify(VerifyOption vo) {
   if (!SafepointSynchronize::is_at_safepoint()) {
@@ -541,28 +572,32 @@
   }
 }
 
-double G1HeapVerifier::verify(bool guard, const char* msg) {
+double G1HeapVerifier::verify(G1VerifyType type, VerifyOption vo, const char* msg) {
   double verify_time_ms = 0.0;
 
-  if (guard && _g1h->total_collections() >= VerifyGCStartAt) {
+  if (should_verify(type) && _g1h->total_collections() >= VerifyGCStartAt) {
     double verify_start = os::elapsedTime();
     HandleMark hm;  // Discard invalid handles created during verification
     prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, msg);
+    Universe::verify(vo, msg);
     verify_time_ms = (os::elapsedTime() - verify_start) * 1000;
   }
 
   return verify_time_ms;
 }
 
-void G1HeapVerifier::verify_before_gc() {
-  double verify_time_ms = verify(VerifyBeforeGC, "Before GC");
-  _g1h->g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms);
+void G1HeapVerifier::verify_before_gc(G1VerifyType type) {
+  if (VerifyBeforeGC) {
+    double verify_time_ms = verify(type, VerifyOption_G1UsePrevMarking, "Before GC");
+    _g1h->g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms);
+  }
 }
 
-void G1HeapVerifier::verify_after_gc() {
-  double verify_time_ms = verify(VerifyAfterGC, "After GC");
-  _g1h->g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms);
+void G1HeapVerifier::verify_after_gc(G1VerifyType type) {
+  if (VerifyAfterGC) {
+    double verify_time_ms = verify(type, VerifyOption_G1UsePrevMarking, "After GC");
+    _g1h->g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms);
+  }
 }
 
 
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp	Fri Dec 01 08:56:22 2017 +0100
@@ -34,6 +34,7 @@
 class G1HeapVerifier : public CHeapObj<mtGC> {
 private:
   G1CollectedHeap* _g1h;
+  int _enabled_verification_types;
 
   // verify_region_sets() performs verification over the region
   // lists. It will be compiled in the product code to be used when
@@ -41,8 +42,21 @@
   void verify_region_sets();
 
 public:
+  enum G1VerifyType {
+    G1VerifyYoungOnly   =  1, // -XX:VerifyGCType=young-only
+    G1VerifyInitialMark =  2, // -XX:VerifyGCType=initial-mark
+    G1VerifyMixed       =  4, // -XX:VerifyGCType=mixed
+    G1VerifyRemark      =  8, // -XX:VerifyGCType=remark
+    G1VerifyCleanup     = 16, // -XX:VerifyGCType=cleanup
+    G1VerifyFull        = 32, // -XX:VerifyGCType=full
+    G1VerifyAll         = -1
+  };
 
-  G1HeapVerifier(G1CollectedHeap* heap) : _g1h(heap) { }
+  G1HeapVerifier(G1CollectedHeap* heap) : _g1h(heap), _enabled_verification_types(G1VerifyAll) { }
+
+  void parse_verification_type(const char* type);
+  void enable_verification_type(G1VerifyType type);
+  bool should_verify(G1VerifyType type);
 
   // Perform verification.
 
@@ -73,9 +87,9 @@
 #endif // HEAP_REGION_SET_FORCE_VERIFY
 
   void prepare_for_verify();
-  double verify(bool guard, const char* msg);
-  void verify_before_gc();
-  void verify_after_gc();
+  double verify(G1VerifyType type, VerifyOption vo, const char* msg);
+  void verify_before_gc(G1VerifyType type);
+  void verify_after_gc(G1VerifyType type);
 
 #ifndef PRODUCT
   // Make sure that the given bitmap has no marked objects in the
--- a/src/hotspot/share/gc/shared/gcArguments.cpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/shared/gcArguments.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "gc/shared/gcArguments.hpp"
 #include "gc/serial/serialArguments.hpp"
+#include "logging/log.hpp"
 #include "memory/allocation.inline.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/globals.hpp"
@@ -84,6 +85,12 @@
 #endif // INCLUDE_ALL_GCS
 }
 
+bool GCArguments::parse_verification_type(const char* type) {
+  log_warning(gc, verify)("VerifyGCType is not supported by this collector.");
+  // Return false to avoid multiple warnings.
+  return false;
+}
+
 void GCArguments::initialize_flags() {
 #if INCLUDE_ALL_GCS
   if (MinHeapFreeRatio == 100) {
@@ -99,6 +106,24 @@
 #endif // INCLUDE_ALL_GCS
 }
 
+void GCArguments::post_heap_initialize() {
+  if (strlen(VerifyGCType) > 0) {
+    const char delimiter[] = " ,\n";
+    size_t length = strlen(VerifyGCType);
+    char* type_list = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal);
+    strncpy(type_list, VerifyGCType, length + 1);
+    char* token = strtok(type_list, delimiter);
+    while (token != NULL) {
+      bool success = parse_verification_type(token);
+      if (!success) {
+        break;
+      }
+      token = strtok(NULL, delimiter);
+    }
+    FREE_C_HEAP_ARRAY(char, type_list);
+  }
+}
+
 jint GCArguments::initialize() {
   assert(!is_initialized(), "GC arguments already initialized");
 
--- a/src/hotspot/share/gc/shared/gcArguments.hpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/gc/shared/gcArguments.hpp	Fri Dec 01 08:56:22 2017 +0100
@@ -46,8 +46,16 @@
   static bool is_initialized();
   static GCArguments* arguments();
 
+  void post_heap_initialize();
+
   virtual void initialize_flags();
 
+  // Collector specific function to allow finer grained verification
+  // through VerifyGCType. If not overridden the default version will
+  // warn that the flag is not supported for the given collector.
+  // Returns true if parsing should continue, false otherwise.
+  virtual bool parse_verification_type(const char* type);
+
   virtual size_t conservative_max_heap_alignment() = 0;
 
   virtual CollectedHeap* create_heap() = 0;
--- a/src/hotspot/share/memory/universe.cpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/memory/universe.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -768,6 +768,7 @@
   }
   log_info(gc)("Using %s", _collectedHeap->name());
 
+  GCArguments::arguments()->post_heap_initialize();
   ThreadLocalAllocBuffer::set_max_size(Universe::heap()->max_tlab_size());
 
 #ifdef _LP64
--- a/src/hotspot/share/runtime/globals.hpp	Fri Dec 01 11:40:39 2017 +0530
+++ b/src/hotspot/share/runtime/globals.hpp	Fri Dec 01 08:56:22 2017 +0100
@@ -2268,6 +2268,10 @@
   diagnostic(bool, VerifyDuringGC, false,                                   \
           "Verify memory system during GC (between phases)")                \
                                                                             \
+  diagnostic(ccstrlist, VerifyGCType, "",                                   \
+             "GC type(s) to verify when Verify*GC is enabled."              \
+             "Available types are collector specific.")                     \
+                                                                            \
   diagnostic(ccstrlist, VerifySubSet, "",                                   \
           "Memory sub-systems to verify when Verify*GC flag(s) "            \
           "are enabled. One or more sub-systems can be specified "          \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp	Fri Dec 01 08:56:22 2017 +0100
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/g1/g1HeapVerifier.hpp"
+#include "logging/logConfiguration.hpp"
+#include "unittest.hpp"
+
+TEST(G1HeapVerifier, parse) {
+  G1HeapVerifier verifier(NULL);
+
+  LogConfiguration::configure_stdout(LogLevel::Off, true, LOG_TAGS(gc, verify));
+
+  // Default is to verify everything.
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyAll));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyInitialMark));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyFull));
+
+  // Setting one will disable all other.
+  verifier.parse_verification_type("full");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyAll));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyInitialMark));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyFull));
+
+  // Verify case sensitivity.
+  verifier.parse_verification_type("YOUNG-ONLY");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+  verifier.parse_verification_type("young-only");
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+
+  // Verify perfect match
+  verifier.parse_verification_type("mixedgc");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  verifier.parse_verification_type("mixe");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  verifier.parse_verification_type("mixed");
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+
+  // Verify the last three
+  verifier.parse_verification_type("initial-mark");
+  verifier.parse_verification_type("remark");
+  verifier.parse_verification_type("cleanup");
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup));
+
+  // Enabling all is not the same as G1VerifyAll
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyAll));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java	Fri Dec 01 08:56:22 2017 +0100
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test TestVerifyGCType
+ * @summary Test the VerifyGCType flag to ensure basic functionality.
+ * @key gc
+ * @requires vm.gc.G1
+ * @library /test/lib
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run driver TestVerifyGCType
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import sun.hotspot.WhiteBox;
+
+public class TestVerifyGCType {
+    public static final String VERIFY_TAG    = "[gc,verify]";
+    public static final String VERIFY_BEFORE = "Verifying Before GC";
+    public static final String VERIFY_DURING = "Verifying During GC";
+    public static final String VERIFY_AFTER  = "Verifying After GC";
+
+    public static void main(String args[]) throws Exception {
+        testAllVerificationEnabled();
+        testAllExplicitlyEnabled();
+        testFullAndRemark();
+        testConcurrentMark();
+        testBadVerificationType();
+        testUnsupportedCollector();
+    }
+
+    private static void testAllVerificationEnabled() throws Exception {
+        // Test with all verification enabled
+        OutputAnalyzer output = testWithVerificationType(new String[0]);
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", true, false, true, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", true, false, true, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testAllExplicitlyEnabled() throws Exception {
+        OutputAnalyzer output;
+        // Test with all explicitly enabled
+        output = testWithVerificationType(new String[] {
+                "young-only", "initial-mark", "mixed", "remark", "cleanup", "full"});
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", true, false, true, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", true, false, true, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testFullAndRemark() throws Exception {
+        OutputAnalyzer output;
+        // Test with full and remark
+        output = testWithVerificationType(new String[] {"remark", "full"});
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", false, false, false, output.getStdout());
+        verifyCollection("Pause Initial Mark", false, false, false, output.getStdout());
+        verifyCollection("Pause Mixed", false, false, false, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, false, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testConcurrentMark() throws Exception {
+        OutputAnalyzer output;
+        // Test with full and remark
+        output = testWithVerificationType(new String[] {"initial-mark", "cleanup", "remark"});
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", false, false, false, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", false, false, false, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", false, false, false, output.getStdout());
+    }
+
+    private static void testBadVerificationType() throws Exception {
+        OutputAnalyzer output;
+        // Test bad type
+        output = testWithVerificationType(new String[] {"old"});
+        output.shouldHaveExitValue(0);
+
+        output.shouldMatch("VerifyGCType: '.*' is unknown. Available types are: young-only, initial-mark, mixed, remark, cleanup and full");
+        verifyCollection("Pause Young", true, false, true, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", true, false, true, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testUnsupportedCollector() throws Exception {
+        OutputAnalyzer output;
+        // Test bad gc
+        output = testWithBadGC();
+        output.shouldHaveExitValue(0);
+        output.shouldMatch("VerifyGCType is not supported by this collector.");
+    }
+
+    private static OutputAnalyzer testWithVerificationType(String[] types) throws Exception {
+        ArrayList<String> basicOpts = new ArrayList<>();
+        Collections.addAll(basicOpts, new String[] {
+                                       "-Xbootclasspath/a:.",
+                                       "-XX:+UnlockDiagnosticVMOptions",
+                                       "-XX:+UseG1GC",
+                                       "-XX:+WhiteBoxAPI",
+                                       "-XX:+ExplicitGCInvokesConcurrent",
+                                       "-Xlog:gc,gc+start,gc+verify=info",
+                                       "-XX:+VerifyBeforeGC",
+                                       "-XX:+VerifyAfterGC",
+                                       "-XX:+VerifyDuringGC"});
+
+        for(String verifyType : types) {
+            basicOpts.add("-XX:VerifyGCType="+verifyType);
+        }
+
+        basicOpts.add(TriggerGCs.class.getName());
+
+        ProcessBuilder procBuilder =  ProcessTools.createJavaProcessBuilder(basicOpts.toArray(
+                                                                            new String[basicOpts.size()]));
+        OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
+        return analyzer;
+    }
+
+    private static OutputAnalyzer testWithBadGC() throws Exception {
+        ProcessBuilder procBuilder =  ProcessTools.createJavaProcessBuilder(new String[] {
+                "-XX:+UseParallelGC",
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:VerifyGCType=full",
+                "-version"});
+
+        OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
+        return analyzer;
+    }
+
+    private static void verifyCollection(String name, boolean expectBefore, boolean expectDuring, boolean expectAfter, String data) {
+        CollectionInfo ci = CollectionInfo.parseFirst(name, data);
+        Asserts.assertTrue(ci != null, "Expected GC not found: " + name);
+
+        // Verify Before
+        verifyType(ci, expectBefore, VERIFY_BEFORE);
+        // Verify During
+        verifyType(ci, expectDuring, VERIFY_DURING);
+        // Verify After
+        verifyType(ci, expectAfter, VERIFY_AFTER);
+    }
+
+    private static void verifyType(CollectionInfo ci, boolean shouldExist, String pattern) {
+        if (shouldExist) {
+            Asserts.assertTrue(ci.containsVerification(pattern), "Missing expected verification for: " + ci.getName());
+        } else {
+            Asserts.assertFalse(ci.containsVerification(pattern), "Found unexpected verification for: " + ci.getName());
+        }
+    }
+
+    public static class CollectionInfo {
+        String name;
+        ArrayList<String> verification;
+        public CollectionInfo(String name) {
+            this.name = name;
+            this.verification = new ArrayList<>();
+            System.out.println("Created CollectionInfo: " + name);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void addVerification(String verify) {
+            System.out.println("Adding: " + verify);
+            verification.add(verify);
+        }
+
+        public boolean containsVerification(String contains) {
+            for (String entry : verification) {
+                if (entry.contains(contains)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        static CollectionInfo parseFirst(String name, String data) {
+            CollectionInfo result = null;
+            int firstIndex = data.indexOf(name);
+            if (firstIndex == -1) {
+                return result;
+            }
+            int nextIndex = data.indexOf(name, firstIndex + 1);
+            if (nextIndex == -1) {
+                return result;
+            }
+            // Found an entry for this name
+            result = new CollectionInfo(name);
+            String collectionData = data.substring(firstIndex, nextIndex + name.length());
+            for (String line : collectionData.split(System.getProperty("line.separator"))) {
+                if (line.contains(VERIFY_TAG)) {
+                    result.addVerification(line);
+                }
+            }
+            return result;
+        }
+    }
+
+    public static class TriggerGCs {
+        public static void main(String args[]) throws Exception {
+            WhiteBox wb = WhiteBox.getWhiteBox();
+            // Trigger the different GCs using the WhiteBox API and System.gc()
+            // to start a concurrent cycle with -XX:+ExplicitGCInvokesConcurrent.
+            wb.fullGC();  // full
+            System.gc();  // initial-mark, remark and cleanup
+            // Sleep to make sure concurrent cycle is done
+            Thread.sleep(1000);
+            wb.youngGC(); // young-only
+            wb.youngGC(); // mixed
+        }
+    }
+}