[metrics] Dump ArtMetrics on SIGQUIT

ART now writes a current snapshot of its internal metrics in response to
a SIGQUIT.

Example output:
```
*** ART internal metrics ***

ClassVerificationTotalTime: count = 863833
JitMethodCompileTime: range = 0...1000000, buckets: 244,5,1,0,1,0,0,0,0,1,0,0,0,0,0

*** Done dumping ART internal metrics ***
```

This includes a new StreamBackend, which is used to write ART metrics to
an output stream in a human readable format.

Bug: 170149255
Test: m test-art-host-gtest-art_libartbase_tests
Change-Id: Iaf8bcee5a4993e70ac4e36940591a734fe1a6697
diff --git a/libartbase/base/metrics.cc b/libartbase/base/metrics.cc
index e146856..6c79b47 100644
--- a/libartbase/base/metrics.cc
+++ b/libartbase/base/metrics.cc
@@ -69,6 +69,48 @@
 #undef ART_HISTOGRAM
 }
 
+void ArtMetrics::DumpForSigQuit(std::ostream& os) const {
+  os << "\n*** ART internal metrics ***\n\n";
+  StreamBackend backend{os};
+  ReportAllMetrics(&backend);
+  os << "\n*** Done dumping ART internal metrics ***\n";
+}
+
+StreamBackend::StreamBackend(std::ostream& os) : os_{os} {}
+
+void StreamBackend::BeginSession([[maybe_unused]] const SessionData& session_data) {
+  // Not needed for now.
+}
+
+void StreamBackend::EndSession() {
+  // Not needed for now.
+}
+
+void StreamBackend::ReportCounter(DatumId counter_type, uint64_t value) {
+  os_ << DatumName(counter_type) << ": count = " << value << "\n";
+}
+
+void StreamBackend::ReportHistogram(DatumId histogram_type,
+                                    int64_t minimum_value_,
+                                    int64_t maximum_value_,
+                                    const std::vector<uint32_t>& buckets) {
+  os_ << DatumName(histogram_type) << ": range = " << minimum_value_ << "..." << maximum_value_;
+  if (buckets.size() > 0) {
+    os_ << ", buckets: ";
+    bool first = true;
+    for (const auto& count : buckets) {
+      if (!first) {
+        os_ << ",";
+      }
+      first = false;
+      os_ << count;
+    }
+    os_ << "\n";
+  } else {
+    os_ << ", no buckets\n";
+  }
+}
+
 }  // namespace metrics
 }  // namespace art
 
diff --git a/libartbase/base/metrics.h b/libartbase/base/metrics.h
index d5a4873..0c0227a 100644
--- a/libartbase/base/metrics.h
+++ b/libartbase/base/metrics.h
@@ -202,6 +202,25 @@
   static_assert(std::atomic<value_t>::is_always_lock_free);
 };
 
+// A backend that writes metrics in a human-readable format to an std::ostream.
+class StreamBackend : public MetricsBackend {
+ public:
+  explicit StreamBackend(std::ostream& os);
+
+  void BeginSession(const SessionData& session_data) override;
+  void EndSession() override;
+
+  void ReportCounter(DatumId counter_type, uint64_t value) override;
+
+  void ReportHistogram(DatumId histogram_type,
+                       int64_t low_value,
+                       int64_t high_value,
+                       const std::vector<uint32_t>& buckets) override;
+
+ private:
+  std::ostream& os_;
+};
+
 /**
  * AutoTimer simplifies time-based metrics collection.
  *
@@ -279,6 +298,7 @@
   ArtMetrics();
 
   void ReportAllMetrics(MetricsBackend* backend) const;
+  void DumpForSigQuit(std::ostream& os) const;
 
 #define ART_COUNTER(name)                                       \
   MetricsCounter<DatumId::k##name>* name() { return &name##_; } \
diff --git a/libartbase/base/metrics_test.cc b/libartbase/base/metrics_test.cc
index 20ac92c..f568ea0 100644
--- a/libartbase/base/metrics_test.cc
+++ b/libartbase/base/metrics_test.cc
@@ -205,6 +205,27 @@
   EXPECT_GT(GetBuckets(test_histogram)[0], 0u);
 }
 
+// Makes sure all defined metrics are included when dumping through StreamBackend.
+TEST_F(MetricsTest, StreamBackendDumpAllMetrics) {
+  ArtMetrics metrics;
+  std::stringstream os;
+  StreamBackend backend(os);
+
+  metrics.ReportAllMetrics(&backend);
+
+  // Make sure the resulting string lists all the counters.
+#define COUNTER(name) \
+  EXPECT_NE(os.str().find(DatumName(DatumId::k##name)), std::string::npos)
+  ART_COUNTERS(COUNTER);
+#undef COUNTER
+
+  // Make sure the resulting string lists all the histograms.
+#define HISTOGRAM(name, num_buckets, minimum_value, maximum_value) \
+  EXPECT_NE(os.str().find(DatumName(DatumId::k##name)), std::string::npos)
+  ART_HISTOGRAMS(HISTOGRAM);
+#undef HISTOGRAM
+}
+
 }  // namespace metrics
 }  // namespace art
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 70e3ae3..9c0b4df 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2020,6 +2020,7 @@
   }
   DumpDeoptimizations(os);
   TrackedAllocators::Dump(os);
+  GetMetrics()->DumpForSigQuit(os);
   os << "\n";
 
   thread_list_->DumpForSigQuit(os);