zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit c0504a8fa87f66c5ddb2c02c35c10c945f4bb8b9 (tree)
parent 1c8d50e0624c7099e82a35c78b135c59e2dbeaa3
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Sat, 23 May 2026 21:46:06 -0700

fuzzer: get it working again

Diffstat:
Mlib/compiler/Maker.zig | 2++
Mlib/compiler/Maker/Fuzz.zig | 187++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mlib/compiler/Maker/Graph.zig | 1+
Mlib/compiler/Maker/Step/Compile.zig | 15++++++---------
Mlib/compiler/Maker/Step/Run.zig | 16++++++++++++++--
5 files changed, 146 insertions(+), 75 deletions(-)

diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig @@ -317,6 +317,7 @@ pub fn main(init: process.Init.Minimal) !void { if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) }; } else if (mem.eql(u8, arg, "--fuzz")) { fuzz = .{ .forever = undefined }; + graph.fuzzing = true; if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) }; } else if (mem.startsWith(u8, arg, "--fuzz=")) { const value = arg["--fuzz=".len..]; @@ -352,6 +353,7 @@ pub fn main(init: process.Init.Minimal) !void { .amount = normalized_amount, }, }; + graph.fuzzing = true; } else if (mem.eql(u8, arg, "-fincremental")) { graph.incremental = true; } else if (mem.eql(u8, arg, "-fno-incremental")) { diff --git a/lib/compiler/Maker/Fuzz.zig b/lib/compiler/Maker/Fuzz.zig @@ -83,6 +83,7 @@ pub fn init( const graph = maker.graph; const gpa = graph.cache.gpa; const io = graph.io; + const conf = &maker.scanned_config.configuration; const run_steps: []const Configuration.Step.Index = steps: { var steps: std.ArrayList(Configuration.Step.Index) = .empty; @@ -92,13 +93,13 @@ pub fn init( var rebuild_group: Io.Group = .init; defer rebuild_group.cancel(io); - for (all_steps) |step| { - if (true) @panic("TODO update the fuzzer"); - const run = step.cast(std.Build.Step.Run) orelse continue; - if (run.producer == null) continue; + for (all_steps) |step_index| { + const conf_run = step_index.ptr(conf).extended.cast(conf, Configuration.Step.Run) orelse continue; + if (conf_run.producer.value == null) continue; + const run = &maker.stepByIndex(step_index).extended.run; if (run.fuzz_tests.items.len == 0) continue; - try steps.append(gpa, run); - rebuild_group.async(io, rebuildTestsWorkerRun, .{ run, gpa, rebuild_node }); + try steps.append(gpa, step_index); + rebuild_group.async(io, rebuildTestsWorkerRun, .{ maker, step_index, rebuild_node }); } if (steps.items.len == 0) fatal("no fuzz tests found", .{}); @@ -109,10 +110,10 @@ pub fn init( }; errdefer gpa.free(run_steps); - for (run_steps) |run_step_index| { - if (true) @panic("TODO update the fuzzer"); - assert(run_step_index.fuzz_tests.items.len > 0); - if (run_step_index.rebuilt_executable == null) + for (run_steps) |run_index| { + const run = &maker.stepByIndex(run_index).extended.run; + assert(run.fuzz_tests.items.len > 0); + if (run.rebuilt_executable == null) fatal("one or more unit tests failed to be rebuilt in fuzz mode", .{}); } @@ -144,11 +145,10 @@ pub fn start(fuzz: *Fuzz) void { fatal("unable to spawn coverage task: {t}", .{err}); } - if (true) @panic("TODO update the fuzzer"); - - for (fuzz.run_steps) |run| { + for (fuzz.run_steps) |run_index| { + const run = &maker.stepByIndex(run_index).extended.run; assert(run.rebuilt_executable != null); - fuzz.group.async(io, fuzzWorkerRun, .{ fuzz, run }); + fuzz.group.async(io, fuzzWorkerRun, .{ fuzz, run_index }); } } @@ -163,67 +163,111 @@ pub fn deinit(fuzz: *Fuzz) void { gpa.free(fuzz.run_steps); } -fn rebuildTestsWorkerRun(run: Configuration.Step.Index, gpa: Allocator, parent_prog_node: std.Progress.Node) void { - rebuildTestsWorkerRunFallible(run, gpa, parent_prog_node) catch |err| { - const compile = run.producer.?; - log.err("step '{s}': failed to rebuild in fuzz mode: {t}", .{ compile.step.name, err }); +fn rebuildTestsWorkerRun( + maker: *Maker, + run_index: Configuration.Step.Index, + parent_prog_node: std.Progress.Node, +) void { + rebuildTestsWorkerRunFallible(maker, run_index, parent_prog_node) catch |err| { + const conf = &maker.scanned_config.configuration; + const conf_run = run_index.ptr(conf).extended.cast(conf, Configuration.Step.Run).?; + const comp_index = conf_run.producer.value.?; + const step_name = comp_index.ptr(conf).name.slice(conf); + log.err("step {s}: failed to rebuild in fuzz mode: {t}", .{ step_name, err }); }; } -fn rebuildTestsWorkerRunFallible(run: Configuration.Step.Index, gpa: Allocator, parent_prog_node: std.Progress.Node) !void { - const graph = run.step.owner.graph; +fn rebuildTestsWorkerRunFallible( + maker: *Maker, + run_index: Configuration.Step.Index, + parent_prog_node: std.Progress.Node, +) !void { + const graph = maker.graph; const io = graph.io; - const compile = run.producer.?; - const prog_node = parent_prog_node.start(compile.step.name, 0); + const gpa = maker.gpa; + const conf = &maker.scanned_config.configuration; + const run = &maker.stepByIndex(run_index).extended.run; + const conf_run = run_index.ptr(conf).extended.cast(conf, Configuration.Step.Run).?; + const comp_index = conf_run.producer.value.?; + const comp_step = maker.stepByIndex(comp_index); + const comp = &comp_step.extended.compile; + const conf_comp_step = comp_index.ptr(conf); + const conf_comp = conf_comp_step.extended.cast(conf, Configuration.Step.Compile).?; + const root_module = conf_comp.root_module.get(conf); + const target = root_module.resolved_target.get(conf).?.result.get(conf); + + const prog_node = parent_prog_node.start(conf_comp_step.name.slice(conf), 0); defer prog_node.end(); - const result = compile.rebuildInFuzzMode(gpa, prog_node); + const result = comp.rebuildInFuzzMode(maker, comp_index, prog_node); - const show_compile_errors = compile.step.result_error_bundle.errorMessageCount() > 0; - const show_error_msgs = compile.step.result_error_msgs.items.len > 0; - const show_stderr = compile.step.result_stderr.len > 0; + const show_compile_errors = comp_step.result_error_bundle.errorMessageCount() > 0; + const show_error_msgs = comp_step.result_error_msgs.items.len > 0; + const show_stderr = comp_step.result_stderr.len > 0; if (show_error_msgs or show_compile_errors or show_stderr) { var buf: [256]u8 = undefined; const stderr = try io.lockStderr(&buf, graph.stderr_mode); defer io.unlockStderr(); - Maker.printErrorMessages(gpa, &compile.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; + maker.printErrorMessages(comp_index, .{}, stderr.terminal(), .verbose, .indent) catch {}; } const rebuilt_bin_path = result catch |err| switch (err) { error.MakeFailed => return, else => |other| return other, }; - run.rebuilt_executable = try rebuilt_bin_path.join(gpa, compile.out_filename); + const compile_filename = try std.zig.binNameAlloc(gpa, .{ + .root_name = conf_comp.root_name.slice(conf), + .cpu_arch = target.flags.cpu_arch.unwrap().?, + .os_tag = target.flags.os_tag.unwrap().?, + .ofmt = target.flags.object_format.unwrap().?, + .abi = target.flags.abi.unwrap().?, + .output_mode = switch (conf_comp.flags3.kind) { + .lib => .Lib, + .obj, .test_obj => .Obj, + .exe, .@"test" => .Exe, + }, + .link_mode = conf_comp.flags2.linkage.unwrap(), + .version = if (conf_comp.version.value) |v| + std.SemanticVersion.parse(v.slice(conf)) catch unreachable + else + null, + }); + defer gpa.free(compile_filename); + + run.rebuilt_executable = try rebuilt_bin_path.join(gpa, compile_filename); } -fn fuzzWorkerRun(fuzz: *Fuzz, run: Configuration.Step.Index) void { - const owner = run.step.owner; - const gpa = owner.allocator; - const graph = owner.graph; +fn fuzzWorkerRun(fuzz: *Fuzz, run_index: Configuration.Step.Index) void { + const maker = fuzz.maker; + const graph = maker.graph; const io = graph.io; + const conf = &maker.scanned_config.configuration; + const run = &maker.stepByIndex(run_index).extended.run; - run.rerunInFuzzMode(run, fuzz, fuzz.prog_node) catch |err| switch (err) { + run.rerunInFuzzMode(run_index, fuzz, fuzz.prog_node) catch |err| switch (err) { error.MakeFailed => { var buf: [256]u8 = undefined; const stderr = io.lockStderr(&buf, graph.stderr_mode) catch |e| switch (e) { error.Canceled => return, }; defer io.unlockStderr(); - Maker.printErrorMessages(gpa, &run.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; + maker.printErrorMessages(run_index, .{}, stderr.terminal(), .verbose, .indent) catch {}; return; }, else => { - log.err("step '{s}': failed to rerun in fuzz mode: {t}", .{ run.step.name, err }); + const step_name = run_index.ptr(conf).name.slice(conf); + log.err("step {s}: failed to rerun in fuzz mode: {t}", .{ step_name, err }); return; }, }; } pub fn serveSourcesTar(fuzz: *Fuzz, req: *std.http.Server.Request) !void { - if (true) @panic("TODO update the fuzzer"); assert(fuzz.mode == .forever); - const gpa = fuzz.maker.gpa; + const maker = fuzz.maker; + const gpa = maker.gpa; + const conf = &maker.scanned_config.configuration; var arena_state: std.heap.ArenaAllocator = .init(gpa); defer arena_state.deinit(); @@ -233,8 +277,11 @@ pub fn serveSourcesTar(fuzz: *Fuzz, req: *std.http.Server.Request) !void { var dedup_table: DedupTable = .empty; defer dedup_table.deinit(gpa); - for (fuzz.run_steps) |run_step| { - const compile_inputs = run_step.producer.?.step.inputs.table; + for (fuzz.run_steps) |run_index| { + const conf_run = run_index.ptr(conf).extended.cast(conf, Configuration.Step.Run) orelse continue; + const comp_index = conf_run.producer.value.?; + const comp_step = maker.stepByIndex(comp_index); + const compile_inputs = comp_step.inputs.table; for (compile_inputs.keys(), compile_inputs.values()) |dir_path, *file_list| { try dedup_table.ensureUnusedCapacity(gpa, file_list.items.len); for (file_list.items) |sub_path| { @@ -372,14 +419,15 @@ fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void { fuzz.msg_queue.clearRetainingCapacity(); } } -fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage_id: u64) error{ OutOfMemory, AlreadyReported, Canceled }!void { - if (true) @panic("TODO update the fuzzer"); +fn prepareTables(fuzz: *Fuzz, run_index: Configuration.Step.Index, coverage_id: u64) error{ OutOfMemory, AlreadyReported, Canceled }!void { assert(fuzz.mode == .forever); const ws = fuzz.mode.forever.ws; const maker = fuzz.maker; const graph = maker.graph; const io = graph.io; const gpa = maker.gpa; + const conf = &maker.scanned_config.configuration; + const cache_root = graph.local_cache_root; try fuzz.coverage_mutex.lock(io); defer fuzz.coverage_mutex.unlock(io); @@ -405,37 +453,45 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage }; errdefer gop.value_ptr.coverage.deinit(gpa); - const rebuilt_exe_path = run_step_index.rebuilt_executable.?; - const target = run_step_index.producer.?.rootModuleTarget(); + const run_step = maker.stepByIndex(run_index); + const conf_run_step = run_index.ptr(conf); + const conf_run = conf_run_step.extended.cast(conf, Configuration.Step.Run).?; + const comp_index = conf_run.producer.value.?; + const conf_comp_step = comp_index.ptr(conf); + const conf_comp = conf_comp_step.extended.cast(conf, Configuration.Step.Compile).?; + const rebuilt_exe_path = run_step.extended.run.rebuilt_executable.?; + const root_module = conf_comp.root_module.get(conf); + const target = root_module.resolved_target.get(conf).?.result.get(conf); + var debug_info = std.debug.Info.load( gpa, io, rebuilt_exe_path, &gop.value_ptr.coverage, - target.ofmt, - target.cpu.arch, + target.flags.object_format.unwrap().?, + target.flags.cpu_arch.unwrap().?, ) catch |err| { - log.err("step '{s}': failed to load debug information for '{f}': {t}", .{ - run_step_index.step.name, rebuilt_exe_path, err, + log.err("step {s}: failed to load debug information for {f}: {t}", .{ + conf_run_step.name.slice(conf), rebuilt_exe_path, err, }); return error.AlreadyReported; }; defer debug_info.deinit(gpa); const coverage_file_path: Build.Cache.Path = .{ - .root_dir = run_step_index.step.owner.cache_root, + .root_dir = cache_root, .sub_path = "v/" ++ std.fmt.hex(coverage_id), }; var coverage_file = coverage_file_path.root_dir.handle.openFile(io, coverage_file_path.sub_path, .{}) catch |err| { - log.err("step '{s}': failed to load coverage file '{f}': {t}", .{ - run_step_index.step.name, coverage_file_path, err, + log.err("step {s}: failed to load coverage file {f}: {t}", .{ + conf_run_step.name.slice(conf), coverage_file_path, err, }); return error.AlreadyReported; }; defer coverage_file.close(io); const file_size = coverage_file.length(io) catch |err| { - log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err }); + log.err("unable to check len of coverage file {f}: {t}", .{ coverage_file_path, err }); return error.AlreadyReported; }; @@ -447,7 +503,7 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage coverage_file.handle, 0, ) catch |err| { - log.err("failed to map coverage file '{f}': {t}", .{ coverage_file_path, err }); + log.err("failed to map coverage file {f}: {t}", .{ coverage_file_path, err }); return error.AlreadyReported; }; gop.value_ptr.mapped_memory = mapped_memory; @@ -538,11 +594,12 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte } pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { - if (true) @panic("TODO update the fuzzer"); assert(fuzz.mode == .limit); const maker = fuzz.maker; const graph = maker.graph; const io = graph.io; + const cache_root = graph.local_cache_root; + const conf = &maker.scanned_config.configuration; try fuzz.group.await(io); fuzz.group = .init; @@ -552,13 +609,15 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { if (msg != .coverage) continue; const cov = msg.coverage; + const run_step_name = cov.run.ptr(conf).name.slice(conf); + const run = &maker.stepByIndex(cov.run).extended.run; const coverage_file_path: std.Build.Cache.Path = .{ - .root_dir = cov.run.step.owner.cache_root, + .root_dir = cache_root, .sub_path = "v/" ++ std.fmt.hex(cov.id), }; var coverage_file = coverage_file_path.root_dir.handle.openFile(io, coverage_file_path.sub_path, .{}) catch |err| { - fatal("step '{s}': failed to load coverage file '{f}': {t}", .{ - cov.run.step.name, coverage_file_path, err, + fatal("step {s}: failed to load coverage file {f}: {t}", .{ + run_step_name, coverage_file_path, err, }); }; defer coverage_file.close(io); @@ -569,14 +628,14 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { var header: fuzz_abi.SeenPcsHeader = undefined; r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| { - fatal("step '{s}': failed to read from coverage file '{f}': {t}", .{ - cov.run.step.name, coverage_file_path, err, + fatal("step {s}: failed to read from coverage file {f}: {t}", .{ + run_step_name, coverage_file_path, err, }); }; if (header.pcs_len == 0) { - fatal("step '{s}': corrupted coverage file '{f}': pcs_len was zero", .{ - cov.run.step.name, coverage_file_path, + fatal("step {s}: corrupted coverage file {f}: pcs_len was zero", .{ + run_step_name, coverage_file_path, }); } @@ -584,8 +643,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len); for (0..chunk_count) |_| { const seen = r.interface.takeInt(usize, .little) catch |err| { - fatal("step '{s}': failed to read from coverage file '{f}': {t}", .{ - cov.run.step.name, coverage_file_path, err, + fatal("step {s}: failed to read from coverage file {f}: {t}", .{ + run_step_name, coverage_file_path, err, }); }; seen_count += @popCount(seen); @@ -602,8 +661,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { \\Coverage: {}/{} -> {}/{} ({:.02}%) \\ , .{ - cov.run.step.name, - cov.run.fuzz_tests.items[0], + run_step_name, + run.fuzz_tests.items[0], cov.id, cov.cumulative.runs, header.n_runs, diff --git a/lib/compiler/Maker/Graph.zig b/lib/compiler/Maker/Graph.zig @@ -31,6 +31,7 @@ reference_trace: ?u32 = null, debug_log_scopes: std.ArrayList([]const u8) = .empty, debug_compile_errors: bool = false, debug_incremental: bool = false, +fuzzing: bool = false, verbose: bool = false, verbose_air: bool = false, verbose_cc: bool = false, diff --git a/lib/compiler/Maker/Step/Compile.zig b/lib/compiler/Maker/Step/Compile.zig @@ -947,11 +947,11 @@ fn lowerZigArgs( pub fn rebuildInFuzzMode( compile: *Compile, maker: *Maker, - step_index: Configuration.Step.Index, + compile_index: Configuration.Step.Index, progress_node: std.Progress.Node, ) !Path { - const gpa = maker.graph.gpa; - const step = maker.stepByIndex(step_index); + const gpa = maker.gpa; + const step = maker.stepByIndex(compile_index); step.result_error_msgs.clearRetainingCapacity(); step.result_stderr = ""; @@ -966,8 +966,8 @@ pub fn rebuildInFuzzMode( const zig_args = &compile.zig_args; zig_args.clearRetainingCapacity(); - try lowerZigArgs(compile, maker, progress_node, zig_args, true); - const maybe_output_bin_path = try step.evalZigProcess(zig_args.items, progress_node, false, maker); + try lowerZigArgs(compile, compile_index, maker, progress_node, zig_args, true); + const maybe_output_bin_path = try Step.evalZigProcess(compile_index, maker, zig_args.items, progress_node, false); return maybe_output_bin_path.?; } @@ -980,10 +980,7 @@ fn addFlag(gpa: Allocator, args: *std.ArrayList([]const u8), comptime name: []co try args.append(gpa, if (cond) "-f" ++ name else "-fno-" ++ name); } -fn checkCompileErrors( - maker: *Maker, - step_index: Configuration.Step.Index, -) Step.ExtendedMakeError!void { +fn checkCompileErrors(maker: *Maker, step_index: Configuration.Step.Index) Step.ExtendedMakeError!void { const step = maker.stepByIndex(step_index); const graph = maker.graph; const arena = graph.arena; // TODO don't leak into the process arena diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig @@ -67,6 +67,7 @@ pub fn make( } } + man.hash.add(graph.fuzzing); man.hash.add(conf_run.flags.color); man.hash.add(conf_run.flags.disable_zig_progress); @@ -234,7 +235,8 @@ pub fn make( } // Whether the Run step has side effects *other than* updating the output arguments. - const has_side_effects = conf_run.flags.has_side_effects or any_cli_positionals or + // When fuzzing we need to always run the test runner to populate fuzz_tests. + const has_side_effects = graph.fuzzing or conf_run.flags.has_side_effects or any_cli_positionals or switch (conf_run.flags.stdio) { .infer_from_args => !any_output_args and conf_run.captured_stdout.value == null and @@ -1511,7 +1513,7 @@ const IndexedOutput = struct { pub fn rerunInFuzzMode( run: *Run, run_index: Configuration.Step.Index, - fuzz: *std.Build.Fuzz, + fuzz: *Fuzz, prog_node: std.Progress.Node, ) !void { const maker = fuzz.maker; @@ -1524,6 +1526,7 @@ pub fn rerunInFuzzMode( const conf_step = run_index.ptr(conf); const conf_run = conf_step.extended.get(conf.extra).run; const argv_list = &run.argv; + const cache_root = graph.local_cache_root; argv_list.clearRetainingCapacity(); @@ -1599,6 +1602,15 @@ pub fn rerunInFuzzMode( } } + if (conf_run.flags.test_runner_mode) { + const cache_dir_string = try convertPathArg(run_index, maker, .{ .root_dir = cache_root }); + + try argv_list.ensureUnusedCapacity(gpa, 3); + argv_list.appendAssumeCapacity(try allocPrint(arena, "--cache-dir={s}", .{cache_dir_string})); + argv_list.appendAssumeCapacity(try allocPrint(arena, "--seed=0x{x}", .{graph.random_seed})); + argv_list.appendAssumeCapacity("--listen=-"); + } + if (step.result_failed_command) |cmd| { gpa.free(cmd); step.result_failed_command = null;