8302206: Factor out duplicate G1VerificationClosure

Reviewed-by: kbarrett, ayang
This commit is contained in:
Thomas Schatzl 2023-02-20 10:21:21 +00:00
parent eaae0baeba
commit e971f90a0b
11 changed files with 150 additions and 228 deletions

@ -42,7 +42,7 @@ G1FullGCMarker::G1FullGCMarker(G1FullCollector* collector,
_objarray_stack(),
_preserved_stack(preserved_stack),
_mark_closure(worker_id, this, ClassLoaderData::_claim_stw_fullgc_mark, G1CollectedHeap::heap()->ref_processor_stw()),
_verify_closure(VerifyOption::G1UseFullMarking),
_verify_closure(G1CollectedHeap::heap(), VerifyOption::G1UseFullMarking),
_stack_closure(this),
_cld_closure(mark_closure(), ClassLoaderData::_claim_stw_fullgc_mark),
_mark_stats_cache(mark_stats, G1RegionMarkStatsCache::RegionMarkStatsCacheSize) {

@ -26,6 +26,7 @@
#define SHARE_GC_G1_G1FULLGCMARKER_HPP
#include "gc/g1/g1FullGCOopClosures.hpp"
#include "gc/g1/g1OopClosures.hpp"
#include "gc/g1/g1RegionMarkStatsCache.hpp"
#include "gc/shared/preservedMarks.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
@ -62,7 +63,7 @@ class G1FullGCMarker : public CHeapObj<mtGC> {
// Marking closures
G1MarkAndPushClosure _mark_closure;
G1VerifyOopClosure _verify_closure;
G1VerifyLiveClosure _verify_closure;
G1FollowStackClosure _stack_closure;
CLDToOopClosure _cld_closure;
StringDedup::Requests _string_dedup_requests;

@ -33,6 +33,7 @@
#include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp"
#include "gc/g1/g1FullCollector.inline.hpp"
#include "gc/g1/g1FullGCOopClosures.inline.hpp"
#include "gc/g1/g1OopClosures.inline.hpp"
#include "gc/g1/g1RegionMarkStatsCache.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/shared/continuationGCSupport.inline.hpp"
@ -126,7 +127,7 @@ void G1FullGCMarker::follow_array_chunk(objArrayOop array, int index) {
if (VerifyDuringGC) {
_verify_closure.set_containing_obj(array);
array->oop_iterate_range(&_verify_closure, beg_index, end_index);
if (_verify_closure.failures()) {
if (_verify_closure.has_failures()) {
fatal("there should not have been any failures");
}
}
@ -146,8 +147,7 @@ inline void G1FullGCMarker::follow_object(oop obj) {
}
_verify_closure.set_containing_obj(obj);
obj->oop_iterate(&_verify_closure);
if (_verify_closure.failures()) {
log_warning(gc, verify)("Failed after %d", _verify_closure._cc);
if (_verify_closure.has_failures()) {
fatal("there should not have been any failures");
}
}

@ -40,62 +40,3 @@ void G1FollowStackClosure::do_void() { _marker->follow_marking_stacks(); }
void G1FullKeepAliveClosure::do_oop(oop* p) { do_oop_work(p); }
void G1FullKeepAliveClosure::do_oop(narrowOop* p) { do_oop_work(p); }
G1VerifyOopClosure::G1VerifyOopClosure(VerifyOption option) :
_g1h(G1CollectedHeap::heap()),
_failures(false),
_containing_obj(NULL),
_verify_option(option),
_cc(0) {
}
void G1VerifyOopClosure::print_object(outputStream* out, oop obj) {
#ifdef PRODUCT
Klass* k = obj->klass();
const char* class_name = InstanceKlass::cast(k)->external_name();
out->print_cr("class name %s", class_name);
#else // PRODUCT
obj->print_on(out);
#endif // PRODUCT
}
template <class T> void G1VerifyOopClosure::do_oop_work(T* p) {
T heap_oop = RawAccess<>::oop_load(p);
if (!CompressedOops::is_null(heap_oop)) {
_cc++;
oop obj = CompressedOops::decode_not_null(heap_oop);
bool failed = false;
if (!_g1h->is_in(obj) || _g1h->is_obj_dead_cond(obj, _verify_option)) {
MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
LogStreamHandle(Error, gc, verify) yy;
if (!_failures) {
yy.cr();
yy.print_cr("----------");
}
if (!_g1h->is_in(obj)) {
HeapRegion* from = _g1h->heap_region_containing(p);
yy.print_cr("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region " HR_FORMAT,
p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from));
print_object(&yy, _containing_obj);
yy.print_cr("points to obj " PTR_FORMAT " not in the heap",
p2i(obj));
} else {
HeapRegion* from = _g1h->heap_region_containing(p);
HeapRegion* to = _g1h->heap_region_containing(obj);
yy.print_cr("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region " HR_FORMAT,
p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from));
print_object(&yy, _containing_obj);
yy.print_cr("points to dead obj " PTR_FORMAT " in region " HR_FORMAT,
p2i(obj), HR_FORMAT_PARAMS(to));
print_object(&yy, obj);
}
yy.print_cr("----------");
yy.flush();
_failures = true;
failed = true;
}
}
}
template void G1VerifyOopClosure::do_oop_work(oop*);
template void G1VerifyOopClosure::do_oop_work(narrowOop*);

@ -86,30 +86,6 @@ public:
virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; }
};
class G1VerifyOopClosure: public BasicOopIterateClosure {
private:
G1CollectedHeap* _g1h;
bool _failures;
oop _containing_obj;
VerifyOption _verify_option;
public:
int _cc;
G1VerifyOopClosure(VerifyOption option);
void set_containing_obj(oop obj) {
_containing_obj = obj;
}
bool failures() { return _failures; }
void print_object(outputStream* out, oop obj);
template <class T> void do_oop_work(T* p);
void do_oop(oop* p) { do_oop_work(p); }
void do_oop(narrowOop* p) { do_oop_work(p); }
};
class G1FollowStackClosure: public VoidClosure {
G1FullGCMarker* _marker;

@ -58,3 +58,16 @@ void G1CLDScanClosure::do_cld(ClassLoaderData* cld) {
}
_count++;
}
G1VerificationClosure::G1VerificationClosure(G1CollectedHeap* g1h, VerifyOption vo) :
_g1h(g1h), _containing_obj(nullptr), _num_failures(0), _vo(vo) { }
void G1VerificationClosure::print_object(outputStream* out, oop obj) {
#ifdef PRODUCT
Klass* k = obj->klass();
const char* class_name = k->external_name();
out->print_cr("class name %s", class_name);
#else // PRODUCT
obj->print_on(out);
#endif // PRODUCT
}

@ -234,4 +234,36 @@ public:
virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; }
};
class G1VerificationClosure : public BasicOopIterateClosure {
protected:
G1CollectedHeap* _g1h;
oop _containing_obj;
size_t _num_failures;
VerifyOption _vo;
public:
G1VerificationClosure(G1CollectedHeap* g1h, VerifyOption vo);
void set_containing_obj(oop obj) {
_containing_obj = obj;
}
bool has_failures() { return _num_failures != 0; }
size_t num_failures() { return _num_failures; }
void print_object(outputStream* out, oop obj);
};
class G1VerifyLiveClosure : public G1VerificationClosure {
template <class T>
inline void do_oop_work(T* p);
public:
G1VerifyLiveClosure(G1CollectedHeap* g1h, VerifyOption vo) : G1VerificationClosure(g1h, vo) {}
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop(oop* p) { do_oop_work(p); }
};
#endif // SHARE_GC_G1_G1OOPCLOSURES_HPP

