[metrics] Report some ART metadata

Adds reporting for some metadata associated with ART metrics,
including timestamp relative to ART startup, the session id, and the
process's user id. It additionally outputs placeholders for the
compilation reason and compiler filter, but these need some additional
plumbing from the Runtime and OatFileManager to fill in, so those will
come in a followup CL.

This CL also includes a fair amount of refactoring around metrics
reporting and handling the session data.

Example output:

    *** ART internal metrics ***
      Metadata:
        timestamp_since_start_ms: 768
        session_id: 5026277321588503825
        uid: 123456
        compilation_reason: Unknown
        compiler_filter: (unspecified)
      Metrics:
        ClassVerificationTotalTime: count = 4167
        JitMethodCompileTime: range = 0...1000000, buckets: 7,0,0,0,0,0
    *** Done dumping ART internal metrics ***

Test: ./test/run-test --host --jit 2232-write-metrics-to-log
Change-Id: Ic74b503b135d71099d9e26bf660b60e4cc3a46bc
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 778e789..6915b7c 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -22,11 +22,11 @@
 #include <string>
 #include <vector>
 
+#include "base/compiler_filter.h"
 #include "base/globals.h"
 #include "base/hash_set.h"
 #include "base/macros.h"
 #include "base/utils.h"
-#include "compiler_filter.h"
 #include "optimizing/register_allocator.h"
 
 namespace art {
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
index 14f5112..7e2f846 100644
--- a/compiler/driver/compiler_options_map.h
+++ b/compiler/driver/compiler_options_map.h
@@ -20,9 +20,9 @@
 #include <string>
 #include <vector>
 
+#include "base/compiler_filter.h"
 #include "base/variant_map.h"
 #include "cmdline_types.h"
-#include "compiler_filter.h"
 
 namespace art {
 
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index c3304b5..515a37c 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -22,13 +22,13 @@
 
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
+#include "base/compiler_filter.h"
 #include "base/file_utils.h"
 #include "base/logging.h"  // For InitLogging.
 #include "base/mutex.h"
 #include "base/os.h"
 #include "base/string_view_cpp20.h"
 #include "base/utils.h"
-#include "compiler_filter.h"
 #include "class_loader_context.h"
 #include "dex/dex_file.h"
 #include "noop_compiler_callbacks.h"
diff --git a/dexoptanalyzer/dexoptanalyzer_test.cc b/dexoptanalyzer/dexoptanalyzer_test.cc
index 4fb0b9e..42be473 100644
--- a/dexoptanalyzer/dexoptanalyzer_test.cc
+++ b/dexoptanalyzer/dexoptanalyzer_test.cc
@@ -17,7 +17,7 @@
 #include <gtest/gtest.h>
 
 #include "arch/instruction_set.h"
-#include "compiler_filter.h"
+#include "base/compiler_filter.h"
 #include "dexopt_test.h"
 
 namespace art {
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 60dcf17..c3a6364 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -24,6 +24,7 @@
         "base/arena_allocator.cc",
         "base/arena_bit_vector.cc",
         "base/bit_vector.cc",
+        "base/compiler_filter.cc",
         "base/enums.cc",
         "base/file_magic.cc",
         "base/file_utils.cc",
@@ -243,6 +244,7 @@
         "base/bit_table_test.cc",
         "base/bit_utils_test.cc",
         "base/bit_vector_test.cc",
+        "base/compiler_filter_test.cc",
         "base/file_utils_test.cc",
         "base/hash_map_test.cc",
         "base/hash_set_test.cc",
diff --git a/runtime/compiler_filter.cc b/libartbase/base/compiler_filter.cc
similarity index 100%
rename from runtime/compiler_filter.cc
rename to libartbase/base/compiler_filter.cc
diff --git a/runtime/compiler_filter.h b/libartbase/base/compiler_filter.h
similarity index 96%
rename from runtime/compiler_filter.h
rename to libartbase/base/compiler_filter.h
index 0b930a4..b4a83ec 100644
--- a/runtime/compiler_filter.h
+++ b/libartbase/base/compiler_filter.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_COMPILER_FILTER_H_
-#define ART_RUNTIME_COMPILER_FILTER_H_
+#ifndef ART_LIBARTBASE_BASE_COMPILER_FILTER_H_
+#define ART_LIBARTBASE_BASE_COMPILER_FILTER_H_
 
 #include <iosfwd>
 #include <string>
@@ -110,4 +110,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_COMPILER_FILTER_H_
+#endif  // ART_LIBARTBASE_BASE_COMPILER_FILTER_H_
diff --git a/runtime/compiler_filter_test.cc b/libartbase/base/compiler_filter_test.cc
similarity index 100%
rename from runtime/compiler_filter_test.cc
rename to libartbase/base/compiler_filter_test.cc
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index 18f84e9..20bbc52 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -22,12 +22,13 @@
 #include <array>
 #include <atomic>
 #include <optional>
-#include <ostream>
+#include <sstream>
 #include <string_view>
 #include <thread>
 #include <vector>
 
 #include "android-base/logging.h"
+#include "base/compiler_filter.h"
 #include "base/time_utils.h"
 
 #pragma clang diagnostic push
@@ -79,10 +80,59 @@
 #undef ART_HISTOGRAM
 };
 
+// We log compilation reasons as part of the metadata we report. Since elsewhere compilation reasons
+// are specified as a string, we define them as an enum here which indicates the reasons that we
+// support.
+enum class CompilationReason {
+  kError,
+  kUnknown,
+  kFirstBoot,
+  kBoot,
+  kInstall,
+  kBgDexopt,
+  kABOTA,
+  kInactive,
+  kShared,
+  kInstallWithDexMetadata,
+};
+
+constexpr const char* CompilationReasonName(CompilationReason reason) {
+  switch (reason) {
+    case CompilationReason::kError:
+      return "Error";
+    case CompilationReason::kUnknown:
+      return "Unknown";
+    case CompilationReason::kFirstBoot:
+      return "FirstBoot";
+    case CompilationReason::kBoot:
+      return "Boot";
+    case CompilationReason::kInstall:
+      return "Install";
+    case CompilationReason::kBgDexopt:
+      return "BgDexopt";
+    case CompilationReason::kABOTA:
+      return "ABOTA";
+    case CompilationReason::kInactive:
+      return "Inactive";
+    case CompilationReason::kShared:
+      return "Shared";
+    case CompilationReason::kInstallWithDexMetadata:
+      return "InstallWithDexMetadata";
+  }
+}
+
+// SessionData contains metadata about a metrics session (basically the lifetime of an ART process).
+// This information should not change for the lifetime of the session.
 struct SessionData {
-  const uint64_t session_id;
-  const std::string_view package_name;
-  // TODO: compiler filter / dexopt state
+  static SessionData CreateDefault();
+
+  static constexpr int64_t kInvalidSessionId = -1;
+  static constexpr int32_t kInvalidUserId = -1;
+
+  int64_t session_id;
+  int32_t uid;
+  CompilationReason compilation_reason;
+  std::optional<CompilerFilter::Filter> compiler_filter;
 };
 
 // MetricsBackends are used by a metrics reporter to write metrics to some external location. For
@@ -91,7 +141,6 @@
  public:
   virtual ~MetricsBackend() {}
 
- protected:
   // Begins an ART metrics session.
   //
   // This is called by the metrics reporter when the runtime is starting up. The session_data
@@ -100,12 +149,9 @@
   // for this process.
   virtual void BeginSession(const SessionData& session_data) = 0;
 
-  // Marks the end of a metrics session.
-  //
-  // The metrics reporter will call this when metrics reported ends (e.g. when the runtime is
-  // shutting down). No further metrics will be reported for this session. Note that EndSession is
-  // not guaranteed to be called, since clean shutdowns for the runtime are quite rare in practice.
-  virtual void EndSession() = 0;
+ protected:
+  // Called by the metrics reporter to indicate that a new metrics report is starting.
+  virtual void BeginReport(uint64_t timestamp_millis) = 0;
 
   // Called by the metrics reporter to give the current value of the counter with id counter_type.
   //
@@ -129,10 +175,14 @@
                                int64_t maximum_value,
                                const std::vector<uint32_t>& buckets) = 0;
 
+  // Called by the metrics reporter to indicate that the current metrics report is complete.
+  virtual void EndReport() = 0;
+
   template <DatumId counter_type>
   friend class MetricsCounter;
   template <DatumId histogram_type, size_t num_buckets, int64_t low_value, int64_t high_value>
   friend class MetricsHistogram;
+  friend class ArtMetrics;
 };
 
 template <DatumId counter_type>
