diff options
| -rw-r--r-- | runtime/gc/collector/concurrent_copying.cc | 181 | ||||
| -rw-r--r-- | runtime/gc/collector/concurrent_copying.h | 12 | ||||
| -rw-r--r-- | runtime/gc/space/region_space.cc | 6 | ||||
| -rw-r--r-- | runtime/gc/space/region_space.h | 2 | ||||
| -rw-r--r-- | runtime/gc/verification.cc | 2 | ||||
| -rw-r--r-- | runtime/mirror/object-inl.h | 3 |
6 files changed, 159 insertions, 47 deletions
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 1e0c0b16e4..f6e74dab28 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -440,7 +440,7 @@ class ConcurrentCopying::FlipCallback : public Closure { if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) { cc->GrayAllNewlyDirtyImmuneObjects(); if (kIsDebugBuild) { - // Check that all non-gray immune objects only refernce immune objects. + // Check that all non-gray immune objects only reference immune objects. cc->VerifyGrayImmuneObjects(); } } @@ -1806,27 +1806,82 @@ void ConcurrentCopying::ReclaimPhase() { } } -// Assert the to-space invariant. +std::string ConcurrentCopying::DumpReferenceInfo(mirror::Object* ref, + const char* ref_name, + std::string indent) { + std::ostringstream oss; + oss << indent << heap_->GetVerification()->DumpObjectInfo(ref, ref_name) << '\n'; + if (ref != nullptr) { + if (kUseBakerReadBarrier) { + oss << indent << ref_name << "->GetMarkBit()=" << ref->GetMarkBit() << '\n'; + oss << indent << ref_name << "->GetReadBarrierState()=" << ref->GetReadBarrierState() << '\n'; + } + } + if (region_space_->HasAddress(ref)) { + oss << indent << "Region containing " << ref_name << ":" << '\n'; + region_space_->DumpRegionForObject(oss, ref); + if (region_space_bitmap_ != nullptr) { + oss << indent << "region_space_bitmap_->Test(" << ref_name << ")=" + << std::boolalpha << region_space_bitmap_->Test(ref) << std::noboolalpha; + } + } + return oss.str(); +} + +std::string ConcurrentCopying::DumpHeapReference(mirror::Object* obj, + MemberOffset offset, + mirror::Object* ref) { + std::ostringstream oss; + std::string indent = " "; + oss << indent << "Invalid reference: ref=" << ref + << " referenced from: object=" << obj << " offset= " << offset << '\n'; + // Information about `obj`. + oss << DumpReferenceInfo(obj, "obj", indent) << '\n'; + // Information about `ref`. + oss << DumpReferenceInfo(ref, "ref", indent); + return oss.str(); +} + void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset, mirror::Object* ref) { - CHECK_EQ(heap_->collector_type_, kCollectorTypeCC); + CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_); if (is_asserting_to_space_invariant_) { - using RegionType = space::RegionSpace::RegionType; - space::RegionSpace::RegionType type = region_space_->GetRegionType(ref); - if (type == RegionType::kRegionTypeToSpace) { - // OK. - return; - } else if (type == RegionType::kRegionTypeUnevacFromSpace) { - CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; - } else if (UNLIKELY(type == RegionType::kRegionTypeFromSpace)) { - // Not OK. Do extra logging. - if (obj != nullptr) { - LogFromSpaceRefHolder(obj, offset); + if (region_space_->HasAddress(ref)) { + // Check to-space invariant in region space (moving space). + using RegionType = space::RegionSpace::RegionType; + space::RegionSpace::RegionType type = region_space_->GetRegionType(ref); + if (type == RegionType::kRegionTypeToSpace) { + // OK. + return; + } else if (type == RegionType::kRegionTypeUnevacFromSpace) { + if (!IsMarkedInUnevacFromSpace(ref)) { + LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:"; + LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref); + } + CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; + } else { + // Not OK: either a from-space ref or a reference in an unused region. + // Do extra logging. + if (type == RegionType::kRegionTypeFromSpace) { + LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:"; + } else { + LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":"; + } + LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref); + if (obj != nullptr) { + LogFromSpaceRefHolder(obj, offset); + } + ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); + LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:"; + region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); + PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); + LOG(FATAL) << "Invalid reference " << ref + << " referenced from object " << obj << " at offset " << offset; } - ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); - CHECK(false) << "Found from-space ref " << ref << " " << ref->PrettyTypeOf(); } else { + // Check to-space invariant in non-moving space. AssertToSpaceInvariantInNonMovingSpace(obj, ref); } } @@ -1857,39 +1912,66 @@ class RootPrinter { } }; +std::string ConcurrentCopying::DumpGcRoot(mirror::Object* ref) { + std::ostringstream oss; + std::string indent = " "; + oss << indent << "Invalid GC root: ref=" << ref << '\n'; + // Information about `ref`. + oss << DumpReferenceInfo(ref, "ref", indent); + return oss.str(); +} + void ConcurrentCopying::AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref) { - CHECK(heap_->collector_type_ == kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_); + CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_); if (is_asserting_to_space_invariant_) { - if (region_space_->IsInToSpace(ref)) { - // OK. - return; - } else if (region_space_->IsInUnevacFromSpace(ref)) { - CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; - } else if (region_space_->IsInFromSpace(ref)) { - // Not OK. Do extra logging. - if (gc_root_source == nullptr) { - // No info. - } else if (gc_root_source->HasArtField()) { - ArtField* field = gc_root_source->GetArtField(); - LOG(FATAL_WITHOUT_ABORT) << "gc root in field " << field << " " - << ArtField::PrettyField(field); - RootPrinter root_printer; - field->VisitRoots(root_printer); - } else if (gc_root_source->HasArtMethod()) { - ArtMethod* method = gc_root_source->GetArtMethod(); - LOG(FATAL_WITHOUT_ABORT) << "gc root in method " << method << " " - << ArtMethod::PrettyMethod(method); - RootPrinter root_printer; - method->VisitRoots(root_printer, kRuntimePointerSize); + if (region_space_->HasAddress(ref)) { + // Check to-space invariant in region space (moving space). + using RegionType = space::RegionSpace::RegionType; + space::RegionSpace::RegionType type = region_space_->GetRegionType(ref); + if (type == RegionType::kRegionTypeToSpace) { + // OK. + return; + } else if (type == RegionType::kRegionTypeUnevacFromSpace) { + if (!IsMarkedInUnevacFromSpace(ref)) { + LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:"; + LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref); + } + CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; + } else { + // Not OK: either a from-space ref or a reference in an unused region. + // Do extra logging. + if (type == RegionType::kRegionTypeFromSpace) { + LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:"; + } else { + LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":"; + } + LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref); + if (gc_root_source == nullptr) { + // No info. + } else if (gc_root_source->HasArtField()) { + ArtField* field = gc_root_source->GetArtField(); + LOG(FATAL_WITHOUT_ABORT) << "gc root in field " << field << " " + << ArtField::PrettyField(field); + RootPrinter root_printer; + field->VisitRoots(root_printer); + } else if (gc_root_source->HasArtMethod()) { + ArtMethod* method = gc_root_source->GetArtMethod(); + LOG(FATAL_WITHOUT_ABORT) << "gc root in method " << method << " " + << ArtMethod::PrettyMethod(method); + RootPrinter root_printer; + method->VisitRoots(root_printer, kRuntimePointerSize); + } + ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); + LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:"; + region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); + PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); + LOG(FATAL) << "Invalid reference " << ref; } - ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); - region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); - MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); - CHECK(false) << "Found from-space ref " << ref << " " << ref->PrettyTypeOf(); } else { - AssertToSpaceInvariantInNonMovingSpace(nullptr, ref); + // Check to-space invariant in non-moving space. + AssertToSpaceInvariantInNonMovingSpace(/* obj */ nullptr, ref); } } } @@ -1944,6 +2026,7 @@ void ConcurrentCopying::LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj, mirror::Object* ref) { + CHECK(!region_space_->HasAddress(ref)) << "obj=" << obj << " ref=" << ref; // In a non-moving spaces. Check that the ref is marked. if (immune_spaces_.ContainsObject(ref)) { if (kUseBakerReadBarrier) { @@ -2428,6 +2511,9 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) { to_ref = nullptr; } } else { + // At this point, `from_ref` should not be in the region space + // (i.e. within an "unused" region). + DCHECK(!region_space_->HasAddress(from_ref)) << from_ref; // from_ref is in a non-moving space. if (immune_spaces_.ContainsObject(from_ref)) { // An immune object is alive. @@ -2598,7 +2684,12 @@ void ConcurrentCopying::FinishPhase() { DCHECK(rb_mark_bit_stack_ != nullptr); const auto* limit = rb_mark_bit_stack_->End(); for (StackReference<mirror::Object>* it = rb_mark_bit_stack_->Begin(); it != limit; ++it) { - CHECK(it->AsMirrorPtr()->AtomicSetMarkBit(1, 0)); + CHECK(it->AsMirrorPtr()->AtomicSetMarkBit(1, 0)) + << "rb_mark_bit_stack_->Begin()" << rb_mark_bit_stack_->Begin() << '\n' + << "rb_mark_bit_stack_->End()" << rb_mark_bit_stack_->End() << '\n' + << "rb_mark_bit_stack_->IsFull()" + << std::boolalpha << rb_mark_bit_stack_->IsFull() << std::noboolalpha << '\n' + << DumpReferenceInfo(it->AsMirrorPtr(), "*it"); } rb_mark_bit_stack_->Reset(); } diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index 8b4b58e7b1..42d37d8395 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -100,8 +100,10 @@ class ConcurrentCopying : public GarbageCollector { space::RegionSpace* RegionSpace() { return region_space_; } + // Assert the to-space invariant for a heap reference `ref` held in `obj` at offset `offset`. void AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset, mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); + // Assert the to-space invariant for a GC root reference `ref`. void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); bool IsInToSpace(mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -232,6 +234,16 @@ class ConcurrentCopying : public GarbageCollector { void ComputeUnevacFromSpaceLiveRatio(); void LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset offset) REQUIRES_SHARED(Locks::mutator_lock_); + // Dump information about reference `ref` and return it as a string. + // Use `ref_name` to name the reference in messages. Each message is prefixed with `indent`. + std::string DumpReferenceInfo(mirror::Object* ref, const char* ref_name, std::string indent = "") + REQUIRES_SHARED(Locks::mutator_lock_); + // Dump information about heap reference `ref`, referenced from object `obj` at offset `offset`, + // and return it as a string. + std::string DumpHeapReference(mirror::Object* obj, MemberOffset offset, mirror::Object* ref) + REQUIRES_SHARED(Locks::mutator_lock_); + // Dump information about GC root `ref` and return it as a string. + std::string DumpGcRoot(mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); void AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj, mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); void ReenableWeakRefAccess(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index a505f32c49..d58b76bd6b 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -435,6 +435,12 @@ void RegionSpace::Dump(std::ostream& os) const { << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit()); } +void RegionSpace::DumpRegionForObject(std::ostream& os, mirror::Object* obj) { + CHECK(HasAddress(obj)); + MutexLock mu(Thread::Current(), region_lock_); + RefToRegionUnlocked(obj)->Dump(os); +} + void RegionSpace::DumpRegions(std::ostream& os) { MutexLock mu(Thread::Current(), region_lock_); for (size_t i = 0; i < num_regions_; ++i) { diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index 55c2772129..0e51f37a62 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -101,6 +101,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { void Dump(std::ostream& os) const; void DumpRegions(std::ostream& os) REQUIRES(!region_lock_); + // Dump region containing object `obj`. Precondition: `obj` is in the region space. + void DumpRegionForObject(std::ostream& os, mirror::Object* obj) REQUIRES(!region_lock_); void DumpNonFreeRegions(std::ostream& os) REQUIRES(!region_lock_); size_t RevokeThreadLocalBuffers(Thread* thread) REQUIRES(!region_lock_); diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc index d99b37762f..fb5db1147f 100644 --- a/runtime/gc/verification.cc +++ b/runtime/gc/verification.cc @@ -140,7 +140,7 @@ bool Verification::IsValidClass(const void* addr) const { if (!IsValidHeapObjectAddress(k1)) { return false; } - // k should be class class, take the class again to verify. + // `k1` should be class class, take the class again to verify. // Note that this check may not be valid for the no image space since the class class might move // around from moving GC. mirror::Class* k2 = k1->GetClass<kVerifyNone, kWithoutReadBarrier>(); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 6e2a07c9e0..968548876e 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -160,7 +160,8 @@ inline bool Object::VerifierInstanceOf(ObjPtr<Class> klass) { template<VerifyObjectFlags kVerifyFlags> inline bool Object::InstanceOf(ObjPtr<Class> klass) { DCHECK(klass != nullptr); - DCHECK(GetClass<kVerifyNone>() != nullptr); + DCHECK(GetClass<kVerifyNone>() != nullptr) + << "this=" << std::hex << reinterpret_cast<uintptr_t>(this) << std::dec; return klass->IsAssignableFrom(GetClass<kVerifyFlags>()); } |