@ -27,12 +27,14 @@
#include "gc/g1/g1OopClosures.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1ConcurrentMark.inline.hpp"
#include "gc/g1/g1ParScanThreadState.inline.hpp"
#include "gc/g1/g1RemSet.hpp"
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/g1/heapRegionRemSet.inline.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/iterator.inline.hpp"
#include "oops/access.inline.hpp"
#include "oops/compressedOops.inline.hpp"
@ -270,4 +272,47 @@ template <class T> void G1RebuildRemSetClosure::do_oop_work(T* p) {
}
}
template <class T>
inline void G1VerifyLiveClosure::do_oop_work(T* p) {
assert(_containing_obj != nullptr, "Precondition");
assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo), "Precondition");
T heap_oop = RawAccess<>::oop_load(p);
if (CompressedOops::is_null(heap_oop)) {
return;
}
ResourceMark rm;
Log(gc, verify) log;
LogStream ls(log.error());
oop obj = CompressedOops::decode_raw_not_null(heap_oop);
bool is_in_heap = _g1h->is_in(obj);
if (!is_in_heap || _g1h->is_obj_dead_cond(obj, _vo)) {
MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
if (!has_failures()) {
log.error("----------");
}
HeapRegion* from = _g1h->heap_region_containing(p);
log.error("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region " HR_FORMAT,
p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from));
print_object(&ls, _containing_obj);
if (!is_in_heap) {
log.error("points to address " PTR_FORMAT " outside of heap", p2i(obj));
} else {
HeapRegion* to = _g1h->heap_region_containing(obj);
log.error("points to dead obj " PTR_FORMAT " in region " HR_FORMAT " remset %s",
p2i(obj), HR_FORMAT_PARAMS(to), to->rem_set()->get_state_str());
print_object(&ls, obj);
}
log.error("----------");
_num_failures++;
}
}
#endif // SHARE_GC_G1_G1OOPCLOSURES_INLINE_HPP

