92 explicit TraceBuffer(std::span<TraceRecord> storage) noexcept
93 : storage_{storage} {}
101 TraceBuffer(
const TraceBuffer&) =
delete;
102 auto operator=(
const TraceBuffer&) -> TraceBuffer& =
delete;
103 TraceBuffer(TraceBuffer&&) =
delete;
104 auto operator=(TraceBuffer&&) -> TraceBuffer& =
delete;
111 void record(TraceCategory category, std::string_view label,
112 std::uint64_t value)
noexcept {
113 const auto seq = sequence_++;
114 if (storage_.empty() ||
115 static_cast<std::size_t
>(category) >= trace_category_count) {
119 if (count_ == storage_.size()) {
120 head_ = (head_ + 1) % storage_.size();
125 const auto slot = (head_ + count_ - 1) % storage_.size();
126 storage_[slot] =
TraceRecord{category, label, value, seq};
133 void record_timing(TraceCategory category, std::uint64_t nanos)
noexcept {
134 const auto index =
static_cast<std::size_t
>(category);
135 if (index >= stats_.size()) {
138 auto& stats = stats_[index];
139 if (stats.samples == 0) {
140 stats.min_ns = nanos;
141 stats.max_ns = nanos;
143 if (nanos < stats.min_ns) {
144 stats.min_ns = nanos;
146 if (nanos > stats.max_ns) {
147 stats.max_ns = nanos;
151 stats.total_ns += nanos;
154 [[nodiscard]]
auto size()
const noexcept -> std::size_t {
return count_; }
156 [[nodiscard]]
auto capacity()
const noexcept -> std::size_t {
157 return storage_.size();
160 [[nodiscard]]
bool empty()
const noexcept {
return count_ == 0; }
162 [[nodiscard]]
bool full()
const noexcept {
return count_ == storage_.size(); }
165 [[nodiscard]]
auto dropped()
const noexcept -> std::uint64_t {
171 [[nodiscard]]
auto operator[](std::size_t index)
const noexcept
173 return storage_[(head_ + index) % storage_.size()];
178 [[nodiscard]]
auto stats(TraceCategory category)
const noexcept
181 const auto index =
static_cast<std::size_t
>(category);
182 if (index >= stats_.size()) {
185 return stats_[index];
190 [[nodiscard]]
auto category_stats()
const noexcept
191 ->
const std::array<TraceCategoryStats, trace_category_count>& {
195 void clear()
noexcept {
200 for (
auto& stats : stats_) {
206 std::span<TraceRecord> storage_;
207 std::array<TraceCategoryStats, trace_category_count> stats_{};
208 std::size_t head_ = 0;
209 std::size_t count_ = 0;
210 std::uint64_t sequence_ = 0;
211 std::uint64_t dropped_ = 0;
261 ScopedTimer(TraceCategory category, std::string_view label) noexcept
262 : target_{active_trace_buffer},
265 start_{std::chrono::steady_clock::now()} {}
267 ScopedTimer(
const ScopedTimer&) =
delete;
268 auto operator=(
const ScopedTimer&) -> ScopedTimer& =
delete;
271 if (target_ ==
nullptr) {
274 const auto elapsed = std::chrono::steady_clock::now() - start_;
276 std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
278 ticks < 0 ? std::uint64_t{0} :
static_cast<std::uint64_t
>(ticks);
279 target_->record_timing(category_, nanos);
280 target_->record(category_, label_, nanos);
285 TraceCategory category_;
286 std::string_view label_;
287 std::chrono::steady_clock::time_point start_;