Improve heap verification for invalid roots.
The new root verification prints the root type and owner thread id as
well as the type of the object.
Also a bit of work for planned multi-threaded verification.
Bug: 14289301
Change-Id: Ia73c517dc11ec6dd82f3d945604ee3836b3db536
diff --git a/runtime/Android.mk b/runtime/Android.mk
index a64e137..1521caa 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -297,6 +297,7 @@
lock_word.h \
mirror/class.h \
oat.h \
+ object_callbacks.h \
quick/inline_method_analyser.h \
thread.h \
thread_state.h \
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 706d1de..d37f2ad 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1861,39 +1861,54 @@
// Verify a reference from an object.
class VerifyReferenceVisitor {
public:
- explicit VerifyReferenceVisitor(Heap* heap, bool verify_referent)
+ explicit VerifyReferenceVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
- : heap_(heap), failed_(false), verify_referent_(verify_referent) {}
+ : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {}
- bool Failed() const {
- return failed_;
+ size_t GetFailureCount() const {
+ return fail_count_->Load();
}
void operator()(mirror::Class* klass, mirror::Reference* ref) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (verify_referent_) {
- this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ VerifyReference(ref, ref->GetReferent(), mirror::Reference::ReferentOffset());
}
}
void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- this->operator()(obj, obj->GetFieldObject<mirror::Object>(offset), offset);
+ VerifyReference(obj, obj->GetFieldObject<mirror::Object>(offset), offset);
}
+ bool IsLive(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
+ return heap_->IsLiveObjectLocked(obj, true, false, true);
+ }
+
+ static void VerifyRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
+ RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
+ if (!visitor->VerifyReference(nullptr, *root, MemberOffset(0))) {
+ LOG(ERROR) << "Root " << *root << " is dead with type " << PrettyTypeOf(*root)
+ << " thread_id= " << thread_id << " root_type= " << root_type;
+ }
+ }
+
+ private:
// TODO: Fix the no thread safety analysis.
- void operator()(mirror::Object* obj, mirror::Object* ref, MemberOffset offset) const
+ // Returns false on failure.
+ bool VerifyReference(mirror::Object* obj, mirror::Object* ref, MemberOffset offset) const
NO_THREAD_SAFETY_ANALYSIS {
if (ref == nullptr || IsLive(ref)) {
// Verify that the reference is live.
- return;
+ return true;
}
- if (!failed_) {
+ if (fail_count_->FetchAndAdd(1) == 0) {
// Print message on only on first failure to prevent spam.
LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!";
- failed_ = true;
}
if (obj != nullptr) {
+ // Only do this part for non roots.
accounting::CardTable* card_table = heap_->GetCardTable();
accounting::ObjectStack* alloc_stack = heap_->allocation_stack_.get();
accounting::ObjectStack* live_stack = heap_->live_stack_.get();
@@ -1972,42 +1987,29 @@
// Search to see if any of the roots reference our reference.
arg = const_cast<void*>(reinterpret_cast<const void*>(ref));
Runtime::Current()->VisitRoots(&RootMatchesObjectVisitor, arg);
- } else {
- LOG(ERROR) << "Root " << ref << " is dead with type " << PrettyTypeOf(ref);
}
+ return false;
}
- bool IsLive(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
- return heap_->IsLiveObjectLocked(obj, true, false, true);
- }
-
- static void VerifyRoots(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
- VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
- (*visitor)(nullptr, *root, MemberOffset(0));
- }
-
- private:
Heap* const heap_;
- mutable bool failed_;
- bool verify_referent_;
+ Atomic<size_t>* const fail_count_;
+ const bool verify_referent_;
};
// Verify all references within an object, for use with HeapBitmap::Visit.
class VerifyObjectVisitor {
public:
- explicit VerifyObjectVisitor(Heap* heap, bool verify_referent)
- : heap_(heap), failed_(false), verify_referent_(verify_referent) {
+ explicit VerifyObjectVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent)
+ : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {
}
void operator()(mirror::Object* obj) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
// Note: we are verifying the references in obj but not obj itself, this is because obj must
// be live or else how did we find it in the live bitmap?
- VerifyReferenceVisitor visitor(heap_, verify_referent_);
+ VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_);
// The class doesn't count as a reference but we should verify it anyways.
obj->VisitReferences<true>(visitor, visitor);
- failed_ = failed_ || visitor.Failed();
}
static void VisitCallback(mirror::Object* obj, void* arg)
@@ -2016,18 +2018,18 @@
visitor->operator()(obj);
}
- bool Failed() const {
- return failed_;
+ size_t GetFailureCount() const {
+ return fail_count_->Load();
}
private:
Heap* const heap_;
- mutable bool failed_;
+ Atomic<size_t>* const fail_count_;
const bool verify_referent_;
};
// Must do this with mutators suspended since we are directly accessing the allocation stacks.
-bool Heap::VerifyHeapReferences(bool verify_referents) {
+size_t Heap::VerifyHeapReferences(bool verify_referents) {
Thread* self = Thread::Current();
Locks::mutator_lock_->AssertExclusiveHeld(self);
// Lets sort our allocation stacks so that we can efficiently binary search them.
@@ -2036,7 +2038,8 @@
// Since we sorted the allocation stack content, need to revoke all
// thread-local allocation stacks.
RevokeAllThreadLocalAllocationStacks(self);
- VerifyObjectVisitor visitor(this, verify_referents);
+ Atomic<size_t> fail_count_(0);
+ VerifyObjectVisitor visitor(this, &fail_count_, verify_referents);
// Verify objects in the allocation stack since these will be objects which were:
// 1. Allocated prior to the GC (pre GC verification).
// 2. Allocated during the GC (pre sweep GC verification).
@@ -2044,8 +2047,8 @@
// pointing to dead objects if they are not reachable.
VisitObjects(VerifyObjectVisitor::VisitCallback, &visitor);
// Verify the roots:
- Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRoots, &visitor);
- if (visitor.Failed()) {
+ Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRootCallback, &visitor);
+ if (visitor.GetFailureCount() > 0) {
// Dump mod-union tables.
for (const auto& table_pair : mod_union_tables_) {
accounting::ModUnionTable* mod_union_table = table_pair.second;
@@ -2057,9 +2060,8 @@
remembered_set->Dump(LOG(ERROR) << remembered_set->GetName() << ": ");
}
DumpSpaces();
- return false;
}
- return true;
+ return visitor.GetFailureCount();
}
class VerifyReferenceCardVisitor {
@@ -2262,8 +2264,10 @@
if (verify_pre_gc_heap_) {
TimingLogger::ScopedSplit split("PreGcVerifyHeapReferences", timings);
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- if (!VerifyHeapReferences()) {
- LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed";
+ size_t failures = VerifyHeapReferences();
+ if (failures > 0) {
+ LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed with " << failures
+ << " failures";
}
}
// Check that all objects which reference things in the live stack are on dirty cards.
@@ -2316,8 +2320,10 @@
SwapSemiSpaces();
// Pass in false since concurrent reference processing can mean that the reference referents
// may point to dead objects at the point which PreSweepingGcVerification is called.
- if (!VerifyHeapReferences(false)) {
- LOG(FATAL) << "Pre sweeping " << gc->GetName() << " GC verification failed";
+ size_t failures = VerifyHeapReferences(false);
+ if (failures > 0) {
+ LOG(FATAL) << "Pre sweeping " << gc->GetName() << " GC verification failed with " << failures
+ << " failures";
}
SwapSemiSpaces();
gc->SwapBitmaps();
@@ -2342,8 +2348,10 @@
if (verify_post_gc_heap_) {
TimingLogger::ScopedSplit split("PostGcVerifyHeapReferences", timings);
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- if (!VerifyHeapReferences()) {
- LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed";
+ size_t failures = VerifyHeapReferences();
+ if (failures > 0) {
+ LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed with " << failures
+ << " failures";
}
}
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index eea2879..6fe0dcf 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -109,8 +109,6 @@
};
std::ostream& operator<<(std::ostream& os, const ProcessState& process_state);
-std::ostream& operator<<(std::ostream& os, const RootType& root_type);
-
class Heap {
public:
// If true, measure the total allocation time.
@@ -218,7 +216,8 @@
// Check sanity of all live references.
void VerifyHeap() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
- bool VerifyHeapReferences(bool verify_referents = true)
+ // Returns how many failures occured.
+ size_t VerifyHeapReferences(bool verify_referents = true)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
bool VerifyMissingCardMarks()
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 767c197..dd8ce16 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -17,6 +17,8 @@
#ifndef ART_RUNTIME_OBJECT_CALLBACKS_H_
#define ART_RUNTIME_OBJECT_CALLBACKS_H_
+// For ostream.
+#include <ostream>
// For uint32_t.
#include <stdint.h>
// For size_t.
@@ -46,6 +48,7 @@
kRootVMInternal,
kRootJNIMonitor,
};
+std::ostream& operator<<(std::ostream& os, const RootType& root_type);
// Returns the new address of the object, returns root if it has not moved. tid and root_type are
// only used by hprof.