// Copyright 2022 The Centipede Authors.
//
// 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
//
//      https://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.

// TODO(ussuri): Upgrade to optionally measure the metrics of a given thread,
//  not the entire process (available via /proc/self/tasks/<tid>/<file>).

//------------------------------------------------------------------------------
//                                RUsageProfiler
//
// A profiler for the current process's timing and system memory usage. Unlike
// external sampling profilers that require code instrumentation and slow
// profiling runs, RUsageProfiler's stat collection and reporting are
// permanently compiled into the client's code, consume no additional CPU cycles
// or RAM when idle and very few additional cycles or RAM when active, and can
// be activated at any time, e.g. by simply passing a flag.
//
// Another difference is that RUsageProfiler doesn't just track timing, but
// other system resource usage as well, such as several types of the process's
// memory.
//
// While traditional profilers report performance of functions, the focus of
// RUsageProfiler is performance of higher-level logical units of processing
// that can both be smaller than a single function or span multiple functions,
// classes, and modules.
//
// To achieve that, profiling metrics collection is explicit, intrusive and
// driven entirely by client code: RUsageProfiler profiles only the bits of
// logic it is told to profile, rather than every function call
// indiscriminately. The downside is that client code requires explicit
// profiling statements. The upside is that the client gets a very different
// level of control, as well as a differently structured and differently focused
// resource usage stats, unavailable with external sampling profilers: for
// example, RUsageProfiler makes it very easy to measure the overall resource
// usage dynamics of a complicated code path regardless of what function calls
// it makes or which external libraries it uses, or to print a comparison
// diagram of resource usage by different stages of a multi-stage processor
// module.
//
// Resource usage collection is available via one of or a combination of:
// 1) Explicit snapshots at specific execution checkpoints.
// 2) An asynchronous timelapse sequence of automatic snapshots taken at
//    regular intervals between two checkpoints.
//
//                                BASIC USAGE
//
// At construction, RUsageProfiler ctor records and logs an initial snapshot of
// the metrics requested by the client; at destruction, the dtor logs the
// current resource usage and the delta from the time of construction.
//
//                               ADVANCED USAGE
//
// Additional intermediate snapshots can be recorded at a client's request.
// These snapshots can also be immediately logged, with both the absolute and
// delta metrics printed.
//
// A final chronological report of the resource usage can also be generated and
// logged. The report contains each tracked metric's snapshot history over this
// RUsageProfiler object's lifetime. Each snapshot is annotated with the source
// location and time. The metric values are printed in the numeric and
// pseudo-graphical form (as a progress-like bar representing the value relative
// to its overall observed range).
//
//                             TIMELAPSE PROFILING
//
// RUsageProfiler also supports a limited timelapse mode. In contrast to
// traditional sampling profilers, it simply takes snapshots of resource usage
// at regular intervals, but doesn't collect per-function call usage stats.
//
// This lends it particularly useful for detection of problematic resource usage
// patterns in a blackbox or near-blackbox external API calls or modules,
// such as third-party libs, or measuring the effect of differently tuned
// parameter configurations on the performance profile of complicated,
// multi-function, multi-class, or multi-module pieces of logic.
//
//                              MULTI-THREADING
//
// RUsageProfiler is thread-safe.
//
// Keep in mind that this is a process-scoped profiler, not thread-scoped: it
// records and reports the current _process's_ timing and resource usage, not
// the current _thread's_. This means that in a multi-threaded context, the
// numbers recorded by each snapshot will reflect the timing and memory used up
// by _all_ the threads of the process. For example, if a thread pool executes
// the same profiled function in N threads, the stats reported by the
// function-level profiler may be erratic and not very indicative of the
// function's true performance.
//
//                          EXAMPLE USAGE - DIRECT
//
//  void foo() {
//    // Logs the initial snapshot:
//    RUsageProfiler profiler{kAllMetrics, ABSL_LOC, __func__};
//    ...
//    profiler.TakeSnapshot(ABSL_LOC);  // Takes another snapshot
//    ...
//    profiler.TakeSnapshot(ABSL_LOC).Log();  // Records and logs a snapshot
//    ...
//    VLOG(1) << profiler.TakeSnapshot(ABSL_LOC);  // A different way to log
//    ...
//  }  // Dtor logs a final snapshot
//
//                           EXAMPLE USAGE - MACROS
//
//  void foo() {
//    RPROF_THIS_FUNCTION(VLOG_IS_ON(2));  // Profile the function @ --v>=2
//    ...
//    RPROF_SNAPSHOT_AND_LOG();  // Record and log a function-level snapshot
//    for (...) {
//        RPROF_THIS_SCOPE(VLOG_IS_ON(3));  // Profile loop iterations @ --v>=3
//        ...
//    }
//    RPROF_SNAPSHOT();  // Record (not log) another function-level snapshot
//  }  // Dtor logs a final snapshot and a chronological report
//
//                              EXAMPLE SNAPSHOTS
//
// clang-format off
//  I1105 16:52:20.831313  932765 foo.cc:79] PROFILER [P.1 DoSomethingFn()] SNAPSHOT [S.0 INITIAL]:  // NOLINT
//    [P.1:S.0] TIMING   | Wall:          11us | User:           2us | Sys:            4us | CpuUtil:      8.11% | CpuCores:       0.6 |  // NOLINT
//    [P.1:S.0] MEMORY   | RSS:        119.79M | VSize:        2.04G | VPeak:        2.04G | Data:       152.45M | ShMem:       12.83M |  // NOLINT
//  I1105 16:52:38.130159  932926 foo.cc:119] PROFILER [P.2 Heartbeats] SNAPSHOT [S.1 Timelapse]:  // NOLINT
//    [P.6:S.4] TIMING   | Wall:         3.00s | User:          23ms | Sys:           71ms | CpuUtil:      0.00% | CpuCores:       0.0 |  // NOLINT
//    [P.6:S.4] MEMORY   | RSS:        146.48M | VSize:        2.05G | VPeak:        2.05G | Data:       298.46M | ShMem:       12.85M |  // NOLINT
//  I1105 16:52:23.880263  932765 foo.cc:82] PROFILER [P.1 DoSomethingFn()] SNAPSHOT [S.1 Scope 1 done]:  // NOLINT
//    [P.1:S.1] TIMING   | Wall:         3.05s | User:          16ms | Sys:           33ms | CpuUtil:      0.00% | CpuCores:       0.0 |  // NOLINT
//    [P.1:S.1] TIMING Δ | Wall:        +3.05s | User:         +16ms | Sys:          +33ms | CpuUtil:     -8.11% | CpuCores:      -0.5 |  // NOLINT
//    [P.1:S.1] MEMORY   | RSS:        167.75M | VSize:        2.04G | VPeak:        2.04G | Data:       200.58M | ShMem:       12.83M |  // NOLINT
//  I1105 16:52:26.913993  932765 foo.cc:89] PROFILER [P.1 DoSomethingFn()] SNAPSHOT [S.3 Loop iteration 0 done]:  // NOLINT
//    [P.1:S.3] TIMING   | Wall:         6.08s | User:          37ms | Sys:           46ms | CpuUtil:      0.00% | CpuCores:       0.0 |  // NOLINT
//    [P.1:S.3] MEMORY   | RSS:        148.27M | VSize:        2.04G | VPeak:        2.04G | Data:       200.70M | ShMem:       12.83M |  // NOLINT
//  I1105 16:52:43.133988  932926 foo.cc:119] PROFILER [P.2 Heartbeats] SNAPSHOT [S.2 Timelapse]:  // NOLINT
//    [P.6:S.9] TIMING   | Wall:         8.01s | User:          42ms | Sys:          103ms | CpuUtil:     18.95% | CpuCores:       0.0 |  // NOLINT
//    [P.6:S.9] TIMING Δ | Wall:        +1.00s | User:          +1ms | Sys:          +10ms | CpuUtil:     -0.49% | CpuCores:      -0.0 |  // NOLINT
//    [P.6:S.9] MEMORY   | RSS:        158.96M | VSize:        2.05G | VPeak:        2.05G | Data:       298.71M | ShMem:       12.85M |  // NOLINT
//  I1105 16:52:28.962669  932765 foo.cc:79] PROFILER [P.1 DoSomethingFn()] SNAPSHOT [S.6 FINAL]:  // NOLINT
//    [P.1:S.6] TIMING   | Wall:         8.13s | User:          54ms | Sys:           75ms | CpuUtil:     22.56% | CpuCores:       0.0 |  // NOLINT
//    [P.1:S.6] TIMING Δ | Wall:         +17ms | User:          +9ms | Sys:           +6ms | CpuUtil:    +22.56% | CpuCores:      +0.0 |  // NOLINT
//    [P.1:S.6] MEMORY   | RSS:        145.92M | VSize:        2.04G | VPeak:        2.04G | Data:       211.08M | ShMem:       12.83M |  // NOLINT
//    ...
// clang-format off
//
//                       EXAMPLE FINAL REPORT (TRUNCATED)
//
// clang-format off
//
//  I1105 16:52:28.963056  932765 foo.cc:79] PROFILER [P.1 WasteTimeAndGobbleBytes()] FINAL REPORT:  // NOLINT
//
//  === TIMING [P.1 DoSomethingFn()] ===
//
//  WALL TIME:
//    foo.cc:79 @ 16:52:20.83 [P.1:S.0 INITIAL         ]       11us [--------------------------------------------------]  // NOLINT
//    foo.cc:82 @ 16:52:23.88 [P.1:S.1 Scope 1         ]      3.05s [##################--------------------------------]  // NOLINT
//    foo.cc:86 @ 16:52:25.90 [P.1:S.2 Scope 2         ]      5.07s [###############################-------------------]  // NOLINT
//    foo.cc:89 @ 16:52:26.91 [P.1:S.3 Loop iteration 0]      6.08s [#####################################-------------]  // NOLINT
//    foo.cc:89 @ 16:52:27.92 [P.1:S.4 Loop iteration 1]      7.10s [###########################################-------]  // NOLINT
//    foo.cc:79 @ 16:52:28.96 [P.1:S.6 FINAL           ]      8.13s [##################################################]  // NOLINT
//  ... Same stats for other timing metrics.
//
//  === Δ TIMING [P.1 DoSomethingFn()] ===
//
//  Δ WALL TIME:
//    foo.cc:79 @ 16:52:20.83 [P.1:S.0 INITIAL         ]       +0ns [--------------------------------------------------]  // NOLINT
//    foo.cc:82 @ 16:52:23.88 [P.1:S.1 Scope 1         ]     +3.05s [##################################################]  // NOLINT
//    foo.cc:86 @ 16:52:25.90 [P.1:S.2 Scope 2         ]     +2.03s [#################################-----------------]  // NOLINT
//    foo.cc:89 @ 16:52:26.91 [P.1:S.3 Loop iteration 0]     +1.01s [################----------------------------------]  // NOLINT
//    foo.cc:89 @ 16:52:27.92 [P.1:S.4 Loop iteration 1]     +1.01s [################----------------------------------]  // NOLINT
//    foo.cc:79 @ 16:52:28.96 [P.1:S.6 FINAL           ]      +17ms [--------------------------------------------------]  // NOLINT
//  ... Same stats for other timing metrics.
//
//  === MEMORY USAGE [P.1 DoSomethingFn()] ===
//
//  RESIDENT SET SIZE:
//    foo.cc:79 @ 16:52:20.83 [P.1:S.0 INITIAL         ]    119.79M [--------------------------------------------------]  // NOLINT
//    foo.cc:82 @ 16:52:23.88 [P.1:S.1 Scope 1         ]    167.75M [##################################################]  // NOLINT
//    foo.cc:86 @ 16:52:25.90 [P.1:S.2 Scope 2         ]    139.66M [####################------------------------------]  // NOLINT
//    foo.cc:89 @ 16:52:26.91 [P.1:S.3 Loop iteration 0]    148.27M [#############################---------------------]  // NOLINT
//    foo.cc:89 @ 16:52:27.92 [P.1:S.4 Loop iteration 1]    156.07M [#####################################-------------]  // NOLINT
//    foo.cc:79 @ 16:52:28.96 [P.1:S.6 FINAL           ]    145.92M [###########################-----------------------]  // NOLINT
//  ... Same stats for other memory types.
//
//  === Δ MEMORY USAGE [P.1 DoSomethingFn()] ===
//
//  Δ RESIDENT SET SIZE:
//    foo.cc:79 @ 16:52:20.83 [P.1:S.0 INITIAL         ]        +0B [------------------|--------------------------------]  // NOLINT
//    foo.cc:82 @ 16:52:23.88 [P.1:S.1 Scope 1         ]    +47.95M [------------------|################################]  // NOLINT
//    foo.cc:86 @ 16:52:25.90 [P.1:S.2 Scope 2         ]    -28.09M [##################|--------------------------------]  // NOLINT
//    foo.cc:89 @ 16:52:26.91 [P.1:S.3 Loop iteration 0]     +8.62M [------------------|######--------------------------]  // NOLINT
//    foo.cc:89 @ 16:52:27.92 [P.1:S.4 Loop iteration 1]     +7.80M [------------------|#####---------------------------]  // NOLINT
//    foo.cc:79 @ 16:52:28.96 [P.1:S.6 FINAL           ]    -19.88M [-----#############|--------------------------------]  // NOLINT
//  ... Same stats for other memory types.
//
// clang-format on
//------------------------------------------------------------------------------

