Skip to main content

Module trace

Module trace 

Source
Expand description

Runtime observability.

ADR 0001 asks the runtime to trace host calls, capability use, and task lifecycle “without language-specific application hooks” — a program does not opt in to being traced, and it cannot opt out of being traceable. This module defines the event shape and where events go; crate::host and (in a later pass) the interpreter are the only places that produce them.

A trace is also the input to cove replay, which reproduces a run’s Host API interactions without calling a host. That is why TraceEvent::HostCall carries the call’s arguments and its result and not only its shape: a trace that says a call happened is enough to inspect a run, and not enough to reproduce one.

§JSON schema

JsonlSink writes one JSON object per line, and every Duration field is rendered as an integer count of nanoseconds under a key ending in _ns. The first line is a header declaring TRACE_FORMAT_VERSION, so a reader that does not know the version can reject the trace rather than misread it. These keys are a stable, documented interface — a trace format that changes silently breaks whatever reads it:

{"event":"trace_header","version":<u32>,"backend":"ast"|"vm","values":"full"|"redacted","entry":<string>,"args":[<string>...]}
{"event":"task_spawned","id":<u64>,"parent":<u64|null>,"scope":<string>}
{"event":"task_completed","id":<u64>,"cpu_ns":<u64>}
{"event":"task_cancelled","id":<u64>}
{"event":"host_call","task":<u64>,"module":<string>,"op":<string>,"capability":<string>,"wait_ns":<u64>,"granted":<bool>,"args":[<value>...],"outcome":<outcome>|null}
{"event":"entry_enter","module":<string>,"function":<string>}
{"event":"entry_exit","module":<string>,"function":<string>,"cpu_ns":<u64>,"wait_ns":<u64>}
{"event":"heap_collected","task":<u64>,"allocated":<u64>,"freed":<u64>,"live_objects":<u64>,"live_bytes":<u64>,"pause_ns":<u64>}
{"event":"heap_summary","collections":<u64>,"object_count":<u64>|null,"allocated_bytes":<u64>|null,"live_bytes":<u64>|null,"peak_bytes":<u64>|null,"pause_ns":<u64>|null,"allocated_words":<u64>|null,"capacity_words":<u64>|null,"live_words":<u64>|null}
{"event":"run_ended","outcome":<outcome-name>,"message":<string>|null}

A task is the id of the task that did the thing, and the entry’s own id is crate::runtime::ENTRY_TASK: the entry is not a spawned task, so it takes the one id the run never hands out. An <outcome-name> is one of the names RunOutcome::as_str writes.

An <outcome> is null for a call that never reached the host, and otherwise one of:

{"kind":"value","value":<value>}
{"kind":"error","message":<string>}
{"kind":"not_recordable"}

A <value> is a tagged encoding of one Value, covering the shapes that cross the Host API boundary:

{"type":"unit"}
{"type":"bool","value":<bool>}
{"type":"int","value":<i64>}
{"type":"float","value":<number>}
{"type":"duration","ns":<i64>}
{"type":"string","value":<string>}
{"type":"array","items":[<value>...]}
{"type":"enum","name":<string>,"case":<string>,"payload":[<value>...]}
{"type":"struct","name":<string>,"fields":[{"name":<string>,"value":<value>}...]}
{"type":"resource","name":<string>,"id":<i64>}
{"type":"redacted","of":<string>}
{"type":"opaque","of":<string>,"shown":<string>}

redacted is what ValueCapture::Redacted writes in place of every recorded value; opaque is what a value the encoding cannot represent — a vector, a closure, a task handle — leaves behind. Both are readable and neither can be replayed, which is exactly the distinction cove replay reports.

§What an event may carry

An event is produced by whichever task made the call and written by the one sink the run shares, so every event crosses a thread boundary. What may cross one is what the Language Card’s task-safety rule allows, which cove_runtime::task::Transfer both decides and carries — so that is the form a RecordedValue keeps a value in. A value that may not cross keeps instead what a trace could have said about it anyway: what it was and what it printed as, which is the opaque the format already writes for a vector. The two features agree by construction: a value a task could not have carried is a value a replay could not have reproduced.

Structs§

JsonlSink
Writes one JSON object per line to W, flushing after every event so a trace is visible as it happens rather than only at exit.
NullSink
Discards every event. The default when a run is not being traced.
Timing
Accumulates wait time separately from total elapsed time, so a caller can report CPU as elapsed - wait.
TraceHeader
What a trace declares about itself before its first event.

Enums§

HostOutcome
What a host call produced, when the trace records it.
RecordedValue
One value as a trace records it.
RecordingBackend
Which backend produced a recording.
RunOutcome
How a run ended.
TraceEvent
One recorded runtime event.
ValueCapture
How much of a host call’s arguments and results a trace records.

Constants§

RUNTIME_VERSION
The version of the cove-runtime crate a program ran against — CARGO_PKG_VERSION at build time, and nothing more than that.
TRACE_FORMAT_VERSION
The version of the JSONL trace format this build writes, and the only one it reads.

Traits§

TraceSink
Where trace events go.

Functions§

create_trace_file
Creates the file a trace is written to, readable only by its owner where the platform can say so.
value_to_json
Renders one Value in the trace’s value encoding, honouring capture.