Show size/alloc/free per Dalvik heap space in dumpsys

Add the heap size/alloc/free stats to the Dalvik heap space breakdown section in dumpsys meminfo.

Also, now the zygote heap has a distict ashmem region name.

Bug: 9532137
Bug: 8266259
Change-Id: Ieeb02f5f5ebf7ffe35d4b55ad81d46989af2827b
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 021d8e7..adc7b3c 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -145,7 +145,7 @@
   CHECK(large_object_space_ != NULL) << "Failed to create large object space";
   AddDiscontinuousSpace(large_object_space_);
 
-  alloc_space_ = space::DlMallocSpace::Create("alloc space",
+  alloc_space_ = space::DlMallocSpace::Create(Runtime::Current()->IsZygote() ? "zygote space" : "alloc space",
                                               initial_size,
                                               growth_limit, capacity,
                                               requested_alloc_space_begin);
@@ -955,7 +955,7 @@
   // Turns the current alloc space into a Zygote space and obtain the new alloc space composed
   // of the remaining available heap memory.
   space::DlMallocSpace* zygote_space = alloc_space_;
-  alloc_space_ = zygote_space->CreateZygoteSpace();
+  alloc_space_ = zygote_space->CreateZygoteSpace("alloc space");
   alloc_space_->SetFootprintLimit(alloc_space_->Capacity());
 
   // Change the GC retention policy of the zygote space to only collect when full.
@@ -1916,5 +1916,27 @@
   } while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size));
 }
 
+int64_t Heap::GetTotalMemory() const {
+  int64_t ret = 0;
+  typedef std::vector<space::ContinuousSpace*>::const_iterator It;
+  for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) {
+    space::ContinuousSpace* space = *it;
+    if (space->IsImageSpace()) {
+      // Currently don't include the image space.
+    } else if (space->IsDlMallocSpace()) {
+      // Zygote or alloc space
+      ret += space->AsDlMallocSpace()->GetFootprint();
+    }
+  }
+  typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2;
+  for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) {
+    space::DiscontinuousSpace* space = *it;
+    if (space->IsLargeObjectSpace()) {
+      ret += space->AsLargeObjectSpace()->GetBytesAllocated();
+    }
+  }
+  return ret;
+}
+
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index feccba3..f978c6a 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -307,11 +307,7 @@
 
   // Implements java.lang.Runtime.totalMemory, returning the amount of memory consumed by an
   // application.
-  int64_t GetTotalMemory() const {
-    // TODO: we use the footprint limit here which is conservative wrt number of pages really used.
-    //       We could implement a more accurate count across all spaces.
-    return max_allowed_footprint_;
-  }
+  int64_t GetTotalMemory() const;
 
   // Implements java.lang.Runtime.freeMemory.
   int64_t GetFreeMemory() const {
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 02acd28..74a9469 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -286,7 +286,7 @@
   }
 }
 
-DlMallocSpace* DlMallocSpace::CreateZygoteSpace() {
+DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) {
   end_ = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(end_), kPageSize));
   DCHECK(IsAligned<accounting::CardTable::kCardSize>(begin_));
   DCHECK(IsAligned<accounting::CardTable::kCardSize>(end_));
@@ -316,20 +316,19 @@
   VLOG(heap) << "Size " << GetMemMap()->Size();
   VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit);
   VLOG(heap) << "Capacity " << PrettySize(capacity);
-  UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(GetName(), End(), capacity, PROT_READ | PROT_WRITE));
+  UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(alloc_space_name, End(), capacity, PROT_READ | PROT_WRITE));
   void* mspace = CreateMallocSpace(end_, starting_size, initial_size);
   // Protect memory beyond the initial size.
   byte* end = mem_map->Begin() + starting_size;
   if (capacity - initial_size > 0) {
-    CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name_.c_str());
+    CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), alloc_space_name);
   }
   DlMallocSpace* alloc_space =
-      new DlMallocSpace(name_, mem_map.release(), mspace, end_, end, growth_limit);
+      new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, growth_limit);
   live_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
   CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End()));
   mark_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
   CHECK_EQ(mark_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End()));
-  name_ += "-zygote-transformed";
   VLOG(heap) << "zygote space creation done";
   return alloc_space;
 }
@@ -449,6 +448,11 @@
   callback(NULL, NULL, 0, arg);  // Indicate end of a space.
 }
 
+size_t DlMallocSpace::GetFootprint() {
+  MutexLock mu(Thread::Current(), lock_);
+  return mspace_footprint(mspace_);
+}
+
 size_t DlMallocSpace::GetFootprintLimit() {
   MutexLock mu(Thread::Current(), lock_);
   return mspace_footprint_limit(mspace_);
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index 8a4314c..c15d0ba 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -73,6 +73,10 @@
   // in use, indicated by num_bytes equaling zero.
   void Walk(WalkCallback callback, void* arg);
 
+  // Returns the number of bytes that the space has currently obtained from the system. This is
+  // greater or equal to the amount of live data in the space.
+  size_t GetFootprint();
+
   // Returns the number of bytes that the heap is allowed to obtain from the system via MoreCore.
   size_t GetFootprintLimit();
 
@@ -113,7 +117,7 @@
   void SwapBitmaps();
 
   // Turn ourself into a zygote space and return a new alloc space which has our unused memory.
-  DlMallocSpace* CreateZygoteSpace();
+  DlMallocSpace* CreateZygoteSpace(const char* alloc_space_name);
 
   uint64_t GetBytesAllocated() const {
     return num_bytes_allocated_;
diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc
index 08ae894..3003140 100644
--- a/runtime/gc/space/space_test.cc
+++ b/runtime/gc/space/space_test.cc
@@ -123,7 +123,7 @@
 
     // Make sure that the zygote space isn't directly at the start of the space.
     space->Alloc(self, 1U * MB);
-    space = space->CreateZygoteSpace();
+    space = space->CreateZygoteSpace("alloc space");
 
     // Make space findable to the heap, will also delete space when runtime is cleaned up
     AddContinuousSpace(space);
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 992998e..5943a6a 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -20,6 +20,9 @@
 #include "class_linker.h"
 #include "common_throws.h"
 #include "debugger.h"
+#include "gc/space/dlmalloc_space.h"
+#include "gc/space/large_object_space.h"
+#include "gc/space/space-inl.h"
 #include "hprof/hprof.h"
 #include "jni_internal.h"
 #include "mirror/class.h"
@@ -234,6 +237,69 @@
   return count;
 }
 
+// We export the VM internal per-heap-space size/alloc/free metrics
+// for the zygote space, alloc space (application heap), and the large
+// object space for dumpsys meminfo. The other memory region data such
+// as PSS, private/shared dirty/shared data are available via
+// /proc/<pid>/smaps.
+static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) {
+  jlong* arr = (jlong*) env->GetPrimitiveArrayCritical(data, 0);
+  if (arr == NULL || env->GetArrayLength(data) < 9) {
+    return;
+  }
+
+  size_t allocSize = 0;
+  size_t allocUsed = 0;
+  size_t zygoteSize = 0;
+  size_t zygoteUsed = 0;
+  size_t largeObjectsSize = 0;
+  size_t largeObjectsUsed = 0;
+
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  const std::vector<gc::space::ContinuousSpace*>& continuous_spaces = heap->GetContinuousSpaces();
+  const std::vector<gc::space::DiscontinuousSpace*>& discontinuous_spaces = heap->GetDiscontinuousSpaces();
+  typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It;
+  for (It it = continuous_spaces.begin(), end = continuous_spaces.end(); it != end; ++it) {
+    gc::space::ContinuousSpace* space = *it;
+    if (space->IsImageSpace()) {
+      // Currently don't include the image space.
+    } else if (space->IsZygoteSpace()) {
+      gc::space::DlMallocSpace* dlmalloc_space = space->AsDlMallocSpace();
+      zygoteSize += dlmalloc_space->GetFootprint();
+      zygoteUsed += dlmalloc_space->GetBytesAllocated();
+    } else {
+      // This is the alloc space.
+      gc::space::DlMallocSpace* dlmalloc_space = space->AsDlMallocSpace();
+      allocSize += dlmalloc_space->GetFootprint();
+      allocUsed += dlmalloc_space->GetBytesAllocated();
+    }
+  }
+  typedef std::vector<gc::space::DiscontinuousSpace*>::const_iterator It2;
+  for (It2 it = discontinuous_spaces.begin(), end = discontinuous_spaces.end(); it != end; ++it) {
+    gc::space::DiscontinuousSpace* space = *it;
+    if (space->IsLargeObjectSpace()) {
+      largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated();
+      largeObjectsUsed += largeObjectsSize;
+    }
+  }
+
+  size_t allocFree = allocSize - allocUsed;
+  size_t zygoteFree = zygoteSize - zygoteUsed;
+  size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed;
+
+  int j = 0;
+  arr[j++] = allocSize;
+  arr[j++] = allocUsed;
+  arr[j++] = allocFree;
+  arr[j++] = zygoteSize;
+  arr[j++] = zygoteUsed;
+  arr[j++] = zygoteFree;
+  arr[j++] = largeObjectsSize;
+  arr[j++] = largeObjectsUsed;
+  arr[j++] = largeObjectsFree;
+  env->ReleasePrimitiveArrayCritical(data, arr, 0);
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
   NATIVE_METHOD(VMDebug, crash, "()V"),
@@ -241,6 +307,7 @@
   NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
   NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
   NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
+  NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"),
   NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"),
   NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
   NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),