Fix a DCHECK failure when causing GC from DDMS.

Bug: 13647069
Change-Id: Iae2746b2b7b4493fcf5f0d40d2bf36a9b2d2efc8
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 9af9c7a..20e720b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3837,7 +3837,6 @@
   } else {
     gc::Heap* heap = Runtime::Current()->GetHeap();
     const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces();
-    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It;
     for (It cur = spaces.begin(), end = spaces.end(); cur != end; ++cur) {
       if ((*cur)->IsMallocSpace()) {
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 3c65205..012267b 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -231,7 +231,7 @@
   if (!rosalloc_->DoesReleaseAllPages()) {
     VLOG(heap) << "RosAllocSpace::Trim() ";
     size_t reclaimed = 0;
-    InspectAllRosAlloc(DlmallocMadviseCallback, &reclaimed);
+    InspectAllRosAlloc(DlmallocMadviseCallback, &reclaimed, false);
     return reclaimed;
   }
   return 0;
@@ -239,8 +239,7 @@
 
 void RosAllocSpace::Walk(void(*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
                          void* arg) {
-  InspectAllRosAlloc(callback, arg);
-  callback(NULL, NULL, 0, arg);  // Indicate end of a space.
+  InspectAllRosAlloc(callback, arg, true);
 }
 
 size_t RosAllocSpace::GetFootprint() {
@@ -268,35 +267,56 @@
 
 uint64_t RosAllocSpace::GetBytesAllocated() {
   size_t bytes_allocated = 0;
-  InspectAllRosAlloc(art::gc::allocator::RosAlloc::BytesAllocatedCallback, &bytes_allocated);
+  InspectAllRosAlloc(art::gc::allocator::RosAlloc::BytesAllocatedCallback, &bytes_allocated, false);
   return bytes_allocated;
 }
 
 uint64_t RosAllocSpace::GetObjectsAllocated() {
   size_t objects_allocated = 0;
-  InspectAllRosAlloc(art::gc::allocator::RosAlloc::ObjectsAllocatedCallback, &objects_allocated);
+  InspectAllRosAlloc(art::gc::allocator::RosAlloc::ObjectsAllocatedCallback, &objects_allocated, false);
   return objects_allocated;
 }
 
+void RosAllocSpace::InspectAllRosAllocWithSuspendAll(
+    void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
+    void* arg, bool do_null_callback_at_end) NO_THREAD_SAFETY_ANALYSIS {
+  // TODO: NO_THREAD_SAFETY_ANALYSIS.
+  Thread* self = Thread::Current();
+  ThreadList* tl = Runtime::Current()->GetThreadList();
+  tl->SuspendAll();
+  {
+    MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+    MutexLock mu2(self, *Locks::thread_list_lock_);
+    rosalloc_->InspectAll(callback, arg);
+    if (do_null_callback_at_end) {
+      callback(NULL, NULL, 0, arg);  // Indicate end of a space.
+    }
+  }
+  tl->ResumeAll();
+}
+
 void RosAllocSpace::InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
-                                       void* arg) NO_THREAD_SAFETY_ANALYSIS {
+                                       void* arg, bool do_null_callback_at_end) NO_THREAD_SAFETY_ANALYSIS {
   // TODO: NO_THREAD_SAFETY_ANALYSIS.
   Thread* self = Thread::Current();
   if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
     // The mutators are already suspended. For example, a call path
     // from SignalCatcher::HandleSigQuit().
     rosalloc_->InspectAll(callback, arg);
-  } else {
-    // The mutators are not suspended yet.
-    DCHECK(!Locks::mutator_lock_->IsSharedHeld(self));
-    ThreadList* tl = Runtime::Current()->GetThreadList();
-    tl->SuspendAll();
-    {
-      MutexLock mu(self, *Locks::runtime_shutdown_lock_);
-      MutexLock mu2(self, *Locks::thread_list_lock_);
-      rosalloc_->InspectAll(callback, arg);
+    if (do_null_callback_at_end) {
+      callback(NULL, NULL, 0, arg);  // Indicate end of a space.
     }
-    tl->ResumeAll();
+  } else if (Locks::mutator_lock_->IsSharedHeld(self)) {
+    // The mutators are not suspended yet and we have a shared access
+    // to the mutator lock. Temporarily release the shared access by
+    // transitioning to the suspend state, and suspend the mutators.
+    self->TransitionFromRunnableToSuspended(kSuspended);
+    InspectAllRosAllocWithSuspendAll(callback, arg, do_null_callback_at_end);
+    self->TransitionFromSuspendedToRunnable();
+    Locks::mutator_lock_->AssertSharedHeld(self);
+  } else {
+    // The mutators are not suspended yet. Suspend the mutators.
+    InspectAllRosAllocWithSuspendAll(callback, arg, do_null_callback_at_end);
   }
 }
 
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 949ec08..900e7a9 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -124,7 +124,11 @@
                                              size_t maximum_size, bool low_memory_mode);
 
   void InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
-                          void* arg)
+                          void* arg, bool do_null_callback_at_end)
+      LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_, Locks::thread_list_lock_);
+  void InspectAllRosAllocWithSuspendAll(
+      void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
+      void* arg, bool do_null_callback_at_end)
       LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_, Locks::thread_list_lock_);
 
   // Underlying rosalloc.