blob: 18e3a2313b477c47cc8f76dcbaf02413c6fe42ff [file] [log] [blame]
// 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 <thread> // NOLINT
#include "absl/strings/str_cat.h"
#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.
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<<(const std::string& 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_JPROF(profiler) \
::centipede::perf::JitProfiler& FUNCTION_LEVEL_JPROF_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_