Commit 9bb0b43ea3

Loris Cro <kappaloris@gmail.com>
2025-09-25 17:02:43
implement review suggestions
1 parent 0feacc2
Changed files (5)
lib
test
standalone
libfuzzer
lib/compiler/build_runner.zig
@@ -280,21 +280,21 @@ pub fn main() !void {
                 }
             } else if (mem.startsWith(u8, arg, "--fuzz=")) {
                 const value = arg["--fuzz=".len..];
-                if (value.len == 0) fatal("missing argument to --fuzz\n", .{});
+                if (value.len == 0) fatal("missing argument to --fuzz", .{});
 
                 const unit: u8 = value[value.len - 1];
-                const digits = switch (value[value.len - 1]) {
+                const digits = switch (unit) {
                     '0'...'9' => value,
                     'K', 'M', 'G' => value[0 .. value.len - 1],
                     else => fatal(
-                        "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]\n",
+                        "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]",
                         .{},
                     ),
                 };
 
                 const amount = std.fmt.parseInt(u64, digits, 10) catch {
                     fatal(
-                        "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]\n",
+                        "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]",
                         .{},
                     );
                 };
@@ -305,7 +305,7 @@ pub fn main() !void {
                     'K' => 1000,
                     'M' => 1_000_000,
                     'G' => 1_000_000_000,
-                }) catch fatal("fuzzing limit amount overflows u64\n", .{});
+                }) catch fatal("fuzzing limit amount overflows u64", .{});
 
                 fuzz = .{
                     .limit = .{
@@ -520,7 +520,11 @@ pub fn main() !void {
         };
 
         if (run.web_server) |*web_server| {
-            if (fuzz) |mode| assert(mode == .forever);
+            if (fuzz) |mode| if (mode != .forever) fatal(
+                "error: limited fuzzing is not implemented yet for --webui",
+                .{},
+            );
+
             web_server.finishBuild(.{ .fuzz = fuzz != null });
         }
 
lib/compiler/test_runner.zig
@@ -56,20 +56,21 @@ pub fn main() void {
         }
     }
 
-    fba.reset();
     if (builtin.fuzz) {
         const cache_dir = opt_cache_dir orelse @panic("missing --cache-dir=[path] argument");
         fuzz_abi.fuzzer_init(.fromSlice(cache_dir));
     }
 
+    fba.reset();
+
     if (listen) {
-        return mainServer(opt_cache_dir) catch @panic("internal test runner failure");
+        return mainServer() catch @panic("internal test runner failure");
     } else {
         return mainTerminal();
     }
 }
 
-fn mainServer(opt_cache_dir: ?[]const u8) !void {
+fn mainServer() !void {
     @disableInstrumentation();
     var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer);
     var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
@@ -79,66 +80,14 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void {
         .zig_version = builtin.zig_version_string,
     });
 
-    if (builtin.fuzz) blk: {
-        const cache_dir = opt_cache_dir.?;
-        const coverage_id = fuzz_abi.fuzzer_coverage_id();
-        const coverage_file_path: std.Build.Cache.Path = .{
-            .root_dir = .{
-                .path = cache_dir,
-                .handle = std.fs.cwd().openDir(cache_dir, .{}) catch |err| {
-                    if (err == error.FileNotFound) {
-                        try server.serveCoverageIdMessage(coverage_id, 0, 0, 0);
-                        break :blk;
-                    }
-
-                    fatal("failed to access cache dir '{s}': {s}", .{
-                        cache_dir, @errorName(err),
-                    });
-                },
-            },
-            .sub_path = "v/" ++ std.fmt.hex(coverage_id),
-        };
-
-        var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {
-            if (err == error.FileNotFound) {
-                try server.serveCoverageIdMessage(coverage_id, 0, 0, 0);
-                break :blk;
-            }
-
-            fatal("failed to load coverage file '{f}': {s}", .{
-                coverage_file_path, @errorName(err),
-            });
-        };
-        defer coverage_file.close();
-
-        var rbuf: [0x1000]u8 = undefined;
-        var r = coverage_file.reader(&rbuf);
-
-        var header: fuzz_abi.SeenPcsHeader = undefined;
-        r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {
-            fatal("failed to read from coverage file '{f}': {s}", .{
-                coverage_file_path, @errorName(err),
-            });
-        };
-
-        if (header.pcs_len == 0) {
-            fatal("corrupted coverage file '{f}': pcs_len was zero", .{
-                coverage_file_path,
-            });
-        }
-
-        var seen_count: usize = 0;
-        const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len);
-        for (0..chunk_count) |_| {
-            const seen = r.interface.takeInt(usize, .little) catch |err| {
-                fatal("failed to read from coverage file '{f}': {s}", .{
-                    coverage_file_path, @errorName(err),
-                });
-            };
-            seen_count += @popCount(seen);
-        }
-
-        try server.serveCoverageIdMessage(coverage_id, header.n_runs, header.unique_runs, seen_count);
+    if (builtin.fuzz) {
+        const coverage = fuzz_abi.fuzzer_coverage();
+        try server.serveCoverageIdMessage(
+            coverage.id,
+            coverage.runs,
+            coverage.unique,
+            coverage.seen,
+        );
     }
 
     while (true) {
@@ -235,7 +184,7 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void {
                         if (@errorReturnTrace()) |trace| {
                             std.debug.dumpStackTrace(trace.*);
                         }
-                        std.debug.print("failed with error.{s}\n", .{@errorName(err)});
+                        std.debug.print("failed with error.{t}\n", .{err});
                         std.process.exit(1);
                     },
                 };
