Crafter.Debug/implementations/Crafter.Debug-Timeline.cpp

131 lines
4.5 KiB
C++
Raw Normal View History

2026-11-01 01:40:59 +01:00
// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
module Crafter.Debug:Timeline_impl;
import std;
import Crafter.Debug.Core;
import :Registry;
import :Timeline;
using namespace Crafter::Debug;
using namespace Crafter::Debug::Detail;
namespace {
constexpr std::size_t capacity = 8192;
bool Stored(std::uint64_t failures) {
return failures <= 10 || std::has_single_bit(failures);
}
}
std::string_view Crafter::Debug::EntryKindName(EntryKind kind) {
switch (kind) {
case EntryKind::Log: return "log";
case EntryKind::Warn: return "warn";
case EntryKind::Error: return "error";
case EntryKind::Check: return "check";
case EntryKind::Event: return "event";
case EntryKind::Agent: return "agent";
case EntryKind::Pause: return "pause";
case EntryKind::Stdio: return "stdio";
}
return "unknown";
}
std::uint8_t Crafter::Debug::Detail::Level(EntryKind kind) {
switch (kind) {
case EntryKind::Warn: return 1;
case EntryKind::Error:
case EntryKind::Check: return 2;
default: return 0;
}
}
TimelineEntry Crafter::Debug::Detail::MakeEntry(EntryKind kind, std::string_view category, std::string message, const std::source_location& location, Value payload) {
TimelineEntry entry;
entry.kind = kind;
entry.category = std::string(category);
entry.message = std::move(message);
entry.file = location.file_name();
entry.line = location.line();
entry.payload = std::move(payload);
return entry;
}
Timeline& Timeline::Instance() {
static Timeline timeline;
return timeline;
}
std::uint64_t Timeline::Append(TimelineEntry entry) {
entry.frame = CurrentFrame();
{
std::lock_guard lock(mutex);
entry.seconds = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
entry.sequence = nextSequence++;
entries.push_back(entry);
if (entries.size() > capacity) entries.pop_front();
}
Notify(entry);
return entry.sequence;
}
void Timeline::RecordCheck(std::string_view category, std::string message, const std::source_location& location) {
TimelineEntry entry = MakeEntry(EntryKind::Check, category, std::move(message), location);
entry.frame = CurrentFrame();
{
std::lock_guard lock(mutex);
entry.seconds = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
SiteCount& site = checks[{ entry.file, entry.line }];
site.category = entry.category;
entry.count = ++site.failures;
if (Stored(entry.count)) {
entry.sequence = nextSequence++;
entries.push_back(entry);
if (entries.size() > capacity) entries.pop_front();
}
}
Notify(entry);
}
std::vector<TimelineEntry> Timeline::Query(const TimelineQuery& query) const {
std::vector<TimelineEntry> matched;
std::lock_guard lock(mutex);
for (const TimelineEntry& entry : entries) {
if (entry.sequence <= query.afterSequence) continue;
if (query.sinceFrame && entry.frame < *query.sinceFrame) continue;
if (Level(entry.kind) < query.minLevel) continue;
if (!query.kinds.empty() && std::ranges::find(query.kinds, entry.kind) == query.kinds.end()) continue;
if (!query.category.empty() && !GlobMatch(query.category, entry.category)) continue;
if (!query.grep.empty() && entry.message.find(query.grep) == std::string::npos && entry.category.find(query.grep) == std::string::npos) continue;
matched.push_back(entry);
}
if (query.last != 0 && matched.size() > query.last) matched.erase(matched.begin(), matched.end() - static_cast<std::ptrdiff_t>(query.last));
return matched;
}
std::uint64_t Timeline::LastSequence() const {
std::lock_guard lock(mutex);
return nextSequence - 1;
}
std::vector<CheckSite> Timeline::CheckSites() const {
std::lock_guard lock(mutex);
std::vector<CheckSite> sites;
for (const auto& [site, count] : checks) sites.push_back({ site.first, site.second, count.category, count.failures });
return sites;
}
void Timeline::SetListener(std::function<void(const TimelineEntry&)> callback) {
std::lock_guard lock(mutex);
listener = callback ? std::make_shared<const std::function<void(const TimelineEntry&)>>(std::move(callback)) : nullptr;
}
void Timeline::Notify(const TimelineEntry& entry) const {
std::shared_ptr<const std::function<void(const TimelineEntry&)>> current;
{
std::lock_guard lock(mutex);
current = listener;
}
if (current) (*current)(entry);
}