Skip to documentation
SLOP

tiny.profiling.execute

Reference tiny.profiling execute

Defined in tiny.profiling.

API (34)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallsNo direct callstest; no linksrc.profiling.executetest: profiling execution scope parse...optionsparseCycleArgsoptionsparseRunArgsexecute.Scopeparse
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest; no linksrc.profiling.executetest: profiling execution returns a c...test; no linksrc.profiling.executetest: profiling execution timeout sto...test; no linksrc.profiling.executetest: profiling execution times out a...private; no linksrc.profiling.executegroupReceiptexecutepollGroupprivate; no linksrc.profiling.executestopGroupAsexecuteawaitGroupUntil
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsexecuteplantest; no linksrc.profiling.executetest: profiling execution builds chil...test; no linksrc.profiling.executetest: profiling execution formats cwd...test; no linksrc.profiling.executetest: profiling execution resolves th...private; no linksrc.profiling.executebuildArgvprivate; no linksrc.profiling.executeisRootScopeexecutechildArgv
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate; no linksrc.profiling.commandplanRunDryRunWorkloadprivate; no linksrc.profiling.commandwriteRunDryRunWorkloaddriver.prepareprepareWorkloadprivate; no linksrc.profiling.driver.preparerunSetupexecuterunRecordedCommand+6 moreprivate; no linksrc.profiling.executewriteArgvexecutecommandText
Static calls · unresolved targets: 1 · external targets: 4.
Called byCallsNo direct callsprivate; no linksrc.profiling.executestopChildAstest; no linksrc.profiling.executetest: profiling execution returns a c...test; no linksrc.profiling.executetest: profiling execution timeout sto...test; no linksrc.profiling.executetest: profiling execution times out a...executedeadlineNanoseconds
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate; no linksrc.profiling.driver.fixture.dataresetTestPrepareddriver.prepareprepareWorkloadtest; no linksrc.profiling.executetest: profiling execution applies bou...catalogvalidateEnvironmentexecuteenvironmentWithOverrides
Static calls · unresolved targets: 1 · external targets: 4.
Called byCallsNo direct callsdriver.validationbuildArgstest; no linksrc.profiling.executetest: profiling execution scope parse...private; no linksrc.profiling.experiment.variantcatalogBuildexecuteoptimizeBuildArg
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest; no linksrc.profiling.executetest: profiling execution gives packa...test; no linksrc.profiling.executetest: profiling execution marks direc...test; no linksrc.profiling.executetest: profiling execution plans direc...test; no linksrc.profiling.executetest: profiling execution plans root-...private; no linksrc.profiling.executebuildArgvexecutechildArgvprivate; no linksrc.profiling.executeisRootScopeexecuteplan
Static calls · unresolved targets: 1 · external targets: 4.
Called byCallsNo direct callsexecuteawaitGroupUntilprivate; no linksrc.profiling.executestopChildAsexecutepollGroup
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsexecuterunChildtest; no linksrc.profiling.executetest: profiling execution exports ben...test; no linksrc.profiling.executetest: profiling execution only export...test; no linksrc.profiling.executetest: profiling execution scrubs stal...private; no linksrc.profiling.executeputOrScrubexecuteputArtifactEnv
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsdriver.acquirerunChildAcquisitiondriver.preparerunWarmupstest; no linksrc.profiling.executetest: profiling execution records chi...private; no linksrc.profiling.executeawaitChildexecuteputArtifactEnvprivate; no linksrc.profiling.executeresetArtifactprivate; no linksrc.profiling.executespawnFailureResultprivate; no linksrc.profiling.executewriteSpawnFailureexecuterunChild
Static calls · unresolved targets: 0 · external targets: 7.
Called byCallsprivate; no linksrc.profiling.driver.preparerunSetupexecuterunRecordedCommandtest; no linksrc.profiling.executetest: profiling execution records the...private; no linksrc.profiling.experiment.acquire.RunnercaptureMeasurementprivate; no linksrc.profiling.experiment.acquirerunOptionalCommand+2 moreprivate; no linksrc.profiling.executeawaitChildprivate; no linksrc.profiling.executespawnFailureResultprivate; no linksrc.profiling.executewriteSpawnFailureexecuterunCommand
Static calls · unresolved targets: 0 · external targets: 5.
Called byCallsdriver.acquirerunResetprivate; no linksrc.profiling.driver.preparerunPrepareexecutecommandTextexecuterunCommandexecuterunRecordedCommand
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest; no linksrc.profiling.executetest: profiling execution returns a c...test; no linksrc.profiling.executetest: profiling execution timeout sto...test; no linksrc.profiling.executetest: profiling execution times out a...private; no linksrc.profiling.executechildProcessGroupexecutespawnGroup
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallstest; no linksrc.profiling.executetest: profiling execution timeout sto...private; no linksrc.profiling.executestopGroupAsexecutestopGroup
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate; no linksrc.profiling.commandwriteRunDryRunTextdriver.runexecuteRuntest; no linksrc.profiling.executetest: profiling execution resolves th...private; no linksrc.profiling.experiment.variantcatalogBuildprivate; no linksrc.profiling.experiment.variantexplicitBuildreport.driftruntiny.sysenvgetexecutezig
Static calls · unresolved targets: 0 · external targets: 0.

Source: src/profiling/execute.zig

