Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

141 lines
4.6 KiB
C++

// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
#pragma once
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <hilti/rt/configuration.h>
#include <hilti/rt/global-state.h>
#include <hilti/rt/profiler-state.h>
namespace hilti::rt {
class Profiler;
namespace profiler {
std::optional<Profiler> start(std::string_view name, std::optional<uint64_t> volume = std::nullopt);
void stop(std::optional<Profiler>& p, std::optional<uint64_t> volume = std::nullopt);
namespace detail {
// Internal initialization function, called from library's `init()` when
// profiling has been requested.
extern void init();
// Internal shutdown function, called from library's `done()`. Produces a final
// profiling report.
extern void done();
} // namespace detail
} // namespace profiler
/**
* Class representing one block of code to profile. The constructor records a
* first measurement, and the destructor records a second. The delta between
* the two measurements is then added to a global total kept for respective
* block of code. Blocks are identified through descriptive names, which will
* be shows as part of the final report.
*
* Profilers can't be instantiated directly; use the `start()` and `stop()` API instead.
*/
class Profiler {
public:
/**
* Default constructor instantiating a no-op profiler that's not actively
* recording any measurement. The only purpose of constructor is allowing
* to pre-allocate a local profiler variable that a real profiler can later be move into.
*/
Profiler() = default;
Profiler(const Profiler& other) = delete;
Profiler(Profiler&& other) = default;
/** Destructor concluding any pending measurement. */
~Profiler() { record(snapshot()); }
Profiler& operator=(const Profiler& other) = delete;
Profiler& operator=(Profiler&& other) = default;
/** Take final measurement and record the delta between first and final. */
void record(const profiler::Measurement& end);
/** Returns true if the profiler is currently taking an active measurement. */
operator bool() const { return ! _name.empty(); }
/**
* Take and return a single measurement.
*
* @param volume optional current absolute volume to record with the measurement
*/
static profiler::Measurement snapshot(std::optional<uint64_t> volume = std::nullopt);
protected:
/**
* Constructor starting a new measurement. Don't call directly, use
* `profiler::start()` instead.
*
* @param name descriptive, unique name of the block of code to profile
* @param volume optional initial absolute volume to record with the measurement
*/
Profiler(std::string_view name, std::optional<uint64_t> volume) : _name(name), _start(snapshot(volume)) {
_register();
}
private:
friend std::optional<Profiler> profiler::start(std::string_view name, std::optional<uint64_t> volume);
friend void profiler::detail::done();
void _register() const;
std::string _name; // Name of block to profile; empty is not active.
profiler::Measurement _start; // Initial measurement at construction time.
};
namespace profiler {
/**
* Start profiling of a code block. The returned profiler will be recording
* until either `profiler::stop()` is called with it, or until the profiler
* instances goes out of scope, whatever comes first.
*
* @param name descriptive, unique name of the block of code to profile.
* @param volume optional initial absolute volume to record with the measurement
* @return profiler instance representing the active measurement
*/
inline std::optional<Profiler> start(std::string_view name, std::optional<uint64_t> volume) {
if ( ::hilti::rt::detail::unsafeGlobalState()->profiling_enabled )
return Profiler(name, volume);
else
return {};
}
/**
* Stops profiling a block of code, recording the delta between now and when it
* was started.
*
* @param p profiler instance to stop
* @param volume optional absolute volume to record with the final measurement
*/
inline void stop(std::optional<Profiler>& p, std::optional<uint64_t> volume) {
if ( p )
p->record(Profiler::snapshot(volume));
}
/**
* Retrieves the measurement state for a code block by name, if known.
* This is primarily for testing purposes.
*
* @param name of the block of code to return data for
* @return measurement state, or unset if no data is available
*/
std::optional<Measurement> get(const std::string& name);
/** Produce end-of-process summary profiling report. */
extern void report();
} // namespace profiler
} // namespace hilti::rt