8191821: Finer granularity for GC verification

Reviewed-by: tschatzl, poonam, sangheki
This commit is contained in:
Stefan Johansson 2017-12-01 08:56:22 +01:00
parent 9a3de631aa
commit ea04c5cfb3
13 changed files with 457 additions and 36 deletions

@ -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 @@ void G1Arguments::initialize_flags() {
#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>();
}

@ -32,6 +32,7 @@ class CollectedHeap;
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();
};

@ -1084,7 +1084,6 @@ void G1CollectedHeap::print_hrm_post_compaction() {
PostCompactionPrinterClosure cl(hr_printer());
heap_region_iterate(&cl);
}
}
void G1CollectedHeap::abort_concurrent_cycle() {
@ -1133,7 +1132,7 @@ void G1CollectedHeap::verify_before_full_collection(bool explicit_gc) {
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 @@ void G1CollectedHeap::verify_after_full_collection() {
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 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
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 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
heap_region_iterate(&v_cl);
}
_verifier->verify_before_gc();
_verifier->verify_before_gc(verify_type);
_verifier->check_bitmaps("GC Start");
@ -3165,7 +3168,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
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");

@ -1015,9 +1015,7 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) {
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 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) {
// 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 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) {
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 @@ void G1ConcurrentMark::cleanup() {
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 @@ void G1ConcurrentMark::cleanup() {
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");

@ -245,8 +245,8 @@ void G1FullCollector::run_task(AbstractGangTask* task) {
}
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 @@ void G1FullCollector::verify_after_marking() {
// 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);
}

@ -376,6 +376,37 @@ public:
}
};
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 @@ void G1HeapVerifier::prepare_for_verify() {
}
}
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);
}
}

@ -34,6 +34,7 @@ class G1CollectedHeap;
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 @@ private:
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 @@ public:
#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

@ -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 @@ void GCArguments::select_gc_ergonomically() {
#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 @@ void GCArguments::initialize_flags() {
#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");

@ -46,8 +46,16 @@ public:
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;

@ -768,6 +768,7 @@ jint Universe::initialize_heap() {
}
log_info(gc)("Using %s", _collectedHeap->name());
GCArguments::arguments()->post_heap_initialize();
ThreadLocalAllocBuffer::set_max_size(Universe::heap()->max_tlab_size());
#ifdef _LP64

@ -2268,6 +2268,10 @@ public:
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 " \

@ -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));
}

@ -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
}
}
}