Skip to content

Commit e3f58bd

Browse files
committed
add runs per second to fuzzing ui
closes #21025
1 parent 9dc75f0 commit e3f58bd

File tree

6 files changed

+70
-2
lines changed

6 files changed

+70
-2
lines changed

lib/fuzzer/web/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
<ul>
147147
<li>Total Runs: <span id="statTotalRuns"></span></li>
148148
<li>Unique Runs: <span id="statUniqueRuns"></span></li>
149+
<li>Speed (Runs/Second): <span id="statSpeed"></span></li>
149150
<li>Coverage: <span id="statCoverage"></span></li>
150151
<li>Entry Points: <ul id="entryPointsList"></ul></li>
151152
</ul>

lib/fuzzer/web/main.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
const domSourceText = document.getElementById("sourceText");
66
const domStatTotalRuns = document.getElementById("statTotalRuns");
77
const domStatUniqueRuns = document.getElementById("statUniqueRuns");
8+
const domStatSpeed = document.getElementById("statSpeed");
89
const domStatCoverage = document.getElementById("statCoverage");
910
const domEntryPointsList = document.getElementById("entryPointsList");
1011

@@ -31,6 +32,9 @@
3132
const msg = decodeString(ptr, len);
3233
throw new Error("panic: " + msg);
3334
},
35+
timestamp: function () {
36+
return BigInt(new Date());
37+
},
3438
emitSourceIndexChange: onSourceIndexChange,
3539
emitCoverageUpdate: onCoverageUpdate,
3640
emitEntryPointsUpdate: renderStats,
@@ -157,6 +161,7 @@
157161
domStatTotalRuns.innerText = totalRuns;
158162
domStatUniqueRuns.innerText = uniqueRuns + " (" + percent(uniqueRuns, totalRuns) + "%)";
159163
domStatCoverage.innerText = coveredSourceLocations + " / " + totalSourceLocations + " (" + percent(coveredSourceLocations, totalSourceLocations) + "%)";
164+
domStatSpeed.innerText = wasm_exports.totalRunsPerSecond().toFixed(0);
160165

161166
const entryPoints = unwrapInt32Array(wasm_exports.entryPoints());
162167
resizeDomList(domEntryPointsList, entryPoints.length, "<li></li>");

lib/fuzzer/web/main.zig

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ const Walk = @import("Walk");
1010
const Decl = Walk.Decl;
1111
const html_render = @import("html_render");
1212