#ifndef THIRD_PARTY_CENTIPEDE_RUSAGE_PROFILER_H_
#define THIRD_PARTY_CENTIPEDE_RUSAGE_PROFILER_H_

#include <atomic>
#include <cstdint>
#include <deque>
#include <iosfwd>
#include <memory>
#include <ostream>
#include <string>
#include <string_view>

#include "absl/base/thread_annotations.h"
#include "absl/strings/str_cat.h"  // IWYU pragma: keep
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "./centipede/rusage_stats.h"

namespace centipede::perf {

// A simple source location wrapper. Typically, construct as
// `SourceLocation{__FILE__, __LINE__}` and pass around by-value.
// TODO(ussuri): Switch to absl::SourceLocation or std::source_location.
struct SourceLocation {
  explicit SourceLocation() = default;
  SourceLocation(const char* file, int line) : file{file}, line{line} {}

  const char* const file = "<unknown>";
  const int line = 0;
};

class RUsageProfiler {
 public:
  //----------------------------------------------------------------------------
  //                                 Types

  // A profiling snapshot.
  struct Snapshot {
    // Returns this snapshot's source location.
    std::string WhereStr() const;
    // Same as above, but shortens the file path to the basename.
    std::string ShortWhereStr() const;

    // Returns this snapshot's recording date/time in local timezone.
    std::string WhenStr() const;
    // Same as above, but omits the date.
    std::string ShortWhenStr() const;

