Expand description
#[derive(Event)] for backbeat.
Annotate a #[repr(C)] struct to make it a traceable event. The derive reflects the struct’s
fields into a const [EventSchema] (offsets via core::mem::offset_of!, widths via
core::mem::size_of), computes a content-addressed [EventId] by hashing that whole schema,
and implements [backbeat::Event] — whose zerocopy::IntoBytes bound makes recording a single
memcpy and rejects padded layouts the reader can’t describe.
use backbeat::{Event, EventEnum};
use backbeat::zerocopy::{Immutable, IntoBytes};
/// Which way a frame is going.
#[derive(EventEnum, IntoBytes, Immutable, Clone, Copy)]
#[repr(u8)]
enum Direction { Incoming = 0, Outgoing = 1 }
/// A frame was queued for sending.
#[derive(Event, IntoBytes, Immutable)]
#[event(namespace = "my_crate::frame")]
#[repr(C)]
struct QueueData {
#[event(key)] packet_number: u64,
/// Offset into the stream.
#[event(unit = "bytes")] offset: u64,
direction: Direction, // a strongly-typed enum field
is_fin: bool,
}The generated code exposes QueueData::SCHEMA (an EventSchema), QueueData::ID (its
EventId), and QueueData::QUALIFIED_NAME, and implements Event. Because the id hashes the
whole schema, two builds whose layout or field metadata differ get distinct ids and never alias
in a dump’s registry.
Container attributes (on the struct):
#[event(namespace = "…")]— required; the event’s namespace prefix.#[event(span = enter)]/#[event(span = exit)]— mark this event as one half of a span, so the trace converter can pair begin/end records into a duration slice. A spanned event must carry exactly one#[event(span_id)]field.
Field attributes (mutually-exclusive roles — at most one per field):
#[event(key)]— promote this field to a top-level join/index column in the output table.#[event(span_id)]— thisu64is the span’s own id (required on aspan = enter|exitevent; the enter and exit halves carry the same value so the converter pairs them).#[event(parent_span_id)]— thisu64is the enclosing span’s id, linking this event under its parent. Allowed on any event, including plain (non-span) ones.
Other field attributes (combine with a role):
#[event(unit = "…")]— attach a unit hint ("bytes","ns", …) carried into the output.#[event(sentinel = <const>)]— declare an in-band “absent” marker for an integer field (e.g.sentinel = u64::MAXfor an unassigned packet number, orsentinel = 0for adump_idonly some events carry). The converter maps a field equal to its sentinel to SQL NULL, so the wire format needs no nulls yetWHERE x IS NULLworks.#[event(interned)]/#[event(interned(dynamic))]— the (u32) field is an intern id resolved against the dump’s intern table;dynamicmarks runtime-built values.
Enum-typed fields use a separate #[derive(EventEnum)] on the (#[repr(uN)], fieldless) enum;
the field then carries the strong type and the schema records its value→label map automatically.
Field and struct doc comments (///) are lifted verbatim into the schema’s description
fields, so the embedded registry documents itself.