Check the live bytes value of a region against the region bitmap.

When the live bytes count of a region is defined (i.e. different
from -1), check that it matches the number of (allocated) bytes
used by live objects in this region, according to the region
space bitmap.

This check is only enabled in debug mode.

Test: art/test.py
Bug: 74064045
Change-Id: I602f5ac7e6a69d52437628844b970a2db9481857
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 0701330..f97b976 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -42,6 +42,9 @@
 // points to a valid, non-protected memory area.
 static constexpr uint32_t kPoisonDeadObject = 0xBADDB01D;  // "BADDROID"
 
+// Whether we check a region's live bytes count against the region bitmap.
+static constexpr bool kCheckLiveBytesAgainstRegionBitmap = kIsDebugBuild;
+
 MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity,
                                   uint8_t* requested_begin) {
   CHECK_ALIGNED(capacity, kRegionSize);
@@ -316,6 +319,9 @@
   };
   for (size_t i = 0; i < std::min(num_regions_, non_free_region_index_limit_); ++i) {
     Region* r = &regions_[i];
+    if (kCheckLiveBytesAgainstRegionBitmap) {
+      CheckLiveBytesAgainstRegionBitmap(r);
+    }
     if (r->IsInFromSpace()) {
       *cleared_bytes += r->BytesAllocated();
       *cleared_objects += r->ObjectsAllocated();
@@ -404,6 +410,42 @@
   num_evac_regions_ = 0;
 }
 
+void RegionSpace::CheckLiveBytesAgainstRegionBitmap(Region* r) {
+  if (r->LiveBytes() == static_cast<size_t>(-1)) {
+    // Live bytes count is undefined for `r`; nothing to check here.
+    return;
+  }
+
+  // Functor walking the region space bitmap for the range corresponding
+  // to region `r` and calculating the sum of live bytes.
+  size_t live_bytes_recount = 0u;
+  auto recount_live_bytes =
+      [&r, &live_bytes_recount](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK_ALIGNED(obj, kAlignment);
+    if (r->IsLarge()) {
+      // If `r` is a large region, then it contains at most one
+      // object, which must start at the beginning of the
+      // region. The live byte count in that case is equal to the
+      // allocated regions (large region + large tails regions).
+      DCHECK_EQ(reinterpret_cast<uint8_t*>(obj), r->Begin());
+      DCHECK_EQ(live_bytes_recount, 0u);
+      live_bytes_recount = r->Top() - r->Begin();
+    } else {
+      DCHECK(r->IsAllocated())
+          << "r->State()=" << r->State() << " r->LiveBytes()=" << r->LiveBytes();
+      size_t obj_size = obj->SizeOf<kDefaultVerifyFlags>();
+      size_t alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
+      live_bytes_recount += alloc_size;
+    }
+  };
+  // Visit live objects in `r` and recount the live bytes.
+  GetLiveBitmap()->VisitMarkedRange(reinterpret_cast<uintptr_t>(r->Begin()),
+                                    reinterpret_cast<uintptr_t>(r->Top()),
+                                    recount_live_bytes);
+  // Check that this recount matches the region's current live bytes count.
+  DCHECK_EQ(live_bytes_recount, r->LiveBytes());
+}
+
 // Poison the memory area in range [`begin`, `end`) with value `kPoisonDeadObject`.
 static void PoisonUnevacuatedRange(uint8_t* begin, uint8_t* end) {
   static constexpr size_t kPoisonDeadObjectSize = sizeof(kPoisonDeadObject);
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index fa33a8a..90f1f1d 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -602,6 +602,11 @@
                                     /* out */ size_t* bytes_tl_bulk_allocated,
                                     /* out */ size_t* next_region = nullptr) REQUIRES(region_lock_);
 
+  // Check that the value of `r->LiveBytes()` matches the number of
+  // (allocated) bytes used by live objects according to the live bits
+  // in the region space bitmap range corresponding to region `r`.
+  void CheckLiveBytesAgainstRegionBitmap(Region* r);
+
   // Poison memory areas used by dead objects within unevacuated
   // region `r`. This is meant to detect dangling references to dead
   // objects earlier in debug mode.