    // Returns this snapshot's formatted metrics. The formatting is consistent
    // across snapshots, so if printed in a loop, these will form a table.
    std::string FormattedMetricsStr() const;
    // Same as above, but the metrics are printed in one line without the
    // table-like formatting.
    std::string ShortMetricsStr() const;

    // Logs this snapshot to LOG(INFO). The source location that annotates the
    // log message is set to `location` instead of the actual call's location.
    // Returns *this so clients can do either of
    //   Snapshot s = profiler.TakeSnapshot();
    //   Snapshot s = profiler.TakeSnapshot().Log();
    const Snapshot& Log() const;

    // Writes a short version of this snapshot to an ostream.
    friend std::ostream& operator<<(std::ostream&, const Snapshot&);

    // Metadata.
    const int64_t id = -1;
    const std::string title;
    const SourceLocation location{};
    const absl::Time time;

    // The parent profiler's data.
    const int profiler_id = -1;
    const std::string profiler_desc;

    // Recorded metrics.
    const RUsageTiming timing = RUsageTiming::Zero();
    const RUsageTiming delta_timing = RUsageTiming::Zero();
    const RUsageMemory memory = RUsageMemory::Zero();
    const RUsageMemory delta_memory = RUsageMemory::Zero();
  };

  // An abstract interface used to stream in a profiling report in
  // GenerateReport(). Also used inside PrintReport() to overcome the LOG()'s
  // limitation on the size of a single printed message.
  class ReportSink {
   public:
    virtual ~ReportSink() = default;
    virtual ReportSink& operator<<(std::string_view fragment) = 0;
  };