@@ -208,13 +258,16 @@
   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 {
+// A backend that writes metrics in a human-readable format to a string.
+//
+// This is used as a base for LogBackend and FileBackend.
+class StringBackend : public MetricsBackend {
  public:
-  explicit StreamBackend(std::ostream& os);
+  StringBackend();
 
   void BeginSession(const SessionData& session_data) override;
-  void EndSession() override;
+
+  void BeginReport(uint64_t timestamp_millis) override;
 
   void ReportCounter(DatumId counter_type, uint64_t value) override;
 
@@ -223,8 +276,40 @@
                        int64_t high_value,
                        const std::vector<uint32_t>& buckets) override;
 
+  void EndReport() override;
+
+  std::string GetAndResetBuffer();
+
  private:
-  std::ostream& os_;
+  std::ostringstream os_;
+  std::optional<SessionData> session_data_;
+};
+
+// A backend that writes metrics in human-readable format to the log (i.e. logcat).
+class LogBackend : public StringBackend {
+ public:
+  explicit LogBackend(android::base::LogSeverity level);
+
+  void BeginReport(uint64_t timestamp_millis) override;
+  void EndReport() override;
+
+ private:
+  android::base::LogSeverity level_;
+};
+
+// A backend that writes metrics to a file.
+//
+// These are currently written in the same human-readable format used by StringBackend and
+// LogBackend, but we will probably want a more machine-readable format in the future.
+class FileBackend : public StringBackend {
+ public:
+  explicit FileBackend(std::string filename);
+
+  void BeginReport(uint64_t timestamp_millis) override;
+  void EndReport() override;
+
+ private:
+  std::string filename_;
 };
 
 /**
@@ -323,14 +408,7 @@
 #undef ART_HISTOGRAM
 
  private:
-  // This field is only included to allow us expand the ART_COUNTERS and ART_HISTOGRAMS macro in
-  // the initializer list in ArtMetrics::ArtMetrics. See metrics.cc for how it's used.
-  //
-  // It's declared as a zero-length array so it has no runtime space impact.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-private-field"
-  int unused_[0];
-#pragma clang diagnostic pop  // -Wunused-private-field
+  uint64_t beginning_timestamp_;
 
 #define ART_COUNTER(name) MetricsCounter<DatumId::k##name> name##_;
   ART_COUNTERS(ART_COUNTER)
diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc
index 81d7215..c650fdf 100644
--- a/libartbase/base/metrics/metrics_common.cc
+++ b/libartbase/base/metrics/metrics_common.cc
@@ -16,8 +16,10 @@
 
 #include <sstream>
 
+#include "android-base/file.h"
 #include "android-base/logging.h"
 #include "base/macros.h"
+#include "base/scoped_flock.h"
 #include "metrics.h"
 
 #pragma clang diagnostic push
@@ -46,7 +48,22 @@
   }
 }
 
-ArtMetrics::ArtMetrics() : unused_ {}
+SessionData SessionData::CreateDefault() {
+#ifdef _WIN32
+  int32_t uid = kInvalidUserId;  // Windows does not support getuid();
+#else
+  int32_t uid = static_cast<int32_t>(getuid());
+#endif
+
+  return SessionData{
+    .compilation_reason = CompilationReason::kUnknown,
+    .compiler_filter = std::nullopt,
+    .session_id = kInvalidSessionId,
+    .uid = uid,
+  };
+}
+
+ArtMetrics::ArtMetrics() : beginning_timestamp_ {MilliTime()}
 #define ART_COUNTER(name) \
   , name##_ {}
 ART_COUNTERS(ART_COUNTER)
@@ -59,6 +76,8 @@
 }
 
 void ArtMetrics::ReportAllMetrics(MetricsBackend* backend) const {
+  backend->BeginReport(MilliTime() - beginning_timestamp_);
+
 // Dump counters
 #define ART_COUNTER(name) name()->Report(backend);
   ART_COUNTERS(ART_COUNTER)
@@ -68,34 +87,58 @@
 #define ART_HISTOGRAM(name, num_buckets, low_value, high_value) name()->Report(backend);
   ART_HISTOGRAMS(ART_HISTOGRAM)
 #undef ART_HISTOGRAM
+
+  backend->EndReport();
 }
 
 void ArtMetrics::DumpForSigQuit(std::ostream& os) const {
-  os << "\n*** ART internal metrics ***\n\n";
-  StreamBackend backend{os};
+  StringBackend backend;
   ReportAllMetrics(&backend);
-  os << "\n*** Done dumping ART internal metrics ***\n";
+  os << backend.GetAndResetBuffer();
 }
 
-StreamBackend::StreamBackend(std::ostream& os) : os_{os} {}
+StringBackend::StringBackend() {}
 
-void StreamBackend::BeginSession([[maybe_unused]] const SessionData& session_data) {
-  // Not needed for now.
+std::string StringBackend::GetAndResetBuffer() {
+  std::string result = os_.str();
+  os_.clear();
+  os_.str("");
+  return result;
 }
 
-void StreamBackend::EndSession() {
-  // Not needed for now.
+void StringBackend::BeginSession(const SessionData& session_data) {
+  session_data_ = session_data;
 }
 
-void StreamBackend::ReportCounter(DatumId counter_type, uint64_t value) {
-  os_ << DatumName(counter_type) << ": count = " << value << "\n";
+void StringBackend::BeginReport(uint64_t timestamp_since_start_ms) {
+  os_ << "\n*** ART internal metrics ***\n";
+  os_ << "  Metadata:\n";
+  os_ << "    timestamp_since_start_ms: " << timestamp_since_start_ms << "\n";
+  if (session_data_.has_value()) {
+    os_ << "    session_id: " << session_data_->session_id << "\n";
+    os_ << "    uid: " << session_data_->uid << "\n";
+    os_ << "    compilation_reason: " << CompilationReasonName(session_data_->compilation_reason)
+        << "\n";
+    os_ << "    compiler_filter: "
+        << (session_data_->compiler_filter.has_value()
+                ? CompilerFilter::NameOfFilter(session_data_->compiler_filter.value())
+                : "(unspecified)")
+        << "\n";
+  }
+  os_ << "  Metrics:\n";
 }
 
-void StreamBackend::ReportHistogram(DatumId histogram_type,
+void StringBackend::EndReport() { os_ << "*** Done dumping ART internal metrics ***\n"; }
+
+void StringBackend::ReportCounter(DatumId counter_type, uint64_t value) {
+  os_ << "    " << DatumName(counter_type) << ": count = " << value << "\n";
+}
+
+void StringBackend::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_;
+  os_ << "    " << DatumName(histogram_type) << ": range = " << minimum_value_ << "..." << maximum_value_;
   if (buckets.size() > 0) {
     os_ << ", buckets: ";
     bool first = true;
@@ -112,6 +155,39 @@
   }
 }
 
+LogBackend::LogBackend(android::base::LogSeverity level) : level_{level} {}
+
+void LogBackend::BeginReport(uint64_t timestamp_since_start_ms) {
+  GetAndResetBuffer();
+  StringBackend::BeginReport(timestamp_since_start_ms);
+}
+
+void LogBackend::EndReport() {
+  StringBackend::EndReport();
+  LOG_STREAM(level_) << GetAndResetBuffer();
+}
+
+FileBackend::FileBackend(std::string filename) : filename_{filename} {}
+
+void FileBackend::BeginReport(uint64_t timestamp_since_start_ms) {
+  GetAndResetBuffer();
+  StringBackend::BeginReport(timestamp_since_start_ms);
+}
+
+void FileBackend::EndReport() {
+  StringBackend::EndReport();
+  std::string error_message;
+  auto file{
+      LockedFile::Open(filename_.c_str(), O_CREAT | O_WRONLY | O_APPEND, true, &error_message)};
+  if (file.get() == nullptr) {
+    LOG(WARNING) << "Could open metrics file '" << filename_ << "': " << error_message;
+  } else {
+    if (!android::base::WriteStringToFd(GetAndResetBuffer(), file.get()->Fd())) {
+      PLOG(WARNING) << "Error writing metrics to file";
+    }
+  }
+}
+
 }  // namespace metrics
 }  // namespace art
 
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index f568ea0..4643f14 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -208,20 +208,20 @@
 // Makes sure all defined metrics are included when dumping through StreamBackend.
 TEST_F(MetricsTest, StreamBackendDumpAllMetrics) {
   ArtMetrics metrics;
-  std::stringstream os;
-  StreamBackend backend(os);
+  StringBackend backend;
 
   metrics.ReportAllMetrics(&backend);
 
   // Make sure the resulting string lists all the counters.
+  const std::string result = backend.GetAndResetBuffer();
 #define COUNTER(name) \
-  EXPECT_NE(os.str().find(DatumName(DatumId::k##name)), std::string::npos)
+  EXPECT_NE(result.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)
+  EXPECT_NE(result.find(DatumName(DatumId::k##name)), std::string::npos)
   ART_HISTOGRAMS(HISTOGRAM);
 #undef HISTOGRAM
 }
diff --git a/libartbase/base/metrics/metrics_test.h b/libartbase/base/metrics/metrics_test.h
index 72ca90e..4a7dad8 100644
--- a/libartbase/base/metrics/metrics_test.h
+++ b/libartbase/base/metrics/metrics_test.h
@@ -34,7 +34,8 @@
 class TestBackendBase : public MetricsBackend {
  public:
   void BeginSession([[maybe_unused]] const SessionData& session_data) override {}
-  void EndSession() override {}
+
+  void BeginReport([[maybe_unused]] uint64_t timestamp_since_start_ms) override {}
 
   void ReportCounter([[maybe_unused]] DatumId counter_type,
                      [[maybe_unused]] uint64_t value) override {}
@@ -43,6 +44,8 @@
                        [[maybe_unused]] int64_t low_value_,
                        [[maybe_unused]] int64_t high_value,
                        [[maybe_unused]] const std::vector<uint32_t>& buckets) override {}
+
+  void EndReport() override {}
 };
 
 template <DatumId counter_type>
diff --git a/runtime/Android.bp b/runtime/Android.bp
index ca82dcf..4608af1 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -80,7 +80,6 @@
         "class_table.cc",
         "common_throws.cc",
         "compat_framework.cc",
-        "compiler_filter.cc",
         "debug_print.cc",
         "debugger.cc",
         "dex/dex_file_annotations.cc",
@@ -660,7 +659,6 @@
         "class_linker_test.cc",
         "class_loader_context_test.cc",
         "class_table_test.cc",
-        "compiler_filter_test.cc",
         "entrypoints/math_entrypoints_test.cc",
         "entrypoints/quick/quick_trampoline_entrypoints_test.cc",
         "entrypoints_order_test.cc",
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index c7d5230..dcc3d8d 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -24,6 +24,7 @@
 #include "android-base/strings.h"
 
 #include "art_method-inl.h"
+#include "base/compiler_filter.h"
 #include "base/enums.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/scoped_arena_containers.h"
@@ -31,7 +32,6 @@
 #include "base/systrace.h"
 #include "base/time_utils.h"
 #include "class_table-inl.h"
-#include "compiler_filter.h"
 #include "dex/dex_file_loader.h"
 #include "dex_reference_collection.h"
 #include "gc/collector_type.h"
diff --git a/runtime/metrics_reporter.cc b/runtime/metrics_reporter.cc
index d3ec909..2004c7d 100644
--- a/runtime/metrics_reporter.cc
+++ b/runtime/metrics_reporter.cc
@@ -16,8 +16,6 @@
 
 #include "metrics_reporter.h"
 
-#include "android-base/file.h"
-#include "base/scoped_flock.h"
 #include "runtime.h"
 #include "runtime_options.h"
 #include "thread-current-inl.h"
@@ -28,8 +26,6 @@
 namespace art {
 namespace metrics {
 
-using android::base::WriteStringToFd;
-
 std::unique_ptr<MetricsReporter> MetricsReporter::Create(ReportingConfig config, Runtime* runtime) {
   // We can't use std::make_unique here because the MetricsReporter constructor is private.
   return std::unique_ptr<MetricsReporter>{new MetricsReporter{std::move(config), runtime}};
@@ -40,12 +36,12 @@
 
 MetricsReporter::~MetricsReporter() { MaybeStopBackgroundThread(); }
 
-void MetricsReporter::MaybeStartBackgroundThread() {
-  if (config_.BackgroundReportingEnabled()) {
-    CHECK(!thread_.has_value());
+void MetricsReporter::MaybeStartBackgroundThread(SessionData session_data) {
+  CHECK(!thread_.has_value());
 
-    thread_.emplace(&MetricsReporter::BackgroundThreadRun, this);
-  }
+  thread_.emplace(&MetricsReporter::BackgroundThreadRun, this);
+
+  messages_.SendMessage(BeginSessionMessage{session_data});
 }
 
 void MetricsReporter::MaybeStopBackgroundThread() {
@@ -53,10 +49,6 @@
     messages_.SendMessage(ShutdownRequestedMessage{});
     thread_->join();
   }
-  // Do one final metrics report, if enabled.
-  if (config_.report_metrics_on_shutdown) {
-    ReportMetrics();
-  }
 }
 
 void MetricsReporter::NotifyStartupCompleted() {
@@ -76,13 +68,33 @@
                                                       /*create_peer=*/true);
   bool running = true;
 
