diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index ad971da5fcd..98de36cd59f 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -367,9 +367,7 @@ public: _failures = true; } } else { - bool failures = false; - r->verify(_vo, &failures); - if (failures) { + if (r->verify(_vo)) { _failures = true; } else if (!r->is_starts_humongous()) { VerifyObjsInRegionClosure not_dead_yet_cl(r, _vo); diff --git a/src/hotspot/share/gc/g1/g1OopClosures.cpp b/src/hotspot/share/gc/g1/g1OopClosures.cpp index b6fbc0ce356..50f76de3250 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.cpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.cpp @@ -58,16 +58,3 @@ 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 -} \ No newline at end of file diff --git a/src/hotspot/share/gc/g1/g1OopClosures.hpp b/src/hotspot/share/gc/g1/g1OopClosures.hpp index d70fb9343d4..f585dbdeb2d 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.hpp @@ -234,36 +234,4 @@ 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 - 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 diff --git a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp index b1a0c4abc33..fb7b0dafa15 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp @@ -272,47 +272,4 @@ template void G1RebuildRemSetClosure::do_oop_work(T* p) { } } -template -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(G1RareEvent_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 diff --git a/src/hotspot/share/gc/g1/heapRegion.cpp b/src/hotspot/share/gc/g1/heapRegion.cpp index f7caa250a7d..fa529dc1336 100644 --- a/src/hotspot/share/gc/g1/heapRegion.cpp +++ b/src/hotspot/share/gc/g1/heapRegion.cpp @@ -386,10 +386,10 @@ public: bool failures() { return _failures; } }; -void HeapRegion::verify_code_roots(VerifyOption vo, bool* failures) const { +bool HeapRegion::verify_code_roots(VerifyOption vo) const { if (!G1VerifyHeapRegionCodeRoots) { // We're not verifying code roots. - return; + return false; } if (vo == VerifyOption::G1UseFullMarking) { // Marking verification during a full GC is performed after class @@ -399,7 +399,7 @@ void HeapRegion::verify_code_roots(VerifyOption vo, bool* failures) const { // actual GC. Skip verifying the code roots in this particular // time. assert(VerifyDuringGC, "only way to get here"); - return; + return false; } HeapRegionRemSet* hrrs = rem_set(); @@ -408,29 +408,27 @@ void HeapRegion::verify_code_roots(VerifyOption vo, bool* failures) const { // if this region is empty then there should be no entries // on its code root list if (is_empty()) { - if (code_roots_length > 0) { + bool has_code_roots = code_roots_length > 0; + if (has_code_roots) { log_error(gc, verify)("region " HR_FORMAT " is empty but has " SIZE_FORMAT " code root entries", HR_FORMAT_PARAMS(this), code_roots_length); - *failures = true; } - return; + return has_code_roots; } if (is_continues_humongous()) { - if (code_roots_length > 0) { + bool has_code_roots = code_roots_length > 0; + if (has_code_roots) { log_error(gc, verify)("region " HR_FORMAT " is a continuation of a humongous region but has " SIZE_FORMAT " code root entries", HR_FORMAT_PARAMS(this), code_roots_length); - *failures = true; } - return; + return has_code_roots; } VerifyCodeRootCodeBlobClosure cb_cl(this); code_roots_do(&cb_cl); - if (cb_cl.failures()) { - *failures = true; - } + return cb_cl.failures(); } void HeapRegion::print() const { print_on(tty); } @@ -459,34 +457,100 @@ void HeapRegion::print_on(outputStream* st) const { st->print_cr(""); } -class VerifyRemSetClosure : public G1VerificationClosure { +static bool is_oop_safe(oop obj) { + if (!oopDesc::is_oop(obj)) { + log_error(gc, verify)(PTR_FORMAT " not an oop", p2i(obj)); + return false; + } + + // Now examine the Klass a little more closely. + Klass* klass = obj->klass_raw(); + + bool is_metaspace_object = Metaspace::contains(klass); + if (!is_metaspace_object) { + log_error(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " " + "not metadata", p2i(klass), p2i(obj)); + return false; + } else if (!klass->is_klass()) { + log_error(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " " + "not a klass", p2i(klass), p2i(obj)); + return false; + } + + return true; +} + +// Closure that glues together validity check for oop references (first), +// then optionally verifies the remembered set for that reference. +class G1VerifyLiveAndRemSetClosure : public BasicOopIterateClosure { + using CardValue = CardTable::CardValue; + + G1CollectedHeap* _g1h; + VerifyOption _vo; + bool _verify_remsets; + + oop _containing_obj; + + size_t _num_failures; + G1CardTable *_ct; - template - void do_oop_work(T* p) { - assert(_containing_obj != nullptr, "Precondition"); - assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo), "Precondition"); + bool has_failures() const { return _num_failures != 0; } - T heap_oop = RawAccess<>::oop_load(p); - if (CompressedOops::is_null(heap_oop)) { - return; + void print_object(outputStream* out, oop obj) { +#ifdef PRODUCT + obj->print_name_on(out); +#else // PRODUCT + obj->print_on(out); +#endif // PRODUCT + } + + template + bool verify_liveness(T* p, oop obj) { + bool is_in_heap = _g1h->is_in(obj); + + if (!is_in_heap || _g1h->is_obj_dead_cond(obj, _vo)) { + ResourceMark rm; + Log(gc, verify) log; + LogStream ls(log.error()); + + MutexLocker x(G1RareEvent_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++; + return true; } + return false; + } - oop obj = CompressedOops::decode_raw_not_null(heap_oop); - + template + void verify_remset(T* p, oop obj) { 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()) { + HeapRegion* to = _g1h->heap_region_containing(obj); + if (from != to && !from->is_young() && 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(); + CardValue cv_obj = *_ct->byte_for_const(_containing_obj); + CardValue cv_field = *_ct->byte_for_const(p); + const CardValue dirty = G1CardTable::dirty_card_val(); - bool is_bad = !(from->is_young() || - to->rem_set()->contains_reference(p) || + bool is_bad = !(to->rem_set()->contains_reference(p) || (_containing_obj->is_objArray() ? cv_field == dirty : cv_obj == dirty || cv_field == dirty)); @@ -507,9 +571,7 @@ class VerifyRemSetClosure : public G1VerificationClosure { _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); - } + print_object(&ls, obj); log.error("Obj head CTE = %d, field CTE = %d.", cv_obj, cv_field); log.error("----------"); _num_failures++; @@ -517,92 +579,84 @@ class VerifyRemSetClosure : public G1VerificationClosure { } } -public: - VerifyRemSetClosure(G1CollectedHeap* g1h, VerifyOption vo) : G1VerificationClosure(g1h, vo), _ct(g1h->card_table()) {} + template + void do_oop_work(T* p) { + assert(_containing_obj != nullptr, "must be"); + assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo), "Precondition"); - 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. -class G1Mux2Closure : public BasicOopIterateClosure { - OopClosure* _c1; - OopClosure* _c2; -public: - G1Mux2Closure(OopClosure *c1, OopClosure *c2) { _c1 = c1; _c2 = c2; } - template inline void do_oop_work(T* p) { - // Apply first closure; then apply the second. - _c1->do_oop(p); - _c2->do_oop(p); - } - virtual inline void do_oop(oop* p) { do_oop_work(p); } - virtual inline void do_oop(narrowOop* p) { do_oop_work(p); } -}; - -void HeapRegion::verify(VerifyOption vo, - bool* failures) const { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - *failures = false; - HeapWord* p = bottom(); - HeapWord* prev_p = NULL; - 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. - assert(!is_region_humongous || is_starts_humongous(), "invariant"); - size_t object_num = 0; - while (p < top()) { - oop obj = cast_to_oop(p); - size_t obj_size = block_size(p); - object_num += 1; - - if (!g1h->is_obj_dead_cond(obj, this, vo)) { - if (oopDesc::is_oop(obj)) { - Klass* klass = obj->klass(); - bool is_metaspace_object = Metaspace::contains(klass); - if (!is_metaspace_object) { - log_error(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " " - "not metadata", p2i(klass), p2i(obj)); - *failures = true; - return; - } else if (!klass->is_klass()) { - log_error(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " " - "not a klass", p2i(klass), p2i(obj)); - *failures = true; - return; - } else { - vl_cl.set_containing_obj(obj); - if (!g1h->collector_state()->in_full_gc() || G1VerifyRSetsDuringFullGC) { - // verify liveness and rem_set - vr_cl.set_containing_obj(obj); - G1Mux2Closure mux(&vl_cl, &vr_cl); - obj->oop_iterate(&mux); - - if (vr_cl.has_failures()) { - *failures = true; - } - if (vr_cl.num_failures() >= G1MaxVerifyFailures) { - return; - } - } else { - // verify only liveness - obj->oop_iterate(&vl_cl); - } - if (vl_cl.has_failures()) { - *failures = true; - } - if (vl_cl.num_failures() >= G1MaxVerifyFailures) { - return; - } - } - } else { - log_error(gc, verify)(PTR_FORMAT " not an oop", p2i(obj)); - *failures = true; - return; - } + T heap_oop = RawAccess<>::oop_load(p); + if (CompressedOops::is_null(heap_oop)) { + return; } - prev_p = p; - p += obj_size; + oop obj = CompressedOops::decode_not_null(heap_oop); + + bool is_live = verify_liveness(p, obj); + if (is_live && _verify_remsets) { + verify_remset(p, obj); + } + } + +public: + G1VerifyLiveAndRemSetClosure(G1CollectedHeap* g1h, VerifyOption vo, bool verify_remsets) : + _g1h(G1CollectedHeap::heap()), + _vo(vo), + _verify_remsets(verify_remsets), + _containing_obj(nullptr), + _num_failures(0), + _ct(_g1h->card_table()) { } + + void set_containing_obj(oop const obj) { + _containing_obj = obj; + } + + size_t num_failures() const { return _num_failures; } + + virtual inline void do_oop(narrowOop* p) { do_oop_work(p); } + virtual inline void do_oop(oop* p) { do_oop_work(p); } +}; + +bool HeapRegion::verify_liveness_and_remset(VerifyOption vo) const { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + + bool verify_rem_sets = !g1h->collector_state()->in_full_gc() || G1VerifyRSetsDuringFullGC; + G1VerifyLiveAndRemSetClosure cl(g1h, vo, verify_rem_sets); + + size_t other_failures = 0; + + HeapWord* p; + for (p = bottom(); p < top(); p += block_size(p)) { + oop obj = cast_to_oop(p); + + if (g1h->is_obj_dead_cond(obj, this, vo)) { + continue; + } + + if (is_oop_safe(obj)) { + cl.set_containing_obj(obj); + obj->oop_iterate(&cl); + } else { + other_failures++; + } + + if ((cl.num_failures() + other_failures) >= G1MaxVerifyFailures) { + return true; + } + } + + if (!is_humongous() && p != top()) { + log_error(gc, verify)("end of last object " PTR_FORMAT " does not match top " PTR_FORMAT, + p2i(p), p2i(top())); + return true; + } + return false; +} + +bool HeapRegion::verify(VerifyOption vo) const { + // We cast p to an oop, so region-bottom must be an obj-start. + assert(!is_humongous() || is_starts_humongous(), "invariant"); + + if (verify_liveness_and_remset(vo)) { + return true; } // Only regions in old generation contain valid BOT. @@ -610,23 +664,15 @@ void HeapRegion::verify(VerifyOption vo, _bot_part.verify(); } - if (is_region_humongous) { + if (is_humongous()) { oop obj = cast_to_oop(this->humongous_start_region()->bottom()); if (cast_from_oop(obj) > bottom() || cast_from_oop(obj) + obj->size() < bottom()) { log_error(gc, verify)("this humongous region is not part of its' humongous object " PTR_FORMAT, p2i(obj)); - *failures = true; - return; + return true; } } - if (!is_region_humongous && p != top()) { - log_error(gc, verify)("end of last object " PTR_FORMAT " " - "does not match top " PTR_FORMAT, p2i(p), p2i(top())); - *failures = true; - return; - } - - verify_code_roots(vo, failures); + return verify_code_roots(vo); } void HeapRegion::clear(bool mangle_space) { diff --git a/src/hotspot/share/gc/g1/heapRegion.hpp b/src/hotspot/share/gc/g1/heapRegion.hpp index 4bccda28bf2..08d6c26f830 100644 --- a/src/hotspot/share/gc/g1/heapRegion.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.hpp @@ -579,12 +579,14 @@ public: // Verify that the entries on the code root list for this // region are live and include at least one pointer into this region. - void verify_code_roots(VerifyOption vo, bool* failures) const; + // Returns whether there has been a failure. + bool verify_code_roots(VerifyOption vo) const; + bool verify_liveness_and_remset(VerifyOption vo) const; void print() const; void print_on(outputStream* st) const; - void verify(VerifyOption vo, bool* failures) const; + bool verify(VerifyOption vo) const; }; // HeapRegionClosure is used for iterating over regions. diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index eeacc6c4570..44a0744c0a3 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -223,7 +223,7 @@ bool CollectedHeap::is_oop(oop object) const { return false; } - if (is_in(object->klass_or_null())) { + if (is_in(object->klass_raw())) { return false; } diff --git a/src/hotspot/share/oops/oop.cpp b/src/hotspot/share/oops/oop.cpp index f64d76145fd..c9a39c7cad6 100644 --- a/src/hotspot/share/oops/oop.cpp +++ b/src/hotspot/share/oops/oop.cpp @@ -53,6 +53,16 @@ void oopDesc::print_address_on(outputStream* st) const { } +void oopDesc::print_name_on(outputStream* st) const { + if (*((juint*)this) == badHeapWordVal) { + st->print_cr("BAD WORD"); + } else if (*((juint*)this) == badMetaWordVal) { + st->print_cr("BAD META WORD"); + } else { + st->print_cr("%s", klass()->external_name()); + } +} + void oopDesc::print() { print_on(tty); } void oopDesc::print_address() { print_address_on(tty); } diff --git a/src/hotspot/share/oops/oop.hpp b/src/hotspot/share/oops/oop.hpp index b0ef4ef03af..9f8e0ce1f2b 100644 --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp @@ -225,9 +225,10 @@ class oopDesc { void release_address_field_put(int offset, address contents); // printing functions for VM debugging - void print_on(outputStream* st) const; // First level print - void print_value_on(outputStream* st) const; // Second level print. + void print_on(outputStream* st) const; // First level print + void print_value_on(outputStream* st) const; // Second level print. void print_address_on(outputStream* st) const; // Address printing + void print_name_on(outputStream* st) const; // External name printing. // printing on default output stream void print();