Add RosAlloc stats dump.

For better understanding of the RosAlloc space.

(cherrypick commit 565c2d9bce43c430d4267c82f5702160d971e712)

Bug: 27744947
Bug: 9986565

Change-Id: I8309761a68fbc143bbcd8458a9194085aace7c3e
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 2a55a43..bd84d0d 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -2102,6 +2102,94 @@
   }
 }
 
+void RosAlloc::DumpStats(std::ostream& os) {
+  Thread* self = Thread::Current();
+  CHECK(Locks::mutator_lock_->IsExclusiveHeld(self))
+      << "The mutator locks isn't exclusively locked at " << __PRETTY_FUNCTION__;
+  size_t num_large_objects = 0;
+  size_t num_pages_large_objects = 0;
+  // These arrays are zero initialized.
+  std::unique_ptr<size_t[]> num_runs(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_pages_runs(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_slots(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_used_slots(new size_t[kNumOfSizeBrackets]());
+  std::unique_ptr<size_t[]> num_metadata_bytes(new size_t[kNumOfSizeBrackets]());
+  ReaderMutexLock rmu(self, bulk_free_lock_);
+  MutexLock lock_mu(self, lock_);
+  for (size_t i = 0; i < page_map_size_; ) {
+    uint8_t pm = page_map_[i];
+    switch (pm) {
+      case kPageMapReleased:
+      case kPageMapEmpty:
+        ++i;
+        break;
+      case kPageMapLargeObject: {
+        size_t num_pages = 1;
+        size_t idx = i + 1;
+        while (idx < page_map_size_ && page_map_[idx] == kPageMapLargeObjectPart) {
+          num_pages++;
+          idx++;
+        }
+        num_large_objects++;
+        num_pages_large_objects += num_pages;
+        i += num_pages;
+        break;
+      }
+      case kPageMapLargeObjectPart:
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl
+                   << DumpPageMap();
+        break;
+      case kPageMapRun: {
+        Run* run = reinterpret_cast<Run*>(base_ + i * kPageSize);
+        size_t idx = run->size_bracket_idx_;
+        size_t num_pages = numOfPages[idx];
+        num_runs[idx]++;
+        num_pages_runs[idx] += num_pages;
+        num_slots[idx] += numOfSlots[idx];
+        size_t num_free_slots = run->NumberOfFreeSlots();
+        num_used_slots[idx] += numOfSlots[idx] - num_free_slots;
+        num_metadata_bytes[idx] += headerSizes[idx];
+        i += num_pages;
+        break;
+      }
+      case kPageMapRunPart:
+        // Fall-through.
+      default:
+        LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl
+                   << DumpPageMap();
+        break;
+    }
+  }
+  os << "RosAlloc stats:\n";
+  for (size_t i = 0; i < kNumOfSizeBrackets; ++i) {
+    os << "Bracket " << i << " (" << bracketSizes[i] << "):"
+       << " #runs=" << num_runs[i]
+       << " #pages=" << num_pages_runs[i]
+       << " (" << PrettySize(num_pages_runs[i] * kPageSize) << ")"
+       << " #metadata_bytes=" << PrettySize(num_metadata_bytes[i])
+       << " #slots=" << num_slots[i] << " (" << PrettySize(num_slots[i] * bracketSizes[i]) << ")"
+       << " #used_slots=" << num_used_slots[i]
+       << " (" << PrettySize(num_used_slots[i] * bracketSizes[i]) << ")\n";
+  }
+  os << "Large #allocations=" << num_large_objects
+     << " #pages=" << num_pages_large_objects
+     << " (" << PrettySize(num_pages_large_objects * kPageSize) << ")\n";
+  size_t total_num_pages = 0;
+  size_t total_metadata_bytes = 0;
+  size_t total_allocated_bytes = 0;
+  for (size_t i = 0; i < kNumOfSizeBrackets; ++i) {
+    total_num_pages += num_pages_runs[i];
+    total_metadata_bytes += num_metadata_bytes[i];
+    total_allocated_bytes += num_used_slots[i] * bracketSizes[i];
+  }
+  total_num_pages += num_pages_large_objects;
+  total_allocated_bytes += num_pages_large_objects * kPageSize;
+  os << "Total #total_bytes=" << PrettySize(total_num_pages * kPageSize)
+     << " #metadata_bytes=" << PrettySize(total_metadata_bytes)
+     << " #used_bytes=" << PrettySize(total_allocated_bytes) << "\n";
+  os << "\n";
+}
+
 }  // namespace allocator
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index b12cb5b..1fa2d1a 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -928,6 +928,9 @@
   void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes)
       REQUIRES(!bulk_free_lock_, !lock_);
 
+  void DumpStats(std::ostream& os)
+      REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_) REQUIRES(!bulk_free_lock_);
+
  private:
   friend std::ostream& operator<<(std::ostream& os, const RosAlloc::PageMapKind& rhs);
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index a96847f..01db90a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -116,6 +116,8 @@
 
 // For deterministic compilation, we need the heap to be at a well-known address.
 static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
+// Dump the rosalloc stats on SIGQUIT.
+static constexpr bool kDumpRosAllocStatsOnSigQuit = false;
 
 static inline bool CareAboutPauseTimes() {
   return Runtime::Current()->InJankPerceptibleProcessState();
@@ -1179,6 +1181,10 @@
     }
   }
 
+  if (kDumpRosAllocStatsOnSigQuit && rosalloc_space_ != nullptr) {
+    rosalloc_space_->DumpStats(os);
+  }
+
   BaseMutex::DumpAll(os);
 }
 
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 203d3bc..b016095 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -368,6 +368,11 @@
   SetFootprintLimit(footprint_limit);
 }
 
+void RosAllocSpace::DumpStats(std::ostream& os) {
+  ScopedSuspendAll ssa(__FUNCTION__);
+  rosalloc_->DumpStats(os);
+}
+
 }  // namespace space
 
 namespace allocator {
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index bc14738..b175fbf 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -144,6 +144,8 @@
     rosalloc_->LogFragmentationAllocFailure(os, failed_alloc_bytes);
   }
 
+  void DumpStats(std::ostream& os);
+
  protected:
   RosAllocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
                 allocator::RosAlloc* rosalloc, uint8_t* begin, uint8_t* end, uint8_t* limit,