+  // Configure the backends
+  if (config_.dump_to_logcat) {
+    backends_.emplace_back(new LogBackend(LogSeverity::INFO));
+  }
+  if (config_.dump_to_file.has_value()) {
+    backends_.emplace_back(new FileBackend(config_.dump_to_file.value()));
+  }
+
   MaybeResetTimeout();
 
   while (running) {
     messages_.SwitchReceive(
+        [&](BeginSessionMessage message) {
+          LOG_STREAM(DEBUG) << "Received session metadata";
+
+          for (auto& backend : backends_) {
+            backend->BeginSession(message.session_data);
+          }
+        },
         [&]([[maybe_unused]] ShutdownRequestedMessage message) {
           LOG_STREAM(DEBUG) << "Shutdown request received";
           running = false;
+
+          // Do one final metrics report, if enabled.
+          if (config_.report_metrics_on_shutdown) {
+            ReportMetrics();
+          }
         },
         [&]([[maybe_unused]] TimeoutExpiredMessage message) {
           LOG_STREAM(DEBUG) << "Timer expired, reporting metrics";
@@ -110,34 +122,10 @@
 }
 
 void MetricsReporter::ReportMetrics() const {
-  if (config_.dump_to_logcat) {
-    LOG_STREAM(INFO) << "\n*** ART internal metrics ***\n\n";
-    // LOG_STREAM(INFO) destroys the stream at the end of the statement, which makes it tricky pass
-    // it to store as a field in StreamBackend. To get around this, we use an immediately-invoked
-    // lambda expression to act as a let-binding, letting us access the stream for long enough to
-    // dump the metrics.
-    [this](std::ostream& os) {
-      StreamBackend backend{os};
-      runtime_->GetMetrics()->ReportAllMetrics(&backend);
-    }(LOG_STREAM(INFO));
-    LOG_STREAM(INFO) << "\n*** Done dumping ART internal metrics ***\n";
-  }
-  if (config_.dump_to_file.has_value()) {
-    const auto& filename = config_.dump_to_file.value();
-    std::ostringstream stream;
-    StreamBackend backend{stream};
-    runtime_->GetMetrics()->ReportAllMetrics(&backend);
+  ArtMetrics* metrics{runtime_->GetMetrics()};
 
-    std::string error_message;
-    auto file{
-        LockedFile::Open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, true, &error_message)};
-    if (file.get() == nullptr) {
-      LOG(WARNING) << "Could open metrics file '" << filename << "': " << error_message;
-    } else {
-      if (!WriteStringToFd(stream.str(), file.get()->Fd())) {
-        PLOG(WARNING) << "Error writing metrics to file";
-      }
-    }
+  for (auto& backend : backends_) {
+    metrics->ReportAllMetrics(backend.get());
   }
 }
 
diff --git a/runtime/metrics_reporter.h b/runtime/metrics_reporter.h
index 4b5dbe5..a9cd1f3 100644
--- a/runtime/metrics_reporter.h
+++ b/runtime/metrics_reporter.h
@@ -46,13 +46,6 @@
 
   // Returns whether any options are set that enables metrics reporting.
   constexpr bool ReportingEnabled() const { return dump_to_logcat || dump_to_file.has_value(); }
-
-  // Returns whether any options are set that requires a background reporting thread.
-  constexpr bool BackgroundReportingEnabled() const {
-    // If any reporting is enabled, we always need to do at least the startup report in the
-    // background.
-    return ReportingEnabled();
-  }
 };
 
 // MetricsReporter handles periodically reporting ART metrics.
@@ -64,7 +57,7 @@
   ~MetricsReporter();
 
   // Creates and runs the background reporting thread.
-  void MaybeStartBackgroundThread();
+  void MaybeStartBackgroundThread(SessionData session_data);
 
   // Sends a request to the background thread to shutdown.
   void MaybeStopBackgroundThread();
@@ -89,7 +82,7 @@
 
   const ReportingConfig config_;
   Runtime* runtime_;
-
+  std::vector<std::unique_ptr<MetricsBackend>> backends_;
   std::optional<std::thread> thread_;
 
   // A message indicating that the reporting thread should shut down.
@@ -98,7 +91,13 @@
   // A message indicating that app startup has completed.
   struct StartupCompletedMessage {};
 
-  MessageQueue<ShutdownRequestedMessage, StartupCompletedMessage> messages_;
+  // A message marking the beginning of a metrics logging session.
+  //
+  // The primary purpose of this is to pass the session metadata from the Runtime to the metrics
+  // backends.
+  struct BeginSessionMessage{ SessionData session_data; };
+
+  MessageQueue<ShutdownRequestedMessage, StartupCompletedMessage, BeginSessionMessage> messages_;
 };
 
 }  // namespace metrics
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 8bb8ce8..a966d66 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -21,6 +21,7 @@
 #include "android-base/stringprintf.h"
 
 #include "base/casts.h"
