master
  1//! This file is shared among Zig code running in wildly different contexts:
  2//! * The build runner, running on the host computer
  3//! * The build system web interface Wasm code, running in the browser
  4//! * `libfuzzer`, compiled alongside unit tests
  5//!
  6//! All of these components interface to some degree via an ABI:
  7//! * The build runner communicates with the web interface over a WebSocket connection
  8//! * The build runner communicates with `libfuzzer` over a shared memory-mapped file
  9
 10// Check that no WebSocket message type has implicit padding bits. This ensures we never send any
 11// undefined bits over the wire, and also helps validate that the layout doesn't differ between, for
 12// instance, the web server in `std.Build` and the Wasm client.
 13comptime {
 14    const check = struct {
 15        fn check(comptime T: type) void {
 16            const std = @import("std");
 17            std.debug.assert(@typeInfo(T) == .@"struct");
 18            std.debug.assert(@typeInfo(T).@"struct".layout == .@"extern");
 19            std.debug.assert(std.meta.hasUniqueRepresentation(T));
 20        }
 21    }.check;
 22
 23    // server->client
 24    check(Hello);
 25    check(StatusUpdate);
 26    check(StepUpdate);
 27    check(fuzz.SourceIndexHeader);
 28    check(fuzz.CoverageUpdateHeader);
 29    check(fuzz.EntryPointHeader);
 30    check(time_report.GenericResult);
 31    check(time_report.CompileResult);
 32
 33    // client->server
 34    check(Rebuild);
 35}
 36
 37/// All WebSocket messages sent by the server to the client begin with a `ToClientTag` byte. This
 38/// enum is non-exhaustive only to avoid Illegal Behavior when malformed messages are sent over the
 39/// socket; unnamed tags are an error condition and should terminate the connection.
 40///
 41/// Every tag has a curresponding `extern struct` representing the full message (or a header of the
 42/// message if it is variable-length). For instance, `.hello` corresponds to `Hello`.
 43///
 44/// When introducing a tag, make sure to add a corresponding `extern struct` whose first field is
 45/// this enum, and `check` its layout in the `comptime` block above.
 46pub const ToClientTag = enum(u8) {
 47    hello,
 48    status_update,
 49    step_update,
 50
 51    // `--fuzz`
 52    fuzz_source_index,
 53    fuzz_coverage_update,
 54    fuzz_entry_points,
 55
 56    // `--time-report`
 57    time_report_generic_result,
 58    time_report_compile_result,
 59    time_report_run_test_result,
 60
 61    _,
 62};
 63
 64/// Like `ToClientTag`, but for messages sent by the client to the server.
 65pub const ToServerTag = enum(u8) {
 66    rebuild,
 67
 68    _,
 69};
 70
 71/// The current overall status of the build runner.
 72/// Keep in sync with indices in web UI `main.js:updateBuildStatus`.
 73pub const BuildStatus = enum(u8) {
 74    idle,
 75    watching,
 76    running,
 77    fuzz_init,
 78};
 79
 80/// WebSocket server->client.
 81///
 82/// Sent by the server as the first message after a WebSocket connection opens to provide basic
 83/// information about the server, the build graph, etc.
 84///
 85/// Trailing:
 86/// * `step_name_len: u32` for each `steps_len`
 87/// * `step_name: [step_name_len]u8` for each `step_name_len`
 88/// * `step_status: u8` for every 4 `steps_len`; every 2 bits is a `StepUpdate.Status`, LSBs first
 89pub const Hello = extern struct {
 90    tag: ToClientTag = .hello,
 91
 92    status: BuildStatus,
 93    flags: Flags,
 94
 95    /// Any message containing a timestamp represents it as a number of nanoseconds relative to when
 96    /// the build began. This field is the current timestamp, represented in that form.
 97    timestamp: i64 align(4),
 98
 99    /// The number of steps in the build graph which are reachable from the top-level step[s] being
100    /// run; in other words, the number of steps which will be executed by this build. The name of
101    /// each step trails this message.
102    steps_len: u32 align(1),
103
104    pub const Flags = packed struct(u16) {
105        /// Whether time reporting is enabled.
106        time_report: bool,
107        _: u15 = 0,
108    };
109};
110/// WebSocket server->client.
111///
112/// Indicates that the build status has changed.
113pub const StatusUpdate = extern struct {
114    tag: ToClientTag = .status_update,
115    new: BuildStatus,
116};
117/// WebSocket server->client.
118///
119/// Indicates a change in a step's status.
120pub const StepUpdate = extern struct {
121    tag: ToClientTag = .step_update,
122    step_idx: u32 align(1),
123    bits: packed struct(u8) {
124        status: Status,
125        _: u6 = 0,
126    },
127    /// Keep in sync with indices in web UI `main.js:updateStepStatus`.
128    pub const Status = enum(u2) {
129        pending,
130        wip,
131        success,
132        failure,
133    };
134};
135
136pub const Rebuild = extern struct {
137    tag: ToServerTag = .rebuild,
138};
139
140/// ABI bits specifically relating to the fuzzer interface.
141pub const fuzz = struct {
142    pub const TestOne = *const fn (Slice) callconv(.c) void;
143    pub extern fn fuzzer_init(cache_dir_path: Slice) void;
144    pub extern fn fuzzer_coverage() Coverage;
145    pub extern fn fuzzer_init_test(test_one: TestOne, unit_test_name: Slice) void;
146    pub extern fn fuzzer_new_input(bytes: Slice) void;
147    pub extern fn fuzzer_main(limit_kind: LimitKind, amount: u64) void;
148    pub extern fn fuzzer_unslide_address(addr: usize) usize;
149
150    pub const Slice = extern struct {
151        ptr: [*]const u8,
152        len: usize,
153
154        pub fn toSlice(s: Slice) []const u8 {
155            return s.ptr[0..s.len];
156        }
157
158        pub fn fromSlice(s: []const u8) Slice {
159            return .{ .ptr = s.ptr, .len = s.len };
160        }
161    };
162
163    pub const LimitKind = enum(u8) { forever, iterations };
164
165    /// libfuzzer uses this and its usize is the one that counts. To match the ABI,
166    /// make the ints be the size of the target used with libfuzzer.
167    ///
168    /// Trailing:
169    /// * 1 bit per pc_addr, usize elements
170    /// * pc_addr: usize for each pcs_len
171    pub const SeenPcsHeader = extern struct {
172        n_runs: usize,
173        unique_runs: usize,
174        pcs_len: usize,
175
176        /// Used for comptime assertions. Provides a mechanism for strategically
177        /// causing compile errors.
178        pub const trailing = .{
179            .pc_bits_usize,
180            .pc_addr,
181        };
182
183        pub fn headerEnd(header: *const SeenPcsHeader) []const usize {
184            const ptr: [*]align(@alignOf(usize)) const u8 = @ptrCast(header);
185            const header_end_ptr: [*]const usize = @ptrCast(ptr + @sizeOf(SeenPcsHeader));
186            const pcs_len = header.pcs_len;
187            return header_end_ptr[0 .. pcs_len + seenElemsLen(pcs_len)];
188        }
189
190        pub fn seenBits(header: *const SeenPcsHeader) []const usize {
191            return header.headerEnd()[0..seenElemsLen(header.pcs_len)];
192        }
193
194        pub fn seenElemsLen(pcs_len: usize) usize {
195            return (pcs_len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
196        }
197
198        pub fn pcAddrs(header: *const SeenPcsHeader) []const usize {
199            const pcs_len = header.pcs_len;
200            return header.headerEnd()[seenElemsLen(pcs_len)..][0..pcs_len];
201        }
202    };
203
204    /// WebSocket server->client.
205    ///
206    /// Sent once, when fuzzing starts, to indicate the available coverage data.
207    ///
208    /// Trailing:
209    /// * std.debug.Coverage.String for each directories_len
210    /// * std.debug.Coverage.File for each files_len
211    /// * std.debug.Coverage.SourceLocation for each source_locations_len
212    /// * u8 for each string_bytes_len
213    pub const SourceIndexHeader = extern struct {
214        tag: ToClientTag = .fuzz_source_index,
215        _: [3]u8 = @splat(0),
216        directories_len: u32,
217        files_len: u32,
218        source_locations_len: u32,
219        string_bytes_len: u32,
220        /// When, according to the server, fuzzing started.
221        start_timestamp: i64 align(4),
222    };
223
224    /// WebSocket server->client.
225    ///
226    /// Sent whenever the set of covered source locations is updated.
227    ///
228    /// Trailing:
229    /// * one bit per source_locations_len, contained in u64 elements
230    pub const CoverageUpdateHeader = extern struct {
231        tag: ToClientTag = .fuzz_coverage_update,
232        _: [7]u8 = @splat(0),
233        n_runs: u64,
234        unique_runs: u64,
235
236        pub const trailing = .{
237            .pc_bits_usize,
238        };
239    };
240
241    /// WebSocket server->client.
242    ///
243    /// Sent whenever the set of entry points is updated.
244    ///
245    /// Trailing:
246    /// * one u32 index of source_locations per locsLen()
247    pub const EntryPointHeader = extern struct {
248        tag: ToClientTag = .fuzz_entry_points,
249        locs_len_raw: [3]u8,
250
251        pub fn locsLen(hdr: EntryPointHeader) u24 {
252            return @bitCast(hdr.locs_len_raw);
253        }
254        pub fn init(locs_len: u24) EntryPointHeader {
255            return .{ .locs_len_raw = @bitCast(locs_len) };
256        }
257    };
258
259    /// Sent by lib/fuzzer to test_runner to obtain information about the
260    /// active memory mapped input file and cumulative stats about previous
261    /// fuzzing runs.
262    pub const Coverage = extern struct {
263        id: u64,
264        runs: u64,
265        unique: u64,
266        seen: u64,
267    };
268};
269
270/// ABI bits specifically relating to the time report interface.
271pub const time_report = struct {
272    /// WebSocket server->client.
273    ///
274    /// Sent after a `Step` finishes, providing the time taken to execute the step.
275    pub const GenericResult = extern struct {
276        tag: ToClientTag = .time_report_generic_result,
277        step_idx: u32 align(1),
278        ns_total: u64 align(1),
279    };
280
281    /// WebSocket server->client.
282    ///
283    /// Sent after a `Step.Compile` finishes, providing the step's time report.
284    ///
285    /// Trailing:
286    /// * `llvm_pass_timings: [llvm_pass_timings_len]u8` (ASCII-encoded)
287    /// * for each `files_len`:
288    ///   * `name` (null-terminated UTF-8 string)
289    /// * for each `decls_len`:
290    ///   * `name` (null-terminated UTF-8 string)
291    ///   * `file: u32` (index of file this decl is in)
292    ///   * `sema_ns: u64` (nanoseconds spent semantically analyzing this decl)
293    ///   * `codegen_ns: u64` (nanoseconds spent semantically analyzing this decl)
294    ///   * `link_ns: u64` (nanoseconds spent semantically analyzing this decl)
295    pub const CompileResult = extern struct {
296        tag: ToClientTag = .time_report_compile_result,
297
298        step_idx: u32 align(1),
299
300        flags: Flags,
301        stats: Stats align(1),
302        ns_total: u64 align(1),
303
304        llvm_pass_timings_len: u32 align(1),
305        files_len: u32 align(1),
306        decls_len: u32 align(1),
307
308        pub const Flags = packed struct(u8) {
309            use_llvm: bool,
310            _: u7 = 0,
311        };
312
313        pub const Stats = extern struct {
314            n_reachable_files: u32,
315            n_imported_files: u32,
316            n_generic_instances: u32,
317            n_inline_calls: u32,
318
319            cpu_ns_parse: u64,
320            cpu_ns_astgen: u64,
321            cpu_ns_sema: u64,
322            cpu_ns_codegen: u64,
323            cpu_ns_link: u64,
324
325            real_ns_files: u64,
326            real_ns_decls: u64,
327            real_ns_llvm_emit: u64,
328            real_ns_link_flush: u64,
329
330            pub const init: Stats = .{
331                .n_reachable_files = 0,
332                .n_imported_files = 0,
333                .n_generic_instances = 0,
334                .n_inline_calls = 0,
335                .cpu_ns_parse = 0,
336                .cpu_ns_astgen = 0,
337                .cpu_ns_sema = 0,
338                .cpu_ns_codegen = 0,
339                .cpu_ns_link = 0,
340                .real_ns_files = 0,
341                .real_ns_decls = 0,
342                .real_ns_llvm_emit = 0,
343                .real_ns_link_flush = 0,
344            };
345        };
346    };
347
348    /// WebSocket server->client.
349    ///
350    /// Sent after a `Step.Run` for a Zig test executable finishes, providing the test's time report.
351    ///
352    /// Trailing:
353    /// * for each `tests_len`:
354    ///   * `test_ns: u64` (nanoseconds spent running this test)
355    /// * for each `tests_len`:
356    ///   * `name` (null-terminated UTF-8 string)
357    pub const RunTestResult = extern struct {
358        tag: ToClientTag = .time_report_run_test_result,
359        step_idx: u32 align(1),
360        tests_len: u32 align(1),
361    };
362};