8191821: Finer granularity for GC verification
Reviewed-by: tschatzl, poonam, sangheki
--- 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
+ }
+ }
+}