Skip to documentation
SLOP

tiny.profiling.report.memory

Reference tiny.profiling report memory

Defined in report.

API (17)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsreport.memoryruntest; no linksrc.profiling.report.memorytest: profiling memory report account...test; no linksrc.profiling.report.memorytest: profiling memory report names i...test; no linksrc.profiling.report.pagetest: page run and workload render ta...report.sitegenerateprivate; no linksrc.profiling.report.memoryaccountprivate; no linksrc.profiling.report.memoryloadTraceprivate; no linksrc.profiling.report.memoryrssBytesreport.memoryload
Static calls · unresolved targets: 2 · external targets: 2.
Called byCallsNo direct callersreport.memoryloadreport.memorywriteJsonreport.memorywriteTextreport.memoryrun
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsreport.memoryruntest; no linksrc.profiling.report.memorytest: profiling memory report account...test; no linksrc.profiling.report.memorytest: profiling memory report renders...private; no linksrc.profiling.report.memorywriteAllocationRankingsJsonprivate; no linksrc.profiling.report.memorywriteTotalsJsonprivate; no linksrc.profiling.report.memorywriteWorkloadJsonreport.memorywriteJson
Static calls · unresolved targets: 1 · external targets: 7.
Called byCallsreport.memoryrunprivate; no linksrc.profiling.report.memoryrankedWorkloadsprivate; no linksrc.profiling.report.memorywriteAllocationRankingsTextprivate; no linksrc.profiling.report.memorywriteWorkloadDetailsTextreport.memorywriteText
Static calls · unresolved targets: 0 · external targets: 4.

Source: src/profiling/report/memory.zig

