| author | |
| committer | |
| log | d5bfa657c48e9d023bb789fbf8dacbcbd42f528d |
| tree | b5934c60fcecd60f3710594c94d0f26cbcf20ab4 |
| parent | eff332fd042a4ef556409c7650c6b74da0e30235 |
| parent | 9d1e47d220a50fc5db4b622bd86e2fe51673c163 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31470
Reviewed-by: Andrew Kelley <andrew@ziglang.org>8 files changed, 103 insertions(+), 50 deletions(-)
lib/build-web/fuzz.zig+1-1| ... | @@ -255,7 +255,7 @@ fn unpackSourcesInner(tar_bytes: []u8) !void { | ... | @@ -255,7 +255,7 @@ fn unpackSourcesInner(tar_bytes: []u8) !void { |
| 255 | } | 255 | } |
| 256 | 256 | ||
| 257 | fn updateStats() error{OutOfMemory}!void { | 257 | fn updateStats() error{OutOfMemory}!void { |
| 258 | @setFloatMode(.optimized); | 258 | // No @setFloatMode(.optimized) since some stats may be at zero and lead to divisions by zero |
| 259 | 259 | ||
| 260 | if (recent_coverage_update.items.len == 0) return; | 260 | if (recent_coverage_update.items.len == 0) return; |
| 261 | 261 |
lib/compiler/test_runner.zig+20-3| ... | @@ -180,7 +180,23 @@ fn mainServer(init: std.process.Init.Minimal) !void { | ... | @@ -180,7 +180,23 @@ fn mainServer(init: std.process.Init.Minimal) !void { |
| 180 | // since they are not present. | 180 | // since they are not present. |
| 181 | if (!builtin.fuzz) unreachable; | 181 | if (!builtin.fuzz) unreachable; |
| 182 | 182 | ||
| 183 | const index = try server.receiveBody_u32(); | 183 | const index: u32 = @intCast(index: { |
| 184 | testing.allocator_instance = .{}; | ||
| 185 | defer if (testing.allocator_instance.deinit() == .leak) { | ||
| 186 | @panic("internal test runner memory leak"); | ||
| 187 | }; | ||
| 188 | |||
| 189 | const name_len = try server.receiveBody_u32(); | ||
| 190 | const name = try server.in.readAlloc(testing.allocator, @intCast(name_len)); | ||
| 191 | defer testing.allocator.free(name); | ||
| 192 | for (0.., builtin.test_functions) |i, test_fn| { | ||
| 193 | if (std.mem.eql(u8, name, test_fn.name)) { | ||
| 194 | break :index i; | ||
| 195 | } | ||
| 196 | } else { | ||
| 197 | std.debug.panic("fuzz test {s} no longer exists", .{name}); | ||
| 198 | } | ||
| 199 | }); | ||
| 184 | const mode: fuzz_abi.LimitKind = @enumFromInt(try server.receiveBody_u8()); | 200 | const mode: fuzz_abi.LimitKind = @enumFromInt(try server.receiveBody_u8()); |
| 185 | const amount_or_instance = try server.receiveBody_u64(); | 201 | const amount_or_instance = try server.receiveBody_u64(); |
| 186 | 202 | ||
| ... | @@ -406,13 +422,13 @@ pub fn fuzz( | ... | @@ -406,13 +422,13 @@ pub fn fuzz( |
| 406 | const global = struct { | 422 | const global = struct { |
| 407 | var ctx: @TypeOf(context) = undefined; | 423 | var ctx: @TypeOf(context) = undefined; |
| 408 | 424 | ||
| 409 | fn test_one() callconv(.c) void { | 425 | fn test_one() callconv(.c) bool { |
| 410 | @disableInstrumentation(); | 426 | @disableInstrumentation(); |
| 411 | testing.allocator_instance = .{}; | 427 | testing.allocator_instance = .{}; |
| 412 | defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1); | 428 | defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1); |
| 413 | log_err_count = 0; | 429 | log_err_count = 0; |
| 414 | testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) { | 430 | testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) { |
| 415 | error.SkipZigTest => return, | 431 | error.SkipZigTest => return true, |
| 416 | else => { | 432 | else => { |
| 417 | const stderr = std.debug.lockStderr(&.{}).terminal(); | 433 | const stderr = std.debug.lockStderr(&.{}).terminal(); |
| 418 | p: { | 434 | p: { |
| ... | @@ -429,6 +445,7 @@ pub fn fuzz( | ... | @@ -429,6 +445,7 @@ pub fn fuzz( |
| 429 | stderr.writer.print("error logs detected\n", .{}) catch {}; | 445 | stderr.writer.print("error logs detected\n", .{}) catch {}; |
| 430 | std.process.exit(1); | 446 | std.process.exit(1); |
| 431 | } | 447 | } |
| 448 | return false; | ||
| 432 | } | 449 | } |
| 433 | }; | 450 | }; |
| 434 | if (builtin.fuzz) { | 451 | if (builtin.fuzz) { |
lib/fuzzer.zig+44-18| ... | @@ -686,7 +686,7 @@ const Fuzzer = struct { | ... | @@ -686,7 +686,7 @@ const Fuzzer = struct { |
| 686 | const len = mem.readInt(u32, f.mmap_input.mmap.memory[0..4], .little); | 686 | const len = mem.readInt(u32, f.mmap_input.mmap.memory[0..4], .little); |
| 687 | if (len < f.mmap_input.mmap.memory[4..].len) { | 687 | if (len < f.mmap_input.mmap.memory[4..].len) { |
| 688 | f.mmap_input.len = len; | 688 | f.mmap_input.len = len; |
| 689 | f.runBytes(f.mmap_input.inputSlice(), .bytes_dry); | 689 | _ = f.runBytes(f.mmap_input.inputSlice(), .bytes_dry); |
| 690 | f.mmap_input.clearRetainingCapacity(); | 690 | f.mmap_input.clearRetainingCapacity(); |
| 691 | } | 691 | } |
| 692 | } | 692 | } |
| ... | @@ -703,7 +703,7 @@ const Fuzzer = struct { | ... | @@ -703,7 +703,7 @@ const Fuzzer = struct { |
| 703 | else => panic("failed to read corpus file '{s}': {t}", .{ name, e }), | 703 | else => panic("failed to read corpus file '{s}': {t}", .{ name, e }), |
| 704 | }; | 704 | }; |
| 705 | defer gpa.free(bytes); | 705 | defer gpa.free(bytes); |
| 706 | f.newInput(bytes, false); | 706 | f.newInputExternal(bytes); |
| 707 | } | 707 | } |
| 708 | f.corpus_pos = @enumFromInt(0); | 708 | f.corpus_pos = @enumFromInt(0); |
| 709 | } | 709 | } |
| ... | @@ -761,12 +761,13 @@ const Fuzzer = struct { | ... | @@ -761,12 +761,13 @@ const Fuzzer = struct { |
| 761 | return fresh; | 761 | return fresh; |
| 762 | } | 762 | } |
| 763 | 763 | ||
| 764 | fn runBytes(f: *Fuzzer, bytes: []const u8, mode: Input.Index) void { | 764 | /// Returns if `error.SkipZigTest` was indicated |
| 765 | fn runBytes(f: *Fuzzer, bytes: []const u8, mode: Input.Index) bool { | ||
| 765 | assert(mode == .bytes_dry or mode == .bytes_fresh); | 766 | assert(mode == .bytes_dry or mode == .bytes_fresh); |
| 766 | 767 | ||
| 767 | f.bytes_input = .{ .in = bytes }; | 768 | f.bytes_input = .{ .in = bytes }; |
| 768 | f.corpus_pos = mode; | 769 | f.corpus_pos = mode; |
| 769 | f.run(0); // 0 since `f.uid_data` is unused | 770 | return f.run(0); // 0 since `f.uid_data` is unused |
| 770 | } | 771 | } |
| 771 | 772 | ||
| 772 | fn updateSeenPcs(f: *Fuzzer) void { | 773 | fn updateSeenPcs(f: *Fuzzer) void { |
| ... | @@ -861,8 +862,21 @@ const Fuzzer = struct { | ... | @@ -861,8 +862,21 @@ const Fuzzer = struct { |
| 861 | } | 862 | } |
| 862 | } | 863 | } |
| 863 | 864 | ||
| 864 | pub fn newInput(f: *Fuzzer, bytes: []const u8, modify_fs_corpus: bool) void { | 865 | pub fn newInputExternal(f: *Fuzzer, bytes: []const u8) void { |
| 865 | f.runBytes(bytes, .bytes_fresh); | 866 | // All inputs including the corpus are required to go through the memory |
| 867 | // mapped input in case they cause a crash so they can be identified. | ||
| 868 | f.mmap_input.appendSlice(bytes); | ||
| 869 | f.newInput(false); | ||
| 870 | f.mmap_input.clearRetainingCapacity(); | ||
| 871 | } | ||
| 872 | |||
| 873 | fn newInput(f: *Fuzzer, modify_fs_corpus: bool) void { | ||
| 874 | const bytes = f.mmap_input.inputSlice(); | ||
| 875 | // `error.SkipZigTest` here can be from one of these causes: | ||
| 876 | // * The test has changed and a previous corpus input is being used | ||
| 877 | // * An input provided by the test results in it | ||
| 878 | // * The test is non-deterministic | ||
| 879 | if (f.runBytes(bytes, .bytes_fresh)) return; | ||
| 866 | f.req_values = f.input_builder.total_ints + f.input_builder.total_bytes; | 880 | f.req_values = f.input_builder.total_ints + f.input_builder.total_bytes; |
| 867 | f.req_bytes = @intCast(f.input_builder.bytes_table.items.len); | 881 | f.req_bytes = @intCast(f.input_builder.bytes_table.items.len); |
| 868 | var input = f.input_builder.build(); | 882 | var input = f.input_builder.build(); |
| ... | @@ -996,15 +1010,17 @@ const Fuzzer = struct { | ... | @@ -996,15 +1010,17 @@ const Fuzzer = struct { |
| 996 | panic("failed to write corpus file '{s}': {t}", .{ name, e }); | 1010 | panic("failed to write corpus file '{s}': {t}", .{ name, e }); |
| 997 | } | 1011 | } |
| 998 | 1012 | ||
| 999 | fn run(f: *Fuzzer, input_uids: usize) void { | 1013 | /// Returns if `error.SkipZigTest` was indicated |
| 1014 | fn run(f: *Fuzzer, input_uids: usize) bool { | ||
| 1000 | @memset(exec.pc_counters, 0); | 1015 | @memset(exec.pc_counters, 0); |
| 1001 | f.uid_data_i.items.len = input_uids; | 1016 | f.uid_data_i.items.len = input_uids; |
| 1002 | @memset(f.uid_data_i.items, 0); | 1017 | @memset(f.uid_data_i.items, 0); |
| 1003 | f.req_values = 0; | 1018 | f.req_values = 0; |
| 1004 | f.req_bytes = 0; | 1019 | f.req_bytes = 0; |
| 1005 | 1020 | ||
| 1006 | f.test_one(); | 1021 | const skip = f.test_one(); |
| 1007 | _ = @atomicRmw(usize, &exec.seenPcsHeader().n_runs, .Add, 1, .monotonic); | 1022 | _ = @atomicRmw(usize, &exec.seenPcsHeader().n_runs, .Add, 1, .monotonic); |
| 1023 | return skip; | ||
| 1008 | } | 1024 | } |
| 1009 | 1025 | ||
| 1010 | /// Returns a number of mutations to perform from 1-4 | 1026 | /// Returns a number of mutations to perform from 1-4 |
| ... | @@ -1076,12 +1092,12 @@ const Fuzzer = struct { | ... | @@ -1076,12 +1092,12 @@ const Fuzzer = struct { |
| 1076 | i.* = data.order[order_i]; | 1092 | i.* = data.order[order_i]; |
| 1077 | }; | 1093 | }; |
| 1078 | 1094 | ||
| 1079 | f.run(data.uid_slices.entries.len); | 1095 | const skip = f.run(data.uid_slices.entries.len); |
| 1080 | if (f.isFresh()) { | 1096 | if (!skip and f.isFresh()) { |
| 1081 | @branchHint(.unlikely); | 1097 | @branchHint(.unlikely); |
| 1082 | 1098 | ||
| 1083 | _ = @atomicRmw(usize, &exec.seenPcsHeader().unique_runs, .Add, 1, .monotonic); | 1099 | _ = @atomicRmw(usize, &exec.seenPcsHeader().unique_runs, .Add, 1, .monotonic); |
| 1084 | f.newInput(f.mmap_input.inputSlice(), true); | 1100 | f.newInput(true); |
| 1085 | } | 1101 | } |
| 1086 | f.mmap_input.clearRetainingCapacity(); | 1102 | f.mmap_input.clearRetainingCapacity(); |
| 1087 | 1103 | ||
| ... | @@ -1320,13 +1336,23 @@ const Fuzzer = struct { | ... | @@ -1320,13 +1336,23 @@ const Fuzzer = struct { |
| 1320 | 1336 | ||
| 1321 | if (opts.copy != 0) { | 1337 | if (opts.copy != 0) { |
| 1322 | if (opts.fresh == 0 or slice_i == data_slice.len) return .fresh; | 1338 | if (opts.fresh == 0 or slice_i == data_slice.len) return .fresh; |
| 1323 | return .{ .mutate = switch (uid.kind) { | 1339 | switch (uid.kind) { |
| 1324 | .int => .{ .int = data.ints[data_i] }, | 1340 | .int => { |
| 1325 | .bytes => .{ .bytes = b: { | 1341 | const int = data.ints[data_i]; |
| 1342 | if (weightsContain(int, weights)) { | ||
| 1343 | @branchHint(.likely); | ||
| 1344 | return .{ .mutate = .{ .int = int } }; | ||
| 1345 | } | ||
| 1346 | }, | ||
| 1347 | .bytes => { | ||
| 1326 | const entry = data.bytes.entries[data_i]; | 1348 | const entry = data.bytes.entries[data_i]; |
| 1327 | break :b data.bytes.table[entry.off..][0..entry.len]; | 1349 | const bytes = data.bytes.table[entry.off..][0..entry.len]; |
| 1328 | } }, | 1350 | if (weightsContainBytes(bytes, weights)) { |
| 1329 | } }; | 1351 | @branchHint(.likely); |
| 1352 | return .{ .mutate = .{ .bytes = bytes } }; | ||
| 1353 | } | ||
| 1354 | }, | ||
| 1355 | } | ||
| 1330 | } | 1356 | } |
| 1331 | 1357 | ||
| 1332 | if (!opts.splice) { | 1358 | if (!opts.splice) { |
| ... | @@ -1665,7 +1691,7 @@ export fn fuzzer_set_test(test_one: abi.TestOne, unit_test_name: abi.Slice) void | ... | @@ -1665,7 +1691,7 @@ export fn fuzzer_set_test(test_one: abi.TestOne, unit_test_name: abi.Slice) void |
| 1665 | 1691 | ||
| 1666 | export fn fuzzer_new_input(bytes: abi.Slice) void { | 1692 | export fn fuzzer_new_input(bytes: abi.Slice) void { |
| 1667 | if (bytes.len == 0) return; // An entry of length zero is always present | 1693 | if (bytes.len == 0) return; // An entry of length zero is always present |
| 1668 | fuzzer.newInput(bytes.toSlice(), false); | 1694 | fuzzer.newInputExternal(bytes.toSlice()); |
| 1669 | } | 1695 | } |
| 1670 | 1696 | ||
| 1671 | export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void { | 1697 | export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void { |
lib/std/Build/Fuzz.zig+7-8| ... | @@ -145,9 +145,9 @@ pub fn start(fuzz: *Fuzz) void { | ... | @@ -145,9 +145,9 @@ pub fn start(fuzz: *Fuzz) void { |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | for (fuzz.run_steps) |run| { | 147 | for (fuzz.run_steps) |run| { |
| 148 | for (run.fuzz_tests.items) |unit_test_index| { | 148 | for (run.fuzz_tests.items) |unit_test_name| { |
| 149 | assert(run.rebuilt_executable != null); | 149 | assert(run.rebuilt_executable != null); |
| 150 | fuzz.group.async(io, fuzzWorkerRun, .{ fuzz, run, unit_test_index }); | 150 | fuzz.group.async(io, fuzzWorkerRun, .{ fuzz, run, unit_test_name }); |
| 151 | } | 151 | } |
| 152 | } | 152 | } |
| 153 | } | 153 | } |
| ... | @@ -193,17 +193,16 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_nod | ... | @@ -193,17 +193,16 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_nod |
| 193 | run.rebuilt_executable = try rebuilt_bin_path.join(gpa, compile.out_filename); | 193 | run.rebuilt_executable = try rebuilt_bin_path.join(gpa, compile.out_filename); |
| 194 | } | 194 | } |
| 195 | 195 | ||
| 196 | fn fuzzWorkerRun(fuzz: *Fuzz, run: *Step.Run, unit_test_index: u32) void { | 196 | fn fuzzWorkerRun(fuzz: *Fuzz, run: *Step.Run, unit_test_name: []const u8) void { |
| 197 | const owner = run.step.owner; | 197 | const owner = run.step.owner; |
| 198 | const gpa = owner.allocator; | 198 | const gpa = owner.allocator; |
| 199 | const graph = owner.graph; | 199 | const graph = owner.graph; |
| 200 | const io = graph.io; | 200 | const io = graph.io; |
| 201 | const test_name = run.cached_test_metadata.?.testName(unit_test_index); | ||
| 202 | 201 | ||
| 203 | const prog_node = fuzz.prog_node.start(test_name, 0); | 202 | const prog_node = fuzz.prog_node.start(unit_test_name, 0); |
| 204 | defer prog_node.end(); | 203 | defer prog_node.end(); |
| 205 | 204 | ||
| 206 | run.rerunInFuzzMode(fuzz, unit_test_index, prog_node) catch |err| switch (err) { | 205 | run.rerunInFuzzMode(fuzz, unit_test_name, prog_node) catch |err| switch (err) { |
| 207 | error.MakeFailed => { | 206 | error.MakeFailed => { |
| 208 | var buf: [256]u8 = undefined; | 207 | var buf: [256]u8 = undefined; |
| 209 | const stderr = io.lockStderr(&buf, graph.stderr_mode) catch |e| switch (e) { | 208 | const stderr = io.lockStderr(&buf, graph.stderr_mode) catch |e| switch (e) { |
| ... | @@ -214,7 +213,7 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: *Step.Run, unit_test_index: u32) void { | ... | @@ -214,7 +213,7 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: *Step.Run, unit_test_index: u32) void { |
| 214 | return; | 213 | return; |
| 215 | }, | 214 | }, |
| 216 | else => { | 215 | else => { |
| 217 | log.err("step '{s}': failed to rerun '{s}' in fuzz mode: {t}", .{ run.step.name, test_name, err }); | 216 | log.err("step '{s}': failed to rerun '{s}' in fuzz mode: {t}", .{ run.step.name, unit_test_name, err }); |
| 218 | return; | 217 | return; |
| 219 | }, | 218 | }, |
| 220 | }; | 219 | }; |
| ... | @@ -588,7 +587,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { | ... | @@ -588,7 +587,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { |
| 588 | \\ | 587 | \\ |
| 589 | , .{ | 588 | , .{ |
| 590 | cov.run.step.name, | 589 | cov.run.step.name, |
| 591 | cov.run.cached_test_metadata.?.testName(cov.run.fuzz_tests.items[0]), | 590 | cov.run.fuzz_tests.items[0], |
| 592 | cov.id, | 591 | cov.id, |
| 593 | cov.cumulative.runs, | 592 | cov.cumulative.runs, |
| 594 | header.n_runs, | 593 | header.n_runs, |
lib/std/Build/Step/Run.zig+18-14| ... | @@ -88,9 +88,10 @@ dep_output_file: ?*Output, | ... | @@ -88,9 +88,10 @@ dep_output_file: ?*Output, |
| 88 | 88 | ||
| 89 | has_side_effects: bool, | 89 | has_side_effects: bool, |
| 90 | 90 | ||
| 91 | /// If this is a Zig unit test binary, this tracks the indexes of the unit | 91 | /// If this is a Zig unit test binary, this tracks the names of the unit |
| 92 | /// tests that are also fuzz tests. | 92 | /// tests that are also fuzz tests. Indexes cannot be used as they may |
| 93 | fuzz_tests: std.ArrayList(u32), | 93 | /// change between reruns. |
| 94 | fuzz_tests: std.ArrayList([]const u8), | ||
| 94 | cached_test_metadata: ?CachedTestMetadata = null, | 95 | cached_test_metadata: ?CachedTestMetadata = null, |
| 95 | 96 | ||
| 96 | /// Populated during the fuzz phase if this run step corresponds to a unit test | 97 | /// Populated during the fuzz phase if this run step corresponds to a unit test |
| ... | @@ -1067,7 +1068,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -1067,7 +1068,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 1067 | pub fn rerunInFuzzMode( | 1068 | pub fn rerunInFuzzMode( |
| 1068 | run: *Run, | 1069 | run: *Run, |
| 1069 | fuzz: *std.Build.Fuzz, | 1070 | fuzz: *std.Build.Fuzz, |
| 1070 | unit_test_index: u32, | 1071 | unit_test_name: []const u8, |
| 1071 | prog_node: std.Progress.Node, | 1072 | prog_node: std.Progress.Node, |
| 1072 | ) !void { | 1073 | ) !void { |
| 1073 | const step = &run.step; | 1074 | const step = &run.step; |
| ... | @@ -1138,7 +1139,7 @@ pub fn rerunInFuzzMode( | ... | @@ -1138,7 +1139,7 @@ pub fn rerunInFuzzMode( |
| 1138 | .unit_test_timeout_ns = null, // don't time out fuzz tests for now | 1139 | .unit_test_timeout_ns = null, // don't time out fuzz tests for now |
| 1139 | .gpa = fuzz.gpa, | 1140 | .gpa = fuzz.gpa, |
| 1140 | }, .{ | 1141 | }, .{ |
| 1141 | .unit_test_index = unit_test_index, | 1142 | .unit_test_name = unit_test_name, |
| 1142 | .fuzz = fuzz, | 1143 | .fuzz = fuzz, |
| 1143 | }); | 1144 | }); |
| 1144 | } | 1145 | } |
| ... | @@ -1210,7 +1211,7 @@ fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool { | ... | @@ -1210,7 +1211,7 @@ fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool { |
| 1210 | 1211 | ||
| 1211 | const FuzzContext = struct { | 1212 | const FuzzContext = struct { |
| 1212 | fuzz: *std.Build.Fuzz, | 1213 | fuzz: *std.Build.Fuzz, |
| 1213 | unit_test_index: u32, | 1214 | unit_test_name: []const u8, |
| 1214 | }; | 1215 | }; |
| 1215 | 1216 | ||
| 1216 | fn runCommand( | 1217 | fn runCommand( |
| ... | @@ -1843,7 +1844,7 @@ fn waitZigTest( | ... | @@ -1843,7 +1844,7 @@ fn waitZigTest( |
| 1843 | sendRunFuzzTestMessage( | 1844 | sendRunFuzzTestMessage( |
| 1844 | io, | 1845 | io, |
| 1845 | child.stdin.?, | 1846 | child.stdin.?, |
| 1846 | ctx.unit_test_index, | 1847 | ctx.unit_test_name, |
| 1847 | .forever, | 1848 | .forever, |
| 1848 | 0, // instance ID; will be used by multiprocess forever fuzzing in the future | 1849 | 0, // instance ID; will be used by multiprocess forever fuzzing in the future |
| 1849 | ) catch |err| return .{ .write_failed = err }; | 1850 | ) catch |err| return .{ .write_failed = err }; |
| ... | @@ -1852,7 +1853,7 @@ fn waitZigTest( | ... | @@ -1852,7 +1853,7 @@ fn waitZigTest( |
| 1852 | sendRunFuzzTestMessage( | 1853 | sendRunFuzzTestMessage( |
| 1853 | io, | 1854 | io, |
| 1854 | child.stdin.?, | 1855 | child.stdin.?, |
| 1855 | ctx.unit_test_index, | 1856 | ctx.unit_test_name, |
| 1856 | .iterations, | 1857 | .iterations, |
| 1857 | limit.amount, | 1858 | limit.amount, |
| 1858 | ) catch |err| return .{ .write_failed = err }; | 1859 | ) catch |err| return .{ .write_failed = err }; |
| ... | @@ -2001,10 +2002,10 @@ fn waitZigTest( | ... | @@ -2001,10 +2002,10 @@ fn waitZigTest( |
| 2001 | results.leak_count +|= leak_count; | 2002 | results.leak_count +|= leak_count; |
| 2002 | results.log_err_count +|= log_err_count; | 2003 | results.log_err_count +|= log_err_count; |
| 2003 | 2004 | ||
| 2004 | if (tr_hdr.flags.fuzz) try run.fuzz_tests.append(gpa, tr_hdr.index); | 2005 | if (tr_hdr.flags.fuzz) try run.fuzz_tests.append(gpa, md.testName(tr_hdr.index)); |
| 2005 | 2006 | ||
| 2006 | if (tr_hdr.flags.status == .fail) { | 2007 | if (tr_hdr.flags.status == .fail) { |
| 2007 | const name = std.mem.sliceTo(md.testName(tr_hdr.index), 0); | 2008 | const name = md.testName(tr_hdr.index); |
| 2008 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); | 2009 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); |
| 2009 | stderr.tossBuffered(); | 2010 | stderr.tossBuffered(); |
| 2010 | if (stderr_bytes.len == 0) { | 2011 | if (stderr_bytes.len == 0) { |
| ... | @@ -2013,12 +2014,12 @@ fn waitZigTest( | ... | @@ -2013,12 +2014,12 @@ fn waitZigTest( |
| 2013 | try run.step.addError("'{s}' failed:\n{s}", .{ name, stderr_bytes }); | 2014 | try run.step.addError("'{s}' failed:\n{s}", .{ name, stderr_bytes }); |
| 2014 | } | 2015 | } |
| 2015 | } else if (leak_count > 0) { | 2016 | } else if (leak_count > 0) { |
| 2016 | const name = std.mem.sliceTo(md.testName(tr_hdr.index), 0); | 2017 | const name = md.testName(tr_hdr.index); |
| 2017 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); | 2018 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); |
| 2018 | stderr.tossBuffered(); | 2019 | stderr.tossBuffered(); |
| 2019 | try run.step.addError("'{s}' leaked {d} allocations:\n{s}", .{ name, leak_count, stderr_bytes }); | 2020 | try run.step.addError("'{s}' leaked {d} allocations:\n{s}", .{ name, leak_count, stderr_bytes }); |
| 2020 | } else if (log_err_count > 0) { | 2021 | } else if (log_err_count > 0) { |
| 2021 | const name = std.mem.sliceTo(md.testName(tr_hdr.index), 0); | 2022 | const name = md.testName(tr_hdr.index); |
| 2022 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); | 2023 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); |
| 2023 | stderr.tossBuffered(); | 2024 | stderr.tossBuffered(); |
| 2024 | try run.step.addError("'{s}' logged {d} errors:\n{s}", .{ name, log_err_count, stderr_bytes }); | 2025 | try run.step.addError("'{s}' logged {d} errors:\n{s}", .{ name, log_err_count, stderr_bytes }); |
| ... | @@ -2148,7 +2149,7 @@ fn sendRunTestMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag, in | ... | @@ -2148,7 +2149,7 @@ fn sendRunTestMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag, in |
| 2148 | fn sendRunFuzzTestMessage( | 2149 | fn sendRunFuzzTestMessage( |
| 2149 | io: Io, | 2150 | io: Io, |
| 2150 | file: Io.File, | 2151 | file: Io.File, |
| 2151 | index: u32, | 2152 | test_name: []const u8, |
| 2152 | kind: std.Build.abi.fuzz.LimitKind, | 2153 | kind: std.Build.abi.fuzz.LimitKind, |
| 2153 | amount_or_instance: u64, | 2154 | amount_or_instance: u64, |
| 2154 | ) !void { | 2155 | ) !void { |
| ... | @@ -2160,7 +2161,10 @@ fn sendRunFuzzTestMessage( | ... | @@ -2160,7 +2161,10 @@ fn sendRunFuzzTestMessage( |
| 2160 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | 2161 | w.interface.writeStruct(header, .little) catch |err| switch (err) { |
| 2161 | error.WriteFailed => return w.err.?, | 2162 | error.WriteFailed => return w.err.?, |
| 2162 | }; | 2163 | }; |
| 2163 | w.interface.writeInt(u32, index, .little) catch |err| switch (err) { | 2164 | w.interface.writeInt(u32, @intCast(test_name.len), .little) catch |err| switch (err) { |
| 2165 | error.WriteFailed => return w.err.?, | ||
| 2166 | }; | ||
| 2167 | w.interface.writeAll(test_name) catch |err| switch (err) { | ||
| 2164 | error.WriteFailed => return w.err.?, | 2168 | error.WriteFailed => return w.err.?, |
| 2165 | }; | 2169 | }; |
| 2166 | w.interface.writeByte(@intFromEnum(kind)) catch |err| switch (err) { | 2170 | w.interface.writeByte(@intFromEnum(kind)) catch |err| switch (err) { |
lib/std/Build/abi.zig+8-4| ... | @@ -139,7 +139,8 @@ pub const Rebuild = extern struct { | ... | @@ -139,7 +139,8 @@ pub const Rebuild = extern struct { |
| 139 | 139 | ||
| 140 | /// ABI bits specifically relating to the fuzzer interface. | 140 | /// ABI bits specifically relating to the fuzzer interface. |
| 141 | pub const fuzz = struct { | 141 | pub const fuzz = struct { |
| 142 | pub const TestOne = *const fn () callconv(.c) void; | 142 | /// Returns if `error.SkipZigTest` was indicated |
| 143 | pub const TestOne = *const fn () callconv(.c) bool; | ||
| 143 | 144 | ||
| 144 | /// A unique value to identify the related requests across runs | 145 | /// A unique value to identify the related requests across runs |
| 145 | pub const Uid = packed struct(u32) { | 146 | pub const Uid = packed struct(u32) { |
| ... | @@ -249,9 +250,12 @@ pub const fuzz = struct { | ... | @@ -249,9 +250,12 @@ pub const fuzz = struct { |
| 249 | } | 250 | } |
| 250 | // Reject types that don't have a fixed bitsize (esp. usize) | 251 | // Reject types that don't have a fixed bitsize (esp. usize) |
| 251 | // since they are not gauraunteed to fit in a u64 across targets. | 252 | // since they are not gauraunteed to fit in a u64 across targets. |
| 252 | if (std.mem.indexOfScalar(type, &.{ | 253 | // |
| 253 | usize, c_char, c_ushort, c_uint, c_ulong, c_ulonglong, | 254 | // std.mem.indexOfScalar is not used to avoid backward branches |
| 254 | }, T) != null) { | 255 | // and preserve the eval branch quota. |
| 256 | if (T == usize or T == c_char or T == c_ushort or | ||
| 257 | T == c_uint or T == c_ulong or T == c_ulonglong) | ||
| 258 | { | ||
| 255 | @compileError("type does not have a fixed bitsize: " ++ @typeName(T)); | 259 | @compileError("type does not have a fixed bitsize: " ++ @typeName(T)); |
| 256 | } | 260 | } |
| 257 | } | 261 | } |
lib/std/zig/Client.zig+2-1| ... | @@ -35,7 +35,8 @@ pub const Message = struct { | ... | @@ -35,7 +35,8 @@ pub const Message = struct { |
| 35 | run_test, | 35 | run_test, |
| 36 | /// Ask the test runner to start fuzzing a particular test forever or for a given amount of time/iterations. | 36 | /// Ask the test runner to start fuzzing a particular test forever or for a given amount of time/iterations. |
| 37 | /// The message body is: | 37 | /// The message body is: |
| 38 | /// - a u32 test index. | 38 | /// - a u32 test name len. |
| 39 | /// - a test name with the above length | ||
| 39 | /// - a u8 test limit kind (std.Build.api.fuzz.LimitKind) | 40 | /// - a u8 test limit kind (std.Build.api.fuzz.LimitKind) |
| 40 | /// - a u64 value whose meaning depends on FuzzLimitKind (either a limit amount or an instance id) | 41 | /// - a u64 value whose meaning depends on FuzzLimitKind (either a limit amount or an instance id) |
| 41 | start_fuzzing, | 42 | start_fuzzing, |
test/standalone/libfuzzer/main.zig+3-1| ... | @@ -2,7 +2,9 @@ const std = @import("std"); | ... | @@ -2,7 +2,9 @@ const std = @import("std"); |
| 2 | const abi = std.Build.abi.fuzz; | 2 | const abi = std.Build.abi.fuzz; |
| 3 | const native_endian = @import("builtin").cpu.arch.endian(); | 3 | const native_endian = @import("builtin").cpu.arch.endian(); |
| 4 | 4 | ||
| 5 | fn testOne() callconv(.c) void {} | 5 | fn testOne() callconv(.c) bool { |
| 6 | return false; | ||
| 7 | } | ||
| 6 | 8 | ||
| 7 | pub fn main(init: std.process.Init) !void { | 9 | pub fn main(init: std.process.Init) !void { |
| 8 | const gpa = init.gpa; | 10 | const gpa = init.gpa; |