+#include "base/compiler_filter.h"
 #include "base/file_utils.h"
 #include "base/hiddenapi_domain.h"
 #include "base/logging.h"
@@ -31,7 +32,6 @@
 #include "class_linker.h"
 #include "class_loader_context.h"
 #include "common_throws.h"
-#include "compiler_filter.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
diff --git a/runtime/oat.h b/runtime/oat.h
index 0dc49c3..e7e9a58 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -20,9 +20,9 @@
 #include <array>
 #include <vector>
 
+#include "base/compiler_filter.h"
 #include "base/macros.h"
 #include "base/safe_map.h"
-#include "compiler_filter.h"
 
 namespace art {
 
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 9e21b7e..d4f4d95 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -23,12 +23,12 @@
 #include <vector>
 
 #include "base/array_ref.h"
+#include "base/compiler_filter.h"
 #include "base/mutex.h"
 #include "base/os.h"
 #include "base/safe_map.h"
 #include "base/tracking_safe_map.h"
 #include "class_status.h"
-#include "compiler_filter.h"
 #include "dex/dex_file_layout.h"
 #include "dex/type_lookup_table.h"
 #include "dex/utf.h"
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 97254d2..f9e099b 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -24,6 +24,7 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
+#include "base/compiler_filter.h"
 #include "base/file_utils.h"
 #include "base/logging.h"  // For VLOG.
 #include "base/macros.h"
@@ -34,7 +35,6 @@
 #include "base/utils.h"
 #include "class_linker.h"
 #include "class_loader_context.h"
-#include "compiler_filter.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file_loader.h"
 #include "exec_utils.h"
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 19c625b..9f9c91f 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -22,11 +22,11 @@
 #include <sstream>
 #include <string>
 
+#include "base/compiler_filter.h"
 #include "arch/instruction_set.h"
 #include "base/os.h"
 #include "base/scoped_flock.h"
 #include "base/unix_file/fd_file.h"
-#include "compiler_filter.h"
 #include "class_loader_context.h"
 #include "oat_file.h"
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 043384d..2eeb373 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1070,7 +1070,10 @@
   heap_->ResetGcPerformanceInfo();
 
   if (metrics_reporter_ != nullptr) {
-    metrics_reporter_->MaybeStartBackgroundThread();
+    metrics::SessionData session_data{metrics::SessionData::CreateDefault()};
+    session_data.session_id = GetRandomNumber<int64_t>(0, std::numeric_limits<int64_t>::max());
+    // TODO: set session_data.compilation_reason and session_data.compiler_filter
+    metrics_reporter_->MaybeStartBackgroundThread(session_data);
   }
 
   StartSignalCatcher();