Skip to documentation
SLOP

tiny.trace.replay

Reference tiny.trace replay

Defined in tiny.trace.

API (21)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsNo direct callsreplaywriteUserEventsJsonlreplay.UserEventEntryfromEvent
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.trace.src.replaypayloadIsJsonprivate sourcelib.trace.src.replayuserPayloadProtocolAllocprivate sourcelib.trace.src.replaywriteTimepointFieldreplay.UserEventEntrywriteJsonLine
Static calls · unresolved targets: 0 · external targets: 7.
Called byCallsNo direct callsprivate sourcelib.trace.src.cliparseUserEventStatsArgsreplay.UserEventStatSortparse
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.trace.src.cliframesprivate sourcelib.trace.src.replayframesFromSliceprivate sourcelib.trace.src.replaypopFrameprivate sourcelib.trace.src.replaypushFrameprivate sourcelib.trace.src.replayrecordSafepointreplayframesAtAlloc
Static calls · unresolved targets: 1 · external targets: 4.
Called byCallsNo direct callsprivate sourcelib.trace.src.clireplayCommandprivate sourcelib.trace.src.replaynearestCheckpointFromSlicereplaynearestCheckpointTimepoint
Static calls · unresolved targets: 1 · external targets: 3.
Called byCallsprivate sourcelib.trace.src.cliobjectsprivate sourcelib.trace.src.replaywriteObjectsFromSliceObjectEntryfromAllocationAllocObjectEntryfromFreereplaywriteObjectsJsonl
Static calls · unresolved targets: 0 · external targets: 12.
Called byCallsprivate sourcelib.trace.src.clisummaryprivate sourcelib.trace.src.replaywriteSummaryFromSliceprivate; no linktools.smg.src.storage.summaryrecordreplaywriteSummaryJsonl
Static calls · unresolved targets: 1 · external targets: 9.
Called byCallsprivate sourcelib.trace.src.clithreadsprivate sourcelib.trace.src.replaywriteThreadsFromSliceThreadEntryinitprivate sourcelib.trace.src.replayfindThreadreplaywriteThreadsJsonl
Static calls · unresolved targets: 2 · external targets: 3.
Called byCallsprivate sourcelib.trace.src.clitimepointsprivate sourcelib.trace.src.replaywriteTimepointsFromSliceTimepointEntryfromEventreplaywriteTimepointsJsonl
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsprivate sourcelib.trace.src.cliuserEventStatsprivate sourcelib.trace.src.replaywriteUserEventStatsFromSliceprivate sourcelib.trace.src.replay.UserEventStatEntrydeinitprivate sourcelib.trace.src.replaypayloadJsonValueToTextAllocprivate sourcelib.trace.src.replaypayloadJsonValueToUnsignedprivate sourcelib.trace.src.replayuserEventMatchesprivate sourcelib.trace.src.replayuserEventStatIndexreplaywriteUserEventStatsJsonl
Static calls · unresolved targets: 1 · external targets: 6.
Called byCallsprivate sourcelib.trace.src.cliuserEventsprivate sourcelib.trace.src.replaywriteUserEventsFromSlicereplay.UserEventEntryfromEventprivate sourcelib.trace.src.replayuserEventMatchesreplaywriteUserEventsJsonl
Static calls · unresolved targets: 0 · external targets: 2.

Source: lib/trace/src/replay.zig