  //----------------------------------------------------------------------------
  //                                 APIs

  // Which metric categories to track and report.
  enum Metrics : unsigned {
    kMetricsOff = 0,
    kSnapTiming = 1,   // Timing at the time of snapshot
    kDeltaTiming = 2,  // Delta timing from the previous snapshot
    kTiming = kSnapTiming | kDeltaTiming,
    kSnapMemory = 4,   // Memory at the time of snapshot
    kDeltaMemory = 8,  // Delta memory from the previous snapshot
    kMemory = kSnapMemory | kDeltaMemory,
    kAllMetrics = kTiming | kMemory,
  };
  using MetricsMask = decltype(kMetricsOff | kMetricsOff);

  // Automatic logging enabled via RAII.
  enum RaiiActions : unsigned {
    kRaiiOff = 0,
    kCtorSnapshot = 1,
    kDtorSnapshot = 2,
    kDtorReport = 4,
    kFinalReport = kDtorReport,
    kRaiiSnapshots = kCtorSnapshot | kDtorSnapshot,
    kAllRaii = kRaiiSnapshots | kFinalReport
  };
  using RaiiActionsMask = decltype(kRaiiOff | kRaiiOff);

  // Initializes this profiler and possibly takes an initial snapshot if
  // raii_actions & kCtorSnapshot != 0. SourceLocation `location` parameter is
  // used to annotate this profiler's log messages with the source location of
  // the caller, as if the caller printed them. That makes it easy to attribute
  // the logged resource usage to the actual user rather than RUsageProfiler.
  RUsageProfiler(                     //
      RUsageScope scope,              // Which process/thread to monitor
      MetricsMask metrics,            // Which metrics to track
      RaiiActionsMask raii_actions,   // Which RAII logs to enable
      SourceLocation location,        // Pass SourceLocation{__FILE__, __LINE__}
      std::string description = "");  // Annotate logs in addition to ID