zig
const std = @import("std");const memtrace = @import("memtrace");const pretty = @import("pretty");const pretty_usage = @import("pretty_usage");const sys = @import("sys");const analyze = @import("../root.zig").analyze;const memory = @import("../root.zig").memory;const record = @import("../root.zig").record;const reducer = @import("../root.zig").reduction;const pretty_json = pretty.json;const fixture_trace_version = std.fmt.comptimePrint(    "{d}",    .{memtrace.event.format_version},);pub const Options = struct {    input: []const u8,    json: bool = false,    top: usize = 10,    min_bytes: usize = 0,};pub const Report = struct {    run: analyze.Run,    workloads: []const Workload,    totals: Totals,};pub const Workload = struct {    name: []const u8,    package: []const u8,    step: []const u8,    status: []const u8,    max_rss_kib: ?i64,    max_rss_bytes: ?u64,    trace_path: ?[]const u8,    trace: ?Trace,    trace_fault: ?TraceFault,    memory_metrics: []const memory.Metric,    accounting: Accounting,};pub const TraceFault = struct {    path: []const u8,    line: u64,    reason: []const u8,};pub const Totals = struct {    workloads: usize = 0,    rss_workloads: usize = 0,    traced_workloads: usize = 0,    complete_traces: usize = 0,    partial_traces: usize = 0,    invalid_traces: usize = 0,    rss_bytes_sum: u64 = 0,    attributed_retained_bytes_sum: u64 = 0,    unclassified_bytes_sum: u64 = 0,};pub const Accounting = struct {    state: []const u8,    rss_bytes: ?u64,    attributed_retained_bytes: ?u64,    unclassified_bytes: ?u64,    coverage_percent: ?f64,};pub const Trace = struct {    path: []const u8,    integrity: memtrace.CaptureIntegrity,    summary: TraceSummary,    scopes: []const Scope,    rankings: TraceRankings,    snapshot: ?memtrace.analysis.Snapshot = null,};pub const TraceRankings = struct {    retained: TraceRanking,    traffic: TraceRanking,    lifetime: TraceRanking,    pub fn get(        self: TraceRankings,        sort: memtrace.analysis.Sort,    ) TraceRanking {        return switch (sort) {            .retained => self.retained,            .traffic => self.traffic,            .lifetime => self.lifetime,        };    }};pub const TraceRanking = struct {    owners: []const Scope,    sources: []const Source,};pub const TraceSummary = memtrace.analysis.Summary;pub const Scope = memtrace.analysis.Scope;pub const Source = memtrace.analysis.Source;pub fn run(allocator: std.mem.Allocator, options: Options) !u8 {    const report = try load(allocator, options);    if (options.json) {        var buffer: [8192]u8 = undefined;        var file_writer = sys.stdio.stdout().writer(std.Options.debug_io, &buffer);        defer file_writer.interface.flush() catch {};        try writeJson(allocator, &file_writer.interface, report, options);    } else {        try writeText(allocator, pretty_usage.Terminal.stdout(allocator, .{}), report, options);    }    return 0;}pub fn load(allocator: std.mem.Allocator, options: Options) !Report {    const run_value = try analyze.loadRun(allocator, options.input);    var rows: std.ArrayList(Workload) = .empty;    var totals = Totals{ .workloads = run_value.workloads.len };    for (run_value.workloads) |workload| {        const artifacts = workload.analysisArtifacts();        const allocations_path = if (artifacts) |actual| actual.allocations else null;        const loaded = try loadTrace(allocator, allocations_path, options);        const trace: ?Trace = switch (loaded) {            .trace => |actual| actual,            .none, .fault => null,        };        const fault: ?TraceFault = switch (loaded) {            .fault => |actual| actual,            .none, .trace => null,        };        const accounting = if (fault == null) account(workload, trace) else Accounting{            .state = "trace_invalid",            .rss_bytes = rssBytes(workload.max_rss_kib),            .attributed_retained_bytes = null,            .unclassified_bytes = null,            .coverage_percent = null,        };        if (accounting.rss_bytes) |rss| {            totals.rss_workloads += 1;            totals.rss_bytes_sum +|= rss;        }        if (trace) |actual| {            totals.traced_workloads += 1;            if (std.mem.eql(u8, actual.integrity.status, "complete")) {                totals.complete_traces += 1;                totals.attributed_retained_bytes_sum +|= actual.summary.retained_bytes;            } else {                totals.partial_traces += 1;            }        }        if (fault != null) totals.invalid_traces += 1;        if (accounting.unclassified_bytes) |bytes| totals.unclassified_bytes_sum +|= bytes;        try rows.append(allocator, .{            .name = workload.name,            .package = workload.package,            .step = workload.step,            .status = workload.status,            .max_rss_kib = workload.max_rss_kib,            .max_rss_bytes = rssBytes(workload.max_rss_kib),            .trace_path = allocations_path,            .trace = trace,            .trace_fault = fault,            .memory_metrics = workload.memory_metrics,            .accounting = accounting,        });    }    return .{        .run = run_value,        .workloads = try rows.toOwnedSlice(allocator),        .totals = totals,    };}const LoadedTrace = union(enum) {    none,    trace: Trace,    fault: TraceFault,};fn availableSymbolBinary(path: []const u8) !?[]const u8 {    const file = (if (std.fs.path.isAbsolute(path))        sys.fs.openAbsoluteFile(path, .{})    else        sys.fs.cwd().openFile(sys.fs.debugIo(), path, .{})) catch |err| switch (err) {        error.FileNotFound => return null,        else => |actual| return actual,    };    defer sys.fs.closeHandle(file);    return path;}fn loadTrace(allocator: std.mem.Allocator, path: ?[]const u8, options: Options) !LoadedTrace {    const actual_path = path orelse return .none;    var file = sys.fs.cwd().openFile(sys.fs.debugIo(), actual_path, .{}) catch |err| switch (err) {        error.FileNotFound => return .none,        else => |actual| return actual,    };    defer file.close(sys.fs.debugIo());    var analyzer = memtrace.Analyzer.init(allocator);    defer analyzer.deinit();    if (try ingestTrace(&analyzer, file, actual_path)) |fault| return .{ .fault = fault };    const executable_path = try memtrace.stack.identity.artifactPathAlloc(        allocator,        actual_path,    );    defer allocator.free(executable_path);    const symbol_binary = try availableSymbolBinary(executable_path);    const path_copy = try allocator.dupe(u8, actual_path);    errdefer allocator.free(path_copy);    var snapshot = try analyzer.snapshot(allocator, .{        .top = options.top,        .min_bytes = options.min_bytes,        .site_symbol_binary = symbol_binary,    });    errdefer snapshot.deinit();    const retained = snapshot.rankings.retained;    const traffic = snapshot.rankings.traffic;    const lifetime = snapshot.rankings.lifetime;    return .{        .trace = .{            .path = path_copy,            .integrity = snapshot.integrity,            .summary = snapshot.summary,            .scopes = retained.scopes,            .rankings = .{                .retained = .{                    .owners = retained.scopes,                    .sources = retained.sources,                },                .traffic = .{                    .owners = traffic.scopes,                    .sources = traffic.sources,                },                .lifetime = .{                    .owners = lifetime.scopes,                    .sources = lifetime.sources,                },            },            .snapshot = snapshot,        },    };}fn ingestTrace(analyzer: *memtrace.Analyzer, file: std.Io.File, path: []const u8) !?TraceFault {    var buffer: [64 * 1024]u8 = undefined;    var reader = file.reader(sys.fs.debugIo(), &buffer);    var line_number: u64 = 0;    while (true) {        const line = reader.interface.takeDelimiter('\n') catch |err| switch (err) {            error.ReadFailed => return reader.err.?,            else => return err,        };        const actual = line orelse break;        line_number += 1;        analyzer.ingestJsonLine(actual) catch |err| switch (err) {            error.OutOfMemory => return error.OutOfMemory,            else => return .{                .path = path,                .line = line_number,                .reason = @errorName(err),            },        };    }    return null;}fn account(workload: analyze.Workload, trace: ?Trace) Accounting {    const rss = rssBytes(workload.max_rss_kib);    const actual_trace = trace orelse return .{        .state = "missing_trace",        .rss_bytes = rss,        .attributed_retained_bytes = null,        .unclassified_bytes = null,        .coverage_percent = null,    };    const attributed = actual_trace.summary.retained_bytes;    if (!std.mem.eql(u8, actual_trace.integrity.status, "complete")) return .{        .state = "trace_partial",        .rss_bytes = rss,        .attributed_retained_bytes = attributed,        .unclassified_bytes = null,        .coverage_percent = null,    };    const rss_value = rss orelse return .{        .state = "missing_rss",        .rss_bytes = null,        .attributed_retained_bytes = attributed,        .unclassified_bytes = null,        .coverage_percent = null,    };    if (rss_value == 0) return .{        .state = "missing_rss",        .rss_bytes = rss_value,        .attributed_retained_bytes = attributed,        .unclassified_bytes = null,        .coverage_percent = null,    };    if (attributed > rss_value) return .{        .state = "over_attributed",        .rss_bytes = rss_value,        .attributed_retained_bytes = attributed,        .unclassified_bytes = 0,        .coverage_percent = (@as(f64, @floatFromInt(attributed)) / @as(f64, @floatFromInt(rss_value))) * 100,    };    return .{        .state = "measured",        .rss_bytes = rss_value,        .attributed_retained_bytes = attributed,        .unclassified_bytes = rss_value - attributed,        .coverage_percent = (@as(f64, @floatFromInt(attributed)) / @as(f64, @floatFromInt(rss_value))) * 100,    };}fn rssBytes(max_rss_kib: ?i64) ?u64 {    const kib = max_rss_kib orelse return null;    if (kib < 0) return null;    return @as(u64, @intCast(kib)) * 1024;}pub fn writeText(allocator: std.mem.Allocator, terminal: pretty_usage.Terminal, report: Report, options: Options) !void {    try terminal.writeTextFmt("memory report: {s}\n", .{report.run.ref.run_id});    try terminal.writeTextFmt("artifacts: {s}\n", .{report.run.ref.root});    try terminal.writeTextFmt(        "workloads: {d}, rss {d}, traced {d} (complete {d}, partial {d}), " ++            "invalid traces {d}, memory metrics {d}\n",        .{            report.totals.workloads,            report.totals.rss_workloads,            report.totals.traced_workloads,            report.totals.complete_traces,            report.totals.partial_traces,            report.totals.invalid_traces,            report.run.memoryMetricCount(),        },    );    try terminal.writeTextFmt(        "accountability: rss_bytes_sum {d}, attributed_retained_bytes_sum {d}, unclassified_bytes_sum {d}\n",        .{ report.totals.rss_bytes_sum, report.totals.attributed_retained_bytes_sum, report.totals.unclassified_bytes_sum },    );    try writeAllocationRankingsText(        allocator,        terminal,        report.workloads,        options.top,    );    const ranked = try rankedWorkloads(allocator, report.workloads);    defer allocator.free(ranked);    const count = @min(options.top, ranked.len);    for (ranked[0..count], 0..) |workload_index, rank| {        const workload = report.workloads[workload_index];        try terminal.writeTextFmt("  {d}. {s}: {s}", .{ rank + 1, workload.name, workload.accounting.state });        if (workload.max_rss_bytes) |rss| try terminal.writeTextFmt(", rss {d} bytes", .{rss});        if (workload.trace) |trace| {            try terminal.writeTextFmt(                ", integrity {s}, retained {d} bytes, live {d} bytes, allocated {d} bytes",                .{                    trace.integrity.status,                    trace.summary.retained_bytes,                    trace.summary.live_bytes,                    trace.summary.allocated_bytes,                },            );        }        if (workload.accounting.unclassified_bytes) |bytes| try terminal.writeTextFmt(", unclassified {d} bytes", .{bytes});        if (workload.accounting.coverage_percent) |coverage| try terminal.writeTextFmt(", coverage {d:.2}%", .{coverage});        if (workload.trace_fault) |fault| {            try terminal.writeTextFmt(", trace invalid: {s} at {s}:{d}", .{ fault.reason, fault.path, fault.line });        }        try terminal.writeText("\n");        try writeWorkloadDetailsText(allocator, terminal, workload, options.top);    }}fn writeAllocationRankingsText(    allocator: std.mem.Allocator,    terminal: pretty_usage.Terminal,    workloads: []const Workload,    top: usize,) !void {    inline for (std.meta.tags(memtrace.analysis.Sort)) |sort| {        const ranked = try rankedTraceWorkloads(allocator, workloads, sort);        defer allocator.free(ranked);        if (ranked.len != 0) {            try terminal.writeTextFmt(                "allocation {s} ranking:\n",                .{sort.name()},            );            const count = @min(top, ranked.len);            for (ranked[0..count], 0..) |workload_index, rank| {                const workload = workloads[workload_index];                const summary = workload.trace.?.summary;                try terminal.writeTextFmt(                    "  {d}. {s}: allocated {d} bytes in {d} allocations, retained {d} bytes, lifetime byte-events total {d}, mean {d}, max {d}; duration events total {d}, mean {d}, max {d}\n",                    .{                        rank + 1,                        workload.name,                        summary.allocated_bytes,                        summary.allocations,                        summary.retained_bytes,                        summary.lifetime_total_byte_events,                        summary.lifetime_mean_byte_events,                        summary.lifetime_max_byte_events,                        summary.lifetime_total_events,                        summary.lifetime_mean_events,                        summary.lifetime_max_events,                    },                );            }        }    }}fn writeWorkloadDetailsText(allocator: std.mem.Allocator, terminal: pretty_usage.Terminal, workload: Workload, top: usize) !void {    if (workload.trace) |trace| {        if (trace.integrity.message) |message| {            try terminal.writeTextFmt(                "     trace caveat {s}: {s}; action {s}\n",                .{ trace.integrity.status, message, trace.integrity.action },            );        }        inline for (std.meta.tags(memtrace.analysis.Sort)) |sort| {            const ranking = trace.rankings.get(sort);            const owner_count = @min(top, ranking.owners.len);            if (owner_count != 0) {                try terminal.writeTextFmt(                    "     {s} owners:\n",                    .{sort.name()},                );            }            for (ranking.owners[0..owner_count]) |owner| {                try terminal.writeTextFmt(                    "       {s}: allocated {d} bytes in {d} allocations, retained {d} bytes, lifetime byte-events total {d}, mean {d}, max {d}; duration events total {d}, mean {d}, max {d}\n",                    .{                        owner.scope,                        owner.allocated_bytes,                        owner.allocations,                        owner.retained_bytes,                        owner.lifetime_total_byte_events,                        owner.lifetime_mean_byte_events,                        owner.lifetime_max_byte_events,                        owner.lifetime_total_events,                        owner.lifetime_mean_events,                        owner.lifetime_max_events,                    },                );            }            const source_count = @min(top, ranking.sources.len);            if (source_count != 0) {                try terminal.writeTextFmt(                    "     {s} sources:\n",                    .{sort.name()},                );            }            for (ranking.sources[0..source_count]) |source| {                try terminal.writeText("       ");                if (source.function) |function| {                    try terminal.writeText(function);                } else {                    try terminal.writeTextFmt(                        "return_address=0x{x}",                        .{source.return_address},                    );                }                if (source.location) |location| {                    if (location.len != 0) {                        try terminal.writeTextFmt(" at {s}", .{location});                    }                }                try terminal.writeTextFmt(                    ": allocated {d} bytes in {d} allocations, retained {d} bytes, lifetime byte-events total {d}, mean {d}, max {d}; duration events total {d}, mean {d}, max {d}\n",                    .{                        source.allocated_bytes,                        source.allocations,                        source.retained_bytes,                        source.lifetime_total_byte_events,                        source.lifetime_mean_byte_events,                        source.lifetime_max_byte_events,                        source.lifetime_total_events,                        source.lifetime_mean_events,                        source.lifetime_max_events,                    },                );            }        }    }    const metric_count = @min(top, workload.memory_metrics.len);    if (metric_count == 0) return;    try terminal.writeText("     metrics:\n");    const ranked = try rankedMetrics(allocator, workload.memory_metrics);    defer allocator.free(ranked);    for (ranked[0..metric_count]) |metric_index| {        const row = workload.memory_metrics[metric_index];        try terminal.writeTextFmt(            "       {s}: {d:.2} {s} ({s}, {d} sample(s))\n",            .{                row.label,                row.value,                row.unit.name(),                row.distribution.name(),                row.sample_count,            },        );    }}pub fn writeJson(    allocator: std.mem.Allocator,    writer: *std.Io.Writer,    report: Report,    options: Options,) !void {    var out = pretty_json.Writer.init(writer, .minified);    try out.beginObject();    try out.objectField("schema");    try out.write("tiny.profiling.memory-report/v1");    try out.objectField("run");    try out.beginObject();    try out.objectField("run_id");    try out.write(report.run.ref.run_id);    try out.objectField("root");    try out.write(report.run.ref.root);    try out.objectField("results");    try out.write(report.run.ref.results_path);    try out.endObject();    try out.objectField("totals");    try writeTotalsJson(&out, report.totals);    try out.objectField("allocation_rankings");    try writeAllocationRankingsJson(        allocator,        &out,        report.workloads,        options.top,    );    try out.objectField("workloads");    try out.beginArray();    for (report.workloads) |workload| try writeWorkloadJson(&out, workload, options);    try out.endArray();    try out.endObject();    try writer.writeByte('\n');}fn writeTotalsJson(out: *pretty_json.Writer, totals: Totals) !void {    try out.beginObject();    try out.objectField("workloads");    try out.write(totals.workloads);    try out.objectField("rss_workloads");    try out.write(totals.rss_workloads);    try out.objectField("traced_workloads");    try out.write(totals.traced_workloads);    try out.objectField("complete_traces");    try out.write(totals.complete_traces);    try out.objectField("partial_traces");    try out.write(totals.partial_traces);    try out.objectField("rss_bytes_sum");    try out.write(totals.rss_bytes_sum);    try out.objectField("attributed_retained_bytes_sum");    try out.write(totals.attributed_retained_bytes_sum);    try out.objectField("unclassified_bytes_sum");    try out.write(totals.unclassified_bytes_sum);    try out.objectField("invalid_traces");    try out.write(totals.invalid_traces);    try out.endObject();}fn writeAllocationRankingsJson(    allocator: std.mem.Allocator,    out: *pretty_json.Writer,    workloads: []const Workload,    top: usize,) !void {    try out.beginObject();    inline for (std.meta.tags(memtrace.analysis.Sort)) |sort| {        try out.objectField(sort.name());        try out.beginArray();        const ranked = try rankedTraceWorkloads(allocator, workloads, sort);        defer allocator.free(ranked);        const count = @min(top, ranked.len);        for (ranked[0..count], 0..) |workload_index, rank| {            const workload = workloads[workload_index];            const summary = workload.trace.?.summary;            try out.beginObject();            try out.objectField("rank");            try out.write(rank + 1);            try out.objectField("workload");            try out.write(workload.name);            try out.objectField("package");            try out.write(workload.package);            try out.objectField("allocated_bytes");            try out.write(summary.allocated_bytes);            try out.objectField("allocations");            try out.write(summary.allocations);            try out.objectField("retained_bytes");            try out.write(summary.retained_bytes);            try out.objectField("high_water_retained_bytes");            try out.write(summary.high_water_retained_bytes);            try out.objectField("lifetime_total_events");            try out.write(summary.lifetime_total_events);            try out.objectField("lifetime_mean_events");            try out.write(summary.lifetime_mean_events);            try out.objectField("lifetime_max_events");            try out.write(summary.lifetime_max_events);            try out.objectField("lifetime_total_byte_events");            try out.write(summary.lifetime_total_byte_events);            try out.objectField("lifetime_mean_byte_events");            try out.write(summary.lifetime_mean_byte_events);            try out.objectField("lifetime_max_byte_events");            try out.write(summary.lifetime_max_byte_events);            try out.endObject();        }        try out.endArray();    }    try out.endObject();}fn writeWorkloadJson(out: *pretty_json.Writer, workload: Workload, options: Options) !void {    try out.beginObject();    try out.objectField("name");    try out.write(workload.name);    try out.objectField("package");    try out.write(workload.package);    try out.objectField("step");    try out.write(workload.step);    try out.objectField("status");    try out.write(workload.status);    try out.objectField("max_rss_kib");    try out.write(workload.max_rss_kib);    try out.objectField("max_rss_bytes");    try out.write(workload.max_rss_bytes);    try out.objectField("trace_path");    try out.write(workload.trace_path);    try out.objectField("accounting");    try writeAccountingJson(out, workload.accounting);    try out.objectField("trace");    if (workload.trace) |trace| {        try writeTraceJson(out, trace, options.top);    } else {        try out.write(null);    }    try out.objectField("trace_fault");    if (workload.trace_fault) |fault| {        try out.beginObject();        try out.objectField("path");        try out.write(fault.path);        try out.objectField("line");        try out.write(fault.line);        try out.objectField("reason");        try out.write(fault.reason);        try out.endObject();    } else {        try out.write(null);    }    try out.objectField("memory_metrics");    try writeMetricsJson(out, workload.memory_metrics, options.top);    try out.endObject();}fn writeAccountingJson(out: *pretty_json.Writer, accounting: Accounting) !void {    try out.beginObject();    try out.objectField("state");    try out.write(accounting.state);    try out.objectField("rss_bytes");    try out.write(accounting.rss_bytes);    try out.objectField("attributed_retained_bytes");    try out.write(accounting.attributed_retained_bytes);    try out.objectField("unclassified_bytes");    try out.write(accounting.unclassified_bytes);    try out.objectField("coverage_percent");    try out.write(accounting.coverage_percent);    try out.endObject();}fn writeTraceJson(out: *pretty_json.Writer, trace: Trace, top: usize) !void {    try out.beginObject();    try out.objectField("path");    try out.write(trace.path);    try out.objectField("capture_integrity");    try writeTraceIntegrityJson(out, trace.integrity);    try out.objectField("summary");    try writeTraceSummaryJson(out, trace.summary);    try out.objectField("scopes");    try out.beginArray();    const count = @min(top, trace.scopes.len);    for (trace.scopes[0..count]) |scope| try writeScopeJson(out, scope);    try out.endArray();    try out.objectField("allocation_rankings");    try writeTraceRankingsJson(out, trace.rankings, top);    try out.endObject();}fn writeTraceRankingsJson(    out: *pretty_json.Writer,    rankings: TraceRankings,    top: usize,) !void {    try out.beginObject();    inline for (std.meta.tags(memtrace.analysis.Sort)) |sort| {        const ranking = rankings.get(sort);        try out.objectField(sort.name());        try out.beginObject();        try out.objectField("owners");        try out.beginArray();        const owner_count = @min(top, ranking.owners.len);        for (ranking.owners[0..owner_count]) |owner| {            try writeScopeJson(out, owner);        }        try out.endArray();        try out.objectField("sources");        try out.beginArray();        const source_count = @min(top, ranking.sources.len);        for (ranking.sources[0..source_count]) |source| {            try writeSourceJson(out, source);        }        try out.endArray();        try out.endObject();    }    try out.endObject();}fn writeTraceIntegrityJson(    out: *pretty_json.Writer,    integrity: memtrace.CaptureIntegrity,) !void {    try out.beginObject();    try out.objectField("method");    try out.write(memtrace.analysis.capture_integrity_method);    try out.objectField("status");    try out.write(integrity.status);    try out.objectField("action");    try out.write(integrity.action);    try out.objectField("message");    try out.write(integrity.message);    try out.objectField("event_count");    try out.write(integrity.event_count);    try out.objectField("sequenced_event_count");    try out.write(integrity.sequenced_event_count);    try out.objectField("unsequenced_event_count");    try out.write(integrity.unsequenced_event_count);    try out.objectField("first_sequence");    try out.write(integrity.first_sequence);    try out.objectField("last_sequence");    try out.write(integrity.last_sequence);    try out.objectField("sequence_gap_count");    try out.write(integrity.sequence_gap_count);    try out.objectField("missing_sequence_event_count");    try out.write(integrity.missing_sequence_event_count);    try out.objectField("sequence_regression_count");    try out.write(integrity.sequence_regression_count);    try out.objectField("start_event_count");    try out.write(integrity.start_event_count);    try out.objectField("stop_event_count");    try out.write(integrity.stop_event_count);    try out.objectField("start_sequence");    try out.write(integrity.start_sequence);    try out.objectField("stop_sequence");    try out.write(integrity.stop_sequence);    try out.objectField("unbalanced_event_count");    try out.write(integrity.unbalanced_event_count);    try out.endObject();}fn writeTraceSummaryJson(out: *pretty_json.Writer, summary: TraceSummary) !void {    try out.beginObject();    try out.objectField("events");    try out.write(summary.events);    try out.objectField("allocations");    try out.write(summary.allocations);    try out.objectField("frees");    try out.write(summary.frees);    try out.objectField("live_allocations");    try out.write(summary.live_allocations);    try out.objectField("allocated_bytes");    try out.write(summary.allocated_bytes);    try out.objectField("freed_bytes");    try out.write(summary.freed_bytes);    try out.objectField("live_bytes");    try out.write(summary.live_bytes);    try out.objectField("high_water_live_bytes");    try out.write(summary.high_water_live_bytes);    try out.objectField("retained_bytes");    try out.write(summary.retained_bytes);    try out.objectField("high_water_retained_bytes");    try out.write(summary.high_water_retained_bytes);    try out.objectField("completed_lifetimes");    try out.write(summary.completed_lifetimes);    try out.objectField("lifetime_total_events");    try out.write(summary.lifetime_total_events);    try out.objectField("lifetime_mean_events");    try out.write(summary.lifetime_mean_events);    try out.objectField("lifetime_max_events");    try out.write(summary.lifetime_max_events);    try out.objectField("lifetime_total_byte_events");    try out.write(summary.lifetime_total_byte_events);    try out.objectField("lifetime_mean_byte_events");    try out.write(summary.lifetime_mean_byte_events);    try out.objectField("lifetime_max_byte_events");    try out.write(summary.lifetime_max_byte_events);    try out.endObject();}fn writeScopeJson(out: *pretty_json.Writer, scope: Scope) !void {    try out.beginObject();    try out.objectField("scope");    try out.write(scope.scope);    try out.objectField("retained_bytes");    try out.write(scope.retained_bytes);    try out.objectField("high_water_retained_bytes");    try out.write(scope.high_water_retained_bytes);    try out.objectField("live_bytes");    try out.write(scope.live_bytes);    try out.objectField("high_water_live_bytes");    try out.write(scope.high_water_live_bytes);    try out.objectField("allocated_bytes");    try out.write(scope.allocated_bytes);    try out.objectField("freed_bytes");    try out.write(scope.freed_bytes);    try out.objectField("allocations");    try out.write(scope.allocations);    try out.objectField("frees");    try out.write(scope.frees);    try out.objectField("live_allocations");    try out.write(scope.live_allocations);    try out.objectField("completed_lifetimes");    try out.write(scope.completed_lifetimes);    try out.objectField("lifetime_total_events");    try out.write(scope.lifetime_total_events);    try out.objectField("lifetime_mean_events");    try out.write(scope.lifetime_mean_events);    try out.objectField("lifetime_max_events");    try out.write(scope.lifetime_max_events);    try out.objectField("lifetime_total_byte_events");    try out.write(scope.lifetime_total_byte_events);    try out.objectField("lifetime_mean_byte_events");    try out.write(scope.lifetime_mean_byte_events);    try out.objectField("lifetime_max_byte_events");    try out.write(scope.lifetime_max_byte_events);    try out.endObject();}fn writeSourceJson(out: *pretty_json.Writer, source: Source) !void {    try out.beginObject();    try out.objectField("return_address");    try out.write(source.return_address);    try out.objectField("function");    try out.write(source.function);    try out.objectField("location");    try out.write(source.location);    try out.objectField("retained_bytes");    try out.write(source.retained_bytes);    try out.objectField("high_water_retained_bytes");    try out.write(source.high_water_retained_bytes);    try out.objectField("live_bytes");    try out.write(source.live_bytes);    try out.objectField("high_water_live_bytes");    try out.write(source.high_water_live_bytes);    try out.objectField("allocated_bytes");    try out.write(source.allocated_bytes);    try out.objectField("freed_bytes");    try out.write(source.freed_bytes);    try out.objectField("allocations");    try out.write(source.allocations);    try out.objectField("frees");    try out.write(source.frees);    try out.objectField("live_allocations");    try out.write(source.live_allocations);    try out.objectField("completed_lifetimes");    try out.write(source.completed_lifetimes);    try out.objectField("lifetime_total_events");    try out.write(source.lifetime_total_events);    try out.objectField("lifetime_mean_events");    try out.write(source.lifetime_mean_events);    try out.objectField("lifetime_max_events");    try out.write(source.lifetime_max_events);    try out.objectField("lifetime_total_byte_events");    try out.write(source.lifetime_total_byte_events);    try out.objectField("lifetime_mean_byte_events");    try out.write(source.lifetime_mean_byte_events);    try out.objectField("lifetime_max_byte_events");    try out.write(source.lifetime_max_byte_events);    try out.endObject();}fn writeMetricsJson(out: *pretty_json.Writer, metrics: []const memory.Metric, top: usize) !void {    try out.beginArray();    const count = @min(top, metrics.len);    for (metrics[0..count]) |row| {        try out.beginObject();        try out.objectField("key");        try out.write(row.key);        try out.objectField("label");        try out.write(row.label);        try out.objectField("unit");        try out.write(row.unit.name());        try out.objectField("value");        try out.write(row.value);        try out.objectField("sample_count");        try out.write(row.sample_count);        try out.objectField("distribution");        try out.write(row.distribution.name());        try out.objectField("source_kind");        try out.write(row.source_kind);        try out.objectField("source_path");        try out.write(row.source_path);        try out.objectField("source_line");        try out.write(row.source_line);        try out.endObject();    }    try out.endArray();}fn workload_retained_score(workload: Workload) u64 {    if (workload.accounting.unclassified_bytes) |bytes| return bytes;    if (workload.max_rss_bytes) |bytes| return bytes;    if (workload.trace) |trace| return trace.summary.retained_bytes;    return 0;}fn workload_retained_descending(items: []const Workload, left: usize, right: usize) bool {    const left_score = workload_retained_score(items[left]);    const right_score = workload_retained_score(items[right]);    if (left_score == right_score) return std.mem.lessThan(u8, items[left].name, items[right].name);    return left_score > right_score;}fn metric_value_descending(items: []const memory.Metric, left: usize, right: usize) bool {    if (items[left].value == items[right].value) {        return std.mem.lessThan(u8, items[left].label, items[right].label);    }    return items[left].value > items[right].value;}const TraceRankContext = struct {    workloads: []const Workload,    sort: memtrace.analysis.Sort,};fn trace_workload_descending(    context: TraceRankContext,    left: usize,    right: usize,) bool {    const left_summary = context.workloads[left].trace.?.summary;    const right_summary = context.workloads[right].trace.?.summary;    if (traceSummaryOrder(        context.sort,        left_summary,        right_summary,    )) |order| {        return order;    }    return std.mem.lessThan(        u8,        context.workloads[left].name,        context.workloads[right].name,    );}fn traceSummaryOrder(    sort: memtrace.analysis.Sort,    left: TraceSummary,    right: TraceSummary,) ?bool {    switch (sort) {        .retained => {            if (left.retained_bytes != right.retained_bytes) {                return left.retained_bytes > right.retained_bytes;            }            if (left.high_water_retained_bytes !=                right.high_water_retained_bytes)            {                return left.high_water_retained_bytes >                    right.high_water_retained_bytes;            }        },        .traffic => {            if (left.allocated_bytes != right.allocated_bytes) {                return left.allocated_bytes > right.allocated_bytes;            }            if (left.allocations != right.allocations) {                return left.allocations > right.allocations;            }        },        .lifetime => {            if (left.lifetime_total_byte_events !=                right.lifetime_total_byte_events)            {                return left.lifetime_total_byte_events >                    right.lifetime_total_byte_events;            }            if (left.lifetime_total_events != right.lifetime_total_events) {                return left.lifetime_total_events >                    right.lifetime_total_events;            }            if (left.lifetime_max_events != right.lifetime_max_events) {                return left.lifetime_max_events > right.lifetime_max_events;            }            if (left.lifetime_mean_events != right.lifetime_mean_events) {                return left.lifetime_mean_events > right.lifetime_mean_events;            }        },    }    if (left.retained_bytes != right.retained_bytes) {        return left.retained_bytes > right.retained_bytes;    }    if (left.allocated_bytes != right.allocated_bytes) {        return left.allocated_bytes > right.allocated_bytes;    }    if (left.lifetime_total_byte_events !=        right.lifetime_total_byte_events)    {        return left.lifetime_total_byte_events >            right.lifetime_total_byte_events;    }    if (left.lifetime_total_events != right.lifetime_total_events) {        return left.lifetime_total_events > right.lifetime_total_events;    }    return null;}fn rankedTraceWorkloads(    allocator: std.mem.Allocator,    workloads: []const Workload,    sort: memtrace.analysis.Sort,) ![]usize {    var indices: std.ArrayList(usize) = .empty;    for (workloads, 0..) |workload, index| {        const trace = workload.trace orelse continue;        if (!std.mem.eql(u8, trace.integrity.status, "complete")) continue;        try indices.append(allocator, index);    }    const owned = try indices.toOwnedSlice(allocator);    std.mem.sort(        usize,        owned,        TraceRankContext{ .workloads = workloads, .sort = sort },        trace_workload_descending,    );    return owned;}fn rankedWorkloads(allocator: std.mem.Allocator, workloads: []const Workload) ![]usize {    const indices = try allocator.alloc(usize, workloads.len);    for (indices, 0..) |*slot, index| slot.* = index;    std.mem.sort(usize, indices, workloads, workload_retained_descending);    return indices;}fn rankedMetrics(allocator: std.mem.Allocator, metrics: []const memory.Metric) ![]usize {    const indices = try allocator.alloc(usize, metrics.len);    for (indices, 0..) |*slot, index| slot.* = index;    std.mem.sort(usize, indices, metrics, metric_value_descending);    return indices;}fn fixtureIntegrity(status: []const u8) memtrace.CaptureIntegrity {    return .{        .status = status,        .action = "inspect trace integrity",        .message = null,        .event_count = 0,        .sequenced_event_count = 0,        .unsequenced_event_count = 0,        .first_sequence = null,        .last_sequence = null,        .sequence_gap_count = 0,        .missing_sequence_event_count = 0,        .sequence_regression_count = 0,        .start_event_count = 0,        .stop_event_count = 0,        .start_sequence = null,        .stop_sequence = null,        .unbalanced_event_count = 0,    };}fn fixtureRankWorkload(    name: []const u8,    integrity_status: []const u8,    summary: TraceSummary,) Workload {    const empty_ranking = TraceRanking{        .owners = &.{},        .sources = &.{},    };    return .{        .name = name,        .package = "lib/fixture",        .step = "fixture-bench",        .status = "passed",        .max_rss_kib = null,        .max_rss_bytes = null,        .trace_path = name,        .trace = .{            .path = name,            .integrity = fixtureIntegrity(integrity_status),            .summary = summary,            .scopes = &.{},            .rankings = .{                .retained = empty_ranking,                .traffic = empty_ranking,                .lifetime = empty_ranking,            },        },        .trace_fault = null,        .memory_metrics = &.{},        .accounting = .{            .state = "measured",            .rss_bytes = null,            .attributed_retained_bytes = null,            .unclassified_bytes = null,            .coverage_percent = null,        },    };}test "profiling memory rankings separate allocation pressure dimensions" {    const workloads = [_]Workload{        fixtureRankWorkload("retained", "complete", .{            .retained_bytes = 300,            .allocated_bytes = 100,            .allocations = 10,            .lifetime_total_events = 10,            .lifetime_total_byte_events = 100,        }),        fixtureRankWorkload("traffic", "complete", .{            .retained_bytes = 20,            .allocated_bytes = 500,            .allocations = 50,            .lifetime_total_events = 20,            .lifetime_total_byte_events = 200,        }),        fixtureRankWorkload("lifetime", "complete", .{            .retained_bytes = 10,            .allocated_bytes = 20,            .allocations = 2,            .lifetime_total_events = 1,            .lifetime_total_byte_events = 900,        }),        fixtureRankWorkload("partial", "missing_stop_event", .{            .retained_bytes = 1000,            .allocated_bytes = 1000,            .allocations = 1000,            .lifetime_total_events = 1000,            .lifetime_total_byte_events = 1000,        }),    };    const retained = try rankedTraceWorkloads(        std.testing.allocator,        &workloads,        .retained,    );    defer std.testing.allocator.free(retained);    const traffic = try rankedTraceWorkloads(        std.testing.allocator,        &workloads,        .traffic,    );    defer std.testing.allocator.free(traffic);    const lifetime = try rankedTraceWorkloads(        std.testing.allocator,        &workloads,        .lifetime,    );    defer std.testing.allocator.free(lifetime);    try std.testing.expectEqual(@as(usize, 3), retained.len);    try std.testing.expectEqualStrings("retained", workloads[retained[0]].name);    try std.testing.expectEqualStrings("traffic", workloads[traffic[0]].name);    try std.testing.expectEqualStrings("lifetime", workloads[lifetime[0]].name);}const fixture_trace_start =    "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":1,\"kind\":\"trace.start\"}\n";const fixture_allocator =    "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":2,\"kind\":\"allocator\",\"allocator_id\":1," ++    "\"retains_freed_memory\":true}\n";const fixture_allocation =    "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":3,\"kind\":\"alloc\",\"allocator_id\":1," ++    "\"allocation_id\":1,\"address\":128,\"len\":64,\"scope\":\"root/phase\"," ++    "\"return_address\":2748}\n";const fixture_free =    "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":4,\"kind\":\"free\",\"allocator_id\":1," ++    "\"allocation_id\":1,\"address\":128,\"len\":64,\"scope\":\"root/phase\"," ++    "\"return_address\":2748}\n";const fixture_trace_stop_four =    "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":4,\"kind\":\"trace.stop\"}\n";const fixture_trace_stop_five =    "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":5,\"kind\":\"trace.stop\"}\n";fn reportFixtureWorkloadRow(    allocator: std.mem.Allocator,    run_id: []const u8,    name: []const u8,    package: []const u8,    step: []const u8,) ![]u8 {    var bytes: std.Io.Writer.Allocating = .init(allocator);    errdefer bytes.deinit();    var json_writer = pretty_json.Writer.init(&bytes.writer, .minified);    const row = try json_writer.object();    try row.fields(.{        .schema = record.workload_schema,        .event = "workload",        .run_id = run_id,    });    try row.field("workload", .{        .name = name,        .package = package,        .step = step,        .surface = "profile",    });    try row.fields(.{        .scope = "whole-step",        .causal = false,        .tracy = false,        .trace_allocations = true,    });    try row.field("status", .{        .state = "passed",        .exit_code = @as(u8, 0),    });    try row.field("timing", .{ .wall_ns = @as(u8, 10) });    try row.field("resources", .{ .max_rss_kib = @as(u8, 1) });    const workload_root = try std.fmt.allocPrint(        allocator,        "zig-out/profiling/{s}/workloads/{s}",        .{ run_id, name },    );    defer allocator.free(workload_root);    const measurement_row = try row.object("measurement");    try measurement_row.fields(.{        .execution_count = @as(u8, 1),        .acquisition_order = "fixed_plan_order",        .workload_output_retention = "all_measured_executions",        .measured_artifact_layout = "workload_root",        .allocation_summary_retention = "disabled",    });    try measurement_row.field("benchmark_process_domain", .{        .benchmark_surface = false,        .direct_execution = false,        .host_profiler = false,        .tracy = false,        .causal = false,        .allocation_trace = true,        .capture_control = false,    });    try measurement_row.field("benchmark_process_reduction", .{        .schema = reducer.schema,        .state = "not_applicable",        .process_count = @as(u8, 1),        .reason = "outside_direct_benchmark_domain",    });    const executions = try measurement_row.array("executions");    const execution = try executions.object();    try execution.fields(.{        .index = @as(u8, 1),        .spawn_state = "spawned",        .pid = @as(u8, 101),        .exit_code = @as(u8, 0),        .wall_ns = @as(u8, 10),    });    const artifacts = try execution.object("artifacts");    try artifacts.field("root", workload_root);    inline for (.{        .{ "stdout", "/stdout.txt" },        .{ "stderr", "/stderr.txt" },        .{ "bench_jsonl", "/bench.jsonl" },        .{ "coz_jsonl", "/bench.coz.jsonl" },        .{ "coz_analysis", "/bench.coz.analysis.json" },        .{ "tracy_jsonl", "/bench.tracy.jsonl" },        .{ "tracy_summary", "/bench.tracy.summary.jsonl" },    }) |field| {        try artifacts.stringParts(            field[0],            &.{ workload_root, field[1] },        );    }    try artifacts.stringParts(        "allocations",        &.{ workload_root, "/allocations.jsonl" },    );    try artifacts.stringParts(        "structured",        &.{ workload_root, "/structured.jsonl" },    );    try artifacts.fields(.{        .structured_rows = @as(u8, 0),        .structured_parse_errors = @as(u8, 0),    });    try artifacts.end();    try execution.end();    try executions.end();    try measurement_row.end();    const workload_artifacts = try row.object("artifacts");    try workload_artifacts.field("root", workload_root);    try workload_artifacts.end();    try row.endLine();    return try bytes.toOwnedSlice();}test "profiling memory report accounts rss against retained trace bytes" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const base = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const root = try std.fs.path.join(allocator, &.{ base, "run-a" });    const workload_dir = try std.fs.path.join(allocator, &.{ root, "workloads", "w" });    try sys.fs.createDirPath(workload_dir);    const manifest_path = try std.fs.path.join(allocator, &.{ root, "manifest.json" });    try sys.fs.writeFile(manifest_path,        \\{"schema":"tiny.profiling.run/v1","event":"manifest","run_id":"run-a","selection":{"suite":"smoke","filters":[]},"artifacts":{"root":"zig-out/profiling/run-a","results":"zig-out/profiling/run-a/results.jsonl","manifest":"zig-out/profiling/run-a/manifest.json"}}        \\    );    const results_path = try std.fs.path.join(allocator, &.{ root, "results.jsonl" });    const run_start =        \\{"schema":"tiny.profiling.run/v1","event":"run_start","run_id":"run-a","selection":{"suite":"smoke","filters":[]},"artifacts":{"root":"zig-out/profiling/run-a"}}        \\    ;    const workload_row = try reportFixtureWorkloadRow(allocator, "run-a", "w", "lib/w", "w-bench");    const results = try std.mem.concat(allocator, u8, &.{ run_start, workload_row });    try sys.fs.writeFile(results_path, results);    const allocations_path = try std.fs.path.join(allocator, &.{ workload_dir, "allocations.jsonl" });    try sys.fs.writeFile(        allocations_path,        fixture_trace_start ++ fixture_allocator ++ fixture_allocation ++            fixture_free ++ fixture_trace_stop_five,    );    const report = try load(allocator, .{ .input = root, .top = 4 });    try std.testing.expectEqual(@as(usize, 1), report.workloads.len);    try std.testing.expectEqualStrings("measured", report.workloads[0].accounting.state);    try std.testing.expectEqual(@as(u64, 1024), report.workloads[0].accounting.rss_bytes.?);    try std.testing.expectEqual(@as(u64, 64), report.workloads[0].accounting.attributed_retained_bytes.?);    try std.testing.expectEqual(@as(u64, 960), report.workloads[0].accounting.unclassified_bytes.?);    try std.testing.expectEqual(@as(u64, 64), report.workloads[0].trace.?.summary.retained_bytes);    try std.testing.expectEqual(        @as(u64, 64),        report.workloads[0].trace.?.summary.lifetime_total_byte_events,    );    try std.testing.expectEqualStrings("root/phase", report.workloads[0].trace.?.scopes[0].scope);    inline for (std.meta.tags(memtrace.analysis.Sort)) |sort| {        const ranking = report.workloads[0].trace.?.rankings.get(sort);        try std.testing.expectEqual(@as(usize, 1), ranking.owners.len);        try std.testing.expectEqualStrings(            "root/phase",            ranking.owners[0].scope,        );        try std.testing.expectEqual(@as(usize, 1), ranking.sources.len);        try std.testing.expectEqual(            @as(u64, 2748),            ranking.sources[0].return_address,        );        try std.testing.expectEqual(            @as(u64, 64),            ranking.sources[0].allocated_bytes,        );        try std.testing.expect(ranking.sources[0].function == null);        try std.testing.expect(ranking.sources[0].location == null);        try std.testing.expectEqual(            @as(u64, 1),            ranking.sources[0].completed_lifetimes,        );    }    try std.testing.expectEqual(        @as(u64, 64),        report.workloads[0].trace.?.rankings.lifetime.sources[0]            .lifetime_total_byte_events,    );    var json_out: std.Io.Writer.Allocating = .init(allocator);    try writeJson(        allocator,        &json_out.writer,        report,        .{ .input = root, .top = 4 },    );    const rendered = try json_out.toOwnedSlice();    try std.testing.expect(std.mem.indexOf(        u8,        rendered,        "\"allocation_rankings\":{\"retained\":[{\"rank\":1,\"workload\":\"w\"",    ) != null);    try std.testing.expect(std.mem.indexOf(        u8,        rendered,        "\"traffic\":{\"owners\":[{\"scope\":\"root/phase\"",    ) != null);    try std.testing.expect(std.mem.indexOf(        u8,        rendered,        "\"lifetime\":{\"owners\":[{\"scope\":\"root/phase\"",    ) != null);    try std.testing.expect(std.mem.indexOf(        u8,        rendered,        "\"sources\":[{\"return_address\":2748",    ) != null);    try std.testing.expect(std.mem.indexOf(        u8,        rendered,        "\"lifetime_total_byte_events\":64",    ) != null);    try sys.fs.writeFile(        allocations_path,        fixture_trace_start ++ fixture_allocator ++ fixture_allocation,    );    const partial = try load(allocator, .{ .input = root, .top = 4 });    try std.testing.expectEqualStrings("trace_partial", partial.workloads[0].accounting.state);    try std.testing.expectEqualStrings(        "missing_stop_event",        partial.workloads[0].trace.?.integrity.status,    );    try std.testing.expect(partial.workloads[0].accounting.coverage_percent == null);}test "profiling memory report names invalid trace rows instead of aborting" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const base = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const root = try std.fs.path.join(allocator, &.{ base, "run-b" });    const good_dir = try std.fs.path.join(allocator, &.{ root, "workloads", "good" });    const bad_dir = try std.fs.path.join(allocator, &.{ root, "workloads", "bad" });    try sys.fs.createDirPath(good_dir);    try sys.fs.createDirPath(bad_dir);    const manifest_path = try std.fs.path.join(allocator, &.{ root, "manifest.json" });    try sys.fs.writeFile(manifest_path,        \\{"schema":"tiny.profiling.run/v1","event":"manifest","run_id":"run-b","selection":{"suite":"smoke","filters":[]},"artifacts":{"root":"zig-out/profiling/run-b","results":"zig-out/profiling/run-b/results.jsonl","manifest":"zig-out/profiling/run-b/manifest.json"}}        \\    );    const results_path = try std.fs.path.join(allocator, &.{ root, "results.jsonl" });    const run_start =        \\{"schema":"tiny.profiling.run/v1","event":"run_start","run_id":"run-b","selection":{"suite":"smoke","filters":[]},"artifacts":{"root":"zig-out/profiling/run-b"}}        \\    ;    const good_row = try reportFixtureWorkloadRow(        allocator,        "run-b",        "good",        "lib/good",        "good-bench",    );    const bad_row = try reportFixtureWorkloadRow(allocator, "run-b", "bad", "lib/bad", "bad-bench");    const results = try std.mem.concat(allocator, u8, &.{ run_start, good_row, bad_row });    try sys.fs.writeFile(results_path, results);    const good_trace = try std.fs.path.join(allocator, &.{ good_dir, "allocations.jsonl" });    try sys.fs.writeFile(        good_trace,        fixture_trace_start ++ fixture_allocator ++ fixture_allocation ++            fixture_trace_stop_four,    );    const bad_trace = try std.fs.path.join(allocator, &.{ bad_dir, "allocations.jsonl" });    try sys.fs.writeFile(        bad_trace,        "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":1,\"kind\":\"trace.start\"}\n" ++            "{\"v\":" ++ fixture_trace_version ++ ",\"seq\":2,\"kind\":\"mystery\"}\n",    );    const report = try load(allocator, .{ .input = root, .top = 4 });    try std.testing.expectEqual(@as(usize, 2), report.workloads.len);    try std.testing.expectEqual(@as(usize, 1), report.totals.invalid_traces);    try std.testing.expectEqual(@as(usize, 1), report.totals.traced_workloads);    try std.testing.expectEqual(@as(usize, 1), report.totals.complete_traces);    const good = report.workloads[0];    try std.testing.expectEqualStrings("good", good.name);    try std.testing.expect(good.trace_fault == null);    try std.testing.expect(good.trace != null);    const bad = report.workloads[1];    try std.testing.expectEqualStrings("bad", bad.name);    try std.testing.expectEqualStrings("trace_invalid", bad.accounting.state);    try std.testing.expect(bad.trace == null);    const fault = bad.trace_fault.?;    try std.testing.expectEqual(@as(u64, 2), fault.line);    try std.testing.expectEqualStrings("InvalidEventJson", fault.reason);}test "profiling memory report renders json schema" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const run_value = analyze.Run{        .ref = .{ .run_id = "run-a", .root = "root", .manifest_path = "root/manifest.json", .results_path = "root/results.jsonl" },        .workloads = &.{},    };    const report = Report{ .run = run_value, .workloads = &.{}, .totals = .{} };    var out: std.Io.Writer.Allocating = .init(allocator);    const options = Options{ .input = "root" };    try writeJson(allocator, &out.writer, report, options);    const rendered = try out.toOwnedSlice();    defer allocator.free(rendered);    try std.testing.expect(std.mem.indexOf(u8, rendered, "\"schema\":\"tiny.profiling.memory-report/v1\"") != null);}

Source: src/profiling/report/root.zig:14

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

Audit

Definitions18
Public names18
Members46
Version26.7.0
Revisiondaab053ee433