Fix missing card mark verification.

Fixed card mark verification code to handle aged cards.

Refactored some duplicated code in Heap.cc.

Fixed a broken check in card table scan.

Change-Id: I59f2072684fc20873ababcbc5e59640059e25ff7
diff --git a/src/gc/card_table.h b/src/gc/card_table.h
index 2a20fc8..d0d25fd 100644
--- a/src/gc/card_table.h
+++ b/src/gc/card_table.h
@@ -51,7 +51,12 @@
 
   // Is the object on a dirty card?
   bool IsDirty(const Object* obj) const {
-    return *CardFromAddr(obj) == kCardDirty;
+    return GetCard(obj) == kCardDirty;
+  }
+
+  // Return the state of the card at an address.
+  byte GetCard(const Object* obj) const {
+    return *CardFromAddr(obj);
   }
 
   // Visit and clear cards within memory range, only visits dirty cards.
@@ -192,9 +197,11 @@
       }
       uintptr_t start_word = *word_cur;
       for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
-        if ((start_word & 0xFF) == minimum_age) {
+        if ((start_word & 0xFF) >= minimum_age) {
           byte* card = reinterpret_cast<byte*>(word_cur) + i;
-          DCHECK_EQ(*card, start_word & 0xFF);
+          const byte card_byte = *card;
+          DCHECK(card_byte == (start_word & 0xFF) || card_byte == kCardDirty)
+              << "card " << static_cast<size_t>(card_byte) << " word " << (start_word & 0xFF);
           uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card));
           uintptr_t end = start + kCardSize;
           bitmap->VisitMarkedRange(start, end, visitor, finger_visitor);
diff --git a/src/heap.cc b/src/heap.cc
index 2c52ff2..f55efd6 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1030,35 +1030,11 @@
     // Process dirty cards and add dirty cards to mod union tables.
     ProcessCards(timings);
 
+    // Bind live to mark bitmaps.
+    BindBitmaps(gc_type, mark_sweep);
+    timings.AddSplit("BindBitmaps");
+
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-    if (gc_type == kGcTypePartial) {
-      // Copy the mark bits over from the live bits, do this as early as possible or else we can
-      // accidentally un-mark roots.
-      // Needed for scanning dirty objects.
-      for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) {
-        if ((*it)->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) {
-          mark_sweep.BindLiveToMarkBitmap(*it);
-        }
-      }
-      timings.AddSplit("BindLiveToMarked");
-
-      // We can assume that everything from the start of the first space to the alloc space is marked.
-      mark_sweep.SetImmuneRange(reinterpret_cast<Object*>(spaces_[0]->Begin()),
-                                reinterpret_cast<Object*>(alloc_space_->Begin()));
-    } else if (gc_type == kGcTypeSticky) {
-      for (Spaces::iterator it = spaces_.begin();it != spaces_.end(); ++it) {
-        if ((*it)->GetGcRetentionPolicy() != kGcRetentionPolicyNeverCollect) {
-          mark_sweep.BindLiveToMarkBitmap(*it);
-        }
-      }
-      timings.AddSplit("BindLiveToMarkBitmap");
-      large_object_space_->CopyLiveToMarked();
-      timings.AddSplit("CopyLiveToMarked");
-      mark_sweep.SetImmuneRange(reinterpret_cast<Object*>(spaces_[0]->Begin()),
-                                reinterpret_cast<Object*>(alloc_space_->Begin()));
-    }
-    mark_sweep.FindDefaultMarkBitmap();
-
     mark_sweep.MarkRoots();
     mark_sweep.MarkConcurrentRoots();
     timings.AddSplit("MarkRoots");
@@ -1361,20 +1337,24 @@
         failed_(failed) {
   }
 