  // This version turns on all RAII logging and immediately initiates timelapse
  // snapshots at the specified interval, unless the interval is
  // absl::ZeroDuration or absl::InfiniteDuration.
  //
  // Dtor will stop taking snapshots and print a chronological report.
  // Snapshotting can also be manually stopped at any time using
  // StopTimelapse().
  //
  // As with manually started timelapse snapshotting (via StartTimelapse()),
  // the client can still request explicit snapshots at any time, interleaved
  // with timelapse ones.
  RUsageProfiler(                         //
      RUsageScope scope,                  // Which process/thread to monitor
      MetricsMask metrics,                // Which metrics to track
      absl::Duration timelapse_interval,  // Take timelapse snapshots this often
      bool also_log_timelapses,           // Log timelapse snapshots as taken
      SourceLocation location,            // SourceLocation{__FILE__, __LINE__}
      std::string description = "");      // Annotate logs in addition to ID

  // Logs the final report as returned by GenerateReport().
  ~RUsageProfiler();

  // Records and returns a snapshot of the current metrics. The snapshot's
  // source location is set to `location`, so its Log() will print a log message
  // as if it were emitted by the `location` source line. As such, the rule of
  // thumb should be to pass `SourceLocation{__FILE__, __LINE__}`. The returned
  // reference remains valid until RUsageProfiler is destroyed.
  const Snapshot& TakeSnapshot(SourceLocation loc, std::string title = "");