@ -213,10 +213,9 @@
"set container.") \
range(1, 100) \
\
develop(intx, G1MaxVerifyFailures, -1, \
"The maximum number of verification failures to print. " \
"-1 means print all.") \
range(-1, max_jint) \
develop(size_t, G1MaxVerifyFailures, SIZE_MAX, \
"The maximum number of verification failures to print.") \
range(1, SIZE_MAX) \
\
product(uintx, G1ReservePercent, 10, \
"It determines the minimum reserve we should have in the heap " \

@ -459,47 +459,13 @@ void HeapRegion::print_on(outputStream* st) const {
st->print_cr("");
}
class G1VerificationClosure : public BasicOopIterateClosure {
protected:
G1CollectedHeap* _g1h;
class VerifyRemSetClosure : public G1VerificationClosure {
G1CardTable *_ct;
oop _containing_obj;
bool _failures;
int _n_failures;
VerifyOption _vo;
public:
G1VerificationClosure(G1CollectedHeap* g1h, VerifyOption vo) :
_g1h(g1h), _ct(g1h->card_table()),
_containing_obj(NULL), _failures(false), _n_failures(0), _vo(vo) {
}
void set_containing_obj(oop obj) {
_containing_obj = obj;
}
bool failures() { return _failures; }
int n_failures() { return _n_failures; }
void print_object(outputStream* out, oop obj) {
#ifdef PRODUCT
Klass* k = obj->klass();
const char* class_name = k->external_name();
out->print_cr("class name %s", class_name);
#else // PRODUCT
obj->print_on(out);
#endif // PRODUCT
}
};
class VerifyLiveClosure : public G1VerificationClosure {
template <class T>
void do_oop_work(T* p) {
assert(_containing_obj != NULL, "Precondition");
assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo),
"Precondition");
assert(_containing_obj != nullptr, "Precondition");
assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo), "Precondition");
T heap_oop = RawAccess<>::oop_load(p);
if (CompressedOops::is_null(heap_oop)) {
@ -507,103 +473,55 @@ class VerifyLiveClosure : public G1VerificationClosure {
}
oop obj = CompressedOops::decode_raw_not_null(heap_oop);
bool is_in_heap = _g1h->is_in(obj);
if (!is_in_heap || _g1h->is_obj_dead_cond(obj, _vo)) {
MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
Log(gc, verify) log;
if (!_failures) {
log.error("----------");
}
ResourceMark rm;
HeapRegion* from = _g1h->heap_region_containing(p);
log.error("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region " HR_FORMAT,
p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from));
LogStream ls(log.error());
print_object(&ls, _containing_obj);
if (!is_in_heap) {
log.error("points to address " PTR_FORMAT " outside of heap", p2i(obj));
} else {
HeapRegion* to = _g1h->heap_region_containing(obj);
log.error("points to dead obj " PTR_FORMAT " in region " HR_FORMAT " remset %s",
p2i(obj), HR_FORMAT_PARAMS(to), to->rem_set()->get_state_str());
print_object(&ls, obj);
}
log.error("----------");
_failures = true;
_n_failures++;
}
}
public:
VerifyLiveClosure(G1CollectedHeap* g1h, VerifyOption vo) : G1VerificationClosure(g1h, vo) {}
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop(oop* p) { do_oop_work(p); }
};
class VerifyRemSetClosure : public G1VerificationClosure {
public:
VerifyRemSetClosure(G1CollectedHeap* g1h, VerifyOption vo) : G1VerificationClosure(g1h, vo) {}
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop(oop* p) { do_oop_work(p); }
template <class T>
void do_oop_work(T* p) {
assert(_containing_obj != NULL, "Precondition");
assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo),
"Precondition");
verify_remembered_set(p);
}
template <class T>
void verify_remembered_set(T* p) {
T heap_oop = RawAccess<>::oop_load(p);
Log(gc, verify) log;
if (!CompressedOops::is_null(heap_oop)) {
oop obj = CompressedOops::decode_not_null(heap_oop);
HeapRegion* from = _g1h->heap_region_containing(p);
HeapRegion* to = _g1h->heap_region_containing(obj);
if (from != NULL && to != NULL &&
HeapRegion* from = _g1h->heap_region_containing(p);
HeapRegion* to = _g1h->heap_region_containing_or_null(obj);
if (to != nullptr &&
from != to &&
!to->is_pinned() &&
to->rem_set()->is_complete()) {
jbyte cv_obj = *_ct->byte_for_const(_containing_obj);
jbyte cv_field = *_ct->byte_for_const(p);
const jbyte dirty = G1CardTable::dirty_card_val();
bool is_bad = !(from->is_young()
|| to->rem_set()->contains_reference(p)
|| (_containing_obj->is_objArray() ?
cv_field == dirty :
cv_obj == dirty || cv_field == dirty));
if (is_bad) {
MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
jbyte cv_obj = *_ct->byte_for_const(_containing_obj);
jbyte cv_field = *_ct->byte_for_const(p);
const jbyte dirty = G1CardTable::dirty_card_val();
if (!_failures) {
log.error("----------");
}
log.error("Missing rem set entry:");
log.error("Field " PTR_FORMAT " of obj " PTR_FORMAT " in region " HR_FORMAT,
p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from));
ResourceMark rm;
LogStream ls(log.error());
_containing_obj->print_on(&ls);
log.error("points to obj " PTR_FORMAT " in region " HR_FORMAT " remset %s",
p2i(obj), HR_FORMAT_PARAMS(to), to->rem_set()->get_state_str());
if (oopDesc::is_oop(obj)) {
obj->print_on(&ls);
}
log.error("Obj head CTE = %d, field CTE = %d.", cv_obj, cv_field);
bool is_bad = !(from->is_young() ||
to->rem_set()->contains_reference(p) ||
(_containing_obj->is_objArray() ?
cv_field == dirty :
cv_obj == dirty || cv_field == dirty));
if (is_bad) {
ResourceMark rm;
Log(gc, verify) log;
LogStream ls(log.error());
MutexLocker x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
if (!has_failures()) {
log.error("----------");
_failures = true;
_n_failures++;
}
log.error("Missing rem set entry:");
log.error("Field " PTR_FORMAT " of obj " PTR_FORMAT " in region " HR_FORMAT,
p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from));
_containing_obj->print_on(&ls);
log.error("points to obj " PTR_FORMAT " in region " HR_FORMAT " remset %s",
p2i(obj), HR_FORMAT_PARAMS(to), to->rem_set()->get_state_str());
if (oopDesc::is_oop(obj)) {
obj->print_on(&ls);
}
log.error("Obj head CTE = %d, field CTE = %d.", cv_obj, cv_field);
log.error("----------");
_num_failures++;
}
}
}
public:
VerifyRemSetClosure(G1CollectedHeap* g1h, VerifyOption vo) : G1VerificationClosure(g1h, vo), _ct(g1h->card_table()) {}
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop(oop* p) { do_oop_work(p); }
};
// Closure that applies the given two closures in sequence.
@ -627,7 +545,7 @@ void HeapRegion::verify(VerifyOption vo,
*failures = false;
HeapWord* p = bottom();
HeapWord* prev_p = NULL;
VerifyLiveClosure vl_cl(g1h, vo);
G1VerifyLiveClosure vl_cl(g1h, vo);
VerifyRemSetClosure vr_cl(g1h, vo);
bool is_region_humongous = is_humongous();
// We cast p to an oop, so region-bottom must be an obj-start.
@ -660,22 +578,20 @@ void HeapRegion::verify(VerifyOption vo,
G1Mux2Closure mux(&vl_cl, &vr_cl);
obj->oop_iterate(&mux);
if (vr_cl.failures()) {
if (vr_cl.has_failures()) {
*failures = true;
}
if (G1MaxVerifyFailures >= 0 &&
vr_cl.n_failures() >= G1MaxVerifyFailures) {
if (vr_cl.num_failures() >= G1MaxVerifyFailures) {
return;
}
} else {
// verify only liveness
obj->oop_iterate(&vl_cl);
}
if (vl_cl.failures()) {
if (vl_cl.has_failures()) {
*failures = true;
}
if (G1MaxVerifyFailures >= 0 &&
vl_cl.n_failures() >= G1MaxVerifyFailures) {
if (vl_cl.num_failures() >= G1MaxVerifyFailures) {
return;
}
}
@ -728,11 +644,10 @@ void HeapRegion::verify_rem_set(VerifyOption vo, bool* failures) const {
vr_cl.set_containing_obj(obj);
obj->oop_iterate(&vr_cl);
if (vr_cl.failures()) {
if (vr_cl.has_failures()) {
*failures = true;
}
if (G1MaxVerifyFailures >= 0 &&
vr_cl.n_failures() >= G1MaxVerifyFailures) {
if (vr_cl.num_failures() >= G1MaxVerifyFailures) {
return;
}
} else {

@ -584,9 +584,9 @@ public:
void print() const;
void print_on(outputStream* st) const;
void verify(VerifyOption vo, bool *failures) const;
void verify(VerifyOption vo, bool* failures) const;
void verify_rem_set(VerifyOption vo, bool *failures) const;
void verify_rem_set(VerifyOption vo, bool* failures) const;
void verify_rem_set() const;
};