Profiler Session Lifecycle & Zero-Code Bootstrap
Turn profiling on with a config file — no startup/shutdown code, and no lost trace on crash or exit.
Status: ✅ Implemented · Visibility: Public · Level: 🟢 Start Here · Category: Profiler
🎯 What it solves
A profiler needs someone to start its consumer thread, initialize exporters in the right order, and guarantee the trace is finalized even if the host forgets to tear it down — or crashes. Getting that sequencing right (CPU sampler before session metadata, exporters ready before the first batch, trace header patched exactly once) is boilerplate no application developer should have to write per host. Typhon eliminates it: dropping a config file next to the executable is enough to get a fully working trace, safely finalized on every exit path.
⚙️ How it works (in brief)
ProfilerBootstrap runs automatically at engine load and again inside TyphonRuntime.Create: it reads the
merged configuration, builds the exporters, starts the optional CPU sampler, composes session metadata from the
live runtime, and calls TyphonProfiler.Start. Teardown is wired to the engine storage's disposal so the trace
finalizes on ordinary shutdown; AppDomain.ProcessExit/UnhandledException handlers act as a backup for hosts
that skip disposal or crash. TyphonProfiler.Start/Stop are idempotent, so redundant calls from the bootstrap,
the safety net, and an explicit host call never conflict.
💻 Usage
// 1. Drop next to the executable (or set TYPHON__PROFILER__* env vars) — no code required:
// typhon.telemetry.json
// {
// "Typhon": { "Profiler": { "Enabled": true, "Trace": "session.typhon-trace" } }
// }
using Typhon.Engine;
// 2. Build the runtime as usual — profiling self-wires inside Create() because the config above is active.
var runtime = TyphonRuntime.Create(dbe, schedule =>
{
schedule.PublicTrack.DeclareDag("Sim").CallbackSystem("Tick", ctx => RunGameLogic(ctx));
});
// ...run the workload; every built-in call site (transactions, B+Trees, page cache, WAL, checkpoints, ECS)
// emits automatically — no tracing calls needed in application code...
// 3. Ordinary shutdown finalizes the trace automatically (engine storage disposal fires it). No explicit
// Stop() call is required, but the host may end the session earlier if it wants to:
TyphonProfiler.Stop();
| Signal | Effect |
|---|---|
Typhon.Telemetry.Enabled / Profiler.Enabled = false |
Bootstrap is a no-op; zero runtime cost |
| Engine storage disposed (normal shutdown) | Primary teardown — trace finalized deterministically |
AppDomain.ProcessExit / terminating unhandled exception |
Backup teardown for hosts that never dispose |
TyphonProfiler.Stop() called explicitly |
Early, host-initiated teardown (idempotent) |
⚠️ Guarantees & limits
- Zero host code required for the common case — a
typhon.telemetry.jsonfile plusTyphonRuntime.Createis the entire integration surface. TyphonProfiler.Start/Stopare idempotent: a double-start is a no-op, a double-stop is a no-op — safe under restart-after-crash or a redundant safety-net firing.- Bootstrap failures (port already in use, unwritable trace path) are caught and logged to stderr; the host keeps running without profiling rather than crashing.
- The process-exit safety net does not catch
TerminateProcess/taskkill /F/SIGKILL,StackOverflowException, or access violations — the OS reaps the process before managed code runs. Only a clean shutdown or a caught, terminating unhandled exception finalizes the trace. - ProcessExit handlers are budget-capped by the CLR (~2 s); a hung consumer or exporter thread can be cut short, in which case the trailing block of the trace may be lost.
- Session metadata (systems, archetypes, component types, tracks, resource graph) is captured once at start and is immutable for the lifetime of the session.
AttachExporter/DetachExporter(advanced, low-level API) are only valid while the profiler is stopped — mutating exporters while running throwsInvalidOperationException.
🧪 Tests
- ProfilerSessionMetadataBuilderTests — session metadata composition (systems/archetypes/component types derived from
RuntimeOptions); per the fixture's own remarks, the fullProfilerBootstrap.TryStartself-wiring path gates on astatic readonlyflag and can't be unit-tested in isolation, so this covers the part that can be - FileExporterIntegrationTests — exercises
TyphonProfiler.Start/Stopend-to-end (start, emit, stop-finalizes) as part of the file-export round-trip
🔗 Related
- Sibling: Configuration & Performance Tuning — the config this bootstrap reads to decide which exporters and subsystems to start
- Sibling: Trace Export — the exporters this bootstrap builds and attaches before starting the session
- Source:
src/Typhon.Engine/Profiler/public/TyphonProfiler.cs,src/Typhon.Engine/Profiler/internals/ProfilerBootstrap.cs