zig
const std = @import("std");const pretty_json = @import("pretty").json;const event = @import("event.zig");const timepoint_string_capacity = 54;pub const Frame = struct {    depth: usize = 0,    function_id: u64,    site_id: u64,    stack_map_id: u64,    entered_at: event.Timepoint,    last_timepoint: event.Timepoint,    synthetic: bool = false,    pub fn writeJsonLine(self: Frame, writer: *std.Io.Writer) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("event", "frame");        try object.field("depth", self.depth);        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 writeTimepointField(object, "entered_at", self.entered_at);        try writeTimepointField(object, "last_timepoint", self.last_timepoint);        try object.field("synthetic", self.synthetic);        try object.endLine();    }};pub const TimepointEntry = struct {    index: usize,    timepoint: event.Timepoint,    kind: event.EventKind,    function_id: u64 = 0,    site_id: u64 = 0,    stack_map_id: u64 = 0,    object_id: u64 = 0,    label: ?[]const u8 = null,    operation: ?[]const u8 = null,    pub fn fromEvent(index: usize, item: event.Event) TimepointEntry {        return .{            .index = index,            .timepoint = item.timepoint,            .kind = item.kind,            .function_id = item.function_id,            .site_id = item.site_id,            .stack_map_id = item.stack_map_id,            .object_id = item.object_id,            .label = item.label,            .operation = item.operation,        };    }    pub fn writeJsonLine(self: TimepointEntry, writer: *std.Io.Writer) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("event", "timepoint");        try object.field("index", self.index);        try writeTimepointField(object, "timepoint", self.timepoint);        try object.field("epoch", self.timepoint.epoch);        try object.field("thread", self.timepoint.thread_id);        try object.field("seq", self.timepoint.seq);        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);        if (self.label) |label| try object.field("label", label);        if (self.operation) |operation| try object.field("operation", operation);        try object.endLine();    }};pub const UserEventFilter = struct {    label: ?[]const u8 = null,    protocol: ?[]const u8 = null,    payload_filters: []const UserPayloadFilter = &.{},};pub const UserPayloadFilter = struct {    field: []const u8,    value: []const u8,};pub const UserEventStatSort = enum {    total,    count,    min,    max,    mean,    group,    pub fn parse(text: []const u8) ?UserEventStatSort {        if (std.mem.eql(u8, text, "total")) return .total;        if (std.mem.eql(u8, text, "count")) return .count;        if (std.mem.eql(u8, text, "min")) return .min;        if (std.mem.eql(u8, text, "max")) return .max;        if (std.mem.eql(u8, text, "mean")) return .mean;        if (std.mem.eql(u8, text, "group")) return .group;        return null;    }};pub const UserEventEntry = struct {    index: usize,    timepoint: event.Timepoint,    label: ?[]const u8 = null,    data: ?[]const u8 = null,    pub fn fromEvent(index: usize, item: event.Event) UserEventEntry {        return .{            .index = index,            .timepoint = item.timepoint,            .label = item.label,            .data = item.data,        };    }    pub fn writeJsonLine(        self: UserEventEntry,        allocator: std.mem.Allocator,        writer: *std.Io.Writer,    ) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("event", "user");        try object.field("index", self.index);        try writeTimepointField(object, "timepoint", self.timepoint);        try object.field("epoch", self.timepoint.epoch);        try object.field("thread", self.timepoint.thread_id);        try object.field("seq", self.timepoint.seq);        if (self.label) |label| try object.field("label", label);        if (try userPayloadProtocolAlloc(allocator, self.data)) |protocol| {            defer allocator.free(protocol);            try object.field("protocol", protocol);        }        if (self.data) |bytes| {            try object.hexString("data_hex", bytes);            if (try payloadIsJson(allocator, bytes)) try object.raw("payload", bytes);        }        try object.endLine();    }};pub const UserEventStatEntry = struct {    group: []u8,    count: usize = 0,    first_index: ?usize = null,    last_index: ?usize = null,    first_timepoint: ?event.Timepoint = null,    last_timepoint: ?event.Timepoint = null,    total: u64 = 0,    min: u64 = 0,    max: u64 = 0,    fn deinit(self: *UserEventStatEntry, allocator: std.mem.Allocator) void {        allocator.free(self.group);        self.* = undefined;    }    fn record(self: *UserEventStatEntry, index: usize, timepoint: event.Timepoint, value: u64) void {        self.count += 1;        if (self.first_index == null) {            self.first_index = index;            self.first_timepoint = timepoint;        }        self.last_index = index;        self.last_timepoint = timepoint;        self.total +|= value;        self.min = if (self.count == 1) value else @min(self.min, value);        self.max = @max(self.max, value);    }    fn mean(self: UserEventStatEntry) u64 {        return if (self.count == 0) 0 else self.total / self.count;    }    fn sortValue(self: UserEventStatEntry, sort: UserEventStatSort) u64 {        return switch (sort) {            .total => self.total,            .count => @intCast(self.count),            .min => self.min,            .max => self.max,            .mean => self.mean(),            .group => 0,        };    }    fn writeJsonLine(        self: UserEventStatEntry,        group_field: []const u8,        metric_field: []const u8,        sort: UserEventStatSort,        writer: *std.Io.Writer,    ) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("event", "user_event_stat");        try object.field("group_field", group_field);        try object.field("group", self.group);        try object.field("metric_field", metric_field);        try object.field("sort_field", @tagName(sort));        try object.field("first_index", self.first_index);        try object.field("last_index", self.last_index);        try writeOptionalTimepointField(object, "first_timepoint", self.first_timepoint);        try writeOptionalTimepointField(object, "last_timepoint", self.last_timepoint);        try object.field("count", self.count);        try object.field("total", self.total);        try object.field("min", self.min);        try object.field("max", self.max);        try object.field("mean", self.mean());        try object.endLine();    }};pub const SummaryEntry = struct {    event_count: usize = 0,    first_timepoint: ?event.Timepoint = null,    last_timepoint: ?event.Timepoint = null,    thread_count: usize = 0,    session_status: ?i64 = null,    session_start_count: usize = 0,    session_end_count: usize = 0,    function_enter_count: usize = 0,    function_exit_count: usize = 0,    safepoint_count: usize = 0,    boundary_count: usize = 0,    allocation_count: usize = 0,    free_count: usize = 0,    checkpoint_count: usize = 0,    checkpoint_restore_count: usize = 0,    user_count: usize = 0,    object_count: usize = 0,    live_object_count: usize = 0,    freed_object_count: usize = 0,    free_without_allocation_count: usize = 0,    allocated_bytes: u64 = 0,    live_bytes: u64 = 0,    pub fn record(self: *SummaryEntry, item: event.Event) void {        if (self.event_count == 0) self.first_timepoint = item.timepoint;        self.last_timepoint = item.timepoint;        self.event_count += 1;        switch (item.kind) {            .session_start => self.session_start_count += 1,            .session_end => {                self.session_end_count += 1;                self.session_status = item.status;            },            .function_enter => self.function_enter_count += 1,            .function_exit => self.function_exit_count += 1,            .safepoint => self.safepoint_count += 1,            .boundary => self.boundary_count += 1,            .allocation => {                self.allocation_count += 1;                self.allocated_bytes += item.size;            },            .free => self.free_count += 1,            .checkpoint => self.checkpoint_count += 1,            .checkpoint_restore => self.checkpoint_restore_count += 1,            .user => self.user_count += 1,        }    }    pub fn writeJsonLine(self: SummaryEntry, writer: *std.Io.Writer) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("event", "summary");        try object.field("event_count", self.event_count);        try writeOptionalTimepointField(object, "first_timepoint", self.first_timepoint);        try writeOptionalTimepointField(object, "last_timepoint", self.last_timepoint);        try object.field("thread_count", self.thread_count);        try object.field("session_status", self.session_status);        try object.field("session_start_count", self.session_start_count);        try object.field("session_end_count", self.session_end_count);        try object.field("function_enter_count", self.function_enter_count);        try object.field("function_exit_count", self.function_exit_count);        try object.field("safepoint_count", self.safepoint_count);        try object.field("boundary_count", self.boundary_count);        try object.field("allocation_count", self.allocation_count);        try object.field("free_count", self.free_count);        try object.field("checkpoint_count", self.checkpoint_count);        try object.field("checkpoint_restore_count", self.checkpoint_restore_count);        try object.field("user_count", self.user_count);        try object.field("object_count", self.object_count);        try object.field("live_object_count", self.live_object_count);        try object.field("freed_object_count", self.freed_object_count);        try object.field(            "free_without_allocation_count",            self.free_without_allocation_count,        );        try object.field("allocated_bytes", self.allocated_bytes);        try object.field("live_bytes", self.live_bytes);        try object.endLine();    }};pub const ThreadEntry = struct {    thread_id: event.ThreadId,    first_index: usize,    last_index: usize,    first_timepoint: event.Timepoint,    last_timepoint: event.Timepoint,    event_count: usize = 0,    session_start_count: usize = 0,    session_end_count: usize = 0,    function_enter_count: usize = 0,    function_exit_count: usize = 0,    safepoint_count: usize = 0,    boundary_count: usize = 0,    allocation_count: usize = 0,    free_count: usize = 0,    checkpoint_count: usize = 0,    checkpoint_restore_count: usize = 0,    user_count: usize = 0,    pub fn init(index: usize, item: event.Event) ThreadEntry {        var entry = ThreadEntry{            .thread_id = item.timepoint.thread_id,            .first_index = index,            .last_index = index,            .first_timepoint = item.timepoint,            .last_timepoint = item.timepoint,        };        entry.record(item, index);        return entry;    }    pub fn record(self: *ThreadEntry, item: event.Event, index: usize) void {        self.last_index = index;        self.last_timepoint = item.timepoint;        self.event_count += 1;        switch (item.kind) {            .session_start => self.session_start_count += 1,            .session_end => self.session_end_count += 1,            .function_enter => self.function_enter_count += 1,            .function_exit => self.function_exit_count += 1,            .safepoint => self.safepoint_count += 1,            .boundary => self.boundary_count += 1,            .allocation => self.allocation_count += 1,            .free => self.free_count += 1,            .checkpoint => self.checkpoint_count += 1,            .checkpoint_restore => self.checkpoint_restore_count += 1,            .user => self.user_count += 1,        }    }    pub fn writeJsonLine(self: ThreadEntry, writer: *std.Io.Writer) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("event", "thread");        try object.field("thread", self.thread_id);        try object.field("first_index", self.first_index);        try object.field("last_index", self.last_index);        try writeTimepointField(object, "first_timepoint", self.first_timepoint);        try writeTimepointField(object, "last_timepoint", self.last_timepoint);        try object.field("event_count", self.event_count);        try object.field("session_start_count", self.session_start_count);        try object.field("session_end_count", self.session_end_count);        try object.field("function_enter_count", self.function_enter_count);        try object.field("function_exit_count", self.function_exit_count);        try object.field("safepoint_count", self.safepoint_count);        try object.field("boundary_count", self.boundary_count);        try object.field("allocation_count", self.allocation_count);        try object.field("free_count", self.free_count);        try object.field("checkpoint_count", self.checkpoint_count);        try object.field("checkpoint_restore_count", self.checkpoint_restore_count);        try object.field("user_count", self.user_count);        try object.endLine();    }};pub const ObjectEntry = struct {    object_id: u64,    allocation_index: ?usize = null,    free_index: ?usize = null,    allocated_at: ?event.Timepoint = null,    freed_at: ?event.Timepoint = null,    size: u64 = 0,    alignment: u32 = 0,    label: ?[]const u8 = null,    pub fn fromAllocationAlloc(allocator: std.mem.Allocator, index: usize, item: event.Event) !ObjectEntry {        return .{            .object_id = item.object_id,            .allocation_index = index,            .allocated_at = item.timepoint,            .size = item.size,            .alignment = item.alignment,            .label = if (item.label) |label| try allocator.dupe(u8, label) else null,        };    }    pub fn fromFree(index: usize, item: event.Event) ObjectEntry {        return .{            .object_id = item.object_id,            .free_index = index,            .freed_at = item.timepoint,        };    }    pub fn status(self: ObjectEntry) []const u8 {        if (self.allocated_at == null) return "free_without_allocation";        if (self.freed_at != null) return "freed";        return "live";    }    pub fn deinit(self: *ObjectEntry, allocator: std.mem.Allocator) void {        if (self.label) |label| allocator.free(label);        self.* = undefined;    }    pub fn writeJsonLine(self: ObjectEntry, writer: *std.Io.Writer) !void {        var stream = pretty_json.Writer.init(writer, .minified);        const object = try stream.object();        try object.field("event", "object");        try object.field("object_id", self.object_id);        try object.field("status", self.status());        try object.field("allocation_index", self.allocation_index);        try object.field("free_index", self.free_index);        try writeOptionalTimepointField(object, "allocated_at", self.allocated_at);        try writeOptionalTimepointField(object, "freed_at", self.freed_at);        try object.field("size", self.size);        try object.field("alignment", self.alignment);        if (self.label) |label| try object.field("label", label);        try object.endLine();    }};pub fn writeTimepointsJsonl(source: event.Source, writer: *std.Io.Writer) !void {    var index: usize = 0;    while (try source.peek()) |item| {        try TimepointEntry.fromEvent(index, item.*).writeJsonLine(writer);        source.advance();        index += 1;    }}pub fn writeUserEventsJsonl(    allocator: std.mem.Allocator,    source: event.Source,    filter: UserEventFilter,    writer: *std.Io.Writer,) !void {    var index: usize = 0;    while (try source.peek()) |item| {        if (item.kind == .user and try userEventMatches(allocator, item.*, filter)) {            try UserEventEntry.fromEvent(index, item.*).writeJsonLine(allocator, writer);        }        source.advance();        index += 1;    }}pub fn writeUserEventStatsJsonl(    allocator: std.mem.Allocator,    source: event.Source,    filter: UserEventFilter,    group_field: []const u8,    metric_field: []const u8,    sort: UserEventStatSort,    writer: *std.Io.Writer,) !void {    var stats: std.ArrayListUnmanaged(UserEventStatEntry) = .empty;    defer {        for (stats.items) |*entry| entry.deinit(allocator);        stats.deinit(allocator);    }    var index: usize = 0;    while (try source.peek()) |item| {        if (item.kind == .user and try userEventMatches(allocator, item.*, filter)) stats: {            const data = item.data orelse break :stats;            const parsed = std.json.parseFromSlice(std.json.Value, allocator, data, .{}) catch break :stats;            defer parsed.deinit();            const object = switch (parsed.value) {                .object => |object| object,                else => break :stats,            };            const group = object.get(group_field) orelse break :stats;            const metric = object.get(metric_field) orelse break :stats;            const metric_value = payloadJsonValueToUnsigned(metric) orelse break :stats;            const group_text = try payloadJsonValueToTextAlloc(allocator, group);            defer allocator.free(group_text);            const stat_index = try userEventStatIndex(allocator, &stats, group_text);            stats.items[stat_index].record(index, item.timepoint, metric_value);        }        source.advance();        index += 1;    }    std.mem.sort(UserEventStatEntry, stats.items, sort, userEventStatGreater);    for (stats.items) |entry| {        try entry.writeJsonLine(group_field, metric_field, sort, writer);    }}pub fn writeSummaryJsonl(    allocator: std.mem.Allocator,    source: event.Source,    writer: *std.Io.Writer,) !void {    var summary = SummaryEntry{};    var thread_ids: std.AutoHashMapUnmanaged(event.ThreadId, void) = .empty;    defer thread_ids.deinit(allocator);    var live_objects: std.AutoHashMapUnmanaged(u64, u64) = .empty;    defer live_objects.deinit(allocator);    while (try source.peek()) |item| {        summary.record(item.*);        try thread_ids.put(allocator, item.timepoint.thread_id, {});        switch (item.kind) {            .allocation => {                const result = try live_objects.getOrPut(allocator, item.object_id);                if (result.found_existing) return error.DuplicateLiveObject;                result.value_ptr.* = item.size;                summary.object_count += 1;                summary.live_bytes += item.size;            },            .free => if (live_objects.fetchRemove(item.object_id)) |removed| {                summary.freed_object_count += 1;                summary.live_bytes -= removed.value;            } else {                summary.object_count += 1;                summary.free_without_allocation_count += 1;            },            else => {},        }        source.advance();    }    summary.thread_count = thread_ids.count();    summary.live_object_count = live_objects.count();    try summary.writeJsonLine(writer);}pub fn writeThreadsJsonl(    allocator: std.mem.Allocator,    source: event.Source,    writer: *std.Io.Writer,) !void {    var threads: std.ArrayListUnmanaged(ThreadEntry) = .empty;    defer threads.deinit(allocator);    var index: usize = 0;    while (try source.peek()) |item| {        if (findThread(threads.items, item.timepoint.thread_id)) |thread_index| {            threads.items[thread_index].record(item.*, index);        } else {            try threads.append(allocator, ThreadEntry.init(index, item.*));        }        source.advance();        index += 1;    }    for (threads.items) |item| {        try item.writeJsonLine(writer);    }}pub fn writeObjectsJsonl(    allocator: std.mem.Allocator,    source: event.Source,    writer: *std.Io.Writer,) !void {    var live: std.AutoHashMapUnmanaged(u64, ObjectEntry) = .empty;    defer {        var iterator = live.valueIterator();        while (iterator.next()) |item| item.deinit(allocator);        live.deinit(allocator);    }    var index: usize = 0;    while (try source.peek()) |item| {        switch (item.kind) {            .allocation => {                var owned = try ObjectEntry.fromAllocationAlloc(allocator, index, item.*);                var inserted = false;                errdefer if (!inserted) owned.deinit(allocator);                const result = try live.getOrPut(allocator, item.object_id);                if (result.found_existing) return error.DuplicateLiveObject;                result.value_ptr.* = owned;                inserted = true;            },            .free => if (live.fetchRemove(item.object_id)) |removed| {                var completed = removed.value;                defer completed.deinit(allocator);                completed.free_index = index;                completed.freed_at = item.timepoint;                try completed.writeJsonLine(writer);            } else {                try ObjectEntry.fromFree(index, item.*).writeJsonLine(writer);            },            else => {},        }        source.advance();        index += 1;    }    var iterator = live.valueIterator();    while (iterator.next()) |item| try item.writeJsonLine(writer);}pub fn nearestCheckpointTimepoint(    source: event.Source,    target: event.Timepoint,) !?event.Timepoint {    var result: ?event.Timepoint = null;    while (try source.peek()) |item| {        if (item.kind == .checkpoint and item.timepoint.beforeOrEqual(target) and            (result == null or result.?.beforeOrEqual(item.timepoint)))        {            result = item.timepoint;        }        source.advance();    }    return result;}fn findThread(entries: []const ThreadEntry, thread_id: event.ThreadId) ?usize {    for (entries, 0..) |entry, index| {        if (entry.thread_id == thread_id) return index;    }    return null;}fn userEventMatches(    allocator: std.mem.Allocator,    item: event.Event,    filter: UserEventFilter,) !bool {    if (filter.label) |expected| {        const actual = item.label orelse return false;        if (!std.mem.eql(u8, actual, expected)) return false;    }    if (filter.protocol) |expected| {        const protocol = try userPayloadProtocolAlloc(allocator, item.data) orelse return false;        defer allocator.free(protocol);        if (!std.mem.eql(u8, protocol, expected)) return false;    }    for (filter.payload_filters) |payload_filter| {        if (!try userPayloadFieldMatches(allocator, item.data, payload_filter.field, payload_filter.value)) return false;    }    return true;}fn userPayloadFieldMatches(    allocator: std.mem.Allocator,    maybe_data: ?[]const u8,    field: []const u8,    expected: []const u8,) !bool {    const data = maybe_data orelse return false;    const parsed = std.json.parseFromSlice(std.json.Value, allocator, data, .{}) catch return false;    defer parsed.deinit();    const object = switch (parsed.value) {        .object => |object| object,        else => return false,    };    const actual = object.get(field) orelse return false;    return payloadJsonValueMatchesText(actual, expected);}fn payloadJsonValueMatchesText(value: std.json.Value, expected: []const u8) bool {    return switch (value) {        .string => |actual| std.mem.eql(u8, actual, expected),        .integer => |actual| blk: {            const parsed = std.fmt.parseInt(i64, expected, 0) catch break :blk false;            break :blk actual == parsed;        },        .float => |actual| blk: {            const parsed = std.fmt.parseFloat(f64, expected) catch break :blk false;            break :blk actual == parsed;        },        .number_string => |actual| std.mem.eql(u8, actual, expected),        .bool => |actual| (actual and std.mem.eql(u8, expected, "true")) or            (!actual and std.mem.eql(u8, expected, "false")),        .null => std.mem.eql(u8, expected, "null"),        .array,        .object,        => false,    };}fn payloadJsonValueToUnsigned(value: std.json.Value) ?u64 {    return switch (value) {        .integer => |actual| if (actual >= 0) @intCast(actual) else null,        .float => |actual| if (actual >= 0 and @floor(actual) == actual) @intFromFloat(actual) else null,        .number_string => |actual| std.fmt.parseUnsigned(u64, actual, 0) catch null,        else => null,    };}fn payloadJsonValueToTextAlloc(    allocator: std.mem.Allocator,    value: std.json.Value,) ![]u8 {    return switch (value) {        .string => |actual| try allocator.dupe(u8, actual),        .integer => |actual| try std.fmt.allocPrint(allocator, "{d}", .{actual}),        .float => |actual| try std.fmt.allocPrint(allocator, "{d}", .{actual}),        .number_string => |actual| try allocator.dupe(u8, actual),        .bool => |actual| try allocator.dupe(u8, if (actual) "true" else "false"),        .null => try allocator.dupe(u8, "null"),        .array,        .object,        => error.UnsupportedPayloadField,    };}fn userEventStatIndex(    allocator: std.mem.Allocator,    stats: *std.ArrayListUnmanaged(UserEventStatEntry),    group: []const u8,) !usize {    for (stats.items, 0..) |entry, index| {        if (std.mem.eql(u8, entry.group, group)) return index;    }    const owned_group = try allocator.dupe(u8, group);    errdefer allocator.free(owned_group);    try stats.append(allocator, .{ .group = owned_group });    return stats.items.len - 1;}fn userEventStatGreater(sort: UserEventStatSort, left: UserEventStatEntry, right: UserEventStatEntry) bool {    if (sort == .group) return std.mem.lessThan(u8, left.group, right.group);    const left_value = left.sortValue(sort);    const right_value = right.sortValue(sort);    if (left_value != right_value) return left_value > right_value;    if (left.total != right.total) return left.total > right.total;    if (left.max != right.max) return left.max > right.max;    return std.mem.lessThan(u8, left.group, right.group);}fn userPayloadProtocolAlloc(    allocator: std.mem.Allocator,    maybe_data: ?[]const u8,) !?[]u8 {    const data = maybe_data orelse return null;    const parsed = std.json.parseFromSlice(std.json.Value, allocator, data, .{}) catch return null;    defer parsed.deinit();    const object = switch (parsed.value) {        .object => |object| object,        else => return null,    };    const protocol = switch (object.get("protocol") orelse return null) {        .string => |text| text,        else => return null,    };    return try allocator.dupe(u8, protocol);}fn payloadIsJson(allocator: std.mem.Allocator, bytes: []const u8) !bool {    const parsed = std.json.parseFromSlice(std.json.Value, allocator, bytes, .{}) catch return false;    parsed.deinit();    return true;}pub fn framesAtAlloc(    allocator: std.mem.Allocator,    source: event.Source,    target: event.Timepoint,) ![]Frame {    var stack: std.ArrayListUnmanaged(Frame) = .empty;    defer stack.deinit(allocator);    while (try source.peek()) |item| {        if (item.timepoint.thread_id == target.thread_id and item.timepoint.beforeOrEqual(target)) {            switch (item.kind) {                .function_enter => try pushFrame(allocator, &stack, item.*, false),                .safepoint => try recordSafepoint(allocator, &stack, item.*),                .function_exit => try popFrame(&stack, item.*),                else => {},            }        }        source.advance();    }    const frames = try allocator.alloc(Frame, stack.items.len);    for (frames, 0..) |*frame, depth| {        frame.* = stack.items[stack.items.len - depth - 1];        frame.depth = depth;    }    return frames;}fn pushFrame(    allocator: std.mem.Allocator,    stack: *std.ArrayListUnmanaged(Frame),    item: event.Event,    synthetic: bool,) !void {    try stack.append(allocator, .{        .function_id = item.function_id,        .site_id = item.site_id,        .stack_map_id = item.stack_map_id,        .entered_at = item.timepoint,        .last_timepoint = item.timepoint,        .synthetic = synthetic,    });}fn recordSafepoint(    allocator: std.mem.Allocator,    stack: *std.ArrayListUnmanaged(Frame),    item: event.Event,) !void {    if (stack.items.len == 0) {        try pushFrame(allocator, stack, item, true);        return;    }    const top = &stack.items[stack.items.len - 1];    if (top.function_id != item.function_id) return error.TraceFrameMismatch;    top.site_id = item.site_id;    top.stack_map_id = item.stack_map_id;    top.last_timepoint = item.timepoint;}fn popFrame(    stack: *std.ArrayListUnmanaged(Frame),    item: event.Event,) !void {    if (stack.items.len == 0) return error.TraceFrameMismatch;    const top = stack.items[stack.items.len - 1];    if (top.function_id != item.function_id) return error.TraceFrameMismatch;    _ = stack.pop();}fn writeTimepointField(object: pretty_json.Object, name: []const u8, value: event.Timepoint) !void {    try object.formattedString(        name,        timepoint_string_capacity,        "{d}:{d}:{d}",        .{ value.epoch, value.thread_id, value.seq },    );}fn writeOptionalTimepointField(    object: pretty_json.Object,    name: []const u8,    value: ?event.Timepoint,) !void {    if (value) |actual| {        try writeTimepointField(object, name, actual);    } else {        try object.field(name, null);    }}const SliceSource = struct {    events: []const event.Event,    cursor: usize = 0,    fn source(self: *SliceSource) event.Source {        return .{ .context = self, .peekFn = peek, .advanceFn = advance, .countFn = count };    }    fn peek(context: *anyopaque) !?*const event.Event {        const self: *SliceSource = @ptrCast(@alignCast(context));        if (self.cursor == self.events.len) return null;        return &self.events[self.cursor];    }    fn advance(context: *anyopaque) void {        const self: *SliceSource = @ptrCast(@alignCast(context));        self.cursor += 1;    }    fn count(context: *anyopaque) u64 {        const self: *SliceSource = @ptrCast(@alignCast(context));        return self.events.len;    }};fn writeTimepointsFromSlice(events: []const event.Event, writer: *std.Io.Writer) !void {    var stream = SliceSource{ .events = events };    try writeTimepointsJsonl(stream.source(), writer);}fn writeUserEventsFromSlice(    allocator: std.mem.Allocator,    events: []const event.Event,    filter: UserEventFilter,    writer: *std.Io.Writer,) !void {    var stream = SliceSource{ .events = events };    try writeUserEventsJsonl(allocator, stream.source(), filter, writer);}fn writeUserEventStatsFromSlice(    allocator: std.mem.Allocator,    events: []const event.Event,    filter: UserEventFilter,    group_field: []const u8,    metric_field: []const u8,    sort: UserEventStatSort,    writer: *std.Io.Writer,) !void {    var stream = SliceSource{ .events = events };    try writeUserEventStatsJsonl(allocator, stream.source(), filter, group_field, metric_field, sort, writer);}fn writeSummaryFromSlice(allocator: std.mem.Allocator, events: []const event.Event, writer: *std.Io.Writer) !void {    var stream = SliceSource{ .events = events };    try writeSummaryJsonl(allocator, stream.source(), writer);}fn writeThreadsFromSlice(allocator: std.mem.Allocator, events: []const event.Event, writer: *std.Io.Writer) !void {    var stream = SliceSource{ .events = events };    try writeThreadsJsonl(allocator, stream.source(), writer);}fn writeObjectsFromSlice(allocator: std.mem.Allocator, events: []const event.Event, writer: *std.Io.Writer) !void {    var stream = SliceSource{ .events = events };    try writeObjectsJsonl(allocator, stream.source(), writer);}fn nearestCheckpointFromSlice(events: []const event.Event, target: event.Timepoint) !?event.Timepoint {    var stream = SliceSource{ .events = events };    return try nearestCheckpointTimepoint(stream.source(), target);}fn framesFromSlice(allocator: std.mem.Allocator, events: []const event.Event, target: event.Timepoint) ![]Frame {    var stream = SliceSource{ .events = events };    return try framesAtAlloc(allocator, stream.source(), target);}test "checkpoint lookup returns nearest predecessor" {    const events = [_]event.Event{        event.Event.checkpoint(.{ .thread_id = 1, .seq = 2 }, "a", "state-a"),        event.Event.user(.{ .thread_id = 1, .seq = 3 }, "note", "ignored"),        event.Event.checkpoint(.{ .thread_id = 1, .seq = 8 }, "b", "state-b"),    };    const found = (try nearestCheckpointFromSlice(&events, .{ .thread_id = 1, .seq = 9 })) orelse        return error.MissingCheckpoint;    try std.testing.expectEqual(@as(u64, 8), found.seq);}test "timepoint query writes compact navigation jsonl" {    const events = [_]event.Event{        event.Event.sessionStart(.{ .thread_id = 1, .seq = 1 }, "run"),        event.Event.safepointReached(.{ .thread_id = 1, .seq = 2 }, .{ .function_id = 20, .site_id = 5 }),        event.Event.user(.{ .thread_id = 1, .seq = 3 }, "note", "ignored"),    };    var out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer out.deinit();    try writeTimepointsFromSlice(&events, &out.writer);    const bytes = try out.toOwnedSlice();    defer std.testing.allocator.free(bytes);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"event\":\"timepoint\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"timepoint\":\"0:1:2\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"kind\":\"safepoint\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"label\":\"run\"") != null);}test "user event query filters labels and payload protocols" {    const events = [_]event.Event{        event.Event.user(            .{ .thread_id = 1, .seq = 1 },            "compiler.query.miss",            "{\"protocol\":\"tiny.compiler.query/v1\",\"phase\":\"analysis\",\"product_kind\":\"function_summary\",\"product_identity\":7}",        ),        event.Event.user(.{ .thread_id = 1, .seq = 2 }, "process.argv", "tiny\x00compile"),        event.Event.user(            .{ .thread_id = 2, .seq = 3 },            "compiler.query.hit",            "{\"protocol\":\"tiny.compiler.query/v1\",\"phase\":\"source_choir\",\"product_kind\":\"source_choir_module\",\"product_identity\":8}",        ),    };    var out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer out.deinit();    try writeUserEventsFromSlice(        std.testing.allocator,        &events,        .{ .protocol = "tiny.compiler.query/v1" },        &out.writer,    );    const bytes = try out.toOwnedSlice();    defer std.testing.allocator.free(bytes);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"event\":\"user\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"label\":\"compiler.query.miss\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"label\":\"compiler.query.hit\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"label\":\"process.argv\"") == null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"protocol\":\"tiny.compiler.query/v1\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"payload\":{\"protocol\":\"tiny.compiler.query/v1\",\"phase\":\"analysis\"") != null);    var label_out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer label_out.deinit();    try writeUserEventsFromSlice(        std.testing.allocator,        &events,        .{ .label = "compiler.query.hit" },        &label_out.writer,    );    const label_bytes = try label_out.toOwnedSlice();    defer std.testing.allocator.free(label_bytes);    try std.testing.expect(std.mem.indexOf(u8, label_bytes, "\"label\":\"compiler.query.hit\"") != null);    try std.testing.expect(std.mem.indexOf(u8, label_bytes, "\"label\":\"compiler.query.miss\"") == null);    var payload_out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer payload_out.deinit();    const product_kind_filters = [_]UserPayloadFilter{.{        .field = "product_kind",        .value = "function_summary",    }};    try writeUserEventsFromSlice(        std.testing.allocator,        &events,        .{            .protocol = "tiny.compiler.query/v1",            .payload_filters = &product_kind_filters,        },        &payload_out.writer,    );    const payload_bytes = try payload_out.toOwnedSlice();    defer std.testing.allocator.free(payload_bytes);    try std.testing.expect(std.mem.indexOf(u8, payload_bytes, "\"product_kind\":\"function_summary\"") != null);    try std.testing.expect(std.mem.indexOf(u8, payload_bytes, "\"product_kind\":\"source_choir_module\"") == null);    var identity_out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer identity_out.deinit();    const identity_filters = [_]UserPayloadFilter{.{        .field = "product_identity",        .value = "8",    }};    try writeUserEventsFromSlice(        std.testing.allocator,        &events,        .{ .payload_filters = &identity_filters },        &identity_out.writer,    );    const identity_bytes = try identity_out.toOwnedSlice();    defer std.testing.allocator.free(identity_bytes);    try std.testing.expect(std.mem.indexOf(u8, identity_bytes, "\"product_identity\":8") != null);    try std.testing.expect(std.mem.indexOf(u8, identity_bytes, "\"product_identity\":7") == null);    var combined_out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer combined_out.deinit();    const combined_filters = [_]UserPayloadFilter{        .{ .field = "product_kind", .value = "source_choir_module" },        .{ .field = "product_identity", .value = "8" },    };    try writeUserEventsFromSlice(        std.testing.allocator,        &events,        .{            .protocol = "tiny.compiler.query/v1",            .payload_filters = &combined_filters,        },        &combined_out.writer,    );    const combined_bytes = try combined_out.toOwnedSlice();    defer std.testing.allocator.free(combined_bytes);    try std.testing.expect(std.mem.indexOf(u8, combined_bytes, "\"product_kind\":\"source_choir_module\"") != null);    try std.testing.expect(std.mem.indexOf(u8, combined_bytes, "\"product_kind\":\"function_summary\"") == null);    try std.testing.expect(std.mem.indexOf(u8, combined_bytes, "\"product_identity\":8") != null);}test "user event stats groups by payload and sums numeric metrics" {    const events = [_]event.Event{        event.Event.user(            .{ .thread_id = 1, .seq = 1 },            "compiler.query.finish",            "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"function_summary\",\"elapsed_ns\":7}",        ),        event.Event.user(            .{ .thread_id = 1, .seq = 2 },            "compiler.query.finish",            "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"partial_evaluation\",\"elapsed_ns\":11}",        ),        event.Event.user(            .{ .thread_id = 1, .seq = 3 },            "compiler.query.finish",            "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"function_summary\",\"elapsed_ns\":13}",        ),        event.Event.user(.{ .thread_id = 1, .seq = 4 }, "process.argv", "tiny\x00compile"),    };    var out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer out.deinit();    try writeUserEventStatsFromSlice(        std.testing.allocator,        &events,        .{            .label = "compiler.query.finish",            .protocol = "tiny.compiler.query/v1",        },        "product_kind",        "elapsed_ns",        .total,        &out.writer,    );    const bytes = try out.toOwnedSlice();    defer std.testing.allocator.free(bytes);    const function_index = std.mem.indexOf(u8, bytes, "\"group\":\"function_summary\"") orelse        return error.TestExpectedResult;    const partial_index = std.mem.indexOf(u8, bytes, "\"group\":\"partial_evaluation\"") orelse        return error.TestExpectedResult;    try std.testing.expect(function_index < partial_index);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"count\":2") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"sort_field\":\"total\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"first_index\":0") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"last_index\":2") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"first_timepoint\":\"0:1:1\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"last_timepoint\":\"0:1:3\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"total\":20") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"min\":7") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"max\":13") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"mean\":10") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"total\":11") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"mean\":11") != null);}test "user event stats can sort by mean metric" {    const events = [_]event.Event{        event.Event.user(            .{ .thread_id = 1, .seq = 1 },            "compiler.query.finish",            "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"function_summary\",\"elapsed_ns\":7}",        ),        event.Event.user(            .{ .thread_id = 1, .seq = 2 },            "compiler.query.finish",            "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"partial_evaluation\",\"elapsed_ns\":11}",        ),        event.Event.user(            .{ .thread_id = 1, .seq = 3 },            "compiler.query.finish",            "{\"protocol\":\"tiny.compiler.query/v1\",\"product_kind\":\"function_summary\",\"elapsed_ns\":13}",        ),    };    var out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer out.deinit();    try writeUserEventStatsFromSlice(        std.testing.allocator,        &events,        .{            .label = "compiler.query.finish",            .protocol = "tiny.compiler.query/v1",        },        "product_kind",        "elapsed_ns",        .mean,        &out.writer,    );    const bytes = try out.toOwnedSlice();    defer std.testing.allocator.free(bytes);    const partial_index = std.mem.indexOf(u8, bytes, "\"group\":\"partial_evaluation\"") orelse        return error.TestExpectedResult;    const function_index = std.mem.indexOf(u8, bytes, "\"group\":\"function_summary\"") orelse        return error.TestExpectedResult;    try std.testing.expect(partial_index < function_index);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"sort_field\":\"mean\"") != null);}test "summary query writes trace-level totals as jsonl" {    const events = [_]event.Event{        event.Event.sessionStart(.{ .thread_id = 1, .seq = 1 }, "run"),        event.Event.allocation(.{ .thread_id = 1, .seq = 2 }, 7, 64, 8, "buffer"),        event.Event.boundaryBytes(.{ .thread_id = 2, .seq = 3 }, "env.TEST", "value"),        event.Event.free(.{ .thread_id = 1, .seq = 4 }, 7),        event.Event.free(.{ .thread_id = 1, .seq = 5 }, 99),        event.Event.sessionEnd(.{ .thread_id = 2, .seq = 6 }, 0),    };    var out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer out.deinit();    try writeSummaryFromSlice(std.testing.allocator, &events, &out.writer);    const bytes = try out.toOwnedSlice();    defer std.testing.allocator.free(bytes);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"event\":\"summary\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"event_count\":6") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"first_timepoint\":\"0:1:1\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"last_timepoint\":\"0:2:6\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"thread_count\":2") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"session_status\":0") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"boundary_count\":1") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"object_count\":2") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"freed_object_count\":1") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"free_without_allocation_count\":1") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"allocated_bytes\":64") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"live_bytes\":0") != null);}test "thread query writes per-thread event ranges and counts" {    const events = [_]event.Event{        event.Event.sessionStart(.{ .thread_id = 1, .seq = 1 }, "run"),        event.Event.functionEnter(.{ .thread_id = 1, .seq = 2 }, .{ .function_id = 20, .site_id = 1 }),        event.Event.safepointReached(.{ .thread_id = 2, .seq = 3 }, .{ .function_id = 30, .site_id = 5 }),        event.Event.boundaryBytes(.{ .thread_id = 1, .seq = 4 }, "env.TEST", "value"),        event.Event.sessionEnd(.{ .thread_id = 2, .seq = 5 }, 0),    };    var out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer out.deinit();    try writeThreadsFromSlice(std.testing.allocator, &events, &out.writer);    const bytes = try out.toOwnedSlice();    defer std.testing.allocator.free(bytes);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"event\":\"thread\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"thread\":1") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"first_timepoint\":\"0:1:1\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"last_timepoint\":\"0:1:4\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"event_count\":3") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"function_enter_count\":1") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"boundary_count\":1") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"thread\":2") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"safepoint_count\":1") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"session_end_count\":1") != null);}test "object query writes allocation lifetimes as jsonl" {    const events = [_]event.Event{        event.Event.allocation(.{ .thread_id = 1, .seq = 1 }, 7, 64, 8, "buffer"),        event.Event.safepointReached(.{ .thread_id = 1, .seq = 2 }, .{ .function_id = 20, .site_id = 5 }),        event.Event.free(.{ .thread_id = 1, .seq = 3 }, 7),        event.Event.allocation(.{ .thread_id = 1, .seq = 4 }, 8, 16, 4, null),        event.Event.free(.{ .thread_id = 1, .seq = 5 }, 99),    };    var out = std.Io.Writer.Allocating.init(std.testing.allocator);    defer out.deinit();    try writeObjectsFromSlice(std.testing.allocator, &events, &out.writer);    const bytes = try out.toOwnedSlice();    defer std.testing.allocator.free(bytes);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"event\":\"object\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"object_id\":7") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"status\":\"freed\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"allocated_at\":\"0:1:1\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"freed_at\":\"0:1:3\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"label\":\"buffer\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"object_id\":8") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"status\":\"live\"") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"object_id\":99") != null);    try std.testing.expect(std.mem.indexOf(u8, bytes, "\"status\":\"free_without_allocation\"") != null);}test "frame query returns active stack at safepoint" {    const events = [_]event.Event{        event.Event.functionEnter(.{ .thread_id = 1, .seq = 1 }, .{ .function_id = 10, .site_id = 1 }),        event.Event.functionEnter(.{ .thread_id = 1, .seq = 2 }, .{ .function_id = 20, .site_id = 1 }),        event.Event.safepointReached(.{ .thread_id = 1, .seq = 3 }, .{ .function_id = 20, .site_id = 9, .stack_map_id = 7 }),        event.Event.functionExit(.{ .thread_id = 1, .seq = 4 }, .{ .function_id = 20, .site_id = 10 }),    };    const frames = try framesFromSlice(std.testing.allocator, &events, .{ .thread_id = 1, .seq = 3 });    defer std.testing.allocator.free(frames);    try std.testing.expectEqual(@as(usize, 2), frames.len);    try std.testing.expectEqual(@as(usize, 0), frames[0].depth);    try std.testing.expectEqual(@as(u64, 20), frames[0].function_id);    try std.testing.expectEqual(@as(u64, 9), frames[0].site_id);    try std.testing.expectEqual(@as(u64, 7), frames[0].stack_map_id);    try std.testing.expectEqual(@as(u64, 3), frames[0].last_timepoint.seq);    try std.testing.expectEqual(@as(usize, 1), frames[1].depth);    try std.testing.expectEqual(@as(u64, 10), frames[1].function_id);}test "frame query drops exited frames by target timepoint" {    const events = [_]event.Event{        event.Event.functionEnter(.{ .thread_id = 1, .seq = 1 }, .{ .function_id = 10, .site_id = 1 }),        event.Event.functionEnter(.{ .thread_id = 1, .seq = 2 }, .{ .function_id = 20, .site_id = 1 }),        event.Event.functionExit(.{ .thread_id = 1, .seq = 3 }, .{ .function_id = 20, .site_id = 2 }),    };    const frames = try framesFromSlice(std.testing.allocator, &events, .{ .thread_id = 1, .seq = 3 });    defer std.testing.allocator.free(frames);    try std.testing.expectEqual(@as(usize, 1), frames.len);    try std.testing.expectEqual(@as(u64, 10), frames[0].function_id);}test "frame query represents safepoint-only traces" {    const events = [_]event.Event{        event.Event.safepointReached(.{ .thread_id = 1, .seq = 1 }, .{ .function_id = 20, .site_id = 5 }),    };    const frames = try framesFromSlice(std.testing.allocator, &events, .{ .thread_id = 1, .seq = 1 });    defer std.testing.allocator.free(frames);    try std.testing.expectEqual(@as(usize, 1), frames.len);    try std.testing.expectEqual(@as(u64, 20), frames[0].function_id);    try std.testing.expect(frames[0].synthetic);}test "frame query ignores other threads" {    const events = [_]event.Event{        event.Event.functionEnter(.{ .thread_id = 1, .seq = 1 }, .{ .function_id = 10, .site_id = 1 }),        event.Event.functionEnter(.{ .thread_id = 2, .seq = 2 }, .{ .function_id = 20, .site_id = 1 }),    };    const frames = try framesFromSlice(std.testing.allocator, &events, .{ .thread_id = 1, .seq = 2 });    defer std.testing.allocator.free(frames);    try std.testing.expectEqual(@as(usize, 1), frames.len);    try std.testing.expectEqual(@as(u64, 10), frames[0].function_id);}

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

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

Audit

Definitions17
Public names17
Members24
Version26.7.0
Revisiondaab053ee433