Include a new heap summary line in the SIGQUIT output.

Looks like this:

  Heap: 87% free, 4MB/32MB; 6327 objects

While I'm here, fix another long-standing TODO to make PrettySize have the
usual google3 behavior. (I took the specific thresholds from Chromium.)

Also distinguish between the more general "Dump" member functions and the
specific SIGQUIT-related ones by consistently calling the latter DumpForSigQuit.

Change-Id: I76e783adc18dd089bac9b348f53dc9860a0fe4b9
diff --git a/src/heap.cc b/src/heap.cc
index eb62807..b4bc7ce 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -641,16 +641,10 @@
   if (VLOG_IS_ON(gc) || gc_was_particularly_slow) {
     // TODO: somehow make the specific GC implementation (here MarkSweep) responsible for logging.
     size_t bytes_freed = initial_size - num_bytes_allocated_;
-    if (bytes_freed > KB) {  // ignore freed bytes in output if > 1KB
-      bytes_freed = RoundDown(bytes_freed, KB);
-    }
-    size_t bytes_allocated = RoundUp(num_bytes_allocated_, KB);
     // lose low nanoseconds in duration. TODO: make this part of PrettyDuration
     duration_ns = (duration_ns / 1000) * 1000;
-    size_t total = GetTotalMemory();
-    size_t percentFree = 100 - static_cast<size_t>(100.0f * static_cast<float>(num_bytes_allocated_) / total);
-    LOG(INFO) << "GC freed " << PrettySize(bytes_freed) << ", " << percentFree << "% free, "
-              << PrettySize(bytes_allocated) << "/" << PrettySize(total) << ", "
+    LOG(INFO) << "GC freed " << PrettySize(bytes_freed) << ", " << GetPercentFree() << "% free, "
+              << PrettySize(num_bytes_allocated_) << "/" << PrettySize(GetTotalMemory()) << ", "
               << "paused " << PrettyDuration(duration_ns);
   }
   Dbg::GcDidFinish();
@@ -663,6 +657,17 @@
   lock_->AssertHeld();
 }
 
+void Heap::DumpForSigQuit(std::ostream& os) {
+  os << "Heap: " << GetPercentFree() << "% free, "
+     << PrettySize(num_bytes_allocated_) << "/" << PrettySize(GetTotalMemory())
+     << "; " << num_objects_allocated_ << " objects";
+}
+
+size_t Heap::GetPercentFree() {
+  size_t total = GetTotalMemory();
+  return 100 - static_cast<size_t>(100.0f * static_cast<float>(num_bytes_allocated_) / total);
+}
+
 void Heap::SetIdealFootprint(size_t max_allowed_footprint) {
   size_t alloc_space_capacity = alloc_space_->Capacity();
   if (max_allowed_footprint > alloc_space_capacity) {
diff --git a/src/heap.h b/src/heap.h
index 7717871..3f0ffc0 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -17,6 +17,7 @@
 #ifndef ART_SRC_HEAP_H_
 #define ART_SRC_HEAP_H_
 
+#include <iosfwd>
 #include <string>
 #include <vector>
 
@@ -219,6 +220,8 @@
     return alloc_space_;
   }
 
+  void DumpForSigQuit(std::ostream& os);
+
  private:
   // Allocates uninitialized storage.
   Object* AllocateLocked(size_t num_bytes);
@@ -239,6 +242,8 @@
   // collection.
   void GrowForUtilization();
 
+  size_t GetPercentFree();
+
   void AddSpace(Space* space);
 
   void VerifyObjectLocked(const Object *obj);
diff --git a/src/runtime.cc b/src/runtime.cc
index 77c887e..947eec5 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -745,13 +745,13 @@
 #undef REGISTER
 }
 
