/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_LIBARTBASE_BASE_METRICS_METRICS_H_
#define ART_LIBARTBASE_BASE_METRICS_METRICS_H_

#include <stdint.h>

#include <array>
#include <atomic>
#include <optional>
#include <sstream>
#include <string_view>
#include <thread>
#include <vector>

#include "android-base/logging.h"
#include "base/bit_utils.h"
#include "base/macros.h"
#include "base/time_utils.h"
#include "jni.h"
#include "tinyxml2.h"

#pragma clang diagnostic push
#pragma clang diagnostic error "-Wconversion"

// See README.md in this directory for how to define metrics.

// Metrics reported as Event Metrics.
#define ART_EVENT_METRICS(METRIC)                                   \
  METRIC(ClassLoadingTotalTime, MetricsCounter)                     \
  METRIC(ClassVerificationTotalTime, MetricsCounter)                \
  METRIC(ClassVerificationCount, MetricsCounter)                    \
  METRIC(WorldStopTimeDuringGCAvg, MetricsAverage)                  \
  METRIC(YoungGcCount, MetricsCounter)                              \
  METRIC(FullGcCount, MetricsCounter)                               \
  METRIC(TotalBytesAllocated, MetricsCounter)                       \
  METRIC(TotalGcCollectionTime, MetricsCounter)                     \
  METRIC(YoungGcThroughputAvg, MetricsAverage)                      \
  METRIC(FullGcThroughputAvg, MetricsAverage)                       \
  METRIC(YoungGcTracingThroughputAvg, MetricsAverage)               \
  METRIC(FullGcTracingThroughputAvg, MetricsAverage)                \
  METRIC(JitMethodCompileTotalTime, MetricsCounter)                 \
  METRIC(JitMethodCompileCount, MetricsCounter)                     \
  METRIC(YoungGcCollectionTime, MetricsHistogram, 15, 0, 60'000)    \
  METRIC(FullGcCollectionTime, MetricsHistogram, 15, 0, 60'000)     \
  METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 10'000)        \
  METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 10'000)         \
  METRIC(YoungGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
  METRIC(FullGcTracingThroughput, MetricsHistogram, 15, 0, 10'000)  \
  METRIC(GcWorldStopTime, MetricsCounter)                           \
  METRIC(GcWorldStopCount, MetricsCounter)                          \
  METRIC(YoungGcScannedBytes, MetricsCounter)                       \
  METRIC(YoungGcFreedBytes, MetricsCounter)                         \
  METRIC(YoungGcDuration, MetricsCounter)                           \
  METRIC(FullGcScannedBytes, MetricsCounter)                        \
  METRIC(FullGcFreedBytes, MetricsCounter)                          \
  METRIC(FullGcDuration, MetricsCounter)

// Increasing counter metrics, reported as Value Metrics in delta increments.
#define ART_VALUE_METRICS(METRIC)                              \
  METRIC(GcWorldStopTimeDelta, MetricsDeltaCounter)            \
  METRIC(GcWorldStopCountDelta, MetricsDeltaCounter)           \
  METRIC(YoungGcScannedBytesDelta, MetricsDeltaCounter)        \
  METRIC(YoungGcFreedBytesDelta, MetricsDeltaCounter)          \
  METRIC(YoungGcDurationDelta, MetricsDeltaCounter)            \
  METRIC(FullGcScannedBytesDelta, MetricsDeltaCounter)         \
  METRIC(FullGcFreedBytesDelta, MetricsDeltaCounter)           \
  METRIC(FullGcDurationDelta, MetricsDeltaCounter)             \
  METRIC(JitMethodCompileTotalTimeDelta, MetricsDeltaCounter)  \
  METRIC(JitMethodCompileCountDelta, MetricsDeltaCounter)      \
  METRIC(ClassVerificationTotalTimeDelta, MetricsDeltaCounter) \
  METRIC(ClassVerificationCountDelta, MetricsDeltaCounter)     \
  METRIC(ClassLoadingTotalTimeDelta, MetricsDeltaCounter)      \
  METRIC(TotalBytesAllocatedDelta, MetricsDeltaCounter)        \
  METRIC(TotalGcCollectionTimeDelta, MetricsDeltaCounter)      \
  METRIC(YoungGcCountDelta, MetricsDeltaCounter)               \
  METRIC(FullGcCountDelta, MetricsDeltaCounter)                \
  METRIC(TimeElapsedDelta, MetricsDeltaCounter)

#define ART_METRICS(METRIC) \
  ART_EVENT_METRICS(METRIC) \
  ART_VALUE_METRICS(METRIC)

// A lot of the metrics implementation code is generated by passing one-off macros into ART_COUNTERS
// and ART_HISTOGRAMS. This means metrics.h and metrics.cc are very #define-heavy, which can be
// challenging to read. The alternative was to require a lot of boilerplate code for each new metric
// added, all of which would need to be rewritten if the metrics implementation changed. Using
// macros lets us add new metrics by adding a single line to either ART_COUNTERS or ART_HISTOGRAMS,
// and modifying the implementation only requires changing the implementation once, instead of once
// per metric.

namespace art {

class Runtime;
struct RuntimeArgumentMap;

[[maybe_unused]] static jlong VMRuntime_getFullGcCount(JNIEnv* env, jclass klass);

namespace metrics {
template <typename value_t>
class MetricsBase;
}  // namespace metrics

namespace gc {
class HeapTest_GCMetrics_Test;
}  // namespace gc

namespace metrics {

/**
 * An enumeration of all ART counters and histograms.
 */
enum class DatumId {
#define METRIC(name, type, ...) k##name,
  ART_METRICS(METRIC)
#undef METRIC
};

// Names come from PackageManagerServiceCompilerMapping.java
#define REASON_NAME_LIST(V)                                               \
  V(kError, "error")                                                      \
  V(kUnknown, "unknown")                                                  \
  V(kFirstBoot, "first-boot")                                             \
  V(kBootAfterOTA, "boot-after-ota")                                      \
  V(kPostBoot, "post-boot")                                               \
  V(kInstall, "install")                                                  \
  V(kInstallFast, "install-fast")                                         \
  V(kInstallBulk, "install-bulk")                                         \
  V(kInstallBulkSecondary, "install-bulk-secondary")                      \
  V(kInstallBulkDowngraded, "install-bulk-downgraded")                    \
  V(kInstallBulkSecondaryDowngraded, "install-bulk-secondary-downgraded") \
  V(kBgDexopt, "bg-dexopt")                                               \
  V(kABOTA, "ab-ota")                                                     \
  V(kInactive, "inactive")                                                \
  V(kShared, "shared")                                                    \
  V(kInstallWithDexMetadata, "install-with-dex-metadata")                 \
  V(kPrebuilt, "prebuilt")                                                \
  V(kCmdLine, "cmdline")                                                  \
  V(kVdex, "vdex")                                                        \
  V(kBootAfterMainlineUpdate, "boot-after-mainline-update")

// 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 {
#define REASON(kind, name) kind,
  REASON_NAME_LIST(REASON)
#undef REASON
};

#define REASON_NAME(kind, kind_name) \
    case CompilationReason::kind: return kind_name;
#define REASON_FROM_NAME(kind, kind_name) \
    if (name == (kind_name)) { return CompilationReason::kind; }

constexpr const char* CompilationReasonName(CompilationReason reason) {
  switch (reason) {
    REASON_NAME_LIST(REASON_NAME)
  }
}

constexpr CompilationReason CompilationReasonFromName(std::string_view name) {
  REASON_NAME_LIST(REASON_FROM_NAME)
  return CompilationReason::kError;
}

#undef REASON_NAME
#undef REASON_FROM_NAME

#define COMPILER_FILTER_REPORTING_LIST(V) \
  V(kError, "error") /* Error (invalid value) condition */ \
  V(kUnknown, "unknown") /* Unknown (not set) condition */ \
  V(kAssumeVerified, "assume-verified") /* Standard compiler filters */ \
  V(kExtract, "extract") \
  V(kVerify, "verify") \
  V(kSpaceProfile, "space-profile") \
  V(kSpace, "space") \
  V(kSpeedProfile, "speed-profile") \
  V(kSpeed, "speed") \
  V(kEverythingProfile, "everything-profile") \
  V(kEverything, "everything") \
  V(kRunFromApk, "run-from-apk") /* Augmented compiler filters as produces by OatFileAssistant#GetOptimizationStatus */ \
  V(kRunFromApkFallback, "run-from-apk-fallback")

// Augmented compiler filter enum, used in the reporting infra.
enum class CompilerFilterReporting {
#define FILTER(kind, name) kind,
  COMPILER_FILTER_REPORTING_LIST(FILTER)
#undef FILTER
};

#define FILTER_NAME(kind, kind_name) \
    case CompilerFilterReporting::kind: return kind_name;
#define FILTER_FROM_NAME(kind, kind_name) \
    if (name == (kind_name)) { return CompilerFilterReporting::kind; }

constexpr const char* CompilerFilterReportingName(CompilerFilterReporting filter) {
  switch (filter) {
    COMPILER_FILTER_REPORTING_LIST(FILTER_NAME)
  }
}

constexpr CompilerFilterReporting CompilerFilterReportingFromName(std::string_view name) {
  COMPILER_FILTER_REPORTING_LIST(FILTER_FROM_NAME)
  return CompilerFilterReporting::kError;
}

#undef FILTER_NAME
#undef FILTER_FROM_NAME

// 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 {
  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;
  CompilerFilterReporting compiler_filter;
};

// MetricsBackends are used by a metrics reporter to write metrics to some external location. For
// example, a backend might write to logcat, or to a file, or to statsd.
class MetricsBackend {
 public:
  virtual ~MetricsBackend() {}

  // Begins an ART metrics session.
  //
  // This is called by the metrics reporter when the runtime is starting up. The session_data
  // includes a session id which is used to correlate any metric reports with the same instance of
  // the ART runtime. Additionally, session_data includes useful metadata such as the package name
  // for this process.
  //
  // It may also be called whenever there is an update to the session metadata (e.g. optimization
  // state).
  virtual void BeginOrUpdateSession(const SessionData& session_data) = 0;

 protected:
  // Called by the metrics reporter to indicate that a new metrics report is starting.
  virtual void BeginReport(uint64_t timestamp_since_start_ms) = 0;

  // Called by the metrics reporter to give the current value of the counter with id counter_type.
  //
  // This will be called multiple times for each counter based on when the metrics reporter chooses
  // to report metrics. For example, the metrics reporter may call this at shutdown or every N
  // minutes. Counters are not reset in between invocations, so the value should represent the
  // total count at the point this method is called.
  virtual void ReportCounter(DatumId counter_type, uint64_t value) = 0;

  // Called by the metrics reporter to report a histogram.
  //
  // This is called similarly to ReportCounter, but instead of receiving a single value, it receives
  // a vector of the value in each bucket. Additionally, the function receives the lower and upper
  // limit for the histogram. Note that these limits are the allowed limits, and not the observed
  // range. Values below the lower limit will be counted in the first bucket, and values above the
  // upper limit will be counted in the last bucket. Backends should store the minimum and maximum
  // values to allow comparisons across module versions, since the minimum and maximum values may
  // change over time.
  virtual void ReportHistogram(DatumId histogram_type,
                               int64_t minimum_value,
                               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, typename T>
  friend class MetricsCounter;
  template <DatumId counter_type, typename T>
  friend class MetricsDeltaCounter;
  template <DatumId histogram_type, size_t num_buckets, int64_t low_value, int64_t high_value>
  friend class MetricsHistogram;
  template <DatumId datum_id, typename T, const T& AccumulatorFunction(const T&, const T&)>
  friend class MetricsAccumulator;
  template <DatumId datum_id, typename T>
  friend class MetricsAverage;
  friend class ArtMetrics;
};

template <typename value_t>
class MetricsBase {
 public:
  virtual void Add(value_t value) = 0;
  virtual ~MetricsBase() { }

 private:
  // Is the metric "null", i.e. never updated or freshly reset?
  // Used for testing purpose only.
  virtual bool IsNull() const = 0;

  ART_FRIEND_TEST(gc::HeapTest, GCMetrics);
};

template <DatumId counter_type, typename T = uint64_t>
class MetricsCounter : public MetricsBase<T> {
 public:
  using value_t = T;
  explicit constexpr MetricsCounter(uint64_t value = 0) : value_{value} {
    // Ensure we do not have any unnecessary data in this class.
    // Adding intptr_t to accommodate vtable, and rounding up to incorporate
    // padding.
    static_assert(RoundUp(sizeof(*this), sizeof(uint64_t))
                  == RoundUp(sizeof(intptr_t) + sizeof(value_t), sizeof(uint64_t)));
  }

  void AddOne() { Add(1u); }
  void Add(value_t value) override {
    value_.fetch_add(value, std::memory_order_relaxed);
  }

  void Report(const std::vector<MetricsBackend*>& backends) const {
    for (MetricsBackend* backend : backends) {
      backend->ReportCounter(counter_type, Value());
    }
  }

 protected:
  void Reset() { value_ = 0; }
  value_t Value() const { return value_.load(std::memory_order_relaxed); }

 private:
  bool IsNull() const override { return Value() == 0; }

  std::atomic<value_t> value_;
  static_assert(std::atomic<value_t>::is_always_lock_free);

  friend class ArtMetrics;
  friend jlong art::VMRuntime_getFullGcCount(JNIEnv* env, jclass klass);
};

template <DatumId datum_id, typename T = uint64_t>
class MetricsAverage final : public MetricsCounter<datum_id, T> {
 public:
  using value_t = T;
  using count_t = T;
  explicit constexpr MetricsAverage(uint64_t value = 0, uint64_t count = 0) :
      MetricsCounter<datum_id, value_t>(value), count_(count) {
    // Ensure we do not have any unnecessary data in this class.
    // Adding intptr_t to accommodate vtable, and rounding up to incorporate
    // padding.
    static_assert(RoundUp(sizeof(*this), sizeof(uint64_t))
                  == RoundUp(sizeof(intptr_t) + sizeof(value_t) + sizeof(count_t),
                             sizeof(uint64_t)));
  }

  // We use release memory-order here and then acquire in Report() to ensure
  // that at least the non-racy reads/writes to this metric are consistent. This
  // doesn't guarantee the atomicity of the change to both fields, but that
  // may not be desired because:
  // 1. The metric eventually becomes consistent.
  // 2. For sufficiently large count_, a few data points which are off shouldn't
  // make a huge difference to the reporter.
  void Add(value_t value) override {
    MetricsCounter<datum_id, value_t>::Add(value);
    count_.fetch_add(1, std::memory_order_release);
  }

  void Report(const std::vector<MetricsBackend*>& backends) const {
    count_t value = MetricsCounter<datum_id, value_t>::Value();
    count_t count = count_.load(std::memory_order_acquire);
    // Avoid divide-by-0.
    count_t average_value = count != 0 ? value / count : 0;
    for (MetricsBackend* backend : backends) {
      backend->ReportCounter(datum_id, average_value);
    }
  }

 protected:
  void Reset() {
    count_ = 0;
    MetricsCounter<datum_id, value_t>::Reset();
  }

 private:
  count_t Count() const { return count_.load(std::memory_order_relaxed); }

  bool IsNull() const override { return Count() == 0; }

  std::atomic<count_t> count_;
  static_assert(std::atomic<count_t>::is_always_lock_free);

  friend class ArtMetrics;
};

template <DatumId datum_id, typename T = uint64_t>
class MetricsDeltaCounter : public MetricsBase<T> {
 public:
  using value_t = T;

  explicit constexpr MetricsDeltaCounter(uint64_t value = 0) : value_{value} {
    // Ensure we do not have any unnecessary data in this class.
    // Adding intptr_t to accommodate vtable, and rounding up to incorporate
    // padding.
    static_assert(RoundUp(sizeof(*this), sizeof(uint64_t)) ==
                  RoundUp(sizeof(intptr_t) + sizeof(value_t), sizeof(uint64_t)));
  }

  void Add(value_t value) override {
    value_.fetch_add(value, std::memory_order_relaxed);
  }
  void AddOne() { Add(1u); }

  void ReportAndReset(const std::vector<MetricsBackend*>& backends) {
    value_t value = value_.exchange(0, std::memory_order_relaxed);
    for (MetricsBackend* backend : backends) {
      backend->ReportCounter(datum_id, value);
    }
  }

  void Reset() { value_ = 0; }

 private:
  value_t Value() const { return value_.load(std::memory_order_relaxed); }

  bool IsNull() const override { return Value() == 0; }

  std::atomic<value_t> value_;
  static_assert(std::atomic<value_t>::is_always_lock_free);

  friend class ArtMetrics;
};

template <DatumId histogram_type_,
          size_t num_buckets_,
          int64_t minimum_value_,
          int64_t maximum_value_>
class MetricsHistogram final : public MetricsBase<int64_t> {
  static_assert(num_buckets_ >= 1);
  static_assert(minimum_value_ < maximum_value_);

 public:
  using value_t = uint32_t;

  constexpr MetricsHistogram() : buckets_{} {
    // Ensure we do not have any unnecessary data in this class.
    // Adding intptr_t to accommodate vtable, and rounding up to incorporate
    // padding.
    static_assert(RoundUp(sizeof(*this), sizeof(uint64_t))
                  == RoundUp(sizeof(intptr_t) + sizeof(value_t) * num_buckets_, sizeof(uint64_t)));
  }

  void Add(int64_t value) override {
    const size_t i = FindBucketId(value);
    buckets_[i].fetch_add(1u, std::memory_order_relaxed);
  }

  void Report(const std::vector<MetricsBackend*>& backends) const {
    for (MetricsBackend* backend : backends) {
      backend->ReportHistogram(histogram_type_, minimum_value_, maximum_value_, GetBuckets());
    }
  }

 protected:
  void Reset() {
    for (auto& bucket : buckets_) {
      bucket = 0;
    }
  }

 private:
  inline constexpr size_t FindBucketId(int64_t value) const {
    // Values below the minimum are clamped into the first bucket.
    if (value <= minimum_value_) {
      return 0;
    }
    // Values above the maximum are clamped into the last bucket.
    if (value >= maximum_value_) {
      return num_buckets_ - 1;
    }
    // Otherise, linearly interpolate the value into the right bucket
    constexpr size_t bucket_width = maximum_value_ - minimum_value_;
    return static_cast<size_t>(value - minimum_value_) * num_buckets_ / bucket_width;
  }

  std::vector<value_t> GetBuckets() const {
    // The loads from buckets_ will all be memory_order_seq_cst, which means they will be acquire
    // loads. This is a stricter memory order than is needed, but this should not be a
    // performance-critical section of code.
    return std::vector<value_t>{buckets_.begin(), buckets_.end()};
  }

  bool IsNull() const override {
    std::vector<value_t> buckets = GetBuckets();
    return std::all_of(buckets.cbegin(), buckets.cend(), [](value_t i) { return i == 0; });
  }

  std::array<std::atomic<value_t>, num_buckets_> buckets_;
  static_assert(std::atomic<value_t>::is_always_lock_free);

  friend class ArtMetrics;
};

template <DatumId datum_id, typename T, const T& AccumulatorFunction(const T&, const T&)>
class MetricsAccumulator final : MetricsBase<T> {
 public:
  explicit constexpr MetricsAccumulator(T value = 0) : value_{value} {
    // Ensure we do not have any unnecessary data in this class.
    // Adding intptr_t to accommodate vtable, and rounding up to incorporate
    // padding.
    static_assert(RoundUp(sizeof(*this), sizeof(uint64_t)) ==
                  RoundUp(sizeof(intptr_t) + sizeof(T), sizeof(uint64_t)));
  }

  void Add(T value) override {
    T current = value_.load(std::memory_order_relaxed);
    T new_value;
    do {
      new_value = AccumulatorFunction(current, value);
      // If the value didn't change, don't bother storing it.
      if (current == new_value) {
        break;
      }
    } while (!value_.compare_exchange_weak(
        current, new_value, std::memory_order_relaxed));
  }

  // Report the metric as a counter, since this has only a single value.
  void Report(MetricsBackend* backend) const {
    backend->ReportCounter(datum_id, static_cast<uint64_t>(Value()));
  }

 protected:
  void Reset() {
    value_ = 0;
  }

 private:
  T Value() const { return value_.load(std::memory_order_relaxed); }

  bool IsNull() const override { return Value() == 0; }

  std::atomic<T> value_;

  friend class ArtMetrics;
};

// Base class for formatting metrics into different formats
// (human-readable text, JSON, etc.)
class MetricsFormatter {
 public:
  virtual ~MetricsFormatter() = default;

  virtual void FormatBeginReport(uint64_t timestamp_since_start_ms,
                                 const std::optional<SessionData>& session_data) = 0;
  virtual void FormatEndReport() = 0;
  virtual void FormatReportCounter(DatumId counter_type, uint64_t value) = 0;
  virtual void FormatReportHistogram(DatumId histogram_type,
                                     int64_t low_value,
                                     int64_t high_value,
                                     const std::vector<uint32_t>& buckets) = 0;
  virtual std::string GetAndResetBuffer() = 0;

 protected:
  const std::string version = "1.0";
};

// Formatter outputting metrics in human-readable text format
class TextFormatter : public MetricsFormatter {
 public:
  TextFormatter() = default;

  void FormatBeginReport(uint64_t timestamp_millis,
                         const std::optional<SessionData>& session_data) override;

  void FormatReportCounter(DatumId counter_type, uint64_t value) override;

  void FormatReportHistogram(DatumId histogram_type,
                             int64_t low_value,
                             int64_t high_value,
                             const std::vector<uint32_t>& buckets) override;

  void FormatEndReport() override;

  std::string GetAndResetBuffer() override;

 private:
  std::ostringstream os_;
};

// Formatter outputting metrics in XML format
class XmlFormatter : public MetricsFormatter {
 public:
  XmlFormatter() = default;

  void FormatBeginReport(uint64_t timestamp_millis,
                         const std::optional<SessionData>& session_data) override;

  void FormatReportCounter(DatumId counter_type, uint64_t value) override;

  void FormatReportHistogram(DatumId histogram_type,
                             int64_t low_value,
                             int64_t high_value,
                             const std::vector<uint32_t>& buckets) override;

  void FormatEndReport() override;

  std::string GetAndResetBuffer() override;

 private:
  tinyxml2::XMLDocument document_;
};

// A backend that writes metrics to a string.
// The format of the metrics' output is delegated
// to the MetricsFormatter class.
//
// This is used as a base for LogBackend and FileBackend.
class StringBackend : public MetricsBackend {
 public:
  explicit StringBackend(std::unique_ptr<MetricsFormatter> formatter);

  void BeginOrUpdateSession(const SessionData& session_data) override;

  void BeginReport(uint64_t timestamp_millis) 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;

  void EndReport() override;

  std::string GetAndResetBuffer();

 private:
  std::unique_ptr<MetricsFormatter> formatter_;
  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(std::unique_ptr<MetricsFormatter> formatter,
                      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.
class FileBackend : public StringBackend {
 public:
  explicit FileBackend(std::unique_ptr<MetricsFormatter> formatter,
                       const std::string& filename);

  void BeginReport(uint64_t timestamp_millis) override;
  void EndReport() override;

 private:
  std::string filename_;
};

/**
 * AutoTimer simplifies time-based metrics collection.
 *
 * Several modes are supported. In the default case, the timer starts immediately and stops when it
 * goes out of scope. Example:
 *
 *     {
 *       AutoTimer timer{metric};
 *       DoStuff();
 *       // timer stops and updates metric automatically here.
 *     }
 *
 * You can also stop the timer early:
 *
 *     timer.Stop();
 *
 * Finally, you can choose to not automatically start the timer at the beginning by passing false as
 * the second argument to the constructor:
 *
 *     AutoTimer timer{metric, false};
 *     DoNotTimeThis();
 *     timer.Start();
 *     TimeThis();
 *
 * Manually started timers will still automatically stop in the destructor, but they can be manually
 * stopped as well.
 *
 * Note that AutoTimer makes calls to MicroTime(), so this may not be suitable on critical paths, or
 * in cases where the counter needs to be started and stopped on different threads.
 */
template <typename Metric>
class AutoTimer {
 public:
  explicit AutoTimer(Metric* metric, bool autostart = true)
      : running_{false}, start_time_microseconds_{}, metric_{metric} {
    if (autostart) {
      Start();
    }
  }

  ~AutoTimer() {
    if (running_) {
      Stop();
    }
  }

  void Start() {
    DCHECK(!running_);
    running_ = true;
    start_time_microseconds_ = MicroTime();
  }

  // Stops a running timer. Returns the time elapsed since starting the timer in microseconds.
  uint64_t Stop() {
    DCHECK(running_);
    uint64_t stop_time_microseconds = MicroTime();
    running_ = false;

    uint64_t elapsed_time = stop_time_microseconds - start_time_microseconds_;
    metric_->Add(static_cast<typename Metric::value_t>(elapsed_time));
    return elapsed_time;
  }

 private:
  bool running_;
  uint64_t start_time_microseconds_;
  Metric* metric_;
};

/**
 * This struct contains all of the metrics that ART reports.
 */
class ArtMetrics {
 public:
  ArtMetrics();

  void ReportAllMetricsAndResetValueMetrics(const std::vector<MetricsBackend*>& backends);
  void DumpForSigQuit(std::ostream& os);

  // Resets all metrics to their initial value. This is intended to be used after forking from the
  // zygote so we don't attribute parent values to the child process.
  void Reset();

#define METRIC_ACCESSORS(name, Kind, ...)                                        \
  Kind<DatumId::k##name, ##__VA_ARGS__>* name() { return &name##_; } \
  const Kind<DatumId::k##name, ##__VA_ARGS__>* name() const { return &name##_; }
  ART_METRICS(METRIC_ACCESSORS)
#undef METRIC_ACCESSORS

 private:
  uint64_t beginning_timestamp_;
  uint64_t last_report_timestamp_;

#define METRIC(name, Kind, ...) Kind<DatumId::k##name, ##__VA_ARGS__> name##_;
  ART_METRICS(METRIC)
#undef METRIC
};

// Returns a human readable name for the given DatumId.
std::string DatumName(DatumId datum);

// We also log the thread type for metrics so we can distinguish things that block the UI thread
// from things that happen on the background thread. This enum keeps track of what thread types we
// support.
enum class ThreadType {
  kMain,
  kBackground,
};

}  // namespace metrics
}  // namespace art

#pragma clang diagnostic pop  // -Wconversion

#endif  // ART_LIBARTBASE_BASE_METRICS_METRICS_H_
