Files
ReSkate/Engine/Core/Debug/trace.h
T
AyeZeeBB 4ac1343b95 Crash reports: self-hosted Sentry instead of Backtrace, and opt-in performance timings
Crash dumps now go to the project's own Sentry server. The launcher's
out-of-process helper is unchanged; only where it posts and what it expects
back are:

- The default endpoint is the Sentry project's minidump URL. Report
  attributes travel as Sentry tags, with the release as reskate@<version>.
- An upload counts as accepted when the server answers with an event ID.
- "backtrace" is "crash_report" throughout: the namespace, files, CMake
  targets, DINGOSDK_CRASH_REPORT_URL, DINGOSDK_BUILD_CRASH_REPORT_TESTS and
  the RESKATE_CRASH_REPORT_URL override.

Performance timings are new, and off unless "Send performance timings" is
ticked in the launcher's settings (RESKATE_TRACING=1). A small tracer
(Engine/Core/Debug/trace.h) posts each finished step to Sentry as a
transaction from a background thread; the launcher and the game it starts
share one trace. Timed: the update check, the Steam download, Thunderstore
installs, the mod merge, starting the game, the launch up to the first
playable level, each level load, joining a session, fetching the identity
lists, and the time spent in each level with its average frame rate and
long frames. Turning crash reports off turns timings off too.
2026-10-12 23:24:33 -05:00

66 lines
2.9 KiB
C++

#pragma once
#include <chrono>
#include <cstdint>
#include <exception>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace dingosdk::trace {
using Tags = std::vector<std::pair<std::string, std::string>>;
using Clock = std::chrono::system_clock;
// A number carried by a transaction, such as an average frame rate.
struct Measurement { std::string name; double value{}; std::string unit{"none"}; };
using Measurements = std::vector<Measurement>;
// Called once logging is up: true when this launch sends timings. Until then,
// and in programs that never call it, record() does nothing. Timings are off
// unless RESKATE_TRACING=1 (the launcher's "Send performance timings" setting),
// and off whenever crash reporting is (RESKATE_CRASH_REPORTING=0).
bool start() noexcept;
// Queues one finished operation as a Sentry transaction. Never waits on the
// network and never throws. A launch's launcher and game share one trace.
void record(std::string_view name, std::string_view operation, std::chrono::milliseconds duration,
bool ok = true, Tags tags = {}, Measurements measurements = {}) noexcept;
// Frame pacing between two takes: the game's frame hook feeds it, and the end
// of a level session takes it. A hitch is a frame of 50 ms or longer.
struct FramePacing { std::uint64_t frames{}, total_ns{}, hitches{}, worst_ns{}; };
void frame(std::uint64_t now_ns) noexcept;
FramePacing take_frame_pacing() noexcept;
// Gives queued operations a moment to reach the server before the process exits.
void flush(std::chrono::milliseconds timeout) noexcept;
// Times its own lifetime. An exception passing through marks the operation failed.
class Scope {
public:
Scope(std::string name, std::string operation)
: name_(std::move(name)), operation_(std::move(operation)) {}
~Scope() {
record(name_, operation_, std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - started_), ok_ && std::uncaught_exceptions() == exceptions_, std::move(tags_));
}
Scope(const Scope&) = delete;
Scope& operator=(const Scope&) = delete;
void tag(std::string key, std::string value) { tags_.emplace_back(std::move(key), std::move(value)); }
void fail() noexcept { ok_ = false; }
private:
std::string name_, operation_;
Tags tags_;
std::chrono::steady_clock::time_point started_{std::chrono::steady_clock::now()};
int exceptions_{std::uncaught_exceptions()};
bool ok_{true};
};
struct Transaction {
std::string name, operation, trace_id, release, process;
Clock::time_point started, finished;
bool ok{true};
Tags tags;
Measurements measurements;
};
// The Sentry envelope for one transaction: envelope header, item header, payload.
std::string envelope(const Transaction& transaction);
// The envelope endpoint beside a minidump endpoint, or empty when the URL is not one.
std::wstring envelope_url(std::wstring_view minidump_url);
}