Skip to documentation
SLOP

tiny.trace.event

Reference tiny.trace event

Defined in tiny.trace.

API (9)

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callstiny.traceevent
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: lib/trace/src/event.zig

zig
const std = @import("std");const pretty_json = @import("pretty").json;const Allocator = std.mem.Allocator;pub const trace_format_version: u32 = 3;pub const ThreadId = u32;pub const Mode = enum {    off,    record,    replay,};pub const Timepoint = struct {    epoch: u64 = 0,    thread_id: ThreadId = 0,    seq: u64 = 0,    pub fn beforeOrEqual(self: Timepoint, other: Timepoint) bool {        if (self.epoch != other.epoch) return self.epoch < other.epoch;        if (self.seq != other.seq) return self.seq <= other.seq;        return self.thread_id <= other.thread_id;    }    pub fn eql(left: Timepoint, right: Timepoint) bool {        return left.epoch == right.epoch and            left.thread_id == right.thread_id and            left.seq == right.seq;    }    pub fn write(self: Timepoint, writer: *std.Io.Writer) !void {        try writer.print("{d}:{d}:{d}", .{ self.epoch, self.thread_id, self.seq });    }};pub const Safepoint = struct {    function_id: u64,    site_id: u64,    stack_map_id: u64 = 0,};pub const EventKind = enum {    session_start,    session_end,    function_enter,    function_exit,    safepoint,    boundary,    allocation,    free,    checkpoint,    checkpoint_restore,    user,    pub fn tag(self: EventKind) []const u8 {        return switch (self) {            .session_start => "session.start",            .session_end => "session.end",            .function_enter => "function.enter",            .function_exit => "function.exit",            .safepoint => "safepoint",            .boundary => "boundary",            .allocation => "allocation",            .free => "free",            .checkpoint => "checkpoint",            .checkpoint_restore => "checkpoint.restore",            .user => "user",        };    }    pub fn fromTag(text: []const u8) ?EventKind {        inline for (            @typeInfo(EventKind).@"enum".field_names,            @typeInfo(EventKind).@"enum".field_values,        ) |field_name, field_name_value| {            const field = .{ .name = field_name, .value = field_name_value };            const kind: EventKind = @fromBackingInt(@intCast(field.value));            if (std.mem.eql(u8, text, kind.tag())) return kind;        }        return null;    }};pub const Event = struct {    kind: EventKind,    timepoint: Timepoint,    function_id: u64 = 0,    site_id: u64 = 0,    stack_map_id: u64 = 0,    object_id: u64 = 0,    size: u64 = 0,    alignment: u32 = 0,    status: i64 = 0,    operation: ?[]const u8 = null,    label: ?[]const u8 = null,    data: ?[]const u8 = null,    pub fn sessionStart(timepoint: Timepoint, label: []const u8) Event {        return .{ .kind = .session_start, .timepoint = timepoint, .label = label };    }    pub fn sessionEnd(timepoint: Timepoint, status: i64) Event {        return .{ .kind = .session_end, .timepoint = timepoint, .status = status };    }    pub fn functionEnter(timepoint: Timepoint, safepoint: Safepoint) Event {        return functionEvent(.function_enter, timepoint, safepoint);    }    pub fn functionExit(timepoint: Timepoint, safepoint: Safepoint) Event {        return functionEvent(.function_exit, timepoint, safepoint);    }    pub fn safepointReached(timepoint: Timepoint, safepoint: Safepoint) Event {        return functionEvent(.safepoint, timepoint, safepoint);    }    pub fn boundaryBytes(timepoint: Timepoint, operation: []const u8, bytes: []const u8) Event {        return .{            .kind = .boundary,            .timepoint = timepoint,            .operation = operation,            .data = bytes,        };    }    pub fn allocation(        timepoint: Timepoint,        object_id: u64,        size: u64,        alignment: u32,        label: ?[]const u8,    ) Event {        return .{            .kind = .allocation,            .timepoint = timepoint,            .object_id = object_id,            .size = size,            .alignment = alignment,            .label = label,        };    }    pub fn free(timepoint: Timepoint, object_id: u64) Event {        return .{ .kind = .free, .timepoint = timepoint, .object_id = object_id };    }    pub fn checkpoint(timepoint: Timepoint, label: []const u8, bytes: []const u8) Event {        return .{            .kind = .checkpoint,            .timepoint = timepoint,            .label = label,            .data = bytes,        };    }    pub fn checkpointRestore(timepoint: Timepoint, label: []const u8) Event {        return .{ .kind = .checkpoint_restore, .timepoint = timepoint, .label = label };    }    pub fn user(timepoint: Timepoint, label: []const u8, bytes: []const u8) Event {        return .{            .kind = .user,            .timepoint = timepoint,            .label = label,            .data = bytes,        };    }    pub fn cloneAlloc(self: Event, allocator: Allocator) !Event {        var cloned = self;        cloned.operation = if (self.operation) |operation|            try allocator.dupe(u8, operation)        else            null;        errdefer if (cloned.operation) |operation| allocator.free(operation);        cloned.label = if (self.label) |label|            try allocator.dupe(u8, label)        else            null;        errdefer if (cloned.label) |label| allocator.free(label);        cloned.data = if (self.data) |data|            try allocator.dupe(u8, data)        else            null;        errdefer if (cloned.data) |data| allocator.free(data);        return cloned;    }    pub fn deinit(self: *Event, allocator: Allocator) void {        if (self.operation) |operation| allocator.free(operation);        if (self.label) |label| allocator.free(label);        if (self.data) |data| allocator.free(data);        self.* = undefined;    }    pub fn eqlForReplay(left: Event, right: Event) bool {        return left.kind == right.kind and            Timepoint.eql(left.timepoint, right.timepoint) and            left.function_id == right.function_id and            left.site_id == right.site_id and            left.stack_map_id == right.stack_map_id and            left.object_id == right.object_id and            left.size == right.size and            left.alignment == right.alignment and            left.status == right.status and            optionalBytesEql(left.operation, right.operation) and            optionalBytesEql(left.label, right.label) and            optionalBytesEql(left.data, right.data);    }    pub fn writeJsonLine(self: Event, writer: *std.Io.Writer) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("v", trace_format_version);        const timepoint = try object.object("timepoint");        try timepoint.field("epoch", self.timepoint.epoch);        try timepoint.field("thread", self.timepoint.thread_id);        try timepoint.field("seq", self.timepoint.seq);        try timepoint.end();        try object.field("kind", self.kind.tag());        try object.field("function_id", self.function_id);        try object.field("site_id", self.site_id);        try object.field("stack_map_id", self.stack_map_id);        try object.field("object_id", self.object_id);        try object.field("size", self.size);        try object.field("alignment", self.alignment);        try object.field("status", self.status);        if (self.operation) |operation| try object.field("operation", operation);        if (self.label) |label| try object.field("label", label);        if (self.data) |data| try object.hexString("data_hex", data);        try object.endLine();    }    pub fn fromJsonLine(allocator: Allocator, line: []const u8) !Event {        const parsed = std.json.parseFromSlice(std.json.Value, allocator, line, .{}) catch            return error.InvalidEventJson;        defer parsed.deinit();        const object = switch (parsed.value) {            .object => |object| object,            else => return error.InvalidEventJson,        };        const version = try jsonU64(object.get("v") orelse return error.InvalidEventJson);        if (version != trace_format_version) return error.UnsupportedTraceVersion;        const timepoint_object = switch (object.get("timepoint") orelse return error.InvalidEventJson) {            .object => |value| value,            else => return error.InvalidEventJson,        };        const kind_text = try jsonString(object.get("kind") orelse return error.InvalidEventJson);        const kind = EventKind.fromTag(kind_text) orelse return error.InvalidEventJson;        var event = Event{            .kind = kind,            .timepoint = .{                .epoch = try jsonU64(timepoint_object.get("epoch") orelse return error.InvalidEventJson),                .thread_id = @intCast(try jsonU64(timepoint_object.get("thread") orelse return error.InvalidEventJson)),                .seq = try jsonU64(timepoint_object.get("seq") orelse return error.InvalidEventJson),            },            .function_id = try jsonU64(object.get("function_id") orelse return error.InvalidEventJson),            .site_id = try jsonU64(object.get("site_id") orelse return error.InvalidEventJson),            .stack_map_id = try jsonU64(object.get("stack_map_id") orelse return error.InvalidEventJson),            .object_id = try jsonU64(object.get("object_id") orelse return error.InvalidEventJson),            .size = try jsonU64(object.get("size") orelse return error.InvalidEventJson),            .alignment = @intCast(try jsonU64(object.get("alignment") orelse return error.InvalidEventJson)),            .status = try jsonI64(object.get("status") orelse return error.InvalidEventJson),        };        errdefer event.deinit(allocator);        if (object.get("operation")) |operation| {            event.operation = try allocator.dupe(u8, try jsonString(operation));        }        if (object.get("label")) |label| {            event.label = try allocator.dupe(u8, try jsonString(label));        }        if (object.get("data_hex")) |data_hex| {            event.data = try decodeHexAlloc(allocator, try jsonString(data_hex));        }        return event;    }};pub const Sink = struct {    context: *anyopaque,    appendFn: *const fn (context: *anyopaque, item: Event) anyerror!void,    pub fn append(self: Sink, item: Event) !void {        try self.appendFn(self.context, item);    }};pub const Source = struct {    context: *anyopaque,    peekFn: *const fn (context: *anyopaque) anyerror!?*const Event,    advanceFn: *const fn (context: *anyopaque) void,    countFn: *const fn (context: *anyopaque) u64,    pub fn peek(self: Source) !?*const Event {        return try self.peekFn(self.context);    }    pub fn advance(self: Source) void {        self.advanceFn(self.context);    }    pub fn count(self: Source) u64 {        return self.countFn(self.context);    }};fn functionEvent(kind: EventKind, timepoint: Timepoint, safepoint: Safepoint) Event {    return .{        .kind = kind,        .timepoint = timepoint,        .function_id = safepoint.function_id,        .site_id = safepoint.site_id,        .stack_map_id = safepoint.stack_map_id,    };}fn optionalBytesEql(left: ?[]const u8, right: ?[]const u8) bool {    if (left == null and right == null) return true;    if (left == null or right == null) return false;    return std.mem.eql(u8, left.?, right.?);}fn jsonString(value: std.json.Value) ![]const u8 {    return switch (value) {        .string => |text| text,        else => error.InvalidEventJson,    };}fn jsonU64(value: std.json.Value) !u64 {    return switch (value) {        .integer => |integer| if (integer < 0) error.InvalidEventJson else @intCast(integer),        .number_string => |text| std.fmt.parseInt(u64, text, 10) catch error.InvalidEventJson,        else => error.InvalidEventJson,    };}fn jsonI64(value: std.json.Value) !i64 {    return switch (value) {        .integer => |integer| @intCast(integer),        else => error.InvalidEventJson,    };}fn decodeHexAlloc(allocator: Allocator, text: []const u8) ![]u8 {    if (text.len % 2 != 0) return error.InvalidEventJson;    const bytes = try allocator.alloc(u8, text.len / 2);    errdefer allocator.free(bytes);    for (bytes, 0..) |*byte, index| {        const high = try hexNibble(text[index * 2]);        const low = try hexNibble(text[index * 2 + 1]);        byte.* = (high << 4) | low;    }    return bytes;}fn hexNibble(byte: u8) !u8 {    return switch (byte) {        '0'...'9' => byte - '0',        'a'...'f' => byte - 'a' + 10,        'A'...'F' => byte - 'A' + 10,        else => error.InvalidEventJson,    };}test "event json roundtrip preserves replay identity" {    const allocator = std.testing.allocator;    const original = Event.boundaryBytes(        .{ .epoch = 7, .thread_id = 1, .seq = 42 },        "env.TEST",        "value\nwith bytes",    );    var out = std.Io.Writer.Allocating.init(allocator);    defer out.deinit();    try original.writeJsonLine(&out.writer);    const bytes = try out.toOwnedSlice();    defer allocator.free(bytes);    var parsed = try Event.fromJsonLine(allocator, std.mem.trimEnd(u8, bytes, "\n"));    defer parsed.deinit(allocator);    try std.testing.expect(original.eqlForReplay(parsed));}test "timepoints sort by epoch then sequence" {    try std.testing.expect((Timepoint{ .epoch = 0, .thread_id = 9, .seq = 10 }).beforeOrEqual(.{        .epoch = 0,        .thread_id = 1,        .seq = 11,    }));    try std.testing.expect(!(Timepoint{ .epoch = 1, .thread_id = 0, .seq = 0 }).beforeOrEqual(.{        .epoch = 0,        .thread_id = 0,        .seq = 100,    }));}

Source: lib/trace/src/root.zig:32

zig
pub const event = @import("event.zig");

Audit

Definitions2
Public names2
Members0
Version26.7.0
Revisiondaab053ee433