13+
/// Nanoseconds.
14+
var server_base_timestamp: i64 = 0;
15+
/// Milliseconds.
16+
var client_base_timestamp: i64 = 0;
17+
/// Relative to `server_base_timestamp`.
18+
var start_fuzzing_timestamp: i64 = undefined;
19+
1320
const js = struct {
1421
extern "js" fn log(ptr: [*]const u8, len: usize) void;
1522
extern "js" fn panic(ptr: [*]const u8, len: usize) noreturn;
23+
extern "js" fn timestamp() i64;
1624
extern "js" fn emitSourceIndexChange() void;
1725
extern "js" fn emitCoverageUpdate() void;
1826
extern "js" fn emitEntryPointsUpdate() void;
@@ -64,6 +72,7 @@ export fn message_end() void {
6472

6573
const tag: abi.ToClientTag = @enumFromInt(msg_bytes[0]);
6674
switch (tag) {
75+
.current_time => return currentTimeMessage(msg_bytes),
6776
.source_index => return sourceIndexMessage(msg_bytes) catch @panic("OOM"),
6877
.coverage_update => return coverageUpdateMessage(msg_bytes) catch @panic("OOM"),
6978
.entry_points => return entryPointsMessage(msg_bytes) catch @panic("OOM"),
@@ -117,16 +126,28 @@ export fn coveredSourceLocations() usize {
117126
return count;
118127
}
119128

129+
fn getCoverageUpdateHeader() *abi.CoverageUpdateHeader {
130+
return @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
131+
}
132+
120133
export fn totalRuns() u64 {
121-
const header: *abi.CoverageUpdateHeader = @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
134+
const header = getCoverageUpdateHeader();
122135
return header.n_runs;
123136
}
124137

125138
export fn uniqueRuns() u64 {
126-
const header: *abi.CoverageUpdateHeader = @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
139+
const header = getCoverageUpdateHeader();
127140
return header.unique_runs;
128141
}
129142

143+
export fn totalRunsPerSecond() f64 {
144+
@setFloatMode(.optimized);
145+
const header = getCoverageUpdateHeader();
146+
const ns_elapsed: f64 = @floatFromInt(nsSince(start_fuzzing_timestamp));
147+
const n_runs: f64 = @floatFromInt(header.n_runs);
148+
return n_runs / (ns_elapsed / std.time.ns_per_s);
149+
}
150+
130151
const String = Slice(u8);
131152

132153
fn Slice(T: type) type {
@@ -189,6 +210,18 @@ fn fatal(comptime format: []const u8, args: anytype) noreturn {
189210
js.panic(line.ptr, line.len);
190211
}
191212

213+
fn currentTimeMessage(msg_bytes: []u8) void {
214+
client_base_timestamp = js.timestamp();
215+
server_base_timestamp = @bitCast(msg_bytes[1..][0..8].*);
216+
}
217+
218+
/// Nanoseconds passed since a server timestamp.
219+
fn nsSince(server_timestamp: i64) i64 {
220+
const ms_passed = js.timestamp() - client_base_timestamp;
221+
const ns_passed = server_base_timestamp - server_timestamp;
222+
return ns_passed + ms_passed * std.time.ns_per_ms;
223+
}
224+
192225
fn sourceIndexMessage(msg_bytes: []u8) error{OutOfMemory}!void {
193226
const Header = abi.SourceIndexHeader;
194227
const header: Header = @bitCast(msg_bytes[0..@sizeOf(Header)].*);
@@ -205,6 +238,7 @@ fn sourceIndexMessage(msg_bytes: []u8) error{OutOfMemory}!void {
205238
const files: []const Coverage.File = @alignCast(std.mem.bytesAsSlice(Coverage.File, msg_bytes[files_start..files_end]));
206239
const source_locations: []const Coverage.SourceLocation = @alignCast(std.mem.bytesAsSlice(Coverage.SourceLocation, msg_bytes[source_locations_start..source_locations_end]));
207240

241+
start_fuzzing_timestamp = header.start_timestamp;
208242
try updateCoverage(directories, files, source_locations, string_bytes);
209243
js.emitSourceIndexChange();
210244
}

lib/std/Build/Fuzz.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub fn start(
6666
.coverage_files = .{},
6767
.coverage_mutex = .{},
6868
.coverage_condition = .{},
69+
70+
.base_timestamp = std.time.nanoTimestamp(),
6971
};
7072

7173
// For accepting HTTP connections.

lib/std/Build/Fuzz/WebServer.zig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ coverage_mutex: std.Thread.Mutex,
3333
/// Signaled when `coverage_files` changes.
3434
coverage_condition: std.Thread.Condition,
3535

36+
/// Time at initialization of WebServer.
37+
base_timestamp: i128,
38+
3639
const fuzzer_bin_name = "fuzzer";
3740
const fuzzer_arch_os_abi = "wasm32-freestanding";
3841
const fuzzer_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
@@ -43,6 +46,7 @@ const CoverageMap = struct {
4346
source_locations: []Coverage.SourceLocation,
4447
/// Elements are indexes into `source_locations` pointing to the unit tests that are being fuzz tested.
4548
entry_points: std.ArrayListUnmanaged(u32),
49+
start_timestamp: i64,
4650

4751
fn deinit(cm: *CoverageMap, gpa: Allocator) void {
4852
std.posix.munmap(cm.mapped_memory);
@@ -87,6 +91,10 @@ pub fn run(ws: *WebServer) void {
8791
}
8892
}
8993

94+
fn now(s: *const WebServer) i64 {
95+
return @intCast(std.time.nanoTimestamp() - s.base_timestamp);
96+
}
97+
9098
fn accept(ws: *WebServer, connection: std.net.Server.Connection) void {
9199
defer connection.stream.close();
92100

@@ -381,6 +389,13 @@ fn serveWebSocket(ws: *WebServer, web_socket: *std.http.WebSocket) !void {
381389
ws.coverage_mutex.lock();
382390
defer ws.coverage_mutex.unlock();
383391

392+
// On first connection, the client needs to know what time the server
393+
// thinks it is to rebase timestamps.
394+
{
395+
const timestamp_message: abi.CurrentTime = .{ .base = ws.now() };
396+
try web_socket.writeMessage(std.mem.asBytes(&timestamp_message), .binary);
397+
}
398+
384399
// On first connection, the client needs all the coverage information
385400
// so that subsequent updates can contain only the updated bits.
386401
var prev_unique_runs: usize = 0;
@@ -416,6 +431,7 @@ fn sendCoverageContext(
416431
.files_len = @intCast(coverage_map.coverage.files.entries.len),
417432
.source_locations_len = @intCast(coverage_map.source_locations.len),
418433
.string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len),
434+
.start_timestamp = coverage_map.start_timestamp,
419435
};
420436
const iovecs: [5]std.posix.iovec_const = .{
421437
makeIov(std.mem.asBytes(&header)),
@@ -582,6 +598,7 @@ fn prepareTables(
582598
.mapped_memory = undefined, // populated below
583599
.source_locations = undefined, // populated below
584600
.entry_points = .{},
601+
.start_timestamp = ws.now(),
585602
};
586603
errdefer gop.value_ptr.coverage.deinit(gpa);
587604

lib/std/Build/Fuzz/abi.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,19 @@ pub const SeenPcsHeader = extern struct {
4343
};
4444

4545
pub const ToClientTag = enum(u8) {
46+
current_time,
4647
source_index,
4748
coverage_update,
4849
entry_points,
4950
_,
5051
};
5152

53+
pub const CurrentTime = extern struct {
54+
tag: ToClientTag = .current_time,
55+
/// Number of nanoseconds that all other timestamps are in reference to.
56+
base: i64 align(1),
57+
};
58+
5259
/// Sent to the fuzzer web client on first connection to the websocket URL.
5360
///
5461
/// Trailing:
@@ -62,6 +69,8 @@ pub const SourceIndexHeader = extern struct {
6269
files_len: u32,
6370
source_locations_len: u32,
6471
string_bytes_len: u32,
72+
/// When, according to the server, fuzzing started.
73+
start_timestamp: i64 align(4),
6574

6675
pub const Flags = packed struct(u32) {
6776
tag: ToClientTag = .source_index,

0 commit comments

Comments
 (0)