@@ -305,11 +254,11 @@ fn mainTerminal() void {
             else => {
                 fail_count += 1;
                 if (have_tty) {
-                    std.debug.print("{d}/{d} {s}...FAIL ({s})\n", .{
-                        i + 1, test_fn_list.len, test_fn.name, @errorName(err),
+                    std.debug.print("{d}/{d} {s}...FAIL ({t})\n", .{
+                        i + 1, test_fn_list.len, test_fn.name, err,
                     });
                 } else {
-                    std.debug.print("FAIL ({s})\n", .{@errorName(err)});
+                    std.debug.print("FAIL ({t})\n", .{err});
                 }
                 if (@errorReturnTrace()) |trace| {
                     std.debug.dumpStackTrace(trace.*);
@@ -450,7 +399,7 @@ pub fn fuzz(
                 else => {
                     std.debug.lockStdErr();
                     if (@errorReturnTrace()) |trace| std.debug.dumpStackTrace(trace.*);
-                    std.debug.print("failed with error.{s}\n", .{@errorName(err)});
+                    std.debug.print("failed with error.{t}\n", .{err});
                     std.process.exit(1);
                 },
             };
lib/std/Build/abi.zig
@@ -140,7 +140,7 @@ pub const Rebuild = extern struct {
 pub const fuzz = struct {
     pub const TestOne = *const fn (Slice) callconv(.c) void;
     pub extern fn fuzzer_init(cache_dir_path: Slice) void;
-    pub extern fn fuzzer_coverage_id() u64;
+    pub extern fn fuzzer_coverage() Coverage;
     pub extern fn fuzzer_init_test(test_one: TestOne, unit_test_name: Slice) void;
     pub extern fn fuzzer_new_input(bytes: Slice) void;
     pub extern fn fuzzer_main(limit_kind: LimitKind, amount: u64) void;
@@ -253,6 +253,16 @@ pub const fuzz = struct {
             return .{ .locs_len_raw = @bitCast(locs_len) };
         }
     };
+
+    /// Sent by lib/fuzzer to test_runner to obtain information about the
+    /// active memory mapped input file and cumulative stats about previous
+    /// fuzzing runs.
+    pub const Coverage = extern struct {
+        id: u64,
+        runs: u64,
+        unique: u64,
+        seen: u64,
+    };
 };
 
 /// ABI bits specifically relating to the time report interface.
lib/fuzzer.zig
@@ -1,5 +1,6 @@
 const builtin = @import("builtin");
 const std = @import("std");
+const fatal = std.process.fatal;
 const mem = std.mem;
 const math = std.math;
 const Allocator = mem.Allocator;
@@ -105,6 +106,7 @@ const Executable = struct {
         const coverage_file_len = @sizeOf(abi.SeenPcsHeader) +
             pc_bitset_usizes * @sizeOf(usize) +
             pcs.len * @sizeOf(usize);
+
         if (populate) {
             defer coverage_file.lock(.shared) catch |e| panic(
                 "failed to demote lock for coverage file '{s}': {t}",
@@ -581,8 +583,21 @@ export fn fuzzer_init(cache_dir_path: abi.Slice) void {
 }
 
 /// Invalid until `fuzzer_init` is called.
-export fn fuzzer_coverage_id() u64 {
-    return exec.pc_digest;
+export fn fuzzer_coverage() abi.Coverage {
+    const coverage_id = exec.pc_digest;
+    const header: *const abi.SeenPcsHeader = @ptrCast(@volatileCast(exec.shared_seen_pcs.items.ptr));
+
+    var seen_count: usize = 0;
+    for (header.seenBits()) |chunk| {
+        seen_count += @popCount(chunk);
+    }
+
+    return .{
+        .id = coverage_id,
+        .runs = header.n_runs,
+        .unique = header.unique_runs,
+        .seen = seen_count,
+    };
 }
 
 /// fuzzer_init must be called beforehand
test/standalone/libfuzzer/main.zig
@@ -24,7 +24,7 @@ pub fn main() !void {
     abi.fuzzer_new_input(.fromSlice(""));
     abi.fuzzer_new_input(.fromSlice("hello"));
 
-    const pc_digest = abi.fuzzer_coverage_id();
+    const pc_digest = abi.fuzzer_coverage().id;
     const coverage_file_path = "v/" ++ std.fmt.hex(pc_digest);
     const coverage_file = try cache_dir.openFile(coverage_file_path, .{});
     defer coverage_file.close();