Add card-table verification in marking pause

Card corresponding to a marked object with unmarked reference should be
dirty in the marking pause.

Bug: 382077046
Test: art/test/testrunner/testrunner.py --host
Change-Id: Ifffcb66a99b43f5613b11576a4630465385a0baa
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 512d741..483b875 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -1315,6 +1315,65 @@
   return true;
 }
 
+template <typename Visitor>
+class MarkCompact::VisitReferencesVisitor {
+ public:
+  explicit VisitReferencesVisitor(Visitor visitor) : visitor_(visitor) {}
+
+  ALWAYS_INLINE void operator()(mirror::Object* obj,
+                                MemberOffset offset,
+                                [[maybe_unused]] bool is_static) const
+      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
+    visitor_(obj->GetFieldObject<mirror::Object>(offset));
+  }
+
+  ALWAYS_INLINE void operator()([[maybe_unused]] ObjPtr<mirror::Class> klass,
+                                ObjPtr<mirror::Reference> ref) const
+      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
+    visitor_(ref.Ptr());
+  }
+
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
+    visitor_(root->AsMirrorPtr());
+  }
+
+ private:
+  Visitor visitor_;
+};
+
+void MarkCompact::VerifyNoMissingCardMarks() {
+  if (kVerifyNoMissingCardMarks) {
+    accounting::CardTable* card_table = heap_->GetCardTable();
+    for (const auto& space : heap_->GetContinuousSpaces()) {
+      auto obj_visitor = [&](mirror::Object* obj) {
+        VisitReferencesVisitor ref_visitor([&](mirror::Object* ref)
+            REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+          if (ref != nullptr && !IsMarked(ref)) {
+            CHECK(card_table->IsDirty(obj))
+                << "obj:" << obj << " (" << obj->PrettyTypeOf() << ") ref:" << ref
+                << " card:" << static_cast<int>(card_table->GetCard(obj))
+                << " space:" << space->GetName()
+                << " retention-policy:" << space->GetGcRetentionPolicy();
+          }
+        });
+        // We can't expect referent to hold the assertion.
+        obj->VisitReferences</*kVisitNativeRoots=*/true>(ref_visitor, VoidFunctor());
+      };
+      space->GetMarkBitmap()->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+                                               reinterpret_cast<uintptr_t>(space->End()),
+                                               obj_visitor);
+    }
+  }
+}
+
 class MarkCompact::VerifyRootMarkedVisitor : public SingleRootVisitor {
  public:
   explicit VerifyRootMarkedVisitor(MarkCompact* collector) : collector_(collector) { }
@@ -1351,6 +1410,7 @@
   {
     // Handle the dirty objects as we are a concurrent GC
     WriterMutexLock mu(thread_running_gc_, *Locks::heap_bitmap_lock_);
+    VerifyNoMissingCardMarks();
     {
       MutexLock mu2(thread_running_gc_, *Locks::runtime_shutdown_lock_);
       MutexLock mu3(thread_running_gc_, *Locks::thread_list_lock_);
@@ -4907,41 +4967,7 @@
   heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, ref, this);
 }
 
-template <typename Visitor>
-class MarkCompact::VisitReferencesVisitor {
- public:
-  explicit VisitReferencesVisitor(Visitor visitor) : visitor_(visitor) {}
-
-  ALWAYS_INLINE void operator()(mirror::Object* obj,
-                                MemberOffset offset,
-                                [[maybe_unused]] bool is_static) const
-      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
-    visitor_(obj->GetFieldObject<mirror::Object>(offset));
-  }
-
-  ALWAYS_INLINE void operator()([[maybe_unused]] ObjPtr<mirror::Class> klass,
-                                ObjPtr<mirror::Reference> ref) const
-      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
-    visitor_(ref.Ptr());
-  }
-
-  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
-      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (!root->IsNull()) {
-      VisitRoot(root);
-    }
-  }
-
-  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
-      REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
-    visitor_(root->AsMirrorPtr());
-  }
-
- private:
-  Visitor visitor_;
-};
-
-void MarkCompact::VerifyNoMissingCardMarks() {
+void MarkCompact::VerifyNoMissingGenerationalCardMarks() {
   if (kVerifyNoMissingCardMarks) {
     accounting::CardTable* card_table = heap_->GetCardTable();
     auto obj_visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -5287,7 +5313,7 @@
     // and card-dirtying by a mutator will spuriosely fail.
     ScopedPause pause(this);
     WriterMutexLock mu(thread_running_gc_, *Locks::heap_bitmap_lock_);
-    VerifyNoMissingCardMarks();
+    VerifyNoMissingGenerationalCardMarks();
   }
   if (kVerifyPostGCObjects && use_generational_) {
     ReaderMutexLock mu(thread_running_gc_, *Locks::mutator_lock_);
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 21d626e..f16dcfa 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -684,6 +684,9 @@
 
   // Verify that cards corresponding to objects containing references to
   // young-gen are dirty.
+  void VerifyNoMissingGenerationalCardMarks()
+      REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+  // Verify that card corresponding to a marked object with unmarked reference is dirty.
   void VerifyNoMissingCardMarks() REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
   // Verify that post-GC objects (all objects except the ones allocated after
   // marking pause) are valid with valid references in them. Bitmap corresponding