  // Starts taking and optionally also logging periodic snapshots at a given
  // interval in a separate thread.
  //
  // Convenient for measuring sample-based resource usage of a black-box
  // external API (e.g. third-party) or a complex bit of logic spanning multiple
  // functions/classes/modules in order to either detect problematic usage
  // patterns or the effect of different parameter configurations on the overall
  // performance. GenerateReport() or PrintReport() are particularly well-suited
  // for viewing the results of timelapse measurements in graphical form.
  //
  // The client is free to continue taking explicit snapshots at any time,
  // interleaved with timelapse ones.
  void StartTimelapse(          //
      SourceLocation loc,       //
      absl::Duration interval,  //
      bool also_log = false,    //
      std::string title = "");

  // Stops taking timelapse snapshots previously initiated by StartTimelapse().
  void StopTimelapse();

  // Returns a vector of manual and timelapse snapshots recorded so far.
  const std::deque<Snapshot>& GetSnapshots() const { return snapshots_; }

  // Prints to `sink` a report consisting of chronological charts for each of
  // the tracked metrics recorded since this profiler's construction up to this
  // point.
  void GenerateReport(ReportSink* report_sink) const;

  // Logs the report returned by GenerateReport(). The log message's source
  // location is set to `location`: as a rule of thumb, pass
  // `SourceLocation{__FILE__, __LINE__}` -- the explanation before
  // TakeSnapshot() does apply here.
  void PrintReport(SourceLocation loc, const std::string& title = "");

 private:
  friend class RUsageProfilerTest_ValidateManualSnapshots_Test;

  //----------------------------------------------------------------------------
  //                                  Data

  // Global instance counter.
  static std::atomic<int> next_id_;

  // Scope (the current process or the current thread).
  const RUsageScope scope_;
  // Metrics and report flavors to keep track of and print.
  const Metrics metrics_;
  // Enabled RAII actions.
  const RaiiActions raii_actions_;
  // The source location where this profiler got created, as recorded by ctor.
  const SourceLocation ctor_loc_;
  // The descriptive name of this profiler provided by the client. Used to
  // annotate verbose log messages.
  const std::string description_;
  // The sequential ID of this profiler. Used to annotate all log
  const int id_;

  // Mutex for the mutable data further below.
  mutable absl::Mutex mutex_;

  // Chronological snapshots. Using std::deque gives a better-than-vector
  // average insertion speed, preserves iterators across insertions, and strikes
  // a balance between vector's and list's additional storage.
  std::deque<Snapshot> snapshots_ ABSL_GUARDED_BY(mutex_);
  // A temporarily lived thread that records and optionally logs timelapse
  // snapshots. (Re)created by each new call to StartTimelapse() and terminated
  // by StopTimelapse() or the dtor, whichever comes first.
  class TimelapseThread;  // Defined in the .cc.
  std::unique_ptr<TimelapseThread> timelapse_thread_ ABSL_GUARDED_BY(mutex_);

  // An auto-starting timer passed to RUsageTiming::Snapshot() in order to track
  // this RUsageProfiler object's lifetime stats rather than the process's
  // lifetime stats, which is the default.
  ProcessTimer timer_;
};

}  // namespace centipede::perf

//------------------------------------------------------------------------------
//               Convenience macros for easy use of RUsageProfiler
//------------------------------------------------------------------------------

// TODO(ussuri): The macros all use RUsageScope::ThisProcess(). Parameterize.

