Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/fuzzer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
<div id="sectStats" class="hidden">
<ul>
<li>Total Runs: <span id="statTotalRuns"></span></li>
<li>Rate: <span id="statRate"></span></li>
<li>Unique Runs: <span id="statUniqueRuns"></span></li>
<li>Coverage: <span id="statCoverage"></span></li>
<li>Lowest Stack: <span id="statLowestStack"></span></li>
Expand Down
8 changes: 8 additions & 0 deletions lib/fuzzer/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const domSectStats = document.getElementById("sectStats");
const domSourceText = document.getElementById("sourceText");
const domStatTotalRuns = document.getElementById("statTotalRuns");
const domStatRate = document.getElementById("statRate");
const domStatUniqueRuns = document.getElementById("statUniqueRuns");
const domStatCoverage = document.getElementById("statCoverage");
const domStatLowestStack = document.getElementById("statLowestStack");
Expand Down Expand Up @@ -34,6 +35,7 @@
},
emitSourceIndexChange: onSourceIndexChange,
emitCoverageUpdate: onCoverageUpdate,
emitPeriodicUpdate: onPeriodicUpdate,
emitEntryPointsUpdate: renderStats,
},
}).then(function(obj) {
Expand Down Expand Up @@ -146,16 +148,22 @@
renderCoverage();
}

function onPeriodicUpdate() {
renderStats();
}

function render() {
domStatus.classList.add("hidden");
}

function renderStats() {
const totalRuns = wasm_exports.totalRuns();
const cyclesPerSecond = wasm_exports.cyclesPerSecond();
const uniqueRuns = wasm_exports.uniqueRuns();
const totalSourceLocations = wasm_exports.totalSourceLocations();
const coveredSourceLocations = wasm_exports.coveredSourceLocations();
domStatTotalRuns.innerText = totalRuns;
domStatRate.innerText = cyclesPerSecond + " cycles / second";
domStatUniqueRuns.innerText = uniqueRuns + " (" + percent(uniqueRuns, totalRuns) + "%)";
domStatCoverage.innerText = coveredSourceLocations + " / " + totalSourceLocations + " (" + percent(coveredSourceLocations, totalSourceLocations) + "%)";
domStatLowestStack.innerText = unwrapString(wasm_exports.lowestStack());
Expand Down
13 changes: 13 additions & 0 deletions lib/fuzzer/wasm/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const js = struct {
extern "js" fn panic(ptr: [*]const u8, len: usize) noreturn;
extern "js" fn emitSourceIndexChange() void;
extern "js" fn emitCoverageUpdate() void;
extern "js" fn emitPeriodicUpdate() void;
extern "js" fn emitEntryPointsUpdate() void;
};

Expand Down Expand Up @@ -67,6 +68,7 @@ export fn message_end() void {
.source_index => return sourceIndexMessage(msg_bytes) catch @panic("OOM"),
.coverage_update => return coverageUpdateMessage(msg_bytes) catch @panic("OOM"),
.entry_points => return entryPointsMessage(msg_bytes) catch @panic("OOM"),
.periodic => return periodicMessage(msg_bytes),
_ => unreachable,
}
}
Expand Down Expand Up @@ -129,6 +131,10 @@ export fn totalRuns() u64 {
return header.n_runs;
}

export fn cyclesPerSecond() u64 {
return recent_periodic_update.cycles_per_second;
}

export fn uniqueRuns() u64 {
const header: *abi.CoverageUpdateHeader = @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
return header.unique_runs;
Expand Down Expand Up @@ -235,6 +241,11 @@ export fn entryPoints() Slice(u32) {
return Slice(u32).init(entry_points.items);
}

fn periodicMessage(msg_bytes: []u8) void {
recent_periodic_update = std.mem.bytesToValue(abi.PeriodicUpdateHeader, msg_bytes);
js.emitPeriodicUpdate();
}

/// Index into `coverage_source_locations`.
const SourceLocationIndex = enum(u32) {
_,
Expand Down Expand Up @@ -350,6 +361,8 @@ var coverage = Coverage.init;
var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{};
/// Contains the most recent coverage update message, unmodified.
var recent_coverage_update: std.ArrayListAlignedUnmanaged(u8, @alignOf(u64)) = .{};
/// Contains the most recent periodic update message, unmodified.
var recent_periodic_update: abi.PeriodicUpdateHeader = .{ .cycles_per_second = 0 };

fn updateCoverage(
directories: []const Coverage.String,
Expand Down
4 changes: 3 additions & 1 deletion lib/std/Build/Fuzz.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const build_runner = @import("root");
pub const WebServer = @import("Fuzz/WebServer.zig");
pub const abi = @import("Fuzz/abi.zig");

pub const StartError = Allocator.Error || std.time.Timer.Error;

pub fn start(
gpa: Allocator,
arena: Allocator,
Expand All @@ -24,7 +26,7 @@ pub fn start(
ttyconf: std.io.tty.Config,
listen_address: std.net.Address,
prog_node: std.Progress.Node,
) Allocator.Error!void {
) StartError!void {
const fuzz_run_steps = block: {
const rebuild_node = prog_node.start("Rebuilding Unit Tests", 0);
defer rebuild_node.end();
Expand Down
24 changes: 22 additions & 2 deletions lib/std/Build/Fuzz/WebServer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ fn buildWasmBinary(
try std.fmt.allocPrint(arena, "-MWalk={}", .{walk_src_path}), //
"--dep", "Walk", //
try std.fmt.allocPrint(arena, "-Mhtml_render={}", .{html_render_src_path}), //
"--zig-lib-dir",
ws.zig_lib_directory.path orelse ".",
"--listen=-",
});

Expand Down Expand Up @@ -385,10 +387,12 @@ fn serveWebSocket(ws: *WebServer, web_socket: *std.http.WebSocket) !void {
// so that subsequent updates can contain only the updated bits.
var prev_unique_runs: usize = 0;
var prev_entry_points: usize = 0;
try sendCoverageContext(ws, web_socket, &prev_unique_runs, &prev_entry_points);
var timer = try std.time.Timer.start();
var last_cycle_timed: usize = 0;
try sendCoverageContext(ws, web_socket, &prev_unique_runs, &prev_entry_points, &timer, &last_cycle_timed);
while (true) {
ws.coverage_condition.timedWait(&ws.coverage_mutex, std.time.ns_per_ms * 500) catch {};
try sendCoverageContext(ws, web_socket, &prev_unique_runs, &prev_entry_points);
try sendCoverageContext(ws, web_socket, &prev_unique_runs, &prev_entry_points, &timer, &last_cycle_timed);
}
}

Expand All @@ -397,6 +401,8 @@ fn sendCoverageContext(
web_socket: *std.http.WebSocket,
prev_unique_runs: *usize,
prev_entry_points: *usize,
timer: *std.time.Timer,
last_cycle_timed: *usize,
) !void {
const coverage_maps = ws.coverage_files.values();
if (coverage_maps.len == 0) return;
Expand All @@ -407,6 +413,7 @@ fn sendCoverageContext(
const n_runs = @atomicLoad(usize, &cov_header.n_runs, .monotonic);
const unique_runs = @atomicLoad(usize, &cov_header.unique_runs, .monotonic);
const lowest_stack = @atomicLoad(usize, &cov_header.lowest_stack, .monotonic);
const duration = timer.read();
if (prev_unique_runs.* != unique_runs) {
// There has been an update.
if (prev_unique_runs.* == 0) {
Expand Down Expand Up @@ -456,6 +463,19 @@ fn sendCoverageContext(

prev_entry_points.* = coverage_map.entry_points.items.len;
}

if (duration >= std.time.ns_per_s) {
const time_s: usize = @intCast(duration / std.time.ns_per_s);
const window_size = n_runs -% last_cycle_timed.*;
const cycles_per_second = window_size / time_s;
last_cycle_timed.* = n_runs;

try web_socket.writeMessage(std.mem.asBytes(&abi.PeriodicUpdateHeader{
.cycles_per_second = cycles_per_second,
Copy link
Member

@andrewrk andrewrk Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be calculated client side.

the only additional thing that needs to be sent by the server is two pieces of static data that are already known when the connection is made:

  • what time the server thinks it currently is
  • what time according to the server fuzzing started

then the client can do the calculation based on what time it is currently and the number of runs.

}), .binary);

timer.reset();
}
}

fn serveSourcesTar(ws: *WebServer, request: *std.http.Server.Request) !void {
Expand Down
12 changes: 12 additions & 0 deletions lib/std/Build/Fuzz/abi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub const ToClientTag = enum(u8) {
source_index,
coverage_update,
entry_points,
periodic,
_,
};

Expand Down Expand Up @@ -103,3 +104,14 @@ pub const EntryPointHeader = extern struct {
locs_len: u24,
};
};

/// Sent to the fuzzer web client on a periodic basis.
pub const PeriodicUpdateHeader = extern struct {
flags: Flags = .{},
cycles_per_second: u64,

pub const Flags = packed struct(u64) {
tag: ToClientTag = .periodic,
_: u56 = 0,
};
};