--- old/src/hotspot/share/gc/g1/g1Arguments.cpp 2017-11-29 16:40:32.614970962 +0100 +++ new/src/hotspot/share/gc/g1/g1Arguments.cpp 2017-11-29 16:40:32.366961159 +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" @@ -94,6 +95,12 @@ log_trace(gc)("MarkStackSize: %uk MarkStackSizeMax: %uk", (unsigned int) (MarkStackSize / K), (uint) (MarkStackSizeMax / K)); } +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(); } --- old/src/hotspot/share/gc/g1/g1Arguments.hpp 2017-11-29 16:40:33.419002740 +0100 +++ new/src/hotspot/share/gc/g1/g1Arguments.hpp 2017-11-29 16:40:33.174993096 +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(); }; --- old/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2017-11-29 16:40:34.199033570 +0100 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2017-11-29 16:40:33.923022661 +0100 @@ -1083,7 +1083,6 @@ PostCompactionPrinterClosure cl(hr_printer()); heap_region_iterate(&cl); } - } void G1CollectedHeap::abort_concurrent_cycle() { @@ -1132,7 +1131,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"); } @@ -1173,7 +1172,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 @@ -2937,13 +2936,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); @@ -2984,7 +2987,7 @@ heap_region_iterate(&v_cl); } - _verifier->verify_before_gc(); + _verifier->verify_before_gc(verify_type); _verifier->check_bitmaps("GC Start"); @@ -3144,7 +3147,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"); --- old/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp 2017-11-29 16:40:35.799096811 +0100 +++ new/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp 2017-11-29 16:40:35.559087325 +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"); --- old/src/hotspot/share/gc/g1/g1FullCollector.cpp 2017-11-29 16:40:36.599128431 +0100 +++ new/src/hotspot/share/gc/g1/g1FullCollector.cpp 2017-11-29 16:40:36.347118471 +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); } --- old/src/hotspot/share/gc/g1/g1HeapVerifier.cpp 2017-11-29 16:40:37.427161159 +0100 +++ new/src/hotspot/share/gc/g1/g1HeapVerifier.cpp 2017-11-29 16:40:37.159150566 +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); + } } --- old/src/hotspot/share/gc/g1/g1HeapVerifier.hpp 2017-11-29 16:40:38.267194360 +0100 +++ new/src/hotspot/share/gc/g1/g1HeapVerifier.hpp 2017-11-29 16:40:38.003183925 +0100 @@ -34,6 +34,7 @@ class G1HeapVerifier : public CHeapObj { 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: - - G1HeapVerifier(G1CollectedHeap* heap) : _g1h(heap) { } + 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), _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 --- old/src/hotspot/share/gc/shared/gcArguments.cpp 2017-11-29 16:40:39.111227720 +0100 +++ new/src/hotspot/share/gc/shared/gcArguments.cpp 2017-11-29 16:40:38.871218234 +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"); --- old/src/hotspot/share/gc/shared/gcArguments.hpp 2017-11-29 16:40:39.947260763 +0100 +++ new/src/hotspot/share/gc/shared/gcArguments.hpp 2017-11-29 16:40:39.683250329 +0100 @@ -46,8 +46,15 @@ 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. + virtual bool parse_verification_type(const char* type); + virtual size_t conservative_max_heap_alignment() = 0; virtual CollectedHeap* create_heap() = 0; --- old/src/hotspot/share/memory/universe.cpp 2017-11-29 16:40:40.755292700 +0100 +++ new/src/hotspot/share/memory/universe.cpp 2017-11-29 16:40:40.515283214 +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 --- old/src/hotspot/share/runtime/globals.hpp 2017-11-29 16:40:41.595325902 +0100 +++ new/src/hotspot/share/runtime/globals.hpp 2017-11-29 16:40:41.351316258 +0100 @@ -2271,6 +2271,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 2017-11-09 09:13:26.856051352 +0100 +++ new/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp 2017-11-29 16:40:42.171348669 +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 2017-11-09 09:13:26.856051352 +0100 +++ new/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java 2017-11-29 16:40:42.979380606 +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 basicOpts = new ArrayList<>(); + Collections.addAll(basicOpts, new String[] { + "-Xbootclasspath/a:.", + "-XX:+UseG1GC", + "-XX:+WhiteBoxAPI", + "-XX:+ExplicitGCInvokesConcurrent", + "-Xlog:gc,gc+start,gc+verify=info", + "-XX:+UnlockDiagnosticVMOptions", + "-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 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 differnt 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 + } + } +}