authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2025-09-25 17:02:43+02:00
committergravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2025-09-25 18:20:19+02:00
log9bb0b43ea3ababb715a15bba8c09ba71e9c3ccc2
treed2f43d4ac88949889e5971327426fb099e576941
parent0feacc2b81679514c0168a6ba4c0decafeb2e43e

implement review suggestions


5 files changed, 56 insertions(+), 78 deletions(-)

lib/compiler/build_runner.zig+10-6
...@@ -280,21 +280,21 @@ pub fn main() !void {...@@ -280,21 +280,21 @@ pub fn main() !void {
280 }280 }
281 } else if (mem.startsWith(u8, arg, "--fuzz=")) {281 } else if (mem.startsWith(u8, arg, "--fuzz=")) {
282 const value = arg["--fuzz=".len..];282 const value = arg["--fuzz=".len..];
283 if (value.len == 0) fatal("missing argument to --fuzz\n", .{});283 if (value.len == 0) fatal("missing argument to --fuzz", .{});
284284
285 const unit: u8 = value[value.len - 1];285 const unit: u8 = value[value.len - 1];
286 const digits = switch (value[value.len - 1]) {286 const digits = switch (unit) {
287 '0'...'9' => value,287 '0'...'9' => value,
288 'K', 'M', 'G' => value[0 .. value.len - 1],288 'K', 'M', 'G' => value[0 .. value.len - 1],
289 else => fatal(289 else => fatal(
290 "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]\n",290 "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]",
291 .{},291 .{},
292 ),292 ),
293 };293 };
294294
295 const amount = std.fmt.parseInt(u64, digits, 10) catch {295 const amount = std.fmt.parseInt(u64, digits, 10) catch {
296 fatal(296 fatal(
297 "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]\n",297 "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]",
298 .{},298 .{},
299 );299 );
300 };300 };
...@@ -305,7 +305,7 @@ pub fn main() !void {...@@ -305,7 +305,7 @@ pub fn main() !void {
305 'K' => 1000,305 'K' => 1000,
306 'M' => 1_000_000,306 'M' => 1_000_000,
307 'G' => 1_000_000_000,307 'G' => 1_000_000_000,
308 }) catch fatal("fuzzing limit amount overflows u64\n", .{});308 }) catch fatal("fuzzing limit amount overflows u64", .{});
309309
310 fuzz = .{310 fuzz = .{
311 .limit = .{311 .limit = .{
...@@ -520,7 +520,11 @@ pub fn main() !void {...@@ -520,7 +520,11 @@ pub fn main() !void {
520 };520 };
521521
522 if (run.web_server) |*web_server| {522 if (run.web_server) |*web_server| {
523 if (fuzz) |mode| assert(mode == .forever);523 if (fuzz) |mode| if (mode != .forever) fatal(
524 "error: limited fuzzing is not implemented yet for --webui",
525 .{},
526 );
527
524 web_server.finishBuild(.{ .fuzz = fuzz != null });528 web_server.finishBuild(.{ .fuzz = fuzz != null });
525 }529 }
526530
lib/compiler/test_runner.zig+17-68
...@@ -56,20 +56,21 @@ pub fn main() void {...@@ -56,20 +56,21 @@ pub fn main() void {
56 }56 }
57 }57 }
5858
59 fba.reset();
60 if (builtin.fuzz) {59 if (builtin.fuzz) {
61 const cache_dir = opt_cache_dir orelse @panic("missing --cache-dir=[path] argument");60 const cache_dir = opt_cache_dir orelse @panic("missing --cache-dir=[path] argument");
62 fuzz_abi.fuzzer_init(.fromSlice(cache_dir));61 fuzz_abi.fuzzer_init(.fromSlice(cache_dir));
63 }62 }
6463
64 fba.reset();
65
65 if (listen) {66 if (listen) {
66 return mainServer(opt_cache_dir) catch @panic("internal test runner failure");67 return mainServer() catch @panic("internal test runner failure");
67 } else {68 } else {
68 return mainTerminal();69 return mainTerminal();
69 }70 }
70}71}
7172
72fn mainServer(opt_cache_dir: ?[]const u8) !void {73fn mainServer() !void {
73 @disableInstrumentation();74 @disableInstrumentation();
74 var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer);75 var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer);
75 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);76 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
...@@ -79,66 +80,14 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void {...@@ -79,66 +80,14 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void {
79 .zig_version = builtin.zig_version_string,80 .zig_version = builtin.zig_version_string,
80 });81 });
8182
82 if (builtin.fuzz) blk: {83 if (builtin.fuzz) {
83 const cache_dir = opt_cache_dir.?;84 const coverage = fuzz_abi.fuzzer_coverage();
84 const coverage_id = fuzz_abi.fuzzer_coverage_id();85 try server.serveCoverageIdMessage(
85 const coverage_file_path: std.Build.Cache.Path = .{86 coverage.id,
86 .root_dir = .{87 coverage.runs,
87 .path = cache_dir,88 coverage.unique,
88 .handle = std.fs.cwd().openDir(cache_dir, .{}) catch |err| {89 coverage.seen,
89 if (err == error.FileNotFound) {90 );
90 try server.serveCoverageIdMessage(coverage_id, 0, 0, 0);
91 break :blk;
92 }
93
94 fatal("failed to access cache dir '{s}': {s}", .{
95 cache_dir, @errorName(err),
96 });
97 },
98 },
99 .sub_path = "v/" ++ std.fmt.hex(coverage_id),
100 };
101
102 var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {
103 if (err == error.FileNotFound) {
104 try server.serveCoverageIdMessage(coverage_id, 0, 0, 0);
105 break :blk;
106 }
107
108 fatal("failed to load coverage file '{f}': {s}", .{
109 coverage_file_path, @errorName(err),
110 });
111 };
112 defer coverage_file.close();
113
114 var rbuf: [0x1000]u8 = undefined;
115 var r = coverage_file.reader(&rbuf);
116
117 var header: fuzz_abi.SeenPcsHeader = undefined;
118 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {
119 fatal("failed to read from coverage file '{f}': {s}", .{
120 coverage_file_path, @errorName(err),
121 });
122 };
123
124 if (header.pcs_len == 0) {
125 fatal("corrupted coverage file '{f}': pcs_len was zero", .{
126 coverage_file_path,
127 });
128 }
129
130 var seen_count: usize = 0;
131 const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len);
132 for (0..chunk_count) |_| {
133 const seen = r.interface.takeInt(usize, .little) catch |err| {
134 fatal("failed to read from coverage file '{f}': {s}", .{
135 coverage_file_path, @errorName(err),
136 });
137 };
138 seen_count += @popCount(seen);
139 }
140
141 try server.serveCoverageIdMessage(coverage_id, header.n_runs, header.unique_runs, seen_count);
142 }91 }
14392
144 while (true) {93 while (true) {
...@@ -235,7 +184,7 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void {...@@ -235,7 +184,7 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void {
235 if (@errorReturnTrace()) |trace| {184 if (@errorReturnTrace()) |trace| {
236 std.debug.dumpStackTrace(trace.*);185 std.debug.dumpStackTrace(trace.*);
237 }186 }
238 std.debug.print("failed with error.{s}\n", .{@errorName(err)});187 std.debug.print("failed with error.{t}\n", .{err});
239 std.process.exit(1);188 std.process.exit(1);
240 },189 },
241 };190 };
...@@ -305,11 +254,11 @@ fn mainTerminal() void {...@@ -305,11 +254,11 @@ fn mainTerminal() void {
305 else => {254 else => {
306 fail_count += 1;255 fail_count += 1;
307 if (have_tty) {256 if (have_tty) {
308 std.debug.print("{d}/{d} {s}...FAIL ({s})\n", .{257 std.debug.print("{d}/{d} {s}...FAIL ({t})\n", .{
309 i + 1, test_fn_list.len, test_fn.name, @errorName(err),258 i + 1, test_fn_list.len, test_fn.name, err,
310 });259 });
311 } else {260 } else {
312 std.debug.print("FAIL ({s})\n", .{@errorName(err)});261 std.debug.print("FAIL ({t})\n", .{err});
313 }262 }
314 if (@errorReturnTrace()) |trace| {263 if (@errorReturnTrace()) |trace| {
315 std.debug.dumpStackTrace(trace.*);264 std.debug.dumpStackTrace(trace.*);
...@@ -450,7 +399,7 @@ pub fn fuzz(...@@ -450,7 +399,7 @@ pub fn fuzz(
450 else => {399 else => {
451 std.debug.lockStdErr();400 std.debug.lockStdErr();
452 if (@errorReturnTrace()) |trace| std.debug.dumpStackTrace(trace.*);401 if (@errorReturnTrace()) |trace| std.debug.dumpStackTrace(trace.*);
453 std.debug.print("failed with error.{s}\n", .{@errorName(err)});402 std.debug.print("failed with error.{t}\n", .{err});
454 std.process.exit(1);403 std.process.exit(1);
455 },404 },
456 };405 };
lib/fuzzer.zig+17-2
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("std");2const std = @import("std");
3const fatal = std.process.fatal;
3const mem = std.mem;4const mem = std.mem;
4const math = std.math;5const math = std.math;
5const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
...@@ -105,6 +106,7 @@ const Executable = struct {...@@ -105,6 +106,7 @@ const Executable = struct {
105 const coverage_file_len = @sizeOf(abi.SeenPcsHeader) +106 const coverage_file_len = @sizeOf(abi.SeenPcsHeader) +
106 pc_bitset_usizes * @sizeOf(usize) +107 pc_bitset_usizes * @sizeOf(usize) +
107 pcs.len * @sizeOf(usize);108 pcs.len * @sizeOf(usize);
109
108 if (populate) {110 if (populate) {
109 defer coverage_file.lock(.shared) catch |e| panic(111 defer coverage_file.lock(.shared) catch |e| panic(
110 "failed to demote lock for coverage file '{s}': {t}",112 "failed to demote lock for coverage file '{s}': {t}",
...@@ -581,8 +583,21 @@ export fn fuzzer_init(cache_dir_path: abi.Slice) void {...@@ -581,8 +583,21 @@ export fn fuzzer_init(cache_dir_path: abi.Slice) void {
581}583}
582584
583/// Invalid until `fuzzer_init` is called.585/// Invalid until `fuzzer_init` is called.
584export fn fuzzer_coverage_id() u64 {586export fn fuzzer_coverage() abi.Coverage {
585 return exec.pc_digest;587 const coverage_id = exec.pc_digest;
588 const header: *const abi.SeenPcsHeader = @ptrCast(@volatileCast(exec.shared_seen_pcs.items.ptr));
589
590 var seen_count: usize = 0;
591 for (header.seenBits()) |chunk| {
592 seen_count += @popCount(chunk);
593 }
594
595 return .{
596 .id = coverage_id,
597 .runs = header.n_runs,
598 .unique = header.unique_runs,
599 .seen = seen_count,
600 };
586}601}
587602
588/// fuzzer_init must be called beforehand603/// fuzzer_init must be called beforehand
lib/std/Build/abi.zig+11-1
...@@ -140,7 +140,7 @@ pub const Rebuild = extern struct {...@@ -140,7 +140,7 @@ pub const Rebuild = extern struct {
140pub const fuzz = struct {140pub const fuzz = struct {
141 pub const TestOne = *const fn (Slice) callconv(.c) void;141 pub const TestOne = *const fn (Slice) callconv(.c) void;
142 pub extern fn fuzzer_init(cache_dir_path: Slice) void;142 pub extern fn fuzzer_init(cache_dir_path: Slice) void;
143 pub extern fn fuzzer_coverage_id() u64;143 pub extern fn fuzzer_coverage() Coverage;
144 pub extern fn fuzzer_init_test(test_one: TestOne, unit_test_name: Slice) void;144 pub extern fn fuzzer_init_test(test_one: TestOne, unit_test_name: Slice) void;
145 pub extern fn fuzzer_new_input(bytes: Slice) void;145 pub extern fn fuzzer_new_input(bytes: Slice) void;
146 pub extern fn fuzzer_main(limit_kind: LimitKind, amount: u64) void;146 pub extern fn fuzzer_main(limit_kind: LimitKind, amount: u64) void;
...@@ -253,6 +253,16 @@ pub const fuzz = struct {...@@ -253,6 +253,16 @@ pub const fuzz = struct {
253 return .{ .locs_len_raw = @bitCast(locs_len) };253 return .{ .locs_len_raw = @bitCast(locs_len) };
254 }254 }
255 };255 };
256
257 /// Sent by lib/fuzzer to test_runner to obtain information about the
258 /// active memory mapped input file and cumulative stats about previous
259 /// fuzzing runs.
260 pub const Coverage = extern struct {
261 id: u64,
262 runs: u64,
263 unique: u64,
264 seen: u64,
265 };
256};266};
257267
258/// ABI bits specifically relating to the time report interface.268/// ABI bits specifically relating to the time report interface.
test/standalone/libfuzzer/main.zig+1-1
...@@ -24,7 +24,7 @@ pub fn main() !void {...@@ -24,7 +24,7 @@ pub fn main() !void {
24 abi.fuzzer_new_input(.fromSlice(""));24 abi.fuzzer_new_input(.fromSlice(""));
25 abi.fuzzer_new_input(.fromSlice("hello"));25 abi.fuzzer_new_input(.fromSlice("hello"));
2626
27 const pc_digest = abi.fuzzer_coverage_id();27 const pc_digest = abi.fuzzer_coverage().id;
28 const coverage_file_path = "v/" ++ std.fmt.hex(pc_digest);28 const coverage_file_path = "v/" ++ std.fmt.hex(pc_digest);
29 const coverage_file = try cache_dir.openFile(coverage_file_path, .{});29 const coverage_file = try cache_dir.openFile(coverage_file_path, .{});
30 defer coverage_file.close();30 defer coverage_file.close();