#define RPROF_NAME(prefix, line) RPROF_NAME_CONCAT(prefix, line)
#define RPROF_NAME_CONCAT(prefix, line) prefix##line
#define FUNCTION_LEVEL_RPROF_NAME RPROF_NAME(rprof_, 0)
#define SCOPE_LEVEL_RPROF_NAME RPROF_NAME(rprof_, __LINE__)

// Profile the timing and resource usage of the current function, with an option
// to take additional intermediate snapshots via RPROF_SNAPSHOT* later in the
// function.
//
// The intended canonical place to call this macro is right after the function's
// open brace or precondition checks: with just that, the entire function's
// system timing and resource usage will be logged upon return. Only one such
// macro call is allowed per function.
// clang-format off
#define RPROF_THIS_FUNCTION(enable)                                        \
  centipede::perf::RUsageProfiler FUNCTION_LEVEL_RPROF_NAME = {            \
      /*scope=*/centipede::perf::RUsageScope::ThisProcess(),               \
      /*metrics=*/(enable) ? centipede::perf::RUsageProfiler::kAllMetrics  \
                           : centipede::perf::RUsageProfiler::kMetricsOff, \
      /*raii_actions=*/centipede::perf::RUsageProfiler::kRaiiSnapshots,    \
      /*location=*/{__FILE__, __LINE__},                                   \
      /*description=*/absl::StrCat(__func__, "()")                         \
  }
// clang-format on

// Same as RPROF_THIS_FUNCTION, but with a full report printed at return from
// the function.
// clang-format off
#define RPROF_THIS_FUNCTION_WITH_REPORT(enable)                            \
  centipede::perf::RUsageProfiler FUNCTION_LEVEL_RPROF_NAME = {            \
      /*scope=*/centipede::perf::RUsageScope::ThisProcess(),               \
      /*metrics=*/(enable) ? centipede::perf::RUsageProfiler::kAllMetrics  \
                           : centipede::perf::RUsageProfiler::kMetricsOff, \
      /*raii_actions=*/centipede::perf::RUsageProfiler::kAllRaii,          \
      /*location=*/{__FILE__, __LINE__},                                   \
      /*description=*/absl::StrCat(__func__, "()")                         \
  }
// clang-format on

// Same as RPROF_THIS_FUNCTION, but immediately initiates timelapse snapshots
// at the specified `interval` and prints a final report for them. Additional
// snapshots can still be taken with RPROF_SNAPSHOT*.
// clang-format off
#define RPROF_THIS_FUNCTION_WITH_TIMELAPSE(                                \
    enable, timelapse_interval, also_log_timelapses)                       \
  centipede::perf::RUsageProfiler FUNCTION_LEVEL_RPROF_NAME = {            \
      /*scope=*/centipede::perf::RUsageScope::ThisProcess(),               \
      /*metrics=*/(enable) ? centipede::perf::RUsageProfiler::kAllMetrics  \
                           : centipede::perf::RUsageProfiler::kMetricsOff, \
      /*timelapse_interval=*/timelapse_interval,                           \
      /*also_log_timelapses=*/also_log_timelapses,                         \
      /*location=*/{__FILE__, __LINE__},                                   \
      /*description=*/absl::StrCat(__func__, "()")                         \
  }
// clang-format on

// Sets an existing RUsageProfiler as this function's profiler such that it can
// be used with `RPROF_SNAPSHOT` and other similar macros below, which normally
// work with the other `RPROF_THIS_FUNCTION.*` macros.
// clang-format off
#define RPROF_THIS_FUNCTION_BY_EXISTING_RPROF(profiler)                 \
  ::centipede::perf::RUsageProfiler& FUNCTION_LEVEL_RPROF_NAME = profiler;
// clang-format on

