Skip to documentation
SLOP

tiny.profiling.host.counters

Reference tiny.profiling host counters

Defined in host.

API (14)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallsprivate; no linksrc.profiling.commandplannedMeasuredArtifactCounttest; no linksrc.profiling.commandtest: profiling CLI falls back from u...private; no linksrc.profiling.commandwriteRunDryRunCounterMetadatatest; no linksrc.profiling.driver.testtest: profiling driver resets success...test; no linksrc.profiling.host.counterstest: profiling counters classify a f...+14 moreprivate; no linksrc.profiling.host.countersattachGroupCsvPathsprivate; no linksrc.profiling.host.countersplanRepetitionCsvPathsprivate; no linksrc.profiling.host.countersplanRunshost.countersplan
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallstest; no linksrc.profiling.host.counterstest: profiling counters classify a f...test; no linksrc.profiling.host.counterstest: profiling counters leave ready ...test; no linksrc.profiling.host.counterstest: profiling counters report missi...test; no linksrc.profiling.host.counterstest: profiling counters summarize a ...test; no linksrc.profiling.host.counterstest: profiling counters summarize gr...+5 moreprivate; no linksrc.profiling.host.countersaddSystemWideCaveatprivate; no linksrc.profiling.host.countersfailedprivate; no linksrc.profiling.host.countersmergeLaneSummariesprivate; no linksrc.profiling.host.countersmissingToolhost.counterssummarize
Static calls · unresolved targets: 1 · external targets: 3.
Called byCallstest; no linksrc.profiling.host.counterstest: profiling counters wrap workloa...test; no linksrc.profiling.host.counterstest: profiling energy counters use s...hostwraphost.counterswrapRunArgvhost.counterswrapArgv
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsdriver.measurerunMeasuredWorkloadtest; no linksrc.profiling.host.counterstest: profiling counters plan bounded...host.counterswrapArgvhost.counterswrapRunArgv
Static calls · unresolved targets: 0 · external targets: 2.

Source: src/profiling/host/counters.zig