zig
const std = @import("std");const sys = @import("sys");const catalog = @import("catalog.zig");const wait_poll_ms: u64 = 2;const maximum_grace_ms: u64 = 60_000;pub const default_termination_grace_ms: u64 = 100;pub const zig_env_var = "TINY_PROFILE_ZIG";pub const build_flag = "-fno-incremental";pub const Zig = struct {    executable: []const u8,};pub const GroupStatus = enum {    exited,    stopped,    timed_out,};pub const GroupIsolation = enum {    child,    process_group,};pub const GroupReceipt = struct {    status: GroupStatus,    isolation: GroupIsolation,    pid: u64,    term: ?sys.process.Termination,    elapsed_ns: u64,    terminate_sent: bool,    kill_sent: bool,};pub const Group = struct {    child: sys.process.Child,    child_id: sys.process.Child.Id,    started_monotonic_ns: i128,    term: ?sys.process.Termination = null,    isolation: GroupIsolation,    pub fn active(self: *const Group) bool {        return self.child.id != null;    }};pub fn zig(environ_map: ?*const sys.process.Environ.Map) Zig {    const env = environ_map orelse return .{ .executable = "zig" };    return .{        .executable = env.get(zig_env_var) orelse "zig",    };}const test_zig: Zig = .{ .executable = "zig" };pub const default_optimize: std.builtin.OptimizeMode = .fast;pub fn optimizeBuildArg(mode: std.builtin.OptimizeMode) []const u8 {    return switch (mode) {        .debug => "-Doptimize=debug",        .safe => "-Doptimize=safe",        .fast => "-Doptimize=fast",        .small => "-Doptimize=small",    };}const retain_root_symbols_args = [_][]const u8{    "-Dproduct-strip-binaries=false",    "-Dtool-strip-binaries=false",};pub const Result = struct {    exit_code: i64,    wall_ns: u64,    resource_usage_source: ?sys.process.ResourceUsageSource = null,    maxrss_kib: ?i64 = null,    user_s: ?f64 = null,    system_s: ?f64 = null,    minor_page_faults: ?u64 = null,    major_page_faults: ?u64 = null,    voluntary_context_switches: ?u64 = null,    involuntary_context_switches: ?u64 = null,    pid: ?u64 = null,};pub const CommandResult = struct {    command: []const u8,    argv: []const []const u8,    cwd: ?[]const u8,    stdout_path: []const u8,    stderr_path: []const u8,    execution: Result,};pub const ArtifactEnv = struct {    stdout: []const u8,    stderr: []const u8,    bench_jsonl: []const u8,    coz_jsonl: []const u8,    coz_analysis: []const u8,    tracy_jsonl: []const u8,    tracy_summary: []const u8,    allocations: []const u8,};pub const Scope = enum {    step,    binary,    mixed,    pub fn parse(value: []const u8) ?Scope {        if (std.mem.eql(u8, value, "step")) return .step;        if (std.mem.eql(u8, value, "binary")) return .binary;        if (std.mem.eql(u8, value, "mixed")) return .mixed;        return null;    }    pub fn name(self: Scope) []const u8 {        return @tagName(self);    }    pub fn directFor(self: Scope, workload: catalog.Workload) bool {        return switch (self) {            .step => false,            .binary => true,            .mixed => workload.requiresDirectExecution(),        };    }};pub const Plan = struct {    setup_argv: ?[]const []const u8 = null,    argv: []const []const u8,    cwd: ?[]const u8 = null,    direct: bool = false,    missing_bin: bool = false,};pub fn plan(    allocator: std.mem.Allocator,    zig_command: Zig,    workload: catalog.Workload,    forwarded: []const []const u8,    direct: bool,    build_args: []const []const u8,) !Plan {    if (direct) {        if (workload.bin) |bin| {            const setup_scope = bin.scope(workload.package);            const setup_argv = try buildArgv(                allocator,                zig_command,                bin.step,                build_args,                workload.buildOptionsFor(setup_scope),                isRootScope(setup_scope),                &.{},                false,            );            var argv: std.ArrayList([]const u8) = .empty;            try argv.append(allocator, bin.path);            try argv.appendSlice(allocator, bin.args);            if (workload.forwards_args and forwarded.len != 0) try argv.appendSlice(allocator, forwarded);            const scope = bin.scope(workload.package);            return .{                .setup_argv = setup_argv,                .argv = try argv.toOwnedSlice(allocator),                .cwd = if (std.mem.eql(u8, scope, ".")) null else scope,                .direct = true,            };        }        return .{            .argv = try childArgv(allocator, zig_command, workload, forwarded, build_args),            .cwd = workload.cwd,            .missing_bin = true,        };    }    return .{        .argv = try childArgv(allocator, zig_command, workload, forwarded, build_args),        .cwd = workload.cwd,    };}pub fn childArgv(    allocator: std.mem.Allocator,    zig_command: Zig,    workload: catalog.Workload,    forwarded: []const []const u8,    build_args: []const []const u8,) ![]const []const u8 {    const scope = workload.cwd orelse ".";    return try buildArgv(        allocator,        zig_command,        workload.step,        build_args,        workload.buildOptionsFor(scope),        isRootScope(scope),        forwarded,        workload.forwards_args,    );}fn buildArgv(    allocator: std.mem.Allocator,    zig_command: Zig,    step: []const u8,    build_args: []const []const u8,    package_options: []const []const u8,    root_build: bool,    forwarded: []const []const u8,    forwards_args: bool,) ![]const []const u8 {    if (root_build) std.debug.assert(package_options.len == 0);    const root_count: usize = if (root_build) retain_root_symbols_args.len else 0;    const forwarded_count: usize = if (forwards_args and forwarded.len != 0)        forwarded.len + 1    else        0;    const argv = try allocator.alloc(        []const u8,        4 + build_args.len + package_options.len + root_count + forwarded_count,    );    argv[0] = zig_command.executable;    argv[1] = "build";    argv[2] = build_flag;    argv[3] = step;    @memcpy(argv[4 .. 4 + build_args.len], build_args);    var index = 4 + build_args.len;    @memcpy(argv[index..][0..package_options.len], package_options);    index += package_options.len;    if (root_build) {        @memcpy(            argv[index..][0..retain_root_symbols_args.len],            retain_root_symbols_args[0..],        );        index += retain_root_symbols_args.len;    }    if (forwarded_count != 0) {        argv[index] = "--";        index += 1;        for (forwarded, 0..) |arg, forwarded_index| {            argv[index + forwarded_index] = arg;        }    }    return argv;}fn isRootScope(scope: []const u8) bool {    return std.mem.eql(u8, scope, ".");}pub fn commandText(allocator: std.mem.Allocator, cwd: ?[]const u8, setup_argv: ?[]const []const u8, argv: []const []const u8) ![]const u8 {    var out: std.Io.Writer.Allocating = .init(allocator);    errdefer out.deinit();    const grouped = cwd != null or setup_argv != null;    if (cwd) |path| {        try out.writer.print("(cd {s} && ", .{path});    } else if (grouped) {        try out.writer.writeByte('(');    }    if (setup_argv) |setup| {        try writeArgv(&out.writer, setup);        try out.writer.writeAll(" && ");    }    try writeArgv(&out.writer, argv);    if (grouped) try out.writer.writeByte(')');    return try out.toOwnedSlice();}fn writeArgv(writer: *std.Io.Writer, argv: []const []const u8) !void {    for (argv, 0..) |arg, index| {        if (index != 0) try writer.writeByte(' ');        try writer.writeAll(arg);    }}pub const BenchControl = struct {    causal: bool = false,    min_time_ns: ?u64 = null,    filter: ?[]const u8 = null,};pub fn environmentWithOverrides(    allocator: std.mem.Allocator,    base_env: ?*const sys.process.Environ.Map,    overrides: []const catalog.EnvironmentOverride,) !sys.process.Environ.Map {    try catalog.validateEnvironment(overrides);    var result = if (base_env) |actual|        try actual.clone(allocator)    else        sys.process.Environ.Map.init(allocator);    errdefer result.deinit();    for (overrides) |entry| {        if (entry.value) |value| {            try result.put(entry.name, value);        } else {            _ = result.swapRemove(entry.name);        }    }    return result;}pub fn spawnGroup(    process_io: std.Io,    options: sys.process.SpawnOptions,) !Group {    std.debug.assert(options.argv.len != 0);    var grouped_options = options;    grouped_options.pgid = childProcessGroup();    const started_monotonic_ns = sys.time.nanoTimestamp();    var child = try sys.process.spawn(process_io, grouped_options);    errdefer sys.process.killAndReap(&child, process_io);    const child_id = child.id orelse return error.MissingChildProcessId;    return .{        .child = child,        .child_id = child_id,        .started_monotonic_ns = started_monotonic_ns,        .isolation = if (grouped_options.pgid == null)            .child        else            .process_group,    };}pub fn pollGroup(group: *Group) !?sys.process.Termination {    std.debug.assert(group.active());    const outcome = (try sys.process.waitNoHang(group.child_id)) orelse        return null;    group.child.id = null;    group.term = outcome.term;    return outcome.term;}pub fn awaitGroupUntil(    group: *Group,    process_io: std.Io,    deadline_monotonic_ns: i128,    grace_ms: u64,) !GroupReceipt {    std.debug.assert(group.active());    while (true) {        if (sys.time.nanoTimestamp() >= deadline_monotonic_ns) {            return stopGroupAs(group, process_io, grace_ms, .timed_out);        }        if (try pollGroup(group)) |_| {            return groupReceipt(group, .exited, false, false);        }        sys.time.sleepMilliseconds(wait_poll_ms);    }}pub fn stopGroup(    group: *Group,    process_io: std.Io,    grace_ms: u64,) !GroupReceipt {    return stopGroupAs(group, process_io, grace_ms, .stopped);}pub fn deadlineNanoseconds(start_monotonic_ns: i128, timeout_ms: u64) i128 {    const timeout_ns = @as(i128, @intCast(timeout_ms)) * std.time.ns_per_ms;    return start_monotonic_ns + timeout_ns;}fn stopGroupAs(    group: *Group,    process_io: std.Io,    grace_ms: u64,    status: GroupStatus,) !GroupReceipt {    std.debug.assert(grace_ms <= maximum_grace_ms);    if (!group.active()) {        return groupReceipt(group, status, false, false);    }    if (group.isolation == .child) {        return stopChildAs(group, process_io, grace_ms, status);    }    const terminate_sent = try signalGroup(group.child_id, .terminate);    if (!terminate_sent) {        group.term = try sys.process.wait(&group.child, process_io);        return groupReceipt(group, status, false, false);    }    sys.time.sleepMilliseconds(grace_ms);    const kill_sent = try signalGroup(group.child_id, .kill);    group.term = try sys.process.wait(&group.child, process_io);    return groupReceipt(group, status, true, kill_sent);}fn stopChildAs(    group: *Group,    process_io: std.Io,    grace_ms: u64,    status: GroupStatus,) !GroupReceipt {    sys.process.requestTermination(&group.child, process_io);    const deadline = deadlineNanoseconds(sys.time.nanoTimestamp(), grace_ms);    while (sys.time.nanoTimestamp() < deadline) {        if (try pollGroup(group)) |_| {            return groupReceipt(group, status, true, false);        }        sys.time.sleepMilliseconds(wait_poll_ms);    }    sys.process.killAndReap(&group.child, process_io);    return groupReceipt(group, status, true, true);}fn signalGroup(    child_id: sys.process.Child.Id,    signal: sys.process.ChildSignal,) !bool {    sys.process.signalChildGroup(child_id, signal) catch |err| switch (err) {        error.ProcessNotFound => return false,        else => return err,    };    return true;}fn groupReceipt(    group: *const Group,    status: GroupStatus,    terminate_sent: bool,    kill_sent: bool,) GroupReceipt {    const finished_monotonic_ns = sys.time.nanoTimestamp();    return .{        .status = status,        .isolation = group.isolation,        .pid = @intCast(group.child_id),        .term = group.term,        .elapsed_ns = @intCast(@max(            finished_monotonic_ns - group.started_monotonic_ns,            0,        )),        .terminate_sent = terminate_sent,        .kill_sent = kill_sent,    };}fn childProcessGroup() @TypeOf(    @as(sys.process.SpawnOptions, undefined).pgid,) {    return switch (comptime sys.process.childSignalPolicy()) {        .unsupported => null,        .linux_syscall, .posix_host => 0,    };}pub fn runChild(    allocator: std.mem.Allocator,    process_io: std.Io,    base_env: ?*const sys.process.Environ.Map,    cwd: ?[]const u8,    argv: []const []const u8,    artifacts: ArtifactEnv,    trace_allocations: bool,    tracy: bool,    control: BenchControl,) !Result {    const stdout_file = try sys.fs.createFile(artifacts.stdout, .{ .truncate = true, .read = true });    defer sys.fs.closeHandle(stdout_file);    const stderr_file = try sys.fs.createFile(artifacts.stderr, .{ .truncate = true, .read = true });    defer sys.fs.closeHandle(stderr_file);    try resetArtifact(artifacts.bench_jsonl);    try resetArtifact(artifacts.coz_jsonl);    try resetArtifact(artifacts.coz_analysis);    try resetArtifact(artifacts.tracy_jsonl);    try resetArtifact(artifacts.tracy_summary);    try resetArtifact(artifacts.allocations);    var env = if (base_env) |actual| try actual.clone(allocator) else null;    defer if (env) |*actual| actual.deinit();    if (env) |*actual| {        try putArtifactEnv(actual, artifacts, trace_allocations, tracy, control);    }    const start = sys.time.nanoTimestamp();    var child = sys.process.spawn(process_io, .{        .argv = argv,        .environ_map = if (env) |*actual| actual else null,        .cwd = if (cwd) |path| .{ .path = path } else .inherit,        .stdin = .inherit,        .stdout = .{ .file = stdout_file },        .stderr = .{ .file = stderr_file },        .request_resource_usage_statistics = false,    }) catch |err| {        try writeSpawnFailure(stderr_file, err);        return spawnFailureResult(start, err);    };    errdefer sys.process.killAndReap(&child, process_io);    const pid: u64 = @intCast(child.id orelse return error.MissingChildProcessId);    return try awaitChild(process_io, &child, start, pid);}pub fn runCommand(    process_io: std.Io,    base_env: ?*const sys.process.Environ.Map,    cwd: ?[]const u8,    argv: []const []const u8,    stdout_path: []const u8,    stderr_path: []const u8,) !Result {    const stdout_file = try sys.fs.createFile(stdout_path, .{ .truncate = true, .read = true });    defer sys.fs.closeHandle(stdout_file);    const stderr_file = try sys.fs.createFile(stderr_path, .{ .truncate = true, .read = true });    defer sys.fs.closeHandle(stderr_file);    const start = sys.time.nanoTimestamp();    var child = sys.process.spawn(process_io, .{        .argv = argv,        .environ_map = base_env,        .cwd = if (cwd) |path| .{ .path = path } else .inherit,        .stdin = .inherit,        .stdout = .{ .file = stdout_file },        .stderr = .{ .file = stderr_file },        .request_resource_usage_statistics = false,    }) catch |err| {        try writeSpawnFailure(stderr_file, err);        return spawnFailureResult(start, err);    };    errdefer sys.process.killAndReap(&child, process_io);    const pid: u64 = @intCast(child.id orelse return error.MissingChildProcessId);    return try awaitChild(process_io, &child, start, pid);}pub fn runRecordedCommand(    allocator: std.mem.Allocator,    process_io: std.Io,    base_env: ?*const sys.process.Environ.Map,    cwd: ?[]const u8,    argv: []const []const u8,    stdout_path: []const u8,    stderr_path: []const u8,) !CommandResult {    return .{        .command = try commandText(allocator, cwd, null, argv),        .argv = argv,        .cwd = cwd,        .stdout_path = stdout_path,        .stderr_path = stderr_path,        .execution = try runCommand(            process_io,            base_env,            cwd,            argv,            stdout_path,            stderr_path,        ),    };}fn awaitChild(    process_io: std.Io,    child: *sys.process.Child,    start: i128,    pid: u64,) !Result {    if (child.id) |child_pid| {        while (true) {            const outcome = try sys.process.waitNoHang(child_pid);            if (outcome) |finished| {                child.id = null;                return resultFromTerm(start, pid, finished.term, finished.usage);            }            sys.time.sleepMilliseconds(wait_poll_ms);        }    }    const term = try sys.process.wait(child, process_io);    return resultFromTerm(start, pid, term, .{});}pub fn putArtifactEnv(    env: *sys.process.Environ.Map,    artifacts: ArtifactEnv,    trace_allocations: bool,    tracy: bool,    control: BenchControl,) !void {    try env.put("BENCH_JSONL", artifacts.bench_jsonl);    try env.put("BENCH_COZ_JSONL", artifacts.coz_jsonl);    try env.put("BENCH_COZ_ANALYSIS_JSON", artifacts.coz_analysis);    try putOrScrub(env, "BENCH_TRACY_JSONL", if (tracy) artifacts.tracy_jsonl else null);    try putOrScrub(env, "BENCH_TRACY_SUMMARY_JSONL", if (tracy) artifacts.tracy_summary else null);    try putOrScrub(env, "TINY_PROFILE_ALLOCATIONS_PATH", if (trace_allocations) artifacts.allocations else null);    try putOrScrub(env, "BENCH_COZ_EXPERIMENTS", if (control.causal) "1" else null);    var min_time_buffer: [20]u8 = undefined;    const min_time_text: ?[]const u8 = if (control.min_time_ns) |ns|        std.fmt.bufPrint(&min_time_buffer, "{d}", .{ns}) catch unreachable    else        null;    try putOrScrub(env, "BENCH_MIN_TIME_NS", min_time_text);    try putOrScrub(env, "BENCH_FILTER", control.filter);}fn putOrScrub(env: *sys.process.Environ.Map, key: []const u8, value: ?[]const u8) !void {    if (value) |actual| {        try env.put(key, actual);    } else {        _ = env.swapRemove(key);    }}fn resetArtifact(path: []const u8) !void {    const file = try sys.fs.createFile(path, .{ .truncate = true });    sys.fs.closeHandle(file);}fn resultFromTerm(    start_ns: i128,    pid: ?u64,    term: sys.process.Termination,    usage: sys.process.ResourceUsage,) Result {    const end_ns = sys.time.nanoTimestamp();    const wall_ns: u64 = @intCast(@max(end_ns - start_ns, 0));    return .{        .pid = pid,        .exit_code = sys.process.exitCode(term),        .wall_ns = wall_ns,        .resource_usage_source = usage.source,        .maxrss_kib = usage.maxrss_kib,        .user_s = usage.user_s,        .system_s = usage.system_s,        .minor_page_faults = usage.minor_page_faults,        .major_page_faults = usage.major_page_faults,        .voluntary_context_switches = usage.voluntary_context_switches,        .involuntary_context_switches = usage.involuntary_context_switches,    };}fn writeSpawnFailure(file: std.Io.File, err: anyerror) !void {    var buffer: [256]u8 = undefined;    var writer = file.writer(sys.fs.debugIo(), &buffer);    try writer.interface.print("spawn failed: {s}\n", .{@errorName(err)});    try writer.interface.flush();}fn spawnFailureResult(start_ns: i128, err: anyerror) Result {    const end_ns = sys.time.nanoTimestamp();    return .{        .exit_code = spawnFailureExitCode(err),        .wall_ns = @intCast(@max(end_ns - start_ns, 0)),    };}fn spawnFailureExitCode(err: anyerror) i64 {    return switch (err) {        error.AccessDenied => 126,        else => 127,    };}test "profiling execution builds child argv with forwarded args only when supported" {    const allocator = std.testing.allocator;    const forwarded = &.{ "--test-filter", "x" };    const with_forwarding = try childArgv(        allocator,        test_zig,        catalog.find("choir.compiler").?,        forwarded,        &.{"-Doptimize=fast"},    );    defer allocator.free(with_forwarding);    try std.testing.expectEqualSlices(        []const u8,        &.{            "zig",            "build",            "-fno-incremental",            "choir-compiler-bench",            "-Doptimize=fast",            retain_root_symbols_args[0],            retain_root_symbols_args[1],            "--",            "--test-filter",            "x",        },        with_forwarding,    );    const without_forwarding = try childArgv(        allocator,        test_zig,        catalog.find("gpalloc.allocator").?,        forwarded,        &.{"-Doptimize=fast"},    );    defer allocator.free(without_forwarding);    try std.testing.expectEqualSlices(        []const u8,        &.{            "zig",            "build",            "-fno-incremental",            "gpalloc-bench",            "-Doptimize=fast",            retain_root_symbols_args[0],            retain_root_symbols_args[1],        },        without_forwarding,    );}test "profiling execution resolves the invoking Zig build command from the environment" {    const allocator = std.testing.allocator;    var env = sys.process.Environ.Map.init(allocator);    defer env.deinit();    try std.testing.expectEqualStrings("zig", zig(null).executable);    try std.testing.expectEqualStrings("zig", zig(&env).executable);    try env.put(zig_env_var, "/opt/zig/zig");    try std.testing.expectEqualStrings("/opt/zig/zig", zig(&env).executable);    const argv = try childArgv(        allocator,        zig(&env),        catalog.find("gpalloc.allocator").?,        &.{},        &.{"-Doptimize=fast"},    );    defer allocator.free(argv);    try std.testing.expectEqualStrings("/opt/zig/zig", argv[0]);    try std.testing.expectEqualStrings("-fno-incremental", argv[2]);}test "profiling execution formats cwd commands" {    const allocator = std.testing.allocator;    const workload = catalog.find("gpalloc.driver").?;    const argv = try childArgv(        allocator,        test_zig,        workload,        &.{},        &.{"-Doptimize=safe"},    );    defer allocator.free(argv);    const text = try commandText(allocator, workload.cwd, null, argv);    defer allocator.free(text);    try std.testing.expectEqualStrings(        "(cd lib/gpalloc && zig build -fno-incremental bench-driver -Doptimize=safe)",        text,    );}test "profiling execution gives package build options only to package-scoped builds" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const opt_in = catalog.find("tldr.linker").?;    try std.testing.expectEqualSlices([]const u8, &.{"-Dprofiling=true"}, opt_in.package_build_options);    const root_step = try plan(arena, test_zig, opt_in, &.{}, false, &.{"-Doptimize=fast"});    try std.testing.expect(root_step.cwd == null);    try std.testing.expectEqualSlices(        []const u8,        &.{            "zig",            "build",            "-fno-incremental",            "tldr-bench",            "-Doptimize=fast",            retain_root_symbols_args[0],            retain_root_symbols_args[1],        },        root_step.argv,    );    const direct = try plan(arena, test_zig, opt_in, &.{}, true, &.{"-Doptimize=fast"});    try std.testing.expectEqualStrings("lib/tldr", direct.cwd.?);    try std.testing.expectEqualSlices(        []const u8,        &.{ "zig", "build", "-fno-incremental", "bench-bin", "-Doptimize=fast", "-Dprofiling=true" },        direct.setup_argv.?,    );    const default = catalog.find("gpalloc.allocator").?;    try std.testing.expectEqual(@as(usize, 0), default.package_build_options.len);    const default_direct = try plan(arena, test_zig, default, &.{}, true, &.{"-Doptimize=fast"});    try std.testing.expectEqualStrings("lib/gpalloc", default_direct.cwd.?);    try std.testing.expectEqualSlices(        []const u8,        &.{ "zig", "build", "-fno-incremental", "bench-bin", "-Doptimize=fast" },        default_direct.setup_argv.?,    );    var local = opt_in;    local.bin = null;    local.cwd = opt_in.package;    local.step = opt_in.localStep();    const local_step = try plan(arena, test_zig, local, &.{}, false, &.{});    try std.testing.expectEqualSlices(        []const u8,        &.{ "zig", "build", "-fno-incremental", "bench", "-Dprofiling=true" },        local_step.argv,    );}test "profiling execution scope parses user names" {    try std.testing.expectEqual(Scope.step, Scope.parse("step").?);    try std.testing.expectEqual(Scope.binary, Scope.parse("binary").?);    try std.testing.expectEqual(Scope.mixed, Scope.parse("mixed").?);    try std.testing.expect(Scope.parse("whole-step") == null);    const ordinary_binary = catalog.find("mprompt.smoke").?;    const ordinary_step = catalog.find("choir.versus").?;    const configured = catalog.Workload{        .name = "configured",        .package = "fixture",        .step = "fixture-test",        .summary = "direct execution fixture",        .tier = .smoke,        .surface = .benchmark,        .environment = &.{.{ .name = "FIXTURE_DIRECT", .value = "1" }},    };    try std.testing.expect(!Scope.step.directFor(ordinary_binary));    try std.testing.expect(Scope.binary.directFor(ordinary_binary));    try std.testing.expect(!Scope.mixed.directFor(ordinary_binary));    try std.testing.expect(!Scope.mixed.directFor(ordinary_step));    try std.testing.expect(Scope.mixed.directFor(configured));    try std.testing.expectEqualStrings(        "-Doptimize=small",        optimizeBuildArg(.small),    );}test "profiling execution formats setup phases into command text" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const setup = [_][]const u8{ "zig", "build", "-fno-incremental", "bench-bin" };    const argv = [_][]const u8{ "zig-out/bin/mprompt-bench", "--workers", "128" };    const text = try commandText(allocator, "lib/mprompt", &setup, &argv);    try std.testing.expectEqualStrings("(cd lib/mprompt && zig build -fno-incremental bench-bin && zig-out/bin/mprompt-bench --workers 128)", text);}test "profiling execution plans direct workloads through their binaries" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const workload = catalog.Workload{        .name = "example.bench",        .package = "lib/example",        .step = "example-bench",        .summary = "example",        .tier = .smoke,        .surface = .benchmark,        .forwards_args = true,        .bin = .{ .path = "zig-out/bin/example-bench", .args = &.{"--count"} },    };    const direct = try plan(        allocator,        .{ .executable = "/opt/zig/zig" },        workload,        &.{},        true,        &.{ "-Doptimize=fast", "-Dcpu=x86_64_v3" },    );    try std.testing.expect(direct.direct);    try std.testing.expect(!direct.missing_bin);    try std.testing.expectEqualStrings("lib/example", direct.cwd.?);    try std.testing.expectEqualSlices(        []const u8,        &.{            "/opt/zig/zig",            "build",            "-fno-incremental",            "bench-bin",            "-Doptimize=fast",            "-Dcpu=x86_64_v3",        },        direct.setup_argv.?,    );    try std.testing.expectEqualSlices([]const u8, &.{ "zig-out/bin/example-bench", "--count" }, direct.argv);    const forwarded = try plan(allocator, test_zig, workload, &.{"--fast"}, true, &.{});    try std.testing.expectEqualSlices([]const u8, &.{ "zig-out/bin/example-bench", "--count", "--fast" }, forwarded.argv);    const whole_step = try plan(        allocator,        test_zig,        workload,        &.{},        false,        &.{"-Doptimize=fast"},    );    try std.testing.expect(!whole_step.direct);    try std.testing.expect(whole_step.setup_argv == null);    try std.testing.expectEqualSlices(        []const u8,        &.{            "zig",            "build",            "-fno-incremental",            "example-bench",            "-Doptimize=fast",            retain_root_symbols_args[0],            retain_root_symbols_args[1],        },        whole_step.argv,    );}test "profiling execution plans root-scoped binaries at the repository root" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const workload = catalog.Workload{        .name = "example.rooted",        .package = "lib/example",        .step = "example-bench",        .summary = "example",        .tier = .smoke,        .surface = .benchmark,        .bin = .{ .step = "example-bench-bin", .path = "zig-out/bin/example-bench", .cwd = "." },    };    const direct = try plan(        allocator,        test_zig,        workload,        &.{},        true,        &.{"-Doptimize=small"},    );    try std.testing.expect(direct.direct);    try std.testing.expect(direct.cwd == null);    try std.testing.expectEqualSlices(        []const u8,        &.{            "zig",            "build",            "-fno-incremental",            "example-bench-bin",            "-Doptimize=small",            retain_root_symbols_args[0],            retain_root_symbols_args[1],        },        direct.setup_argv.?,    );    try std.testing.expectEqualSlices([]const u8, &.{"zig-out/bin/example-bench"}, direct.argv);}test "profiling execution marks direct plans without binaries" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const workload = catalog.Workload{        .name = "example.nobin",        .package = "lib/example",        .step = "example-bench",        .summary = "example",        .tier = .smoke,        .surface = .benchmark,    };    const planned = try plan(        allocator,        test_zig,        workload,        &.{},        true,        &.{"-Doptimize=fast"},    );    try std.testing.expect(planned.missing_bin);    try std.testing.expect(!planned.direct);    try std.testing.expect(planned.setup_argv == null);    try std.testing.expectEqualSlices(        []const u8,        &.{            "zig",            "build",            "-fno-incremental",            "example-bench",            "-Doptimize=fast",            retain_root_symbols_args[0],            retain_root_symbols_args[1],        },        planned.argv,    );}test "profiling execution only exports requested in-process trace paths" {    const allocator = std.testing.allocator;    const artifacts = ArtifactEnv{        .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",        .allocations = "allocations.jsonl",    };    var default_env = sys.process.Environ.Map.init(allocator);    defer default_env.deinit();    try putArtifactEnv(&default_env, artifacts, false, false, .{});    try std.testing.expect(default_env.get("BENCH_COZ_EXPERIMENTS") == null);    try std.testing.expectEqualStrings("bench.jsonl", default_env.get("BENCH_JSONL").?);    try std.testing.expect(default_env.get("BENCH_TRACY_JSONL") == null);    try std.testing.expect(default_env.get("TINY_PROFILE_ALLOCATIONS_PATH") == null);    var trace_env = sys.process.Environ.Map.init(allocator);    defer trace_env.deinit();    try putArtifactEnv(&trace_env, artifacts, true, true, .{ .causal = true });    try std.testing.expectEqualStrings("1", trace_env.get("BENCH_COZ_EXPERIMENTS").?);    try std.testing.expectEqualStrings("allocations.jsonl", trace_env.get("TINY_PROFILE_ALLOCATIONS_PATH").?);    try std.testing.expectEqualStrings("bench.tracy.jsonl", trace_env.get("BENCH_TRACY_JSONL").?);    try std.testing.expectEqualStrings("bench.tracy.summary.jsonl", trace_env.get("BENCH_TRACY_SUMMARY_JSONL").?);    try std.testing.expect(trace_env.get("BENCH_MIN_TIME_NS") == null);    try std.testing.expect(trace_env.get("BENCH_FILTER") == null);}test "profiling execution exports benchmark controls" {    const allocator = std.testing.allocator;    const artifacts = ArtifactEnv{        .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",        .allocations = "allocations.jsonl",    };    var env = sys.process.Environ.Map.init(allocator);    defer env.deinit();    try putArtifactEnv(&env, artifacts, false, false, .{        .causal = true,        .min_time_ns = 30_000_000_000,        .filter = "small alloc",    });    try std.testing.expectEqualStrings("1", env.get("BENCH_COZ_EXPERIMENTS").?);    try std.testing.expectEqualStrings("30000000000", env.get("BENCH_MIN_TIME_NS").?);    try std.testing.expectEqualStrings("small alloc", env.get("BENCH_FILTER").?);}test "profiling execution scrubs stale bench control variables from the parent" {    const allocator = std.testing.allocator;    const artifacts = ArtifactEnv{        .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",        .allocations = "allocations.jsonl",    };    var env = sys.process.Environ.Map.init(allocator);    defer env.deinit();    try env.put("BENCH_COZ_EXPERIMENTS", "1");    try env.put("BENCH_MIN_TIME_NS", "30000000000");    try env.put("BENCH_FILTER", "small alloc");    try env.put("BENCH_TRACY_JSONL", "stale.tracy.jsonl");    try env.put("BENCH_TRACY_SUMMARY_JSONL", "stale.tracy.summary.jsonl");    try env.put("TINY_PROFILE_ALLOCATIONS_PATH", "stale.jsonl");    try putArtifactEnv(&env, artifacts, false, false, .{});    try std.testing.expect(env.get("BENCH_COZ_EXPERIMENTS") == null);    try std.testing.expect(env.get("BENCH_MIN_TIME_NS") == null);    try std.testing.expect(env.get("BENCH_FILTER") == null);    try std.testing.expect(env.get("BENCH_TRACY_JSONL") == null);    try std.testing.expect(env.get("BENCH_TRACY_SUMMARY_JSONL") == null);    try std.testing.expect(env.get("TINY_PROFILE_ALLOCATIONS_PATH") == null);}test "profiling execution applies bounded set and unset child environment overrides" {    const allocator = std.testing.allocator;    var parent = sys.process.Environ.Map.init(allocator);    defer parent.deinit();    try parent.put("HOME", "/parent");    try parent.put("FIXTURE_OBSERVATION_PATH", "/stale");    try parent.put("KEPT", "yes");    var child = try environmentWithOverrides(allocator, &parent, &.{        .{ .name = "HOME", .value = "/fixture" },        .{ .name = "FIXTURE_OBSERVATION_PATH" },    });    defer child.deinit();    try std.testing.expectEqualStrings("/fixture", child.get("HOME").?);    try std.testing.expect(child.get("FIXTURE_OBSERVATION_PATH") == null);    try std.testing.expectEqualStrings("yes", child.get("KEPT").?);    try std.testing.expectEqualStrings("/parent", parent.get("HOME").?);    try std.testing.expectEqualStrings(        "/stale",        parent.get("FIXTURE_OBSERVATION_PATH").?,    );}test "profiling execution records child spawn failures" {    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 root = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const artifacts = ArtifactEnv{        .stdout = try std.fs.path.join(allocator, &.{ root, "stdout.txt" }),        .stderr = try std.fs.path.join(allocator, &.{ root, "stderr.txt" }),        .bench_jsonl = try std.fs.path.join(allocator, &.{ root, "bench.jsonl" }),        .coz_jsonl = try std.fs.path.join(allocator, &.{ root, "bench.coz.jsonl" }),        .coz_analysis = try std.fs.path.join(allocator, &.{ root, "bench.coz.analysis.json" }),        .tracy_jsonl = try std.fs.path.join(allocator, &.{ root, "bench.tracy.jsonl" }),        .tracy_summary = try std.fs.path.join(allocator, &.{ root, "bench.tracy.summary.jsonl" }),        .allocations = try std.fs.path.join(allocator, &.{ root, "allocations.jsonl" }),    };    const result = try runChild(        allocator,        std.testing.io,        null,        null,        &.{"definitely-missing-tiny-profile-child"},        artifacts,        false,        false,        .{},    );    try std.testing.expectEqual(@as(i64, 127), result.exit_code);    try std.testing.expect(result.pid == null);    const stderr = try sys.fs.readFileAlloc(allocator, artifacts.stderr, 4096);    try std.testing.expect(std.mem.indexOf(u8, stderr, "spawn failed:") != null);}test "profiling execution records the waited child PID" {    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 root = try tmp.parent_dir.realPathFileAlloc(        std.testing.io,        tmp.sub_path[0..],        allocator,    );    const stdout_path = try std.fs.path.join(allocator, &.{ root, "stdout.txt" });    const stderr_path = try std.fs.path.join(allocator, &.{ root, "stderr.txt" });    const execution = try runCommand(        std.testing.io,        null,        null,        &.{"/bin/true"},        stdout_path,        stderr_path,    );    try std.testing.expect((execution.pid orelse 0) > 0);    try std.testing.expectEqual(@as(i64, 0), execution.exit_code);}test "profiling execution result retains an explicit process identity" {    const result = resultFromTerm(        0,        42,        .{ .exited = 0 },        .{},    );    try std.testing.expectEqual(@as(?u64, 42), result.pid);}test "profiling execution returns a completed process group receipt" {    var group = try spawnGroup(std.testing.io, .{        .argv = &.{"/bin/true"},        .stdin = .ignore,        .stdout = .ignore,        .stderr = .ignore,    });    const receipt = try awaitGroupUntil(        &group,        std.testing.io,        deadlineNanoseconds(sys.time.nanoTimestamp(), 1_000),        10,    );    try std.testing.expectEqual(GroupStatus.exited, receipt.status);    try std.testing.expectEqual(GroupIsolation.process_group, receipt.isolation);    try std.testing.expectEqual(@as(i64, 0), sys.process.exitCode(receipt.term.?));    try std.testing.expect(!receipt.terminate_sent);    try std.testing.expect(!receipt.kill_sent);    try expectProcessGone(@intCast(receipt.pid));}test "profiling execution times out and reaps its process group" {    var group = try spawnGroup(std.testing.io, .{        .argv = &.{ "/bin/sh", "-c", "trap '' TERM; while :; do :; done" },        .stdin = .ignore,        .stdout = .ignore,        .stderr = .ignore,    });    const receipt = try awaitGroupUntil(        &group,        std.testing.io,        deadlineNanoseconds(sys.time.nanoTimestamp(), 20),        10,    );    try std.testing.expectEqual(GroupStatus.timed_out, receipt.status);    try std.testing.expectEqual(GroupIsolation.process_group, receipt.isolation);    try std.testing.expect(receipt.terminate_sent);    try std.testing.expect(receipt.kill_sent);    try std.testing.expect(receipt.term != null);    try expectProcessGone(@intCast(receipt.pid));}test "profiling execution timeout stops a resistant descendant" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    const allocator = std.testing.allocator;    const root = try tmp.parent_dir.realPathFileAlloc(        std.testing.io,        tmp.sub_path[0..],        allocator,    );    defer allocator.free(root);    const pid_path = try std.fs.path.join(allocator, &.{ root, "descendant.pid" });    defer allocator.free(pid_path);    var group = try spawnGroup(std.testing.io, .{        .argv = &.{            "/bin/sh",            "-c",            "trap '' TERM; /bin/sh -c \"trap '' TERM; while :; do :; done\" & " ++                "echo $! > \"$1\"; wait",            "tiny-process-group-fixture",            pid_path,        },        .stdin = .ignore,        .stdout = .ignore,        .stderr = .ignore,    });    defer if (group.active()) {        _ = stopGroup(&group, std.testing.io, 10) catch {};    };    try waitForPath(pid_path);    const receipt = try awaitGroupUntil(        &group,        std.testing.io,        deadlineNanoseconds(sys.time.nanoTimestamp(), 20),        20,    );    try std.testing.expectEqual(GroupStatus.timed_out, receipt.status);    try std.testing.expect(receipt.kill_sent);    const pid_text = try sys.fs.readFileAlloc(allocator, pid_path, 64);    defer allocator.free(pid_text);    const descendant_pid = try std.fmt.parseInt(        sys.process.ProcessId,        std.mem.trim(u8, pid_text, " \t\r\n"),        10,    );    try expectProcessExited(descendant_pid);    try expectProcessGone(@intCast(receipt.pid));}/// An orphan's adopting supervisor owns reaping its zombie entry.fn expectProcessExited(process_id: sys.process.ProcessId) !void {    std.debug.assert(process_id > 0);    for (0..100) |_| {        const observed = sys.process.inspect.info(process_id) catch |err| switch (err) {            error.ProcessNotFound => return,            else => return err,        };        if (!observed.alive) return;        sys.time.sleepMilliseconds(5);    }    return error.TestExpectedProcessExit;}fn waitForPath(path: []const u8) !void {    std.debug.assert(path.len != 0);    var attempt: u8 = 0;    while (attempt < 200) : (attempt += 1) {        if (sys.fs.exists(path)) return;        sys.time.sleepMilliseconds(5);    }    return error.ProcessFixtureDidNotStart;}fn expectProcessGone(process_id: sys.process.ProcessId) !void {    std.debug.assert(process_id > 0);    var attempt: u8 = 0;    while (attempt < 100) : (attempt += 1) {        if (!try sys.process.processAlive(process_id)) return;        sys.time.sleepMilliseconds(5);    }    try std.testing.expect(false);}

Source: src/profiling/root.zig:24

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

Complete caller list for execute.commandText

11 direct callers.

Complete caller list for execute.runCommand

7 direct callers.

Audit

Definitions35
Public names35
Members54
Version26.7.0
Revisiondaab053ee433