implement review suggestions

This commit is contained in:
Loris Cro 2025-09-25 17:02:43 +02:00
parent 0feacc2b81
commit 9bb0b43ea3
5 changed files with 56 additions and 78 deletions

View File

@ -280,21 +280,21 @@ pub fn main() !void {
} }
} else if (mem.startsWith(u8, arg, "--fuzz=")) { } else if (mem.startsWith(u8, arg, "--fuzz=")) {
const value = arg["--fuzz=".len..]; 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 unit: u8 = value[value.len - 1];
const digits = switch (value[value.len - 1]) { const digits = switch (unit) {
'0'...'9' => value, '0'...'9' => value,
'K', 'M', 'G' => value[0 .. value.len - 1], 'K', 'M', 'G' => value[0 .. value.len - 1],
else => fatal( 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 { const amount = std.fmt.parseInt(u64, digits, 10) catch {
fatal( 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, 'K' => 1000,
'M' => 1_000_000, 'M' => 1_000_000,
'G' => 1_000_000_000, 'G' => 1_000_000_000,
}) catch fatal("fuzzing limit amount overflows u64\n", .{}); }) catch fatal("fuzzing limit amount overflows u64", .{});
fuzz = .{ fuzz = .{
.limit = .{ .limit = .{
@ -520,7 +520,11 @@ pub fn main() !void {
}; };
if (run.web_server) |*web_server| { 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 }); web_server.finishBuild(.{ .fuzz = fuzz != null });
} }

View File

@ -56,20 +56,21 @@ pub fn main() void {
} }
} }
fba.reset();
if (builtin.fuzz) { if (builtin.fuzz) {
const cache_dir = opt_cache_dir orelse @panic("missing --cache-dir=[path] argument"); const cache_dir = opt_cache_dir orelse @panic("missing --cache-dir=[path] argument");
fuzz_abi.fuzzer_init(.fromSlice(cache_dir)); fuzz_abi.fuzzer_init(.fromSlice(cache_dir));
} }
fba.reset();
if (listen) { if (listen) {
return mainServer(opt_cache_dir) catch @panic("internal test runner failure"); return mainServer() catch @panic("internal test runner failure");
} else { } else {
return mainTerminal(); return mainTerminal();
} }
} }
fn mainServer(opt_cache_dir: ?[]const u8) !void { fn mainServer() !void {
@disableInstrumentation(); @disableInstrumentation();
var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer); var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer);
var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_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, .zig_version = builtin.zig_version_string,
}); });
if (builtin.fuzz) blk: { if (builtin.fuzz) {
const cache_dir = opt_cache_dir.?; const coverage = fuzz_abi.fuzzer_coverage();
const coverage_id = fuzz_abi.fuzzer_coverage_id(); try server.serveCoverageIdMessage(
const coverage_file_path: std.Build.Cache.Path = .{ coverage.id,
.root_dir = .{ coverage.runs,
.path = cache_dir, coverage.unique,
.handle = std.fs.cwd().openDir(cache_dir, .{}) catch |err| { coverage.seen,
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);
} }
while (true) { while (true) {
@ -235,7 +184,7 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void {
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(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); std.process.exit(1);
}, },
}; };
@ -305,11 +254,11 @@ fn mainTerminal() void {
else => { else => {
fail_count += 1; fail_count += 1;
if (have_tty) { if (have_tty) {
std.debug.print("{d}/{d} {s}...FAIL ({s})\n", .{ std.debug.print("{d}/{d} {s}...FAIL ({t})\n", .{
i + 1, test_fn_list.len, test_fn.name, @errorName(err), i + 1, test_fn_list.len, test_fn.name, err,
}); });
} else { } else {
std.debug.print("FAIL ({s})\n", .{@errorName(err)}); std.debug.print("FAIL ({t})\n", .{err});
} }
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*); std.debug.dumpStackTrace(trace.*);
@ -450,7 +399,7 @@ pub fn fuzz(
else => { else => {
std.debug.lockStdErr(); std.debug.lockStdErr();
if (@errorReturnTrace()) |trace| std.debug.dumpStackTrace(trace.*); 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); std.process.exit(1);
}, },
}; };

View File

@ -1,5 +1,6 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const fatal = std.process.fatal;
const mem = std.mem; const mem = std.mem;
const math = std.math; const math = std.math;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
@ -105,6 +106,7 @@ const Executable = struct {
const coverage_file_len = @sizeOf(abi.SeenPcsHeader) + const coverage_file_len = @sizeOf(abi.SeenPcsHeader) +
pc_bitset_usizes * @sizeOf(usize) + pc_bitset_usizes * @sizeOf(usize) +
pcs.len * @sizeOf(usize); pcs.len * @sizeOf(usize);
if (populate) { if (populate) {
defer coverage_file.lock(.shared) catch |e| panic( defer coverage_file.lock(.shared) catch |e| panic(
"failed to demote lock for coverage file '{s}': {t}", "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. /// Invalid until `fuzzer_init` is called.
export fn fuzzer_coverage_id() u64 { export fn fuzzer_coverage() abi.Coverage {
return exec.pc_digest; 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 /// fuzzer_init must be called beforehand

View File

@ -140,7 +140,7 @@ pub const Rebuild = extern struct {
pub const fuzz = struct { pub const fuzz = struct {
pub const TestOne = *const fn (Slice) callconv(.c) void; pub const TestOne = *const fn (Slice) callconv(.c) void;
pub extern fn fuzzer_init(cache_dir_path: Slice) 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_init_test(test_one: TestOne, unit_test_name: Slice) void;
pub extern fn fuzzer_new_input(bytes: Slice) void; pub extern fn fuzzer_new_input(bytes: Slice) void;
pub extern fn fuzzer_main(limit_kind: LimitKind, amount: u64) 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) }; 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. /// ABI bits specifically relating to the time report interface.

View File

@ -24,7 +24,7 @@ pub fn main() !void {
abi.fuzzer_new_input(.fromSlice("")); abi.fuzzer_new_input(.fromSlice(""));
abi.fuzzer_new_input(.fromSlice("hello")); 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_path = "v/" ++ std.fmt.hex(pc_digest);
const coverage_file = try cache_dir.openFile(coverage_file_path, .{}); const coverage_file = try cache_dir.openFile(coverage_file_path, .{});
defer coverage_file.close(); defer coverage_file.close();