zig
const std = @import("std");const capture = @import("capture");const sys = @import("sys");const root = @import("root.zig");pub const tool = "perf";pub const kind = "perf_stat";pub const default_events =    "page-faults,{cycles,instructions,task-clock},{branches,branch-misses}";pub const energy_events = "duration_time,power/energy-pkg/";pub const default_repeat: u32 = 3;pub const max_group_size: u32 = 100;const max_capture_bytes = 64 * 1024 * 1024;pub const Options = struct {    events: []const u8 = default_events,    repeat: u32 = default_repeat,    group_size: u32 = 0,    system_wide: bool = false,    pub fn validate(self: Options) !void {        if (std.mem.trim(u8, self.events, " \t\r\n").len == 0) return error.InvalidCounterEvents;        if (!root.process.validRepeat(self.repeat)) return error.InvalidCounterRepeat;        if (self.group_size > max_group_size) return error.InvalidCounterGroupSize;    }};pub const Run = struct {    group_index: u32,    repetition_index: u32,    events: []const u8,    csv_path: []const u8,};pub const Lane = struct {    csv_path: []const u8,    summary_path: []const u8,    event_groups: []const capture.perfstat.EventGroup,    event_expression: []const u8,    kernel_event_groups: []const capture.perfstat.KernelEventGroup,    runs: []const Run,    repetition_csv_paths: []const []const u8,    system_wide: bool,};pub fn plan(allocator: std.mem.Allocator, options: Options, workload_root: []const u8) !Lane {    try options.validate();    const csv_path = try std.fs.path.join(allocator, &.{ workload_root, "perf.stat.csv" });    const event_expression = try capture.perfstat.normalizeEventExpression(        allocator,        options.events,    );    const groups = try capture.perfstat.eventGroups(        allocator,        event_expression,        options.group_size,    );    const grouped = groups.len > 1;    const repeated = options.repeat > 1;    const runs = try planRuns(allocator, options, workload_root, groups);    const repetition_csv_paths = try planRepetitionCsvPaths(        allocator,        workload_root,        runs,        options.repeat,        grouped,        repeated,    );    try attachGroupCsvPaths(        allocator,        workload_root,        csv_path,        groups,        runs,        grouped,        repeated,    );    return .{        .csv_path = csv_path,        .summary_path = try std.fs.path.join(            allocator,            &.{ workload_root, "perf.stat.summary.json" },        ),        .event_groups = groups,        .event_expression = event_expression,        .kernel_event_groups = try capture.perfstat.kernelEventGroups(            allocator,            event_expression,        ),        .runs = runs,        .repetition_csv_paths = repetition_csv_paths,        .system_wide = options.system_wide,    };}fn planRuns(    allocator: std.mem.Allocator,    options: Options,    workload_root: []const u8,    groups: []const capture.perfstat.EventGroup,) ![]const Run {    const execution_count = std.math.mul(        usize,        groups.len,        @as(usize, options.repeat),    ) catch return error.TooManyCounterExecutions;    if (execution_count > root.process.max_executions) return error.TooManyCounterExecutions;    const grouped = groups.len > 1;    const repeated = options.repeat > 1;    const runs = try allocator.alloc(Run, execution_count);    var run_index: usize = 0;    for (0..options.repeat) |repetition_index| {        for (groups) |group| {            const name = try runCsvName(                allocator,                grouped,                repeated,                group.index,                @intCast(repetition_index + 1),            );            runs[run_index] = .{                .group_index = group.index,                .repetition_index = @intCast(repetition_index + 1),                .events = group.events,                .csv_path = try std.fs.path.join(allocator, &.{ workload_root, name }),            };            run_index += 1;        }    }    return runs;}fn planRepetitionCsvPaths(    allocator: std.mem.Allocator,    workload_root: []const u8,    runs: []const Run,    repeat: u32,    grouped: bool,    repeated: bool,) ![]const []const u8 {    const repetition_csv_paths = try allocator.alloc(        []const u8,        if (repeated) repeat else 0,    );    for (repetition_csv_paths, 0..) |*csv_path, repetition_index| {        if (grouped) {            const name = try std.fmt.allocPrint(                allocator,                "perf.stat.repeat.{d}.csv",                .{repetition_index + 1},            );            csv_path.* = try std.fs.path.join(allocator, &.{ workload_root, name });        } else {            csv_path.* = runs[repetition_index].csv_path;        }    }    return repetition_csv_paths;}fn attachGroupCsvPaths(    allocator: std.mem.Allocator,    workload_root: []const u8,    csv_path: []const u8,    groups: []capture.perfstat.EventGroup,    runs: []const Run,    grouped: bool,    repeated: bool,) !void {    for (groups, 0..) |*group, group_index| {        if (!grouped) {            group.csv_path = csv_path;        } else if (!repeated) {            group.csv_path = runs[group_index].csv_path;        } else {            const name = try std.fmt.allocPrint(                allocator,                "perf.stat.group.{d}.csv",                .{group.index},            );            group.csv_path = try std.fs.path.join(allocator, &.{ workload_root, name });        }    }}fn runCsvName(    allocator: std.mem.Allocator,    grouped: bool,    repeated: bool,    group_index: u32,    repetition_index: u32,) ![]const u8 {    if (grouped and repeated) return try std.fmt.allocPrint(        allocator,        "perf.stat.group.{d}.repeat.{d}.csv",        .{ group_index, repetition_index },    );    if (grouped) return try std.fmt.allocPrint(        allocator,        "perf.stat.group.{d}.csv",        .{group_index},    );    if (repeated) return try std.fmt.allocPrint(        allocator,        "perf.stat.repeat.{d}.csv",        .{repetition_index},    );    return "perf.stat.csv";}pub fn wrapArgv(allocator: std.mem.Allocator, options: Options, argv: []const []const u8, lane: Lane) ![]const []const u8 {    return try wrapRunArgv(allocator, options, argv, lane, 0);}pub fn wrapRunArgv(    allocator: std.mem.Allocator,    options: Options,    argv: []const []const u8,    lane: Lane,    run_index: usize,) ![]const []const u8 {    try options.validate();    std.debug.assert(run_index < lane.runs.len);    const run = lane.runs[run_index];    return try capture.perfstat.command(        allocator,        tool,        argv,        run.csv_path,        run.events,        .{ .system_wide = options.system_wide },    );}pub fn summarize(    allocator: std.mem.Allocator,    lane: Lane,    exit_code: i64,    stderr_path: []const u8,    tool_available: bool,) !root.Capture {    if (!tool_available) return missingTool(lane);    var summaries: std.ArrayList(capture.perfstat.Summary) = .empty;    for (lane.runs) |run| {        const summary = capture.perfstat.parseCsv(allocator, run.csv_path) catch |err| switch (err) {            error.FileNotFound => return try failed(allocator, lane, exit_code, stderr_path),            else => |actual| return actual,        };        if (summary.counters.len == 0) return try failed(allocator, lane, exit_code, stderr_path);        try summaries.append(allocator, summary);    }    var summary = try mergeLaneSummaries(allocator, lane, summaries.items);    summary.event_expression = lane.event_expression;    summary.kernel_event_groups = lane.kernel_event_groups;    if (lane.system_wide) summary = try addSystemWideCaveat(allocator, summary);    try capture.perfstat.writeSummaryFile(lane.summary_path, summary);    const evidence_caveat = capture.perfstat.counterEvidenceCaveat(summary);    return .{        .kind = kind,        .tool = tool,        .capture_path = lane.csv_path,        .summary_path = lane.summary_path,        .state = "summary_written",        .caveat_kind = if (evidence_caveat) |row| row.kind else null,        .caveat_message = if (evidence_caveat) |row| row.message else null,    };}fn mergeLaneSummaries(    allocator: std.mem.Allocator,    lane: Lane,    summaries: []const capture.perfstat.Summary,) !capture.perfstat.Summary {    const grouped = lane.event_groups.len > 1;    const repeated = lane.repetition_csv_paths.len > 1;    if (!grouped and !repeated) return summaries[0];    try writeCombinedCsv(allocator, lane.csv_path, summaries);    if (!grouped) return try capture.perfstat.mergeRepeatedSummaries(        allocator,        lane.csv_path,        summaries,    );    if (!repeated) return try capture.perfstat.mergeSummaries(        allocator,        lane.csv_path,        lane.event_groups,        summaries,    );    return try mergeGroupedRepetitions(allocator, lane, summaries);}fn mergeGroupedRepetitions(    allocator: std.mem.Allocator,    lane: Lane,    summaries: []const capture.perfstat.Summary,) !capture.perfstat.Summary {    const group_count = lane.event_groups.len;    var repetition_summaries: std.ArrayList(capture.perfstat.Summary) = .empty;    for (lane.repetition_csv_paths, 0..) |csv_path, repetition_index| {        const start = repetition_index * group_count;        const end = start + group_count;        try writeCombinedCsv(allocator, csv_path, summaries[start..end]);        try repetition_summaries.append(            allocator,            try capture.perfstat.mergeSummaries(                allocator,                csv_path,                try repetitionGroups(allocator, lane, repetition_index),                summaries[start..end],            ),        );    }    var group_summaries: std.ArrayList(capture.perfstat.Summary) = .empty;    for (lane.event_groups, 0..) |group, group_index| {        group_summaries.clearRetainingCapacity();        for (0..lane.repetition_csv_paths.len) |repetition_index| {            try group_summaries.append(                allocator,                summaries[repetition_index * group_count + group_index],            );        }        try writeCombinedCsv(allocator, group.csv_path, group_summaries.items);    }    return try capture.perfstat.mergeGroupedRepeatedSummaries(        allocator,        lane.csv_path,        lane.event_groups,        repetition_summaries.items,    );}fn addSystemWideCaveat(    allocator: std.mem.Allocator,    summary: capture.perfstat.Summary,) !capture.perfstat.Summary {    var result = summary;    const caveats = try allocator.alloc(capture.perfstat.Caveat, summary.caveats.len + 1);    @memcpy(caveats[0..summary.caveats.len], summary.caveats);    caveats[summary.caveats.len] = .{        .event = "perf.stat",        .kind = "system_wide_scope",        .message = "system-wide counters include concurrent host activity and " ++            "are not attributed exclusively to the workload process",        .value_text = "",        .running_percent = null,    };    result.caveats = caveats;    return result;}fn repetitionGroups(    allocator: std.mem.Allocator,    lane: Lane,    repetition_index: usize,) ![]const capture.perfstat.EventGroup {    const groups = try allocator.alloc(capture.perfstat.EventGroup, lane.event_groups.len);    const start = repetition_index * lane.event_groups.len;    for (groups, lane.event_groups, 0..) |*destination, source, group_index| {        destination.* = source;        destination.csv_path = lane.runs[start + group_index].csv_path;    }    return groups;}fn writeCombinedCsv(    allocator: std.mem.Allocator,    path: []const u8,    summaries: []const capture.perfstat.Summary,) !void {    var file = try sys.fs.cwd().createFile(std.Options.debug_io, path, .{ .truncate = true });    defer file.close(std.Options.debug_io);    var buffer: [8192]u8 = undefined;    var file_writer = file.writer(std.Options.debug_io, &buffer);    const writer = &file_writer.interface;    for (summaries) |summary| {        const text = try sys.fs.readFileAlloc(allocator, summary.csv_path, max_capture_bytes);        defer allocator.free(text);        try writer.writeAll(text);        if (text.len == 0 or text[text.len - 1] != '\n') try writer.writeByte('\n');    }    try writer.flush();}fn missingTool(lane: Lane) root.Capture {    return .{        .kind = kind,        .tool = tool,        .capture_path = lane.csv_path,        .summary_path = lane.summary_path,        .state = "tool_missing",        .caveat_kind = "tool_unavailable",        .caveat_message = "perf is not runnable on this host; counters were not captured",    };}fn failed(allocator: std.mem.Allocator, lane: Lane, exit_code: i64, stderr_path: []const u8) !root.Capture {    const row = try capture.caveat.classifyCommandCapture(allocator, .{        .phase = kind,        .exit_code = if (exit_code == 0) 1 else exit_code,        .timed_out = false,        .capture_path = stderr_path,    }) orelse capture.caveat.classifyCommandText(.{        .phase = kind,        .exit_code = exit_code,        .timed_out = false,        .capture_path = stderr_path,    }, "");    return .{        .kind = kind,        .tool = tool,        .capture_path = lane.csv_path,        .summary_path = lane.summary_path,        .state = "tool_failed",        .caveat_kind = row.kind,        .caveat_message = row.message,    };}test "profiling counters wrap workload argv in perf stat" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    try std.testing.expectEqualStrings(        "page-faults,{cycles,instructions,task-clock},{branches,branch-misses}",        default_events,    );    try std.testing.expectEqual(@as(u32, 3), default_repeat);    const lane = try plan(allocator, .{}, "zig-out/profiling/run/workloads/w");    const argv = [_][]const u8{ "zig", "build", "bench" };    const wrapped = try wrapArgv(allocator, .{}, &argv, lane);    try std.testing.expectEqualStrings("perf", wrapped[0]);    try std.testing.expectEqualStrings("stat", wrapped[1]);    try std.testing.expectEqual(@as(usize, default_repeat), lane.runs.len);    try std.testing.expectEqualStrings(default_events, lane.runs[0].events);    try std.testing.expectEqualStrings(default_events, lane.event_expression);    try std.testing.expectEqual(@as(usize, 2), lane.kernel_event_groups.len);    try std.testing.expectEqual(        capture.perfstat.KernelGroupConstraint.strong,        lane.kernel_event_groups[0].constraint,    );    try std.testing.expectEqualStrings(default_events, wrapped[5]);    try std.testing.expectEqualStrings(lane.runs[0].csv_path, wrapped[7]);    try std.testing.expectEqualStrings("zig", wrapped[wrapped.len - 3]);    try std.testing.expectEqualStrings("zig-out/profiling/run/workloads/w/perf.stat.csv", lane.csv_path);}test "profiling energy counters use system wide perf scope" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const options = Options{        .events = energy_events,        .system_wide = true,    };    const lane = try plan(allocator, options, "zig-out/profiling/run/workloads/w");    const argv = [_][]const u8{"workload"};    const wrapped = try wrapArgv(allocator, options, &argv, lane);    try std.testing.expect(lane.system_wide);    try std.testing.expectEqualStrings("-a", wrapped[2]);    try std.testing.expectEqualStrings(energy_events, wrapped[6]);    try std.testing.expectEqualStrings("workload", wrapped[wrapped.len - 1]);}test "profiling counters plan bounded default repetitions" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const options = Options{};    const lane = try plan(allocator, options, "zig-out/profiling/run/workloads/w");    try std.testing.expectEqual(@as(usize, default_repeat), lane.runs.len);    try std.testing.expectEqualStrings(        "zig-out/profiling/run/workloads/w/perf.stat.repeat.1.csv",        lane.runs[0].csv_path,    );    try std.testing.expectEqualStrings(        "zig-out/profiling/run/workloads/w/perf.stat.repeat.3.csv",        lane.runs[2].csv_path,    );    const argv = [_][]const u8{ "zig", "build", "bench" };    const wrapped = try wrapRunArgv(allocator, options, &argv, lane, 1);    try std.testing.expectEqualStrings(lane.runs[1].csv_path, wrapped[7]);    try std.testing.expectError(error.InvalidCounterRepeat, (Options{ .repeat = 0 }).validate());    try std.testing.expectError(error.InvalidCounterRepeat, (Options{ .repeat = root.process.max_repeat + 1 }).validate());}test "profiling counters plan bounded event group repetition matrix" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const options = Options{        .events = "task-clock,{cycles,instructions},branches",        .repeat = 2,        .group_size = 1,    };    const lane = try plan(allocator, options, "zig-out/profiling/run/workloads/w");    try std.testing.expectEqual(@as(usize, 3), lane.event_groups.len);    try std.testing.expectEqual(@as(usize, 1), lane.kernel_event_groups.len);    try std.testing.expectEqualStrings(        "cycles,instructions",        lane.kernel_event_groups[0].events,    );    try std.testing.expectEqual(@as(usize, 6), lane.runs.len);    try std.testing.expectEqualStrings("task-clock", lane.runs[0].events);    try std.testing.expectEqualStrings("{cycles,instructions}", lane.runs[1].events);    try std.testing.expectEqual(@as(u32, 2), lane.runs[3].repetition_index);    try std.testing.expectEqualStrings(        "zig-out/profiling/run/workloads/w/perf.stat.group.3.repeat.2.csv",        lane.runs[5].csv_path,    );    try std.testing.expectEqualStrings(        "zig-out/profiling/run/workloads/w/perf.stat.group.2.csv",        lane.event_groups[1].csv_path,    );    try std.testing.expectEqualStrings(        "zig-out/profiling/run/workloads/w/perf.stat.repeat.2.csv",        lane.repetition_csv_paths[1],    );    try std.testing.expectError(        error.TooManyCounterExecutions,        plan(allocator, .{ .events = "a,b", .repeat = root.process.max_repeat, .group_size = 1 }, "root"),    );    try std.testing.expectError(        error.InvalidCounterGroupSize,        (Options{ .group_size = max_group_size + 1 }).validate(),    );    try std.testing.expectError(        error.InvalidCounterEvents,        (Options{ .events = " \t" }).validate(),    );}test "profiling counters summarize a captured csv" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{ .repeat = 1 }, dir);    try sys.fs.writeFile(lane.csv_path,        \\# started on Fri Jul  4 10:00:00 2026        \\1000.50,msec,task-clock,1000500000,100.00,1.000,CPUs utilized        \\2000000,,instructions,1000500000,100.00,2.00,insn per cycle        \\    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expectEqualStrings("add_counter_repetitions", row.caveat_kind.?);    try std.testing.expectEqualStrings(        "perf counter values are single-execution point estimates; add repetitions",        row.caveat_message.?,    );    try std.testing.expect(sys.fs.exists(lane.summary_path));    const summary = try capture.perfstat.parseSummaryFile(allocator, lane.summary_path);    const schedule = capture.perfstat.counterSchedule(summary);    try std.testing.expectEqualStrings("strong_kernel_groups", schedule.status);    try std.testing.expectEqualStrings(default_events, schedule.event_expression.?);    try std.testing.expectEqual(@as(u64, 2), schedule.strong_group_count);    try std.testing.expectEqual(@as(u64, 5), schedule.grouped_event_count);}test "profiling system wide counters retain attribution caveat" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{        .events = energy_events,        .repeat = 1,        .system_wide = true,    }, dir);    try sys.fs.writeFile(lane.csv_path,        \\1000000000,,duration_time,1,100.00,,        \\10,Joules,power/energy-pkg/u,0,100.00,,        \\    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    _ = try summarize(allocator, lane, 0, stderr_path, true);    const summary = try capture.perfstat.parseSummaryFile(allocator, lane.summary_path);    try std.testing.expectEqualStrings("system_wide_scope", summary.caveats[0].kind);    try std.testing.expect(std.mem.indexOf(        u8,        summary.caveats[0].message,        "not attributed exclusively",    ) != null);}test "profiling counters surface excluded kernel evidence caveats" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{        .events = "context-switches,cpu-migrations,page-faults",        .repeat = 1,    }, dir);    try sys.fs.writeFile(lane.runs[0].csv_path,        \\0,,context-switches:u,100,100.00,,        \\0,,cpu-migrations:u,100,100.00,,        \\90,,page-faults:u,100,100.00,,        \\    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expectEqualStrings("excluded_kernel_events", row.caveat_kind.?);    try std.testing.expect(std.mem.indexOf(        u8,        row.caveat_message.?,        "request kernel access",    ) != null);}test "profiling counters leave ready repeated evidence uncaveated" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{ .events = "instructions", .repeat = 3 }, dir);    for (lane.runs) |run| try sys.fs.writeFile(        run.csv_path,        "100,,instructions:u,100,100.00,,\n",    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expect(row.caveat_kind == null);    try std.testing.expect(row.caveat_message == null);}test "profiling counters summarize separate event groups" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{        .events = "instructions,branches",        .repeat = 1,        .group_size = 1,    }, dir);    for (lane.runs, [_]u64{ 100, 40 }) |run, value| {        try sys.fs.writeFile(            run.csv_path,            try std.fmt.allocPrint(allocator, "{d},,{s},100,100.00,,\n", .{ value, run.events }),        );    }    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    const summary = try capture.perfstat.parseSummaryFile(allocator, lane.summary_path);    try std.testing.expectEqual(@as(usize, 2), summary.event_groups.len);    try std.testing.expectEqual(@as(usize, 0), summary.repetitions.len);    try std.testing.expectEqual(@as(usize, 2), summary.counters.len);    try std.testing.expectEqual(@as(usize, 1), summary.caveats.len);    try std.testing.expectEqualStrings(        "grouped_point_estimates_separate_executions",        capture.perfstat.measurementDesign(summary).status,    );}test "profiling counters summarize raw repetitions" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{ .repeat = 2 }, dir);    try sys.fs.writeFile(lane.runs[0].csv_path,        \\100,,instructions,100,100.00,,        \\    );    try sys.fs.writeFile(lane.runs[1].csv_path,        \\120,,instructions,100,100.00,,        \\    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expect(sys.fs.exists(lane.csv_path));    const summary = try capture.perfstat.mergeRepeatedSummaries(allocator, lane.csv_path, &.{        try capture.perfstat.parseCsv(allocator, lane.runs[0].csv_path),        try capture.perfstat.parseCsv(allocator, lane.runs[1].csv_path),    });    try std.testing.expectEqual(@as(u64, 2), summary.counter_stability[0].samples);    try std.testing.expectEqual(@as(f64, 110), summary.counters[0].value.?);}test "profiling counters summarize grouped raw repetitions" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{        .events = "instructions,branches",        .repeat = 2,        .group_size = 1,    }, dir);    const values = [_]u64{ 100, 40, 120, 60 };    for (lane.runs, values) |run, value| {        try sys.fs.writeFile(            run.csv_path,            try std.fmt.allocPrint(allocator, "{d},,{s},100,100.00,,\n", .{ value, run.events }),        );    }    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    const summary = try capture.perfstat.parseSummaryFile(allocator, lane.summary_path);    try std.testing.expectEqual(@as(usize, 2), summary.event_groups.len);    try std.testing.expectEqual(@as(usize, 2), summary.repetitions.len);    try std.testing.expectEqual(@as(usize, 2), summary.repetitions[0].counters.len);    try std.testing.expectEqual(@as(u64, 2), summary.counter_stability[0].samples);    try std.testing.expectEqual(@as(usize, 1), summary.caveats.len);    try std.testing.expectEqualStrings(        "grouped_repeated_separate_executions",        capture.perfstat.measurementDesign(summary).status,    );    try std.testing.expectEqual(@as(u64, 4), capture.perfstat.measurementDesign(summary).execution_count);    try std.testing.expect(sys.fs.exists(lane.event_groups[0].csv_path));    try std.testing.expect(sys.fs.exists(lane.repetition_csv_paths[0]));}test "profiling counters classify a failed capture" {    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 dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{}, dir);    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "perf stat: Permission denied\n");    const row = try summarize(allocator, lane, 1, stderr_path, true);    try std.testing.expectEqualStrings("tool_failed", row.state);    try std.testing.expectEqualStrings("permission_denied", row.caveat_kind.?);}test "profiling counters report missing tools as caveats" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const lane = try plan(allocator, .{}, "zig-out/profiling/run/workloads/w");    const row = try summarize(allocator, lane, 0, "missing-stderr.txt", false);    try std.testing.expectEqualStrings("tool_missing", row.state);    try std.testing.expectEqualStrings("tool_unavailable", row.caveat_kind.?);}

Source: src/profiling/host/root.zig:3

zig
pub const counters = runtime.counters;

Complete caller list for host.counters.plan

19 direct callers.

Complete caller list for host.counters.summarize

10 direct callers.

Audit

Definitions15
Public names15
Members16
Version26.7.0
Revisiondaab053ee433