tiny.profiling.command
Defined in tiny.profiling.
API (2)
Actions
Public operations.
Source
Source: src/profiling/command.zig
zig
const std = @import("std");const capture = @import("capture");const pretty = @import("pretty");const pretty_usage = @import("pretty_usage");const sys = @import("sys");const namespace = @import("root.zig");const analyze = namespace.analyze;const baseline = namespace.baseline;const catalog = namespace.catalog;const code_layout = namespace.code_layout;const cycle = namespace.cycle;const drift = namespace.report.drift;const driver = namespace.driver;const experiment = namespace.experiment;const execute = namespace.execute;const host = namespace.host;const iteration = namespace.iteration;const json_util = namespace.json;const scheduling = namespace.order;const plan = namespace.plan;const perturbation = namespace.perturbation;const question = namespace.question;const report = namespace.report.memory;const record = namespace.record;const recovery = namespace.recovery;const web = namespace.report;const pretty_json = pretty.json;const Allocator = std.mem.Allocator;const options_mod = namespace.options;const help_mod = namespace.help;pub fn run( memory: driver.Memory, process_io: std.Io, args: []const []const u8,) !u8 { return runWithEnv(memory, process_io, null, args);}pub fn runWithEnv( memory: driver.Memory, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, args: []const []const u8,) !u8 { return runUnchecked(memory, process_io, environ_map, args) catch |err| { try pretty_usage.Terminal.stderr(memory.command, .{}).writeErrorTextFmt( "profile", "{s}", .{@errorName(err)}, ); return 2; };}fn runUnchecked( memory: driver.Memory, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, args: []const []const u8,) !u8 { const allocator = memory.command; if (args.len == 0 or pretty_usage.isHelpArg(args[0])) { try pretty_usage.Terminal.stdout(allocator, .{}).writeHelp(help_mod.help); return 0; } if (pretty_usage.hasHelpArg(args[1..])) { return try help_mod.writeCommandHelp(allocator, args); } const command = args[0]; const rest = args[1..]; if (std.mem.eql(u8, command, "plan")) return try runPlan(allocator, rest); if (std.mem.eql(u8, command, "catalog")) return try runCatalog(allocator, rest); if (std.mem.eql(u8, command, "run")) { return try runWorkloads(memory, process_io, environ_map, rest); } if (std.mem.eql(u8, command, "experiment")) { return try runExperiment(allocator, process_io, environ_map, rest); } if (std.mem.eql(u8, command, "iteration")) { return try runIteration(allocator, process_io, environ_map, rest); } if (std.mem.eql(u8, command, "question")) { return try runQuestion(allocator, rest); } if (std.mem.eql(u8, command, "cycle")) { return try runCycle(memory, process_io, environ_map, rest); } if (std.mem.eql(u8, command, "analyze")) return try runAnalyze(allocator, rest); if (std.mem.eql(u8, command, "memory")) return try runMemory(allocator, rest); if (std.mem.eql(u8, command, "baseline")) return try runBaseline(allocator, rest); if (std.mem.eql(u8, command, "check")) return try drift.run(allocator, process_io, environ_map, rest); if (std.mem.eql(u8, command, "code-layout")) return try code_layout.run(allocator, rest); if (std.mem.eql(u8, command, "recovery")) return try recovery.run(allocator, rest); if (std.mem.eql(u8, command, "compare")) return try runCompare(allocator, rest); if (std.mem.eql(u8, command, "web")) return try runWeb(allocator, rest); try pretty_usage.Terminal.stderr(allocator, .{}).writeErrorTextFmt("profile", "unknown command: {s}", .{command}); return 2;}fn runPlan(allocator: Allocator, args: []const []const u8) !u8 { const options = try options_mod.parsePlanArgs(allocator, args); try plan.validateFilters(options.filters); if (options.json) { try writePlanJson(options); } else { try writePlanText(allocator, options); } return 0;}fn runCatalog(allocator: Allocator, args: []const []const u8) !u8 { var json = false; for (args) |arg| { if (std.mem.eql(u8, arg, "--json")) { json = true; } else { return error.UnknownArgument; } } const options = options_mod.PlanOptions{ .suite = .all, .json = json }; if (json) { try writePlanJson(options); } else { try writePlanText(allocator, options); } return 0;}fn runWorkloads( memory: driver.Memory, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, args: []const []const u8,) !u8 { const allocator = memory.command; const options = try options_mod.parseRunArgs(allocator, args); try plan.validateFilters(options.plan.filters); try options_mod.validateRunOptions(options); if (options.plan.json) { try writeRunPlanJson(allocator, process_io, options); return 0; } if (options.dry_run) { try writeRunDryRunText(allocator, process_io, environ_map, options); return 0; } const selection = options_mod.selectionFromPlan(options.plan); const run_id = options.run_id orelse try record.defaultRunId(allocator, process_io); const outcome = try driver.executeRun(memory, process_io, environ_map, .{ .selection = selection, .run_id = run_id, .output_dir = options.output_dir, .forwarded = options.forwarded, .continue_on_failure = options.continue_on_failure, .host_lanes = options.host_lanes, .execution_scope = options.execution_scope, .warmup_repeat = options.warmup_repeat, .measure_repeat = options.measure_repeat, .interleave_seed = options.interleave_seed, .capture_control = options.capture_control, .trace_allocations = options.trace_allocations, .tracy = options.tracy, .control = options.control, }); if (options.save_baseline) |name| { const entry = try baseline.save(allocator, name, outcome.paths.root); try pretty_usage.Terminal.stderr(allocator, .{}).writeTextFmt( "profile: saved baseline {s} -> {s}\n", .{ entry.name, entry.run.run_id }, ); } if (outcome.failed == 0) { try pretty_usage.Terminal.stderr(allocator, .{}).writeTextFmt( "profile: completed {d} workload(s), manifest {s}\n", .{ outcome.ran, outcome.paths.manifest }, ); return 0; } try pretty_usage.Terminal.stderr(allocator, .{}).writeErrorTextFmt( "profile", "{d} workload(s) failed", .{outcome.failed}, ); return outcome.exit_code;}fn runExperiment( allocator: Allocator, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, args: []const []const u8,) !u8 { const options = try options_mod.parseExperimentArgs(args); if (options.dry_run) { const validated = try experiment.parse.load( allocator, options.plan_path, ); if (options.json) { try copyFileToStdout(allocator, options.plan_path); } else { try pretty_usage.Terminal.stdout(allocator, .{}).writeTextFmt( "experiment: {s}\n scenario: {s}\n metric: {s}\n calibration pairs: {d}\n evaluation pairs: {d}-{d}\n", .{ validated.name, validated.scenario.name, @tagName(validated.metric), validated.design.calibration_pairs, validated.design.minimum_evaluation_pairs, validated.design.maximum_evaluation_pairs, }, ); } return 0; } const outcome = try experiment.run.execute( allocator, process_io, environ_map, .{ .plan_path = options.plan_path, .output_dir = options.output_dir, .run_id = options.run_id, }, ); if (options.json) { try copyFileToStdout(allocator, outcome.receipt_path); } else { try pretty_usage.Terminal.stderr(allocator, .{}).writeTextFmt( "profile experiment: {s} ({s}: {s})\n receipt: {s}\n", .{ @tagName(outcome.verdict), @tagName(outcome.support), outcome.reason, outcome.receipt_path, }, ); } return experimentExitCode(outcome);}fn runIteration( allocator: Allocator, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, args: []const []const u8,) !u8 { const options = try options_mod.parseIterationArgs(args); if (options.dry_run) { const validated = try iteration.parse.load( allocator, options.plan_path, ); if (options.json) { try copyFileToStdout(allocator, options.plan_path); } else { try pretty_usage.Terminal.stdout(allocator, .{}).writeTextFmt( "iteration: {s}\n step: {s}\n patch: {s}\n timeout: {d} ms\n", .{ validated.name, validated.step, validated.patch.path, validated.timeout_ms, }, ); } return 0; } const outcome = try iteration.run.execute( allocator, process_io, environ_map, .{ .plan_path = options.plan_path, .output_dir = options.output_dir, .run_id = options.run_id, }, ); if (options.json) { try copyFileToStdout(allocator, outcome.receipt_path); } else { try pretty_usage.Terminal.stderr(allocator, .{}).writeTextFmt( "profile iteration: {s} ({s})\n receipt: {s}\n", .{ if (outcome.supported) "supported" else "unsupported", outcome.reason, outcome.receipt_path, }, ); } return if (outcome.supported) 0 else 3;}fn runQuestion( allocator: Allocator, args: []const []const u8,) !u8 { const options = try options_mod.parseQuestionArgs(args); const routed = try question.route(allocator, options.route); if (options.json) { var buffer: [8192]u8 = undefined; var writer = sys.stdio.stdout().writer( std.Options.debug_io, &buffer, ); var out = pretty_json.Writer.init( &writer.interface, .minified, ); try question.writeJson(&out, routed); try writer.interface.writeByte('\n'); try writer.interface.flush(); return 0; } var terminal = pretty_usage.Terminal.stdout(allocator, .{}); try terminal.writeTextFmt( "profile question: {s}\n owner: {s}\n evidence: {s}\n", .{ @tagName(routed.kind), routed.owner, routed.evidence }, ); try writeQuestionCommand(&terminal, "capture", routed.capture); for (routed.followups, 0..) |command, index| { try terminal.writeTextFmt(" followup {d}:", .{index + 1}); try writeQuestionCommandTail(&terminal, command); } try terminal.writeTextFmt(" caveat: {s}\n", .{routed.caveat}); if (routed.placeholders.len != 0) { try terminal.writeText(" required:"); for (routed.placeholders) |placeholder| { try terminal.writeTextFmt(" {s}", .{placeholder}); } try terminal.writeText("\n"); } return 0;}fn writeQuestionCommand( terminal: *pretty_usage.Terminal, label: []const u8, command: question.Command,) !void { try terminal.writeTextFmt(" {s}:", .{label}); try writeQuestionCommandTail(terminal, command);}fn writeQuestionCommandTail( terminal: *pretty_usage.Terminal, command: question.Command,) !void { if (command.cwd) |cwd| { try terminal.writeTextFmt(" (cwd {s})", .{cwd}); } for (command.argv) |argument| { try terminal.writeTextFmt(" {s}", .{argument}); } try terminal.writeText("\n");}fn copyFileToStdout( allocator: Allocator, path: []const u8,) !void { const text = try sys.fs.readFileAlloc( allocator, path, 64 * 1024 * 1024, ); var buffer: [64 * 1024]u8 = undefined; var writer = sys.stdio.stdout().writer(std.Options.debug_io, &buffer); try writer.interface.writeAll(text); if (text.len == 0 or text[text.len - 1] != '\n') { try writer.interface.writeByte('\n'); } try writer.interface.flush();}fn experimentExitCode(outcome: experiment.run.Outcome) u8 { if (outcome.support != .supported) return 3; return switch (outcome.verdict) { .faster, .equivalent => 0, .slower => 1, .inconclusive, .unsupported => 3, };}fn runCycle( memory: driver.Memory, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, args: []const []const u8,) !u8 { const allocator = memory.command; const options = try options_mod.parseCycleArgs(allocator, args); try baseline.validateName(options.baseline_name); try plan.validateFilters(options.filters); try driver.validateMeasureRepeat( options.measure_repeat, options.host_lanes, false, false, ); try perturbation.validateCompatibility( options.capture_control, options.measure_repeat, options.execution_scope, options.host_lanes, false, false, false, ); try driver.validateInterleave( options.interleave_seed, (plan.Selection{ .suite = options.suite, .filters = options.filters, }).count(), options.measure_repeat, !options.stop_on_failure, ); return try cycle.run(memory, process_io, environ_map, options);}fn runAnalyze(allocator: Allocator, args: []const []const u8) !u8 { return try analyze.run(allocator, try options_mod.parseAnalyzeArgs(args));}fn runMemory(allocator: Allocator, args: []const []const u8) !u8 { return try report.run(allocator, try options_mod.parseMemoryArgs(args));}fn runBaseline(allocator: Allocator, args: []const []const u8) !u8 { if (args.len == 0) return error.MissingArgument; const command = args[0]; if (std.mem.eql(u8, command, "save")) { if (args.len != 3) return error.MissingArgument; const entry = try baseline.save(allocator, args[1], args[2]); try pretty_usage.Terminal.stdout(allocator, .{}).writeTextFmt("baseline {s}: {s}\n", .{ entry.name, entry.run.run_id }); return 0; } if (std.mem.eql(u8, command, "show")) { if (args.len != 2) return error.MissingArgument; const run_ref = try baseline.resolveRun(allocator, args[1]); try pretty_usage.Terminal.stdout(allocator, .{}).writeTextFmt("run {s}\nroot {s}\nresults {s}\nmanifest {s}\n", .{ run_ref.run_id, run_ref.root, run_ref.results_path, run_ref.manifest_path }); return 0; } if (std.mem.eql(u8, command, "list")) { if (args.len != 1) return error.UnknownArgument; const entries = try baseline.list(allocator); const terminal = pretty_usage.Terminal.stdout(allocator, .{}); if (entries.len == 0) { try terminal.writeText("baselines: none\n"); } else { try terminal.writeText("baselines:\n"); for (entries) |entry| try terminal.writeTextFmt(" {s}: {s} {s}\n", .{ entry.name, entry.run.run_id, entry.run.root }); } return 0; } if (std.mem.eql(u8, command, "delete")) { if (args.len != 2) return error.MissingArgument; const entry = try baseline.delete(allocator, args[1]); try pretty_usage.Terminal.stdout(allocator, .{}).writeTextFmt("deleted baseline {s}: {s}\n", .{ entry.name, entry.run.run_id }); return 0; } if (std.mem.eql(u8, command, "prune")) { const options = try options_mod.parseBaselinePruneArgs(args[1..]); const rows = try baseline.prune(allocator, options); const terminal = pretty_usage.Terminal.stdout(allocator, .{}); if (rows.len == 0) { try terminal.writeText("baselines pruned: none\n"); } else { for (rows) |row| { try terminal.writeTextFmt("{s} baseline {s}: {s} {s}\n", .{ if (row.deleted) "pruned" else "would prune", row.entry.name, row.entry.run.run_id, row.path, }); } } return 0; } return error.UnknownArgument;}fn runWeb(allocator: Allocator, args: []const []const u8) !u8 { const options = try options_mod.parseWebArgs(args); if (options.serve) { try web.serve.run(.{ .site = options.site, .address = options.address, .port = options.port, }); return 0; } const result = try web.generate(allocator, options.site); 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 {}; var out = pretty_json.Writer.init(&file_writer.interface, .minified); try out.beginObject(); try out.objectField("schema"); try out.write("tiny.profiling.web/v1"); try out.objectField("runs"); try out.write(result.runs); try out.objectField("pages"); try out.write(result.pages); try out.objectField("output"); try out.write(result.output_dir); try out.objectField("index"); try out.write(result.index_path); try out.endObject(); try file_writer.interface.writeByte('\n'); return 0; } try pretty_usage.Terminal.stdout(allocator, .{}).writeTextFmt("profile web: {d} run(s), {d} page(s)\nopen {s}\n", .{ result.runs, result.pages, result.index_path, }); return 0;}fn runCompare(allocator: Allocator, args: []const []const u8) !u8 { var stdout_buffer: [8192]u8 = undefined; var stdout_writer = sys.stdio.stdout().writer(std.Options.debug_io, &stdout_buffer); defer stdout_writer.interface.flush() catch {}; var stderr_buffer: [8192]u8 = undefined; var stderr_writer = sys.stdio.stderr().writer(std.Options.debug_io, &stderr_buffer); defer stderr_writer.interface.flush() catch {}; return try capture.compare.run(allocator, &stdout_writer.interface, &stderr_writer.interface, args);}fn selectedCount(options: options_mod.PlanOptions) usize { return options_mod.selectionFromPlan(options).count();}fn writePlanText(allocator: Allocator, options: options_mod.PlanOptions) !void { const terminal = pretty_usage.Terminal.stdout(allocator, .{}); try terminal.writeTextFmt("suite {s}: {d} workload(s)\n", .{ options.suite.name(), selectedCount(options) }); const selection = options_mod.selectionFromPlan(options); const allocation_coverage = plan.allocationCoverage(selection); try terminal.writeTextFmt( "allocation tracking: counters supported={d} unknown={d} unsupported={d}; traces supported={d} default={d} unknown={d} unsupported={d}; allocator-priority gaps counters={d} traces={d}\n", .{ allocation_coverage.counters_supported, allocation_coverage.counters_unknown, allocation_coverage.counters_unsupported, allocation_coverage.traces_supported, allocation_coverage.traces_default, allocation_coverage.traces_unknown, allocation_coverage.traces_unsupported, allocation_coverage.priority_counter_gaps, allocation_coverage.priority_trace_gaps, }, ); for (catalog.workloads) |workload| { if (!selection.selected(workload)) continue; if (workload.cwd) |cwd| { try terminal.writeTextFmt("(cd {s} && zig build {s}) {s} {s}\n", .{ cwd, workload.step, workload.name, workload.summary }); } else { try terminal.writeTextFmt("zig build {s} {s} {s}\n", .{ workload.step, workload.name, workload.summary }); } }}fn writeRunDryRunText( allocator: Allocator, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, options: options_mod.RunOptions,) !void { const terminal = pretty_usage.Terminal.stdout(allocator, .{}); const selection = options_mod.selectionFromPlan(options.plan); const run_id = options.run_id orelse try record.defaultRunId(allocator, process_io); const paths = try record.makePaths(allocator, options.output_dir, run_id); try writeRunDryRunHeader(terminal, options, run_id, paths, selection.count()); const optimize = driver.effectiveOptimize(options.host_lanes); try terminal.writeTextFmt("optimize: {s}\n", .{@tagName(optimize)}); try writeRunDryRunCounterMetadata(allocator, terminal, options, paths.root); const zig_command = execute.zig(environ_map); const control = try driver.effectiveBenchControl(options.control, options.host_lanes); const build_args = try driver.buildArgs( allocator, options.host_lanes.buildArgs(), options.tracy, optimize, ); const probe_request = try options.host_lanes.probeRequest(allocator); const tool_probe = if (probe_request) |probe_value| try host.probe(allocator, process_io, probe_value) else host.ToolProbe{ .available = true }; if (options.host_lanes.any()) { try terminal.writeTextFmt("profiler_tool: {s}\n", .{options.host_lanes.tool().?}); try terminal.writeTextFmt( "profiler_tool_version: {s}\n", .{tool_probe.version orelse "unknown"}, ); } const context = RunDryRunContext{ .allocator = allocator, .terminal = terminal, .options = options, .paths = paths, .zig_command = zig_command, .build_args = build_args, .control = control, .tool_available = tool_probe.available, }; for (catalog.workloads) |workload| { if (!selection.selected(workload)) continue; try writeRunDryRunWorkload(context, workload); }}const RunDryRunContext = struct { allocator: Allocator, terminal: pretty_usage.Terminal, options: options_mod.RunOptions, paths: record.Paths, zig_command: execute.Zig, build_args: []const []const u8, control: execute.BenchControl, tool_available: bool,};const PlannedDryRunWorkload = struct { execution: execute.Plan, paths: record.WorkloadPaths, command: []const u8, artifact_count: usize, first_execution_paths: ?record.ExecutionPaths, environment: execute.ArtifactEnv,};fn writeRunDryRunHeader( terminal: pretty_usage.Terminal, options: options_mod.RunOptions, run_id: []const u8, paths: record.Paths, workload_count: usize,) !void { try terminal.writeTextFmt( "profile dry-run: suite {s}, {d} workload(s)\n", .{ options.plan.suite.name(), workload_count }, ); try terminal.writeTextFmt("run_id: {s}\n", .{run_id}); try terminal.writeTextFmt("artifacts: {s}\n", .{paths.root}); if (options.forwarded.len != 0) { try terminal.writeText("forwarded args:"); for (options.forwarded) |arg| try terminal.writeTextFmt(" {s}", .{arg}); try terminal.writeText("\n"); } try terminal.writeTextFmt( "execution_scope: {s}\n", .{options.execution_scope.name()}, ); try terminal.writeTextFmt( "continue_on_failure: {s}\n", .{if (options.continue_on_failure) "true" else "false"}, ); try terminal.writeTextFmt("warmup_repeat: {d}\n", .{options.warmup_repeat}); try terminal.writeTextFmt("measure_repeat: {d}\n", .{options.measure_repeat}); try writeRunDryRunOrder(terminal, options, workload_count); try writeRunDryRunRetention(terminal, options);}fn writeRunDryRunOrder( terminal: pretty_usage.Terminal, options: options_mod.RunOptions, workload_count: usize,) !void { const seed = options.interleave_seed orelse { try terminal.writeText("measurement_order: blocked\n"); return; }; try terminal.writeText("measurement_order: random_interleaved\n"); try terminal.writeTextFmt("interleave_seed: {d}\n", .{seed}); try terminal.writeTextFmt( "interleave_schedule_algorithm: {s}\n", .{scheduling.schedule_algorithm}, ); try terminal.writeTextFmt( "interleave_setup_order: {s}\n", .{scheduling.setup_order}, ); try terminal.writeTextFmt( "interleave_warmup_placement: {s}\n", .{scheduling.warmup_placement}, ); try terminal.writeTextFmt( "interleave_failure_policy: {s}\n", .{scheduling.failure_policy}, ); try terminal.writeTextFmt( "planned_process_acquisitions: {d}\n", .{workload_count * @as(usize, options.measure_repeat)}, );}fn writeRunDryRunRetention( terminal: pretty_usage.Terminal, options: options_mod.RunOptions,) !void { try terminal.writeTextFmt( "capture_control_repeat: {d}\n", .{options.capture_control.repeat}, ); if (options.capture_control.enabled()) { try terminal.writeTextFmt( "capture_control_seed: {d}\n", .{options.capture_control.seed}, ); try terminal.writeText("capture_control_order: seeded_balanced_within_pair\n"); try terminal.writeText("control_output_retention: last_control_execution\n"); } try terminal.writeTextFmt( "workload_output_retention: {s}\n", .{if (options.capture_control.enabled()) "profiler_capture_contract" else "all_measured_executions"}, ); if (options.measure_repeat > 1) { try terminal.writeText( "allocation_summary_retention: all_traced_measured_executions\n", ); } if (options.warmup_repeat != 0) { try terminal.writeText("warmup_output_retention: last_warmup_execution\n"); try terminal.writeText("warmup_capture_controls: disabled\n"); }}fn writeRunDryRunCounterMetadata( allocator: Allocator, terminal: pretty_usage.Terminal, options: options_mod.RunOptions, root: []const u8,) !void { const counters = options.host_lanes.counters orelse return; const lane = try host.counters.plan(allocator, counters, root); try terminal.writeTextFmt("counter_group_size: {d}\n", .{counters.group_size}); try terminal.writeTextFmt("counter_event_groups: {d}\n", .{lane.event_groups.len}); try terminal.writeTextFmt("counter_repeat: {d}\n", .{counters.repeat}); try terminal.writeTextFmt("counter_executions: {d}\n", .{lane.runs.len});}fn planRunDryRunWorkload( context: RunDryRunContext, workload: catalog.Workload,) !PlannedDryRunWorkload { const direct_wanted = context.options.execution_scope.directFor(workload) or context.options.host_lanes.requiresDirect(); const execution = try execute.plan( context.allocator, context.zig_command, workload, context.options.forwarded, direct_wanted, context.build_args, ); const paths = try record.workloadPaths( context.allocator, context.paths, workload.name, ); const wrapped = if (execution.missing_bin) host.Wrapped{ .argv = execution.argv } else try host.wrap( context.allocator, context.options.host_lanes, execution.argv, try driver.absolutePath(context.allocator, paths.root), context.tool_available, ); const artifact_count = try plannedMeasuredArtifactCount( context.allocator, context.options, paths, context.tool_available and !execution.missing_bin, ); const first_execution_paths = if (artifact_count == 0) null else try record.executionPaths(context.allocator, paths, artifact_count, 1); return .{ .execution = execution, .paths = paths, .command = try execute.commandText( context.allocator, execution.cwd, execution.setup_argv, wrapped.argv, ), .artifact_count = artifact_count, .first_execution_paths = first_execution_paths, .environment = if (first_execution_paths) |actual| try driver.absoluteExecutionArtifactEnv(context.allocator, actual) else try driver.absoluteArtifactEnv(context.allocator, paths), };}fn writeRunDryRunWorkload( context: RunDryRunContext, workload: catalog.Workload,) !void { const planned = try planRunDryRunWorkload(context, workload); const terminal = context.terminal; try terminal.writeTextFmt("{s}\n", .{workload.name}); try terminal.writeTextFmt( " scope: {s}\n", .{if (planned.execution.direct) host.direct_scope else host.scope}, ); try terminal.writeTextFmt(" command: {s}\n", .{planned.command}); if (context.options.warmup_repeat != 0) { const warmup_command = try execute.commandText( context.allocator, planned.execution.cwd, null, planned.execution.argv, ); try terminal.writeTextFmt(" warmup_command: {s}\n", .{warmup_command}); try terminal.writeTextFmt( " warmup_stdout: {s}\n warmup_stderr: {s}\n", .{ planned.paths.warmup.stdout, planned.paths.warmup.stderr }, ); } if (planned.execution.cwd) |cwd| { try terminal.writeTextFmt(" cwd: {s}\n", .{cwd}); } if (planned.execution.missing_bin and context.options.execution_scope == .binary) { try terminal.writeText( " caveat: binary execution requested but the workload declares no benchmark binary\n", ); } if (planned.execution.missing_bin and context.options.host_lanes.any()) { try terminal.writeText( " caveat: no_direct_binary; runs whole-step without the requested lane\n", ); } try terminal.writeTextFmt(" artifacts: {s}\n", .{planned.paths.root}); try terminal.writeTextFmt( " measured_execution_artifacts: {d}\n", .{planned.artifact_count}, ); if (planned.first_execution_paths) |actual| { try terminal.writeTextFmt( " first_measured_execution_artifacts: {s}\n", .{actual.root}, ); } try writeRunDryRunCaptureControl(context, workload, planned.paths); try writeRunDryRunEnvironment(context, workload, planned.environment);}fn writeRunDryRunCaptureControl( context: RunDryRunContext, workload: catalog.Workload, paths: record.WorkloadPaths,) !void { if (!context.options.capture_control.enabled()) return; const workload_seed = perturbation.workloadSeed( context.options.capture_control.seed, workload.name, ); const orders = try perturbation.schedule( context.allocator, context.options.capture_control.repeat, workload_seed, ); const control_paths = try perturbation.makePaths(context.allocator, paths.root); try context.terminal.writeTextFmt( " capture_control_workload_seed: {d}\n", .{workload_seed}, ); try context.terminal.writeText(" capture_control_schedule:"); for (orders) |order| { try context.terminal.writeTextFmt( " {s}", .{switch (order) { .control_first => "control->capture", .capture_first => "capture->control", }}, ); } try context.terminal.writeText("\n"); try context.terminal.writeTextFmt( " control_stdout: {s}\n control_stderr: {s}\n", .{ control_paths.stdout, control_paths.stderr }, );}fn writeRunDryRunEnvironment( context: RunDryRunContext, workload: catalog.Workload, paths: execute.ArtifactEnv,) !void { const terminal = context.terminal; try terminal.writeTextFmt(" BENCH_JSONL={s}\n", .{paths.bench_jsonl}); try terminal.writeTextFmt(" BENCH_COZ_JSONL={s}\n", .{paths.coz_jsonl}); try terminal.writeTextFmt( " BENCH_COZ_ANALYSIS_JSON={s}\n", .{paths.coz_analysis}, ); if (context.options.tracy) { try terminal.writeTextFmt(" BENCH_TRACY_JSONL={s}\n", .{paths.tracy_jsonl}); try terminal.writeTextFmt( " BENCH_TRACY_SUMMARY_JSONL={s}\n", .{paths.tracy_summary}, ); } if (context.control.causal) try terminal.writeText(" BENCH_COZ_EXPERIMENTS=1\n"); if (context.control.min_time_ns) |min_time_ns| { try terminal.writeTextFmt(" BENCH_MIN_TIME_NS={d}\n", .{min_time_ns}); } if (context.control.filter) |filter| { try terminal.writeTextFmt(" BENCH_FILTER={s}\n", .{filter}); } if (context.options.trace_allocations or workload.tracesAllocationsByDefault()) { try terminal.writeTextFmt( " TINY_PROFILE_ALLOCATIONS_PATH={s}\n", .{paths.allocations}, ); }}fn writePlanJson(options: options_mod.PlanOptions) !void { var buffer: [8192]u8 = undefined; var file_writer = sys.stdio.stdout().writer(std.Options.debug_io, &buffer); defer file_writer.interface.flush() catch {}; var out = pretty_json.Writer.init(&file_writer.interface, .minified); try out.beginObject(); try out.objectField("schema"); try out.write("tiny.profiling.plan/v1"); try out.objectField("suite"); try out.write(options.suite.name()); try out.objectField("workload_count"); try out.write(selectedCount(options)); const selection = options_mod.selectionFromPlan(options); try out.objectField("allocation_coverage"); try writeAllocationCoverageJson(&out, selection); try out.objectField("workloads"); try out.beginArray(); for (catalog.workloads) |workload| { if (!selection.selected(workload)) continue; try writeWorkloadJson(&out, workload); } try out.endArray(); try out.endObject(); try file_writer.interface.writeByte('\n');}fn writeRunPlanJson( allocator: Allocator, process_io: std.Io, options: options_mod.RunOptions,) !void { const run_id = options.run_id orelse try record.defaultRunId(allocator, process_io); const paths = try record.makePaths(allocator, options.output_dir, run_id); const probe_request = try options.host_lanes.probeRequest(allocator); const tool_available = if (probe_request) |request| (try host.probe(allocator, process_io, request)).available else true; var buffer: [8192]u8 = undefined; var file_writer = sys.stdio.stdout().writer(std.Options.debug_io, &buffer); defer file_writer.interface.flush() catch {}; try writeRunPlanJsonDocument( allocator, &file_writer.interface, options, paths, tool_available, );}fn writeRunPlanJsonDocument( allocator: Allocator, writer: *std.Io.Writer, options: options_mod.RunOptions, paths: record.Paths, tool_available: bool,) !void { var out = pretty_json.Writer.init(writer, .minified); try out.beginObject(); try out.objectField("schema"); try out.write("tiny.profiling.run-plan/v1"); try out.objectField("run_id"); try out.write(std.fs.path.basename(paths.root)); try out.objectField("artifact_root"); try out.write(paths.root); try out.objectField("suite"); try out.write(options.plan.suite.name()); try out.objectField("measure_repeat"); try out.write(options.measure_repeat); try out.objectField("workload_output_retention"); try out.write(if (options.capture_control.enabled()) "profiler_capture_contract" else "all_measured_executions"); try out.objectField("workloads"); try writeRunPlanWorkloadsJson( allocator, &out, options, paths, tool_available, ); try out.endObject(); try writer.writeByte('\n');}fn writeRunPlanWorkloadsJson( allocator: Allocator, out: *pretty_json.Writer, options: options_mod.RunOptions, paths: record.Paths, tool_available: bool,) !void { const selection = options_mod.selectionFromPlan(options.plan); try out.beginArray(); for (catalog.workloads) |workload| { if (!selection.selected(workload)) continue; const workload_paths = try record.workloadPaths( allocator, paths, workload.name, ); const direct_wanted = options.execution_scope.directFor(workload) or options.host_lanes.requiresDirect(); const count = try plannedMeasuredArtifactCount( allocator, options, workload_paths, tool_available and (!direct_wanted or workload.bin != null), ); try out.beginObject(); try out.objectField("name"); try out.write(workload.name); try out.objectField("artifact_root"); try out.write(workload_paths.root); try out.objectField("measured_execution_artifact_count"); try out.write(count); try out.objectField("measured_execution_artifacts"); try writeRunPlanExecutionArtifacts(allocator, out, workload_paths, count); try out.endObject(); } try out.endArray();}fn plannedMeasuredArtifactCount( allocator: Allocator, options: options_mod.RunOptions, paths: record.WorkloadPaths, counter_lane_available: bool,) !usize { if (options.capture_control.enabled()) return 0; if (options.host_lanes.counters) |counter_options| { if (!counter_lane_available) return options.measure_repeat; const lane = try host.counters.plan(allocator, counter_options, paths.root); return lane.runs.len; } return options.measure_repeat;}fn writeRunPlanExecutionArtifacts( allocator: Allocator, out: *pretty_json.Writer, workload_paths: record.WorkloadPaths, count: usize,) !void { try out.beginArray(); for (0..count) |index| { const paths = try record.executionPaths( allocator, workload_paths, count, index + 1, ); try out.beginObject(); try out.objectField("index"); try out.write(index + 1); inline for (.{ .{ "root", paths.root }, .{ "stdout", paths.stdout }, .{ "stderr", paths.stderr }, .{ "bench_jsonl", paths.bench_jsonl }, .{ "structured", paths.structured }, }) |field| { try out.objectField(field[0]); try out.write(field[1]); } try out.endObject(); } try out.endArray();}fn writeAllocationCoverageJson( out: *pretty_json.Writer, selection: plan.Selection,) !void { const coverage = plan.allocationCoverage(selection); try out.beginObject(); try out.objectField("workloads"); try out.write(coverage.workloads); try out.objectField("counters"); try out.beginObject(); try out.objectField("supported"); try out.write(coverage.counters_supported); try out.objectField("unknown"); try out.write(coverage.counters_unknown); try out.objectField("unsupported"); try out.write(coverage.counters_unsupported); try out.endObject(); try out.objectField("traces"); try out.beginObject(); try out.objectField("supported"); try out.write(coverage.traces_supported); try out.objectField("default_on"); try out.write(coverage.traces_default); try out.objectField("unknown"); try out.write(coverage.traces_unknown); try out.objectField("unsupported"); try out.write(coverage.traces_unsupported); try out.endObject(); try out.objectField("allocator_priority"); try out.beginObject(); try out.objectField("workloads"); try out.write(coverage.priority_workloads); try out.objectField("counters_supported"); try out.write(coverage.priority_counters_supported); try out.objectField("traces_supported"); try out.write(coverage.priority_traces_supported); try out.objectField("counter_gaps"); try out.beginArray(); for (catalog.workloads) |workload| { if (selection.selected(workload) and workload.isAllocationPriority() and !workload.supportsAllocationCounters()) { try out.write(workload.name); } } try out.endArray(); try out.objectField("trace_gaps"); try out.beginArray(); for (catalog.workloads) |workload| { if (selection.selected(workload) and workload.isAllocationPriority() and !workload.supportsAllocationTrace()) { try out.write(workload.name); } } try out.endArray(); try out.endObject(); try out.endObject();}fn writeWorkloadJson(out: *pretty_json.Writer, workload: catalog.Workload) !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("local_step"); try out.write(workload.localStep()); try out.objectField("cwd"); try out.write(workload.cwd); try out.objectField("summary"); try out.write(workload.summary); try out.objectField("tier"); try out.write(@tagName(workload.tier)); try out.objectField("surface"); try out.write(@tagName(workload.surface)); try out.objectField("forwards_args"); try out.write(workload.forwards_args); try out.objectField("allocation_tracking"); try out.beginObject(); try out.objectField("counters"); try out.write(@tagName(workload.allocation_tracking.counters)); try out.objectField("trace"); try out.write(@tagName(workload.allocation_tracking.trace)); try out.objectField("baseline"); try out.write(@tagName(workload.allocationBaselinePolicy())); try out.objectField("regression_budget_percent"); try out.write(workload.allocationBudgetPercent()); try out.endObject(); try out.objectField("trace_allocations"); try out.write(workload.tracesAllocationsByDefault()); try out.objectField("history_paths"); try out.write(workload.history_paths); try out.objectField("thresholds"); try out.beginObject(); try out.objectField("wall_percent"); try out.write(workload.wall_threshold_percent); try out.objectField("rss_percent"); try out.write(workload.rss_threshold_percent); try out.objectField("metric_percent"); try out.write(workload.metric_threshold_percent); try out.endObject(); try out.objectField("priority"); try out.beginObject(); try out.objectField("weight"); try out.write(workload.priorityWeight()); try out.objectField("component"); try out.write(workload.priorityComponent()); try out.endObject(); try out.endObject();}test "profiling CLI writes measured execution artifacts dry run" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); const options = options_mod.RunOptions{ .plan = .{ .suite = .all, .filters = &.{"gpalloc.allocator"} }, .dry_run = true, .run_id = "run-a", .measure_repeat = 15, }; const paths = try record.makePaths(allocator, "zig-out/profiling", "run-a"); var output: std.Io.Writer.Allocating = .init(allocator); try writeRunPlanJsonDocument(allocator, &output.writer, options, paths, true); const value = try std.json.parseFromSliceLeaky( std.json.Value, allocator, output.written(), .{}, ); const object = try json_util.object(value); try std.testing.expectEqualStrings( "tiny.profiling.run-plan/v1", json_util.string(object.get("schema")).?, ); try std.testing.expectEqualStrings( "all_measured_executions", json_util.string(object.get("workload_output_retention")).?, ); const workloads = try json_util.array(object.get("workloads").?); try std.testing.expectEqual(@as(usize, 1), workloads.items.len); const workload = try json_util.object(workloads.items[0]); try std.testing.expectEqual( @as(u64, 15), json_util.asU64(workload.get("measured_execution_artifact_count")).?, ); const artifacts = try json_util.array( workload.get("measured_execution_artifacts").?, ); try std.testing.expectEqual(@as(usize, 15), artifacts.items.len); const first = try json_util.object(artifacts.items[0]); const last = try json_util.object(artifacts.items[14]); try std.testing.expectEqual(@as(u64, 1), json_util.asU64(first.get("index")).?); try std.testing.expectEqual(@as(u64, 15), json_util.asU64(last.get("index")).?); try std.testing.expect(std.mem.endsWith( u8, json_util.string(first.get("root")).?, "/001", )); inline for (.{ .{ "stdout", "/001/stdout.txt" }, .{ "bench_jsonl", "/001/bench.jsonl" }, .{ "structured", "/001/structured.jsonl" }, }) |field| { try std.testing.expect(std.mem.endsWith( u8, json_util.string(first.get(field[0])).?, field[1], )); } try std.testing.expect(std.mem.endsWith( u8, json_util.string(last.get("root")).?, "/015", )); try std.testing.expect( std.mem.indexOf(u8, output.written(), "last_execution") == null, );}test "profiling CLI falls back from unavailable counter lane artifact counts" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); const options = options_mod.RunOptions{ .host_lanes = .{ .counters = .{ .events = "cycles,instructions", .repeat = 3, .group_size = 1, } }, .measure_repeat = 2, }; const run_paths = try record.makePaths(allocator, "zig-out/profiling", "run-a"); const paths = try record.workloadPaths(allocator, run_paths, "w"); const lane = try host.counters.plan( allocator, options.host_lanes.counters.?, paths.root, ); try std.testing.expectEqual( lane.runs.len, try plannedMeasuredArtifactCount(allocator, options, paths, true), ); try std.testing.expectEqual( @as(usize, 2), try plannedMeasuredArtifactCount(allocator, options, paths, false), );}test "profiling CLI plans capture-control artifact contract" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); const options = options_mod.RunOptions{ .plan = .{ .suite = .all, .filters = &.{"gpalloc.allocator"} }, .dry_run = true, .run_id = "run-a", .capture_control = .{ .repeat = 2 }, }; const paths = try record.makePaths(allocator, "zig-out/profiling", "run-a"); var output: std.Io.Writer.Allocating = .init(allocator); try writeRunPlanJsonDocument(allocator, &output.writer, options, paths, true); const value = try std.json.parseFromSliceLeaky( std.json.Value, allocator, output.written(), .{}, ); const object = try json_util.object(value); try std.testing.expectEqualStrings( "profiler_capture_contract", json_util.string(object.get("workload_output_retention")).?, ); const workloads = try json_util.array(object.get("workloads").?); const workload = try json_util.object(workloads.items[0]); try std.testing.expectEqual( @as(u64, 0), json_util.asU64(workload.get("measured_execution_artifact_count")).?, ); const artifacts = try json_util.array( workload.get("measured_execution_artifacts").?, ); try std.testing.expectEqual(@as(usize, 0), artifacts.items.len);}Source: src/profiling/root.zig:16
zig
pub const command = @import("command.zig");Audit
| Definitions | 3 |
|---|---|
| Public names | 3 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |