103 explicit TraceBuffer(std::span<TraceRecord> storage) noexcept
104 : storage_{storage} {}
112 TraceBuffer(
const TraceBuffer&) =
delete;
113 auto operator=(
const TraceBuffer&) -> TraceBuffer& =
delete;
114 TraceBuffer(TraceBuffer&&) =
delete;
115 auto operator=(TraceBuffer&&) -> TraceBuffer& =
delete;
122 void record(TraceCategory category, std::string_view label,
123 std::uint64_t value)
noexcept {
124 record_impl(category, label, value, TraceRecordKind::Event, 0, 0);
129 std::uint64_t nanos, std::uint64_t allocation_bytes = 0,
130 std::uint64_t deallocation_bytes = 0) noexcept {
131 record_timing(category, nanos);
132 record_impl(category, label, nanos, TraceRecordKind::Duration,
133 allocation_bytes, deallocation_bytes);
137 void record_impl(TraceCategory category, std::string_view label,
138 std::uint64_t value, TraceRecordKind kind,
139 std::uint64_t allocation_bytes,
140 std::uint64_t deallocation_bytes)
noexcept {
141 const auto seq = sequence_++;
142 if (storage_.empty() ||
143 static_cast<std::size_t
>(category) >= trace_category_count) {
147 if (count_ == storage_.size()) {
148 head_ = (head_ + 1) % storage_.size();
153 const auto slot = (head_ + count_ - 1) % storage_.size();
154 storage_[slot] = TraceRecord{
155 category, label, value, seq, kind, allocation_bytes,
164 void record_timing(TraceCategory category, std::uint64_t nanos)
noexcept {
165 const auto index =
static_cast<std::size_t
>(category);
166 if (index >= stats_.size()) {
169 auto& stats = stats_[index];
170 if (stats.samples == 0) {
171 stats.min_ns = nanos;
172 stats.max_ns = nanos;
174 if (nanos < stats.min_ns) {
175 stats.min_ns = nanos;
177 if (nanos > stats.max_ns) {
178 stats.max_ns = nanos;
182 stats.total_ns += nanos;
185 [[nodiscard]]
auto size() const noexcept -> std::
size_t {
return count_; }
187 [[nodiscard]]
auto capacity() const noexcept -> std::
size_t {
188 return storage_.size();
191 [[nodiscard]]
bool empty() const noexcept {
return count_ == 0; }
193 [[nodiscard]]
bool full() const noexcept {
return count_ == storage_.size(); }
196 [[nodiscard]]
auto dropped() const noexcept -> std::uint64_t {
202 [[nodiscard]]
auto operator[](std::size_t index)
const noexcept
203 ->
const TraceRecord& {
204 return storage_[(head_ + index) % storage_.size()];
209 [[nodiscard]]
auto stats(TraceCategory category)
const noexcept
210 ->
const TraceCategoryStats& {
211 static constexpr TraceCategoryStats kZero{};
212 const auto index =
static_cast<std::size_t
>(category);
213 if (index >= stats_.size()) {
216 return stats_[index];
221 [[nodiscard]]
auto all_stats() const noexcept
222 -> const std::array<TraceCategoryStats, trace_category_count>& {
226 void clear() noexcept {
231 for (
auto& stats : stats_) {
237 std::span<TraceRecord> storage_;
238 std::array<TraceCategoryStats, trace_category_count> stats_{};
239 std::size_t head_ = 0;
240 std::size_t count_ = 0;
241 std::uint64_t sequence_ = 0;
242 std::uint64_t dropped_ = 0;
292 ScopedTimer(TraceCategory category, std::string_view label) noexcept
293 : target_{active_trace_buffer},
294 allocation_target_{active_allocation_counters},
295 allocation_scope_id_{active_allocation_scope_id},
298 start_{target_ ==
nullptr ? std::chrono::steady_clock::time_point{}
299 : std::chrono::steady_clock::now()},
300 allocation_bytes_at_start_{allocation_target_ ==
nullptr
302 : allocation_target_->allocation_bytes},
303 deallocation_bytes_at_start_{
304 allocation_target_ ==
nullptr
306 : allocation_target_->deallocation_bytes} {}
308 ScopedTimer(
const ScopedTimer&) =
delete;
309 auto operator=(
const ScopedTimer&) -> ScopedTimer& =
delete;
312 if (target_ ==
nullptr) {
315 const auto elapsed = std::chrono::steady_clock::now() - start_;
317 std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
319 ticks < 0 ? std::uint64_t{0} :
static_cast<std::uint64_t
>(ticks);
320 const auto allocation_scope_is_active =
321 allocation_target_ !=
nullptr &&
322 allocation_target_ == active_allocation_counters &&
323 allocation_scope_id_ == active_allocation_scope_id;
324 const auto allocation_bytes =
325 !allocation_scope_is_active || allocation_target_->allocation_bytes <
326 allocation_bytes_at_start_
328 : allocation_target_->allocation_bytes - allocation_bytes_at_start_;
329 const auto deallocation_bytes =
330 !allocation_scope_is_active || allocation_target_->deallocation_bytes <
331 deallocation_bytes_at_start_
333 : allocation_target_->deallocation_bytes -
334 deallocation_bytes_at_start_;
335 target_->
record_span(category_, label_, nanos, allocation_bytes,
342 std::uint64_t allocation_scope_id_;
343 TraceCategory category_;
344 std::string_view label_;
345 std::chrono::steady_clock::time_point start_;
346 std::uint64_t allocation_bytes_at_start_;
347 std::uint64_t deallocation_bytes_at_start_;