tiny.profiling.iteration.run
Defined in iteration.
API (3)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: src/profiling/iteration/root.zig:5
zig
pub const run = @import("run.zig");Source: src/profiling/iteration/run.zig
zig
const std = @import("std");const sys = @import("sys");const profiling = @import("../root.zig");const batch = @import("batch.zig");const model = @import("model.zig");const parse = @import("parse.zig");const receipt = @import("receipt.zig");const environment = profiling.environment;const process = profiling.execute;const fingerprint = profiling.fingerprint;const host = profiling.host;const record = profiling.record;const poll_ms: u64 = 25;const timing_runner_option = "-Dtiming-runner=true";pub const Options = struct { plan_path: []const u8, output_dir: []const u8 = record.default_output_dir, run_id: ?[]const u8 = null,};pub const Outcome = struct { run_id: []const u8, artifact_root: []const u8, receipt_path: []const u8, supported: bool, reason: []const u8,};const Paths = struct { root: []const u8, receipt: []const u8, backup: []const u8, watcher_receipts: []const u8, clean_receipts: []const u8, clean_cache: []const u8, watcher_stdout: []const u8, watcher_stderr: []const u8, clean_stdout: []const u8, clean_stderr: []const u8,};const Source = struct { path: []const u8, original: []const u8, edited: []const u8, permissions: sys.fs.FilePermissions, before: fingerprint.File,};const PhaseClock = struct { started_unix_ns: i128, started_monotonic_ns: i128, host_state_before: host.state.Snapshot, fn start() PhaseClock { return .{ .started_unix_ns = sys.time.realNanoTimestamp(), .started_monotonic_ns = sys.time.nanoTimestamp(), .host_state_before = host.state.capture(), }; }};pub fn execute( allocator: std.mem.Allocator, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, options: Options,) !Outcome { const started_unix_ns = sys.time.realNanoTimestamp(); const plan_path = try sys.fs.realPathAlloc(allocator, options.plan_path); const plan_before = try fingerprint.inspect(plan_path); const plan = try parse.load(allocator, plan_path); const plan_after = try fingerprint.inspect(plan_path); if (!sameFile(plan_before, plan_after)) return error.InputChanged; const run_id = options.run_id orelse try record.prefixedRunId(allocator, process_io, "iteration"); try validateRunId(run_id); const paths = try preparePaths( allocator, options.output_dir, run_id, ); const repository_root = try sys.fs.realPathAlloc(allocator, "."); const source = try prepareSource( allocator, repository_root, plan, ); try sys.fs.writeFile(paths.backup, source.original); const host_state_start = host.state.capture(); const host_environment = try environment.collect( allocator, process_io, ); const watcher_token = try std.fmt.allocPrint( allocator, "{s}-watch", .{run_id}, ); const clean_token = try std.fmt.allocPrint( allocator, "{s}-clean", .{run_id}, ); const watcher_argv = try watcherArguments( allocator, repository_root, plan, paths.watcher_receipts, watcher_token, ); const clean_argv = try cleanArguments( allocator, repository_root, plan, paths.clean_receipts, clean_token, ); const baseline_clock = PhaseClock.start(); var watcher = try spawnLogged( process_io, environ_map, repository_root, watcher_argv, paths.watcher_stdout, paths.watcher_stderr, ); var watcher_open = true; defer if (watcher_open) { _ = process.stopGroup( &watcher, process_io, process.default_termination_grace_ms, ) catch {}; }; const baseline = try waitWatcherPhase( allocator, &watcher, paths.watcher_receipts, watcher_token, 0, null, plan.timeout_ms, "baseline", baseline_clock, ); const edit_clock = PhaseClock.start(); const edited_file = try replaceExact( allocator, source, source.original, source.edited, run_id, ); var source_edited = true; errdefer if (source_edited) { _ = replaceExact( allocator, source, source.edited, source.original, run_id, ) catch {}; }; const edit = try waitWatcherPhase( allocator, &watcher, paths.watcher_receipts, watcher_token, 1, baseline.evidence.marker.finished_unix_ns, plan.timeout_ms, "edit", edit_clock, ); const clean_clock = PhaseClock.start(); const clean = try runCleanPhase( allocator, process_io, environ_map, repository_root, clean_argv, paths, clean_token, plan.timeout_ms, clean_clock, ); const revert_clock = PhaseClock.start(); const reverted_file = try replaceExact( allocator, source, source.edited, source.original, run_id, ); source_edited = false; const revert = try waitWatcherPhase( allocator, &watcher, paths.watcher_receipts, watcher_token, 2, edit.evidence.marker.finished_unix_ns, plan.timeout_ms, "revert", revert_clock, ); _ = try process.stopGroup( &watcher, process_io, process.default_termination_grace_ms, ); watcher_open = false; const assessment = assess( baseline.evidence, edit.evidence, clean.evidence, revert.evidence, ); try receipt.writeFile( allocator, paths.receipt, .{ .run_id = run_id, .artifact_root = paths.root, .plan_path = plan_path, .plan_file = plan_after, .plan = plan, .repository_root = repository_root, .started_unix_ns = started_unix_ns, .finished_unix_ns = sys.time.realNanoTimestamp(), .host_environment = host_environment, .host_state_start = host_state_start, .source = .{ .path = source.path, .backup_path = paths.backup, .before = source.before, .edited = edited_file, .reverted = reverted_file, }, .watcher_argv = watcher_argv, .clean_argv = clean_argv, .artifacts = .{ .watcher_stdout = paths.watcher_stdout, .watcher_stderr = paths.watcher_stderr, .clean_stdout = paths.clean_stdout, .clean_stderr = paths.clean_stderr, .watcher_receipts = paths.watcher_receipts, .clean_receipts = paths.clean_receipts, .clean_cache = paths.clean_cache, }, .baseline = baseline, .edit = edit, .clean = clean, .revert = revert, .assessment = assessment, }, ); return .{ .run_id = run_id, .artifact_root = paths.root, .receipt_path = paths.receipt, .supported = assessment.supported, .reason = assessment.reason, };}fn preparePaths( allocator: std.mem.Allocator, output_dir: []const u8, run_id: []const u8,) !Paths { const requested_root = try std.fs.path.join( allocator, &.{ output_dir, run_id }, ); if (sys.fs.exists(requested_root)) { return error.IterationRunExists; } try sys.fs.createDirPath(requested_root); const root = try sys.fs.realPathAlloc(allocator, requested_root); const paths = Paths{ .root = root, .receipt = try join(allocator, root, "receipt.json"), .backup = try join(allocator, root, "source-before"), .watcher_receipts = try join( allocator, root, "watch-receipts", ), .clean_receipts = try join( allocator, root, "clean-receipts", ), .clean_cache = try join(allocator, root, "clean-cache"), .watcher_stdout = try join( allocator, root, "watcher.stdout", ), .watcher_stderr = try join( allocator, root, "watcher.stderr", ), .clean_stdout = try join( allocator, root, "clean.stdout", ), .clean_stderr = try join( allocator, root, "clean.stderr", ), }; try sys.fs.createDirPath(paths.watcher_receipts); try sys.fs.createDirPath(paths.clean_receipts); try sys.fs.createDirPath(paths.clean_cache); return paths;}fn prepareSource( allocator: std.mem.Allocator, repository_root: []const u8, plan: model.Plan,) !Source { const requested = try std.fs.path.resolve( allocator, &.{ repository_root, plan.patch.path }, ); const path = try sys.fs.realPathAlloc(allocator, requested); if (!pathWithin(repository_root, path) or std.mem.eql(u8, repository_root, path)) { return error.IterationPatchOutsideRepository; } const original = try sys.fs.readFileAlloc( allocator, path, model.maximum_source_bytes, ); const first = std.mem.indexOf( u8, original, plan.patch.before, ) orelse return error.IterationPatchNotFound; if (std.mem.indexOf( u8, original[first + 1 ..], plan.patch.before, ) != null) { return error.IterationPatchRepeated; } const edited_length = std.math.add( usize, original.len - plan.patch.before.len, plan.patch.after.len, ) catch return error.IterationSourceTooLarge; if (edited_length > model.maximum_source_bytes) { return error.IterationSourceTooLarge; } const edited = try allocator.alloc(u8, edited_length); @memcpy(edited[0..first], original[0..first]); @memcpy( edited[first..][0..plan.patch.after.len], plan.patch.after, ); const suffix = original[first + plan.patch.before.len ..]; @memcpy( edited[first + plan.patch.after.len ..], suffix, ); const stat = try sys.fs.statFile(path); const before = try fingerprint.inspect(path); if (before.bytes != original.len or !std.mem.eql(u8, &before.sha256, &digest(original))) { return error.InputChanged; } return .{ .path = path, .original = original, .edited = edited, .permissions = stat.permissions, .before = before, };}fn replaceExact( allocator: std.mem.Allocator, source: Source, expected: []const u8, replacement: []const u8, run_id: []const u8,) !fingerprint.File { const current = try sys.fs.readFileAlloc( allocator, source.path, model.maximum_source_bytes, ); if (!std.mem.eql(u8, current, expected)) { return error.IterationSourceChanged; } const pending = try std.fmt.allocPrint( allocator, "{s}.tiny-iteration-{s}.pending", .{ source.path, run_id }, ); defer sys.fs.deleteFile(pending) catch {}; var file = try sys.fs.createFile(pending, .{ .truncate = true }); var open = true; defer if (open) file.close(sys.fs.debugIo()); try sys.fs.writeHandleAll(file, replacement); file.close(sys.fs.debugIo()); open = false; try sys.fs.setFilePermissions( pending, source.permissions, .{}, ); try sys.fs.rename(pending, source.path); const result = try fingerprint.inspect(source.path); const expected_digest = digest(replacement); if (result.bytes != replacement.len or !std.mem.eql(u8, &result.sha256, &expected_digest)) { return error.IterationSourceWriteMismatch; } return result;}fn watcherArguments( allocator: std.mem.Allocator, repository_root: []const u8, plan: model.Plan, receipt_directory: []const u8, token: []const u8,) ![]const []const u8 { const iterate = try std.fs.path.join( allocator, &.{ repository_root, "skills/worktree/iterate" }, ); var arguments: std.ArrayList([]const u8) = .empty; try arguments.appendSlice( allocator, &.{ iterate, "iteration-observe" }, ); try arguments.appendSlice(allocator, plan.args); try appendGeneratedOptions( allocator, &arguments, plan.step, receipt_directory, token, ); return try arguments.toOwnedSlice(allocator);}fn cleanArguments( allocator: std.mem.Allocator, repository_root: []const u8, plan: model.Plan, receipt_directory: []const u8, token: []const u8,) ![]const []const u8 { const zig = try std.fs.path.join( allocator, &.{ repository_root, "skills/worktree/bin/zig" }, ); var arguments: std.ArrayList([]const u8) = .empty; try arguments.appendSlice( allocator, &.{ zig, "build", "-fno-incremental", "iteration-observe", }, ); try arguments.appendSlice(allocator, plan.args); try appendGeneratedOptions( allocator, &arguments, plan.step, receipt_directory, token, ); return try arguments.toOwnedSlice(allocator);}fn appendGeneratedOptions( allocator: std.mem.Allocator, arguments: *std.ArrayList([]const u8), step: []const u8, receipt_directory: []const u8, token: []const u8,) !void { try arguments.append(allocator, timing_runner_option); try arguments.append( allocator, try option(allocator, "iteration-step", step), ); try arguments.append( allocator, try option( allocator, "iteration-receipt-dir", receipt_directory, ), ); try arguments.append( allocator, try option(allocator, "iteration-receipt-token", token), ); try arguments.append( allocator, try option( allocator, "timing-receipt-dir", receipt_directory, ), ); try arguments.append( allocator, try option(allocator, "timing-receipt-token", token), );}fn option( allocator: std.mem.Allocator, name: []const u8, value: []const u8,) ![]const u8 { return try std.fmt.allocPrint( allocator, "-D{s}={s}", .{ name, value }, );}fn spawnLogged( process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, cwd: []const u8, argv: []const []const u8, stdout_path: []const u8, stderr_path: []const u8,) !process.Group { const stdout_file = try sys.fs.createFile( stdout_path, .{ .truncate = true }, ); defer sys.fs.closeHandle(stdout_file); const stderr_file = try sys.fs.createFile( stderr_path, .{ .truncate = true }, ); defer sys.fs.closeHandle(stderr_file); return try process.spawnGroup(process_io, .{ .argv = argv, .cwd = .{ .path = cwd }, .environ_map = environ_map, .stdin = .ignore, .stdout = .{ .file = stdout_file }, .stderr = .{ .file = stderr_file }, });}fn waitWatcherPhase( allocator: std.mem.Allocator, watcher: *process.Group, directory: []const u8, token: []const u8, marker_index: usize, previous_marker_unix_ns: ?i128, timeout_ms: u64, name: []const u8, clock: PhaseClock,) !receipt.Phase { const deadline = process.deadlineNanoseconds( clock.started_monotonic_ns, timeout_ms, ); while (true) { var scratch_state = std.heap.ArenaAllocator.init(allocator); defer scratch_state.deinit(); if (try batch.markerAt( scratch_state.allocator(), directory, token, marker_index, )) |temporary_marker| { const marker = batch.Marker{ .path = try allocator.dupe( u8, temporary_marker.path, ), .finished_unix_ns = temporary_marker.finished_unix_ns, }; return finishPhase( allocator, directory, token, previous_marker_unix_ns, marker, name, clock, ); } if (!watcher.active()) return error.IterationWatcherExited; if (try process.pollGroup(watcher)) |_| { return error.IterationWatcherExited; } if (sys.time.nanoTimestamp() >= deadline) { return error.IterationWatcherTimeout; } sys.time.sleepMilliseconds(poll_ms); }}fn runCleanPhase( allocator: std.mem.Allocator, process_io: std.Io, environ_map: ?*const sys.process.Environ.Map, repository_root: []const u8, argv: []const []const u8, paths: Paths, token: []const u8, timeout_ms: u64, clock: PhaseClock,) !receipt.Phase { var environment_map = if (environ_map) |base| try base.clone(allocator) else sys.process.Environ.Map.init(allocator); defer environment_map.deinit(); try environment_map.put( "ZIG_LOCAL_CACHE_DIR", paths.clean_cache, ); var child = try spawnLogged( process_io, &environment_map, repository_root, argv, paths.clean_stdout, paths.clean_stderr, ); errdefer { _ = process.stopGroup( &child, process_io, process.default_termination_grace_ms, ) catch {}; } const execution = try process.awaitGroupUntil( &child, process_io, process.deadlineNanoseconds( clock.started_monotonic_ns, timeout_ms, ), process.default_termination_grace_ms, ); if (execution.status == .timed_out) { return error.IterationCommandTimeout; } const term = execution.term orelse return error.MissingIterationCommandTermination; if (sys.process.exitCode(term) != 0) { return error.IterationCleanBuildFailed; } var scratch_state = std.heap.ArenaAllocator.init(allocator); defer scratch_state.deinit(); const temporary_marker = (try batch.markerAt( scratch_state.allocator(), paths.clean_receipts, token, 0, )) orelse return error.MissingIterationCleanMarker; const marker = batch.Marker{ .path = try allocator.dupe(u8, temporary_marker.path), .finished_unix_ns = temporary_marker.finished_unix_ns, }; return finishPhase( allocator, paths.clean_receipts, token, null, marker, "clean", clock, );}fn finishPhase( allocator: std.mem.Allocator, directory: []const u8, token: []const u8, previous_marker_unix_ns: ?i128, marker: batch.Marker, name: []const u8, clock: PhaseClock,) !receipt.Phase { const finished_unix_ns = sys.time.realNanoTimestamp(); const finished_monotonic_ns = sys.time.nanoTimestamp(); return .{ .name = name, .external_started_unix_ns = clock.started_unix_ns, .external_finished_unix_ns = finished_unix_ns, .external_elapsed_ns_observed = @max( finished_monotonic_ns - clock.started_monotonic_ns, 0, ), .host_state_before = clock.host_state_before, .host_state_after = host.state.capture(), .evidence = try batch.load( allocator, directory, token, previous_marker_unix_ns, marker, ), };}fn assess( baseline: batch.Batch, edit: batch.Batch, clean: batch.Batch, revert: batch.Batch,) receipt.Assessment { const edit_clean = equivalence(edit, clean); const baseline_revert = equivalence(baseline, revert); const receipts_present = baseline.aggregate.receipt_count > 0 and edit.aggregate.receipt_count > 0 and clean.aggregate.receipt_count > 0 and revert.aggregate.receipt_count > 0; const tests_executed = baseline.aggregate.executed_test_count > 0 and edit.aggregate.executed_test_count > 0 and clean.aggregate.executed_test_count > 0 and revert.aggregate.executed_test_count > 0; const supported = receipts_present and tests_executed and edit_clean.supported() and baseline_revert.supported(); const reason = if (!receipts_present) "no_machine_test_receipts" else if (!tests_executed) "no_tests_executed" else if (!edit_clean.count_equal) "edit_clean_test_counts_differ" else if (!edit_clean.semantic_equal) "edit_clean_semantic_output_differs" else if (!edit_clean.zig_version_equal) "edit_clean_zig_versions_differ" else if (!baseline_revert.count_equal) "baseline_revert_test_counts_differ" else if (!baseline_revert.semantic_equal) "baseline_revert_semantic_output_differs" else if (!baseline_revert.zig_version_equal) "baseline_revert_zig_versions_differ" else "clean_equivalence_and_revert_reproducibility_supported"; return .{ .supported = supported, .reason = reason, .edit_clean = edit_clean, .baseline_revert = baseline_revert, };}fn equivalence( left: batch.Batch, right: batch.Batch,) receipt.Equivalence { return .{ .count_equal = countsEqual( left.aggregate, right.aggregate, ), .semantic_equal = std.mem.eql( u8, &left.aggregate.semantic_sha256, &right.aggregate.semantic_sha256, ), .zig_version_equal = zigVersionsEqual(left, right), };}fn countsEqual( left: batch.Aggregate, right: batch.Aggregate,) bool { return left.receipt_count == right.receipt_count and left.selected_test_count == right.selected_test_count and left.executed_test_count == right.executed_test_count and left.passed_test_count == right.passed_test_count and left.skipped_test_count == right.skipped_test_count and left.failed_test_count == right.failed_test_count;}fn zigVersionsEqual( left: batch.Batch, right: batch.Batch,) bool { if (left.receipts.len == 0 or right.receipts.len == 0) { return false; } const expected = left.receipts[0].zig_version; for (left.receipts) |item| { if (!std.mem.eql(u8, item.zig_version, expected)) return false; } for (right.receipts) |item| { if (!std.mem.eql(u8, item.zig_version, expected)) return false; } return true;}fn digest(bytes: []const u8) fingerprint.Digest { var result: fingerprint.Digest = undefined; std.crypto.hash.sha2.Sha256.hash(bytes, &result, .{}); return result;}fn sameFile( left: fingerprint.File, right: fingerprint.File,) bool { return left.bytes == right.bytes and std.mem.eql(u8, &left.sha256, &right.sha256);}fn pathWithin(root: []const u8, path: []const u8) bool { if (!std.mem.startsWith(u8, path, root) or path.len <= root.len) { return false; } return std.fs.path.isSep(path[root.len]);}fn join( allocator: std.mem.Allocator, root: []const u8, name: []const u8,) ![]const u8 { return try std.fs.path.join(allocator, &.{ root, name });}fn validateRunId(run_id: []const u8) !void { if (run_id.len == 0 or run_id.len > 100 or std.mem.eql(u8, run_id, ".") or std.mem.eql(u8, run_id, "..")) { return error.InvalidIterationRunId; } for (run_id) |byte| { if (!std.ascii.isAlphanumeric(byte) and byte != '.' and byte != '_' and byte != '-') { return error.InvalidIterationRunId; } }}test "iteration equivalence gates deterministic facts only" { const digest_value = digest("same"); const item = batch.TestReceipt{ .path = "receipt", .zig_version = "zig", .started_unix_ns = 1, .finished_unix_ns = 2, .shard_count = 1, .shard_index = 0, .selected_test_count = 1, .executed_test_count = 1, .passed_test_count = 1, .skipped_test_count = 0, .failed_test_count = 0, .test_ns_observed = 10, .teardown_ns_observed = 1, .semantic_sha256 = digest_value, }; const evidence = batch.Batch{ .marker = .{ .path = "marker", .finished_unix_ns = 3, }, .receipts = &.{item}, .aggregate = .{ .receipt_count = 1, .selected_test_count = 1, .executed_test_count = 1, .passed_test_count = 1, .skipped_test_count = 0, .failed_test_count = 0, .test_ns_observed = 10, .teardown_ns_observed = 1, .semantic_sha256 = digest_value, }, }; const result = assess(evidence, evidence, evidence, evidence); try std.testing.expect(result.supported);}Complete call list for iteration.run.execute
16 direct calls.
tiny.profiling.environment.collect[function] atsrc/profiling/environment.zig:51tiny.profiling.fingerprint.inspect[function] atsrc/profiling/fingerprint.zig:16tiny.profiling.host.state.capture[function] atsrc/profiling/host/state.zig:75src.profiling.iteration.run.PhaseClock.start[function] — private; no exact target atsrc/profiling/iteration/run.zig:58in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.assess[function] — private; no exact target atsrc/profiling/iteration/run.zig:739in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.cleanArguments[function] — private; no exact target atsrc/profiling/iteration/run.zig:470in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.preparePaths[function] — private; no exact target atsrc/profiling/iteration/run.zig:277in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.prepareSource[function] — private; no exact target atsrc/profiling/iteration/run.zig:333in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.replaceExact[function] — private; no exact target atsrc/profiling/iteration/run.zig:400in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.runCleanPhase[function] — private; no exact target atsrc/profiling/iteration/run.zig:632in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.sameFile[function] — private; no exact target atsrc/profiling/iteration/run.zig:840in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.spawnLogged[function] — private; no exact target atsrc/profiling/iteration/run.zig:552in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.validateRunId[function] — private; no exact target atsrc/profiling/iteration/run.zig:865in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.waitWatcherPhase[function] — private; no exact target atsrc/profiling/iteration/run.zig:580in nearest public ownertiny.profiling.iteration.runsrc.profiling.iteration.run.watcherArguments[function] — private; no exact target atsrc/profiling/iteration/run.zig:443in nearest public ownertiny.profiling.iteration.runtiny.profiling.record.prefixedRunId[function] atsrc/profiling/record.zig:194
Audit
| Definitions | 4 |
|---|---|
| Public names | 4 |
| Members | 8 |
| Version | 26.7.0 |
| Revision | daab053ee433 |