-void Runtime::Dump(std::ostream& os) {
-  // TODO: dump other runtime statistics?
+void Runtime::DumpForSigQuit(std::ostream& os) {
   GetClassLinker()->DumpForSigQuit(os);
   GetInternTable()->DumpForSigQuit(os);
+  GetHeap()->DumpForSigQuit(os);
   os << "\n";
 
-  thread_list_->Dump(os);
+  thread_list_->DumpForSigQuit(os);
 }
 
 void Runtime::DumpLockHolders(std::ostream& os) {
diff --git a/src/runtime.h b/src/runtime.h
index 0dea763..2bf1148 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -130,7 +130,7 @@
   // Detaches the current native thread from the runtime.
   void DetachCurrentThread();
 
-  void Dump(std::ostream& os);
+  void DumpForSigQuit(std::ostream& os);
   void DumpLockHolders(std::ostream& os);
 
   ~Runtime();
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index be21372..dd31fa7 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -114,7 +114,7 @@
     os << "Cmd line: " << cmdline << "\n";
   }
 
-  runtime->Dump(os);
+  runtime->DumpForSigQuit(os);
 
   if (false) {
     std::string maps;
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 43a8561..80ab2cf 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -54,7 +54,7 @@
   return thread_list_lock_.GetOwner();
 }
 
-void ThreadList::Dump(std::ostream& os) {
+void ThreadList::DumpForSigQuit(std::ostream& os) {
   ScopedThreadListLock thread_list_lock;
   DumpLocked(os);
 }
diff --git a/src/thread_list.h b/src/thread_list.h
index 0691808..f0b4f6b 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -31,7 +31,7 @@
   explicit ThreadList();
   ~ThreadList();
 
-  void Dump(std::ostream& os);
+  void DumpForSigQuit(std::ostream& os);
   void DumpLocked(std::ostream& os); // For thread suspend timeout dumps.
   pid_t GetLockOwner(); // For SignalCatcher.
 
diff --git a/src/utils.cc b/src/utils.cc
index 70d15bf..ea50073 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -345,16 +345,26 @@
   return result;
 }
 
-std::string PrettySize(size_t size_in_bytes) {
-  if ((size_in_bytes / GB) * GB == size_in_bytes) {
-    return StringPrintf("%zdGB", size_in_bytes / GB);
-  } else if ((size_in_bytes / MB) * MB == size_in_bytes) {
-    return StringPrintf("%zdMB", size_in_bytes / MB);
-  } else if ((size_in_bytes / KB) * KB == size_in_bytes) {
-    return StringPrintf("%zdKB", size_in_bytes / KB);
-  } else {
-    return StringPrintf("%zdB", size_in_bytes);
+std::string PrettySize(size_t byte_count) {
+  // The byte thresholds at which we display amounts.  A byte count is displayed
+  // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
+  static const size_t kUnitThresholds[] = {
+    0,              // B up to...
+    3*1024,         // KB up to...
+    2*1024*1024,    // MB up to...
+    1024*1024*1024  // GB from here.
+  };
+  static const size_t kBytesPerUnit[] = { 1, KB, MB, GB };
+  static const char* const kUnitStrings[] = { "B", "KB", "MB", "GB" };
+
+  int i = arraysize(kUnitThresholds);
+  while (--i > 0) {
+    if (byte_count >= kUnitThresholds[i]) {
+      break;
+    }
   }
+
+  return StringPrintf("%zd%s", byte_count / kBytesPerUnit[i], kUnitStrings[i]);
 }
 
 std::string PrettyDuration(uint64_t nano_duration) {
diff --git a/src/utils.h b/src/utils.h
index f11a769..f94c05e 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -191,7 +191,7 @@
 // Returns a human-readable form of the name of the given class with its class loader.
 std::string PrettyClassAndClassLoader(const Class* c);
 
-// Returns a human-readable size string. e.g. "1MB"
+// Returns a human-readable size string such as "1MB".
 std::string PrettySize(size_t size_in_bytes);
 
 // Returns a human-readable time string which prints every nanosecond while trying to limit the
diff --git a/src/utils_test.cc b/src/utils_test.cc
index e0b2139..311bd16 100644
--- a/src/utils_test.cc
+++ b/src/utils_test.cc
@@ -135,15 +135,17 @@
   if (sizeof(size_t) > sizeof(uint32_t)) {
     EXPECT_EQ("100GB", PrettySize(100 * GB));
   }
-  EXPECT_EQ("1MB", PrettySize(1 * MB));
+  EXPECT_EQ("1024KB", PrettySize(1 * MB));
   EXPECT_EQ("10MB", PrettySize(10 * MB));
   EXPECT_EQ("100MB", PrettySize(100 * MB));
-  EXPECT_EQ("1KB", PrettySize(1 * KB));
+  EXPECT_EQ("1024B", PrettySize(1 * KB));
   EXPECT_EQ("10KB", PrettySize(10 * KB));
   EXPECT_EQ("100KB", PrettySize(100 * KB));
+  EXPECT_EQ("0B", PrettySize(0));
   EXPECT_EQ("1B", PrettySize(1));
   EXPECT_EQ("10B", PrettySize(10));
   EXPECT_EQ("100B", PrettySize(100));
+  EXPECT_EQ("512B", PrettySize(512));
 }
 
 TEST_F(UtilsTest, PrettyDuration) {