// Records and returns an intermediate snapshot using the profiler defined by an
// earlier RPROF_THIS_FUNCTION in the same function. An optional snapshot
// title can be passed as a macro argument.
// NOTE: Here and below, the '##' in front of __VA_ARGS__ eats up the preceding
// comma in case __VA_ARGS__ is empty, thus avoiding a malformed expression.
// clang-format off
#define RPROF_SNAPSHOT(...) \
  FUNCTION_LEVEL_RPROF_NAME.TakeSnapshot( \
      {__FILE__, __LINE__}, ##__VA_ARGS__)
// clang-format on

// Records AND logs an intermediate snapshot using the profiler defined by an
// earlier RPROF_THIS_FUNCTION() in the same function. An optional snapshot
// title can be passed as a macro argument.
// clang-format off
#define RPROF_SNAPSHOT_AND_LOG(...) \
  FUNCTION_LEVEL_RPROF_NAME.TakeSnapshot( \
      {__FILE__, __LINE__}, ##__VA_ARGS__).Log()
// clang-format on

// Starts taking periodic snapshots using the function-level snapshot created by
// an earlier RPROF_THIS_FUNCTION*(). `interval` is an absl::Duration.
// `also_log` will also log the snapshots. An optional snapshot title can be
// passed as the last macro argument.
// clang-format off
#define RPROF_START_TIMELAPSE(interval, also_log, ...) \
  FUNCTION_LEVEL_RPROF_NAME.StartTimelapse( \
      {__FILE__, __LINE__}, interval, also_log, ##__VA_ARGS__)
// clang-format on

#define RPROF_STOP_TIMELAPSE() FUNCTION_LEVEL_RPROF_NAME.StopTimelapse()

// Prints a final report to the log using the profiler defined by an earlier
// RPROF_THIS_FUNCTION in the same function. An optional report title can be
// passed as a macro argument.
// clang-format off
#define RPROF_DUMP_REPORT_TO_LOG(...) \
  FUNCTION_LEVEL_RPROF_NAME.PrintReport({__FILE__, __LINE__}, ##__VA_ARGS__)
// clang-format on

// Profiles a given scope: a snapshot and a delta of the system timing and
// resource usage are logged at the call site and at the scope exit.
//
// Unlike, RPROF_THIS_FUNCTION, RPROF_THIS_SCOPE can be called any number of
// times per scope, provided the calls are on different lines. That includes
// nested scopes.
//
// Also unlike RPROF_THIS_FUNCTION, RPROF_THIS_SCOPE lacks a complimentary
// macro for intermediate snapshotting using the same profiler: this macro is
// intended as a simple, fast way to profile a scope; for anything more
// involved, use RPROF_THIS_FUNCTION() or RUsageProfiler directly.
// clang-format off
#define RPROF_THIS_SCOPE(enable, description)                              \
  centipede::perf::RUsageProfiler SCOPE_LEVEL_RPROF_NAME = {               \
      /*scope=*/centipede::perf::RUsageScope::ThisProcess(),               \
      /*metrics=*/(enable) ? centipede::perf::RUsageProfiler::kAllMetrics  \
                           : centipede::perf::RUsageProfiler::kMetricsOff, \
      /*raii_actions=*/centipede::perf::RUsageProfiler::kRaiiSnapshots,    \
      /*location=*/{__FILE__, __LINE__},                                   \
      /*description=*/description                                          \
  }
// clang-format on

// clang-format off
#define RPROF_THIS_SCOPE_WITH_TIMELAPSE(                                   \
    enable, timelapse_interval, also_log_timelapses, description)          \
  centipede::perf::RUsageProfiler SCOPE_LEVEL_RPROF_NAME = {               \
      /*scope=*/centipede::perf::RUsageScope::ThisProcess(),               \
      /*metrics=*/(enable) ? centipede::perf::RUsageProfiler::kAllMetrics  \
                           : centipede::perf::RUsageProfiler::kMetricsOff, \
      /*timelapse_interval=*/timelapse_interval,                           \
      /*also_log_timelapses=*/also_log_timelapses,                         \
      /*location=*/{__FILE__, __LINE__},                                   \
      /*description=*/description                                          \
  }
// clang-format on

#endif  // THIRD_PARTY_CENTIPEDE_RUSAGE_PROFILER_H_