-  // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for smarter
-  // analysis.
+  // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for
+  // annotalysis on visitors.
   void operator ()(const Object* obj, const Object* ref, const MemberOffset& offset,
                      bool is_static) const NO_THREAD_SAFETY_ANALYSIS {
-    if (ref != NULL && !obj->GetClass()->IsPrimitiveArray()) {
+    // Filter out class references since changing an object's class does not mark the card as dirty.
+    // Also handles large objects, since the only reference they hold is a class reference.
+    if (ref != NULL && !ref->IsClass()) {
       CardTable* card_table = heap_->GetCardTable();
       // If the object is not dirty and it is referencing something in the live stack other than
       // class, then it must be on a dirty card.
       if (!card_table->AddrIsInCardTable(obj)) {
         LOG(ERROR) << "Object " << obj << " is not in the address range of the card table";
         *failed_ = true;
-      } else if (!card_table->IsDirty(obj)) {
+      } else if (card_table->GetCard(obj) < CardTable::kCardDirty - 1) {
+        // Card should be either kCardDirty if it got re-dirtied after we aged it, or
+        // kCardDirty - 1 if it didnt get touched since we aged it.
         ObjectStack* live_stack = heap_->live_stack_.get();
-        if (std::binary_search(live_stack->Begin(), live_stack->End(), ref) && !ref->IsClass()) {
+        if (std::binary_search(live_stack->Begin(), live_stack->End(), ref)) {
           if (std::binary_search(live_stack->Begin(), live_stack->End(), obj)) {
             LOG(ERROR) << "Object " << obj << " found in live stack";
           }
@@ -1515,6 +1495,38 @@
   }
 }
 
+// Bind the live bits to the mark bits of bitmaps based on the gc type.
+void Heap::BindBitmaps(GcType gc_type, MarkSweep& mark_sweep) {
+  WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+  if (gc_type == kGcTypePartial) {
+    // For partial GCs we need to bind the bitmap of the zygote space so that all objects in the
+    // zygote space are viewed as marked.
+    for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) {
+      if ((*it)->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) {
+        mark_sweep.BindLiveToMarkBitmap(*it);
+      }
+    }
+    mark_sweep.SetImmuneRange(reinterpret_cast<Object*>(spaces_.front()->Begin()),
+                              reinterpret_cast<Object*>(alloc_space_->Begin()));
+  } else if (gc_type == kGcTypeSticky) {
+    // For sticky GC, we want to bind the bitmaps of both the zygote space and the alloc space.
+    // This lets us start with the mark bitmap of the previous garbage collection as the current
+    // mark bitmap of the alloc space. After the sticky GC finishes, we then unbind the bitmaps,
+    // making it so that the live bitmap of the alloc space is contains the newly marked objects
+    // from the sticky GC.
+    for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) {
+      if ((*it)->GetGcRetentionPolicy() != kGcRetentionPolicyNeverCollect) {
+        mark_sweep.BindLiveToMarkBitmap(*it);
+      }
+    }
+
+    large_object_space_->CopyLiveToMarked();
+    mark_sweep.SetImmuneRange(reinterpret_cast<Object*>(spaces_.front()->Begin()),
+                              reinterpret_cast<Object*>(alloc_space_->Begin()));
+  }
+  mark_sweep.FindDefaultMarkBitmap();
+}
+
 void Heap::CollectGarbageConcurrentMarkSweepPlan(Thread* self, GcType gc_type, GcCause gc_cause,
                                                  bool clear_soft_references) {
   TimingLogger timings("ConcurrentCollectGarbageInternal", true);
@@ -1532,35 +1544,8 @@
     mark_sweep.Init();
     timings.AddSplit("Init");
 
-    {
-      WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-
-      if (gc_type == kGcTypePartial) {
-        // Copy the mark bits over from the live bits, do this as early as possible or else we can
-        // accidentally un-mark roots.
-        // Needed for scanning dirty objects.
-        for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) {
-          if ((*it)->GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) {
-            mark_sweep.BindLiveToMarkBitmap(*it);
-          }
-        }
-        timings.AddSplit("BindLiveToMark");
-        mark_sweep.SetImmuneRange(reinterpret_cast<Object*>(spaces_.front()->Begin()),
-                                  reinterpret_cast<Object*>(alloc_space_->Begin()));
-      } else if (gc_type == kGcTypeSticky) {
-        for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) {
-          if ((*it)->GetGcRetentionPolicy() != kGcRetentionPolicyNeverCollect) {
-            mark_sweep.BindLiveToMarkBitmap(*it);
-          }
-        }
-        timings.AddSplit("BindLiveToMark");
-        large_object_space_->CopyLiveToMarked();
-        timings.AddSplit("CopyLiveToMarked");
-        mark_sweep.SetImmuneRange(reinterpret_cast<Object*>(spaces_.front()->Begin()),
-                                  reinterpret_cast<Object*>(alloc_space_->Begin()));
-      }
-      mark_sweep.FindDefaultMarkBitmap();
-    }
+    BindBitmaps(gc_type, mark_sweep);
+    timings.AddSplit("BindBitmaps");
 
     if (verify_pre_gc_heap_) {
       thread_list->SuspendAll();
@@ -1676,20 +1661,20 @@
       UnMarkAllocStack(alloc_space_->GetMarkBitmap(), large_object_space_->GetMarkObjects(),
                       allocation_stack_.get());
       timings.AddSplit("UnMarkAllocStack");
-#ifndef NDEBUG
-      if (gc_type == kGcTypeSticky) {
-        // Make sure everything in the live stack isn't something we unmarked.
-        std::sort(allocation_stack_->Begin(), allocation_stack_->End());
-        for (Object** it = live_stack_->Begin(); it != live_stack_->End(); ++it) {
-          DCHECK(!std::binary_search(allocation_stack_->Begin(), allocation_stack_->End(), *it))
-              << "Unmarked object " << *it << " in the live stack";
-        }
-      } else {
-        for (Object** it = allocation_stack_->Begin(); it != allocation_stack_->End(); ++it) {
-          DCHECK(!GetLiveBitmap()->Test(*it)) << "Object " << *it << " is marked as live";
+      if (kIsDebugBuild) {
+        if (gc_type == kGcTypeSticky) {
+          // Make sure everything in the live stack isn't something we unmarked.
+          std::sort(allocation_stack_->Begin(), allocation_stack_->End());
+          for (Object** it = live_stack_->Begin(); it != live_stack_->End(); ++it) {
+            DCHECK(!std::binary_search(allocation_stack_->Begin(), allocation_stack_->End(), *it))
+                << "Unmarked object " << *it << " in the live stack";
+          }
+        } else {
+          for (Object** it = allocation_stack_->Begin(); it != allocation_stack_->End(); ++it) {
+            DCHECK(!GetLiveBitmap()->Test(*it)) << "Object " << *it << " is marked as live";
+          }
         }
       }
-#endif
     }
 
     if (kIsDebugBuild) {
diff --git a/src/heap.h b/src/heap.h
index 584718e..22b009c 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -392,6 +392,9 @@
   // Swap the allocation stack with the live stack.
   void SwapStacks();
 
+  // Bind bitmaps (makes the live and mark bitmaps for immune spaces point to the same bitmap).
+  void BindBitmaps(GcType gc_type, MarkSweep& mark_sweep);
+
   // Clear cards and update the mod union table.
   void ProcessCards(TimingLogger& timings);