authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-01 18:25:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-01 18:25:15-07:00
log5667435bc4eacb36b4c33470784f1662637341b4
tree2a135ee5158625e3772a4aae511fdcb82c605ade
parentc5231a8fb4463f7087c6090c562531065a1869f9
parent742956865c0c55b6650d7b89d923b63ea11cde4f

Merge remote-tracking branch 'origin/master' into wrangle-writer-buffering


119 files changed, 4794 insertions(+), 2622 deletions(-)

lib/build-web/fuzz.zig created+377
...@@ -0,0 +1,377 @@
1// Server timestamp.
2var start_fuzzing_timestamp: i64 = undefined;
3
4const js = struct {
5 extern "fuzz" fn requestSources() void;
6 extern "fuzz" fn ready() void;
7
8 extern "fuzz" fn updateStats(html_ptr: [*]const u8, html_len: usize) void;
9 extern "fuzz" fn updateEntryPoints(html_ptr: [*]const u8, html_len: usize) void;
10 extern "fuzz" fn updateSource(html_ptr: [*]const u8, html_len: usize) void;
11 extern "fuzz" fn updateCoverage(covered_ptr: [*]const SourceLocationIndex, covered_len: u32) void;
12};
13
14pub fn sourceIndexMessage(msg_bytes: []u8) error{OutOfMemory}!void {
15 Walk.files.clearRetainingCapacity();
16 Walk.decls.clearRetainingCapacity();
17 Walk.modules.clearRetainingCapacity();
18 recent_coverage_update.clearRetainingCapacity();
19 selected_source_location = null;
20
21 js.requestSources();
22
23 const Header = abi.fuzz.SourceIndexHeader;
24 const header: Header = @bitCast(msg_bytes[0..@sizeOf(Header)].*);
25
26 const directories_start = @sizeOf(Header);
27 const directories_end = directories_start + header.directories_len * @sizeOf(Coverage.String);
28 const files_start = directories_end;
29 const files_end = files_start + header.files_len * @sizeOf(Coverage.File);
30 const source_locations_start = files_end;
31 const source_locations_end = source_locations_start + header.source_locations_len * @sizeOf(Coverage.SourceLocation);
32 const string_bytes = msg_bytes[source_locations_end..][0..header.string_bytes_len];
33
34 const directories: []const Coverage.String = @alignCast(std.mem.bytesAsSlice(Coverage.String, msg_bytes[directories_start..directories_end]));
35 const files: []const Coverage.File = @alignCast(std.mem.bytesAsSlice(Coverage.File, msg_bytes[files_start..files_end]));
36 const source_locations: []const Coverage.SourceLocation = @alignCast(std.mem.bytesAsSlice(Coverage.SourceLocation, msg_bytes[source_locations_start..source_locations_end]));
37
38 start_fuzzing_timestamp = header.start_timestamp;
39 try updateCoverageSources(directories, files, source_locations, string_bytes);
40 js.ready();
41}
42
43var coverage = Coverage.init;
44/// Index of type `SourceLocationIndex`.
45var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .empty;
46/// Contains the most recent coverage update message, unmodified.
47var recent_coverage_update: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty;
48
49fn updateCoverageSources(
50 directories: []const Coverage.String,
51 files: []const Coverage.File,
52 source_locations: []const Coverage.SourceLocation,
53 string_bytes: []const u8,
54) !void {
55 coverage.directories.clearRetainingCapacity();
56 coverage.files.clearRetainingCapacity();
57 coverage.string_bytes.clearRetainingCapacity();
58 coverage_source_locations.clearRetainingCapacity();
59
60 try coverage_source_locations.appendSlice(gpa, source_locations);
61 try coverage.string_bytes.appendSlice(gpa, string_bytes);
62
63 try coverage.files.entries.resize(gpa, files.len);
64 @memcpy(coverage.files.entries.items(.key), files);
65 try coverage.files.reIndexContext(gpa, .{ .string_bytes = coverage.string_bytes.items });
66
67 try coverage.directories.entries.resize(gpa, directories.len);
68 @memcpy(coverage.directories.entries.items(.key), directories);
69 try coverage.directories.reIndexContext(gpa, .{ .string_bytes = coverage.string_bytes.items });
70}
71
72pub fn coverageUpdateMessage(msg_bytes: []u8) error{OutOfMemory}!void {
73 recent_coverage_update.clearRetainingCapacity();
74 recent_coverage_update.appendSlice(gpa, msg_bytes) catch @panic("OOM");
75 try updateStats();
76 try updateCoverage();
77}
78
79var entry_points: std.ArrayListUnmanaged(SourceLocationIndex) = .empty;
80
81pub fn entryPointsMessage(msg_bytes: []u8) error{OutOfMemory}!void {
82 const header: abi.fuzz.EntryPointHeader = @bitCast(msg_bytes[0..@sizeOf(abi.fuzz.EntryPointHeader)].*);
83 const slis: []align(1) const SourceLocationIndex = @ptrCast(msg_bytes[@sizeOf(abi.fuzz.EntryPointHeader)..]);
84 assert(slis.len == header.locsLen());
85 try entry_points.resize(gpa, slis.len);
86 @memcpy(entry_points.items, slis);
87 try updateEntryPoints();
88}
89
90/// Index into `coverage_source_locations`.
91const SourceLocationIndex = enum(u32) {
92 _,
93
94 fn haveCoverage(sli: SourceLocationIndex) bool {
95 return @intFromEnum(sli) < coverage_source_locations.items.len;
96 }
97
98 fn ptr(sli: SourceLocationIndex) *Coverage.SourceLocation {
99 return &coverage_source_locations.items[@intFromEnum(sli)];
100 }
101
102 fn sourceLocationLinkHtml(
103 sli: SourceLocationIndex,
104 out: *std.ArrayListUnmanaged(u8),
105 focused: bool,
106 ) Allocator.Error!void {
107 const sl = sli.ptr();
108 try out.writer(gpa).print("<code{s}>", .{
109 @as([]const u8, if (focused) " class=\"status-running\"" else ""),
110 });
111 try sli.appendPath(out);
112 try out.writer(gpa).print(":{d}:{d} </code><button class=\"linkish\" onclick=\"wasm_exports.fuzzSelectSli({d});\">View</button>", .{
113 sl.line,
114 sl.column,
115 @intFromEnum(sli),
116 });
117 }
118
119 fn appendPath(sli: SourceLocationIndex, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
120 const sl = sli.ptr();
121 const file = coverage.fileAt(sl.file);
122 const file_name = coverage.stringAt(file.basename);
123 const dir_name = coverage.stringAt(coverage.directories.keys()[file.directory_index]);
124 try html_render.appendEscaped(out, dir_name);
125 try out.appendSlice(gpa, "/");
126 try html_render.appendEscaped(out, file_name);
127 }
128
129 fn toWalkFile(sli: SourceLocationIndex) ?Walk.File.Index {
130 var buf: std.ArrayListUnmanaged(u8) = .empty;
131 defer buf.deinit(gpa);
132 sli.appendPath(&buf) catch @panic("OOM");
133 return @enumFromInt(Walk.files.getIndex(buf.items) orelse return null);
134 }
135
136 fn fileHtml(
137 sli: SourceLocationIndex,
138 out: *std.ArrayListUnmanaged(u8),
139 ) error{ OutOfMemory, SourceUnavailable }!void {
140 const walk_file_index = sli.toWalkFile() orelse return error.SourceUnavailable;
141 const root_node = walk_file_index.findRootDecl().get().ast_node;
142 var annotations: std.ArrayListUnmanaged(html_render.Annotation) = .empty;
143 defer annotations.deinit(gpa);
144 try computeSourceAnnotations(sli.ptr().file, walk_file_index, &annotations, coverage_source_locations.items);
145 html_render.fileSourceHtml(walk_file_index, out, root_node, .{
146 .source_location_annotations = annotations.items,
147 }) catch |err| {
148 fatal("unable to render source: {s}", .{@errorName(err)});
149 };
150 }
151};
152
153fn computeSourceAnnotations(
154 cov_file_index: Coverage.File.Index,
155 walk_file_index: Walk.File.Index,
156 annotations: *std.ArrayListUnmanaged(html_render.Annotation),
157 source_locations: []const Coverage.SourceLocation,
158) !void {
159 // Collect all the source locations from only this file into this array
160 // first, then sort by line, col, so that we can collect annotations with
161 // O(N) time complexity.
162 var locs: std.ArrayListUnmanaged(SourceLocationIndex) = .empty;
163 defer locs.deinit(gpa);
164
165 for (source_locations, 0..) |sl, sli_usize| {
166 if (sl.file != cov_file_index) continue;
167 const sli: SourceLocationIndex = @enumFromInt(sli_usize);
168 try locs.append(gpa, sli);
169 }
170
171 std.mem.sortUnstable(SourceLocationIndex, locs.items, {}, struct {
172 pub fn lessThan(context: void, lhs: SourceLocationIndex, rhs: SourceLocationIndex) bool {
173 _ = context;
174 const lhs_ptr = lhs.ptr();
175 const rhs_ptr = rhs.ptr();
176 if (lhs_ptr.line < rhs_ptr.line) return true;
177 if (lhs_ptr.line > rhs_ptr.line) return false;
178 return lhs_ptr.column < rhs_ptr.column;
179 }
180 }.lessThan);
181
182 const source = walk_file_index.get_ast().source;
183 var line: usize = 1;
184 var column: usize = 1;
185 var next_loc_index: usize = 0;
186 for (source, 0..) |byte, offset| {
187 if (byte == '\n') {
188 line += 1;
189 column = 1;
190 } else {
191 column += 1;
192 }
193 while (true) {
194 if (next_loc_index >= locs.items.len) return;
195 const next_sli = locs.items[next_loc_index];
196 const next_sl = next_sli.ptr();
197 if (next_sl.line > line or (next_sl.line == line and next_sl.column >= column)) break;
198 try annotations.append(gpa, .{
199 .file_byte_offset = offset,
200 .dom_id = @intFromEnum(next_sli),
201 });
202 next_loc_index += 1;
203 }
204 }
205}
206
207export fn fuzzUnpackSources(tar_ptr: [*]u8, tar_len: usize) void {
208 const tar_bytes = tar_ptr[0..tar_len];
209 log.debug("received {d} bytes of sources.tar", .{tar_bytes.len});
210
211 unpackSourcesInner(tar_bytes) catch |err| {
212 fatal("unable to unpack sources.tar: {s}", .{@errorName(err)});
213 };
214}
215
216fn unpackSourcesInner(tar_bytes: []u8) !void {
217 var tar_reader: std.Io.Reader = .fixed(tar_bytes);
218 var file_name_buffer: [1024]u8 = undefined;
219 var link_name_buffer: [1024]u8 = undefined;
220 var it: std.tar.Iterator = .init(&tar_reader, .{
221 .file_name_buffer = &file_name_buffer,
222 .link_name_buffer = &link_name_buffer,
223 });
224 while (try it.next()) |tar_file| {
225 switch (tar_file.kind) {
226 .file => {
227 if (tar_file.size == 0 and tar_file.name.len == 0) break;
228 if (std.mem.endsWith(u8, tar_file.name, ".zig")) {
229 log.debug("found file: '{s}'", .{tar_file.name});
230 const file_name = try gpa.dupe(u8, tar_file.name);
231 if (std.mem.indexOfScalar(u8, file_name, '/')) |pkg_name_end| {
232 const pkg_name = file_name[0..pkg_name_end];
233 const gop = try Walk.modules.getOrPut(gpa, pkg_name);
234 const file: Walk.File.Index = @enumFromInt(Walk.files.entries.len);
235 if (!gop.found_existing or
236 std.mem.eql(u8, file_name[pkg_name_end..], "/root.zig") or
237 std.mem.eql(u8, file_name[pkg_name_end + 1 .. file_name.len - ".zig".len], pkg_name))
238 {
239 gop.value_ptr.* = file;
240 }
241 const file_bytes = tar_reader.take(@intCast(tar_file.size)) catch unreachable;
242 it.unread_file_bytes = 0; // we have read the whole thing
243 assert(file == try Walk.add_file(file_name, file_bytes));
244 }
245 } else {
246 log.warn("skipping: '{s}' - the tar creation should have done that", .{tar_file.name});
247 }
248 },
249 else => continue,
250 }
251 }
252}
253
254fn updateStats() error{OutOfMemory}!void {
255 @setFloatMode(.optimized);
256
257 if (recent_coverage_update.items.len == 0) return;
258
259 const hdr: *abi.fuzz.CoverageUpdateHeader = @alignCast(@ptrCast(
260 recent_coverage_update.items[0..@sizeOf(abi.fuzz.CoverageUpdateHeader)],
261 ));
262
263 const covered_src_locs: usize = n: {
264 var n: usize = 0;
265 const covered_bits = recent_coverage_update.items[@sizeOf(abi.fuzz.CoverageUpdateHeader)..];
266 for (covered_bits) |byte| n += @popCount(byte);
267 break :n n;
268 };
269 const total_src_locs = coverage_source_locations.items.len;
270
271 const avg_speed: f64 = speed: {
272 const ns_elapsed: f64 = @floatFromInt(nsSince(start_fuzzing_timestamp));
273 const n_runs: f64 = @floatFromInt(hdr.n_runs);
274 break :speed n_runs / (ns_elapsed / std.time.ns_per_s);
275 };
276
277 const html = try std.fmt.allocPrint(gpa,
278 \\<span slot="stat-total-runs">{d}</span>
279 \\<span slot="stat-unique-runs">{d} ({d:.1}%)</span>
280 \\<span slot="stat-coverage">{d} / {d} ({d:.1}%)</span>
281 \\<span slot="stat-speed">{d:.0}</span>
282 , .{
283 hdr.n_runs,
284 hdr.unique_runs,
285 @as(f64, @floatFromInt(hdr.unique_runs)) / @as(f64, @floatFromInt(hdr.n_runs)),
286 covered_src_locs,
287 total_src_locs,
288 @as(f64, @floatFromInt(covered_src_locs)) / @as(f64, @floatFromInt(total_src_locs)),
289 avg_speed,
290 });
291 defer gpa.free(html);
292
293 js.updateStats(html.ptr, html.len);
294}
295
296fn updateEntryPoints() error{OutOfMemory}!void {
297 var html: std.ArrayListUnmanaged(u8) = .empty;
298 defer html.deinit(gpa);
299 for (entry_points.items) |sli| {
300 try html.appendSlice(gpa, "<li>");
301 try sli.sourceLocationLinkHtml(&html, selected_source_location == sli);
302 try html.appendSlice(gpa, "</li>\n");
303 }
304 js.updateEntryPoints(html.items.ptr, html.items.len);
305}
306
307fn updateCoverage() error{OutOfMemory}!void {
308 if (recent_coverage_update.items.len == 0) return;
309 const want_file = (selected_source_location orelse return).ptr().file;
310
311 var covered: std.ArrayListUnmanaged(SourceLocationIndex) = .empty;
312 defer covered.deinit(gpa);
313
314 // This code assumes 64-bit elements, which is incorrect if the executable
315 // being fuzzed is not a 64-bit CPU. It also assumes little-endian which
316 // can also be incorrect.
317 comptime assert(abi.fuzz.CoverageUpdateHeader.trailing[0] == .pc_bits_usize);
318 const n_bitset_elems = (coverage_source_locations.items.len + @bitSizeOf(u64) - 1) / @bitSizeOf(u64);
319 const covered_bits = std.mem.bytesAsSlice(
320 u64,
321 recent_coverage_update.items[@sizeOf(abi.fuzz.CoverageUpdateHeader)..][0 .. n_bitset_elems * @sizeOf(u64)],
322 );
323 var sli: SourceLocationIndex = @enumFromInt(0);
324 for (covered_bits) |elem| {
325 try covered.ensureUnusedCapacity(gpa, 64);
326 for (0..@bitSizeOf(u64)) |i| {
327 if ((elem & (@as(u64, 1) << @intCast(i))) != 0) {
328 if (sli.ptr().file == want_file) {
329 covered.appendAssumeCapacity(sli);
330 }
331 }
332 sli = @enumFromInt(@intFromEnum(sli) + 1);
333 }
334 }
335
336 js.updateCoverage(covered.items.ptr, covered.items.len);
337}
338
339fn updateSource() error{OutOfMemory}!void {
340 if (recent_coverage_update.items.len == 0) return;
341 const file_sli = selected_source_location.?;
342 var html: std.ArrayListUnmanaged(u8) = .empty;
343 defer html.deinit(gpa);
344 file_sli.fileHtml(&html) catch |err| switch (err) {
345 error.OutOfMemory => |e| return e,
346 error.SourceUnavailable => {},
347 };
348 js.updateSource(html.items.ptr, html.items.len);
349}
350
351var selected_source_location: ?SourceLocationIndex = null;
352
353/// This function is not used directly by `main.js`, but a reference to it is
354/// emitted by `SourceLocationIndex.sourceLocationLinkHtml`.
355export fn fuzzSelectSli(sli: SourceLocationIndex) void {
356 if (!sli.haveCoverage()) return;
357 selected_source_location = sli;
358 updateEntryPoints() catch @panic("out of memory"); // highlights the selected one green
359 updateSource() catch @panic("out of memory");
360 updateCoverage() catch @panic("out of memory");
361}
362
363const std = @import("std");
364const Allocator = std.mem.Allocator;
365const Coverage = std.debug.Coverage;
366const abi = std.Build.abi;
367const assert = std.debug.assert;
368const gpa = std.heap.wasm_allocator;
369
370const Walk = @import("Walk");
371const html_render = @import("html_render");
372
373const nsSince = @import("main.zig").nsSince;
374const Slice = @import("main.zig").Slice;
375const fatal = @import("main.zig").fatal;
376const log = std.log;
377const String = Slice(u8);
lib/build-web/index.html created+202
...@@ -0,0 +1,202 @@
1<!doctype html>
2
3<meta charset="utf-8">
4<title>Zig Build System</title>
5<link rel="stylesheet" href="style.css">
6<!-- Highly compressed 32x32 Zig logo -->
7<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABSklEQVRYw8WWXbLDIAiFP5xuURYpi+Q+VDvJTYxaY8pLJ52EA5zDj/AD8wRABCw8DeyJBDiAKMiDGaecNYCKYgCvh4EBjPgGh0UVqAB/MEU3D57efDRMiRhWddprCljRAECPCE0Uw4iz4Jn3tP2zFYAB6on4/8NBM1Es+9kl0aKgaMRnwHPpT5MIDb6YzLzp57wNIyIC7iCCdijeL3gv78jZe6cVENn/drRbXbxl4lXSmB3FtbY0iNrjIEwMm6u2VFFjWQCN0qtov6+wANxG/IV7eR8DHw6gzft4NuEXvA8HcDfv31SgyvsMeDUA90/WTd47bsCdv8PUrWzDyw02uIYv13ktgOVr+IqCouila7gWgNYuly/BfVSEdsP5Vdqyiz7pPC40C+p2e21bL5/dByGtAD6eZPuzeznwjoIN748BfyqwmVDyJHCxPwLSkjUkraEXAAAAAElFTkSuQmCC">
8
9<!-- Templates, to be cloned into shadow DOMs by JavaScript -->
10
11<template id="timeReportEntryTemplate">
12 <link rel="stylesheet" href="style.css">
13 <link rel="stylesheet" href="time_report.css">
14 <details>
15 <summary><slot name="step-name"></slot></summary>
16 <div id="genericReport">
17 <div class="stats">
18 Time: <slot name="stat-total-time"></slot><br>
19 </div>
20 </div>
21 <div id="compileReport">
22 <div class="stats">
23 Files Discovered: <slot name="stat-reachable-files"></slot><br>
24 Files Analyzed: <slot name="stat-imported-files"></slot><br>
25 Generic Instances Analyzed: <slot name="stat-generic-instances"></slot><br>
26 Inline Calls Analyzed: <slot name="stat-inline-calls"></slot><br>
27 Compilation Time: <slot name="stat-compilation-time"></slot><br>
28 </div>
29 <table class="time-stats">
30 <thead>
31 <tr>
32 <th scope="col">Pipeline Component</th>
33 <th scope="col" class="tooltip">CPU Time
34 <span class="tooltip-content">Sum across all threads of the time spent in this pipeline component</span>
35 </th>
36 <th scope="col" class="tooltip">Real Time
37 <span class="tooltip-content">Wall-clock time elapsed between the start and end of this compilation phase</span>
38 </th>
39 <th scope="col">Compilation Phase</th>
40 </tr>
41 </thead>
42 <tbody>
43 <tr>
44 <th scope="row" class="tooltip">Parsing
45 <span class="tooltip-content"><code>tokenize</code> converts a file of Zig source code into a sequence of tokens, which are then processed by <code>Parse</code> into an Abstract Syntax Tree (AST).</span>
46 </th>
47 <td><slot name="cpu-time-parse"></slot></td>
48 <td rowspan="2"><slot name="real-time-files"></slot></td>
49 <th scope="row" rowspan="2" class="tooltip">File Lower
50 <span class="tooltip-content">Tokenization, parsing, and lowering of Zig source files to a high-level IR.<br><br>Starting from module roots, every file theoretically accessible through a chain of <code>@import</code> calls is processed. Individual source files are processed serially, but different files are processed in parallel by a thread pool.<br><br>The results of this phase of compilation are cached on disk per source file, meaning the time spent here is typically only relevant to "clean" builds.</span>
51 </th>
52 </tr>
53 <tr>
54 <th scope="row" class="tooltip">AST Lowering
55 <span class="tooltip-content"><code>AstGen</code> converts a file's AST into a high-level SSA IR named Zig Intermediate Representation (ZIR). The resulting ZIR code is cached on disk to avoid, for instance, re-lowering all source files in the Zig standard library each time the compiler is invoked.</span>
56 </th>
57 <td><slot name="cpu-time-astgen"></slot></td>
58 </tr>
59 <tr>
60 <th scope="row" class="tooltip">Semantic Analysis
61 <span class="tooltip-content"><code>Sema</code> interprets ZIR to perform type checking, compile-time code execution, and type resolution, collectively termed "semantic analysis". When a runtime function body is analyzed, it emits Analyzed Intermediate Representation (AIR) code to be sent to the next pipeline component. Semantic analysis is currently entirely single-threaded.</span>
62 </th>
63 <td><slot name="cpu-time-sema"></slot></td>
64 <td rowspan="3"><slot name="real-time-decls"></slot></td>
65 <th scope="row" rowspan="3" class="tooltip">Declaration Lower
66 <span class="tooltip-content">Semantic analysis, code generation, and linking, at the granularity of individual declarations (as opposed to whole source files).<br><br>These components are run in parallel with one another. Semantic analysis is almost always the bottleneck, as it is complex and currently can only run single-threaded.<br><br>This phase completes when a work queue empties, but semantic analysis may add work by one declaration referencing another.<br><br>This is the main phase of compilation, typically taking significantly longer than File Lower (even in a clean build).</span>
67 </th>
68 </tr>
69 <tr>
70 <th scope="row" class="tooltip">Code Generation
71 <span class="tooltip-content"><code>CodeGen</code> converts AIR from <code>Sema</code> into machine instructions in the form of Machine Intermediate Representation (MIR). This work is usually highly parallel, since in most cases, arbitrarily many functions can be run through <code>CodeGen</code> simultaneously.</span>
72 </th>
73 <td><slot name="cpu-time-codegen"></slot></td>
74 </tr>
75 <tr>
76 <th scope="row" class="tooltip">Linking
77 <span class="tooltip-content"><code>link</code> converts MIR from <code>CodeGen</code>, as well as global constants and variables from <code>Sema</code>, and places them in the output binary. MIR is converted to a finished sequence of real instruction bytes.<br><br>When using the LLVM backend, most of this work is instead deferred to the "LLVM Emit" phase.</span>
78 </th>
79 <td><slot name="cpu-time-link"></slot></td>
80 </tr>
81 <tr class="llvm-only">
82 <th class="empty-cell"></th>
83 <td class="empty-cell"></td>
84 <td><slot name="real-time-llvm-emit"></slot></td>
85 <th scope="row" class="tooltip">LLVM Emit
86 <span class="tooltip-content"><b>Only applicable when using the LLVM backend.</b><br><br>Conversion of generated LLVM bitcode to an object file, including any optimization passes.<br><br>When using LLVM, this phase of compilation is typically the slowest by a significant margin. Unfortunately, the Zig compiler implementation has essentially no control over it.</span>
87 </th>
88 </tr>
89 <tr>
90 <th class="empty-cell"></th>
91 <td class="empty-cell"></td>
92 <td><slot name="real-time-link-flush"></slot></td>
93 <th scope="row" class="tooltip">Linker Flush
94 <span class="tooltip-content">Finalizing the emitted binary, and ensuring it is fully written to disk.<br><br>When using LLD, this phase represents the entire linker invocation. Otherwise, the amount of work performed here is dependent on details of Zig's linker implementation for the particular output format, but typically aims to be fairly minimal.</span>
95 </th>
96 </tr>
97 </tbody>
98 </table>
99 <details class="section">
100 <summary>Files</summary>
101 <table class="time-stats">
102 <thead>
103 <tr>
104 <th scope="col">File</th>
105 <th scope="col">Semantic Analysis</th>
106 <th scope="col">Code Generation</th>
107 <th scope="col">Linking</th>
108 </tr>
109 </thead>
110 <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility
111 reasons, so we unfortunately must template on the `id` here. -->
112 <tbody id="fileTableBody"></tbody>
113 </table>
114 </details>
115 <details class="section">
116 <summary>Declarations</summary>
117 <table class="time-stats">
118 <thead>
119 <tr>
120 <th scope="col">File</th>
121 <th scope="col">Declaration</th>
122 <th scope="col" class="tooltip">Analysis Count
123 <span class="tooltip-content">The number of times the compiler analyzed some part of this declaration. If this is a function, <code>inline</code> and <code>comptime</code> calls to it are <i>not</i> included here. Typically, this value is approximately equal to the number of instances of a generic declaration.</span>
124 </th>
125 <th scope="col">Semantic Analysis</th>
126 <th scope="col">Code Generation</th>
127 <th scope="col">Linking</th>
128 </tr>
129 </thead>
130 <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility
131 reasons, so we unfortunately must template on the `id` here. -->
132 <tbody id="declTableBody"></tbody>
133 </table>
134 </details>
135 <details class="section llvm-only">
136 <summary>LLVM Pass Timings</summary>
137 <div><slot name="llvm-pass-timings"></slot></div>
138 </details>
139 </div>
140 </details>
141</template>
142
143<template id="fuzzEntryTemplate">
144 <link rel="stylesheet" href="style.css">
145 <ul>
146 <li>Total Runs: <slot name="stat-total-runs"></slot></li>
147 <li>Unique Runs: <slot name="stat-unique-runs"></slot></li>
148 <li>Speed: <slot name="stat-speed"></slot> runs/sec</li>
149 <li>Coverage: <slot name="stat-coverage"></slot></li>
150 </ul>
151 <!-- I have observed issues in Firefox clicking frequently-updating slotted links, so the entry
152 point list is handled separately since it rarely changes. -->
153 <ul id="entryPointList" class="no-marker"></ul>
154 <div id="source" class="hidden">
155 <h2>Source Code</h2>
156 <pre><code id="sourceText"></code></pre>
157 </div>
158</template>
159
160<!-- The actual body: fairly minimal, content populated by JavaScript -->
161
162<p id="connectionStatus">Loading JavaScript...</p>
163<p class="hidden" id="firefoxWebSocketBullshitExplainer">
164If you are using Firefox and <code>zig build --listen</code> is definitely running, you may be experiencing an unreasonably aggressive exponential
165backoff for WebSocket connection attempts, which is enabled by default and can block connection attempts for up to a minute. To disable this limit,
166open <code>about:config</code> and set the <code>network.websocket.delay-failed-reconnects</code> option to <code>false</code>.
167</p>
168<main class="hidden">
169 <h1>Zig Build System</h1>
170
171 <p><span id="summaryStatus"></span> | <span id="summaryStepCount"></span> steps</p>
172 <button class="big-btn" id="buttonRebuild" disabled>Rebuild</button>
173
174 <ul class="no-marker" id="stepList"></ul>
175
176 <hr>
177
178 <div id="timeReport" class="hidden">
179 <h1>Time Report</h1>
180 <div id="timeReportList"></div>
181 <hr>
182 </div>
183
184 <div id="fuzz" class="hidden">
185 <h1>Fuzzer</h1>
186 <p id="fuzzStatus"></p>
187 <div id="fuzzEntries"></div>
188 <hr>
189 </div>
190
191 <h1>Help</h1>
192 <p>This is the Zig Build System web interface. It allows live interaction with the build system.</p>
193 <p>The following <code>zig build</code> flags can expose extra features of this interface:</p>
194 <ul>
195 <li><code>--time-report</code>: collect and show statistics about the time taken to evaluate a build graph</li>
196 <li><code>--fuzz</code>: enable the fuzzer for any Zig test binaries in the build graph (experimental)</li>
197 </ul>
198</main>
199
200<!-- JavaScript at the very end -->
201
202<script src="main.js"></script>
lib/build-web/main.js created+346
...@@ -0,0 +1,346 @@
1const domConnectionStatus = document.getElementById("connectionStatus");
2const domFirefoxWebSocketBullshitExplainer = document.getElementById("firefoxWebSocketBullshitExplainer");
3
4const domMain = document.getElementsByTagName("main")[0];
5const domSummary = {
6 stepCount: document.getElementById("summaryStepCount"),
7 status: document.getElementById("summaryStatus"),
8};
9const domButtonRebuild = document.getElementById("buttonRebuild");
10const domStepList = document.getElementById("stepList");
11let domSteps = [];
12
13let wasm_promise = fetch("main.wasm");
14let wasm_exports = null;
15
16const text_decoder = new TextDecoder();
17const text_encoder = new TextEncoder();
18
19domButtonRebuild.addEventListener("click", () => wasm_exports.rebuild());
20
21setConnectionStatus("Loading WebAssembly...", false);
22WebAssembly.instantiateStreaming(wasm_promise, {
23 core: {
24 log: function(ptr, len) {
25 const msg = decodeString(ptr, len);
26 console.log(msg);
27 },
28 panic: function (ptr, len) {
29 const msg = decodeString(ptr, len);
30 throw new Error("panic: " + msg);
31 },
32 timestamp: function () {
33 return BigInt(new Date());
34 },
35 hello: hello,
36 updateBuildStatus: updateBuildStatus,
37 updateStepStatus: updateStepStatus,
38 sendWsMessage: (ptr, len) => ws.send(new Uint8Array(wasm_exports.memory.buffer, ptr, len)),
39 },
40 fuzz: {
41 requestSources: fuzzRequestSources,
42 ready: fuzzReady,
43 updateStats: fuzzUpdateStats,
44 updateEntryPoints: fuzzUpdateEntryPoints,
45 updateSource: fuzzUpdateSource,
46 updateCoverage: fuzzUpdateCoverage,
47 },
48 time_report: {
49 updateCompile: timeReportUpdateCompile,
50 updateGeneric: timeReportUpdateGeneric,
51 },
52}).then(function(obj) {
53 setConnectionStatus("Connecting to WebSocket...", true);
54 connectWebSocket();
55
56 wasm_exports = obj.instance.exports;
57 window.wasm = obj; // for debugging
58});
59
60function connectWebSocket() {
61 const host = document.location.host;
62 const pathname = document.location.pathname;
63 const isHttps = document.location.protocol === 'https:';
64 const match = host.match(/^(.+):(\d+)$/);
65 const defaultPort = isHttps ? 443 : 80;
66 const port = match ? parseInt(match[2], 10) : defaultPort;
67 const hostName = match ? match[1] : host;
68 const wsProto = isHttps ? "wss:" : "ws:";
69 const wsUrl = wsProto + '//' + hostName + ':' + port + pathname;
70 ws = new WebSocket(wsUrl);
71 ws.binaryType = "arraybuffer";
72 ws.addEventListener('message', onWebSocketMessage, false);
73 ws.addEventListener('error', onWebSocketClose, false);
74 ws.addEventListener('close', onWebSocketClose, false);
75 ws.addEventListener('open', onWebSocketOpen, false);
76}
77function onWebSocketOpen() {
78 setConnectionStatus("Waiting for data...", false);
79}
80function onWebSocketMessage(ev) {
81 const jsArray = new Uint8Array(ev.data);
82 const ptr = wasm_exports.message_begin(jsArray.length);
83 const wasmArray = new Uint8Array(wasm_exports.memory.buffer, ptr, jsArray.length);
84 wasmArray.set(jsArray);
85 wasm_exports.message_end();
86}
87function onWebSocketClose() {
88 setConnectionStatus("WebSocket connection closed. Re-connecting...", true);
89 ws.removeEventListener('message', onWebSocketMessage, false);
90 ws.removeEventListener('error', onWebSocketClose, false);
91 ws.removeEventListener('close', onWebSocketClose, false);
92 ws.removeEventListener('open', onWebSocketOpen, false);
93 ws = null;
94 setTimeout(connectWebSocket, 1000);
95}
96
97function setConnectionStatus(msg, is_websocket_connect) {
98 domConnectionStatus.textContent = msg;
99 if (msg.length > 0) {
100 domConnectionStatus.classList.remove("hidden");
101 domMain.classList.add("hidden");
102 } else {
103 domConnectionStatus.classList.add("hidden");
104 domMain.classList.remove("hidden");
105 }
106 if (is_websocket_connect) {
107 domFirefoxWebSocketBullshitExplainer.classList.remove("hidden");
108 } else {
109 domFirefoxWebSocketBullshitExplainer.classList.add("hidden");
110 }
111}
112
113function hello(
114 steps_len,
115 build_status,
116 time_report,
117) {
118 domSummary.stepCount.textContent = steps_len;
119 updateBuildStatus(build_status);
120 setConnectionStatus("", false);
121
122 {
123 let entries = [];
124 for (let i = 0; i < steps_len; i += 1) {
125 const step_name = unwrapString(wasm_exports.stepName(i));
126 const code = document.createElement("code");
127 code.textContent = step_name;
128 const li = document.createElement("li");
129 li.appendChild(code);
130 entries.push(li);
131 }
132 domStepList.replaceChildren(...entries);
133 for (let i = 0; i < steps_len; i += 1) {
134 updateStepStatus(i);
135 }
136 }
137
138 if (time_report) timeReportReset(steps_len);
139 fuzzReset();
140}
141
142function updateBuildStatus(s) {
143 let text;
144 let active = false;
145 let reset_time_reports = false;
146 if (s == 0) {
147 text = "Idle";
148 } else if (s == 1) {
149 text = "Watching for changes...";
150 } else if (s == 2) {
151 text = "Running...";
152 active = true;
153 reset_time_reports = true;
154 } else if (s == 3) {
155 text = "Starting fuzzer...";
156 active = true;
157 } else {
158 console.log(`bad build status: ${s}`);
159 }
160 domSummary.status.textContent = text;
161 if (active) {
162 domSummary.status.classList.add("status-running");
163 domSummary.status.classList.remove("status-idle");
164 domButtonRebuild.disabled = true;
165 } else {
166 domSummary.status.classList.remove("status-running");
167 domSummary.status.classList.add("status-idle");
168 domButtonRebuild.disabled = false;
169 }
170 if (reset_time_reports) {
171 // Grey out and collapse all the time reports
172 for (const time_report_host of domTimeReportList.children) {
173 const details = time_report_host.shadowRoot.querySelector(":host > details");
174 details.classList.add("pending");
175 details.open = false;
176 }
177 }
178}
179function updateStepStatus(step_idx) {
180 const li = domStepList.children[step_idx];
181 const step_status = wasm_exports.stepStatus(step_idx);
182 li.classList.remove("step-wip", "step-success", "step-failure");
183 if (step_status == 0) {
184 // pending
185 } else if (step_status == 1) {
186 li.classList.add("step-wip");
187 } else if (step_status == 2) {
188 li.classList.add("step-success");
189 } else if (step_status == 3) {
190 li.classList.add("step-failure");
191 } else {
192 console.log(`bad step status: ${step_status}`);
193 }
194}
195
196function decodeString(ptr, len) {
197 if (len === 0) return "";
198 return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len));
199}
200function getU32Array(ptr, len) {
201 if (len === 0) return new Uint32Array();
202 return new Uint32Array(wasm_exports.memory.buffer, ptr, len);
203}
204function unwrapString(bigint) {
205 const ptr = Number(bigint & 0xffffffffn);
206 const len = Number(bigint >> 32n);
207 return decodeString(ptr, len);
208}
209
210const time_report_entry_template = document.getElementById("timeReportEntryTemplate").content;
211const domTimeReport = document.getElementById("timeReport");
212const domTimeReportList = document.getElementById("timeReportList");
213function timeReportReset(steps_len) {
214 let entries = [];
215 for (let i = 0; i < steps_len; i += 1) {
216 const step_name = unwrapString(wasm_exports.stepName(i));
217 const host = document.createElement("div");
218 const shadow = host.attachShadow({ mode: "open" });
219 shadow.appendChild(time_report_entry_template.cloneNode(true));
220 shadow.querySelector(":host > details").classList.add("pending");
221 const slotted_name = document.createElement("code");
222 slotted_name.setAttribute("slot", "step-name");
223 slotted_name.textContent = step_name;
224 host.appendChild(slotted_name);
225 entries.push(host);
226 }
227 domTimeReportList.replaceChildren(...entries);
228 domTimeReport.classList.remove("hidden");
229}
230function timeReportUpdateCompile(
231 step_idx,
232 inner_html_ptr,
233 inner_html_len,
234 file_table_html_ptr,
235 file_table_html_len,
236 decl_table_html_ptr,
237 decl_table_html_len,
238 use_llvm,
239) {
240 const inner_html = decodeString(inner_html_ptr, inner_html_len);
241 const file_table_html = decodeString(file_table_html_ptr, file_table_html_len);
242 const decl_table_html = decodeString(decl_table_html_ptr, decl_table_html_len);
243
244 const host = domTimeReportList.children.item(step_idx);
245 const shadow = host.shadowRoot;
246
247 shadow.querySelector(":host > details").classList.remove("pending", "no-llvm");
248
249 shadow.getElementById("genericReport").classList.add("hidden");
250 shadow.getElementById("compileReport").classList.remove("hidden");
251
252 if (!use_llvm) shadow.querySelector(":host > details").classList.add("no-llvm");
253 host.innerHTML = inner_html;
254 shadow.getElementById("fileTableBody").innerHTML = file_table_html;
255 shadow.getElementById("declTableBody").innerHTML = decl_table_html;
256}
257function timeReportUpdateGeneric(
258 step_idx,
259 inner_html_ptr,
260 inner_html_len,
261) {
262 const inner_html = decodeString(inner_html_ptr, inner_html_len);
263 const host = domTimeReportList.children.item(step_idx);
264 const shadow = host.shadowRoot;
265 shadow.querySelector(":host > details").classList.remove("pending", "no-llvm");
266 shadow.getElementById("genericReport").classList.remove("hidden");
267 shadow.getElementById("compileReport").classList.add("hidden");
268 host.innerHTML = inner_html;
269}
270
271const fuzz_entry_template = document.getElementById("fuzzEntryTemplate").content;
272const domFuzz = document.getElementById("fuzz");
273const domFuzzStatus = document.getElementById("fuzzStatus");
274const domFuzzEntries = document.getElementById("fuzzEntries");
275let domFuzzInstance = null;
276function fuzzRequestSources() {
277 domFuzzStatus.classList.remove("hidden");
278 domFuzzStatus.textContent = "Loading sources tarball...";
279 fetch("sources.tar").then(function(response) {
280 if (!response.ok) throw new Error("unable to download sources");
281 domFuzzStatus.textContent = "Parsing fuzz test sources...";
282 return response.arrayBuffer();
283 }).then(function(buffer) {
284 if (buffer.length === 0) throw new Error("sources.tar was empty");
285 const js_array = new Uint8Array(buffer);
286 const ptr = wasm_exports.alloc(js_array.length);
287 const wasm_array = new Uint8Array(wasm_exports.memory.buffer, ptr, js_array.length);
288 wasm_array.set(js_array);
289 wasm_exports.fuzzUnpackSources(ptr, js_array.length);
290 domFuzzStatus.textContent = "";
291 domFuzzStatus.classList.add("hidden");
292 });
293}
294function fuzzReady() {
295 domFuzz.classList.remove("hidden");
296
297 // TODO: multiple fuzzer instances
298 if (domFuzzInstance !== null) return;
299
300 const host = document.createElement("div");
301 const shadow = host.attachShadow({ mode: "open" });
302 shadow.appendChild(fuzz_entry_template.cloneNode(true));
303
304 domFuzzInstance = host;
305 domFuzzEntries.appendChild(host);
306}
307function fuzzReset() {
308 domFuzz.classList.add("hidden");
309 domFuzzEntries.replaceChildren();
310 domFuzzInstance = null;
311}
312function fuzzUpdateStats(stats_html_ptr, stats_html_len) {
313 if (domFuzzInstance === null) throw new Error("fuzzUpdateStats called when fuzzer inactive");
314 const stats_html = decodeString(stats_html_ptr, stats_html_len);
315 const host = domFuzzInstance;
316 host.innerHTML = stats_html;
317}
318function fuzzUpdateEntryPoints(entry_points_html_ptr, entry_points_html_len) {
319 if (domFuzzInstance === null) throw new Error("fuzzUpdateEntryPoints called when fuzzer inactive");
320 const entry_points_html = decodeString(entry_points_html_ptr, entry_points_html_len);
321 const domEntryPointList = domFuzzInstance.shadowRoot.getElementById("entryPointList");
322 domEntryPointList.innerHTML = entry_points_html;
323}
324function fuzzUpdateSource(source_html_ptr, source_html_len) {
325 if (domFuzzInstance === null) throw new Error("fuzzUpdateSource called when fuzzer inactive");
326 const source_html = decodeString(source_html_ptr, source_html_len);
327 const domSourceText = domFuzzInstance.shadowRoot.getElementById("sourceText");
328 domSourceText.innerHTML = source_html;
329 domFuzzInstance.shadowRoot.getElementById("source").classList.remove("hidden");
330}
331function fuzzUpdateCoverage(covered_ptr, covered_len) {
332 if (domFuzzInstance === null) throw new Error("fuzzUpdateCoverage called when fuzzer inactive");
333 const shadow = domFuzzInstance.shadowRoot;
334 const domSourceText = shadow.getElementById("sourceText");
335 const covered = getU32Array(covered_ptr, covered_len);
336 for (let i = 0; i < domSourceText.children.length; i += 1) {
337 const childDom = domSourceText.children[i];
338 if (childDom.id != null && childDom.id[0] == "l") {
339 childDom.classList.add("l");
340 childDom.classList.remove("c");
341 }
342 }
343 for (const sli of covered) {
344 shadow.getElementById(`l${sli}`).classList.add("c");
345 }
346}
lib/build-web/main.zig created+213
...@@ -0,0 +1,213 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const abi = std.Build.abi;
4const gpa = std.heap.wasm_allocator;
5const log = std.log;
6const Allocator = std.mem.Allocator;
7
8const fuzz = @import("fuzz.zig");
9const time_report = @import("time_report.zig");
10
11/// Nanoseconds.
12var server_base_timestamp: i64 = 0;
13/// Milliseconds.
14var client_base_timestamp: i64 = 0;
15
16pub var step_list: []Step = &.{};
17/// Not accessed after initialization, but must be freed alongside `step_list`.
18pub var step_list_data: []u8 = &.{};
19
20const Step = struct {
21 name: []const u8,
22 status: abi.StepUpdate.Status,
23};
24
25const js = struct {
26 extern "core" fn log(ptr: [*]const u8, len: usize) void;
27 extern "core" fn panic(ptr: [*]const u8, len: usize) noreturn;
28 extern "core" fn timestamp() i64;
29 extern "core" fn hello(
30 steps_len: u32,
31 status: abi.BuildStatus,
32 time_report: bool,
33 ) void;
34 extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;
35 extern "core" fn updateStepStatus(step_idx: u32) void;
36 extern "core" fn sendWsMessage(ptr: [*]const u8, len: usize) void;
37};
38
39pub const std_options: std.Options = .{
40 .logFn = logFn,
41};
42
43pub fn panic(msg: []const u8, st: ?*std.builtin.StackTrace, addr: ?usize) noreturn {
44 _ = st;
45 _ = addr;
46 log.err("panic: {s}", .{msg});
47 @trap();
48}
49
50fn logFn(
51 comptime message_level: log.Level,
52 comptime scope: @TypeOf(.enum_literal),
53 comptime format: []const u8,
54 args: anytype,
55) void {
56 const level_txt = comptime message_level.asText();
57 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
58 var buf: [500]u8 = undefined;
59 const line = std.fmt.bufPrint(&buf, level_txt ++ prefix2 ++ format, args) catch l: {
60 buf[buf.len - 3 ..][0..3].* = "...".*;
61 break :l &buf;
62 };
63 js.log(line.ptr, line.len);
64}
65
66export fn alloc(n: usize) [*]u8 {
67 const slice = gpa.alloc(u8, n) catch @panic("OOM");
68 return slice.ptr;
69}
70
71var message_buffer: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty;
72
73/// Resizes the message buffer to be the correct length; returns the pointer to
74/// the query string.
75export fn message_begin(len: usize) [*]u8 {
76 message_buffer.resize(gpa, len) catch @panic("OOM");
77 return message_buffer.items.ptr;
78}
79
80export fn message_end() void {
81 const msg_bytes = message_buffer.items;
82
83 const tag: abi.ToClientTag = @enumFromInt(msg_bytes[0]);
84 switch (tag) {
85 _ => @panic("malformed message"),
86
87 .hello => return helloMessage(msg_bytes) catch @panic("OOM"),
88 .status_update => return statusUpdateMessage(msg_bytes) catch @panic("OOM"),
89 .step_update => return stepUpdateMessage(msg_bytes) catch @panic("OOM"),
90
91 .fuzz_source_index => return fuzz.sourceIndexMessage(msg_bytes) catch @panic("OOM"),
92 .fuzz_coverage_update => return fuzz.coverageUpdateMessage(msg_bytes) catch @panic("OOM"),
93 .fuzz_entry_points => return fuzz.entryPointsMessage(msg_bytes) catch @panic("OOM"),
94
95 .time_report_generic_result => return time_report.genericResultMessage(msg_bytes) catch @panic("OOM"),
96 .time_report_compile_result => return time_report.compileResultMessage(msg_bytes) catch @panic("OOM"),
97 }
98}
99
100const String = Slice(u8);
101
102pub fn Slice(T: type) type {
103 return packed struct(u64) {
104 ptr: u32,
105 len: u32,
106
107 pub fn init(s: []const T) @This() {
108 return .{
109 .ptr = @intFromPtr(s.ptr),
110 .len = s.len,
111 };
112 }
113 };
114}
115
116pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
117 var buf: [500]u8 = undefined;
118 const line = std.fmt.bufPrint(&buf, format, args) catch l: {
119 buf[buf.len - 3 ..][0..3].* = "...".*;
120 break :l &buf;
121 };
122 js.panic(line.ptr, line.len);
123}
124
125fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void {
126 if (msg_bytes.len < @sizeOf(abi.Hello)) @panic("malformed Hello message");
127 const hdr: *const abi.Hello = @ptrCast(msg_bytes[0..@sizeOf(abi.Hello)]);
128 const trailing = msg_bytes[@sizeOf(abi.Hello)..];
129
130 client_base_timestamp = js.timestamp();
131 server_base_timestamp = hdr.timestamp;
132
133 const steps = try gpa.alloc(Step, hdr.steps_len);
134 errdefer gpa.free(steps);
135
136 const step_name_lens: []align(1) const u32 = @ptrCast(trailing[0 .. steps.len * 4]);
137
138 const step_name_data_len: usize = len: {
139 var sum: usize = 0;
140 for (step_name_lens) |n| sum += n;
141 break :len sum;
142 };
143 const step_name_data: []const u8 = trailing[steps.len * 4 ..][0..step_name_data_len];
144 const step_status_bits: []const u8 = trailing[steps.len * 4 + step_name_data_len ..];
145
146 const duped_step_name_data = try gpa.dupe(u8, step_name_data);
147 errdefer gpa.free(duped_step_name_data);
148
149 var name_off: usize = 0;
150 for (steps, step_name_lens, 0..) |*step_out, name_len, step_idx| {
151 step_out.* = .{
152 .name = duped_step_name_data[name_off..][0..name_len],
153 .status = @enumFromInt(@as(u2, @truncate(step_status_bits[step_idx / 4] >> @intCast((step_idx % 4) * 2)))),
154 };
155 name_off += name_len;
156 }
157
158 gpa.free(step_list);
159 gpa.free(step_list_data);
160 step_list = steps;
161 step_list_data = duped_step_name_data;
162
163 js.hello(step_list.len, hdr.status, hdr.flags.time_report);
164}
165fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
166 if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");
167 const msg: *const abi.StatusUpdate = @ptrCast(msg_bytes[0..@sizeOf(abi.StatusUpdate)]);
168 js.updateBuildStatus(msg.new);
169}
170fn stepUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
171 if (msg_bytes.len < @sizeOf(abi.StepUpdate)) @panic("malformed StepUpdate message");
172 const msg: *const abi.StepUpdate = @ptrCast(msg_bytes[0..@sizeOf(abi.StepUpdate)]);
173 if (msg.step_idx >= step_list.len) @panic("malformed StepUpdate message");
174 step_list[msg.step_idx].status = msg.bits.status;
175 js.updateStepStatus(msg.step_idx);
176}
177
178export fn stepName(idx: usize) String {
179 return .init(step_list[idx].name);
180}
181export fn stepStatus(idx: usize) u8 {
182 return @intFromEnum(step_list[idx].status);
183}
184
185export fn rebuild() void {
186 const msg: abi.Rebuild = .{};
187 const raw: []const u8 = @ptrCast(&msg);
188 js.sendWsMessage(raw.ptr, raw.len);
189}
190
191/// Nanoseconds passed since a server timestamp.
192pub fn nsSince(server_timestamp: i64) i64 {
193 const ms_passed = js.timestamp() - client_base_timestamp;
194 const ns_passed = server_base_timestamp - server_timestamp;
195 return ns_passed + ms_passed * std.time.ns_per_ms;
196}
197
198pub fn fmtEscapeHtml(unescaped: []const u8) HtmlEscaper {
199 return .{ .unescaped = unescaped };
200}
201const HtmlEscaper = struct {
202 unescaped: []const u8,
203 pub fn format(he: HtmlEscaper, w: *std.Io.Writer) !void {
204 for (he.unescaped) |c| switch (c) {
205 '&' => try w.writeAll("&amp;"),
206 '<' => try w.writeAll("&lt;"),
207 '>' => try w.writeAll("&gt;"),
208 '"' => try w.writeAll("&quot;"),
209 '\'' => try w.writeAll("&#39;"),
210 else => try w.writeByte(c),
211 };
212 }
213};
lib/build-web/style.css created+240
...@@ -0,0 +1,240 @@
1body {
2 font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
3 color: #000000;
4 padding: 1em 10%;
5}
6ul.no-marker {
7 list-style-type: none;
8 padding-left: 0;
9}
10hr {
11 margin: 2em 0;
12}
13.hidden {
14 display: none;
15}
16.empty-cell {
17 background: #ccc;
18}
19table.time-stats > tbody > tr > th {
20 text-align: left;
21}
22table.time-stats > tbody > tr > td {
23 text-align: right;
24}
25details > summary {
26 cursor: pointer;
27 font-size: 1.5em;
28}
29.tooltip {
30 text-decoration: underline;
31 cursor: help;
32}
33.tooltip-content {
34 border-radius: 6px;
35 display: none;
36 position: absolute;
37 background: #fff;
38 border: 1px solid black;
39 max-width: 500px;
40 padding: 1em;
41 text-align: left;
42 font-weight: normal;
43 pointer-events: none;
44}
45.tooltip:hover > .tooltip-content {
46 display: block;
47}
48table {
49 margin: 1.0em auto 1.5em 0;
50 border-collapse: collapse;
51}
52th, td {
53 padding: 0.5em 1em 0.5em 1em;
54 border: 1px solid;
55 border-color: black;
56}
57a, button {
58 color: #2A6286;
59}
60button {
61 background: #eee;
62 cursor: pointer;
63 border: none;
64 border-radius: 3px;
65 padding: 0.2em 0.5em;
66}
67button.big-btn {
68 font-size: 1.3em;
69}
70button.linkish {
71 background: none;
72 text-decoration: underline;
73 padding: 0;
74}
75button:disabled {
76 color: #888;
77 cursor: not-allowed;
78}
79pre {
80 font-family: "Source Code Pro", monospace;
81 font-size: 1em;
82 background-color: #F5F5F5;
83 padding: 1em;
84 margin: 0;
85 overflow-x: auto;
86}
87:not(pre) > code {
88 white-space: break-spaces;
89}
90code {
91 font-family: "Source Code Pro", monospace;
92 font-size: 0.9em;
93}
94code a {
95 color: #000000;
96}
97kbd {
98 color: #000;
99 background-color: #fafbfc;
100 border-color: #d1d5da;
101 border-bottom-color: #c6cbd1;
102 box-shadow-color: #c6cbd1;
103 display: inline-block;
104 padding: 0.3em 0.2em;
105 font: 1.2em monospace;
106 line-height: 0.8em;
107 vertical-align: middle;
108 border: solid 1px;
109 border-radius: 3px;
110 box-shadow: inset 0 -1px 0;
111 cursor: default;
112}
113.status-running { color: #181; }
114.status-idle { color: #444; }
115.step-success { color: #181; }
116.step-failure { color: #d11; }
117.step-wip::before {
118 content: '';
119 position: absolute;
120 margin-left: -1.5em;
121 width: 1em;
122 text-align: center;
123 animation-name: spinner;
124 animation-duration: 0.5s;
125 animation-iteration-count: infinite;
126 animation-timing-function: step-start;
127}
128@keyframes spinner {
129 0% { content: '|'; }
130 25% { content: '/'; }
131 50% { content: '-'; }
132 75% { content: '\\'; }
133 100% { content: '|'; }
134}
135
136.l {
137 display: inline-block;
138 background: red;
139 width: 1em;
140 height: 1em;
141 border-radius: 1em;
142}
143.c {
144 background-color: green;
145}
146
147.tok-kw {
148 color: #333;
149 font-weight: bold;
150}
151.tok-str {
152 color: #d14;
153}
154.tok-builtin {
155 color: #0086b3;
156}
157.tok-comment {
158 color: #777;
159 font-style: italic;
160}
161.tok-fn {
162 color: #900;
163 font-weight: bold;
164}
165.tok-null {
166 color: #008080;
167}
168.tok-number {
169 color: #008080;
170}
171.tok-type {
172 color: #458;
173 font-weight: bold;
174}
175
176@media (prefers-color-scheme: dark) {
177 body {
178 background-color: #111;
179 color: #ddd;
180 }
181 pre {
182 background-color: #222;
183 }
184 a, button {
185 color: #88f;
186 }
187 button {
188 background: #333;
189 }
190 button:disabled {
191 color: #555;
192 }
193 code a {
194 color: #eee;
195 }
196 th, td {
197 border-color: white;
198 }
199 .empty-cell {
200 background: #000;
201 }
202 .tooltip-content {
203 background: #060606;
204 border-color: white;
205 }
206 .status-running { color: #90ee90; }
207 .status-idle { color: #bbb; }
208 .step-success { color: #90ee90; }
209 .step-failure { color: #f66; }
210 .l {
211 background-color: red;
212 }
213 .c {
214 background-color: green;
215 }
216 .tok-kw {
217 color: #eee;
218 }
219 .tok-str {
220 color: #2e5;
221 }
222 .tok-builtin {
223 color: #ff894c;
224 }
225 .tok-comment {
226 color: #aa7;
227 }
228 .tok-fn {
229 color: #B1A0F8;
230 }
231 .tok-null {
232 color: #ff8080;
233 }
234 .tok-number {
235 color: #ff8080;
236 }
237 .tok-type {
238 color: #68f;
239 }
240}
lib/build-web/time_report.css created+43
...@@ -0,0 +1,43 @@
1:host > details {
2 padding: 0.5em 1em;
3 background: #f2f2f2;
4 margin-bottom: 1.0em;
5 overflow-x: scroll;
6}
7:host > details.pending {
8 pointer-events: none;
9 background: #fafafa;
10 color: #666;
11}
12:host > details > div {
13 margin: 1em 2em;
14 overflow: scroll; /* we'll try to avoid overflow, but if it does happen, this makes sense */
15}
16.stats {
17 font-size: 1.2em;
18}
19details.section {
20 margin: 1.0em 0 0 0;
21}
22details.section > summary {
23 font-weight: bold;
24}
25details.section > :not(summary) {
26 margin-left: 2em;
27}
28:host > details.no-llvm .llvm-only {
29 display: none;
30}
31@media (prefers-color-scheme: dark) {
32 :host > details {
33 background: #222;
34 }
35 :host > details.pending {
36 background: #181818;
37 color: #888;
38 }
39}
40th {
41 max-width: 20em; /* don't let the 'file' column get crazy long */
42 overflow-wrap: anywhere; /* avoid overflow where possible */
43}
lib/build-web/time_report.zig created+234
...@@ -0,0 +1,234 @@
1const std = @import("std");
2const gpa = std.heap.wasm_allocator;
3const abi = std.Build.abi.time_report;
4const fmtEscapeHtml = @import("root").fmtEscapeHtml;
5const step_list = &@import("root").step_list;
6
7const js = struct {
8 extern "time_report" fn updateGeneric(
9 /// The index of the step.
10 step_idx: u32,
11 // The HTML which will be used to populate the template slots.
12 inner_html_ptr: [*]const u8,
13 inner_html_len: usize,
14 ) void;
15 extern "time_report" fn updateCompile(
16 /// The index of the step.
17 step_idx: u32,
18 // The HTML which will be used to populate the template slots.
19 inner_html_ptr: [*]const u8,
20 inner_html_len: usize,
21 // The HTML which will populate the <tbody> of the file table.
22 file_table_html_ptr: [*]const u8,
23 file_table_html_len: usize,
24 // The HTML which will populate the <tbody> of the decl table.
25 decl_table_html_ptr: [*]const u8,
26 decl_table_html_len: usize,
27 /// Whether the LLVM backend was used. If not, LLVM-specific statistics are hidden.
28 use_llvm: bool,
29 ) void;
30};
31
32pub fn genericResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
33 if (msg_bytes.len != @sizeOf(abi.GenericResult)) @panic("malformed GenericResult message");
34 const msg: *const abi.GenericResult = @ptrCast(msg_bytes);
35 if (msg.step_idx >= step_list.*.len) @panic("malformed GenericResult message");
36 const inner_html = try std.fmt.allocPrint(gpa,
37 \\<code slot="step-name">{[step_name]f}</code>
38 \\<span slot="stat-total-time">{[stat_total_time]D}</span>
39 , .{
40 .step_name = fmtEscapeHtml(step_list.*[msg.step_idx].name),
41 .stat_total_time = msg.ns_total,
42 });
43 defer gpa.free(inner_html);
44 js.updateGeneric(msg.step_idx, inner_html.ptr, inner_html.len);
45}
46
47pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
48 const max_table_rows = 500;
49
50 if (msg_bytes.len < @sizeOf(abi.CompileResult)) @panic("malformed CompileResult message");
51 const hdr: *const abi.CompileResult = @ptrCast(msg_bytes[0..@sizeOf(abi.CompileResult)]);
52 if (hdr.step_idx >= step_list.*.len) @panic("malformed CompileResult message");
53 var trailing = msg_bytes[@sizeOf(abi.CompileResult)..];
54
55 const llvm_pass_timings = trailing[0..hdr.llvm_pass_timings_len];
56 trailing = trailing[hdr.llvm_pass_timings_len..];
57
58 const FileTimeReport = struct {
59 name: []const u8,
60 ns_sema: u64,
61 ns_codegen: u64,
62 ns_link: u64,
63 };
64 const DeclTimeReport = struct {
65 file_name: []const u8,
66 name: []const u8,
67 sema_count: u32,
68 ns_sema: u64,
69 ns_codegen: u64,
70 ns_link: u64,
71 };
72
73 const slowest_files = try gpa.alloc(FileTimeReport, hdr.files_len);
74 defer gpa.free(slowest_files);
75
76 const slowest_decls = try gpa.alloc(DeclTimeReport, hdr.decls_len);
77 defer gpa.free(slowest_decls);
78
79 for (slowest_files) |*file_out| {
80 const i = std.mem.indexOfScalar(u8, trailing, 0) orelse @panic("malformed CompileResult message");
81 file_out.* = .{
82 .name = trailing[0..i],
83 .ns_sema = 0,
84 .ns_codegen = 0,
85 .ns_link = 0,
86 };
87 trailing = trailing[i + 1 ..];
88 }
89
90 for (slowest_decls) |*decl_out| {
91 const i = std.mem.indexOfScalar(u8, trailing, 0) orelse @panic("malformed CompileResult message");
92 const file_idx = std.mem.readInt(u32, trailing[i..][1..5], .little);
93 const sema_count = std.mem.readInt(u32, trailing[i..][5..9], .little);
94 const sema_ns = std.mem.readInt(u64, trailing[i..][9..17], .little);
95 const codegen_ns = std.mem.readInt(u64, trailing[i..][17..25], .little);
96 const link_ns = std.mem.readInt(u64, trailing[i..][25..33], .little);
97 const file = &slowest_files[file_idx];
98 decl_out.* = .{
99 .file_name = file.name,
100 .name = trailing[0..i],
101 .sema_count = sema_count,
102 .ns_sema = sema_ns,
103 .ns_codegen = codegen_ns,
104 .ns_link = link_ns,
105 };
106 trailing = trailing[i + 33 ..];
107 file.ns_sema += sema_ns;
108 file.ns_codegen += codegen_ns;
109 file.ns_link += link_ns;
110 }
111
112 const S = struct {
113 fn fileLessThan(_: void, lhs: FileTimeReport, rhs: FileTimeReport) bool {
114 const lhs_ns = lhs.ns_sema + lhs.ns_codegen + lhs.ns_link;
115 const rhs_ns = rhs.ns_sema + rhs.ns_codegen + rhs.ns_link;
116 return lhs_ns > rhs_ns; // flipped to sort in reverse order
117 }
118 fn declLessThan(_: void, lhs: DeclTimeReport, rhs: DeclTimeReport) bool {
119 //if (true) return lhs.sema_count > rhs.sema_count;
120 const lhs_ns = lhs.ns_sema + lhs.ns_codegen + lhs.ns_link;
121 const rhs_ns = rhs.ns_sema + rhs.ns_codegen + rhs.ns_link;
122 return lhs_ns > rhs_ns; // flipped to sort in reverse order
123 }
124 };
125 std.mem.sort(FileTimeReport, slowest_files, {}, S.fileLessThan);
126 std.mem.sort(DeclTimeReport, slowest_decls, {}, S.declLessThan);
127
128 const stats = hdr.stats;
129 const inner_html = try std.fmt.allocPrint(gpa,
130 \\<code slot="step-name">{[step_name]f}</code>
131 \\<span slot="stat-reachable-files">{[stat_reachable_files]d}</span>
132 \\<span slot="stat-imported-files">{[stat_imported_files]d}</span>
133 \\<span slot="stat-generic-instances">{[stat_generic_instances]d}</span>
134 \\<span slot="stat-inline-calls">{[stat_inline_calls]d}</span>
135 \\<span slot="stat-compilation-time">{[stat_compilation_time]D}</span>
136 \\<span slot="cpu-time-parse">{[cpu_time_parse]D}</span>
137 \\<span slot="cpu-time-astgen">{[cpu_time_astgen]D}</span>
138 \\<span slot="cpu-time-sema">{[cpu_time_sema]D}</span>
139 \\<span slot="cpu-time-codegen">{[cpu_time_codegen]D}</span>
140 \\<span slot="cpu-time-link">{[cpu_time_link]D}</span>
141 \\<span slot="real-time-files">{[real_time_files]D}</span>
142 \\<span slot="real-time-decls">{[real_time_decls]D}</span>
143 \\<span slot="real-time-llvm-emit">{[real_time_llvm_emit]D}</span>
144 \\<span slot="real-time-link-flush">{[real_time_link_flush]D}</span>
145 \\<pre slot="llvm-pass-timings"><code>{[llvm_pass_timings]f}</code></pre>
146 \\
147 , .{
148 .step_name = fmtEscapeHtml(step_list.*[hdr.step_idx].name),
149 .stat_reachable_files = stats.n_reachable_files,
150 .stat_imported_files = stats.n_imported_files,
151 .stat_generic_instances = stats.n_generic_instances,
152 .stat_inline_calls = stats.n_inline_calls,
153 .stat_compilation_time = hdr.ns_total,
154
155 .cpu_time_parse = stats.cpu_ns_parse,
156 .cpu_time_astgen = stats.cpu_ns_astgen,
157 .cpu_time_sema = stats.cpu_ns_sema,
158 .cpu_time_codegen = stats.cpu_ns_codegen,
159 .cpu_time_link = stats.cpu_ns_link,
160 .real_time_files = stats.real_ns_files,
161 .real_time_decls = stats.real_ns_decls,
162 .real_time_llvm_emit = stats.real_ns_llvm_emit,
163 .real_time_link_flush = stats.real_ns_link_flush,
164
165 .llvm_pass_timings = fmtEscapeHtml(llvm_pass_timings),
166 });
167 defer gpa.free(inner_html);
168
169 var file_table_html: std.ArrayListUnmanaged(u8) = .empty;
170 defer file_table_html.deinit(gpa);
171 for (slowest_files[0..@min(max_table_rows, slowest_files.len)]) |file| {
172 try file_table_html.writer(gpa).print(
173 \\<tr>
174 \\ <th scope="row"><code>{f}</code></th>
175 \\ <td>{D}</td>
176 \\ <td>{D}</td>
177 \\ <td>{D}</td>
178 \\</tr>
179 \\
180 , .{
181 fmtEscapeHtml(file.name),
182 file.ns_sema,
183 file.ns_codegen,
184 file.ns_link,
185 });
186 }
187 if (slowest_files.len > max_table_rows) {
188 try file_table_html.writer(gpa).print(
189 \\<tr><td colspan="4">{d} more rows omitted</td></tr>
190 \\
191 , .{slowest_files.len - max_table_rows});
192 }
193
194 var decl_table_html: std.ArrayListUnmanaged(u8) = .empty;
195 defer decl_table_html.deinit(gpa);
196
197 for (slowest_decls[0..@min(max_table_rows, slowest_decls.len)]) |decl| {
198 try decl_table_html.writer(gpa).print(
199 \\<tr>
200 \\ <th scope="row"><code>{f}</code></th>
201 \\ <th scope="row"><code>{f}</code></th>
202 \\ <td>{d}</td>
203 \\ <td>{D}</td>
204 \\ <td>{D}</td>
205 \\ <td>{D}</td>
206 \\</tr>
207 \\
208 , .{
209 fmtEscapeHtml(decl.file_name),
210 fmtEscapeHtml(decl.name),
211 decl.sema_count,
212 decl.ns_sema,
213 decl.ns_codegen,
214 decl.ns_link,
215 });
216 }
217 if (slowest_decls.len > max_table_rows) {
218 try decl_table_html.writer(gpa).print(
219 \\<tr><td colspan="6">{d} more rows omitted</td></tr>
220 \\
221 , .{slowest_decls.len - max_table_rows});
222 }
223
224 js.updateCompile(
225 hdr.step_idx,
226 inner_html.ptr,
227 inner_html.len,
228 file_table_html.items.ptr,
229 file_table_html.items.len,
230 decl_table_html.items.ptr,
231 decl_table_html.items.len,
232 hdr.flags.use_llvm,
233 );
234}
lib/compiler/build_runner.zig+126-84
...@@ -9,7 +9,7 @@ const ArrayList = std.ArrayList;...@@ -9,7 +9,7 @@ const ArrayList = std.ArrayList;
9const File = std.fs.File;9const File = std.fs.File;
10const Step = std.Build.Step;10const Step = std.Build.Step;
11const Watch = std.Build.Watch;11const Watch = std.Build.Watch;
12const Fuzz = std.Build.Fuzz;12const WebServer = std.Build.WebServer;
13const Allocator = std.mem.Allocator;13const Allocator = std.mem.Allocator;
14const fatal = std.process.fatal;14const fatal = std.process.fatal;
15const Writer = std.io.Writer;15const Writer = std.io.Writer;
...@@ -25,15 +25,16 @@ pub const std_options: std.Options = .{...@@ -25,15 +25,16 @@ pub const std_options: std.Options = .{
25};25};
2626
27pub fn main() !void {27pub fn main() !void {
28 // Here we use an ArenaAllocator backed by a page allocator because a build is a short-lived,28 // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not
29 // one shot program. We don't need to waste time freeing memory and finding places to squish29 // always the case. So, we do need a true gpa for some things.
30 // bytes into. So we free everything all at once at the very end.30 var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init;
31 var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);31 defer _ = debug_gpa_state.deinit();
32 const gpa = debug_gpa_state.allocator();
33
34 // ...but we'll back our arena by `std.heap.page_allocator` for efficiency.
35 var single_threaded_arena: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
32 defer single_threaded_arena.deinit();36 defer single_threaded_arena.deinit();
3337 var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ .child_allocator = single_threaded_arena.allocator() };
34 var thread_safe_arena: std.heap.ThreadSafeAllocator = .{
35 .child_allocator = single_threaded_arena.allocator(),
36 };
37 const arena = thread_safe_arena.allocator();38 const arena = thread_safe_arena.allocator();
3839
39 const args = try process.argsAlloc(arena);40 const args = try process.argsAlloc(arena);
...@@ -81,6 +82,7 @@ pub fn main() !void {...@@ -81,6 +82,7 @@ pub fn main() !void {
81 .query = .{},82 .query = .{},
82 .result = try std.zig.system.resolveTargetQuery(.{}),83 .result = try std.zig.system.resolveTargetQuery(.{}),
83 },84 },
85 .time_report = false,
84 };86 };
8587
86 graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });88 graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
...@@ -113,7 +115,7 @@ pub fn main() !void {...@@ -113,7 +115,7 @@ pub fn main() !void {
113 var watch = false;115 var watch = false;
114 var fuzz = false;116 var fuzz = false;
115 var debounce_interval_ms: u16 = 50;117 var debounce_interval_ms: u16 = 50;
116 var listen_port: u16 = 0;118 var webui_listen: ?std.net.Address = null;
117119
118 while (nextArg(args, &arg_idx)) |arg| {120 while (nextArg(args, &arg_idx)) |arg| {
119 if (mem.startsWith(u8, arg, "-Z")) {121 if (mem.startsWith(u8, arg, "-Z")) {
...@@ -220,13 +222,13 @@ pub fn main() !void {...@@ -220,13 +222,13 @@ pub fn main() !void {
220 next_arg, @errorName(err),222 next_arg, @errorName(err),
221 });223 });
222 };224 };
223 } else if (mem.eql(u8, arg, "--port")) {225 } else if (mem.eql(u8, arg, "--webui")) {
224 const next_arg = nextArg(args, &arg_idx) orelse226 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
225 fatalWithHint("expected u16 after '{s}'", .{arg});227 } else if (mem.startsWith(u8, arg, "--webui=")) {
226 listen_port = std.fmt.parseUnsigned(u16, next_arg, 10) catch |err| {228 const addr_str = arg["--webui=".len..];
227 fatal("unable to parse port '{s}' as unsigned 16-bit integer: {s}\n", .{229 if (std.mem.eql(u8, addr_str, "-")) fatal("web interface cannot listen on stdio", .{});
228 next_arg, @errorName(err),230 webui_listen = std.net.Address.parseIpAndPort(addr_str) catch |err| {
229 });231 fatal("invalid web UI address '{s}': {s}", .{ addr_str, @errorName(err) });
230 };232 };
231 } else if (mem.eql(u8, arg, "--debug-log")) {233 } else if (mem.eql(u8, arg, "--debug-log")) {
232 const next_arg = nextArgOrFatal(args, &arg_idx);234 const next_arg = nextArgOrFatal(args, &arg_idx);
...@@ -267,8 +269,16 @@ pub fn main() !void {...@@ -267,8 +269,16 @@ pub fn main() !void {
267 prominent_compile_errors = true;269 prominent_compile_errors = true;
268 } else if (mem.eql(u8, arg, "--watch")) {270 } else if (mem.eql(u8, arg, "--watch")) {
269 watch = true;271 watch = true;
272 } else if (mem.eql(u8, arg, "--time-report")) {
273 graph.time_report = true;
274 if (webui_listen == null) {
275 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
276 }
270 } else if (mem.eql(u8, arg, "--fuzz")) {277 } else if (mem.eql(u8, arg, "--fuzz")) {
271 fuzz = true;278 fuzz = true;
279 if (webui_listen == null) {
280 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
281 }
272 } else if (mem.eql(u8, arg, "-fincremental")) {282 } else if (mem.eql(u8, arg, "-fincremental")) {
273 graph.incremental = true;283 graph.incremental = true;
274 } else if (mem.eql(u8, arg, "-fno-incremental")) {284 } else if (mem.eql(u8, arg, "-fno-incremental")) {
...@@ -331,6 +341,10 @@ pub fn main() !void {...@@ -331,6 +341,10 @@ pub fn main() !void {
331 }341 }
332 }342 }
333343
344 if (webui_listen != null and watch) fatal(
345 \\the build system does not yet support combining '--webui' and '--watch'; consider omitting '--watch' in favour of the web UI "Rebuild" button
346 , .{});
347
334 const stderr: std.fs.File = .stderr();348 const stderr: std.fs.File = .stderr();
335 const ttyconf = get_tty_conf(color, stderr);349 const ttyconf = get_tty_conf(color, stderr);
336 switch (ttyconf) {350 switch (ttyconf) {
...@@ -394,14 +408,16 @@ pub fn main() !void {...@@ -394,14 +408,16 @@ pub fn main() !void {
394 }408 }
395409
396 var run: Run = .{410 var run: Run = .{
411 .gpa = gpa,
412
397 .max_rss = max_rss,413 .max_rss = max_rss,
398 .max_rss_is_default = false,414 .max_rss_is_default = false,
399 .max_rss_mutex = .{},415 .max_rss_mutex = .{},
400 .skip_oom_steps = skip_oom_steps,416 .skip_oom_steps = skip_oom_steps,
401 .watch = watch,417 .watch = watch,
402 .fuzz = fuzz,418 .web_server = undefined, // set after `prepare`
403 .memory_blocked_steps = std.ArrayList(*Step).init(arena),419 .memory_blocked_steps = .empty,
404 .step_stack = .{},420 .step_stack = .empty,
405 .prominent_compile_errors = prominent_compile_errors,421 .prominent_compile_errors = prominent_compile_errors,
406422
407 .claimed_rss = 0,423 .claimed_rss = 0,
...@@ -410,74 +426,81 @@ pub fn main() !void {...@@ -410,74 +426,81 @@ pub fn main() !void {
410 .stderr = stderr,426 .stderr = stderr,
411 .thread_pool = undefined,427 .thread_pool = undefined,
412 };428 };
429 defer {
430 run.memory_blocked_steps.deinit(gpa);
431 run.step_stack.deinit(gpa);
432 }
413433
414 if (run.max_rss == 0) {434 if (run.max_rss == 0) {
415 run.max_rss = process.totalSystemMemory() catch std.math.maxInt(u64);435 run.max_rss = process.totalSystemMemory() catch std.math.maxInt(u64);
416 run.max_rss_is_default = true;436 run.max_rss_is_default = true;
417 }437 }
418438
419 const gpa = arena;439 prepare(arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) {
420 prepare(gpa, arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) {
421 error.UncleanExit => process.exit(1),440 error.UncleanExit => process.exit(1),
422 else => return err,441 else => return err,
423 };442 };
424443
425 var w: Watch = if (watch and Watch.have_impl) try Watch.init() else undefined;444 var w: Watch = w: {
445 if (!watch) break :w undefined;
446 if (!Watch.have_impl) fatal("--watch not yet implemented for {s}", .{@tagName(builtin.os.tag)});
447 break :w try .init();
448 };
426449
427 try run.thread_pool.init(thread_pool_options);450 try run.thread_pool.init(thread_pool_options);
428 defer run.thread_pool.deinit();451 defer run.thread_pool.deinit();
429452
453 run.web_server = if (webui_listen) |listen_address| .init(.{
454 .gpa = gpa,
455 .thread_pool = &run.thread_pool,
456 .graph = &graph,
457 .all_steps = run.step_stack.keys(),
458 .ttyconf = run.ttyconf,
459 .root_prog_node = main_progress_node,
460 .watch = watch,
461 .listen_address = listen_address,
462 }) else null;
463
464 if (run.web_server) |*ws| {
465 ws.start() catch |err| fatal("failed to start web server: {s}", .{@errorName(err)});
466 }
467
430 rebuild: while (true) {468 rebuild: while (true) {
469 if (run.web_server) |*ws| ws.startBuild();
470
431 runStepNames(471 runStepNames(
432 gpa,
433 builder,472 builder,
434 targets.items,473 targets.items,
435 main_progress_node,474 main_progress_node,
436 &run,475 &run,
437 ) catch |err| switch (err) {476 ) catch |err| switch (err) {
438 error.UncleanExit => {477 error.UncleanExit => {
439 assert(!run.watch);478 assert(!run.watch and run.web_server == null);
440 process.exit(1);479 process.exit(1);
441 },480 },
442 else => return err,481 else => return err,
443 };482 };
444 if (fuzz) {483
445 if (builtin.single_threaded) {484 if (run.web_server) |*web_server| {
446 fatal("--fuzz not yet implemented for single-threaded builds", .{});485 web_server.finishBuild(.{ .fuzz = fuzz });
447 }
448 switch (builtin.os.tag) {
449 // Current implementation depends on two things that need to be ported to Windows:
450 // * Memory-mapping to share data between the fuzzer and build runner.
451 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
452 // many addresses to source locations).
453 .windows => fatal("--fuzz not yet implemented for {s}", .{@tagName(builtin.os.tag)}),
454 else => {},
455 }
456 if (@bitSizeOf(usize) != 64) {
457 // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
458 // being compatible with `std.fs.getEndPos() u64`'s return value. This is not the case
459 // on 32-bit platforms.
460 // Affects or affected by issues #5185, #22523, and #22464.
461 fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
462 }
463 const listen_address = std.net.Address.parseIp("127.0.0.1", listen_port) catch unreachable;
464 try Fuzz.start(
465 gpa,
466 arena,
467 global_cache_directory,
468 zig_lib_directory,
469 zig_exe,
470 &run.thread_pool,
471 run.step_stack.keys(),
472 run.ttyconf,
473 listen_address,
474 main_progress_node,
475 );
476 }486 }
477487
478 if (!watch) return cleanExit();488 if (!watch and run.web_server == null) {
489 return cleanExit();
490 }
479491
480 if (!Watch.have_impl) fatal("--watch not yet implemented for {s}", .{@tagName(builtin.os.tag)});492 if (run.web_server) |*ws| {
493 assert(!watch); // fatal error after CLI parsing
494 while (true) switch (ws.wait()) {
495 .rebuild => {
496 for (run.step_stack.keys()) |step| {
497 step.state = .precheck_done;
498 step.reset(gpa);
499 }
500 continue :rebuild;
501 },
502 };
503 }
481504
482 try w.update(gpa, run.step_stack.keys());505 try w.update(gpa, run.step_stack.keys());
483506
...@@ -491,15 +514,16 @@ pub fn main() !void {...@@ -491,15 +514,16 @@ pub fn main() !void {
491 w.dir_table.entries.len, countSubProcesses(run.step_stack.keys()),514 w.dir_table.entries.len, countSubProcesses(run.step_stack.keys()),
492 }) catch &caption_buf;515 }) catch &caption_buf;
493 var debouncing_node = main_progress_node.start(caption, 0);516 var debouncing_node = main_progress_node.start(caption, 0);
494 var debounce_timeout: Watch.Timeout = .none;517 var in_debounce = false;
495 while (true) switch (try w.wait(gpa, debounce_timeout)) {518 while (true) switch (try w.wait(gpa, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
496 .timeout => {519 .timeout => {
520 assert(in_debounce);
497 debouncing_node.end();521 debouncing_node.end();
498 markFailedStepsDirty(gpa, run.step_stack.keys());522 markFailedStepsDirty(gpa, run.step_stack.keys());
499 continue :rebuild;523 continue :rebuild;
500 },524 },
501 .dirty => if (debounce_timeout == .none) {525 .dirty => if (!in_debounce) {
502 debounce_timeout = .{ .ms = debounce_interval_ms };526 in_debounce = true;
503 debouncing_node.end();527 debouncing_node.end();
504 debouncing_node = main_progress_node.start("Debouncing (Change Detected)", 0);528 debouncing_node = main_progress_node.start("Debouncing (Change Detected)", 0);
505 },529 },
...@@ -530,13 +554,16 @@ fn countSubProcesses(all_steps: []const *Step) usize {...@@ -530,13 +554,16 @@ fn countSubProcesses(all_steps: []const *Step) usize {
530}554}
531555
532const Run = struct {556const Run = struct {
557 gpa: Allocator,
533 max_rss: u64,558 max_rss: u64,
534 max_rss_is_default: bool,559 max_rss_is_default: bool,
535 max_rss_mutex: std.Thread.Mutex,560 max_rss_mutex: std.Thread.Mutex,
536 skip_oom_steps: bool,561 skip_oom_steps: bool,
537 watch: bool,562 watch: bool,
538 fuzz: bool,563 web_server: ?WebServer,
539 memory_blocked_steps: std.ArrayList(*Step),564 /// Allocated into `gpa`.
565 memory_blocked_steps: std.ArrayListUnmanaged(*Step),
566 /// Allocated into `gpa`.
540 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),567 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),
541 prominent_compile_errors: bool,568 prominent_compile_errors: bool,
542 thread_pool: std.Thread.Pool,569 thread_pool: std.Thread.Pool,
...@@ -547,19 +574,19 @@ const Run = struct {...@@ -547,19 +574,19 @@ const Run = struct {
547 stderr: File,574 stderr: File,
548575
549 fn cleanExit(run: Run) void {576 fn cleanExit(run: Run) void {
550 if (run.watch or run.fuzz) return;577 if (run.watch or run.web_server != null) return;
551 return runner.cleanExit();578 return runner.cleanExit();
552 }579 }
553};580};
554581
555fn prepare(582fn prepare(
556 gpa: Allocator,
557 arena: Allocator,583 arena: Allocator,
558 b: *std.Build,584 b: *std.Build,
559 step_names: []const []const u8,585 step_names: []const []const u8,
560 run: *Run,586 run: *Run,
561 seed: u32,587 seed: u32,
562) !void {588) !void {
589 const gpa = run.gpa;
563 const step_stack = &run.step_stack;590 const step_stack = &run.step_stack;
564591
565 if (step_names.len == 0) {592 if (step_names.len == 0) {
...@@ -583,7 +610,7 @@ fn prepare(...@@ -583,7 +610,7 @@ fn prepare(
583 rand.shuffle(*Step, starting_steps);610 rand.shuffle(*Step, starting_steps);
584611
585 for (starting_steps) |s| {612 for (starting_steps) |s| {
586 constructGraphAndCheckForDependencyLoop(b, s, &run.step_stack, rand) catch |err| switch (err) {613 constructGraphAndCheckForDependencyLoop(gpa, b, s, &run.step_stack, rand) catch |err| switch (err) {
587 error.DependencyLoopDetected => return uncleanExit(),614 error.DependencyLoopDetected => return uncleanExit(),
588 else => |e| return e,615 else => |e| return e,
589 };616 };
...@@ -614,12 +641,12 @@ fn prepare(...@@ -614,12 +641,12 @@ fn prepare(
614}641}
615642
616fn runStepNames(643fn runStepNames(
617 gpa: Allocator,
618 b: *std.Build,644 b: *std.Build,
619 step_names: []const []const u8,645 step_names: []const []const u8,
620 parent_prog_node: std.Progress.Node,646 parent_prog_node: std.Progress.Node,
621 run: *Run,647 run: *Run,
622) !void {648) !void {
649 const gpa = run.gpa;
623 const step_stack = &run.step_stack;650 const step_stack = &run.step_stack;
624 const thread_pool = &run.thread_pool;651 const thread_pool = &run.thread_pool;
625652
...@@ -675,6 +702,7 @@ fn runStepNames(...@@ -675,6 +702,7 @@ fn runStepNames(
675 // B will be marked as dependency_failure, while A may never be queued, and thus702 // B will be marked as dependency_failure, while A may never be queued, and thus
676 // remain in the initial state of precheck_done.703 // remain in the initial state of precheck_done.
677 s.state = .dependency_failure;704 s.state = .dependency_failure;
705 if (run.web_server) |*ws| ws.updateStepStatus(s, .failure);
678 pending_count += 1;706 pending_count += 1;
679 },707 },
680 .dependency_failure => pending_count += 1,708 .dependency_failure => pending_count += 1,
...@@ -768,7 +796,7 @@ fn runStepNames(...@@ -768,7 +796,7 @@ fn runStepNames(
768 }796 }
769 }797 }
770798
771 if (!run.watch) {799 if (!run.watch and run.web_server == null) {
772 // Signal to parent process that we have printed compile errors. The800 // Signal to parent process that we have printed compile errors. The
773 // parent process may choose to omit the "following command failed"801 // parent process may choose to omit the "following command failed"
774 // line in this case.802 // line in this case.
...@@ -777,7 +805,7 @@ fn runStepNames(...@@ -777,7 +805,7 @@ fn runStepNames(
777 }805 }
778 }806 }
779807
780 if (!run.watch) return uncleanExit();808 if (!run.watch and run.web_server == null) return uncleanExit();
781}809}
782810
783const PrintNode = struct {811const PrintNode = struct {
...@@ -1022,6 +1050,7 @@ fn printTreeStep(...@@ -1022,6 +1050,7 @@ fn printTreeStep(
1022/// when it finishes executing in `workerMakeOneStep`, it spawns next steps1050/// when it finishes executing in `workerMakeOneStep`, it spawns next steps
1023/// to run in random order1051/// to run in random order
1024fn constructGraphAndCheckForDependencyLoop(1052fn constructGraphAndCheckForDependencyLoop(
1053 gpa: Allocator,
1025 b: *std.Build,1054 b: *std.Build,
1026 s: *Step,1055 s: *Step,
1027 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),1056 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
...@@ -1035,17 +1064,19 @@ fn constructGraphAndCheckForDependencyLoop(...@@ -1035,17 +1064,19 @@ fn constructGraphAndCheckForDependencyLoop(
1035 .precheck_unstarted => {1064 .precheck_unstarted => {
1036 s.state = .precheck_started;1065 s.state = .precheck_started;
10371066
1038 try step_stack.ensureUnusedCapacity(b.allocator, s.dependencies.items.len);1067 try step_stack.ensureUnusedCapacity(gpa, s.dependencies.items.len);
10391068
1040 // We dupe to avoid shuffling the steps in the summary, it depends1069 // We dupe to avoid shuffling the steps in the summary, it depends
1041 // on s.dependencies' order.1070 // on s.dependencies' order.
1042 const deps = b.allocator.dupe(*Step, s.dependencies.items) catch @panic("OOM");1071 const deps = gpa.dupe(*Step, s.dependencies.items) catch @panic("OOM");
1072 defer gpa.free(deps);
1073
1043 rand.shuffle(*Step, deps);1074 rand.shuffle(*Step, deps);
10441075
1045 for (deps) |dep| {1076 for (deps) |dep| {
1046 try step_stack.put(b.allocator, dep, {});1077 try step_stack.put(gpa, dep, {});
1047 try dep.dependants.append(b.allocator, s);1078 try dep.dependants.append(b.allocator, s);
1048 constructGraphAndCheckForDependencyLoop(b, dep, step_stack, rand) catch |err| {1079 constructGraphAndCheckForDependencyLoop(gpa, b, dep, step_stack, rand) catch |err| {
1049 if (err == error.DependencyLoopDetected) {1080 if (err == error.DependencyLoopDetected) {
1050 std.debug.print(" {s}\n", .{s.name});1081 std.debug.print(" {s}\n", .{s.name});
1051 }1082 }
...@@ -1084,6 +1115,7 @@ fn workerMakeOneStep(...@@ -1084,6 +1115,7 @@ fn workerMakeOneStep(
1084 .success, .skipped => continue,1115 .success, .skipped => continue,
1085 .failure, .dependency_failure, .skipped_oom => {1116 .failure, .dependency_failure, .skipped_oom => {
1086 @atomicStore(Step.State, &s.state, .dependency_failure, .seq_cst);1117 @atomicStore(Step.State, &s.state, .dependency_failure, .seq_cst);
1118 if (run.web_server) |*ws| ws.updateStepStatus(s, .failure);
1087 return;1119 return;
1088 },1120 },
1089 .precheck_done, .running => {1121 .precheck_done, .running => {
...@@ -1109,7 +1141,7 @@ fn workerMakeOneStep(...@@ -1109,7 +1141,7 @@ fn workerMakeOneStep(
1109 if (new_claimed_rss > run.max_rss) {1141 if (new_claimed_rss > run.max_rss) {
1110 // Running this step right now could possibly exceed the allotted RSS.1142 // Running this step right now could possibly exceed the allotted RSS.
1111 // Add this step to the queue of memory-blocked steps.1143 // Add this step to the queue of memory-blocked steps.
1112 run.memory_blocked_steps.append(s) catch @panic("OOM");1144 run.memory_blocked_steps.append(run.gpa, s) catch @panic("OOM");
1113 return;1145 return;
1114 }1146 }
11151147
...@@ -1126,10 +1158,14 @@ fn workerMakeOneStep(...@@ -1126,10 +1158,14 @@ fn workerMakeOneStep(
1126 const sub_prog_node = prog_node.start(s.name, 0);1158 const sub_prog_node = prog_node.start(s.name, 0);
1127 defer sub_prog_node.end();1159 defer sub_prog_node.end();
11281160
1161 if (run.web_server) |*ws| ws.updateStepStatus(s, .wip);
1162
1129 const make_result = s.make(.{1163 const make_result = s.make(.{
1130 .progress_node = sub_prog_node,1164 .progress_node = sub_prog_node,
1131 .thread_pool = thread_pool,1165 .thread_pool = thread_pool,
1132 .watch = run.watch,1166 .watch = run.watch,
1167 .web_server = if (run.web_server) |*ws| ws else null,
1168 .gpa = run.gpa,
1133 });1169 });
11341170
1135 // No matter the result, we want to display error/warning messages.1171 // No matter the result, we want to display error/warning messages.
...@@ -1141,21 +1177,24 @@ fn workerMakeOneStep(...@@ -1141,21 +1177,24 @@ fn workerMakeOneStep(
1141 if (show_error_msgs or show_compile_errors or show_stderr) {1177 if (show_error_msgs or show_compile_errors or show_stderr) {
1142 const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);1178 const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);
1143 defer std.debug.unlockStderrWriter();1179 defer std.debug.unlockStderrWriter();
11441180 printErrorMessages(run.gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.prominent_compile_errors) catch {};
1145 const gpa = b.allocator;
1146 printErrorMessages(gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.prominent_compile_errors) catch {};
1147 }1181 }
11481182
1149 handle_result: {1183 handle_result: {
1150 if (make_result) |_| {1184 if (make_result) |_| {
1151 @atomicStore(Step.State, &s.state, .success, .seq_cst);1185 @atomicStore(Step.State, &s.state, .success, .seq_cst);
1186 if (run.web_server) |*ws| ws.updateStepStatus(s, .success);
1152 } else |err| switch (err) {1187 } else |err| switch (err) {
1153 error.MakeFailed => {1188 error.MakeFailed => {
1154 @atomicStore(Step.State, &s.state, .failure, .seq_cst);1189 @atomicStore(Step.State, &s.state, .failure, .seq_cst);
1190 if (run.web_server) |*ws| ws.updateStepStatus(s, .failure);
1155 std.Progress.setStatus(.failure_working);1191 std.Progress.setStatus(.failure_working);
1156 break :handle_result;1192 break :handle_result;
1157 },1193 },
1158 error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .seq_cst),1194 error.MakeSkipped => {
1195 @atomicStore(Step.State, &s.state, .skipped, .seq_cst);
1196 if (run.web_server) |*ws| ws.updateStepStatus(s, .success);
1197 },
1159 }1198 }
11601199
1161 // Successful completion of a step, so we queue up its dependants as well.1200 // Successful completion of a step, so we queue up its dependants as well.
...@@ -1255,10 +1294,10 @@ pub fn printErrorMessages(...@@ -1255,10 +1294,10 @@ pub fn printErrorMessages(
1255}1294}
12561295
1257fn printSteps(builder: *std.Build, w: *Writer) !void {1296fn printSteps(builder: *std.Build, w: *Writer) !void {
1258 const allocator = builder.allocator;1297 const arena = builder.graph.arena;
1259 for (builder.top_level_steps.values()) |top_level_step| {1298 for (builder.top_level_steps.values()) |top_level_step| {
1260 const name = if (&top_level_step.step == builder.default_step)1299 const name = if (&top_level_step.step == builder.default_step)
1261 try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})1300 try fmt.allocPrint(arena, "{s} (default)", .{top_level_step.step.name})
1262 else1301 else
1263 top_level_step.step.name;1302 top_level_step.step.name;
1264 try w.print(" {s:<28} {s}\n", .{ name, top_level_step.description });1303 try w.print(" {s:<28} {s}\n", .{ name, top_level_step.description });
...@@ -1319,8 +1358,11 @@ fn printUsage(b: *std.Build, w: *Writer) !void {...@@ -1319,8 +1358,11 @@ fn printUsage(b: *std.Build, w: *Writer) !void {
1319 \\ needed (Default) Lazy dependencies are fetched as needed1358 \\ needed (Default) Lazy dependencies are fetched as needed
1320 \\ all Lazy dependencies are always fetched1359 \\ all Lazy dependencies are always fetched
1321 \\ --watch Continuously rebuild when source files are modified1360 \\ --watch Continuously rebuild when source files are modified
1322 \\ --fuzz Continuously search for unit test failures
1323 \\ --debounce <ms> Delay before rebuilding after changed file detected1361 \\ --debounce <ms> Delay before rebuilding after changed file detected
1362 \\ --webui[=ip] Enable the web interface on the given IP address
1363 \\ --fuzz Continuously search for unit test failures (implies '--webui')
1364 \\ --time-report Force full rebuild and provide detailed information on
1365 \\ compilation time of Zig source code (implies '--webui')
1324 \\ -fincremental Enable incremental compilation1366 \\ -fincremental Enable incremental compilation
1325 \\ -fno-incremental Disable incremental compilation1367 \\ -fno-incremental Disable incremental compilation
1326 \\1368 \\
...@@ -1328,7 +1370,7 @@ fn printUsage(b: *std.Build, w: *Writer) !void {...@@ -1328,7 +1370,7 @@ fn printUsage(b: *std.Build, w: *Writer) !void {
1328 \\1370 \\
1329 );1371 );
13301372
1331 const arena = b.allocator;1373 const arena = b.graph.arena;
1332 if (b.available_options_list.items.len == 0) {1374 if (b.available_options_list.items.len == 0) {
1333 try w.print(" (none)\n", .{});1375 try w.print(" (none)\n", .{});
1334 } else {1376 } else {
lib/fuzzer.zig+1-1
...@@ -3,7 +3,7 @@ const std = @import("std");...@@ -3,7 +3,7 @@ const std = @import("std");
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const fatal = std.process.fatal;5const fatal = std.process.fatal;
6const SeenPcsHeader = std.Build.Fuzz.abi.SeenPcsHeader;6const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader;
77
8pub const std_options = std.Options{8pub const std_options = std.Options{
9 .logFn = logOverride,9 .logFn = logOverride,
lib/fuzzer/web/index.html deleted-161
...@@ -1,161 +0,0 @@
1<!doctype html>
2<html>
3 <head>
4 <meta charset="utf-8">
5 <title>Zig Build System Interface</title>
6 <style type="text/css">
7 body {
8 font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
9 color: #000000;
10 }
11 .hidden {
12 display: none;
13 }
14 table {
15 width: 100%;
16 }
17 a {
18 color: #2A6286;
19 }
20 pre{
21 font-family:"Source Code Pro",monospace;
22 font-size:1em;
23 background-color:#F5F5F5;
24 padding: 1em;
25 margin: 0;
26 overflow-x: auto;
27 }
28 :not(pre) > code {
29 white-space: break-spaces;
30 }
31 code {
32 font-family:"Source Code Pro",monospace;
33 font-size: 0.9em;
34 }
35 code a {
36 color: #000000;
37 }
38 kbd {
39 color: #000;
40 background-color: #fafbfc;
41 border-color: #d1d5da;
42 border-bottom-color: #c6cbd1;
43 box-shadow-color: #c6cbd1;
44 display: inline-block;
45 padding: 0.3em 0.2em;
46 font: 1.2em monospace;
47 line-height: 0.8em;
48 vertical-align: middle;
49 border: solid 1px;
50 border-radius: 3px;
51 box-shadow: inset 0 -1px 0;
52 cursor: default;
53 }
54
55 .l {
56 display: inline-block;
57 background: red;
58 width: 1em;
59 height: 1em;
60 border-radius: 1em;
61 }
62 .c {
63 background-color: green;
64 }
65
66 .tok-kw {
67 color: #333;
68 font-weight: bold;
69 }
70 .tok-str {
71 color: #d14;
72 }
73 .tok-builtin {
74 color: #0086b3;
75 }
76 .tok-comment {
77 color: #777;
78 font-style: italic;
79 }
80 .tok-fn {
81 color: #900;
82 font-weight: bold;
83 }
84 .tok-null {
85 color: #008080;
86 }
87 .tok-number {
88 color: #008080;
89 }
90 .tok-type {
91 color: #458;
92 font-weight: bold;
93 }
94
95 @media (prefers-color-scheme: dark) {
96 body {
97 background-color: #111;
98 color: #bbb;
99 }
100 pre {
101 background-color: #222;
102 color: #ccc;
103 }
104 a {
105 color: #88f;
106 }
107 code a {
108 color: #ccc;
109 }
110 .l {
111 background-color: red;
112 }
113 .c {
114 background-color: green;
115 }
116 .tok-kw {
117 color: #eee;
118 }
119 .tok-str {
120 color: #2e5;
121 }
122 .tok-builtin {
123 color: #ff894c;
124 }
125 .tok-comment {
126 color: #aa7;
127 }
128 .tok-fn {
129 color: #B1A0F8;
130 }
131 .tok-null {
132 color: #ff8080;
133 }
134 .tok-number {
135 color: #ff8080;
136 }
137 .tok-type {
138 color: #68f;
139 }
140 }
141 </style>
142 </head>
143 <body>
144 <p id="status">Loading JavaScript...</p>
145 <div id="sectStats" class="hidden">
146 <ul>
147 <li>Total Runs: <span id="statTotalRuns"></span></li>
148 <li>Unique Runs: <span id="statUniqueRuns"></span></li>
149 <li>Speed (Runs/Second): <span id="statSpeed"></span></li>
150 <li>Coverage: <span id="statCoverage"></span></li>
151 <li>Entry Points: <ul id="entryPointsList"></ul></li>
152 </ul>
153 </div>
154 <div id="sectSource" class="hidden">
155 <h2>Source Code</h2>
156 <pre><code id="sourceText"></code></pre>
157 </div>
158 <script src="main.js"></script>
159 </body>
160</html>
161
lib/fuzzer/web/main.js deleted-252
...@@ -1,252 +0,0 @@
1(function() {
2 const domStatus = document.getElementById("status");
3 const domSectSource = document.getElementById("sectSource");
4 const domSectStats = document.getElementById("sectStats");
5 const domSourceText = document.getElementById("sourceText");
6 const domStatTotalRuns = document.getElementById("statTotalRuns");
7 const domStatUniqueRuns = document.getElementById("statUniqueRuns");
8 const domStatSpeed = document.getElementById("statSpeed");
9 const domStatCoverage = document.getElementById("statCoverage");
10 const domEntryPointsList = document.getElementById("entryPointsList");
11
12 let wasm_promise = fetch("main.wasm");
13 let sources_promise = fetch("sources.tar").then(function(response) {
14 if (!response.ok) throw new Error("unable to download sources");
15 return response.arrayBuffer();
16 });
17 var wasm_exports = null;
18 var curNavSearch = null;
19 var curNavLocation = null;
20
21 const text_decoder = new TextDecoder();
22 const text_encoder = new TextEncoder();
23
24 domStatus.textContent = "Loading WebAssembly...";
25 WebAssembly.instantiateStreaming(wasm_promise, {
26 js: {
27 log: function(ptr, len) {
28 const msg = decodeString(ptr, len);
29 console.log(msg);
30 },
31 panic: function (ptr, len) {
32 const msg = decodeString(ptr, len);
33 throw new Error("panic: " + msg);
34 },
35 timestamp: function () {
36 return BigInt(new Date());
37 },
38 emitSourceIndexChange: onSourceIndexChange,
39 emitCoverageUpdate: onCoverageUpdate,
40 emitEntryPointsUpdate: renderStats,
41 },
42 }).then(function(obj) {
43 wasm_exports = obj.instance.exports;
44 window.wasm = obj; // for debugging
45 domStatus.textContent = "Loading sources tarball...";
46
47 sources_promise.then(function(buffer) {
48 domStatus.textContent = "Parsing sources...";
49 const js_array = new Uint8Array(buffer);
50 const ptr = wasm_exports.alloc(js_array.length);
51 const wasm_array = new Uint8Array(wasm_exports.memory.buffer, ptr, js_array.length);
52 wasm_array.set(js_array);
53 wasm_exports.unpack(ptr, js_array.length);
54
55 window.addEventListener('popstate', onPopState, false);
56 onHashChange(null);
57
58 domStatus.textContent = "Waiting for server to send source location metadata...";
59 connectWebSocket();
60 });
61 });
62
63 function onPopState(ev) {
64 onHashChange(ev.state);
65 }
66
67 function onHashChange(state) {
68 history.replaceState({}, "");
69 navigate(location.hash);
70 if (state == null) window.scrollTo({top: 0});
71 }
72
73 function navigate(location_hash) {
74 domSectSource.classList.add("hidden");
75
76 curNavLocation = null;
77 curNavSearch = null;
78
79 if (location_hash.length > 1 && location_hash[0] === '#') {
80 const query = location_hash.substring(1);
81 const qpos = query.indexOf("?");
82 let nonSearchPart;
83 if (qpos === -1) {
84 nonSearchPart = query;
85 } else {
86 nonSearchPart = query.substring(0, qpos);
87 curNavSearch = decodeURIComponent(query.substring(qpos + 1));
88 }
89
90 if (nonSearchPart[0] == "l") {
91 curNavLocation = +nonSearchPart.substring(1);
92 renderSource(curNavLocation);
93 }
94 }
95
96 render();
97 }
98
99 function connectWebSocket() {
100 const host = document.location.host;
101 const pathname = document.location.pathname;
102 const isHttps = document.location.protocol === 'https:';
103 const match = host.match(/^(.+):(\d+)$/);
104 const defaultPort = isHttps ? 443 : 80;
105 const port = match ? parseInt(match[2], 10) : defaultPort;
106 const hostName = match ? match[1] : host;
107 const wsProto = isHttps ? "wss:" : "ws:";
108 const wsUrl = wsProto + '//' + hostName + ':' + port + pathname;
109 ws = new WebSocket(wsUrl);
110 ws.binaryType = "arraybuffer";
111 ws.addEventListener('message', onWebSocketMessage, false);
112 ws.addEventListener('error', timeoutThenCreateNew, false);
113 ws.addEventListener('close', timeoutThenCreateNew, false);
114 ws.addEventListener('open', onWebSocketOpen, false);
115 }
116
117 function onWebSocketOpen() {
118 //console.log("web socket opened");
119 }
120
121 function onWebSocketMessage(ev) {
122 wasmOnMessage(ev.data);
123 }
124
125 function timeoutThenCreateNew() {
126 ws.removeEventListener('message', onWebSocketMessage, false);
127 ws.removeEventListener('error', timeoutThenCreateNew, false);
128 ws.removeEventListener('close', timeoutThenCreateNew, false);
129 ws.removeEventListener('open', onWebSocketOpen, false);
130 ws = null;
131 setTimeout(connectWebSocket, 1000);
132 }
133
134 function wasmOnMessage(data) {
135 const jsArray = new Uint8Array(data);
136 const ptr = wasm_exports.message_begin(jsArray.length);
137 const wasmArray = new Uint8Array(wasm_exports.memory.buffer, ptr, jsArray.length);
138 wasmArray.set(jsArray);
139 wasm_exports.message_end();
140 }
141
142 function onSourceIndexChange() {
143 render();
144 if (curNavLocation != null) renderSource(curNavLocation);
145 }
146
147 function onCoverageUpdate() {
148 renderStats();
149 renderCoverage();
150 }
151
152 function render() {
153 domStatus.classList.add("hidden");
154 }
155
156 function renderStats() {
157 const totalRuns = wasm_exports.totalRuns();
158 const uniqueRuns = wasm_exports.uniqueRuns();
159 const totalSourceLocations = wasm_exports.totalSourceLocations();
160 const coveredSourceLocations = wasm_exports.coveredSourceLocations();
161 domStatTotalRuns.innerText = totalRuns;
162 domStatUniqueRuns.innerText = uniqueRuns + " (" + percent(uniqueRuns, totalRuns) + "%)";
163 domStatCoverage.innerText = coveredSourceLocations + " / " + totalSourceLocations + " (" + percent(coveredSourceLocations, totalSourceLocations) + "%)";
164 domStatSpeed.innerText = wasm_exports.totalRunsPerSecond().toFixed(0);
165
166 const entryPoints = unwrapInt32Array(wasm_exports.entryPoints());
167 resizeDomList(domEntryPointsList, entryPoints.length, "<li></li>");
168 for (let i = 0; i < entryPoints.length; i += 1) {
169 const liDom = domEntryPointsList.children[i];
170 liDom.innerHTML = unwrapString(wasm_exports.sourceLocationLinkHtml(entryPoints[i]));
171 }
172
173
174 domSectStats.classList.remove("hidden");
175 }
176
177 function renderCoverage() {
178 if (curNavLocation == null) return;
179 const sourceLocationIndex = curNavLocation;
180
181 for (let i = 0; i < domSourceText.children.length; i += 1) {
182 const childDom = domSourceText.children[i];
183 if (childDom.id != null && childDom.id[0] == "l") {
184 childDom.classList.add("l");
185 childDom.classList.remove("c");
186 }
187 }
188 const coveredList = unwrapInt32Array(wasm_exports.sourceLocationFileCoveredList(sourceLocationIndex));
189 for (let i = 0; i < coveredList.length; i += 1) {
190 document.getElementById("l" + coveredList[i]).classList.add("c");
191 }
192 }
193
194 function resizeDomList(listDom, desiredLen, templateHtml) {
195 for (let i = listDom.childElementCount; i < desiredLen; i += 1) {
196 listDom.insertAdjacentHTML('beforeend', templateHtml);
197 }
198 while (desiredLen < listDom.childElementCount) {
199 listDom.removeChild(listDom.lastChild);
200 }
201 }
202
203 function percent(a, b) {
204 return ((Number(a) / Number(b)) * 100).toFixed(1);
205 }
206
207 function renderSource(sourceLocationIndex) {
208 const pathName = unwrapString(wasm_exports.sourceLocationPath(sourceLocationIndex));
209 if (pathName.length === 0) return;
210
211 const h2 = domSectSource.children[0];
212 h2.innerText = pathName;
213 domSourceText.innerHTML = unwrapString(wasm_exports.sourceLocationFileHtml(sourceLocationIndex));
214
215 domSectSource.classList.remove("hidden");
216
217 // Empirically, Firefox needs this requestAnimationFrame in order for the scrollIntoView to work.
218 requestAnimationFrame(function() {
219 const slDom = document.getElementById("l" + sourceLocationIndex);
220 if (slDom != null) slDom.scrollIntoView({
221 behavior: "smooth",
222 block: "center",
223 });
224 });
225 }
226
227 function decodeString(ptr, len) {
228 if (len === 0) return "";
229 return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len));
230 }
231
232 function unwrapInt32Array(bigint) {
233 const ptr = Number(bigint & 0xffffffffn);
234 const len = Number(bigint >> 32n);
235 if (len === 0) return new Uint32Array();
236 return new Uint32Array(wasm_exports.memory.buffer, ptr, len);
237 }
238
239 function setInputString(s) {
240 const jsArray = text_encoder.encode(s);
241 const len = jsArray.length;
242 const ptr = wasm_exports.set_input_string(len);
243 const wasmArray = new Uint8Array(wasm_exports.memory.buffer, ptr, len);
244 wasmArray.set(jsArray);
245 }
246
247 function unwrapString(bigint) {
248 const ptr = Number(bigint & 0xffffffffn);
249 const len = Number(bigint >> 32n);
250 return decodeString(ptr, len);
251 }
252})();
lib/fuzzer/web/main.zig deleted-455
...@@ -1,455 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const abi = std.Build.Fuzz.abi;
4const gpa = std.heap.wasm_allocator;
5const log = std.log;
6const Coverage = std.debug.Coverage;
7const Allocator = std.mem.Allocator;
8
9const Walk = @import("Walk");
10const Decl = Walk.Decl;
11const html_render = @import("html_render");
12
13/// Nanoseconds.
14var server_base_timestamp: i64 = 0;
15/// Milliseconds.
16var client_base_timestamp: i64 = 0;
17/// Relative to `server_base_timestamp`.
18var start_fuzzing_timestamp: i64 = undefined;
19
20const js = struct {
21 extern "js" fn log(ptr: [*]const u8, len: usize) void;
22 extern "js" fn panic(ptr: [*]const u8, len: usize) noreturn;
23 extern "js" fn timestamp() i64;
24 extern "js" fn emitSourceIndexChange() void;
25 extern "js" fn emitCoverageUpdate() void;
26 extern "js" fn emitEntryPointsUpdate() void;
27};
28
29pub const std_options: std.Options = .{
30 .logFn = logFn,
31};
32
33pub fn panic(msg: []const u8, st: ?*std.builtin.StackTrace, addr: ?usize) noreturn {
34 _ = st;
35 _ = addr;
36 log.err("panic: {s}", .{msg});
37 @trap();
38}
39
40fn logFn(
41 comptime message_level: log.Level,
42 comptime scope: @TypeOf(.enum_literal),
43 comptime format: []const u8,
44 args: anytype,
45) void {
46 const level_txt = comptime message_level.asText();
47 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
48 var buf: [500]u8 = undefined;
49 const line = std.fmt.bufPrint(&buf, level_txt ++ prefix2 ++ format, args) catch l: {
50 buf[buf.len - 3 ..][0..3].* = "...".*;
51 break :l &buf;
52 };
53 js.log(line.ptr, line.len);
54}
55
56export fn alloc(n: usize) [*]u8 {
57 const slice = gpa.alloc(u8, n) catch @panic("OOM");
58 return slice.ptr;
59}
60
61var message_buffer: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty;
62
63/// Resizes the message buffer to be the correct length; returns the pointer to
64/// the query string.
65export fn message_begin(len: usize) [*]u8 {
66 message_buffer.resize(gpa, len) catch @panic("OOM");
67 return message_buffer.items.ptr;
68}
69
70export fn message_end() void {
71 const msg_bytes = message_buffer.items;
72
73 const tag: abi.ToClientTag = @enumFromInt(msg_bytes[0]);
74 switch (tag) {
75 .current_time => return currentTimeMessage(msg_bytes),
76 .source_index => return sourceIndexMessage(msg_bytes) catch @panic("OOM"),
77 .coverage_update => return coverageUpdateMessage(msg_bytes) catch @panic("OOM"),
78 .entry_points => return entryPointsMessage(msg_bytes) catch @panic("OOM"),
79 _ => unreachable,
80 }
81}
82
83export fn unpack(tar_ptr: [*]u8, tar_len: usize) void {
84 const tar_bytes = tar_ptr[0..tar_len];
85 log.debug("received {d} bytes of tar file", .{tar_bytes.len});
86
87 unpackInner(tar_bytes) catch |err| {
88 fatal("unable to unpack tar: {s}", .{@errorName(err)});
89 };
90}
91
92/// Set by `set_input_string`.
93var input_string: std.ArrayListUnmanaged(u8) = .empty;
94var string_result: std.ArrayListUnmanaged(u8) = .empty;
95
96export fn set_input_string(len: usize) [*]u8 {
97 input_string.resize(gpa, len) catch @panic("OOM");
98 return input_string.items.ptr;
99}
100
101/// Looks up the root struct decl corresponding to a file by path.
102/// Uses `input_string`.
103export fn find_file_root() Decl.Index {
104 const file: Walk.File.Index = @enumFromInt(Walk.files.getIndex(input_string.items) orelse return .none);
105 return file.findRootDecl();
106}
107
108export fn decl_source_html(decl_index: Decl.Index) String {
109 const decl = decl_index.get();
110
111 string_result.clearRetainingCapacity();
112 html_render.fileSourceHtml(decl.file, &string_result, decl.ast_node, .{}) catch |err| {
113 fatal("unable to render source: {s}", .{@errorName(err)});
114 };
115 return String.init(string_result.items);
116}
117
118export fn totalSourceLocations() usize {
119 return coverage_source_locations.items.len;
120}
121
122export fn coveredSourceLocations() usize {
123 const covered_bits = recent_coverage_update.items[@sizeOf(abi.CoverageUpdateHeader)..];
124 var count: usize = 0;
125 for (covered_bits) |byte| count += @popCount(byte);
126 return count;
127}
128
129fn getCoverageUpdateHeader() *abi.CoverageUpdateHeader {
130 return @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
131}
132
133export fn totalRuns() u64 {
134 const header = getCoverageUpdateHeader();
135 return header.n_runs;
136}
137
138export fn uniqueRuns() u64 {
139 const header = getCoverageUpdateHeader();
140 return header.unique_runs;
141}
142
143export 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
151const String = Slice(u8);
152
153fn Slice(T: type) type {
154 return packed struct(u64) {
155 ptr: u32,
156 len: u32,
157
158 fn init(s: []const T) @This() {
159 return .{
160 .ptr = @intFromPtr(s.ptr),
161 .len = s.len,
162 };
163 }
164 };
165}
166
167fn unpackInner(tar_bytes: []u8) !void {
168 var fbs = std.io.fixedBufferStream(tar_bytes);
169 var file_name_buffer: [1024]u8 = undefined;
170 var link_name_buffer: [1024]u8 = undefined;
171 var it = std.tar.iterator(fbs.reader(), .{
172 .file_name_buffer = &file_name_buffer,
173 .link_name_buffer = &link_name_buffer,
174 });
175 while (try it.next()) |tar_file| {
176 switch (tar_file.kind) {
177 .file => {
178 if (tar_file.size == 0 and tar_file.name.len == 0) break;
179 if (std.mem.endsWith(u8, tar_file.name, ".zig")) {
180 log.debug("found file: '{s}'", .{tar_file.name});
181 const file_name = try gpa.dupe(u8, tar_file.name);
182 if (std.mem.indexOfScalar(u8, file_name, '/')) |pkg_name_end| {
183 const pkg_name = file_name[0..pkg_name_end];
184 const gop = try Walk.modules.getOrPut(gpa, pkg_name);
185 const file: Walk.File.Index = @enumFromInt(Walk.files.entries.len);
186 if (!gop.found_existing or
187 std.mem.eql(u8, file_name[pkg_name_end..], "/root.zig") or
188 std.mem.eql(u8, file_name[pkg_name_end + 1 .. file_name.len - ".zig".len], pkg_name))
189 {
190 gop.value_ptr.* = file;
191 }
192 const file_bytes = tar_bytes[fbs.pos..][0..@intCast(tar_file.size)];
193 assert(file == try Walk.add_file(file_name, file_bytes));
194 }
195 } else {
196 log.warn("skipping: '{s}' - the tar creation should have done that", .{tar_file.name});
197 }
198 },
199 else => continue,
200 }
201 }
202}
203
204fn fatal(comptime format: []const u8, args: anytype) noreturn {
205 var buf: [500]u8 = undefined;
206 const line = std.fmt.bufPrint(&buf, format, args) catch l: {
207 buf[buf.len - 3 ..][0..3].* = "...".*;
208 break :l &buf;
209 };
210 js.panic(line.ptr, line.len);
211}
212
213fn 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.
219fn 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
225fn sourceIndexMessage(msg_bytes: []u8) error{OutOfMemory}!void {
226 const Header = abi.SourceIndexHeader;
227 const header: Header = @bitCast(msg_bytes[0..@sizeOf(Header)].*);
228
229 const directories_start = @sizeOf(Header);
230 const directories_end = directories_start + header.directories_len * @sizeOf(Coverage.String);
231 const files_start = directories_end;
232 const files_end = files_start + header.files_len * @sizeOf(Coverage.File);
233 const source_locations_start = files_end;
234 const source_locations_end = source_locations_start + header.source_locations_len * @sizeOf(Coverage.SourceLocation);
235 const string_bytes = msg_bytes[source_locations_end..][0..header.string_bytes_len];
236
237 const directories: []const Coverage.String = @alignCast(std.mem.bytesAsSlice(Coverage.String, msg_bytes[directories_start..directories_end]));
238 const files: []const Coverage.File = @alignCast(std.mem.bytesAsSlice(Coverage.File, msg_bytes[files_start..files_end]));
239 const source_locations: []const Coverage.SourceLocation = @alignCast(std.mem.bytesAsSlice(Coverage.SourceLocation, msg_bytes[source_locations_start..source_locations_end]));
240
241 start_fuzzing_timestamp = header.start_timestamp;
242 try updateCoverage(directories, files, source_locations, string_bytes);
243 js.emitSourceIndexChange();
244}
245
246fn coverageUpdateMessage(msg_bytes: []u8) error{OutOfMemory}!void {
247 recent_coverage_update.clearRetainingCapacity();
248 recent_coverage_update.appendSlice(gpa, msg_bytes) catch @panic("OOM");
249 js.emitCoverageUpdate();
250}
251
252var entry_points: std.ArrayListUnmanaged(u32) = .empty;
253
254fn entryPointsMessage(msg_bytes: []u8) error{OutOfMemory}!void {
255 const header: abi.EntryPointHeader = @bitCast(msg_bytes[0..@sizeOf(abi.EntryPointHeader)].*);
256 entry_points.resize(gpa, header.flags.locs_len) catch @panic("OOM");
257 @memcpy(entry_points.items, std.mem.bytesAsSlice(u32, msg_bytes[@sizeOf(abi.EntryPointHeader)..]));
258 js.emitEntryPointsUpdate();
259}
260
261export fn entryPoints() Slice(u32) {
262 return Slice(u32).init(entry_points.items);
263}
264
265/// Index into `coverage_source_locations`.
266const SourceLocationIndex = enum(u32) {
267 _,
268
269 fn haveCoverage(sli: SourceLocationIndex) bool {
270 return @intFromEnum(sli) < coverage_source_locations.items.len;
271 }
272
273 fn ptr(sli: SourceLocationIndex) *Coverage.SourceLocation {
274 return &coverage_source_locations.items[@intFromEnum(sli)];
275 }
276
277 fn sourceLocationLinkHtml(
278 sli: SourceLocationIndex,
279 out: *std.ArrayListUnmanaged(u8),
280 ) Allocator.Error!void {
281 const sl = sli.ptr();
282 try out.writer(gpa).print("<a href=\"#l{d}\">", .{@intFromEnum(sli)});
283 try sli.appendPath(out);
284 try out.writer(gpa).print(":{d}:{d}</a>", .{ sl.line, sl.column });
285 }
286
287 fn appendPath(sli: SourceLocationIndex, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
288 const sl = sli.ptr();
289 const file = coverage.fileAt(sl.file);
290 const file_name = coverage.stringAt(file.basename);
291 const dir_name = coverage.stringAt(coverage.directories.keys()[file.directory_index]);
292 try html_render.appendEscaped(out, dir_name);
293 try out.appendSlice(gpa, "/");
294 try html_render.appendEscaped(out, file_name);
295 }
296
297 fn toWalkFile(sli: SourceLocationIndex) ?Walk.File.Index {
298 var buf: std.ArrayListUnmanaged(u8) = .empty;
299 defer buf.deinit(gpa);
300 sli.appendPath(&buf) catch @panic("OOM");
301 return @enumFromInt(Walk.files.getIndex(buf.items) orelse return null);
302 }
303
304 fn fileHtml(
305 sli: SourceLocationIndex,
306 out: *std.ArrayListUnmanaged(u8),
307 ) error{ OutOfMemory, SourceUnavailable }!void {
308 const walk_file_index = sli.toWalkFile() orelse return error.SourceUnavailable;
309 const root_node = walk_file_index.findRootDecl().get().ast_node;
310 var annotations: std.ArrayListUnmanaged(html_render.Annotation) = .empty;
311 defer annotations.deinit(gpa);
312 try computeSourceAnnotations(sli.ptr().file, walk_file_index, &annotations, coverage_source_locations.items);
313 html_render.fileSourceHtml(walk_file_index, out, root_node, .{
314 .source_location_annotations = annotations.items,
315 }) catch |err| {
316 fatal("unable to render source: {s}", .{@errorName(err)});
317 };
318 }
319};
320
321fn computeSourceAnnotations(
322 cov_file_index: Coverage.File.Index,
323 walk_file_index: Walk.File.Index,
324 annotations: *std.ArrayListUnmanaged(html_render.Annotation),
325 source_locations: []const Coverage.SourceLocation,
326) !void {
327 // Collect all the source locations from only this file into this array
328 // first, then sort by line, col, so that we can collect annotations with
329 // O(N) time complexity.
330 var locs: std.ArrayListUnmanaged(SourceLocationIndex) = .empty;
331 defer locs.deinit(gpa);
332
333 for (source_locations, 0..) |sl, sli_usize| {
334 if (sl.file != cov_file_index) continue;
335 const sli: SourceLocationIndex = @enumFromInt(sli_usize);
336 try locs.append(gpa, sli);
337 }
338
339 std.mem.sortUnstable(SourceLocationIndex, locs.items, {}, struct {
340 pub fn lessThan(context: void, lhs: SourceLocationIndex, rhs: SourceLocationIndex) bool {
341 _ = context;
342 const lhs_ptr = lhs.ptr();
343 const rhs_ptr = rhs.ptr();
344 if (lhs_ptr.line < rhs_ptr.line) return true;
345 if (lhs_ptr.line > rhs_ptr.line) return false;
346 return lhs_ptr.column < rhs_ptr.column;
347 }
348 }.lessThan);
349
350 const source = walk_file_index.get_ast().source;
351 var line: usize = 1;
352 var column: usize = 1;
353 var next_loc_index: usize = 0;
354 for (source, 0..) |byte, offset| {
355 if (byte == '\n') {
356 line += 1;
357 column = 1;
358 } else {
359 column += 1;
360 }
361 while (true) {
362 if (next_loc_index >= locs.items.len) return;
363 const next_sli = locs.items[next_loc_index];
364 const next_sl = next_sli.ptr();
365 if (next_sl.line > line or (next_sl.line == line and next_sl.column >= column)) break;
366 try annotations.append(gpa, .{
367 .file_byte_offset = offset,
368 .dom_id = @intFromEnum(next_sli),
369 });
370 next_loc_index += 1;
371 }
372 }
373}
374
375var coverage = Coverage.init;
376/// Index of type `SourceLocationIndex`.
377var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .empty;
378/// Contains the most recent coverage update message, unmodified.
379var recent_coverage_update: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty;
380
381fn updateCoverage(
382 directories: []const Coverage.String,
383 files: []const Coverage.File,
384 source_locations: []const Coverage.SourceLocation,
385 string_bytes: []const u8,
386) !void {
387 coverage.directories.clearRetainingCapacity();
388 coverage.files.clearRetainingCapacity();
389 coverage.string_bytes.clearRetainingCapacity();
390 coverage_source_locations.clearRetainingCapacity();
391
392 try coverage_source_locations.appendSlice(gpa, source_locations);
393 try coverage.string_bytes.appendSlice(gpa, string_bytes);
394
395 try coverage.files.entries.resize(gpa, files.len);
396 @memcpy(coverage.files.entries.items(.key), files);
397 try coverage.files.reIndexContext(gpa, .{ .string_bytes = coverage.string_bytes.items });
398
399 try coverage.directories.entries.resize(gpa, directories.len);
400 @memcpy(coverage.directories.entries.items(.key), directories);
401 try coverage.directories.reIndexContext(gpa, .{ .string_bytes = coverage.string_bytes.items });
402}
403
404export fn sourceLocationLinkHtml(index: SourceLocationIndex) String {
405 string_result.clearRetainingCapacity();
406 index.sourceLocationLinkHtml(&string_result) catch @panic("OOM");
407 return String.init(string_result.items);
408}
409
410/// Returns empty string if coverage metadata is not available for this source location.
411export fn sourceLocationPath(sli: SourceLocationIndex) String {
412 string_result.clearRetainingCapacity();
413 if (sli.haveCoverage()) sli.appendPath(&string_result) catch @panic("OOM");
414 return String.init(string_result.items);
415}
416
417export fn sourceLocationFileHtml(sli: SourceLocationIndex) String {
418 string_result.clearRetainingCapacity();
419 sli.fileHtml(&string_result) catch |err| switch (err) {
420 error.OutOfMemory => @panic("OOM"),
421 error.SourceUnavailable => {},
422 };
423 return String.init(string_result.items);
424}
425
426export fn sourceLocationFileCoveredList(sli_file: SourceLocationIndex) Slice(SourceLocationIndex) {
427 const global = struct {
428 var result: std.ArrayListUnmanaged(SourceLocationIndex) = .empty;
429 fn add(i: u32, want_file: Coverage.File.Index) void {
430 const src_loc_index: SourceLocationIndex = @enumFromInt(i);
431 if (src_loc_index.ptr().file == want_file) result.appendAssumeCapacity(src_loc_index);
432 }
433 };
434 const want_file = sli_file.ptr().file;
435 global.result.clearRetainingCapacity();
436
437 // This code assumes 64-bit elements, which is incorrect if the executable
438 // being fuzzed is not a 64-bit CPU. It also assumes little-endian which
439 // can also be incorrect.
440 comptime assert(abi.CoverageUpdateHeader.trailing[0] == .pc_bits_usize);
441 const n_bitset_elems = (coverage_source_locations.items.len + @bitSizeOf(u64) - 1) / @bitSizeOf(u64);
442 const covered_bits = std.mem.bytesAsSlice(
443 u64,
444 recent_coverage_update.items[@sizeOf(abi.CoverageUpdateHeader)..][0 .. n_bitset_elems * @sizeOf(u64)],
445 );
446 var sli: u32 = 0;
447 for (covered_bits) |elem| {
448 global.result.ensureUnusedCapacity(gpa, 64) catch @panic("OOM");
449 for (0..@bitSizeOf(u64)) |i| {
450 if ((elem & (@as(u64, 1) << @intCast(i))) != 0) global.add(sli, want_file);
451 sli += 1;
452 }
453 }
454 return Slice(SourceLocationIndex).init(global.result.items);
455}
lib/libc/glibc/abilists
Binary files a/lib/libc/glibc/abilists and b/lib/libc/glibc/abilists differ
lib/libc/glibc/elf/elf.h+11-22
...@@ -837,12 +837,15 @@ typedef struct...@@ -837,12 +837,15 @@ typedef struct
837#define NT_ARM_ZT 0x40d /* ARM SME ZT registers. */837#define NT_ARM_ZT 0x40d /* ARM SME ZT registers. */
838#define NT_ARM_FPMR 0x40e /* ARM floating point mode register. */838#define NT_ARM_FPMR 0x40e /* ARM floating point mode register. */
839#define NT_ARM_POE 0x40f /* ARM POE registers. */839#define NT_ARM_POE 0x40f /* ARM POE registers. */
840#define NT_ARM_GCS 0x410 /* ARM GCS state. */
840#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */841#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */
841#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */842#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */
842#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */843#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */
843#define NT_MIPS_MSA 0x802 /* MIPS SIMD registers. */844#define NT_MIPS_MSA 0x802 /* MIPS SIMD registers. */
844#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */845#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */
845#define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */846#define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */
847#define NT_RISCV_TAGGED_ADDR_CTRL 0x902 /* RISC-V tagged
848 address control */
846#define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers. */849#define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers. */
847#define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and850#define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and
848 status registers. */851 status registers. */
...@@ -2906,19 +2909,6 @@ enum...@@ -2906,19 +2909,6 @@ enum
29062909
2907#define R_AARCH64_NONE 0 /* No relocation. */2910#define R_AARCH64_NONE 0 /* No relocation. */
29082911
2909/* ILP32 AArch64 relocs. */
2910#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */
2911#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */
2912#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */
2913#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */
2914#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */
2915#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */
2916#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */
2917#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */
2918#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */
2919#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */
2920
2921/* LP64 AArch64 relocs. */
2922#define R_AARCH64_ABS64 257 /* Direct 64 bit. */2912#define R_AARCH64_ABS64 257 /* Direct 64 bit. */
2923#define R_AARCH64_ABS32 258 /* Direct 32 bit. */2913#define R_AARCH64_ABS32 258 /* Direct 32 bit. */
2924#define R_AARCH64_ABS16 259 /* Direct 16-bit. */2914#define R_AARCH64_ABS16 259 /* Direct 16-bit. */
...@@ -4091,6 +4081,7 @@ enum...@@ -4091,6 +4081,7 @@ enum
4091#define R_RISCV_TLS_DTPREL64 94081#define R_RISCV_TLS_DTPREL64 9
4092#define R_RISCV_TLS_TPREL32 104082#define R_RISCV_TLS_TPREL32 10
4093#define R_RISCV_TLS_TPREL64 114083#define R_RISCV_TLS_TPREL64 11
4084#define R_RISCV_TLSDESC 12
4094#define R_RISCV_BRANCH 164085#define R_RISCV_BRANCH 16
4095#define R_RISCV_JAL 174086#define R_RISCV_JAL 17
4096#define R_RISCV_CALL 184087#define R_RISCV_CALL 18
...@@ -4116,16 +4107,10 @@ enum...@@ -4116,16 +4107,10 @@ enum
4116#define R_RISCV_SUB16 384107#define R_RISCV_SUB16 38
4117#define R_RISCV_SUB32 394108#define R_RISCV_SUB32 39
4118#define R_RISCV_SUB64 404109#define R_RISCV_SUB64 40
4119#define R_RISCV_GNU_VTINHERIT 414110#define R_RISCV_GOT32_PCREL 41
4120#define R_RISCV_GNU_VTENTRY 42
4121#define R_RISCV_ALIGN 434111#define R_RISCV_ALIGN 43
4122#define R_RISCV_RVC_BRANCH 444112#define R_RISCV_RVC_BRANCH 44
4123#define R_RISCV_RVC_JUMP 454113#define R_RISCV_RVC_JUMP 45
4124#define R_RISCV_RVC_LUI 46
4125#define R_RISCV_GPREL_I 47
4126#define R_RISCV_GPREL_S 48
4127#define R_RISCV_TPREL_I 49
4128#define R_RISCV_TPREL_S 50
4129#define R_RISCV_RELAX 514114#define R_RISCV_RELAX 51
4130#define R_RISCV_SUB6 524115#define R_RISCV_SUB6 52
4131#define R_RISCV_SET6 534116#define R_RISCV_SET6 53
...@@ -4137,8 +4122,12 @@ enum...@@ -4137,8 +4122,12 @@ enum
4137#define R_RISCV_PLT32 594122#define R_RISCV_PLT32 59
4138#define R_RISCV_SET_ULEB128 604123#define R_RISCV_SET_ULEB128 60
4139#define R_RISCV_SUB_ULEB128 614124#define R_RISCV_SUB_ULEB128 61
4125#define R_RISCV_TLSDESC_HI20 62
4126#define R_RISCV_TLSDESC_LOAD_LO12 63
4127#define R_RISCV_TLSDESC_ADD_LO12 64
4128#define R_RISCV_TLSDESC_CALL 65
41404129
4141#define R_RISCV_NUM 624130#define R_RISCV_NUM 66
41424131
4143/* RISC-V specific values for the st_other field. */4132/* RISC-V specific values for the st_other field. */
4144#define STO_RISCV_VARIANT_CC 0x80 /* Function uses variant calling4133#define STO_RISCV_VARIANT_CC 0x80 /* Function uses variant calling
...@@ -4147,7 +4136,7 @@ enum...@@ -4147,7 +4136,7 @@ enum
4147/* RISC-V specific values for the sh_type field. */4136/* RISC-V specific values for the sh_type field. */
4148#define SHT_RISCV_ATTRIBUTES (SHT_LOPROC + 3)4137#define SHT_RISCV_ATTRIBUTES (SHT_LOPROC + 3)
41494138
4150/* RISC-V specific values for the p_type field. */4139/* RISC-V specific values for the p_type field (deprecated). */
4151#define PT_RISCV_ATTRIBUTES (PT_LOPROC + 3)4140#define PT_RISCV_ATTRIBUTES (PT_LOPROC + 3)
41524141
4153/* RISC-V specific values for the d_tag field. */4142/* RISC-V specific values for the d_tag field. */
lib/libc/glibc/include/elf.h+13
...@@ -15,6 +15,19 @@...@@ -15,6 +15,19 @@
15# define ELF_NOTE_NEXT_OFFSET(namesz, descsz, align) \15# define ELF_NOTE_NEXT_OFFSET(namesz, descsz, align) \
16 ALIGN_UP (ELF_NOTE_DESC_OFFSET ((namesz), (align)) + (descsz), (align))16 ALIGN_UP (ELF_NOTE_DESC_OFFSET ((namesz), (align)) + (descsz), (align))
1717
18# ifdef HIDDEN_VAR_NEEDS_DYNAMIC_RELOC
19# define DL_ADDRESS_WITHOUT_RELOC(expr) (expr)
20# else
21/* Evaluate EXPR without run-time relocation for it. EXPR should be an
22 array, an address of an object, or a string literal. */
23# define DL_ADDRESS_WITHOUT_RELOC(expr) \
24 ({ \
25 __auto_type _result = (expr); \
26 asm ("" : "+r" (_result)); \
27 _result; \
28 })
29# endif
30
18/* Some information which is not meant for the public and therefore not31/* Some information which is not meant for the public and therefore not
19 in <elf.h>. */32 in <elf.h>. */
20# include <dl-dtprocnum.h>33# include <dl-dtprocnum.h>
lib/libc/glibc/include/libc-symbols.h+4-4
...@@ -155,7 +155,7 @@...@@ -155,7 +155,7 @@
155 extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))) \155 extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))) \
156 __attribute_copy__ (name);156 __attribute_copy__ (name);
157157
158/* Zig patch. weak_hidden_alias was removed from glibc v2.36 (v2.37?), Zig158/* zig patch: weak_hidden_alias was removed from glibc v2.36 (v2.37?), Zig
159 needs it for the v2.32 and earlier {f,l,}stat wrappers, so only include159 needs it for the v2.32 and earlier {f,l,}stat wrappers, so only include
160 in this header for 2.32 and earlier. */160 in this header for 2.32 and earlier. */
161#if (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 32) || __GLIBC__ < 2161#if (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 32) || __GLIBC__ < 2
...@@ -220,7 +220,7 @@...@@ -220,7 +220,7 @@
220#define __make_section_unallocated(section_string) \220#define __make_section_unallocated(section_string) \
221 asm (".section " section_string "\n\t.previous");221 asm (".section " section_string "\n\t.previous");
222222
223/* Tacking on "\n\t#" to the section name makes gcc put it's bogus223/* Tacking on "\n\t#" to the section name makes gcc put its bogus
224 section attributes on what looks like a comment to the assembler. */224 section attributes on what looks like a comment to the assembler. */
225#ifdef HAVE_SECTION_QUOTES225#ifdef HAVE_SECTION_QUOTES
226# define __sec_comment "\"\n\t#\""226# define __sec_comment "\"\n\t#\""
...@@ -280,7 +280,7 @@ for linking")...@@ -280,7 +280,7 @@ for linking")
280280
281281
282/*282/*
283 283
284*/284*/
285285
286#ifdef HAVE_GNU_RETAIN286#ifdef HAVE_GNU_RETAIN
...@@ -807,7 +807,7 @@ for linking")...@@ -807,7 +807,7 @@ for linking")
807#define libm_ifunc_init()807#define libm_ifunc_init()
808#define libm_ifunc(name, expr) \808#define libm_ifunc(name, expr) \
809 __ifunc (name, name, expr, void, libm_ifunc_init)809 __ifunc (name, name, expr, void, libm_ifunc_init)
810 810
811/* These macros facilitate sharing source files with gnulib.811/* These macros facilitate sharing source files with gnulib.
812812
813 They are here instead of sys/cdefs.h because they should not be813 They are here instead of sys/cdefs.h because they should not be
lib/libc/glibc/include/stdlib.h+15
...@@ -368,6 +368,21 @@ struct abort_msg_s...@@ -368,6 +368,21 @@ struct abort_msg_s
368extern struct abort_msg_s *__abort_msg;368extern struct abort_msg_s *__abort_msg;
369libc_hidden_proto (__abort_msg)369libc_hidden_proto (__abort_msg)
370370
371enum readonly_error_type
372{
373 readonly_noerror,
374 readonly_area_writable,
375 readonly_procfs_inaccessible,
376 readonly_procfs_open_fail,
377};
378
379extern enum readonly_error_type __readonly_area (const void *ptr,
380 size_t size)
381 attribute_hidden;
382extern enum readonly_error_type __readonly_area_fallback (const void *ptr,
383 size_t size)
384 attribute_hidden;
385
371# if IS_IN (rtld)386# if IS_IN (rtld)
372extern __typeof (unsetenv) unsetenv attribute_hidden;387extern __typeof (unsetenv) unsetenv attribute_hidden;
373extern __typeof (__strtoul_internal) __strtoul_internal attribute_hidden;388extern __typeof (__strtoul_internal) __strtoul_internal attribute_hidden;
lib/libc/glibc/io/fcntl.h+5-4
...@@ -168,7 +168,7 @@ typedef __pid_t pid_t;...@@ -168,7 +168,7 @@ typedef __pid_t pid_t;
168#endif168#endif
169169
170170
171/* fcntl was a simple symbol until glibc 2.27 inclusive. glibc 2.28 onwards171/* zig patch: fcntl was a simple symbol until glibc 2.27 inclusive. glibc 2.28 onwards
172 * re-defines it to fcntl64 (via #define) if _FILE_OFFSET_BITS == 64. */172 * re-defines it to fcntl64 (via #define) if _FILE_OFFSET_BITS == 64. */
173#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2173#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2
174/* Do the file control operation described by CMD on FD.174/* Do the file control operation described by CMD on FD.
...@@ -288,16 +288,17 @@ extern int creat64 (const char *__file, mode_t __mode) __nonnull ((1));...@@ -288,16 +288,17 @@ extern int creat64 (const char *__file, mode_t __mode) __nonnull ((1));
288# define F_TEST 3 /* Test a region for other processes locks. */288# define F_TEST 3 /* Test a region for other processes locks. */
289289
290# ifndef __USE_FILE_OFFSET64290# ifndef __USE_FILE_OFFSET64
291extern int lockf (int __fd, int __cmd, off_t __len);291extern int lockf (int __fd, int __cmd, off_t __len) __wur;
292# else292# else
293# ifdef __REDIRECT293# ifdef __REDIRECT
294extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len), lockf64);294extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len),
295 lockf64) __wur;
295# else296# else
296# define lockf lockf64297# define lockf lockf64
297# endif298# endif
298# endif299# endif
299# ifdef __USE_LARGEFILE64300# ifdef __USE_LARGEFILE64
300extern int lockf64 (int __fd, int __cmd, off64_t __len);301extern int lockf64 (int __fd, int __cmd, off64_t __len) __wur;
301# endif302# endif
302#endif303#endif
303304
lib/libc/glibc/posix/bits/types.h+1-1
...@@ -217,7 +217,7 @@ typedef int __sig_atomic_t;...@@ -217,7 +217,7 @@ typedef int __sig_atomic_t;
217/* Seconds since the Epoch, visible to user code when time_t is too217/* Seconds since the Epoch, visible to user code when time_t is too
218 narrow only for consistency with the old way of widening too-narrow218 narrow only for consistency with the old way of widening too-narrow
219 types. User code should never use __time64_t. */219 types. User code should never use __time64_t. */
220/* Zig patch: Don't check __LIBC here because it breaks fstatat.c on x86. */220/* zig patch: Don't check __LIBC here because it breaks fstatat.c on x86. */
221#if __TIMESIZE == 64221#if __TIMESIZE == 64
222# define __time64_t __time_t222# define __time64_t __time_t
223#elif __TIMESIZE != 64223#elif __TIMESIZE != 64
lib/libc/glibc/stdlib/stdlib.h+6
...@@ -985,6 +985,12 @@ __extension__ extern long long int llabs (long long int __x)...@@ -985,6 +985,12 @@ __extension__ extern long long int llabs (long long int __x)
985 __THROW __attribute__ ((__const__)) __wur;985 __THROW __attribute__ ((__const__)) __wur;
986#endif986#endif
987987
988#if __GLIBC_USE (ISOC2Y)
989extern unsigned int uabs (int __x) __THROW __attribute__ ((__const__)) __wur;
990extern unsigned long int ulabs (long int __x) __THROW __attribute__ ((__const__)) __wur;
991__extension__ extern unsigned long long int ullabs (long long int __x)
992 __THROW __attribute__ ((__const__)) __wur;
993#endif
988994
989/* Return the `div_t', `ldiv_t' or `lldiv_t' representation995/* Return the `div_t', `ldiv_t' or `lldiv_t' representation
990 of the value of NUMER over DENOM. */996 of the value of NUMER over DENOM. */
lib/libc/glibc/sysdeps/aarch64/nptl/bits/pthreadtypes-arch.h+7-17
...@@ -21,23 +21,13 @@...@@ -21,23 +21,13 @@
2121
22#include <bits/endian.h>22#include <bits/endian.h>
2323
24#ifdef __ILP32__24#define __SIZEOF_PTHREAD_ATTR_T 64
25# define __SIZEOF_PTHREAD_ATTR_T 3225#define __SIZEOF_PTHREAD_MUTEX_T 48
26# define __SIZEOF_PTHREAD_MUTEX_T 3226#define __SIZEOF_PTHREAD_MUTEXATTR_T 8
27# define __SIZEOF_PTHREAD_MUTEXATTR_T 427#define __SIZEOF_PTHREAD_CONDATTR_T 8
28# define __SIZEOF_PTHREAD_CONDATTR_T 428#define __SIZEOF_PTHREAD_RWLOCK_T 56
29# define __SIZEOF_PTHREAD_RWLOCK_T 4829#define __SIZEOF_PTHREAD_BARRIER_T 32
30# define __SIZEOF_PTHREAD_BARRIER_T 2030#define __SIZEOF_PTHREAD_BARRIERATTR_T 8
31# define __SIZEOF_PTHREAD_BARRIERATTR_T 4
32#else
33# define __SIZEOF_PTHREAD_ATTR_T 64
34# define __SIZEOF_PTHREAD_MUTEX_T 48
35# define __SIZEOF_PTHREAD_MUTEXATTR_T 8
36# define __SIZEOF_PTHREAD_CONDATTR_T 8
37# define __SIZEOF_PTHREAD_RWLOCK_T 56
38# define __SIZEOF_PTHREAD_BARRIER_T 32
39# define __SIZEOF_PTHREAD_BARRIERATTR_T 8
40#endif
41#define __SIZEOF_PTHREAD_COND_T 4831#define __SIZEOF_PTHREAD_COND_T 48
42#define __SIZEOF_PTHREAD_RWLOCKATTR_T 832#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8
4333
lib/libc/glibc/sysdeps/aarch64/start-2.33.S+17-8
...@@ -54,8 +54,8 @@ _start:...@@ -54,8 +54,8 @@ _start:
54 mov x5, x054 mov x5, x0
5555
56 /* Load argc and a pointer to argv */56 /* Load argc and a pointer to argv */
57 ldr PTR_REG (1), [sp, #0]57 ldr x1, [sp, #0]
58 add x2, sp, #PTR_SIZE58 add x2, sp, 8
5959
60 /* Setup stack limit in argument register */60 /* Setup stack limit in argument register */
61 mov x6, sp61 mov x6, sp
...@@ -63,13 +63,13 @@ _start:...@@ -63,13 +63,13 @@ _start:
63#ifdef PIC63#ifdef PIC
64# ifdef SHARED64# ifdef SHARED
65 adrp x0, :got:main65 adrp x0, :got:main
66 ldr PTR_REG (0), [x0, #:got_lo12:main]66 ldr x0, [x0, #:got_lo12:main]
6767
68 adrp x3, :got:__libc_csu_init68 adrp x3, :got:__libc_csu_init
69 ldr PTR_REG (3), [x3, #:got_lo12:__libc_csu_init]69 ldr x3, [x3, #:got_lo12:__libc_csu_init]
7070
71 adrp x4, :got:__libc_csu_fini71 adrp x4, :got:__libc_csu_fini
72 ldr PTR_REG (4), [x4, #:got_lo12:__libc_csu_fini]72 ldr x4, [x4, #:got_lo12:__libc_csu_fini]
73# else73# else
74 adrp x0, __wrap_main74 adrp x0, __wrap_main
75 add x0, x0, :lo12:__wrap_main75 add x0, x0, :lo12:__wrap_main
...@@ -80,9 +80,18 @@ _start:...@@ -80,9 +80,18 @@ _start:
80# endif80# endif
81#else81#else
82 /* Set up the other arguments in registers */82 /* Set up the other arguments in registers */
83 MOVL (0, main)83 movz x0, :abs_g3:main
84 MOVL (3, __libc_csu_init)84 movk x0, :abs_g2_nc:main
85 MOVL (4, __libc_csu_fini)85 movk x0, :abs_g1_nc:main
86 movk x0, :abs_g0_nc:main
87 movz x3, :abs_g3:__libc_csu_init
88 movk x3, :abs_g2_nc:__libc_csu_init
89 movk x3, :abs_g1_nc:__libc_csu_init
90 movk x3, :abs_g0_nc:__libc_csu_init
91 movz x4, :abs_g3:__libc_csu_fini
92 movk x4, :abs_g2_nc:__libc_csu_fini
93 movk x4, :abs_g1_nc:__libc_csu_fini
94 movk x4, :abs_g0_nc:__libc_csu_fini
86#endif95#endif
8796
88 /* __libc_start_main (main, argc, argv, init, fini, rtld_fini,97 /* __libc_start_main (main, argc, argv, init, fini, rtld_fini,
lib/libc/glibc/sysdeps/aarch64/start.S+8-6
...@@ -70,8 +70,8 @@ ENTRY(_start)...@@ -70,8 +70,8 @@ ENTRY(_start)
70 mov x5, x070 mov x5, x0
7171
72 /* Load argc and a pointer to argv */72 /* Load argc and a pointer to argv */
73 ldr PTR_REG (1), [sp, #0]73 ldr x1, [sp, #0]
74 add x2, sp, #PTR_SIZE74 add x2, sp, 8
7575
76 /* Setup stack limit in argument register */76 /* Setup stack limit in argument register */
77 mov x6, sp77 mov x6, sp
...@@ -79,14 +79,16 @@ ENTRY(_start)...@@ -79,14 +79,16 @@ ENTRY(_start)
79#ifdef PIC79#ifdef PIC
80# ifdef SHARED80# ifdef SHARED
81 adrp x0, :got:main81 adrp x0, :got:main
82 ldr PTR_REG (0), [x0, #:got_lo12:main]82 ldr x0, [x0, #:got_lo12:main]
83# else83# else
84 adrp x0, __wrap_main84 adrp x0, __wrap_main
85 add x0, x0, :lo12:__wrap_main85 add x0, x0, :lo12:__wrap_main
86# endif86# endif
87#else87#else
88 /* Set up the other arguments in registers */88 movz x0, :abs_g3:main
89 MOVL (0, main)89 movk x0, :abs_g2_nc:main
90 movk x0, :abs_g1_nc:main
91 movk x0, :abs_g0_nc:main
90#endif92#endif
91 mov x3, #0 /* Used to be init. */93 mov x3, #0 /* Used to be init. */
92 mov x4, #0 /* Used to be fini. */94 mov x4, #0 /* Used to be fini. */
...@@ -106,7 +108,7 @@ ENTRY(_start)...@@ -106,7 +108,7 @@ ENTRY(_start)
106 because crt1.o and rcrt1.o share code and the later must avoid the108 because crt1.o and rcrt1.o share code and the later must avoid the
107 use of GOT relocations before __libc_start_main is called. */109 use of GOT relocations before __libc_start_main is called. */
108__wrap_main:110__wrap_main:
109 BTI_C111 bti c
110 b main112 b main
111#endif113#endif
112END(_start)114END(_start)
lib/libc/glibc/sysdeps/aarch64/sysdep.h+6-81
...@@ -21,59 +21,15 @@...@@ -21,59 +21,15 @@
2121
22#include <sysdeps/generic/sysdep.h>22#include <sysdeps/generic/sysdep.h>
2323
24#ifdef __LP64__
25# define AARCH64_R(NAME) R_AARCH64_ ## NAME
26# define PTR_REG(n) x##n
27# define PTR_LOG_SIZE 3
28# define PTR_ARG(n)
29# define SIZE_ARG(n)
30#else
31# define AARCH64_R(NAME) R_AARCH64_P32_ ## NAME
32# define PTR_REG(n) w##n
33# define PTR_LOG_SIZE 2
34# define PTR_ARG(n) mov w##n, w##n
35# define SIZE_ARG(n) mov w##n, w##n
36#endif
37
38#define PTR_SIZE (1<<PTR_LOG_SIZE)
39
40#ifndef __ASSEMBLER__
41/* Strip pointer authentication code from pointer p. */
42static inline void *
43strip_pac (void *p)
44{
45 register void *ra asm ("x30") = (p);
46 asm ("hint 7 // xpaclri" : "+r"(ra));
47 return ra;
48}
49
50/* This is needed when glibc is built with -mbranch-protection=pac-ret
51 with a gcc that is affected by PR target/94891. */
52# if HAVE_AARCH64_PAC_RET
53# undef RETURN_ADDRESS
54# define RETURN_ADDRESS(n) strip_pac (__builtin_return_address (n))
55# endif
56#endif
57
58#ifdef __ASSEMBLER__24#ifdef __ASSEMBLER__
5925
26/* CFI directive for return address. */
27#define cfi_negate_ra_state .cfi_negate_ra_state
28
60/* Syntactic details of assembler. */29/* Syntactic details of assembler. */
6130
62#define ASM_SIZE_DIRECTIVE(name) .size name,.-name31#define ASM_SIZE_DIRECTIVE(name) .size name,.-name
6332
64/* Branch Target Identitication support. */
65#if HAVE_AARCH64_BTI
66# define BTI_C hint 34
67# define BTI_J hint 36
68#else
69# define BTI_C nop
70# define BTI_J nop
71#endif
72
73/* Return address signing support (pac-ret). */
74#define PACIASP hint 25
75#define AUTIASP hint 29
76
77/* Guarded Control Stack support. */33/* Guarded Control Stack support. */
78#define CHKFEAT_X16 hint 4034#define CHKFEAT_X16 hint 40
79#define MRS_GCSPR(x) mrs x, s3_3_c2_c5_135#define MRS_GCSPR(x) mrs x, s3_3_c2_c5_1
...@@ -103,11 +59,7 @@ strip_pac (void *p)...@@ -103,11 +59,7 @@ strip_pac (void *p)
10359
104/* Add GNU property note with the supported features to all asm code60/* Add GNU property note with the supported features to all asm code
105 where sysdep.h is included. */61 where sysdep.h is included. */
106#if HAVE_AARCH64_BTI && HAVE_AARCH64_PAC_RET
107GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_PAC|FEATURE_1_GCS)62GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_PAC|FEATURE_1_GCS)
108#elif HAVE_AARCH64_BTI
109GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)
110#endif
11163
112/* Define an entry point visible from C. */64/* Define an entry point visible from C. */
113#define ENTRY(name) \65#define ENTRY(name) \
...@@ -116,7 +68,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)...@@ -116,7 +68,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)
116 .p2align 6; \68 .p2align 6; \
117 C_LABEL(name) \69 C_LABEL(name) \
118 cfi_startproc; \70 cfi_startproc; \
119 BTI_C; \71 bti c; \
120 CALL_MCOUNT72 CALL_MCOUNT
12173
122/* Define an entry point visible from C. */74/* Define an entry point visible from C. */
...@@ -126,7 +78,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)...@@ -126,7 +78,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)
126 .p2align align; \78 .p2align align; \
127 C_LABEL(name) \79 C_LABEL(name) \
128 cfi_startproc; \80 cfi_startproc; \
129 BTI_C; \81 bti c; \
130 CALL_MCOUNT82 CALL_MCOUNT
13183
132/* Define an entry point visible from C with a specified alignment and84/* Define an entry point visible from C with a specified alignment and
...@@ -143,7 +95,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)...@@ -143,7 +95,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)
143 .endr; \95 .endr; \
144 C_LABEL(name) \96 C_LABEL(name) \
145 cfi_startproc; \97 cfi_startproc; \
146 BTI_C; \98 bti c; \
147 CALL_MCOUNT99 CALL_MCOUNT
148100
149#undef END101#undef END
...@@ -195,33 +147,6 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)...@@ -195,33 +147,6 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS)
195# define L(name) .L##name147# define L(name) .L##name
196#endif148#endif
197149
198/* Load or store to/from a pc-relative EXPR into/from R, using T.
199 Note R and T are register numbers and not register names. */
200#define LDST_PCREL(OP, R, T, EXPR) \
201 adrp x##T, EXPR; \
202 OP PTR_REG (R), [x##T, #:lo12:EXPR]; \
203
204/* Load or store to/from a got-relative EXPR into/from R, using T.
205 Note R and T are register numbers and not register names. */
206#define LDST_GLOBAL(OP, R, T, EXPR) \
207 adrp x##T, :got:EXPR; \
208 ldr PTR_REG (T), [x##T, #:got_lo12:EXPR]; \
209 OP PTR_REG (R), [x##T];
210
211/* Load an immediate into R.
212 Note R is a register number and not a register name. */
213#ifdef __LP64__
214# define MOVL(R, NAME) \
215 movz PTR_REG (R), #:abs_g3:NAME; \
216 movk PTR_REG (R), #:abs_g2_nc:NAME; \
217 movk PTR_REG (R), #:abs_g1_nc:NAME; \
218 movk PTR_REG (R), #:abs_g0_nc:NAME;
219#else
220# define MOVL(R, NAME) \
221 movz PTR_REG (R), #:abs_g1:NAME; \
222 movk PTR_REG (R), #:abs_g0_nc:NAME;
223#endif
224
225/* Since C identifiers are not normally prefixed with an underscore150/* Since C identifiers are not normally prefixed with an underscore
226 on this system, the asm identifier `syscall_error' intrudes on the151 on this system, the asm identifier `syscall_error' intrudes on the
227 C name space. Make sure we use an innocuous name. */152 C name space. Make sure we use an innocuous name. */
lib/libc/glibc/sysdeps/generic/sysdep.h+3
...@@ -45,6 +45,7 @@...@@ -45,6 +45,7 @@
45# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off45# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off
46# define cfi_offset(reg, off) .cfi_offset reg, off46# define cfi_offset(reg, off) .cfi_offset reg, off
47# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off47# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off
48# define cfi_val_offset(reg, off) .cfi_val_offset reg, off
48# define cfi_register(r1, r2) .cfi_register r1, r249# define cfi_register(r1, r2) .cfi_register r1, r2
49# define cfi_return_column(reg) .cfi_return_column reg50# define cfi_return_column(reg) .cfi_return_column reg
50# define cfi_restore(reg) .cfi_restore reg51# define cfi_restore(reg) .cfi_restore reg
...@@ -74,6 +75,8 @@...@@ -74,6 +75,8 @@
74 ".cfi_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)75 ".cfi_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)
75# define CFI_REL_OFFSET(reg, off) \76# define CFI_REL_OFFSET(reg, off) \
76 ".cfi_rel_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)77 ".cfi_rel_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)
78# define CFI_VAL_OFFSET(reg, off) \
79 ".cfi_val_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)
77# define CFI_REGISTER(r1, r2) \80# define CFI_REGISTER(r1, r2) \
78 ".cfi_register " CFI_STRINGIFY(r1) "," CFI_STRINGIFY(r2)81 ".cfi_register " CFI_STRINGIFY(r1) "," CFI_STRINGIFY(r2)
79# define CFI_RETURN_COLUMN(reg) \82# define CFI_RETURN_COLUMN(reg) \
lib/libc/glibc/sysdeps/htl/libc-lockP.h+10-39
...@@ -75,7 +75,6 @@...@@ -75,7 +75,6 @@
7575
76extern int __pthread_mutex_init (pthread_mutex_t *__mutex,76extern int __pthread_mutex_init (pthread_mutex_t *__mutex,
77 const pthread_mutexattr_t *__mutex_attr);77 const pthread_mutexattr_t *__mutex_attr);
78
79extern int __pthread_mutex_destroy (pthread_mutex_t *__mutex);78extern int __pthread_mutex_destroy (pthread_mutex_t *__mutex);
80libc_hidden_proto (__pthread_mutex_destroy)79libc_hidden_proto (__pthread_mutex_destroy)
8180
...@@ -91,75 +90,47 @@ libc_hidden_proto (__pthread_mutexattr_init)...@@ -91,75 +90,47 @@ libc_hidden_proto (__pthread_mutexattr_init)
91extern int __pthread_mutexattr_destroy (pthread_mutexattr_t *__attr);90extern int __pthread_mutexattr_destroy (pthread_mutexattr_t *__attr);
92libc_hidden_proto (__pthread_mutexattr_destroy)91libc_hidden_proto (__pthread_mutexattr_destroy)
9392
94extern int __pthread_mutexattr_settype (pthread_mutexattr_t *__attr,
95 int __kind);
96
97extern int __pthread_rwlock_init (pthread_rwlock_t *__rwlock,93extern int __pthread_rwlock_init (pthread_rwlock_t *__rwlock,
98 const pthread_rwlockattr_t *__attr);94 const pthread_rwlockattr_t *__attr);
95libc_hidden_proto (__pthread_rwlock_init)
9996
100extern int __pthread_rwlock_destroy (pthread_rwlock_t *__rwlock);97extern int __pthread_rwlock_destroy (pthread_rwlock_t *__rwlock);
98libc_hidden_proto (__pthread_rwlock_destroy)
10199
102extern int __pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock);100extern int __pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock);
101libc_hidden_proto (__pthread_rwlock_rdlock)
103102
104extern int __pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock);103extern int __pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock);
104libc_hidden_proto (__pthread_rwlock_tryrdlock)
105105
106extern int __pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock);106extern int __pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock);
107libc_hidden_proto (__pthread_rwlock_wrlock)
107108
108extern int __pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock);109extern int __pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock);
110libc_hidden_proto (__pthread_rwlock_trywrlock)
109111
110extern int __pthread_rwlock_unlock (pthread_rwlock_t *__rwlock);112extern int __pthread_rwlock_unlock (pthread_rwlock_t *__rwlock);
113libc_hidden_proto (__pthread_rwlock_unlock)
111114
112extern int __pthread_once (pthread_once_t *__once_control,115extern int __pthread_once (pthread_once_t *__once_control,
113 void (*__init_routine) (void));116 void (*__init_routine) (void));
117libc_hidden_proto (__pthread_once);
114118
115extern int __pthread_atfork (void (*__prepare) (void),119extern int __pthread_atfork (void (*__prepare) (void),
116 void (*__parent) (void),120 void (*__parent) (void),
117 void (*__child) (void));121 void (*__child) (void));
118122
123extern int __pthread_setcancelstate (int state, int *oldstate);
124libc_hidden_proto (__pthread_setcancelstate)
119/* Make the pthread functions weak so that we can elide them from125/* Make the pthread functions weak so that we can elide them from
120 single-threaded processes. */126 single-threaded processes. */
121#if !defined(__NO_WEAK_PTHREAD_ALIASES) && !IS_IN (libpthread)127#if !defined(__NO_WEAK_PTHREAD_ALIASES) && !IS_IN (libpthread)
122# ifdef weak_extern128# ifdef weak_extern
123weak_extern (__pthread_mutex_init)
124weak_extern (__pthread_mutex_destroy)
125weak_extern (__pthread_mutex_lock)
126weak_extern (__pthread_mutex_trylock)
127weak_extern (__pthread_mutex_unlock)
128weak_extern (__pthread_mutexattr_settype)
129weak_extern (__pthread_rwlock_init)
130weak_extern (__pthread_rwlock_destroy)
131weak_extern (__pthread_rwlock_rdlock)
132weak_extern (__pthread_rwlock_tryrdlock)
133weak_extern (__pthread_rwlock_wrlock)
134weak_extern (__pthread_rwlock_trywrlock)
135weak_extern (__pthread_rwlock_unlock)
136weak_extern (__pthread_key_create)
137weak_extern (__pthread_setspecific)
138weak_extern (__pthread_getspecific)
139weak_extern (__pthread_once)
140weak_extern (__pthread_initialize)129weak_extern (__pthread_initialize)
141weak_extern (__pthread_atfork)130weak_extern (__pthread_atfork)
142weak_extern (__pthread_setcancelstate)
143# else131# else
144# pragma weak __pthread_mutex_init
145# pragma weak __pthread_mutex_destroy
146# pragma weak __pthread_mutex_lock
147# pragma weak __pthread_mutex_trylock
148# pragma weak __pthread_mutex_unlock
149# pragma weak __pthread_mutexattr_settype
150# pragma weak __pthread_rwlock_destroy
151# pragma weak __pthread_rwlock_rdlock
152# pragma weak __pthread_rwlock_tryrdlock
153# pragma weak __pthread_rwlock_wrlock
154# pragma weak __pthread_rwlock_trywrlock
155# pragma weak __pthread_rwlock_unlock
156# pragma weak __pthread_key_create
157# pragma weak __pthread_setspecific
158# pragma weak __pthread_getspecific
159# pragma weak __pthread_once
160# pragma weak __pthread_initialize132# pragma weak __pthread_initialize
161# pragma weak __pthread_atfork133# pragma weak __pthread_atfork
162# pragma weak __pthread_setcancelstate
163# endif134# endif
164#endif135#endif
165136
lib/libc/glibc/sysdeps/mach/sysdep.h+5
...@@ -20,6 +20,11 @@...@@ -20,6 +20,11 @@
20/* Get the Mach definitions of ENTRY and kernel_trap. */20/* Get the Mach definitions of ENTRY and kernel_trap. */
21#include <mach/machine/syscall_sw.h>21#include <mach/machine/syscall_sw.h>
2222
23/* This macro is defined in Mach system headers, but string functions use it
24 with different definitions depending on whether being compiled for
25 wide-characters or not. */
26#undef P2ALIGN
27
23/* The Mach definitions assume underscores should be prepended to28/* The Mach definitions assume underscores should be prepended to
24 symbol names. Redefine them to do so only when appropriate. */29 symbol names. Redefine them to do so only when appropriate. */
25#undef EXT30#undef EXT
lib/libc/glibc/sysdeps/nptl/pthread.h+5
...@@ -1317,6 +1317,11 @@ extern int pthread_getcpuclockid (pthread_t __thread_id,...@@ -1317,6 +1317,11 @@ extern int pthread_getcpuclockid (pthread_t __thread_id,
1317 __THROW __nonnull ((2));1317 __THROW __nonnull ((2));
1318#endif1318#endif
13191319
1320#ifdef __USE_GNU
1321/* Return the Linux TID for THREAD_ID. Returns -1 on failure. */
1322extern pid_t pthread_gettid_np (pthread_t __thread_id);
1323#endif
1324
13201325
1321/* Install handlers to be called when a new process is created with FORK.1326/* Install handlers to be called when a new process is created with FORK.
1322 The PREPARE handler is called in the parent process just before performing1327 The PREPARE handler is called in the parent process just before performing
lib/libc/glibc/sysdeps/sparc/sparc32/start.S+8-3
...@@ -35,6 +35,7 @@...@@ -35,6 +35,7 @@
3535
36#include <sysdep.h>36#include <sysdep.h>
3737
38#define FRAME_SIZE 104
3839
39 .section ".text"40 .section ".text"
40 .align 441 .align 4
...@@ -48,12 +49,12 @@ _start:...@@ -48,12 +49,12 @@ _start:
48 /* Terminate the stack frame, and reserve space for functions to49 /* Terminate the stack frame, and reserve space for functions to
49 drop their arguments. */50 drop their arguments. */
50 mov %g0, %fp51 mov %g0, %fp
51 sub %sp, 6*4, %sp52 sub %sp, FRAME_SIZE, %sp
5253
53 /* Extract the arguments and environment as encoded on the stack. The54 /* Extract the arguments and environment as encoded on the stack. The
54 argument info starts after one register window (16 words) past the SP. */55 argument info starts after one register window (16 words) past the SP. */
55 ld [%sp+22*4], %o156 ld [%sp+168], %o1
56 add %sp, 23*4, %o257 add %sp, 172, %o2
5758
58 /* Load the addresses of the user entry points. */59 /* Load the addresses of the user entry points. */
59#ifndef PIC60#ifndef PIC
...@@ -73,6 +74,10 @@ _start:...@@ -73,6 +74,10 @@ _start:
73 be NULL. */74 be NULL. */
74 mov %g1, %o575 mov %g1, %o5
7576
77 /* Provide the highest stack address to update the __libc_stack_end (used
78 to enable executable stacks if required). */
79 st %sp, [%sp+23*4]
80
76 /* Let libc do the rest of the initialization, and call main. */81 /* Let libc do the rest of the initialization, and call main. */
77 call __libc_start_main82 call __libc_start_main
78 nop83 nop
lib/libc/glibc/sysdeps/sparc/sparc64/start.S+4
...@@ -74,6 +74,10 @@ _start:...@@ -74,6 +74,10 @@ _start:
74 be NULL. */74 be NULL. */
75 mov %g1, %o575 mov %g1, %o5
7676
77 /* Provide the highest stack address to update the __libc_stack_end (used
78 to enable executable stacks if required). */
79 stx %sp, [%sp+STACK_BIAS+22*8]
80
77 /* Let libc do the rest of the initialization, and call main. */81 /* Let libc do the rest of the initialization, and call main. */
78 call __libc_start_main82 call __libc_start_main
79 nop83 nop
lib/libc/glibc/sysdeps/unix/sysv/linux/aarch64/sysdep.h+2-7
...@@ -152,13 +152,8 @@...@@ -152,13 +152,8 @@
152152
153#else /* not __ASSEMBLER__ */153#else /* not __ASSEMBLER__ */
154154
155# ifdef __LP64__155# define VDSO_NAME "LINUX_2.6.39"
156# define VDSO_NAME "LINUX_2.6.39"156# define VDSO_HASH 123718537
157# define VDSO_HASH 123718537
158# else
159# define VDSO_NAME "LINUX_4.9"
160# define VDSO_HASH 61765625
161# endif
162157
163/* List of system calls which are supported as vsyscalls. */158/* List of system calls which are supported as vsyscalls. */
164# define HAVE_CLOCK_GETRES64_VSYSCALL "__kernel_clock_getres"159# define HAVE_CLOCK_GETRES64_VSYSCALL "__kernel_clock_getres"
lib/libc/glibc/sysdeps/unix/sysv/linux/kernel-features.h+4
...@@ -54,6 +54,10 @@...@@ -54,6 +54,10 @@
54 configurations). */54 configurations). */
55#define __ASSUME_SET_ROBUST_LIST 155#define __ASSUME_SET_ROBUST_LIST 1
5656
57/* The termios2 interface was introduced across all architectures except
58 Alpha in kernel 2.6.22. */
59#define __ASSUME_TERMIOS2 1
60
57/* Support for various CLOEXEC and NONBLOCK flags was added in61/* Support for various CLOEXEC and NONBLOCK flags was added in
58 2.6.27. */62 2.6.27. */
59#define __ASSUME_IN_NONBLOCK 163#define __ASSUME_IN_NONBLOCK 1
lib/libc/glibc/sysdeps/unix/sysv/linux/riscv/sysdep.h+2-1
...@@ -145,11 +145,12 @@...@@ -145,11 +145,12 @@
145# define HAVE_CLOCK_GETRES64_VSYSCALL "__vdso_clock_getres"145# define HAVE_CLOCK_GETRES64_VSYSCALL "__vdso_clock_getres"
146# define HAVE_CLOCK_GETTIME64_VSYSCALL "__vdso_clock_gettime"146# define HAVE_CLOCK_GETTIME64_VSYSCALL "__vdso_clock_gettime"
147# define HAVE_GETTIMEOFDAY_VSYSCALL "__vdso_gettimeofday"147# define HAVE_GETTIMEOFDAY_VSYSCALL "__vdso_gettimeofday"
148# define HAVE_GETRANDOM_VSYSCALL "__vdso_getrandom"
148# else149# else
149# define VDSO_NAME "LINUX_5.4"150# define VDSO_NAME "LINUX_5.4"
150# define VDSO_HASH 61765876151# define VDSO_HASH 61765876
151152
152/* RV32 does not support the gettime VDSO syscalls. */153/* RV32 does not support the gettime and getrandom VDSO syscalls. */
153# endif154# endif
154# define HAVE_CLONE3_WRAPPER 1155# define HAVE_CLONE3_WRAPPER 1
155156
lib/libc/glibc/sysdeps/x86/sysdep.h+29
...@@ -102,6 +102,9 @@...@@ -102,6 +102,9 @@
102 | (1 << X86_XSTATE_ZMM_ID) \102 | (1 << X86_XSTATE_ZMM_ID) \
103 | (1 << X86_XSTATE_APX_F_ID))103 | (1 << X86_XSTATE_APX_F_ID))
104104
105/* The maximum supported xstate ID. */
106# define X86_XSTATE_MAX_ID X86_XSTATE_APX_F_ID
107
105/* AMX state mask. */108/* AMX state mask. */
106# define AMX_STATE_SAVE_MASK \109# define AMX_STATE_SAVE_MASK \
107 ((1 << X86_XSTATE_TILECFG_ID) | (1 << X86_XSTATE_TILEDATA_ID))110 ((1 << X86_XSTATE_TILECFG_ID) | (1 << X86_XSTATE_TILEDATA_ID))
...@@ -123,6 +126,9 @@...@@ -123,6 +126,9 @@
123 | (1 << X86_XSTATE_K_ID) \126 | (1 << X86_XSTATE_K_ID) \
124 | (1 << X86_XSTATE_ZMM_H_ID))127 | (1 << X86_XSTATE_ZMM_H_ID))
125128
129/* The maximum supported xstate ID. */
130# define X86_XSTATE_MAX_ID X86_XSTATE_ZMM_H_ID
131
126/* States to be included in xsave_state_size. */132/* States to be included in xsave_state_size. */
127# define FULL_STATE_SAVE_MASK STATE_SAVE_MASK133# define FULL_STATE_SAVE_MASK STATE_SAVE_MASK
128#endif134#endif
...@@ -177,6 +183,29 @@...@@ -177,6 +183,29 @@
177183
178#define atom_text_section .section ".text.atom", "ax"184#define atom_text_section .section ".text.atom", "ax"
179185
186#ifndef DL_STACK_ALIGNMENT
187/* Due to GCC bug:
188
189 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066
190
191 __tls_get_addr may be called with 8-byte/4-byte stack alignment.
192 Although this bug has been fixed in GCC 4.9.4, 5.3 and 6, we can't
193 assume that stack will be always aligned at 16 bytes. */
194# ifdef __x86_64__
195# define DL_STACK_ALIGNMENT 8
196# define MINIMUM_ALIGNMENT 16
197# else
198# define DL_STACK_ALIGNMENT 4
199# endif
200#endif
201
202/* True if _dl_runtime_resolve/_dl_tlsdesc_dynamic should align stack for
203 STATE_SAVE or align stack to MINIMUM_ALIGNMENT bytes before calling
204 _dl_fixup/__tls_get_addr. */
205#define DL_RUNTIME_RESOLVE_REALIGN_STACK \
206 (STATE_SAVE_ALIGNMENT > DL_STACK_ALIGNMENT \
207 || MINIMUM_ALIGNMENT > DL_STACK_ALIGNMENT)
208
180#endif /* __ASSEMBLER__ */209#endif /* __ASSEMBLER__ */
181210
182#endif /* _X86_SYSDEP_H */211#endif /* _X86_SYSDEP_H */
lib/libc/include/aarch64-linux-gnu/bits/fcntl.h+4-10
...@@ -25,17 +25,11 @@...@@ -25,17 +25,11 @@
25#define __O_NOFOLLOW 010000025#define __O_NOFOLLOW 0100000
26#define __O_DIRECT 020000026#define __O_DIRECT 0200000
2727
28#ifdef __ILP32__28#define __O_LARGEFILE 0
29# define __O_LARGEFILE 0400000
30#else
31# define __O_LARGEFILE 0
32#endif
3329
34#ifdef __LP64__30#define F_GETLK64 5
35# define F_GETLK64 531#define F_SETLK64 6
36# define F_SETLK64 632#define F_SETLKW64 7
37# define F_SETLKW64 7
38#endif
3933
40struct flock34struct flock
41 {35 {
lib/libc/include/aarch64-linux-gnu/bits/math-vector.h+32
...@@ -37,6 +37,10 @@...@@ -37,6 +37,10 @@
37# define __DECL_SIMD_acosh __DECL_SIMD_aarch6437# define __DECL_SIMD_acosh __DECL_SIMD_aarch64
38# undef __DECL_SIMD_acoshf38# undef __DECL_SIMD_acoshf
39# define __DECL_SIMD_acoshf __DECL_SIMD_aarch6439# define __DECL_SIMD_acoshf __DECL_SIMD_aarch64
40# undef __DECL_SIMD_acospi
41# define __DECL_SIMD_acospi __DECL_SIMD_aarch64
42# undef __DECL_SIMD_acospif
43# define __DECL_SIMD_acospif __DECL_SIMD_aarch64
40# undef __DECL_SIMD_asin44# undef __DECL_SIMD_asin
41# define __DECL_SIMD_asin __DECL_SIMD_aarch6445# define __DECL_SIMD_asin __DECL_SIMD_aarch64
42# undef __DECL_SIMD_asinf46# undef __DECL_SIMD_asinf
...@@ -45,6 +49,10 @@...@@ -45,6 +49,10 @@
45# define __DECL_SIMD_asinh __DECL_SIMD_aarch6449# define __DECL_SIMD_asinh __DECL_SIMD_aarch64
46# undef __DECL_SIMD_asinhf50# undef __DECL_SIMD_asinhf
47# define __DECL_SIMD_asinhf __DECL_SIMD_aarch6451# define __DECL_SIMD_asinhf __DECL_SIMD_aarch64
52# undef __DECL_SIMD_asinpi
53# define __DECL_SIMD_asinpi __DECL_SIMD_aarch64
54# undef __DECL_SIMD_asinpif
55# define __DECL_SIMD_asinpif __DECL_SIMD_aarch64
48# undef __DECL_SIMD_atan56# undef __DECL_SIMD_atan
49# define __DECL_SIMD_atan __DECL_SIMD_aarch6457# define __DECL_SIMD_atan __DECL_SIMD_aarch64
50# undef __DECL_SIMD_atanf58# undef __DECL_SIMD_atanf
...@@ -53,10 +61,18 @@...@@ -53,10 +61,18 @@
53# define __DECL_SIMD_atanh __DECL_SIMD_aarch6461# define __DECL_SIMD_atanh __DECL_SIMD_aarch64
54# undef __DECL_SIMD_atanhf62# undef __DECL_SIMD_atanhf
55# define __DECL_SIMD_atanhf __DECL_SIMD_aarch6463# define __DECL_SIMD_atanhf __DECL_SIMD_aarch64
64# undef __DECL_SIMD_atanpi
65# define __DECL_SIMD_atanpi __DECL_SIMD_aarch64
66# undef __DECL_SIMD_atanpif
67# define __DECL_SIMD_atanpif __DECL_SIMD_aarch64
56# undef __DECL_SIMD_atan268# undef __DECL_SIMD_atan2
57# define __DECL_SIMD_atan2 __DECL_SIMD_aarch6469# define __DECL_SIMD_atan2 __DECL_SIMD_aarch64
58# undef __DECL_SIMD_atan2f70# undef __DECL_SIMD_atan2f
59# define __DECL_SIMD_atan2f __DECL_SIMD_aarch6471# define __DECL_SIMD_atan2f __DECL_SIMD_aarch64
72# undef __DECL_SIMD_atan2pi
73# define __DECL_SIMD_atan2pi __DECL_SIMD_aarch64
74# undef __DECL_SIMD_atan2pif
75# define __DECL_SIMD_atan2pif __DECL_SIMD_aarch64
60# undef __DECL_SIMD_cbrt76# undef __DECL_SIMD_cbrt
61# define __DECL_SIMD_cbrt __DECL_SIMD_aarch6477# define __DECL_SIMD_cbrt __DECL_SIMD_aarch64
62# undef __DECL_SIMD_cbrtf78# undef __DECL_SIMD_cbrtf
...@@ -176,12 +192,16 @@ typedef __SVBool_t __sv_bool_t;...@@ -176,12 +192,16 @@ typedef __SVBool_t __sv_bool_t;
176# define __vpcs __attribute__ ((__aarch64_vector_pcs__))192# define __vpcs __attribute__ ((__aarch64_vector_pcs__))
177193
178__vpcs __f32x4_t _ZGVnN4vv_atan2f (__f32x4_t, __f32x4_t);194__vpcs __f32x4_t _ZGVnN4vv_atan2f (__f32x4_t, __f32x4_t);
195__vpcs __f32x4_t _ZGVnN4vv_atan2pif (__f32x4_t, __f32x4_t);
179__vpcs __f32x4_t _ZGVnN4v_acosf (__f32x4_t);196__vpcs __f32x4_t _ZGVnN4v_acosf (__f32x4_t);
180__vpcs __f32x4_t _ZGVnN4v_acoshf (__f32x4_t);197__vpcs __f32x4_t _ZGVnN4v_acoshf (__f32x4_t);
198__vpcs __f32x4_t _ZGVnN4v_acospif (__f32x4_t);
181__vpcs __f32x4_t _ZGVnN4v_asinf (__f32x4_t);199__vpcs __f32x4_t _ZGVnN4v_asinf (__f32x4_t);
182__vpcs __f32x4_t _ZGVnN4v_asinhf (__f32x4_t);200__vpcs __f32x4_t _ZGVnN4v_asinhf (__f32x4_t);
201__vpcs __f32x4_t _ZGVnN4v_asinpif (__f32x4_t);
183__vpcs __f32x4_t _ZGVnN4v_atanf (__f32x4_t);202__vpcs __f32x4_t _ZGVnN4v_atanf (__f32x4_t);
184__vpcs __f32x4_t _ZGVnN4v_atanhf (__f32x4_t);203__vpcs __f32x4_t _ZGVnN4v_atanhf (__f32x4_t);
204__vpcs __f32x4_t _ZGVnN4v_atanpif (__f32x4_t);
185__vpcs __f32x4_t _ZGVnN4v_cbrtf (__f32x4_t);205__vpcs __f32x4_t _ZGVnN4v_cbrtf (__f32x4_t);
186__vpcs __f32x4_t _ZGVnN4v_cosf (__f32x4_t);206__vpcs __f32x4_t _ZGVnN4v_cosf (__f32x4_t);
187__vpcs __f32x4_t _ZGVnN4v_coshf (__f32x4_t);207__vpcs __f32x4_t _ZGVnN4v_coshf (__f32x4_t);
...@@ -207,12 +227,16 @@ __vpcs __f32x4_t _ZGVnN4v_tanhf (__f32x4_t);...@@ -207,12 +227,16 @@ __vpcs __f32x4_t _ZGVnN4v_tanhf (__f32x4_t);
207__vpcs __f32x4_t _ZGVnN4v_tanpif (__f32x4_t);227__vpcs __f32x4_t _ZGVnN4v_tanpif (__f32x4_t);
208228
209__vpcs __f64x2_t _ZGVnN2vv_atan2 (__f64x2_t, __f64x2_t);229__vpcs __f64x2_t _ZGVnN2vv_atan2 (__f64x2_t, __f64x2_t);
230__vpcs __f64x2_t _ZGVnN2vv_atan2pi (__f64x2_t, __f64x2_t);
210__vpcs __f64x2_t _ZGVnN2v_acos (__f64x2_t);231__vpcs __f64x2_t _ZGVnN2v_acos (__f64x2_t);
211__vpcs __f64x2_t _ZGVnN2v_acosh (__f64x2_t);232__vpcs __f64x2_t _ZGVnN2v_acosh (__f64x2_t);
233__vpcs __f64x2_t _ZGVnN2v_acospi (__f64x2_t);
212__vpcs __f64x2_t _ZGVnN2v_asin (__f64x2_t);234__vpcs __f64x2_t _ZGVnN2v_asin (__f64x2_t);
213__vpcs __f64x2_t _ZGVnN2v_asinh (__f64x2_t);235__vpcs __f64x2_t _ZGVnN2v_asinh (__f64x2_t);
236__vpcs __f64x2_t _ZGVnN2v_asinpi (__f64x2_t);
214__vpcs __f64x2_t _ZGVnN2v_atan (__f64x2_t);237__vpcs __f64x2_t _ZGVnN2v_atan (__f64x2_t);
215__vpcs __f64x2_t _ZGVnN2v_atanh (__f64x2_t);238__vpcs __f64x2_t _ZGVnN2v_atanh (__f64x2_t);
239__vpcs __f64x2_t _ZGVnN2v_atanpi (__f64x2_t);
216__vpcs __f64x2_t _ZGVnN2v_cbrt (__f64x2_t);240__vpcs __f64x2_t _ZGVnN2v_cbrt (__f64x2_t);
217__vpcs __f64x2_t _ZGVnN2v_cos (__f64x2_t);241__vpcs __f64x2_t _ZGVnN2v_cos (__f64x2_t);
218__vpcs __f64x2_t _ZGVnN2v_cosh (__f64x2_t);242__vpcs __f64x2_t _ZGVnN2v_cosh (__f64x2_t);
...@@ -243,12 +267,16 @@ __vpcs __f64x2_t _ZGVnN2v_tanpi (__f64x2_t);...@@ -243,12 +267,16 @@ __vpcs __f64x2_t _ZGVnN2v_tanpi (__f64x2_t);
243#ifdef __SVE_VEC_MATH_SUPPORTED267#ifdef __SVE_VEC_MATH_SUPPORTED
244268
245__sv_f32_t _ZGVsMxvv_atan2f (__sv_f32_t, __sv_f32_t, __sv_bool_t);269__sv_f32_t _ZGVsMxvv_atan2f (__sv_f32_t, __sv_f32_t, __sv_bool_t);
270__sv_f32_t _ZGVsMxvv_atan2pif (__sv_f32_t, __sv_f32_t, __sv_bool_t);
246__sv_f32_t _ZGVsMxv_acosf (__sv_f32_t, __sv_bool_t);271__sv_f32_t _ZGVsMxv_acosf (__sv_f32_t, __sv_bool_t);
247__sv_f32_t _ZGVsMxv_acoshf (__sv_f32_t, __sv_bool_t);272__sv_f32_t _ZGVsMxv_acoshf (__sv_f32_t, __sv_bool_t);
273__sv_f32_t _ZGVsMxv_acospif (__sv_f32_t, __sv_bool_t);
248__sv_f32_t _ZGVsMxv_asinf (__sv_f32_t, __sv_bool_t);274__sv_f32_t _ZGVsMxv_asinf (__sv_f32_t, __sv_bool_t);
249__sv_f32_t _ZGVsMxv_asinhf (__sv_f32_t, __sv_bool_t);275__sv_f32_t _ZGVsMxv_asinhf (__sv_f32_t, __sv_bool_t);
276__sv_f32_t _ZGVsMxv_asinpif (__sv_f32_t, __sv_bool_t);
250__sv_f32_t _ZGVsMxv_atanf (__sv_f32_t, __sv_bool_t);277__sv_f32_t _ZGVsMxv_atanf (__sv_f32_t, __sv_bool_t);
251__sv_f32_t _ZGVsMxv_atanhf (__sv_f32_t, __sv_bool_t);278__sv_f32_t _ZGVsMxv_atanhf (__sv_f32_t, __sv_bool_t);
279__sv_f32_t _ZGVsMxv_atanpif (__sv_f32_t, __sv_bool_t);
252__sv_f32_t _ZGVsMxv_cbrtf (__sv_f32_t, __sv_bool_t);280__sv_f32_t _ZGVsMxv_cbrtf (__sv_f32_t, __sv_bool_t);
253__sv_f32_t _ZGVsMxv_cosf (__sv_f32_t, __sv_bool_t);281__sv_f32_t _ZGVsMxv_cosf (__sv_f32_t, __sv_bool_t);
254__sv_f32_t _ZGVsMxv_coshf (__sv_f32_t, __sv_bool_t);282__sv_f32_t _ZGVsMxv_coshf (__sv_f32_t, __sv_bool_t);
...@@ -274,12 +302,16 @@ __sv_f32_t _ZGVsMxv_tanhf (__sv_f32_t, __sv_bool_t);...@@ -274,12 +302,16 @@ __sv_f32_t _ZGVsMxv_tanhf (__sv_f32_t, __sv_bool_t);
274__sv_f32_t _ZGVsMxv_tanpif (__sv_f32_t, __sv_bool_t);302__sv_f32_t _ZGVsMxv_tanpif (__sv_f32_t, __sv_bool_t);
275303
276__sv_f64_t _ZGVsMxvv_atan2 (__sv_f64_t, __sv_f64_t, __sv_bool_t);304__sv_f64_t _ZGVsMxvv_atan2 (__sv_f64_t, __sv_f64_t, __sv_bool_t);
305__sv_f64_t _ZGVsMxvv_atan2pi (__sv_f64_t, __sv_f64_t, __sv_bool_t);
277__sv_f64_t _ZGVsMxv_acos (__sv_f64_t, __sv_bool_t);306__sv_f64_t _ZGVsMxv_acos (__sv_f64_t, __sv_bool_t);
278__sv_f64_t _ZGVsMxv_acosh (__sv_f64_t, __sv_bool_t);307__sv_f64_t _ZGVsMxv_acosh (__sv_f64_t, __sv_bool_t);
308__sv_f64_t _ZGVsMxv_acospi (__sv_f64_t, __sv_bool_t);
279__sv_f64_t _ZGVsMxv_asin (__sv_f64_t, __sv_bool_t);309__sv_f64_t _ZGVsMxv_asin (__sv_f64_t, __sv_bool_t);
280__sv_f64_t _ZGVsMxv_asinh (__sv_f64_t, __sv_bool_t);310__sv_f64_t _ZGVsMxv_asinh (__sv_f64_t, __sv_bool_t);
311__sv_f64_t _ZGVsMxv_asinpi (__sv_f64_t, __sv_bool_t);
281__sv_f64_t _ZGVsMxv_atan (__sv_f64_t, __sv_bool_t);312__sv_f64_t _ZGVsMxv_atan (__sv_f64_t, __sv_bool_t);
282__sv_f64_t _ZGVsMxv_atanh (__sv_f64_t, __sv_bool_t);313__sv_f64_t _ZGVsMxv_atanh (__sv_f64_t, __sv_bool_t);
314__sv_f64_t _ZGVsMxv_atanpi (__sv_f64_t, __sv_bool_t);
283__sv_f64_t _ZGVsMxv_cbrt (__sv_f64_t, __sv_bool_t);315__sv_f64_t _ZGVsMxv_cbrt (__sv_f64_t, __sv_bool_t);
284__sv_f64_t _ZGVsMxv_cos (__sv_f64_t, __sv_bool_t);316__sv_f64_t _ZGVsMxv_cos (__sv_f64_t, __sv_bool_t);
285__sv_f64_t _ZGVsMxv_cosh (__sv_f64_t, __sv_bool_t);317__sv_f64_t _ZGVsMxv_cosh (__sv_f64_t, __sv_bool_t);
lib/libc/include/aarch64-linux-gnu/bits/pthreadtypes-arch.h+7-17
...@@ -21,23 +21,13 @@...@@ -21,23 +21,13 @@
2121
22#include <bits/endian.h>22#include <bits/endian.h>
2323
24#ifdef __ILP32__24#define __SIZEOF_PTHREAD_ATTR_T 64
25# define __SIZEOF_PTHREAD_ATTR_T 3225#define __SIZEOF_PTHREAD_MUTEX_T 48
26# define __SIZEOF_PTHREAD_MUTEX_T 3226#define __SIZEOF_PTHREAD_MUTEXATTR_T 8
27# define __SIZEOF_PTHREAD_MUTEXATTR_T 427#define __SIZEOF_PTHREAD_CONDATTR_T 8
28# define __SIZEOF_PTHREAD_CONDATTR_T 428#define __SIZEOF_PTHREAD_RWLOCK_T 56
29# define __SIZEOF_PTHREAD_RWLOCK_T 4829#define __SIZEOF_PTHREAD_BARRIER_T 32
30# define __SIZEOF_PTHREAD_BARRIER_T 2030#define __SIZEOF_PTHREAD_BARRIERATTR_T 8
31# define __SIZEOF_PTHREAD_BARRIERATTR_T 4
32#else
33# define __SIZEOF_PTHREAD_ATTR_T 64
34# define __SIZEOF_PTHREAD_MUTEX_T 48
35# define __SIZEOF_PTHREAD_MUTEXATTR_T 8
36# define __SIZEOF_PTHREAD_CONDATTR_T 8
37# define __SIZEOF_PTHREAD_RWLOCK_T 56
38# define __SIZEOF_PTHREAD_BARRIER_T 32
39# define __SIZEOF_PTHREAD_BARRIERATTR_T 8
40#endif
41#define __SIZEOF_PTHREAD_COND_T 4831#define __SIZEOF_PTHREAD_COND_T 48
42#define __SIZEOF_PTHREAD_RWLOCKATTR_T 832#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8
4333
lib/libc/include/aarch64-linux-gnu/bits/semaphore.h+1-7
...@@ -20,13 +20,7 @@...@@ -20,13 +20,7 @@
20# error "Never use <bits/semaphore.h> directly; include <semaphore.h> instead."20# error "Never use <bits/semaphore.h> directly; include <semaphore.h> instead."
21#endif21#endif
2222
2323#define __SIZEOF_SEM_T 32
24#ifdef __ILP32__
25# define __SIZEOF_SEM_T 16
26#else
27# define __SIZEOF_SEM_T 32
28#endif
29
3024
31/* Value returned if `sem_open' failed. */25/* Value returned if `sem_open' failed. */
32#define SEM_FAILED ((sem_t *) 0)26#define SEM_FAILED ((sem_t *) 0)
lib/libc/include/aarch64-linux-gnu/bits/wordsize.h+1-8
...@@ -17,12 +17,5 @@...@@ -17,12 +17,5 @@
17 License along with the GNU C Library; if not, see17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */18 <https://www.gnu.org/licenses/>. */
1919
20#ifdef __LP64__20#define __WORDSIZE 64
21# define __WORDSIZE 64
22#else
23# define __WORDSIZE 32
24# define __WORDSIZE32_SIZE_ULONG 1
25# define __WORDSIZE32_PTRDIFF_LONG 1
26#endif
27
28#define __WORDSIZE_TIME64_COMPAT32 021#define __WORDSIZE_TIME64_COMPAT32 0
\ No newline at end of file
lib/libc/include/generic-glibc/arpa/inet.h+5
...@@ -101,6 +101,11 @@ extern char *inet_nsap_ntoa (int __len, const unsigned char *__cp,...@@ -101,6 +101,11 @@ extern char *inet_nsap_ntoa (int __len, const unsigned char *__cp,
101 char *__buf) __THROW;101 char *__buf) __THROW;
102#endif102#endif
103103
104#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
105/* Include functions with security checks. */
106# include <bits/inet-fortified.h>
107#endif
108
104__END_DECLS109__END_DECLS
105110
106#endif /* arpa/inet.h */111#endif /* arpa/inet.h */
\ No newline at end of file
lib/libc/include/generic-glibc/bits/fcntl-linux.h+2
...@@ -379,6 +379,8 @@ struct file_handle...@@ -379,6 +379,8 @@ struct file_handle
379 identity and may not379 identity and may not
380 be usable to380 be usable to
381 open_by_handle_at. */381 open_by_handle_at. */
382# define AT_HANDLE_MNT_ID_UNIQUE 1 /* Return the 64-bit unique mount
383 ID. */
382#endif384#endif
383385
384__BEGIN_DECLS386__BEGIN_DECLS
lib/libc/include/generic-glibc/bits/inet-fortified-decl.h created+42
...@@ -0,0 +1,42 @@
1/* Declarations of checking macros for inet functions.
2 Copyright (C) 2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _BITS_INET_FORTIFIED_DEC_H
20#define _BITS_INET_FORTIFIED_DEC_H 1
21
22#ifndef _ARPA_INET_H
23# error "Never include <bits/inet-fortified-decl.h> directly; use <arpa/inet.h> instead."
24#endif
25
26extern const char *__inet_ntop_chk (int, const void *, char *, socklen_t, size_t);
27
28extern const char *__REDIRECT_FORTIFY_NTH (__inet_ntop_alias,
29 (int, const void *, char *, socklen_t), inet_ntop);
30extern const char *__REDIRECT_NTH (__inet_ntop_chk_warn,
31 (int, const void *, char *, socklen_t, size_t), __inet_ntop_chk)
32 __warnattr ("inet_ntop called with bigger length than "
33 "size of destination buffer");
34
35extern int __inet_pton_chk (int, const char *, void *, size_t);
36
37extern int __REDIRECT_FORTIFY_NTH (__inet_pton_alias,
38 (int, const char *, void *), inet_pton);
39extern int __REDIRECT_NTH (__inet_pton_chk_warn,
40 (int, const char *, void *, size_t), __inet_pton_chk)
41 __warnattr ("inet_pton called with a destination buffer size too small");
42#endif /* bits/inet-fortified-decl.h. */
\ No newline at end of file
lib/libc/include/generic-glibc/bits/inet-fortified.h created+61
...@@ -0,0 +1,61 @@
1/* Checking macros for inet functions.
2 Copyright (C) 2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _BITS_INET_FORTIFIED_H
20#define _BITS_INET_FORTIFIED_H 1
21
22#ifndef _ARPA_INET_H
23# error "Never include <bits/inet-fortified.h> directly; use <arpa/inet.h> instead."
24#endif
25
26#include <bits/inet-fortified-decl.h>
27
28__fortify_function __attribute_overloadable__ const char *
29__NTH (inet_ntop (int __af,
30 __fortify_clang_overload_arg (const void *, __restrict, __src),
31 char *__restrict __dst, socklen_t __dst_size))
32 __fortify_clang_warning_only_if_bos_lt (__dst_size, __dst,
33 "inet_ntop called with bigger length "
34 "than size of destination buffer")
35{
36 return __glibc_fortify (inet_ntop, __dst_size, sizeof (char),
37 __glibc_objsize (__dst),
38 __af, __src, __dst, __dst_size);
39};
40
41__fortify_function __attribute_overloadable__ int
42__NTH (inet_pton (int __af,
43 const char *__restrict __src,
44 __fortify_clang_overload_arg (void *, __restrict, __dst)))
45 __fortify_clang_warning_only_if_bos0_lt
46 (4, __dst, "inet_pton called with destination buffer size less than 4")
47{
48 size_t sz = 0;
49 if (__af == AF_INET)
50 sz = sizeof (struct in_addr);
51 else if (__af == AF_INET6)
52 sz = sizeof (struct in6_addr);
53 else
54 return __inet_pton_alias (__af, __src, __dst);
55
56 return __glibc_fortify (inet_pton, sz, sizeof (char),
57 __glibc_objsize (__dst),
58 __af, __src, __dst);
59};
60
61#endif /* bits/inet-fortified.h. */
\ No newline at end of file
lib/libc/include/generic-glibc/bits/ioctl-types.h-11
...@@ -32,17 +32,6 @@ struct winsize...@@ -32,17 +32,6 @@ struct winsize
32 unsigned short int ws_ypixel;32 unsigned short int ws_ypixel;
33 };33 };
3434
35#define NCC 8
36struct termio
37 {
38 unsigned short int c_iflag; /* input mode flags */
39 unsigned short int c_oflag; /* output mode flags */
40 unsigned short int c_cflag; /* control mode flags */
41 unsigned short int c_lflag; /* local mode flags */
42 unsigned char c_line; /* line discipline */
43 unsigned char c_cc[NCC]; /* control characters */
44};
45
46/* modem lines */35/* modem lines */
47#define TIOCM_LE 0x00136#define TIOCM_LE 0x001
48#define TIOCM_DTR 0x00237#define TIOCM_DTR 0x002
lib/libc/include/generic-glibc/bits/ioctls.h+1-84
...@@ -22,87 +22,4 @@...@@ -22,87 +22,4 @@
22/* Use the definitions from the kernel header files. */22/* Use the definitions from the kernel header files. */
23#include <asm/ioctls.h>23#include <asm/ioctls.h>
2424
25/* Routing table calls. */
26#define SIOCADDRT 0x890B /* add routing table entry */
27#define SIOCDELRT 0x890C /* delete routing table entry */
28#define SIOCRTMSG 0x890D /* call to routing system */
29
30/* Socket configuration controls. */
31#define SIOCGIFNAME 0x8910 /* get iface name */
32#define SIOCSIFLINK 0x8911 /* set iface channel */
33#define SIOCGIFCONF 0x8912 /* get iface list */
34#define SIOCGIFFLAGS 0x8913 /* get flags */
35#define SIOCSIFFLAGS 0x8914 /* set flags */
36#define SIOCGIFADDR 0x8915 /* get PA address */
37#define SIOCSIFADDR 0x8916 /* set PA address */
38#define SIOCGIFDSTADDR 0x8917 /* get remote PA address */
39#define SIOCSIFDSTADDR 0x8918 /* set remote PA address */
40#define SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */
41#define SIOCSIFBRDADDR 0x891a /* set broadcast PA address */
42#define SIOCGIFNETMASK 0x891b /* get network PA mask */
43#define SIOCSIFNETMASK 0x891c /* set network PA mask */
44#define SIOCGIFMETRIC 0x891d /* get metric */
45#define SIOCSIFMETRIC 0x891e /* set metric */
46#define SIOCGIFMEM 0x891f /* get memory address (BSD) */
47#define SIOCSIFMEM 0x8920 /* set memory address (BSD) */
48#define SIOCGIFMTU 0x8921 /* get MTU size */
49#define SIOCSIFMTU 0x8922 /* set MTU size */
50#define SIOCSIFNAME 0x8923 /* set interface name */
51#define SIOCSIFHWADDR 0x8924 /* set hardware address */
52#define SIOCGIFENCAP 0x8925 /* get/set encapsulations */
53#define SIOCSIFENCAP 0x8926
54#define SIOCGIFHWADDR 0x8927 /* Get hardware address */
55#define SIOCGIFSLAVE 0x8929 /* Driver slaving support */
56#define SIOCSIFSLAVE 0x8930
57#define SIOCADDMULTI 0x8931 /* Multicast address lists */
58#define SIOCDELMULTI 0x8932
59#define SIOCGIFINDEX 0x8933 /* name -> if_index mapping */
60#define SIOGIFINDEX SIOCGIFINDEX /* misprint compatibility :-) */
61#define SIOCSIFPFLAGS 0x8934 /* set/get extended flags set */
62#define SIOCGIFPFLAGS 0x8935
63#define SIOCDIFADDR 0x8936 /* delete PA address */
64#define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */
65#define SIOCGIFCOUNT 0x8938 /* get number of devices */
66
67#define SIOCGIFBR 0x8940 /* Bridging support */
68#define SIOCSIFBR 0x8941 /* Set bridging options */
69
70#define SIOCGIFTXQLEN 0x8942 /* Get the tx queue length */
71#define SIOCSIFTXQLEN 0x8943 /* Set the tx queue length */
72
73
74/* ARP cache control calls. */
75 /* 0x8950 - 0x8952 * obsolete calls, don't re-use */
76#define SIOCDARP 0x8953 /* delete ARP table entry */
77#define SIOCGARP 0x8954 /* get ARP table entry */
78#define SIOCSARP 0x8955 /* set ARP table entry */
79
80/* RARP cache control calls. */
81#define SIOCDRARP 0x8960 /* delete RARP table entry */
82#define SIOCGRARP 0x8961 /* get RARP table entry */
83#define SIOCSRARP 0x8962 /* set RARP table entry */
84
85/* Driver configuration calls */
86
87#define SIOCGIFMAP 0x8970 /* Get device parameters */
88#define SIOCSIFMAP 0x8971 /* Set device parameters */
89
90/* DLCI configuration calls */
91
92#define SIOCADDDLCI 0x8980 /* Create new DLCI device */
93#define SIOCDELDLCI 0x8981 /* Delete DLCI device */
94
95/* Device private ioctl calls. */
96
97/* These 16 ioctls are available to devices via the do_ioctl() device
98 vector. Each device should include this file and redefine these
99 names as their own. Because these are device dependent it is a good
100 idea _NOT_ to issue them to random objects and hope. */
101
102#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */
103
104/*
105 * These 16 ioctl calls are protocol private
106 */
107
108#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */
\ No newline at end of file
25#include <linux/sockios.h>
\ No newline at end of file
lib/libc/include/generic-glibc/bits/libm-simd-decl-stubs.h+44
...@@ -373,4 +373,48 @@...@@ -373,4 +373,48 @@
373#define __DECL_SIMD_tanpif32x373#define __DECL_SIMD_tanpif32x
374#define __DECL_SIMD_tanpif64x374#define __DECL_SIMD_tanpif64x
375#define __DECL_SIMD_tanpif128x375#define __DECL_SIMD_tanpif128x
376
377#define __DECL_SIMD_acospi
378#define __DECL_SIMD_acospif
379#define __DECL_SIMD_acospil
380#define __DECL_SIMD_acospif16
381#define __DECL_SIMD_acospif32
382#define __DECL_SIMD_acospif64
383#define __DECL_SIMD_acospif128
384#define __DECL_SIMD_acospif32x
385#define __DECL_SIMD_acospif64x
386#define __DECL_SIMD_acospif128x
387
388#define __DECL_SIMD_asinpi
389#define __DECL_SIMD_asinpif
390#define __DECL_SIMD_asinpil
391#define __DECL_SIMD_asinpif16
392#define __DECL_SIMD_asinpif32
393#define __DECL_SIMD_asinpif64
394#define __DECL_SIMD_asinpif128
395#define __DECL_SIMD_asinpif32x
396#define __DECL_SIMD_asinpif64x
397#define __DECL_SIMD_asinpif128x
398
399#define __DECL_SIMD_atanpi
400#define __DECL_SIMD_atanpif
401#define __DECL_SIMD_atanpil
402#define __DECL_SIMD_atanpif16
403#define __DECL_SIMD_atanpif32
404#define __DECL_SIMD_atanpif64
405#define __DECL_SIMD_atanpif128
406#define __DECL_SIMD_atanpif32x
407#define __DECL_SIMD_atanpif64x
408#define __DECL_SIMD_atanpif128x
409
410#define __DECL_SIMD_atan2pi
411#define __DECL_SIMD_atan2pif
412#define __DECL_SIMD_atan2pil
413#define __DECL_SIMD_atan2pif16
414#define __DECL_SIMD_atan2pif32
415#define __DECL_SIMD_atan2pif64
416#define __DECL_SIMD_atan2pif128
417#define __DECL_SIMD_atan2pif32x
418#define __DECL_SIMD_atan2pif64x
419#define __DECL_SIMD_atan2pif128x
376#endif420#endif
\ No newline at end of file
lib/libc/include/generic-glibc/bits/mathcalls-macros.h+1-1
...@@ -34,7 +34,7 @@...@@ -34,7 +34,7 @@
34#define __MATHCALLX(function,suffix, args, attrib) \34#define __MATHCALLX(function,suffix, args, attrib) \
35 __MATHDECLX (_Mdouble_,function,suffix, args, attrib)35 __MATHDECLX (_Mdouble_,function,suffix, args, attrib)
36#define __MATHDECLX(type, function,suffix, args, attrib) \36#define __MATHDECLX(type, function,suffix, args, attrib) \
37 __MATHDECL_1(type, function,suffix, args) __attribute__ (attrib);37 __MATHDECL_1(type, function,suffix, args) __attribute__ (attrib)
38#define __MATHDECL_1_IMPL(type, function, suffix, args) \38#define __MATHDECL_1_IMPL(type, function, suffix, args) \
39 extern type __MATH_PRECNAME(function,suffix) args __THROW39 extern type __MATH_PRECNAME(function,suffix) args __THROW
40#define __MATHDECL_1(type, function, suffix, args) \40#define __MATHDECL_1(type, function, suffix, args) \
lib/libc/include/generic-glibc/bits/mathcalls.h+21
...@@ -68,12 +68,16 @@ __MATHCALL_VEC (tan,, (_Mdouble_ __x));...@@ -68,12 +68,16 @@ __MATHCALL_VEC (tan,, (_Mdouble_ __x));
68#if __GLIBC_USE (IEC_60559_FUNCS_EXT_C23)68#if __GLIBC_USE (IEC_60559_FUNCS_EXT_C23)
69/* Arc cosine of X, divided by pi. */69/* Arc cosine of X, divided by pi. */
70__MATHCALL (acospi,, (_Mdouble_ __x));70__MATHCALL (acospi,, (_Mdouble_ __x));
71__MATHCALL_VEC (acospi,, (_Mdouble_ __x));
71/* Arc sine of X, divided by pi. */72/* Arc sine of X, divided by pi. */
72__MATHCALL (asinpi,, (_Mdouble_ __x));73__MATHCALL (asinpi,, (_Mdouble_ __x));
74__MATHCALL_VEC (asinpi,, (_Mdouble_ __x));
73/* Arc tangent of X, divided by pi. */75/* Arc tangent of X, divided by pi. */
74__MATHCALL (atanpi,, (_Mdouble_ __x));76__MATHCALL (atanpi,, (_Mdouble_ __x));
77__MATHCALL_VEC (atanpi,, (_Mdouble_ __x));
75/* Arc tangent of Y/X, divided by pi. */78/* Arc tangent of Y/X, divided by pi. */
76__MATHCALL (atan2pi,, (_Mdouble_ __y, _Mdouble_ __x));79__MATHCALL (atan2pi,, (_Mdouble_ __y, _Mdouble_ __x));
80__MATHCALL_VEC (atan2pi,, (_Mdouble_ __y, _Mdouble_ __x));
7781
78/* Cosine of pi * X. */82/* Cosine of pi * X. */
79__MATHCALL_VEC (cospi,, (_Mdouble_ __x));83__MATHCALL_VEC (cospi,, (_Mdouble_ __x));
...@@ -185,6 +189,23 @@ __MATHCALL_VEC (hypot,, (_Mdouble_ __x, _Mdouble_ __y));...@@ -185,6 +189,23 @@ __MATHCALL_VEC (hypot,, (_Mdouble_ __x, _Mdouble_ __y));
185__MATHCALL_VEC (cbrt,, (_Mdouble_ __x));189__MATHCALL_VEC (cbrt,, (_Mdouble_ __x));
186#endif190#endif
187191
192#if __GLIBC_USE (IEC_60559_FUNCS_EXT_C23)
193/* Return 1+X to the Y power. */
194__MATHCALL (compoundn,, (_Mdouble_ __x, long long int __y));
195
196/* Return X to the Y power. */
197__MATHCALL (pown,, (_Mdouble_ __x, long long int __y));
198
199/* Return X to the Y power. */
200__MATHCALL (powr,, (_Mdouble_ __x, _Mdouble_ __y));
201
202/* Return the Yth root of X. */
203__MATHCALL (rootn,, (_Mdouble_ __x, long long int __y));
204
205/* Return the reciprocal of the square root of X. */
206__MATHCALL (rsqrt,, (_Mdouble_ __x));
207#endif
208
188209
189/* Nearest integer, absolute value, and remainder functions. */210/* Nearest integer, absolute value, and remainder functions. */
190211
lib/libc/include/generic-glibc/bits/mman-linux.h+2
...@@ -113,6 +113,8 @@...@@ -113,6 +113,8 @@
113 locked pages too. */113 locked pages too. */
114# define MADV_COLLAPSE 25 /* Synchronous hugepage collapse. */114# define MADV_COLLAPSE 25 /* Synchronous hugepage collapse. */
115# define MADV_HWPOISON 100 /* Poison a page for testing. */115# define MADV_HWPOISON 100 /* Poison a page for testing. */
116# define MADV_GUARD_INSTALL 102 /* Fatal signal on access to range */
117# define MADV_GUARD_REMOVE 103 /* Unguard range */
116#endif118#endif
117119
118/* The POSIX people had to invent similar names for the same things. */120/* The POSIX people had to invent similar names for the same things. */
lib/libc/include/generic-glibc/bits/mman-shared.h+3-4
...@@ -43,10 +43,9 @@...@@ -43,10 +43,9 @@
43# endif43# endif
4444
45/* Access restrictions for pkey_alloc. */45/* Access restrictions for pkey_alloc. */
46# ifndef PKEY_DISABLE_ACCESS46# define PKEY_UNRESTRICTED 0x0
47# define PKEY_DISABLE_ACCESS 0x147# define PKEY_DISABLE_ACCESS 0x1
48# define PKEY_DISABLE_WRITE 0x248# define PKEY_DISABLE_WRITE 0x2
49# endif
5049
51__BEGIN_DECLS50__BEGIN_DECLS
5251
lib/libc/include/generic-glibc/bits/sched.h+1-1
...@@ -152,7 +152,7 @@ int sched_setattr (pid_t tid, struct sched_attr *attr, unsigned int flags)...@@ -152,7 +152,7 @@ int sched_setattr (pid_t tid, struct sched_attr *attr, unsigned int flags)
152 store it in *ATTR. */152 store it in *ATTR. */
153int sched_getattr (pid_t tid, struct sched_attr *attr, unsigned int size,153int sched_getattr (pid_t tid, struct sched_attr *attr, unsigned int size,
154 unsigned int flags)154 unsigned int flags)
155 __THROW __nonnull ((2)) __attr_access ((__write_only__, 2, 3));155 __THROW __nonnull ((2));
156156
157#endif157#endif
158158
lib/libc/include/generic-glibc/bits/string_fortified.h+1-1
...@@ -151,7 +151,7 @@ __NTH (strncat (__fortify_clang_overload_arg (char *, __restrict, __dest),...@@ -151,7 +151,7 @@ __NTH (strncat (__fortify_clang_overload_arg (char *, __restrict, __dest),
151}151}
152152
153/*153/*
154 * strlcpy and strlcat introduced in glibc 2.38154 * zig patch: strlcpy and strlcat introduced in glibc 2.38
155 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da155 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da
156 */156 */
157#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2157#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2
lib/libc/include/generic-glibc/bits/syscall.h+22-2
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1/* Generated at libc build time from syscall list. */1/* Generated at libc build time from syscall list. */
2/* The system call list corresponds to kernel 6.12. */2/* The system call list corresponds to kernel 6.15. */
33
4#ifndef _SYSCALL_H4#ifndef _SYSCALL_H
5# error "Never use <bits/syscall.h> directly; include <sys/syscall.h> instead."5# error "Never use <bits/syscall.h> directly; include <sys/syscall.h> instead."
6#endif6#endif
77
8#define __GLIBC_LINUX_VERSION_CODE 3962888#define __GLIBC_LINUX_VERSION_CODE 397056
99
10#ifdef __NR_FAST_atomic_update10#ifdef __NR_FAST_atomic_update
11# define SYS_FAST_atomic_update __NR_FAST_atomic_update11# define SYS_FAST_atomic_update __NR_FAST_atomic_update
...@@ -703,6 +703,10 @@...@@ -703,6 +703,10 @@
703# define SYS_getxattr __NR_getxattr703# define SYS_getxattr __NR_getxattr
704#endif704#endif
705705
706#ifdef __NR_getxattrat
707# define SYS_getxattrat __NR_getxattrat
708#endif
709
706#ifdef __NR_getxgid710#ifdef __NR_getxgid
707# define SYS_getxgid __NR_getxgid711# define SYS_getxgid __NR_getxgid
708#endif712#endif
...@@ -875,6 +879,10 @@...@@ -875,6 +879,10 @@
875# define SYS_listxattr __NR_listxattr879# define SYS_listxattr __NR_listxattr
876#endif880#endif
877881
882#ifdef __NR_listxattrat
883# define SYS_listxattrat __NR_listxattrat
884#endif
885
878#ifdef __NR_llistxattr886#ifdef __NR_llistxattr
879# define SYS_llistxattr __NR_llistxattr887# define SYS_llistxattr __NR_llistxattr
880#endif888#endif
...@@ -1167,6 +1175,10 @@...@@ -1167,6 +1175,10 @@
1167# define SYS_open_tree __NR_open_tree1175# define SYS_open_tree __NR_open_tree
1168#endif1176#endif
11691177
1178#ifdef __NR_open_tree_attr
1179# define SYS_open_tree_attr __NR_open_tree_attr
1180#endif
1181
1170#ifdef __NR_openat1182#ifdef __NR_openat
1171# define SYS_openat __NR_openat1183# define SYS_openat __NR_openat
1172#endif1184#endif
...@@ -1839,6 +1851,10 @@...@@ -1839,6 +1851,10 @@
1839# define SYS_removexattr __NR_removexattr1851# define SYS_removexattr __NR_removexattr
1840#endif1852#endif
18411853
1854#ifdef __NR_removexattrat
1855# define SYS_removexattrat __NR_removexattrat
1856#endif
1857
1842#ifdef __NR_rename1858#ifdef __NR_rename
1843# define SYS_rename __NR_rename1859# define SYS_rename __NR_rename
1844#endif1860#endif
...@@ -2199,6 +2215,10 @@...@@ -2199,6 +2215,10 @@
2199# define SYS_setxattr __NR_setxattr2215# define SYS_setxattr __NR_setxattr
2200#endif2216#endif
22012217
2218#ifdef __NR_setxattrat
2219# define SYS_setxattrat __NR_setxattrat
2220#endif
2221
2202#ifdef __NR_sgetmask2222#ifdef __NR_sgetmask
2203# define SYS_sgetmask __NR_sgetmask2223# define SYS_sgetmask __NR_sgetmask
2204#endif2224#endif
lib/libc/include/generic-glibc/bits/termios-baud.h+52-25
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1/* termios baud rate selection definitions. Linux/generic version.1/* termios baud rate selection definitions. Universal version for sane speed_t.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.3 This file is part of the GNU C Library.
44
...@@ -20,29 +20,56 @@...@@ -20,29 +20,56 @@
20# error "Never include <bits/termios-baud.h> directly; use <termios.h> instead."20# error "Never include <bits/termios-baud.h> directly; use <termios.h> instead."
21#endif21#endif
2222
23#ifdef __USE_MISC23/* POSIX required baud rates */
24# define CBAUD 000000010017 /* Baud speed mask (not in POSIX). */24#define B0 0U /* Hang up or ispeed == ospeed */
25# define CBAUDEX 000000010000 /* Extra baud speed mask, included in CBAUD.25#define B50 50U
26 (not in POSIX). */26#define B75 75U
27# define CIBAUD 002003600000 /* Input baud rate (not used). */27#define B110 110U
28# define CMSPAR 010000000000 /* Mark or space (stick) parity. */28#define B134 134U /* Really 134.5 baud by POSIX spec */
29# define CRTSCTS 020000000000 /* Flow control. */29#define B150 150U
30#define B200 200U
31#define B300 300U
32#define B600 600U
33#define B1200 1200U
34#define B1800 1800U
35#define B2400 2400U
36#define B4800 4800U
37#define B9600 9600U
38#define B19200 19200U
39#define B38400 38400U
40#ifdef __USE_MISC
41# define EXTA B19200
42# define EXTB B38400
30#endif43#endif
3144
32/* Extra output baud rates (not in POSIX). */
33#define B57600 0010001
34#define B115200 0010002
35#define B230400 0010003
36#define B460800 0010004
37#define B500000 0010005
38#define B576000 0010006
39#define B921600 0010007
40#define B1000000 0010010
41#define B1152000 0010011
42#define B1500000 0010012
43#define B2000000 0010013
44#define B2500000 0010014
45#define B3000000 0010015
46#define B3500000 0010016
47#define B4000000 0010017
48#define __MAX_BAUD B4000000
\ No newline at end of file
45/* Other baud rates, "nonstandard" but known to be used */
46#define B7200 7200U
47#define B14400 14400U
48#define B28800 28800U
49#define B33600 33600U
50#define B57600 57600U
51#define B76800 76800U
52#define B115200 115200U
53#define B153600 153600U
54#define B230400 230400U
55#define B307200 307200U
56#define B460800 460800U
57#define B500000 500000U
58#define B576000 576000U
59#define B614400 614400U
60#define B921600 921600U
61#define B1000000 1000000U
62#define B1152000 1152000U
63#define B1500000 1500000U
64#define B2000000 2000000U
65#define B2500000 2500000U
66#define B3000000 3000000U
67#define B3500000 3500000U
68#define B4000000 4000000U
69#define B5000000 5000000U
70#define B10000000 10000000U
71
72#ifdef __USE_GNU
73#define SPEED_MAX 4294967295U /* maximum valid speed_t value */
74#endif
75#define __MAX_BAUD 4294967295U /* legacy alias for SPEED_MAX */
\ No newline at end of file
lib/libc/include/generic-glibc/bits/termios-c_cflag.h+3-1
...@@ -34,5 +34,7 @@...@@ -34,5 +34,7 @@
34#define CLOCAL 000400034#define CLOCAL 0004000
3535
36#ifdef __USE_MISC36#ifdef __USE_MISC
37# define ADDRB 0400000000037# define ADDRB 04000000000
38# define CMSPAR 010000000000 /* Mark or space (stick) parity. */
39# define CRTSCTS 020000000000 /* Flow control. */
38#endif40#endif
\ No newline at end of file
lib/libc/include/generic-glibc/bits/termios-cbaud.h created+47
...@@ -0,0 +1,47 @@
1/* termios baud rate selection definitions. Linux/generic version.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _TERMIOS_H
20# error "Never include <bits/termios-cbaud.h> directly; use <termios.h> instead."
21#endif
22
23#ifdef __USE_MISC
24# define CBAUD 000000010017 /* Baud speed mask (not in POSIX). */
25# define CBAUDEX 000000010000 /* Extra baud speed mask, included in CBAUD.
26 (not in POSIX). */
27# define CIBAUD 002003600000 /* Input baud rate. */
28# define IBSHIFT 16
29#endif
30
31/* Extra output baud rates (not in POSIX). */
32#define __BOTHER 0010000
33#define __B57600 0010001
34#define __B115200 0010002
35#define __B230400 0010003
36#define __B460800 0010004
37#define __B500000 0010005
38#define __B576000 0010006
39#define __B921600 0010007
40#define __B1000000 0010010
41#define __B1152000 0010011
42#define __B1500000 0010012
43#define __B2000000 0010013
44#define __B2500000 0010014
45#define __B3000000 0010015
46#define __B3500000 0010016
47#define __B4000000 0010017
\ No newline at end of file
lib/libc/include/generic-glibc/bits/termios-struct.h+9-2
...@@ -29,8 +29,15 @@ struct termios...@@ -29,8 +29,15 @@ struct termios
29 tcflag_t c_lflag; /* local mode flags */29 tcflag_t c_lflag; /* local mode flags */
30 cc_t c_line; /* line discipline */30 cc_t c_line; /* line discipline */
31 cc_t c_cc[NCCS]; /* control characters */31 cc_t c_cc[NCCS]; /* control characters */
32 speed_t c_ispeed; /* input speed */32 /* Input and output baud rates. */
33 speed_t c_ospeed; /* output speed */33 __extension__ union {
34 speed_t __ispeed;
35 speed_t c_ispeed;
36 };
34#define _HAVE_STRUCT_TERMIOS_C_ISPEED 137#define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
38 __extension__ union {
39 speed_t __ospeed;
40 speed_t c_ospeed;
41 };
35#define _HAVE_STRUCT_TERMIOS_C_OSPEED 142#define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
36 };43 };
\ No newline at end of file
lib/libc/include/generic-glibc/bits/termios.h+30-22
...@@ -24,35 +24,41 @@ typedef unsigned char cc_t;...@@ -24,35 +24,41 @@ typedef unsigned char cc_t;
24typedef unsigned int speed_t;24typedef unsigned int speed_t;
25typedef unsigned int tcflag_t;25typedef unsigned int tcflag_t;
2626
27#include <bits/termios-struct.h>27#ifdef _TERMIOS_H
28# include <bits/termios-struct.h>
29#endif
30
28#include <bits/termios-c_cc.h>31#include <bits/termios-c_cc.h>
29#include <bits/termios-c_iflag.h>32#include <bits/termios-c_iflag.h>
30#include <bits/termios-c_oflag.h>33#include <bits/termios-c_oflag.h>
3134
32/* c_cflag bit meaning */35/* c_cflag bit meaning */
33#define B0 0000000 /* hang up */36#include <bits/termios-c_cflag.h>
34#define B50 000000137
35#define B75 0000002
36#define B110 0000003
37#define B134 0000004
38#define B150 0000005
39#define B200 0000006
40#define B300 0000007
41#define B600 0000010
42#define B1200 0000011
43#define B1800 0000012
44#define B2400 0000013
45#define B4800 0000014
46#define B9600 0000015
47#define B19200 0000016
48#define B38400 0000017
49#ifdef __USE_MISC38#ifdef __USE_MISC
50# define EXTA B1920039#define __B0 0000000 /* hang up */
51# define EXTB B3840040#define __B50 0000001
41#define __B75 0000002
42#define __B110 0000003
43#define __B134 0000004
44#define __B150 0000005
45#define __B200 0000006
46#define __B300 0000007
47#define __B600 0000010
48#define __B1200 0000011
49#define __B1800 0000012
50#define __B2400 0000013
51#define __B4800 0000014
52#define __B9600 0000015
53#define __B19200 0000016
54#define __B38400 0000017
55#include <bits/termios-cbaud.h>
56
57# define __EXTA __B19200
58# define __EXTB __B38400
59# define BOTHER __BOTHER
52#endif60#endif
53#include <bits/termios-baud.h>
5461
55#include <bits/termios-c_cflag.h>
56#include <bits/termios-c_lflag.h>62#include <bits/termios-c_lflag.h>
5763
58#ifdef __USE_MISC64#ifdef __USE_MISC
...@@ -73,4 +79,6 @@ typedef unsigned int tcflag_t;...@@ -73,4 +79,6 @@ typedef unsigned int tcflag_t;
7379
74#include <bits/termios-tcflow.h>80#include <bits/termios-tcflow.h>
7581
76#include <bits/termios-misc.h>
\ No newline at end of file
82#include <bits/termios-misc.h>
83
84#include <bits/termios-baud.h>
\ No newline at end of file
lib/libc/include/generic-glibc/bits/types/struct_FILE.h+9-1
...@@ -32,6 +32,7 @@...@@ -32,6 +32,7 @@
32#endif32#endif
3333
34#include <bits/types.h>34#include <bits/types.h>
35#include <bits/wordsize.h>
3536
36struct _IO_FILE;37struct _IO_FILE;
37struct _IO_marker;38struct _IO_marker;
...@@ -97,8 +98,15 @@ struct _IO_FILE_complete...@@ -97,8 +98,15 @@ struct _IO_FILE_complete
97 void *_freeres_buf;98 void *_freeres_buf;
98 struct _IO_FILE **_prevchain;99 struct _IO_FILE **_prevchain;
99 int _mode;100 int _mode;
101#if __WORDSIZE == 64
102 int _unused3;
103#endif
104 __uint64_t _total_written;
105#if __WORDSIZE == 32
106 int _unused3;
107#endif
100 /* Make sure we don't get into trouble again. */108 /* Make sure we don't get into trouble again. */
101 char _unused2[15 * sizeof (int) - 5 * sizeof (void *)];109 char _unused2[12 * sizeof (int) - 5 * sizeof (void *)];
102};110};
103111
104/* These macros are used by bits/stdio.h and internal headers. */112/* These macros are used by bits/stdio.h and internal headers. */
lib/libc/include/generic-glibc/dlfcn.h+8-2
...@@ -217,15 +217,21 @@ struct dl_find_object...@@ -217,15 +217,21 @@ struct dl_find_object
217 int dlfo_eh_count; /* Number of exception handling entries. */217 int dlfo_eh_count; /* Number of exception handling entries. */
218 unsigned int __dlfo_eh_count_pad;218 unsigned int __dlfo_eh_count_pad;
219# endif219# endif
220 __extension__ unsigned long long int __dflo_reserved[7];220 void *dlfo_sframe; /* SFrame stack trace data of the object. */
221#if __WORDSIZE == 32
222 unsigned int __dlfo_sframe_pad;
223#endif
224 __extension__ unsigned long long int __dlfo_reserved[6];
221};225};
222226
223/* If ADDRESS is found in an object, fill in *RESULT and return 0.227/* If ADDRESS is found in an object, fill in *RESULT and return 0.
224 Otherwise, return -1. */228 Otherwise, return -1. */
225int _dl_find_object (void *__address, struct dl_find_object *__result) __THROW;229int _dl_find_object (void *__address, struct dl_find_object *__result) __THROW;
226230
227#endif /* __USE_GNU */231/* SFrame stack trace data is valid. */
232#define DLFO_FLAG_SFRAME (1ULL << 0)
228233
234#endif /* __USE_GNU */
229235
230__END_DECLS236__END_DECLS
231237
lib/libc/include/generic-glibc/elf.h+11-22
...@@ -837,12 +837,15 @@ typedef struct...@@ -837,12 +837,15 @@ typedef struct
837#define NT_ARM_ZT 0x40d /* ARM SME ZT registers. */837#define NT_ARM_ZT 0x40d /* ARM SME ZT registers. */
838#define NT_ARM_FPMR 0x40e /* ARM floating point mode register. */838#define NT_ARM_FPMR 0x40e /* ARM floating point mode register. */
839#define NT_ARM_POE 0x40f /* ARM POE registers. */839#define NT_ARM_POE 0x40f /* ARM POE registers. */
840#define NT_ARM_GCS 0x410 /* ARM GCS state. */
840#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */841#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */
841#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */842#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */
842#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */843#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */
843#define NT_MIPS_MSA 0x802 /* MIPS SIMD registers. */844#define NT_MIPS_MSA 0x802 /* MIPS SIMD registers. */
844#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */845#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */
845#define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */846#define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */
847#define NT_RISCV_TAGGED_ADDR_CTRL 0x902 /* RISC-V tagged
848 address control */
846#define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers. */849#define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers. */
847#define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and850#define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and
848 status registers. */851 status registers. */
...@@ -2906,19 +2909,6 @@ enum...@@ -2906,19 +2909,6 @@ enum
29062909
2907#define R_AARCH64_NONE 0 /* No relocation. */2910#define R_AARCH64_NONE 0 /* No relocation. */
29082911
2909/* ILP32 AArch64 relocs. */
2910#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */
2911#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */
2912#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */
2913#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */
2914#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */
2915#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */
2916#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */
2917#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */
2918#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */
2919#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */
2920
2921/* LP64 AArch64 relocs. */
2922#define R_AARCH64_ABS64 257 /* Direct 64 bit. */2912#define R_AARCH64_ABS64 257 /* Direct 64 bit. */
2923#define R_AARCH64_ABS32 258 /* Direct 32 bit. */2913#define R_AARCH64_ABS32 258 /* Direct 32 bit. */
2924#define R_AARCH64_ABS16 259 /* Direct 16-bit. */2914#define R_AARCH64_ABS16 259 /* Direct 16-bit. */
...@@ -4091,6 +4081,7 @@ enum...@@ -4091,6 +4081,7 @@ enum
4091#define R_RISCV_TLS_DTPREL64 94081#define R_RISCV_TLS_DTPREL64 9
4092#define R_RISCV_TLS_TPREL32 104082#define R_RISCV_TLS_TPREL32 10
4093#define R_RISCV_TLS_TPREL64 114083#define R_RISCV_TLS_TPREL64 11
4084#define R_RISCV_TLSDESC 12
4094#define R_RISCV_BRANCH 164085#define R_RISCV_BRANCH 16
4095#define R_RISCV_JAL 174086#define R_RISCV_JAL 17
4096#define R_RISCV_CALL 184087#define R_RISCV_CALL 18
...@@ -4116,16 +4107,10 @@ enum...@@ -4116,16 +4107,10 @@ enum
4116#define R_RISCV_SUB16 384107#define R_RISCV_SUB16 38
4117#define R_RISCV_SUB32 394108#define R_RISCV_SUB32 39
4118#define R_RISCV_SUB64 404109#define R_RISCV_SUB64 40
4119#define R_RISCV_GNU_VTINHERIT 414110#define R_RISCV_GOT32_PCREL 41
4120#define R_RISCV_GNU_VTENTRY 42
4121#define R_RISCV_ALIGN 434111#define R_RISCV_ALIGN 43
4122#define R_RISCV_RVC_BRANCH 444112#define R_RISCV_RVC_BRANCH 44
4123#define R_RISCV_RVC_JUMP 454113#define R_RISCV_RVC_JUMP 45
4124#define R_RISCV_RVC_LUI 46
4125#define R_RISCV_GPREL_I 47
4126#define R_RISCV_GPREL_S 48
4127#define R_RISCV_TPREL_I 49
4128#define R_RISCV_TPREL_S 50
4129#define R_RISCV_RELAX 514114#define R_RISCV_RELAX 51
4130#define R_RISCV_SUB6 524115#define R_RISCV_SUB6 52
4131#define R_RISCV_SET6 534116#define R_RISCV_SET6 53
...@@ -4137,8 +4122,12 @@ enum...@@ -4137,8 +4122,12 @@ enum
4137#define R_RISCV_PLT32 594122#define R_RISCV_PLT32 59
4138#define R_RISCV_SET_ULEB128 604123#define R_RISCV_SET_ULEB128 60
4139#define R_RISCV_SUB_ULEB128 614124#define R_RISCV_SUB_ULEB128 61
4125#define R_RISCV_TLSDESC_HI20 62
4126#define R_RISCV_TLSDESC_LOAD_LO12 63
4127#define R_RISCV_TLSDESC_ADD_LO12 64
4128#define R_RISCV_TLSDESC_CALL 65
41404129
4141#define R_RISCV_NUM 624130#define R_RISCV_NUM 66
41424131
4143/* RISC-V specific values for the st_other field. */4132/* RISC-V specific values for the st_other field. */
4144#define STO_RISCV_VARIANT_CC 0x80 /* Function uses variant calling4133#define STO_RISCV_VARIANT_CC 0x80 /* Function uses variant calling
...@@ -4147,7 +4136,7 @@ enum...@@ -4147,7 +4136,7 @@ enum
4147/* RISC-V specific values for the sh_type field. */4136/* RISC-V specific values for the sh_type field. */
4148#define SHT_RISCV_ATTRIBUTES (SHT_LOPROC + 3)4137#define SHT_RISCV_ATTRIBUTES (SHT_LOPROC + 3)
41494138
4150/* RISC-V specific values for the p_type field. */4139/* RISC-V specific values for the p_type field (deprecated). */
4151#define PT_RISCV_ATTRIBUTES (PT_LOPROC + 3)4140#define PT_RISCV_ATTRIBUTES (PT_LOPROC + 3)
41524141
4153/* RISC-V specific values for the d_tag field. */4142/* RISC-V specific values for the d_tag field. */
lib/libc/include/generic-glibc/fcntl.h+6-5
...@@ -168,7 +168,7 @@ typedef __pid_t pid_t;...@@ -168,7 +168,7 @@ typedef __pid_t pid_t;
168#endif168#endif
169169
170170
171/* fcntl was a simple symbol until glibc 2.27 inclusive.171/* zig patch: fcntl was a simple symbol until glibc 2.27 inclusive.
172 * glibc 2.28 onwards converted it to a macro when compiled with172 * glibc 2.28 onwards converted it to a macro when compiled with
173 * USE_LARGEFILE64. */173 * USE_LARGEFILE64. */
174#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2174#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2
...@@ -289,16 +289,17 @@ extern int creat64 (const char *__file, mode_t __mode) __nonnull ((1));...@@ -289,16 +289,17 @@ extern int creat64 (const char *__file, mode_t __mode) __nonnull ((1));
289# define F_TEST 3 /* Test a region for other processes locks. */289# define F_TEST 3 /* Test a region for other processes locks. */
290290
291# ifndef __USE_FILE_OFFSET64291# ifndef __USE_FILE_OFFSET64
292extern int lockf (int __fd, int __cmd, off_t __len);292extern int lockf (int __fd, int __cmd, off_t __len) __wur;
293# else293# else
294# ifdef __REDIRECT294# ifdef __REDIRECT
295extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len), lockf64);295extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len),
296 lockf64) __wur;
296# else297# else
297# define lockf lockf64298# define lockf lockf64
298# endif299# endif
299# endif300# endif
300# ifdef __USE_LARGEFILE64301# ifdef __USE_LARGEFILE64
301extern int lockf64 (int __fd, int __cmd, off64_t __len);302extern int lockf64 (int __fd, int __cmd, off64_t __len) __wur;
302# endif303# endif
303#endif304#endif
304305
...@@ -351,4 +352,4 @@ extern int posix_fallocate64 (int __fd, off64_t __offset, off64_t __len);...@@ -351,4 +352,4 @@ extern int posix_fallocate64 (int __fd, off64_t __offset, off64_t __len);
351352
352__END_DECLS353__END_DECLS
353354
354#endif /* fcntl.h */
\ No newline at end of file
355#endif /* fcntl.h */
lib/libc/include/generic-glibc/features.h+3-3
...@@ -491,7 +491,7 @@...@@ -491,7 +491,7 @@
491 or without -D_GNU_SOURCE, but -std=c89 -D_GNU_SOURCE will have the491 or without -D_GNU_SOURCE, but -std=c89 -D_GNU_SOURCE will have the
492 old extension. */492 old extension. */
493#if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 7)493#if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 7)
494/* support for ISOC99 was added in glibc-2.7 */494/* zig patch: support for ISOC99 was added in glibc-2.7 */
495# define __GLIBC_USE_DEPRECATED_SCANF 1495# define __GLIBC_USE_DEPRECATED_SCANF 1
496#elif (defined __USE_GNU \496#elif (defined __USE_GNU \
497 && (defined __cplusplus \497 && (defined __cplusplus \
...@@ -503,7 +503,7 @@...@@ -503,7 +503,7 @@
503#endif503#endif
504504
505505
506/* support for ISO C2X strtol was added in 2.38506/* zig patch: support for ISO C2X strtol was added in 2.38
507 * glibc commit 64924422a99690d147a166b4de3103f3bf3eaf6c507 * glibc commit 64924422a99690d147a166b4de3103f3bf3eaf6c
508 */508 */
509#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2509#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2
...@@ -564,4 +564,4 @@...@@ -564,4 +564,4 @@
564#include <gnu/stubs.h>564#include <gnu/stubs.h>
565565
566566
567#endif /* features.h */
\ No newline at end of file
567#endif /* features.h */
lib/libc/include/generic-glibc/glob.h+2-1
...@@ -195,7 +195,8 @@ extern void globfree64 (glob64_t *__pglob) __THROW;...@@ -195,7 +195,8 @@ extern void globfree64 (glob64_t *__pglob) __THROW;
195195
196 This function is not part of the interface specified by POSIX.2196 This function is not part of the interface specified by POSIX.2
197 but several programs want to use it. */197 but several programs want to use it. */
198extern int glob_pattern_p (const char *__pattern, int __quote) __THROW;198extern int glob_pattern_p (const char *__pattern, int __quote) __THROW
199 __nonnull ((1));
199#endif200#endif
200201
201__END_DECLS202__END_DECLS
lib/libc/include/generic-glibc/inttypes.h+5
...@@ -350,6 +350,11 @@ typedef struct...@@ -350,6 +350,11 @@ typedef struct
350/* Compute absolute value of N. */350/* Compute absolute value of N. */
351extern intmax_t imaxabs (intmax_t __n) __THROW __attribute__ ((__const__));351extern intmax_t imaxabs (intmax_t __n) __THROW __attribute__ ((__const__));
352352
353
354#if __GLIBC_USE (ISOC2Y)
355extern uintmax_t uimaxabs (intmax_t __n) __THROW __attribute__ ((__const__));
356#endif
357
353/* Return the `imaxdiv_t' representation of the value of NUMER over DENOM. */358/* Return the `imaxdiv_t' representation of the value of NUMER over DENOM. */
354extern imaxdiv_t imaxdiv (intmax_t __numer, intmax_t __denom)359extern imaxdiv_t imaxdiv (intmax_t __numer, intmax_t __denom)
355 __THROW __attribute__ ((__const__));360 __THROW __attribute__ ((__const__));
lib/libc/include/generic-glibc/malloc.h+2-2
...@@ -52,7 +52,7 @@ extern void *realloc (void *__ptr, size_t __size)...@@ -52,7 +52,7 @@ extern void *realloc (void *__ptr, size_t __size)
52__THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));52__THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
5353
54/*54/*
55 * reallocarray introduced in glibc 2.2655 * zig patch: reallocarray introduced in glibc 2.26
56 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da56 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da
57 */57 */
58#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26) || __GLIBC__ > 258#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26) || __GLIBC__ > 2
...@@ -164,4 +164,4 @@ extern void malloc_stats (void) __THROW;...@@ -164,4 +164,4 @@ extern void malloc_stats (void) __THROW;
164extern int malloc_info (int __options, FILE *__fp) __THROW;164extern int malloc_info (int __options, FILE *__fp) __THROW;
165165
166__END_DECLS166__END_DECLS
167#endif /* malloc.h */
\ No newline at end of file
167#endif /* malloc.h */
lib/libc/include/generic-glibc/netinet/tcp.h+3
...@@ -212,6 +212,9 @@ enum...@@ -212,6 +212,9 @@ enum
212# define TCPI_OPT_ECN 8 /* ECN was negotiated at TCP session init */212# define TCPI_OPT_ECN 8 /* ECN was negotiated at TCP session init */
213# define TCPI_OPT_ECN_SEEN 16 /* we received at least one packet with ECT */213# define TCPI_OPT_ECN_SEEN 16 /* we received at least one packet with ECT */
214# define TCPI_OPT_SYN_DATA 32 /* SYN-ACK acked data in SYN sent or rcvd */214# define TCPI_OPT_SYN_DATA 32 /* SYN-ACK acked data in SYN sent or rcvd */
215# define TCPI_OPT_USEC_TS 64 /* usec timestamps */
216# define TCPI_OPT_TFO_CHILD 128 /* child from a Fast Open option on SYN */
217
215218
216/* Values for tcpi_state. */219/* Values for tcpi_state. */
217enum tcp_ca_state220enum tcp_ca_state
lib/libc/include/generic-glibc/pthread.h+5
...@@ -1317,6 +1317,11 @@ extern int pthread_getcpuclockid (pthread_t __thread_id,...@@ -1317,6 +1317,11 @@ extern int pthread_getcpuclockid (pthread_t __thread_id,
1317 __THROW __nonnull ((2));1317 __THROW __nonnull ((2));
1318#endif1318#endif
13191319
1320#ifdef __USE_GNU
1321/* Return the Linux TID for THREAD_ID. Returns -1 on failure. */
1322extern pid_t pthread_gettid_np (pthread_t __thread_id);
1323#endif
1324
13201325
1321/* Install handlers to be called when a new process is created with FORK.1326/* Install handlers to be called when a new process is created with FORK.
1322 The PREPARE handler is called in the parent process just before performing1327 The PREPARE handler is called in the parent process just before performing
lib/libc/include/generic-glibc/resolv.h+2-2
...@@ -171,7 +171,7 @@ __END_DECLS...@@ -171,7 +171,7 @@ __END_DECLS
171#define res_init __res_init171#define res_init __res_init
172#define res_isourserver __res_isourserver172#define res_isourserver __res_isourserver
173173
174/* In glibc 2.33 and earlier res_search, res_nsearch, res_query, res_nquery,174/* zig patch: In glibc 2.33 and earlier res_search, res_nsearch, res_query, res_nquery,
175 * res_querydomain, res_nquerydomain, dn_skipname, dn_comp, dn_expand were175 * res_querydomain, res_nquerydomain, dn_skipname, dn_comp, dn_expand were
176 * #define'd to __res_search, __res_nsearch, etc. glibc 2.34 onwards removes176 * #define'd to __res_search, __res_nsearch, etc. glibc 2.34 onwards removes
177 * the macros and exposes the symbols directly. New glibc exposes compat177 * the macros and exposes the symbols directly. New glibc exposes compat
...@@ -336,4 +336,4 @@ void res_nclose (res_state) __THROW;...@@ -336,4 +336,4 @@ void res_nclose (res_state) __THROW;
336336
337__END_DECLS337__END_DECLS
338338
339#endif /* !_RESOLV_H_ */
\ No newline at end of file
339#endif /* !_RESOLV_H_ */
lib/libc/include/generic-glibc/stdio.h+3-3
...@@ -168,8 +168,11 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,...@@ -168,8 +168,11 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
168#ifdef __USE_GNU168#ifdef __USE_GNU
169/* Flags for renameat2. */169/* Flags for renameat2. */
170# define RENAME_NOREPLACE (1 << 0)170# define RENAME_NOREPLACE (1 << 0)
171# define AT_RENAME_NOREPLACE RENAME_NOREPLACE
171# define RENAME_EXCHANGE (1 << 1)172# define RENAME_EXCHANGE (1 << 1)
173# define AT_RENAME_EXCHANGE RENAME_EXCHANGE
172# define RENAME_WHITEOUT (1 << 2)174# define RENAME_WHITEOUT (1 << 2)
175# define AT_RENAME_WHITEOUT RENAME_WHITEOUT
173176
174/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with177/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
175 additional flags. */178 additional flags. */
...@@ -604,9 +607,6 @@ extern int fgetc_unlocked (FILE *__stream) __nonnull ((1));...@@ -604,9 +607,6 @@ extern int fgetc_unlocked (FILE *__stream) __nonnull ((1));
604/* Write a character to STREAM.607/* Write a character to STREAM.
605608
606 These functions are possible cancellation points and therefore not609 These functions are possible cancellation points and therefore not
607 marked with __THROW.
608
609 These functions is a possible cancellation point and therefore not
610 marked with __THROW. */610 marked with __THROW. */
611extern int fputc (int __c, FILE *__stream) __nonnull ((2));611extern int fputc (int __c, FILE *__stream) __nonnull ((2));
612extern int putc (int __c, FILE *__stream) __nonnull ((2));612extern int putc (int __c, FILE *__stream) __nonnull ((2));
lib/libc/include/generic-glibc/stdio_ext.h+9-9
...@@ -43,43 +43,43 @@ __BEGIN_DECLS...@@ -43,43 +43,43 @@ __BEGIN_DECLS
4343
44/* Return the size of the buffer of FP in bytes currently in use by44/* Return the size of the buffer of FP in bytes currently in use by
45 the given stream. */45 the given stream. */
46extern size_t __fbufsize (FILE *__fp) __THROW;46extern size_t __fbufsize (FILE *__fp) __THROW __nonnull ((1));
4747
4848
49/* Return non-zero value iff the stream FP is opened readonly, or if the49/* Return non-zero value iff the stream FP is opened readonly, or if the
50 last operation on the stream was a read operation. */50 last operation on the stream was a read operation. */
51extern int __freading (FILE *__fp) __THROW;51extern int __freading (FILE *__fp) __THROW __nonnull ((1));
5252
53/* Return non-zero value iff the stream FP is opened write-only or53/* Return non-zero value iff the stream FP is opened write-only or
54 append-only, or if the last operation on the stream was a write54 append-only, or if the last operation on the stream was a write
55 operation. */55 operation. */
56extern int __fwriting (FILE *__fp) __THROW;56extern int __fwriting (FILE *__fp) __THROW __nonnull ((1));
5757
5858
59/* Return non-zero value iff stream FP is not opened write-only or59/* Return non-zero value iff stream FP is not opened write-only or
60 append-only. */60 append-only. */
61extern int __freadable (FILE *__fp) __THROW;61extern int __freadable (FILE *__fp) __THROW __nonnull ((1));
6262
63/* Return non-zero value iff stream FP is not opened read-only. */63/* Return non-zero value iff stream FP is not opened read-only. */
64extern int __fwritable (FILE *__fp) __THROW;64extern int __fwritable (FILE *__fp) __THROW __nonnull ((1));
6565
6666
67/* Return non-zero value iff the stream FP is line-buffered. */67/* Return non-zero value iff the stream FP is line-buffered. */
68extern int __flbf (FILE *__fp) __THROW;68extern int __flbf (FILE *__fp) __THROW __nonnull ((1));
6969
7070
71/* Discard all pending buffered I/O on the stream FP. */71/* Discard all pending buffered I/O on the stream FP. */
72extern void __fpurge (FILE *__fp) __THROW;72extern void __fpurge (FILE *__fp) __THROW __nonnull ((1));
7373
74/* Return amount of output in bytes pending on a stream FP. */74/* Return amount of output in bytes pending on a stream FP. */
75extern size_t __fpending (FILE *__fp) __THROW;75extern size_t __fpending (FILE *__fp) __THROW __nonnull ((1));
7676
77/* Flush all line-buffered files. */77/* Flush all line-buffered files. */
78extern void _flushlbf (void);78extern void _flushlbf (void);
7979
8080
81/* Set locking status of stream FP to TYPE. */81/* Set locking status of stream FP to TYPE. */
82extern int __fsetlocking (FILE *__fp, int __type) __THROW;82extern int __fsetlocking (FILE *__fp, int __type) __THROW __nonnull ((1));
8383
84__END_DECLS84__END_DECLS
8585
lib/libc/include/generic-glibc/stdlib.h+9-3
...@@ -654,7 +654,7 @@ extern int lcong48_r (unsigned short int __param[7],...@@ -654,7 +654,7 @@ extern int lcong48_r (unsigned short int __param[7],
654 __THROW __nonnull ((1, 2));654 __THROW __nonnull ((1, 2));
655655
656/*656/*
657 * arc4random* symbols introduced in glibc 2.36:657 * zig patch: arc4random* symbols introduced in glibc 2.36:
658 * https://sourceware.org/git/?p=glibc.git;a=blob;f=NEWS;h=8420a65cd06874ee09518366b8fba746a557212a;hb=6f4e0fcfa2d2b0915816a3a3a1d48b4763a7dee2658 * https://sourceware.org/git/?p=glibc.git;a=blob;f=NEWS;h=8420a65cd06874ee09518366b8fba746a557212a;hb=6f4e0fcfa2d2b0915816a3a3a1d48b4763a7dee2
659 */659 */
660# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 36) || __GLIBC__ > 2660# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 36) || __GLIBC__ > 2
...@@ -693,7 +693,7 @@ extern void *realloc (void *__ptr, size_t __size)...@@ -693,7 +693,7 @@ extern void *realloc (void *__ptr, size_t __size)
693extern void free (void *__ptr) __THROW;693extern void free (void *__ptr) __THROW;
694694
695/*695/*
696 * reallocarray introduced in glibc 2.26696 * zig patch: reallocarray introduced in glibc 2.26
697 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da697 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da
698 */698 */
699#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26) || __GLIBC__ > 2699#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26) || __GLIBC__ > 2
...@@ -997,6 +997,12 @@ __extension__ extern long long int llabs (long long int __x)...@@ -997,6 +997,12 @@ __extension__ extern long long int llabs (long long int __x)
997 __THROW __attribute__ ((__const__)) __wur;997 __THROW __attribute__ ((__const__)) __wur;
998#endif998#endif
999999
1000#if __GLIBC_USE (ISOC2Y)
1001extern unsigned int uabs (int __x) __THROW __attribute__ ((__const__)) __wur;
1002extern unsigned long int ulabs (long int __x) __THROW __attribute__ ((__const__)) __wur;
1003__extension__ extern unsigned long long int ullabs (long long int __x)
1004 __THROW __attribute__ ((__const__)) __wur;
1005#endif
10001006
1001/* Return the `div_t', `ldiv_t' or `lldiv_t' representation1007/* Return the `div_t', `ldiv_t' or `lldiv_t' representation
1002 of the value of NUMER over DENOM. */1008 of the value of NUMER over DENOM. */
...@@ -1178,4 +1184,4 @@ extern int ttyslot (void) __THROW;...@@ -1178,4 +1184,4 @@ extern int ttyslot (void) __THROW;
11781184
1179__END_DECLS1185__END_DECLS
11801186
1181#endif /* stdlib.h */
\ No newline at end of file
1187#endif /* stdlib.h */
lib/libc/include/generic-glibc/string.h+2-2
...@@ -502,7 +502,7 @@ extern char *stpncpy (char *__restrict __dest,...@@ -502,7 +502,7 @@ extern char *stpncpy (char *__restrict __dest,
502#endif502#endif
503503
504/*504/*
505 * strlcpy and strlcat introduced in glibc 2.38505 * zig patch: strlcpy and strlcat introduced in glibc 2.38
506 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da506 * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da
507 */507 */
508#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2508#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2
...@@ -557,4 +557,4 @@ extern char *basename (const char *__filename) __THROW __nonnull ((1));...@@ -557,4 +557,4 @@ extern char *basename (const char *__filename) __THROW __nonnull ((1));
557557
558__END_DECLS558__END_DECLS
559559
560#endif /* string.h */
\ No newline at end of file
560#endif /* string.h */
lib/libc/include/generic-glibc/sys/hwprobe.h+29-11
...@@ -21,6 +21,7 @@...@@ -21,6 +21,7 @@
21#define _SYS_HWPROBE_H 121#define _SYS_HWPROBE_H 1
2222
23#include <features.h>23#include <features.h>
24#include <sched.h>
24#include <stddef.h>25#include <stddef.h>
25#include <errno.h>26#include <errno.h>
26#ifdef __has_include27#ifdef __has_include
...@@ -63,22 +64,39 @@ struct riscv_hwprobe {...@@ -63,22 +64,39 @@ struct riscv_hwprobe {
6364
64__BEGIN_DECLS65__BEGIN_DECLS
6566
66extern int __riscv_hwprobe (struct riscv_hwprobe *__pairs, size_t __pair_count,67#if defined __cplusplus || !__GNUC_PREREQ (2, 7)
67 size_t __cpu_count, unsigned long int *__cpus,68# define __RISCV_HWPROBE_CPUS_TYPE cpu_set_t *
69#else
70/* The fourth argument to __riscv_hwprobe should be a null pointer or a
71 pointer to a cpu_set_t (either the fixed-size type or allocated with
72 CPU_ALLOC). However, early versions of this header file used the
73 argument type unsigned long int *. The transparent union allows
74 the argument to be either cpu_set_t * or unsigned long int * for
75 compatibility. The older header file requiring unsigned long int *
76 can be identified by the lack of the __RISCV_HWPROBE_CPUS_TYPE macro.
77 In C++ and with compilers that do not support transparent unions, the
78 argument type must be cpu_set_t *. */
79typedef union {
80 cpu_set_t *__cs;
81 unsigned long int *__ul;
82} __RISCV_HWPROBE_CPUS_TYPE __attribute__ ((__transparent_union__));
83# define __RISCV_HWPROBE_CPUS_TYPE __RISCV_HWPROBE_CPUS_TYPE
84#endif
85
86extern int __riscv_hwprobe (struct riscv_hwprobe *__pairs,
87 size_t __pair_count, size_t __cpusetsize,
88 __RISCV_HWPROBE_CPUS_TYPE __cpus,
68 unsigned int __flags)89 unsigned int __flags)
69 __nonnull ((1)) __wur90 __THROW __nonnull ((1)) __attr_access ((__read_write__, 1, 2));
70 __fortified_attr_access (__read_write__, 1, 2)
71 __fortified_attr_access (__read_only__, 4, 3);
7291
73/* A pointer to the __riscv_hwprobe vDSO function is passed as the second92/* A pointer to the __riscv_hwprobe function is passed as the second
74 argument to ifunc selector routines. Include a function pointer type for93 argument to ifunc selector routines. Include a function pointer type for
75 convenience in calling the function in those settings. */94 convenience in calling the function in those settings. */
76typedef int (*__riscv_hwprobe_t) (struct riscv_hwprobe *__pairs, size_t __pair_count,95typedef int (*__riscv_hwprobe_t) (struct riscv_hwprobe *__pairs,
77 size_t __cpu_count, unsigned long int *__cpus,96 size_t __pair_count, size_t __cpusetsize,
97 __RISCV_HWPROBE_CPUS_TYPE __cpus,
78 unsigned int __flags)98 unsigned int __flags)
79 __nonnull ((1)) __wur99 __nonnull ((1)) __attr_access ((__read_write__, 1, 2));
80 __fortified_attr_access (__read_write__, 1, 2)
81 __fortified_attr_access (__read_only__, 4, 3);
82100
83/* Helper function usable from ifunc selectors that probes a single key. */101/* Helper function usable from ifunc selectors that probes a single key. */
84static __inline int102static __inline int
lib/libc/include/generic-glibc/sys/ifunc.h+59-6
...@@ -19,24 +19,77 @@...@@ -19,24 +19,77 @@
19#ifndef _SYS_IFUNC_H19#ifndef _SYS_IFUNC_H
20#define _SYS_IFUNC_H20#define _SYS_IFUNC_H
2121
22#include <sys/cdefs.h>
23
22/* A second argument is passed to the ifunc resolver. */24/* A second argument is passed to the ifunc resolver. */
23#define _IFUNC_ARG_HWCAP (1ULL << 62)25#define _IFUNC_ARG_HWCAP (1ULL << 62)
2426
25/* The prototype of a gnu indirect function resolver on AArch64 is27/* Maximum number of HWCAP elements that are currently supported. */
28#define _IFUNC_HWCAP_MAX 4
29
30/* The prototype of a GNU indirect function resolver on AArch64 is
31
32 ElfW(Addr) ifunc_resolver (uint64_t, const uint64_t *);
33
34 The following prototype is also compatible:
2635
27 ElfW(Addr) ifunc_resolver (uint64_t, const __ifunc_arg_t *);36 ElfW(Addr) ifunc_resolver (uint64_t, const __ifunc_arg_t *);
2837
29 the first argument should have the _IFUNC_ARG_HWCAP bit set and38 The first argument might have the _IFUNC_ARG_HWCAP bit set and
30 the remaining bits should match the AT_HWCAP settings. */39 the remaining bits should match the AT_HWCAP settings.
40
41 If the _IFUNC_ARG_HWCAP bit is set in the first argument, then
42 the second argument is passed to the resolver function. In
43 this case, the second argument is a const pointer to a buffer
44 that allows to access all available HWCAP elements.
45
46 This buffer has its size in bytes at offset 0. The HWCAP elements
47 are available at offsets 8, 16, 24, 32... respectively for AT_HWCAP,
48 AT_HWCAP2, AT_HWCAP3, AT_HWCAP4... (these offsets are multiples of
49 sizeof (unsigned long)).
50
51 Indirect function resolvers must check availability of HWCAP
52 elements at runtime before accessing them using the size of the
53 buffer. */
3154
32/* Second argument to an ifunc resolver. */
33struct __ifunc_arg_t55struct __ifunc_arg_t
34{56{
35 unsigned long _size; /* Size of the struct, so it can grow. */57 unsigned long _size; /* Size of the struct, so it can grow. */
36 unsigned long _hwcap;58 unsigned long _hwcap;
37 unsigned long _hwcap2;59 unsigned long _hwcap2; /* End of 1st published struct. */
60 unsigned long _hwcap3;
61 unsigned long _hwcap4; /* End of 2nd published struct. */
38};62};
3963
40typedef struct __ifunc_arg_t __ifunc_arg_t;64typedef struct __ifunc_arg_t __ifunc_arg_t;
4165
66/* Constants for IDs of HWCAP elements to be used with the
67 __ifunc_hwcap function below. */
68enum
69{
70 _IFUNC_ARG_AT_HWCAP = 1,
71 _IFUNC_ARG_AT_HWCAP2 = 2,
72 _IFUNC_ARG_AT_HWCAP3 = 3,
73 _IFUNC_ARG_AT_HWCAP4 = 4,
74};
75
76/* A helper function to obtain HWCAP element by its ID from the
77 parameters ARG0 and ARG1 passed to the ifunc resolver. Note that
78 ID 1 corresponds to AT_HWCAP, ID 2 corresponds to AT_HWCAP2, etc.
79 If there is no element available for the requested ID then 0 is
80 returned. If ID doesn't much any supported AT_HWCAP{,2,...} value,
81 then 0 is also returned. */
82static __inline unsigned long __attribute__ ((unused, always_inline))
83__ifunc_hwcap (unsigned long __id,
84 unsigned long __arg0, const unsigned long *__arg1)
85{
86 if (__glibc_likely (__arg0 & _IFUNC_ARG_HWCAP))
87 {
88 const unsigned long size = __arg1[0];
89 const unsigned long offset = __id * sizeof (unsigned long);
90 return offset < size && __id > 0 ? __arg1[__id] : 0;
91 }
92 return __id == 1 ? __arg0 : 0;
93}
94
42#endif95#endif
\ No newline at end of file
lib/libc/include/generic-glibc/sys/mount.h+1-1
...@@ -121,7 +121,7 @@ enum...@@ -121,7 +121,7 @@ enum
121 MS_ACTIVE = 1 << 30,121 MS_ACTIVE = 1 << 30,
122#define MS_ACTIVE MS_ACTIVE122#define MS_ACTIVE MS_ACTIVE
123#undef MS_NOUSER123#undef MS_NOUSER
124 MS_NOUSER = 1 << 31124 MS_NOUSER = 1U << 31
125#define MS_NOUSER MS_NOUSER125#define MS_NOUSER MS_NOUSER
126};126};
127127
lib/libc/include/generic-glibc/sys/ttychars.h-4
...@@ -54,8 +54,4 @@ struct ttychars {...@@ -54,8 +54,4 @@ struct ttychars {
54 char tc_lnextc; /* literal next character */54 char tc_lnextc; /* literal next character */
55};55};
5656
57#ifdef __USE_OLD_TTY
58#include <sys/ttydefaults.h> /* to pick up character defaults */
59#endif
60
61#endif /* sys/ttychars.h */57#endif /* sys/ttychars.h */
\ No newline at end of file
lib/libc/include/generic-glibc/termio.h deleted-6
...@@ -1,6 +0,0 @@
1/* Compatible <termio.h> for old `struct termio' ioctl interface.
2 This is obsolete; use the POSIX.1 `struct termios' interface
3 defined in <termios.h> instead. */
4
5#include <termios.h>
6#include <sys/ioctl.h>
\ No newline at end of file
lib/libc/include/generic-glibc/termios.h+20
...@@ -61,6 +61,26 @@ extern int cfsetispeed (struct termios *__termios_p, speed_t __speed) __THROW;...@@ -61,6 +61,26 @@ extern int cfsetispeed (struct termios *__termios_p, speed_t __speed) __THROW;
61extern int cfsetspeed (struct termios *__termios_p, speed_t __speed) __THROW;61extern int cfsetspeed (struct termios *__termios_p, speed_t __speed) __THROW;
62#endif62#endif
6363
64#ifdef __USE_GNU
65/* Interfaces that are explicitly numeric representations of baud rates */
66typedef speed_t baud_t;
67#define BAUD_MAX SPEED_MAX
68
69/* Return the output baud rate stored in *TERMIOS_P. */
70extern baud_t cfgetobaud (const struct termios *__termios_p) __THROW;
71
72/* Return the input baud rate stored in *TERMIOS_P. */
73extern baud_t cfgetibaud (const struct termios *__termios_p) __THROW;
74
75/* Set the output baud rate stored in *TERMIOS_P to BAUD. */
76extern int cfsetobaud (struct termios *__termios_p, baud_t __baud) __THROW;
77
78/* Set the input baud rate stored in *TERMIOS_P to BAUD. */
79extern int cfsetibaud (struct termios *__termios_p, baud_t __baud) __THROW;
80
81/* Set both the input and output baud rates in *TERMIOS_OP to BAUD. */
82extern int cfsetbaud (struct termios *__termios_p, baud_t __baud) __THROW;
83#endif
6484
65/* Put the state of FD into *TERMIOS_P. */85/* Put the state of FD into *TERMIOS_P. */
66extern int tcgetattr (int __fd, struct termios *__termios_p) __THROW;86extern int tcgetattr (int __fd, struct termios *__termios_p) __THROW;
lib/libc/include/generic-glibc/tgmath.h+18
...@@ -923,6 +923,24 @@...@@ -923,6 +923,24 @@
923/* Return the cube root of X. */923/* Return the cube root of X. */
924#define cbrt(Val) __TGMATH_UNARY_REAL_ONLY (Val, cbrt)924#define cbrt(Val) __TGMATH_UNARY_REAL_ONLY (Val, cbrt)
925925
926#if __GLIBC_USE (IEC_60559_FUNCS_EXT_C23)
927/* Return 1+X to the Y power. */
928# define compoundn(Val1, Val2) \
929 __TGMATH_BINARY_FIRST_REAL_ONLY (Val1, Val2, compoundn)
930
931/* Return X to the Y power. */
932# define pown(Val1, Val2) __TGMATH_BINARY_FIRST_REAL_ONLY (Val1, Val2, pown)
933
934/* Return X to the Y power. */
935# define powr(Val1, Val2) __TGMATH_BINARY_REAL_ONLY (Val1, Val2, powr)
936
937/* Return the Yth root of X. */
938# define rootn(Val1, Val2) __TGMATH_BINARY_FIRST_REAL_ONLY (Val1, Val2, rootn)
939
940/* Return 1/sqrt(X). */
941# define rsqrt(Val) __TGMATH_UNARY_REAL_ONLY (Val, rsqrt)
942#endif
943
926944
927/* Nearest integer, absolute value, and remainder functions. */945/* Nearest integer, absolute value, and remainder functions. */
928946
lib/libc/include/generic-glibc/unistd.h+1-1
...@@ -1231,4 +1231,4 @@ extern int close_range (unsigned int __fd, unsigned int __max_fd,...@@ -1231,4 +1231,4 @@ extern int close_range (unsigned int __fd, unsigned int __max_fd,
12311231
1232__END_DECLS1232__END_DECLS
12331233
1234#endif /* unistd.h */1234#endif /* unistd.h */
\ No newline at end of file
lib/libc/include/mips-linux-gnu/bits/ioctl-types.h-12
...@@ -31,18 +31,6 @@ struct winsize...@@ -31,18 +31,6 @@ struct winsize
31 unsigned short int ws_ypixel;31 unsigned short int ws_ypixel;
32 };32 };
3333
34#define NCC 8
35struct termio
36 {
37 unsigned short int c_iflag; /* input mode flags */
38 unsigned short int c_oflag; /* output mode flags */
39 unsigned short int c_cflag; /* control mode flags */
40 unsigned short int c_lflag; /* local mode flags */
41 char c_line; /* line discipline */
42 /* Yes, this is really NCCS. */
43 unsigned char c_cc[32 /* NCCS */]; /* control characters */
44 };
45
46/* modem lines */34/* modem lines */
47#define TIOCM_LE 0x001 /* line enable */35#define TIOCM_LE 0x001 /* line enable */
48#define TIOCM_DTR 0x002 /* data terminal ready */36#define TIOCM_DTR 0x002 /* data terminal ready */
lib/libc/include/mips-linux-gnu/bits/termios-struct.h deleted-34
...@@ -1,34 +0,0 @@
1/* struct termios definition. Linux/mips version.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _TERMIOS_H
20# error "Never include <bits/termios-struct.h> directly; use <termios.h> instead."
21#endif
22
23#define NCCS 32
24struct termios
25 {
26 tcflag_t c_iflag; /* input mode flags */
27 tcflag_t c_oflag; /* output mode flags */
28 tcflag_t c_cflag; /* control mode flags */
29 tcflag_t c_lflag; /* local mode flags */
30 cc_t c_line; /* line discipline */
31 cc_t c_cc[NCCS]; /* control characters */
32#define _HAVE_STRUCT_TERMIOS_C_ISPEED 0
33#define _HAVE_STRUCT_TERMIOS_C_OSPEED 0
34 };
\ No newline at end of file
lib/libc/include/powerpc-linux-gnu/bits/ioctl-types.h-11
...@@ -32,17 +32,6 @@ struct winsize...@@ -32,17 +32,6 @@ struct winsize
32 unsigned short int ws_ypixel;32 unsigned short int ws_ypixel;
33 };33 };
3434
35#define NCC 10
36struct termio
37 {
38 unsigned short int c_iflag; /* input mode flags */
39 unsigned short int c_oflag; /* output mode flags */
40 unsigned short int c_cflag; /* control mode flags */
41 unsigned short int c_lflag; /* local mode flags */
42 unsigned char c_line; /* line discipline */
43 unsigned char c_cc[NCC]; /* control characters */
44};
45
46/* modem lines */35/* modem lines */
47#define TIOCM_LE 0x00136#define TIOCM_LE 0x001
48#define TIOCM_DTR 0x00237#define TIOCM_DTR 0x002
lib/libc/include/powerpc-linux-gnu/bits/termios-baud.h deleted-45
...@@ -1,45 +0,0 @@
1/* termios baud rate selection definitions. Linux/powerpc version.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _TERMIOS_H
20# error "Never include <bits/termios-baud.h> directly; use <termios.h> instead."
21#endif
22
23#ifdef __USE_MISC
24# define CBAUD 0000377
25# define CBAUDEX 0000020
26# define CMSPAR 010000000000 /* mark or space (stick) parity */
27# define CRTSCTS 020000000000 /* flow control */
28#endif
29
30#define B57600 00020
31#define B115200 00021
32#define B230400 00022
33#define B460800 00023
34#define B500000 00024
35#define B576000 00025
36#define B921600 00026
37#define B1000000 00027
38#define B1152000 00030
39#define B1500000 00031
40#define B2000000 00032
41#define B2500000 00033
42#define B3000000 00034
43#define B3500000 00035
44#define B4000000 00036
45#define __MAX_BAUD B4000000
\ No newline at end of file
lib/libc/include/powerpc-linux-gnu/bits/termios-c_cflag.h+3-1
...@@ -35,5 +35,7 @@...@@ -35,5 +35,7 @@
35#define CLOCAL 0010000035#define CLOCAL 00100000
3636
37#ifdef __USE_MISC37#ifdef __USE_MISC
38# define ADDRB 0400000000038# define ADDRB 04000000000
39# define CMSPAR 010000000000 /* Mark or space (stick) parity. */
40# define CRTSCTS 020000000000 /* Flow control. */
39#endif41#endif
\ No newline at end of file
lib/libc/include/powerpc-linux-gnu/bits/termios-cbaud.h created+45
...@@ -0,0 +1,45 @@
1/* termios baud rate selection definitions. Linux/powerpc version.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _TERMIOS_H
20# error "Never include <bits/termios-cbaud.h> directly; use <termios.h> instead."
21#endif
22
23#ifdef __USE_MISC
24# define CBAUD 000000377
25# define CBAUDEX 000000020
26# define CIBAUD 077600000
27# define IBSHIFT 16
28#endif
29
30#define __B57600 00020
31#define __B115200 00021
32#define __B230400 00022
33#define __B460800 00023
34#define __B500000 00024
35#define __B576000 00025
36#define __B921600 00026
37#define __B1000000 00027
38#define __B1152000 00030
39#define __B1500000 00031
40#define __B2000000 00032
41#define __B2500000 00033
42#define __B3000000 00034
43#define __B3500000 00035
44#define __B4000000 00036
45#define __BOTHER 00037
\ No newline at end of file
lib/libc/include/sparc-linux-gnu/bits/termios-baud.h deleted-46
...@@ -1,46 +0,0 @@
1/* termios baud rate selection definitions. Linux/sparc version.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _TERMIOS_H
20# error "Never include <bits/termios-baud.h> directly; use <termios.h> instead."
21#endif
22
23#ifdef __USE_MISC
24# define CBAUD 0x0000100f
25# define CBAUDEX 0x00001000
26# define CIBAUD 0x100f0000 /* input baud rate (not used) */
27# define CMSPAR 0x40000000 /* mark or space (stick) parity */
28# define CRTSCTS 0x80000000 /* flow control */
29#endif
30
31#define B57600 0x00001001
32#define B115200 0x00001002
33#define B230400 0x00001003
34#define B460800 0x00001004
35#define B76800 0x00001005
36#define B153600 0x00001006
37#define B307200 0x00001007
38#define B614400 0x00001008
39#define B921600 0x00001009
40#define B500000 0x0000100a
41#define B576000 0x0000100b
42#define B1000000 0x0000100c
43#define B1152000 0x0000100d
44#define B1500000 0x0000100e
45#define B2000000 0x0000100f
46#define __MAX_BAUD B2000000
\ No newline at end of file
lib/libc/include/sparc-linux-gnu/bits/termios-cbaud.h created+45
...@@ -0,0 +1,45 @@
1/* termios baud rate selection definitions. Linux/sparc version.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _TERMIOS_H
20# error "Never include <bits/termios-cbaud.h> directly; use <termios.h> instead."
21#endif
22
23#ifdef __USE_MISC
24# define CBAUD 0x0000100f
25# define CBAUDEX 0x00001000
26# define CIBAUD 0x100f0000 /* input baud rate */
27# define IBSHIFT 16
28#endif
29
30#define __B57600 0x00001001
31#define __B115200 0x00001002
32#define __B230400 0x00001003
33#define __B460800 0x00001004
34#define __B76800 0x00001005
35#define __B153600 0x00001006
36#define __B307200 0x00001007
37#define __B614400 0x00001008
38#define __B921600 0x00001009
39#define __B500000 0x0000100a
40#define __B576000 0x0000100b
41#define __B1000000 0x0000100c
42#define __B1152000 0x0000100d
43#define __B1500000 0x0000100e
44#define __B2000000 0x0000100f
45#define __BOTHER 0x00001000
\ No newline at end of file
lib/libc/include/sparc-linux-gnu/bits/termios-struct.h deleted-34
...@@ -1,34 +0,0 @@
1/* struct termios definition. Linux/sparc version.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _TERMIOS_H
20# error "Never include <bits/termios-struct.h> directly; use <termios.h> instead."
21#endif
22
23#define NCCS 17
24struct termios
25 {
26 tcflag_t c_iflag; /* input mode flags */
27 tcflag_t c_oflag; /* output mode flags */
28 tcflag_t c_cflag; /* control mode flags */
29 tcflag_t c_lflag; /* local mode flags */
30 cc_t c_line; /* line discipline */
31 cc_t c_cc[NCCS]; /* control characters */
32#define _HAVE_STRUCT_TERMIOS_C_ISPEED 0
33#define _HAVE_STRUCT_TERMIOS_C_OSPEED 0
34 };
\ No newline at end of file
lib/libc/include/x86-linux-gnu/bits/floatn.h+7-3
...@@ -25,11 +25,15 @@...@@ -25,11 +25,15 @@
25 floating-point type with the IEEE 754 binary128 format, and this25 floating-point type with the IEEE 754 binary128 format, and this
26 glibc includes corresponding *f128 interfaces for it. The required26 glibc includes corresponding *f128 interfaces for it. The required
27 libgcc support was added some time after the basic compiler27 libgcc support was added some time after the basic compiler
28 support, for x86_64 and x86. */28 support, for x86_64 and x86. Intel SYCL compiler doesn't support
29 _Float128: https://github.com/intel/llvm/issues/16903
30 */
29#if (defined __x86_64__ \31#if (defined __x86_64__ \
30 ? __GNUC_PREREQ (4, 3) \32 ? __GNUC_PREREQ (4, 3) \
31 : (defined __GNU__ ? __GNUC_PREREQ (4, 5) : __GNUC_PREREQ (4, 4))) \33 : (defined __GNU__ ? __GNUC_PREREQ (4, 5) : __GNUC_PREREQ (4, 4))) \
32 || __glibc_clang_prereq (3, 4)34 || (__glibc_clang_prereq (3, 9) \
35 && (!defined __INTEL_LLVM_COMPILER \
36 || !defined SYCL_LANGUAGE_VERSION))
33# define __HAVE_FLOAT128 137# define __HAVE_FLOAT128 1
34#else38#else
35# define __HAVE_FLOAT128 039# define __HAVE_FLOAT128 0
...@@ -89,7 +93,7 @@ typedef _Complex float __cfloat128 __attribute__ ((__mode__ (__TC__)));...@@ -89,7 +93,7 @@ typedef _Complex float __cfloat128 __attribute__ ((__mode__ (__TC__)));
89/* The type _Float128 exists only since GCC 7.0. */93/* The type _Float128 exists only since GCC 7.0. */
90# if !__GNUC_PREREQ (7, 0) \94# if !__GNUC_PREREQ (7, 0) \
91 || (defined __cplusplus && !__GNUC_PREREQ (13, 0)) \95 || (defined __cplusplus && !__GNUC_PREREQ (13, 0)) \
92 || __glibc_clang_prereq (3, 4)96 || __glibc_clang_prereq (3, 9)
93typedef __float128 _Float128;97typedef __float128 _Float128;
94# endif98# endif
9599
lib/std/Build.zig+3
...@@ -22,6 +22,8 @@ pub const Step = @import("Build/Step.zig");...@@ -22,6 +22,8 @@ pub const Step = @import("Build/Step.zig");
22pub const Module = @import("Build/Module.zig");22pub const Module = @import("Build/Module.zig");
23pub const Watch = @import("Build/Watch.zig");23pub const Watch = @import("Build/Watch.zig");
24pub const Fuzz = @import("Build/Fuzz.zig");24pub const Fuzz = @import("Build/Fuzz.zig");
25pub const WebServer = @import("Build/WebServer.zig");
26pub const abi = @import("Build/abi.zig");
2527
26/// Shared state among all Build instances.28/// Shared state among all Build instances.
27graph: *Graph,29graph: *Graph,
...@@ -125,6 +127,7 @@ pub const Graph = struct {...@@ -125,6 +127,7 @@ pub const Graph = struct {
125 random_seed: u32 = 0,127 random_seed: u32 = 0,
126 dependency_cache: InitializedDepMap = .empty,128 dependency_cache: InitializedDepMap = .empty,
127 allow_so_scripts: ?bool = null,129 allow_so_scripts: ?bool = null,
130 time_report: bool,
128};131};
129132
130const AvailableDeps = []const struct { []const u8, []const u8 };133const AvailableDeps = []const struct { []const u8, []const u8 };
lib/std/Build/Fuzz.zig+370-81
...@@ -1,108 +1,134 @@...@@ -1,108 +1,134 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");1const std = @import("../std.zig");
3const Build = std.Build;2const Build = std.Build;
3const Cache = Build.Cache;
4const Step = std.Build.Step;4const Step = std.Build.Step;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const fatal = std.process.fatal;6const fatal = std.process.fatal;
7const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
8const log = std.log;8const log = std.log;
9const Coverage = std.debug.Coverage;
10const abi = Build.abi.fuzz;
911
10const Fuzz = @This();12const Fuzz = @This();
11const build_runner = @import("root");13const build_runner = @import("root");
1214
13pub const WebServer = @import("Fuzz/WebServer.zig");15ws: *Build.WebServer,
14pub const abi = @import("Fuzz/abi.zig");16
1517/// Allocated into `ws.gpa`.
16pub fn start(18run_steps: []const *Step.Run,
17 gpa: Allocator,19
18 arena: Allocator,20wait_group: std.Thread.WaitGroup,
19 global_cache_directory: Build.Cache.Directory,21prog_node: std.Progress.Node,
20 zig_lib_directory: Build.Cache.Directory,22
21 zig_exe_path: []const u8,23/// Protects `coverage_files`.
22 thread_pool: *std.Thread.Pool,24coverage_mutex: std.Thread.Mutex,
23 all_steps: []const *Step,25coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap),
24 ttyconf: std.io.tty.Config,26
25 listen_address: std.net.Address,27queue_mutex: std.Thread.Mutex,
26 prog_node: std.Progress.Node,28queue_cond: std.Thread.Condition,
27) Allocator.Error!void {29msg_queue: std.ArrayListUnmanaged(Msg),
28 const fuzz_run_steps = block: {30
29 const rebuild_node = prog_node.start("Rebuilding Unit Tests", 0);31const Msg = union(enum) {
32 coverage: struct {
33 id: u64,
34 run: *Step.Run,
35 },
36 entry_point: struct {
37 coverage_id: u64,
38 addr: u64,
39 },
40};
41
42const CoverageMap = struct {
43 mapped_memory: []align(std.heap.page_size_min) const u8,
44 coverage: Coverage,
45 source_locations: []Coverage.SourceLocation,
46 /// Elements are indexes into `source_locations` pointing to the unit tests that are being fuzz tested.
47 entry_points: std.ArrayListUnmanaged(u32),
48 start_timestamp: i64,
49
50 fn deinit(cm: *CoverageMap, gpa: Allocator) void {
51 std.posix.munmap(cm.mapped_memory);
52 cm.coverage.deinit(gpa);
53 cm.* = undefined;
54 }
55};
56
57pub fn init(ws: *Build.WebServer) Allocator.Error!Fuzz {
58 const gpa = ws.gpa;
59
60 const run_steps: []const *Step.Run = steps: {
61 var steps: std.ArrayListUnmanaged(*Step.Run) = .empty;
62 defer steps.deinit(gpa);
63 const rebuild_node = ws.root_prog_node.start("Rebuilding Unit Tests", 0);
30 defer rebuild_node.end();64 defer rebuild_node.end();
31 var wait_group: std.Thread.WaitGroup = .{};65 var rebuild_wg: std.Thread.WaitGroup = .{};
32 defer wait_group.wait();66 defer rebuild_wg.wait();
33 var fuzz_run_steps: std.ArrayListUnmanaged(*Step.Run) = .empty;67
34 defer fuzz_run_steps.deinit(gpa);68 for (ws.all_steps) |step| {
35 for (all_steps) |step| {
36 const run = step.cast(Step.Run) orelse continue;69 const run = step.cast(Step.Run) orelse continue;
37 if (run.fuzz_tests.items.len > 0 and run.producer != null) {70 if (run.producer == null) continue;
38 thread_pool.spawnWg(&wait_group, rebuildTestsWorkerRun, .{ run, ttyconf, rebuild_node });71 if (run.fuzz_tests.items.len == 0) continue;
39 try fuzz_run_steps.append(gpa, run);72 try steps.append(gpa, run);
40 }73 ws.thread_pool.spawnWg(&rebuild_wg, rebuildTestsWorkerRun, .{ run, gpa, ws.ttyconf, rebuild_node });
41 }74 }
42 if (fuzz_run_steps.items.len == 0) fatal("no fuzz tests found", .{});75
43 rebuild_node.setEstimatedTotalItems(fuzz_run_steps.items.len);76 if (steps.items.len == 0) fatal("no fuzz tests found", .{});
44 break :block try arena.dupe(*Step.Run, fuzz_run_steps.items);77 rebuild_node.setEstimatedTotalItems(steps.items.len);
78 break :steps try gpa.dupe(*Step.Run, steps.items);
45 };79 };
80 errdefer gpa.free(run_steps);
4681
47 // Detect failure.82 for (run_steps) |run| {
48 for (fuzz_run_steps) |run| {
49 assert(run.fuzz_tests.items.len > 0);83 assert(run.fuzz_tests.items.len > 0);
50 if (run.rebuilt_executable == null)84 if (run.rebuilt_executable == null)
51 fatal("one or more unit tests failed to be rebuilt in fuzz mode", .{});85 fatal("one or more unit tests failed to be rebuilt in fuzz mode", .{});
52 }86 }
5387
54 var web_server: WebServer = .{88 return .{
55 .gpa = gpa,89 .ws = ws,
56 .global_cache_directory = global_cache_directory,90 .run_steps = run_steps,
57 .zig_lib_directory = zig_lib_directory,91 .wait_group = .{},
58 .zig_exe_path = zig_exe_path,92 .prog_node = .none,
59 .listen_address = listen_address,93 .coverage_files = .empty,
60 .fuzz_run_steps = fuzz_run_steps,
61
62 .msg_queue = .{},
63 .mutex = .{},
64 .condition = .{},
65
66 .coverage_files = .{},
67 .coverage_mutex = .{},94 .coverage_mutex = .{},
68 .coverage_condition = .{},95 .queue_mutex = .{},
6996 .queue_cond = .{},
70 .base_timestamp = std.time.nanoTimestamp(),97 .msg_queue = .empty,
71 };98 };
99}
72100
73 // For accepting HTTP connections.101pub fn start(fuzz: *Fuzz) void {
74 const web_server_thread = std.Thread.spawn(.{}, WebServer.run, .{&web_server}) catch |err| {102 const ws = fuzz.ws;
75 fatal("unable to spawn web server thread: {s}", .{@errorName(err)});103 fuzz.prog_node = ws.root_prog_node.start("Fuzzing", fuzz.run_steps.len);
76 };
77 defer web_server_thread.join();
78104
79 // For polling messages and sending updates to subscribers.105 // For polling messages and sending updates to subscribers.
80 const coverage_thread = std.Thread.spawn(.{}, WebServer.coverageRun, .{&web_server}) catch |err| {106 fuzz.wait_group.start();
107 _ = std.Thread.spawn(.{}, coverageRun, .{fuzz}) catch |err| {
108 fuzz.wait_group.finish();
81 fatal("unable to spawn coverage thread: {s}", .{@errorName(err)});109 fatal("unable to spawn coverage thread: {s}", .{@errorName(err)});
82 };110 };
83 defer coverage_thread.join();
84
85 {
86 const fuzz_node = prog_node.start("Fuzzing", fuzz_run_steps.len);
87 defer fuzz_node.end();
88 var wait_group: std.Thread.WaitGroup = .{};
89 defer wait_group.wait();
90111
91 for (fuzz_run_steps) |run| {112 for (fuzz.run_steps) |run| {
92 for (run.fuzz_tests.items) |unit_test_index| {113 for (run.fuzz_tests.items) |unit_test_index| {
93 assert(run.rebuilt_executable != null);114 assert(run.rebuilt_executable != null);
94 thread_pool.spawnWg(&wait_group, fuzzWorkerRun, .{115 ws.thread_pool.spawnWg(&fuzz.wait_group, fuzzWorkerRun, .{
95 run, &web_server, unit_test_index, ttyconf, fuzz_node,116 fuzz, run, unit_test_index,
96 });117 });
97 }
98 }118 }
99 }119 }
120}
121pub fn deinit(fuzz: *Fuzz) void {
122 if (true) @panic("TODO: terminate the fuzzer processes");
123 fuzz.wait_group.wait();
124 fuzz.prog_node.end();
100125
101 log.err("all fuzz workers crashed", .{});126 const gpa = fuzz.ws.gpa;
127 gpa.free(fuzz.run_steps);
102}128}
103129
104fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void {130fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void {
105 rebuildTestsWorkerRunFallible(run, ttyconf, parent_prog_node) catch |err| {131 rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| {
106 const compile = run.producer.?;132 const compile = run.producer.?;
107 log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{133 log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{
108 compile.step.name, @errorName(err),134 compile.step.name, @errorName(err),
...@@ -110,14 +136,12 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog...@@ -110,14 +136,12 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog
110 };136 };
111}137}
112138
113fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void {139fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void {
114 const gpa = run.step.owner.allocator;
115
116 const compile = run.producer.?;140 const compile = run.producer.?;
117 const prog_node = parent_prog_node.start(compile.step.name, 0);141 const prog_node = parent_prog_node.start(compile.step.name, 0);
118 defer prog_node.end();142 defer prog_node.end();
119143
120 const result = compile.rebuildInFuzzMode(prog_node);144 const result = compile.rebuildInFuzzMode(gpa, prog_node);
121145
122 const show_compile_errors = compile.step.result_error_bundle.errorMessageCount() > 0;146 const show_compile_errors = compile.step.result_error_bundle.errorMessageCount() > 0;
123 const show_error_msgs = compile.step.result_error_msgs.items.len > 0;147 const show_error_msgs = compile.step.result_error_msgs.items.len > 0;
...@@ -138,24 +162,22 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par...@@ -138,24 +162,22 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par
138}162}
139163
140fn fuzzWorkerRun(164fn fuzzWorkerRun(
165 fuzz: *Fuzz,
141 run: *Step.Run,166 run: *Step.Run,
142 web_server: *WebServer,
143 unit_test_index: u32,167 unit_test_index: u32,
144 ttyconf: std.io.tty.Config,
145 parent_prog_node: std.Progress.Node,
146) void {168) void {
147 const gpa = run.step.owner.allocator;169 const gpa = run.step.owner.allocator;
148 const test_name = run.cached_test_metadata.?.testName(unit_test_index);170 const test_name = run.cached_test_metadata.?.testName(unit_test_index);
149171
150 const prog_node = parent_prog_node.start(test_name, 0);172 const prog_node = fuzz.prog_node.start(test_name, 0);
151 defer prog_node.end();173 defer prog_node.end();
152174
153 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {175 run.rerunInFuzzMode(fuzz, unit_test_index, prog_node) catch |err| switch (err) {
154 error.MakeFailed => {176 error.MakeFailed => {
155 var buf: [256]u8 = undefined;177 var buf: [256]u8 = undefined;
156 const w = std.debug.lockStderrWriter(&buf);178 const w = std.debug.lockStderrWriter(&buf);
157 defer std.debug.unlockStderrWriter();179 defer std.debug.unlockStderrWriter();
158 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, w, false) catch {};180 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = fuzz.ws.ttyconf }, w, false) catch {};
159 return;181 return;
160 },182 },
161 else => {183 else => {
...@@ -166,3 +188,270 @@ fn fuzzWorkerRun(...@@ -166,3 +188,270 @@ fn fuzzWorkerRun(
166 },188 },
167 };189 };
168}190}
191
192pub fn serveSourcesTar(fuzz: *Fuzz, req: *std.http.Server.Request) !void {
193 const gpa = fuzz.ws.gpa;
194
195 var arena_state: std.heap.ArenaAllocator = .init(gpa);
196 defer arena_state.deinit();
197 const arena = arena_state.allocator();
198
199 const DedupTable = std.ArrayHashMapUnmanaged(Build.Cache.Path, void, Build.Cache.Path.TableAdapter, false);
200 var dedup_table: DedupTable = .empty;
201 defer dedup_table.deinit(gpa);
202
203 for (fuzz.run_steps) |run_step| {
204 const compile_inputs = run_step.producer.?.step.inputs.table;
205 for (compile_inputs.keys(), compile_inputs.values()) |dir_path, *file_list| {
206 try dedup_table.ensureUnusedCapacity(gpa, file_list.items.len);
207 for (file_list.items) |sub_path| {
208 if (!std.mem.endsWith(u8, sub_path, ".zig")) continue;
209 const joined_path = try dir_path.join(arena, sub_path);
210 dedup_table.putAssumeCapacity(joined_path, {});
211 }
212 }
213 }
214
215 const deduped_paths = dedup_table.keys();
216 const SortContext = struct {
217 pub fn lessThan(this: @This(), lhs: Build.Cache.Path, rhs: Build.Cache.Path) bool {
218 _ = this;
219 return switch (std.mem.order(u8, lhs.root_dir.path orelse ".", rhs.root_dir.path orelse ".")) {
220 .lt => true,
221 .gt => false,
222 .eq => std.mem.lessThan(u8, lhs.sub_path, rhs.sub_path),
223 };
224 }
225 };
226 std.mem.sortUnstable(Build.Cache.Path, deduped_paths, SortContext{}, SortContext.lessThan);
227 return fuzz.ws.serveTarFile(req, deduped_paths);
228}
229
230pub const Previous = struct {
231 unique_runs: usize,
232 entry_points: usize,
233 pub const init: Previous = .{ .unique_runs = 0, .entry_points = 0 };
234};
235pub fn sendUpdate(
236 fuzz: *Fuzz,
237 socket: *std.http.WebSocket,
238 prev: *Previous,
239) !void {
240 fuzz.coverage_mutex.lock();
241 defer fuzz.coverage_mutex.unlock();
242
243 const coverage_maps = fuzz.coverage_files.values();
244 if (coverage_maps.len == 0) return;
245 // TODO: handle multiple fuzz steps in the WebSocket packets
246 const coverage_map = &coverage_maps[0];
247 const cov_header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
248 // TODO: this isn't sound! We need to do volatile reads of these bits rather than handing the
249 // buffer off to the kernel, because we might race with the fuzzer process[es]. This brings the
250 // whole mmap strategy into question. Incidentally, I wonder if post-writergate we could pass
251 // this data straight to the socket with sendfile...
252 const seen_pcs = cov_header.seenBits();
253 const n_runs = @atomicLoad(usize, &cov_header.n_runs, .monotonic);
254 const unique_runs = @atomicLoad(usize, &cov_header.unique_runs, .monotonic);
255 if (prev.unique_runs != unique_runs) {
256 // There has been an update.
257 if (prev.unique_runs == 0) {
258 // We need to send initial context.
259 const header: abi.SourceIndexHeader = .{
260 .directories_len = @intCast(coverage_map.coverage.directories.entries.len),
261 .files_len = @intCast(coverage_map.coverage.files.entries.len),
262 .source_locations_len = @intCast(coverage_map.source_locations.len),
263 .string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len),
264 .start_timestamp = coverage_map.start_timestamp,
265 };
266 const iovecs: [5]std.posix.iovec_const = .{
267 makeIov(@ptrCast(&header)),
268 makeIov(@ptrCast(coverage_map.coverage.directories.keys())),
269 makeIov(@ptrCast(coverage_map.coverage.files.keys())),
270 makeIov(@ptrCast(coverage_map.source_locations)),
271 makeIov(coverage_map.coverage.string_bytes.items),
272 };
273 try socket.writeMessagev(&iovecs, .binary);
274 }
275
276 const header: abi.CoverageUpdateHeader = .{
277 .n_runs = n_runs,
278 .unique_runs = unique_runs,
279 };
280 const iovecs: [2]std.posix.iovec_const = .{
281 makeIov(@ptrCast(&header)),
282 makeIov(@ptrCast(seen_pcs)),
283 };
284 try socket.writeMessagev(&iovecs, .binary);
285
286 prev.unique_runs = unique_runs;
287 }
288
289 if (prev.entry_points != coverage_map.entry_points.items.len) {
290 const header: abi.EntryPointHeader = .init(@intCast(coverage_map.entry_points.items.len));
291 const iovecs: [2]std.posix.iovec_const = .{
292 makeIov(@ptrCast(&header)),
293 makeIov(@ptrCast(coverage_map.entry_points.items)),
294 };
295 try socket.writeMessagev(&iovecs, .binary);
296
297 prev.entry_points = coverage_map.entry_points.items.len;
298 }
299}
300
301fn coverageRun(fuzz: *Fuzz) void {
302 defer fuzz.wait_group.finish();
303
304 fuzz.queue_mutex.lock();
305 defer fuzz.queue_mutex.unlock();
306
307 while (true) {
308 fuzz.queue_cond.wait(&fuzz.queue_mutex);
309 for (fuzz.msg_queue.items) |msg| switch (msg) {
310 .coverage => |coverage| prepareTables(fuzz, coverage.run, coverage.id) catch |err| switch (err) {
311 error.AlreadyReported => continue,
312 else => |e| log.err("failed to prepare code coverage tables: {s}", .{@errorName(e)}),
313 },
314 .entry_point => |entry_point| addEntryPoint(fuzz, entry_point.coverage_id, entry_point.addr) catch |err| switch (err) {
315 error.AlreadyReported => continue,
316 else => |e| log.err("failed to prepare code coverage tables: {s}", .{@errorName(e)}),
317 },
318 };
319 fuzz.msg_queue.clearRetainingCapacity();
320 }
321}
322fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutOfMemory, AlreadyReported }!void {
323 const ws = fuzz.ws;
324 const gpa = ws.gpa;
325
326 fuzz.coverage_mutex.lock();
327 defer fuzz.coverage_mutex.unlock();
328
329 const gop = try fuzz.coverage_files.getOrPut(gpa, coverage_id);
330 if (gop.found_existing) {
331 // We are fuzzing the same executable with multiple threads.
332 // Perhaps the same unit test; perhaps a different one. In any
333 // case, since the coverage file is the same, we only have to
334 // notice changes to that one file in order to learn coverage for
335 // this particular executable.
336 return;
337 }
338 errdefer _ = fuzz.coverage_files.pop();
339
340 gop.value_ptr.* = .{
341 .coverage = std.debug.Coverage.init,
342 .mapped_memory = undefined, // populated below
343 .source_locations = undefined, // populated below
344 .entry_points = .{},
345 .start_timestamp = ws.now(),
346 };
347 errdefer gop.value_ptr.coverage.deinit(gpa);
348
349 const rebuilt_exe_path = run_step.rebuilt_executable.?;
350 var debug_info = std.debug.Info.load(gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| {
351 log.err("step '{s}': failed to load debug information for '{f}': {s}", .{
352 run_step.step.name, rebuilt_exe_path, @errorName(err),
353 });
354 return error.AlreadyReported;
355 };
356 defer debug_info.deinit(gpa);
357
358 const coverage_file_path: Build.Cache.Path = .{
359 .root_dir = run_step.step.owner.cache_root,
360 .sub_path = "v/" ++ std.fmt.hex(coverage_id),
361 };
362 var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {
363 log.err("step '{s}': failed to load coverage file '{f}': {s}", .{
364 run_step.step.name, coverage_file_path, @errorName(err),
365 });
366 return error.AlreadyReported;
367 };
368 defer coverage_file.close();
369
370 const file_size = coverage_file.getEndPos() catch |err| {
371 log.err("unable to check len of coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });
372 return error.AlreadyReported;
373 };
374
375 const mapped_memory = std.posix.mmap(
376 null,
377 file_size,
378 std.posix.PROT.READ,
379 .{ .TYPE = .SHARED },
380 coverage_file.handle,
381 0,
382 ) catch |err| {
383 log.err("failed to map coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });
384 return error.AlreadyReported;
385 };
386 gop.value_ptr.mapped_memory = mapped_memory;
387
388 const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
389 const pcs = header.pcAddrs();
390 const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
391 errdefer gpa.free(source_locations);
392
393 // Unfortunately the PCs array that LLVM gives us from the 8-bit PC
394 // counters feature is not sorted.
395 var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .{};
396 defer sorted_pcs.deinit(gpa);
397 try sorted_pcs.resize(gpa, pcs.len);
398 @memcpy(sorted_pcs.items(.pc), pcs);
399 for (sorted_pcs.items(.index), 0..) |*v, i| v.* = @intCast(i);
400 sorted_pcs.sortUnstable(struct {
401 addrs: []const u64,
402
403 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
404 return ctx.addrs[a_index] < ctx.addrs[b_index];
405 }
406 }{ .addrs = sorted_pcs.items(.pc) });
407
408 debug_info.resolveAddresses(gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
409 log.err("failed to resolve addresses to source locations: {s}", .{@errorName(err)});
410 return error.AlreadyReported;
411 };
412
413 for (sorted_pcs.items(.index), sorted_pcs.items(.sl)) |i, sl| source_locations[i] = sl;
414 gop.value_ptr.source_locations = source_locations;
415
416 ws.notifyUpdate();
417}
418fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReported, OutOfMemory }!void {
419 fuzz.coverage_mutex.lock();
420 defer fuzz.coverage_mutex.unlock();
421
422 const coverage_map = fuzz.coverage_files.getPtr(coverage_id).?;
423 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
424 const pcs = header.pcAddrs();
425
426 // Since this pcs list is unsorted, we must linear scan for the best index.
427 const index = i: {
428 var best: usize = 0;
429 for (pcs[1..], 1..) |elem_addr, i| {
430 if (elem_addr == addr) break :i i;
431 if (elem_addr > addr) continue;
432 if (elem_addr > pcs[best]) best = i;
433 }
434 break :i best;
435 };
436 if (index >= pcs.len) {
437 log.err("unable to find unit test entry address 0x{x} in source locations (range: 0x{x} to 0x{x})", .{
438 addr, pcs[0], pcs[pcs.len - 1],
439 });
440 return error.AlreadyReported;
441 }
442 if (false) {
443 const sl = coverage_map.source_locations[index];
444 const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);
445 log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{
446 addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1],
447 });
448 }
449 try coverage_map.entry_points.append(fuzz.ws.gpa, @intCast(index));
450}
451
452fn makeIov(s: []const u8) std.posix.iovec_const {
453 return .{
454 .base = s.ptr,
455 .len = s.len,
456 };
457}
lib/std/Build/Fuzz/WebServer.zig deleted-711
...@@ -1,711 +0,0 @@
1const builtin = @import("builtin");
2
3const std = @import("../../std.zig");
4const Allocator = std.mem.Allocator;
5const Build = std.Build;
6const Step = std.Build.Step;
7const Coverage = std.debug.Coverage;
8const abi = std.Build.Fuzz.abi;
9const log = std.log;
10const assert = std.debug.assert;
11const Cache = std.Build.Cache;
12const Path = Cache.Path;
13
14const WebServer = @This();
15
16gpa: Allocator,
17global_cache_directory: Build.Cache.Directory,
18zig_lib_directory: Build.Cache.Directory,
19zig_exe_path: []const u8,
20listen_address: std.net.Address,
21fuzz_run_steps: []const *Step.Run,
22
23/// Messages from fuzz workers. Protected by mutex.
24msg_queue: std.ArrayListUnmanaged(Msg),
25/// Protects `msg_queue` only.
26mutex: std.Thread.Mutex,
27/// Signaled when there is a message in `msg_queue`.
28condition: std.Thread.Condition,
29
30coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap),
31/// Protects `coverage_files` only.
32coverage_mutex: std.Thread.Mutex,
33/// Signaled when `coverage_files` changes.
34coverage_condition: std.Thread.Condition,
35
36/// Time at initialization of WebServer.
37base_timestamp: i128,
38
39const fuzzer_bin_name = "fuzzer";
40const fuzzer_arch_os_abi = "wasm32-freestanding";
41const fuzzer_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
42
43const CoverageMap = struct {
44 mapped_memory: []align(std.heap.page_size_min) const u8,
45 coverage: Coverage,
46 source_locations: []Coverage.SourceLocation,
47 /// Elements are indexes into `source_locations` pointing to the unit tests that are being fuzz tested.
48 entry_points: std.ArrayListUnmanaged(u32),
49 start_timestamp: i64,
50
51 fn deinit(cm: *CoverageMap, gpa: Allocator) void {
52 std.posix.munmap(cm.mapped_memory);
53 cm.coverage.deinit(gpa);
54 cm.* = undefined;
55 }
56};
57
58const Msg = union(enum) {
59 coverage: struct {
60 id: u64,
61 run: *Step.Run,
62 },
63 entry_point: struct {
64 coverage_id: u64,
65 addr: u64,
66 },
67};
68
69pub fn run(ws: *WebServer) void {
70 var http_server = ws.listen_address.listen(.{
71 .reuse_address = true,
72 }) catch |err| {
73 log.err("failed to listen to port {d}: {s}", .{ ws.listen_address.in.getPort(), @errorName(err) });
74 return;
75 };
76 const port = http_server.listen_address.in.getPort();
77 log.info("web interface listening at http://127.0.0.1:{d}/", .{port});
78 if (ws.listen_address.in.getPort() == 0)
79 log.info("hint: pass --port {d} to use this same port next time", .{port});
80
81 while (true) {
82 const connection = http_server.accept() catch |err| {
83 log.err("failed to accept connection: {s}", .{@errorName(err)});
84 return;
85 };
86 _ = std.Thread.spawn(.{}, accept, .{ ws, connection }) catch |err| {
87 log.err("unable to spawn connection thread: {s}", .{@errorName(err)});
88 connection.stream.close();
89 continue;
90 };
91 }
92}
93
94fn now(s: *const WebServer) i64 {
95 return @intCast(std.time.nanoTimestamp() - s.base_timestamp);
96}
97
98fn accept(ws: *WebServer, connection: std.net.Server.Connection) void {
99 defer connection.stream.close();
100
101 var sr = connection.stream.reader();
102 var rb: [0x4000]u8 = undefined;
103 var br = sr.interface().buffered(&rb);
104
105 var sw = connection.stream.writer();
106 var wb: [0x4000]u8 = undefined;
107 var bw = sw.interface().buffered(&wb);
108
109 var server: std.http.Server = .init(&br, &bw);
110 var web_socket: std.http.WebSocket = undefined;
111 var ws_recv_buffer: [0x4000]u8 align(4) = undefined;
112 while (server.reader.state == .ready) {
113 var request = server.receiveHead() catch |err| switch (err) {
114 error.HttpConnectionClosing => return,
115 else => {
116 log.err("closing http connection: {s}", .{@errorName(err)});
117 return;
118 },
119 };
120 if (web_socket.init(&request, &ws_recv_buffer) catch |err| {
121 log.err("initializing web socket: {s}", .{@errorName(err)});
122 return;
123 }) {
124 serveWebSocket(ws, &web_socket) catch |err| {
125 log.err("unable to serve web socket connection: {s}", .{@errorName(err)});
126 return;
127 };
128 } else {
129 serveRequest(ws, &request) catch |err| switch (err) {
130 error.AlreadyReported => return,
131 else => |e| {
132 log.err("unable to serve {s}: {s}", .{ request.head.target, @errorName(e) });
133 return;
134 },
135 };
136 }
137 }
138}
139
140fn serveRequest(ws: *WebServer, request: *std.http.Server.Request) !void {
141 if (std.mem.eql(u8, request.head.target, "/") or
142 std.mem.eql(u8, request.head.target, "/debug") or
143 std.mem.eql(u8, request.head.target, "/debug/"))
144 {
145 try serveFile(ws, request, "fuzzer/web/index.html", "text/html");
146 } else if (std.mem.eql(u8, request.head.target, "/main.js") or
147 std.mem.eql(u8, request.head.target, "/debug/main.js"))
148 {
149 try serveFile(ws, request, "fuzzer/web/main.js", "application/javascript");
150 } else if (std.mem.eql(u8, request.head.target, "/main.wasm")) {
151 try serveWasm(ws, request, .ReleaseFast);
152 } else if (std.mem.eql(u8, request.head.target, "/debug/main.wasm")) {
153 try serveWasm(ws, request, .Debug);
154 } else if (std.mem.eql(u8, request.head.target, "/sources.tar") or
155 std.mem.eql(u8, request.head.target, "/debug/sources.tar"))
156 {
157 try serveSourcesTar(ws, request);
158 } else {
159 try request.respond("not found", .{
160 .status = .not_found,
161 .extra_headers = &.{
162 .{ .name = "content-type", .value = "text/plain" },
163 },
164 });
165 }
166}
167
168fn serveFile(
169 ws: *WebServer,
170 request: *std.http.Server.Request,
171 name: []const u8,
172 content_type: []const u8,
173) !void {
174 const gpa = ws.gpa;
175 // The desired API is actually sendfile, which will require enhancing std.http.Server.
176 // We load the file with every request so that the user can make changes to the file
177 // and refresh the HTML page without restarting this server.
178 const file_contents = ws.zig_lib_directory.handle.readFileAlloc(name, gpa, .limited(10 * 1024 * 1024)) catch |err| {
179 log.err("failed to read '{f}{s}': {t}", .{ ws.zig_lib_directory, name, err });
180 return error.AlreadyReported;
181 };
182 defer gpa.free(file_contents);
183 try request.respond(file_contents, .{
184 .extra_headers = &.{
185 .{ .name = "content-type", .value = content_type },
186 cache_control_header,
187 },
188 });
189}
190
191fn serveWasm(
192 ws: *WebServer,
193 request: *std.http.Server.Request,
194 optimize_mode: std.builtin.OptimizeMode,
195) !void {
196 const gpa = ws.gpa;
197
198 var arena_instance = std.heap.ArenaAllocator.init(gpa);
199 defer arena_instance.deinit();
200 const arena = arena_instance.allocator();
201
202 // Do the compilation every request, so that the user can edit the files
203 // and see the changes without restarting the server.
204 const wasm_base_path = try buildWasmBinary(ws, arena, optimize_mode);
205 const bin_name = try std.zig.binNameAlloc(arena, .{
206 .root_name = fuzzer_bin_name,
207 .target = &(std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{
208 .arch_os_abi = fuzzer_arch_os_abi,
209 .cpu_features = fuzzer_cpu_features,
210 }) catch unreachable) catch unreachable),
211 .output_mode = .Exe,
212 });
213 // std.http.Server does not have a sendfile API yet.
214 const bin_path = try wasm_base_path.join(arena, bin_name);
215 const file_contents = try bin_path.root_dir.handle.readFileAlloc(bin_path.sub_path, gpa, .limited(10 * 1024 * 1024));
216 defer gpa.free(file_contents);
217 try request.respond(file_contents, .{
218 .extra_headers = &.{
219 .{ .name = "content-type", .value = "application/wasm" },
220 cache_control_header,
221 },
222 });
223}
224
225fn buildWasmBinary(
226 ws: *WebServer,
227 arena: Allocator,
228 optimize_mode: std.builtin.OptimizeMode,
229) !Path {
230 const gpa = ws.gpa;
231
232 const main_src_path: Build.Cache.Path = .{
233 .root_dir = ws.zig_lib_directory,
234 .sub_path = "fuzzer/web/main.zig",
235 };
236 const walk_src_path: Build.Cache.Path = .{
237 .root_dir = ws.zig_lib_directory,
238 .sub_path = "docs/wasm/Walk.zig",
239 };
240 const html_render_src_path: Build.Cache.Path = .{
241 .root_dir = ws.zig_lib_directory,
242 .sub_path = "docs/wasm/html_render.zig",
243 };
244
245 var argv: std.ArrayListUnmanaged([]const u8) = .empty;
246
247 try argv.appendSlice(arena, &.{
248 ws.zig_exe_path, "build-exe", //
249 "-fno-entry", //
250 "-O", @tagName(optimize_mode), //
251 "-target", fuzzer_arch_os_abi, //
252 "-mcpu", fuzzer_cpu_features, //
253 "--cache-dir", ws.global_cache_directory.path orelse ".", //
254 "--global-cache-dir", ws.global_cache_directory.path orelse ".", //
255 "--name", fuzzer_bin_name, //
256 "-rdynamic", //
257 "-fsingle-threaded", //
258 "--dep", "Walk", //
259 "--dep", "html_render", //
260 try std.fmt.allocPrint(arena, "-Mroot={f}", .{main_src_path}), //
261 try std.fmt.allocPrint(arena, "-MWalk={f}", .{walk_src_path}), //
262 "--dep", "Walk", //
263 try std.fmt.allocPrint(arena, "-Mhtml_render={f}", .{html_render_src_path}), //
264 "--listen=-",
265 });
266
267 var child = std.process.Child.init(argv.items, gpa);
268 child.stdin_behavior = .Pipe;
269 child.stdout_behavior = .Pipe;
270 child.stderr_behavior = .Pipe;
271 try child.spawn();
272
273 var poller = std.io.poll(gpa, enum { stdout, stderr }, .{
274 .stdout = child.stdout.?,
275 .stderr = child.stderr.?,
276 });
277 defer poller.deinit();
278
279 try sendMessage(child.stdin.?, .update);
280 try sendMessage(child.stdin.?, .exit);
281
282 var result: ?Path = null;
283 var result_error_bundle = std.zig.ErrorBundle.empty;
284
285 const stdout = poller.reader(.stdout);
286
287 poll: while (true) {
288 const Header = std.zig.Server.Message.Header;
289 while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll;
290 const header = stdout.takeStruct(Header, .little) catch unreachable;
291 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
292 const body = stdout.take(header.bytes_len) catch unreachable;
293
294 switch (header.tag) {
295 .zig_version => {
296 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
297 return error.ZigProtocolVersionMismatch;
298 }
299 },
300 .error_bundle => {
301 const EbHdr = std.zig.Server.Message.ErrorBundle;
302 const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body));
303 const extra_bytes =
304 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
305 const string_bytes =
306 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
307 // TODO: use @ptrCast when the compiler supports it
308 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
309 const extra_array = try arena.alloc(u32, unaligned_extra.len);
310 @memcpy(extra_array, unaligned_extra);
311 result_error_bundle = .{
312 .string_bytes = try arena.dupe(u8, string_bytes),
313 .extra = extra_array,
314 };
315 },
316 .emit_digest => {
317 const EmitDigest = std.zig.Server.Message.EmitDigest;
318 const ebp_hdr = @as(*align(1) const EmitDigest, @ptrCast(body));
319 if (!ebp_hdr.flags.cache_hit) {
320 log.info("source changes detected; rebuilt wasm component", .{});
321 }
322 const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
323 result = .{
324 .root_dir = ws.global_cache_directory,
325 .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)),
326 };
327 },
328 else => {}, // ignore other messages
329 }
330 }
331
332 const stderr_contents = try poller.toOwnedSlice(.stderr);
333 if (stderr_contents.len > 0) {
334 std.debug.print("{s}", .{stderr_contents});
335 }
336
337 // Send EOF to stdin.
338 child.stdin.?.close();
339 child.stdin = null;
340
341 switch (try child.wait()) {
342 .Exited => |code| {
343 if (code != 0) {
344 log.err(
345 "the following command exited with error code {d}:\n{s}",
346 .{ code, try Build.Step.allocPrintCmd(arena, null, argv.items) },
347 );
348 return error.WasmCompilationFailed;
349 }
350 },
351 .Signal, .Stopped, .Unknown => {
352 log.err(
353 "the following command terminated unexpectedly:\n{s}",
354 .{try Build.Step.allocPrintCmd(arena, null, argv.items)},
355 );
356 return error.WasmCompilationFailed;
357 },
358 }
359
360 if (result_error_bundle.errorMessageCount() > 0) {
361 const color = std.zig.Color.auto;
362 result_error_bundle.renderToStdErr(color.renderOptions());
363 log.err("the following command failed with {d} compilation errors:\n{s}", .{
364 result_error_bundle.errorMessageCount(),
365 try Build.Step.allocPrintCmd(arena, null, argv.items),
366 });
367 return error.WasmCompilationFailed;
368 }
369
370 return result orelse {
371 log.err("child process failed to report result\n{s}", .{
372 try Build.Step.allocPrintCmd(arena, null, argv.items),
373 });
374 return error.WasmCompilationFailed;
375 };
376}
377
378fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
379 const header: std.zig.Client.Message.Header = .{
380 .tag = tag,
381 .bytes_len = 0,
382 };
383 try file.writeAll(std.mem.asBytes(&header));
384}
385
386fn serveWebSocket(ws: *WebServer, web_socket: *std.http.WebSocket) !void {
387 ws.coverage_mutex.lock();
388 defer ws.coverage_mutex.unlock();
389
390 // On first connection, the client needs to know what time the server
391 // thinks it is to rebase timestamps.
392 {
393 const timestamp_message: abi.CurrentTime = .{ .base = ws.now() };
394 try web_socket.writeMessage(std.mem.asBytes(&timestamp_message), .binary);
395 }
396
397 // On first connection, the client needs all the coverage information
398 // so that subsequent updates can contain only the updated bits.
399 var prev_unique_runs: usize = 0;
400 var prev_entry_points: usize = 0;
401 try sendCoverageContext(ws, web_socket, &prev_unique_runs, &prev_entry_points);
402 while (true) {
403 ws.coverage_condition.timedWait(&ws.coverage_mutex, std.time.ns_per_ms * 500) catch {};
404 try sendCoverageContext(ws, web_socket, &prev_unique_runs, &prev_entry_points);
405 }
406}
407
408fn sendCoverageContext(
409 ws: *WebServer,
410 web_socket: *std.http.WebSocket,
411 prev_unique_runs: *usize,
412 prev_entry_points: *usize,
413) !void {
414 const coverage_maps = ws.coverage_files.values();
415 if (coverage_maps.len == 0) return;
416 // TODO: make each events URL correspond to one coverage map
417 const coverage_map = &coverage_maps[0];
418 const cov_header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
419 const seen_pcs = cov_header.seenBits();
420 const n_runs = @atomicLoad(usize, &cov_header.n_runs, .monotonic);
421 const unique_runs = @atomicLoad(usize, &cov_header.unique_runs, .monotonic);
422 if (prev_unique_runs.* != unique_runs) {
423 // There has been an update.
424 if (prev_unique_runs.* == 0) {
425 // We need to send initial context.
426 const header: abi.SourceIndexHeader = .{
427 .flags = .{},
428 .directories_len = @intCast(coverage_map.coverage.directories.entries.len),
429 .files_len = @intCast(coverage_map.coverage.files.entries.len),
430 .source_locations_len = @intCast(coverage_map.source_locations.len),
431 .string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len),
432 .start_timestamp = coverage_map.start_timestamp,
433 };
434 const iovecs: [5]std.posix.iovec_const = .{
435 makeIov(std.mem.asBytes(&header)),
436 makeIov(std.mem.sliceAsBytes(coverage_map.coverage.directories.keys())),
437 makeIov(std.mem.sliceAsBytes(coverage_map.coverage.files.keys())),
438 makeIov(std.mem.sliceAsBytes(coverage_map.source_locations)),
439 makeIov(coverage_map.coverage.string_bytes.items),
440 };
441 try web_socket.writeMessagev(&iovecs, .binary);
442 }
443
444 const header: abi.CoverageUpdateHeader = .{
445 .n_runs = n_runs,
446 .unique_runs = unique_runs,
447 };
448 const iovecs: [2]std.posix.iovec_const = .{
449 makeIov(std.mem.asBytes(&header)),
450 makeIov(std.mem.sliceAsBytes(seen_pcs)),
451 };
452 try web_socket.writeMessagev(&iovecs, .binary);
453
454 prev_unique_runs.* = unique_runs;
455 }
456
457 if (prev_entry_points.* != coverage_map.entry_points.items.len) {
458 const header: abi.EntryPointHeader = .{
459 .flags = .{
460 .locs_len = @intCast(coverage_map.entry_points.items.len),
461 },
462 };
463 const iovecs: [2]std.posix.iovec_const = .{
464 makeIov(std.mem.asBytes(&header)),
465 makeIov(std.mem.sliceAsBytes(coverage_map.entry_points.items)),
466 };
467 try web_socket.writeMessagev(&iovecs, .binary);
468
469 prev_entry_points.* = coverage_map.entry_points.items.len;
470 }
471}
472
473fn serveSourcesTar(ws: *WebServer, request: *std.http.Server.Request) !void {
474 const gpa = ws.gpa;
475
476 var arena_instance = std.heap.ArenaAllocator.init(gpa);
477 defer arena_instance.deinit();
478 const arena = arena_instance.allocator();
479
480 var body = try request.respondStreaming(.{
481 .respond_options = .{
482 .extra_headers = &.{
483 .{ .name = "content-type", .value = "application/x-tar" },
484 cache_control_header,
485 },
486 },
487 });
488
489 const DedupeTable = std.ArrayHashMapUnmanaged(Build.Cache.Path, void, Build.Cache.Path.TableAdapter, false);
490 var dedupe_table: DedupeTable = .{};
491 defer dedupe_table.deinit(gpa);
492
493 for (ws.fuzz_run_steps) |run_step| {
494 const compile_step_inputs = run_step.producer.?.step.inputs.table;
495 for (compile_step_inputs.keys(), compile_step_inputs.values()) |dir_path, *file_list| {
496 try dedupe_table.ensureUnusedCapacity(gpa, file_list.items.len);
497 for (file_list.items) |sub_path| {
498 // Special file "." means the entire directory.
499 if (std.mem.eql(u8, sub_path, ".")) continue;
500 const joined_path = try dir_path.join(arena, sub_path);
501 _ = dedupe_table.getOrPutAssumeCapacity(joined_path);
502 }
503 }
504 }
505
506 const deduped_paths = dedupe_table.keys();
507 const SortContext = struct {
508 pub fn lessThan(this: @This(), lhs: Build.Cache.Path, rhs: Build.Cache.Path) bool {
509 _ = this;
510 return switch (std.mem.order(u8, lhs.root_dir.path orelse ".", rhs.root_dir.path orelse ".")) {
511 .lt => true,
512 .gt => false,
513 .eq => std.mem.lessThan(u8, lhs.sub_path, rhs.sub_path),
514 };
515 }
516 };
517 std.mem.sortUnstable(Build.Cache.Path, deduped_paths, SortContext{}, SortContext.lessThan);
518
519 var cwd_cache: ?[]const u8 = null;
520
521 var response_writer = body.writer().unbuffered();
522 var archiver: std.tar.Writer = .{ .underlying_writer = &response_writer };
523 var read_buffer: [1024]u8 = undefined;
524
525 for (deduped_paths) |joined_path| {
526 var file = joined_path.root_dir.handle.openFile(joined_path.sub_path, .{}) catch |err| {
527 log.err("failed to open {f}: {s}", .{ joined_path, @errorName(err) });
528 continue;
529 };
530 defer file.close();
531 const stat = try file.stat();
532 var file_reader: std.fs.File.Reader = .initSize(file, &read_buffer, stat.size);
533 archiver.prefix = joined_path.root_dir.path orelse try memoizedCwd(arena, &cwd_cache);
534 try archiver.writeFile(joined_path.sub_path, &file_reader, stat.mtime);
535 }
536
537 try body.end();
538}
539
540fn memoizedCwd(arena: Allocator, opt_ptr: *?[]const u8) ![]const u8 {
541 if (opt_ptr.*) |cached| return cached;
542 const result = try std.process.getCwdAlloc(arena);
543 opt_ptr.* = result;
544 return result;
545}
546
547const cache_control_header: std.http.Header = .{
548 .name = "cache-control",
549 .value = "max-age=0, must-revalidate",
550};
551
552pub fn coverageRun(ws: *WebServer) void {
553 ws.mutex.lock();
554 defer ws.mutex.unlock();
555
556 while (true) {
557 ws.condition.wait(&ws.mutex);
558 for (ws.msg_queue.items) |msg| switch (msg) {
559 .coverage => |coverage| prepareTables(ws, coverage.run, coverage.id) catch |err| switch (err) {
560 error.AlreadyReported => continue,
561 else => |e| log.err("failed to prepare code coverage tables: {s}", .{@errorName(e)}),
562 },
563 .entry_point => |entry_point| addEntryPoint(ws, entry_point.coverage_id, entry_point.addr) catch |err| switch (err) {
564 error.AlreadyReported => continue,
565 else => |e| log.err("failed to prepare code coverage tables: {s}", .{@errorName(e)}),
566 },
567 };
568 ws.msg_queue.clearRetainingCapacity();
569 }
570}
571
572fn prepareTables(
573 ws: *WebServer,
574 run_step: *Step.Run,
575 coverage_id: u64,
576) error{ OutOfMemory, AlreadyReported }!void {
577 const gpa = ws.gpa;
578
579 ws.coverage_mutex.lock();
580 defer ws.coverage_mutex.unlock();
581
582 const gop = try ws.coverage_files.getOrPut(gpa, coverage_id);
583 if (gop.found_existing) {
584 // We are fuzzing the same executable with multiple threads.
585 // Perhaps the same unit test; perhaps a different one. In any
586 // case, since the coverage file is the same, we only have to
587 // notice changes to that one file in order to learn coverage for
588 // this particular executable.
589 return;
590 }
591 errdefer _ = ws.coverage_files.pop();
592
593 gop.value_ptr.* = .{
594 .coverage = std.debug.Coverage.init,
595 .mapped_memory = undefined, // populated below
596 .source_locations = undefined, // populated below
597 .entry_points = .{},
598 .start_timestamp = ws.now(),
599 };
600 errdefer gop.value_ptr.coverage.deinit(gpa);
601
602 const rebuilt_exe_path = run_step.rebuilt_executable.?;
603 var debug_info = std.debug.Info.load(gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| {
604 log.err("step '{s}': failed to load debug information for '{f}': {s}", .{
605 run_step.step.name, rebuilt_exe_path, @errorName(err),
606 });
607 return error.AlreadyReported;
608 };
609 defer debug_info.deinit(gpa);
610
611 const coverage_file_path: Build.Cache.Path = .{
612 .root_dir = run_step.step.owner.cache_root,
613 .sub_path = "v/" ++ std.fmt.hex(coverage_id),
614 };
615 var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {
616 log.err("step '{s}': failed to load coverage file '{f}': {s}", .{
617 run_step.step.name, coverage_file_path, @errorName(err),
618 });
619 return error.AlreadyReported;
620 };
621 defer coverage_file.close();
622
623 const file_size = coverage_file.getEndPos() catch |err| {
624 log.err("unable to check len of coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });
625 return error.AlreadyReported;
626 };
627
628 const mapped_memory = std.posix.mmap(
629 null,
630 file_size,
631 std.posix.PROT.READ,
632 .{ .TYPE = .SHARED },
633 coverage_file.handle,
634 0,
635 ) catch |err| {
636 log.err("failed to map coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });
637 return error.AlreadyReported;
638 };
639 gop.value_ptr.mapped_memory = mapped_memory;
640
641 const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
642 const pcs = header.pcAddrs();
643 const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
644 errdefer gpa.free(source_locations);
645
646 // Unfortunately the PCs array that LLVM gives us from the 8-bit PC
647 // counters feature is not sorted.
648 var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .{};
649 defer sorted_pcs.deinit(gpa);
650 try sorted_pcs.resize(gpa, pcs.len);
651 @memcpy(sorted_pcs.items(.pc), pcs);
652 for (sorted_pcs.items(.index), 0..) |*v, i| v.* = @intCast(i);
653 sorted_pcs.sortUnstable(struct {
654 addrs: []const u64,
655
656 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
657 return ctx.addrs[a_index] < ctx.addrs[b_index];
658 }
659 }{ .addrs = sorted_pcs.items(.pc) });
660
661 debug_info.resolveAddresses(gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
662 log.err("failed to resolve addresses to source locations: {s}", .{@errorName(err)});
663 return error.AlreadyReported;
664 };
665
666 for (sorted_pcs.items(.index), sorted_pcs.items(.sl)) |i, sl| source_locations[i] = sl;
667 gop.value_ptr.source_locations = source_locations;
668
669 ws.coverage_condition.broadcast();
670}
671
672fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyReported, OutOfMemory }!void {
673 ws.coverage_mutex.lock();
674 defer ws.coverage_mutex.unlock();
675
676 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;
677 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
678 const pcs = header.pcAddrs();
679 // Since this pcs list is unsorted, we must linear scan for the best index.
680 const index = i: {
681 var best: usize = 0;
682 for (pcs[1..], 1..) |elem_addr, i| {
683 if (elem_addr == addr) break :i i;
684 if (elem_addr > addr) continue;
685 if (elem_addr > pcs[best]) best = i;
686 }
687 break :i best;
688 };
689 if (index >= pcs.len) {
690 log.err("unable to find unit test entry address 0x{x} in source locations (range: 0x{x} to 0x{x})", .{
691 addr, pcs[0], pcs[pcs.len - 1],
692 });
693 return error.AlreadyReported;
694 }
695 if (false) {
696 const sl = coverage_map.source_locations[index];
697 const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);
698 log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{
699 addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1],
700 });
701 }
702 const gpa = ws.gpa;
703 try coverage_map.entry_points.append(gpa, @intCast(index));
704}
705
706fn makeIov(s: []const u8) std.posix.iovec_const {
707 return .{
708 .base = s.ptr,
709 .len = s.len,
710 };
711}
lib/std/Build/Fuzz/abi.zig deleted-112
...@@ -1,112 +0,0 @@
1//! This file is shared among Zig code running in wildly different contexts:
2//! libfuzzer, compiled alongside unit tests, the build runner, running on the
3//! host computer, and the fuzzing web interface webassembly code running in
4//! the browser. All of these components interface to some degree via an ABI.
5
6/// libfuzzer uses this and its usize is the one that counts. To match the ABI,
7/// make the ints be the size of the target used with libfuzzer.
8///
9/// Trailing:
10/// * 1 bit per pc_addr, usize elements
11/// * pc_addr: usize for each pcs_len
12pub const SeenPcsHeader = extern struct {
13 n_runs: usize,
14 unique_runs: usize,
15 pcs_len: usize,
16
17 /// Used for comptime assertions. Provides a mechanism for strategically
18 /// causing compile errors.
19 pub const trailing = .{
20 .pc_bits_usize,
21 .pc_addr,
22 };
23
24 pub fn headerEnd(header: *const SeenPcsHeader) []const usize {
25 const ptr: [*]align(@alignOf(usize)) const u8 = @ptrCast(header);
26 const header_end_ptr: [*]const usize = @ptrCast(ptr + @sizeOf(SeenPcsHeader));
27 const pcs_len = header.pcs_len;
28 return header_end_ptr[0 .. pcs_len + seenElemsLen(pcs_len)];
29 }
30
31 pub fn seenBits(header: *const SeenPcsHeader) []const usize {
32 return header.headerEnd()[0..seenElemsLen(header.pcs_len)];
33 }
34
35 pub fn seenElemsLen(pcs_len: usize) usize {
36 return (pcs_len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
37 }
38
39 pub fn pcAddrs(header: *const SeenPcsHeader) []const usize {
40 const pcs_len = header.pcs_len;
41 return header.headerEnd()[seenElemsLen(pcs_len)..][0..pcs_len];
42 }
43};
44
45pub const ToClientTag = enum(u8) {
46 current_time,
47 source_index,
48 coverage_update,
49 entry_points,
50 _,
51};
52
53pub 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
59/// Sent to the fuzzer web client on first connection to the websocket URL.
60///
61/// Trailing:
62/// * std.debug.Coverage.String for each directories_len
63/// * std.debug.Coverage.File for each files_len
64/// * std.debug.Coverage.SourceLocation for each source_locations_len
65/// * u8 for each string_bytes_len
66pub const SourceIndexHeader = extern struct {
67 flags: Flags,
68 directories_len: u32,
69 files_len: u32,
70 source_locations_len: u32,
71 string_bytes_len: u32,
72 /// When, according to the server, fuzzing started.
73 start_timestamp: i64 align(4),
74
75 pub const Flags = packed struct(u32) {
76 tag: ToClientTag = .source_index,
77 _: u24 = 0,
78 };
79};
80
81/// Sent to the fuzzer web client whenever the set of covered source locations
82/// changes.
83///
84/// Trailing:
85/// * one bit per source_locations_len, contained in u64 elements
86pub const CoverageUpdateHeader = extern struct {
87 flags: Flags = .{},
88 n_runs: u64,
89 unique_runs: u64,
90
91 pub const Flags = packed struct(u64) {
92 tag: ToClientTag = .coverage_update,
93 _: u56 = 0,
94 };
95
96 pub const trailing = .{
97 .pc_bits_usize,
98 };
99};
100
101/// Sent to the fuzzer web client when the set of entry points is updated.
102///
103/// Trailing:
104/// * one u32 index of source_locations per locs_len
105pub const EntryPointHeader = extern struct {
106 flags: Flags,
107
108 pub const Flags = packed struct(u32) {
109 tag: ToClientTag = .entry_points,
110 locs_len: u24,
111 };
112};
lib/std/Build/Step.zig+55-17
...@@ -72,6 +72,14 @@ pub const MakeOptions = struct {...@@ -72,6 +72,14 @@ pub const MakeOptions = struct {
72 progress_node: std.Progress.Node,72 progress_node: std.Progress.Node,
73 thread_pool: *std.Thread.Pool,73 thread_pool: *std.Thread.Pool,
74 watch: bool,74 watch: bool,
75 web_server: switch (builtin.target.cpu.arch) {
76 else => ?*Build.WebServer,
77 // WASM code references `Build.abi` which happens to incidentally reference this type, but
78 // it currently breaks because `std.net.Address` doesn't work there. Work around for now.
79 .wasm32 => void,
80 },
81 /// Not to be confused with `Build.allocator`, which is an alias of `Build.graph.arena`.
82 gpa: Allocator,
75};83};
7684
77pub const MakeFn = *const fn (step: *Step, options: MakeOptions) anyerror!void;85pub const MakeFn = *const fn (step: *Step, options: MakeOptions) anyerror!void;
...@@ -229,7 +237,17 @@ pub fn init(options: StepOptions) Step {...@@ -229,7 +237,17 @@ pub fn init(options: StepOptions) Step {
229pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void {237pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void {
230 const arena = s.owner.allocator;238 const arena = s.owner.allocator;
231239
232 s.makeFn(s, options) catch |err| switch (err) {240 var timer: ?std.time.Timer = t: {
241 if (!s.owner.graph.time_report) break :t null;
242 if (s.id == .compile) break :t null;
243 break :t std.time.Timer.start() catch @panic("--time-report not supported on this host");
244 };
245 const make_result = s.makeFn(s, options);
246 if (timer) |*t| {
247 options.web_server.?.updateTimeReportGeneric(s, t.read());
248 }
249
250 make_result catch |err| switch (err) {
233 error.MakeFailed => return error.MakeFailed,251 error.MakeFailed => return error.MakeFailed,
234 error.MakeSkipped => return error.MakeSkipped,252 error.MakeSkipped => return error.MakeSkipped,
235 else => {253 else => {
...@@ -372,18 +390,20 @@ pub fn evalZigProcess(...@@ -372,18 +390,20 @@ pub fn evalZigProcess(
372 argv: []const []const u8,390 argv: []const []const u8,
373 prog_node: std.Progress.Node,391 prog_node: std.Progress.Node,
374 watch: bool,392 watch: bool,
393 web_server: ?*Build.WebServer,
394 gpa: Allocator,
375) !?Path {395) !?Path {
376 if (s.getZigProcess()) |zp| update: {396 if (s.getZigProcess()) |zp| update: {
377 assert(watch);397 assert(watch);
378 if (std.Progress.have_ipc) if (zp.progress_ipc_fd) |fd| prog_node.setIpcFd(fd);398 if (std.Progress.have_ipc) if (zp.progress_ipc_fd) |fd| prog_node.setIpcFd(fd);
379 const result = zigProcessUpdate(s, zp, watch) catch |err| switch (err) {399 const result = zigProcessUpdate(s, zp, watch, web_server, gpa) catch |err| switch (err) {
380 error.BrokenPipe => {400 error.BrokenPipe => {
381 // Process restart required.401 // Process restart required.
382 const term = zp.child.wait() catch |e| {402 const term = zp.child.wait() catch |e| {
383 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) });403 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) });
384 };404 };
385 _ = term;405 _ = term;
386 s.clearZigProcess();406 s.clearZigProcess(gpa);
387 break :update;407 break :update;
388 },408 },
389 else => |e| return e,409 else => |e| return e,
...@@ -398,7 +418,7 @@ pub fn evalZigProcess(...@@ -398,7 +418,7 @@ pub fn evalZigProcess(
398 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) });418 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) });
399 };419 };
400 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;420 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
401 s.clearZigProcess();421 s.clearZigProcess(gpa);
402 try handleChildProcessTerm(s, term, null, argv);422 try handleChildProcessTerm(s, term, null, argv);
403 return error.MakeFailed;423 return error.MakeFailed;
404 }424 }
...@@ -408,7 +428,6 @@ pub fn evalZigProcess(...@@ -408,7 +428,6 @@ pub fn evalZigProcess(
408 assert(argv.len != 0);428 assert(argv.len != 0);
409 const b = s.owner;429 const b = s.owner;
410 const arena = b.allocator;430 const arena = b.allocator;
411 const gpa = arena;
412431
413 try handleChildProcUnsupported(s, null, argv);432 try handleChildProcUnsupported(s, null, argv);
414 try handleVerbose(s.owner, null, argv);433 try handleVerbose(s.owner, null, argv);
...@@ -435,9 +454,12 @@ pub fn evalZigProcess(...@@ -435,9 +454,12 @@ pub fn evalZigProcess(
435 .progress_ipc_fd = if (std.Progress.have_ipc) child.progress_node.getIpcFd() else {},454 .progress_ipc_fd = if (std.Progress.have_ipc) child.progress_node.getIpcFd() else {},
436 };455 };
437 if (watch) s.setZigProcess(zp);456 if (watch) s.setZigProcess(zp);
438 defer if (!watch) zp.poller.deinit();457 defer if (!watch) {
458 zp.poller.deinit();
459 gpa.destroy(zp);
460 };
439461
440 const result = try zigProcessUpdate(s, zp, watch);462 const result = try zigProcessUpdate(s, zp, watch, web_server, gpa);
441463
442 if (!watch) {464 if (!watch) {
443 // Send EOF to stdin.465 // Send EOF to stdin.
...@@ -499,7 +521,7 @@ pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {...@@ -499,7 +521,7 @@ pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {
499 };521 };
500}522}
501523
502fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {524fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.WebServer, gpa: Allocator) !?Path {
503 const b = s.owner;525 const b = s.owner;
504 const arena = b.allocator;526 const arena = b.allocator;
505527
...@@ -537,12 +559,14 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {...@@ -537,12 +559,14 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {
537 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];559 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
538 // TODO: use @ptrCast when the compiler supports it560 // TODO: use @ptrCast when the compiler supports it
539 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);561 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
540 const extra_array = try arena.alloc(u32, unaligned_extra.len);562 {
541 @memcpy(extra_array, unaligned_extra);563 s.result_error_bundle = .{ .string_bytes = &.{}, .extra = &.{} };
542 s.result_error_bundle = .{564 errdefer s.result_error_bundle.deinit(gpa);
543 .string_bytes = try arena.dupe(u8, string_bytes),565 s.result_error_bundle.string_bytes = try gpa.dupe(u8, string_bytes);
544 .extra = extra_array,566 const extra = try gpa.alloc(u32, unaligned_extra.len);
545 };567 @memcpy(extra, unaligned_extra);
568 s.result_error_bundle.extra = extra;
569 }
546 // This message indicates the end of the update.570 // This message indicates the end of the update.
547 if (watch) break :poll;571 if (watch) break :poll;
548 },572 },
...@@ -602,6 +626,20 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {...@@ -602,6 +626,20 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {
602 }626 }
603 }627 }
604 },628 },
629 .time_report => if (web_server) |ws| {
630 const TimeReport = std.zig.Server.Message.TimeReport;
631 const tr: *align(1) const TimeReport = @ptrCast(body[0..@sizeOf(TimeReport)]);
632 ws.updateTimeReportCompile(.{
633 .compile = s.cast(Step.Compile).?,
634 .use_llvm = tr.flags.use_llvm,
635 .stats = tr.stats,
636 .ns_total = timer.read(),
637 .llvm_pass_timings_len = tr.llvm_pass_timings_len,
638 .files_len = tr.files_len,
639 .decls_len = tr.decls_len,
640 .trailing = body[@sizeOf(TimeReport)..],
641 });
642 },
605 else => {}, // ignore other messages643 else => {}, // ignore other messages
606 }644 }
607 }645 }
...@@ -630,8 +668,7 @@ fn setZigProcess(s: *Step, zp: *ZigProcess) void {...@@ -630,8 +668,7 @@ fn setZigProcess(s: *Step, zp: *ZigProcess) void {
630 }668 }
631}669}
632670
633fn clearZigProcess(s: *Step) void {671fn clearZigProcess(s: *Step, gpa: Allocator) void {
634 const gpa = s.owner.allocator;
635 switch (s.id) {672 switch (s.id) {
636 .compile => {673 .compile => {
637 const compile = s.cast(Compile).?;674 const compile = s.cast(Compile).?;
...@@ -947,7 +984,8 @@ fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const...@@ -947,7 +984,8 @@ fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const
947 try gop.value_ptr.append(gpa, basename);984 try gop.value_ptr.append(gpa, basename);
948}985}
949986
950fn reset(step: *Step, gpa: Allocator) void {987/// Implementation detail of file watching and forced rebuilds. Prepares the step for being re-evaluated.
988pub fn reset(step: *Step, gpa: Allocator) void {
951 assert(step.state == .precheck_done);989 assert(step.state == .precheck_done);
952990
953 step.result_error_msgs.clearRetainingCapacity();991 step.result_error_msgs.clearRetainingCapacity();
lib/std/Build/Step/Compile.zig+5-4
...@@ -1491,6 +1491,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1491,6 +1491,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1491 if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link");1491 if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link");
1492 if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc");1492 if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc");
1493 if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");1493 if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
1494 if (b.graph.time_report) try zig_args.append("--time-report");
14941495
1495 if (compile.generated_asm != null) try zig_args.append("-femit-asm");1496 if (compile.generated_asm != null) try zig_args.append("-femit-asm");
1496 if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin");1497 if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin");
...@@ -1851,6 +1852,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -1851,6 +1852,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
1851 zig_args,1852 zig_args,
1852 options.progress_node,1853 options.progress_node,
1853 (b.graph.incremental == true) and options.watch,1854 (b.graph.incremental == true) and options.watch,
1855 options.web_server,
1856 options.gpa,
1854 ) catch |err| switch (err) {1857 ) catch |err| switch (err) {
1855 error.NeedCompileErrorCheck => {1858 error.NeedCompileErrorCheck => {
1856 assert(compile.expect_errors != null);1859 assert(compile.expect_errors != null);
...@@ -1905,9 +1908,7 @@ fn outputPath(c: *Compile, out_dir: std.Build.Cache.Path, ea: std.zig.EmitArtifa...@@ -1905,9 +1908,7 @@ fn outputPath(c: *Compile, out_dir: std.Build.Cache.Path, ea: std.zig.EmitArtifa
1905 return out_dir.joinString(arena, name) catch @panic("OOM");1908 return out_dir.joinString(arena, name) catch @panic("OOM");
1906}1909}
19071910
1908pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path {1911pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progress.Node) !Path {
1909 const gpa = c.step.owner.allocator;
1910
1911 c.step.result_error_msgs.clearRetainingCapacity();1912 c.step.result_error_msgs.clearRetainingCapacity();
1912 c.step.result_stderr = "";1913 c.step.result_stderr = "";
19131914
...@@ -1915,7 +1916,7 @@ pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path {...@@ -1915,7 +1916,7 @@ pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path {
1915 c.step.result_error_bundle = std.zig.ErrorBundle.empty;1916 c.step.result_error_bundle = std.zig.ErrorBundle.empty;
19161917
1917 const zig_args = try getZigArgs(c, true);1918 const zig_args = try getZigArgs(c, true);
1918 const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false);1919 const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false, null, gpa);
1919 return maybe_output_bin_path.?;1920 return maybe_output_bin_path.?;
1920}1921}
19211922
lib/std/Build/Step/ObjCopy.zig+1-1
...@@ -236,7 +236,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -236,7 +236,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
236 try argv.appendSlice(&.{ full_src_path, full_dest_path });236 try argv.appendSlice(&.{ full_src_path, full_dest_path });
237237
238 try argv.append("--listen=-");238 try argv.append("--listen=-");
239 _ = try step.evalZigProcess(argv.items, prog_node, false);239 _ = try step.evalZigProcess(argv.items, prog_node, false, options.web_server, options.gpa);
240240
241 objcopy.output_file.path = full_dest_path;241 objcopy.output_file.path = full_dest_path;
242 if (objcopy.output_file_debug) |*file| file.path = full_dest_path_debug;242 if (objcopy.output_file_debug) |*file| file.path = full_dest_path_debug;
lib/std/Build/Step/Options.zig+1
...@@ -549,6 +549,7 @@ test Options {...@@ -549,6 +549,7 @@ test Options {
549 .result = try std.zig.system.resolveTargetQuery(.{}),549 .result = try std.zig.system.resolveTargetQuery(.{}),
550 },550 },
551 .zig_lib_directory = std.Build.Cache.Directory.cwd(),551 .zig_lib_directory = std.Build.Cache.Directory.cwd(),
552 .time_report = false,
552 };553 };
553554
554 var builder = try std.Build.create(555 var builder = try std.Build.create(
lib/std/Build/Step/Run.zig+13-13
...@@ -944,7 +944,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -944,7 +944,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
944944
945pub fn rerunInFuzzMode(945pub fn rerunInFuzzMode(
946 run: *Run,946 run: *Run,
947 web_server: *std.Build.Fuzz.WebServer,947 fuzz: *std.Build.Fuzz,
948 unit_test_index: u32,948 unit_test_index: u32,
949 prog_node: std.Progress.Node,949 prog_node: std.Progress.Node,
950) !void {950) !void {
...@@ -984,7 +984,7 @@ pub fn rerunInFuzzMode(...@@ -984,7 +984,7 @@ pub fn rerunInFuzzMode(
984 const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);984 const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
985 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, .{985 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, .{
986 .unit_test_index = unit_test_index,986 .unit_test_index = unit_test_index,
987 .web_server = web_server,987 .fuzz = fuzz,
988 });988 });
989}989}
990990
...@@ -1054,7 +1054,7 @@ fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term...@@ -1054,7 +1054,7 @@ fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term
1054}1054}
10551055
1056const FuzzContext = struct {1056const FuzzContext = struct {
1057 web_server: *std.Build.Fuzz.WebServer,1057 fuzz: *std.Build.Fuzz,
1058 unit_test_index: u32,1058 unit_test_index: u32,
1059};1059};
10601060
...@@ -1638,31 +1638,31 @@ fn evalZigTest(...@@ -1638,31 +1638,31 @@ fn evalZigTest(
1638 };1638 };
1639 },1639 },
1640 .coverage_id => {1640 .coverage_id => {
1641 const web_server = fuzz_context.?.web_server;1641 const fuzz = fuzz_context.?.fuzz;
1642 const msg_ptr: *align(1) const u64 = @ptrCast(body);1642 const msg_ptr: *align(1) const u64 = @ptrCast(body);
1643 coverage_id = msg_ptr.*;1643 coverage_id = msg_ptr.*;
1644 {1644 {
1645 web_server.mutex.lock();1645 fuzz.queue_mutex.lock();
1646 defer web_server.mutex.unlock();1646 defer fuzz.queue_mutex.unlock();
1647 try web_server.msg_queue.append(web_server.gpa, .{ .coverage = .{1647 try fuzz.msg_queue.append(fuzz.ws.gpa, .{ .coverage = .{
1648 .id = coverage_id.?,1648 .id = coverage_id.?,
1649 .run = run,1649 .run = run,
1650 } });1650 } });
1651 web_server.condition.signal();1651 fuzz.queue_cond.signal();
1652 }1652 }
1653 },1653 },
1654 .fuzz_start_addr => {1654 .fuzz_start_addr => {
1655 const web_server = fuzz_context.?.web_server;1655 const fuzz = fuzz_context.?.fuzz;
1656 const msg_ptr: *align(1) const u64 = @ptrCast(body);1656 const msg_ptr: *align(1) const u64 = @ptrCast(body);
1657 const addr = msg_ptr.*;1657 const addr = msg_ptr.*;
1658 {1658 {
1659 web_server.mutex.lock();1659 fuzz.queue_mutex.lock();
1660 defer web_server.mutex.unlock();1660 defer fuzz.queue_mutex.unlock();
1661 try web_server.msg_queue.append(web_server.gpa, .{ .entry_point = .{1661 try fuzz.msg_queue.append(fuzz.ws.gpa, .{ .entry_point = .{
1662 .addr = addr,1662 .addr = addr,
1663 .coverage_id = coverage_id.?,1663 .coverage_id = coverage_id.?,
1664 } });1664 } });
1665 web_server.condition.signal();1665 fuzz.queue_cond.signal();
1666 }1666 }
1667 },1667 },
1668 else => {}, // ignore other messages1668 else => {}, // ignore other messages
lib/std/Build/Step/TranslateC.zig+1-1
...@@ -187,7 +187,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -187,7 +187,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
187 const c_source_path = translate_c.source.getPath2(b, step);187 const c_source_path = translate_c.source.getPath2(b, step);
188 try argv_list.append(c_source_path);188 try argv_list.append(c_source_path);
189189
190 const output_dir = try step.evalZigProcess(argv_list.items, prog_node, false);190 const output_dir = try step.evalZigProcess(argv_list.items, prog_node, false, options.web_server, options.gpa);
191191
192 const basename = std.fs.path.stem(std.fs.path.basename(c_source_path));192 const basename = std.fs.path.stem(std.fs.path.basename(c_source_path));
193 translate_c.out_basename = b.fmt("{s}.zig", .{basename});193 translate_c.out_basename = b.fmt("{s}.zig", .{basename});
lib/std/Build/WebServer.zig created+823
...@@ -0,0 +1,823 @@
1gpa: Allocator,
2thread_pool: *std.Thread.Pool,
3graph: *const Build.Graph,
4all_steps: []const *Build.Step,
5listen_address: std.net.Address,
6ttyconf: std.io.tty.Config,
7root_prog_node: std.Progress.Node,
8watch: bool,
9
10tcp_server: ?std.net.Server,
11serve_thread: ?std.Thread,
12
13base_timestamp: i128,
14/// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`.
15step_names_trailing: []u8,
16
17/// The bit-packed "step status" data. Values are `abi.StepUpdate.Status`. LSBs are earlier steps.
18/// Accessed atomically.
19step_status_bits: []u8,
20
21fuzz: ?Fuzz,
22time_report_mutex: std.Thread.Mutex,
23time_report_msgs: [][]u8,
24time_report_update_times: []i64,
25
26build_status: std.atomic.Value(abi.BuildStatus),
27/// When an event occurs which means WebSocket clients should be sent updates, call `notifyUpdate`
28/// to increment this value. Each client thread waits for this increment with `std.Thread.Futex`, so
29/// `notifyUpdate` will wake those threads. Updates are sent on a short interval regardless, so it
30/// is recommended to only use `notifyUpdate` for changes which the user should see immediately. For
31/// instance, we do not call `notifyUpdate` when the number of "unique runs" in the fuzzer changes,
32/// because this value changes quickly so this would result in constantly spamming all clients with
33/// an unreasonable number of packets.
34update_id: std.atomic.Value(u32),
35
36runner_request_mutex: std.Thread.Mutex,
37runner_request_ready_cond: std.Thread.Condition,
38runner_request_empty_cond: std.Thread.Condition,
39runner_request: ?RunnerRequest,
40
41/// If a client is not explicitly notified of changes with `notifyUpdate`, it will be sent updates
42/// on a fixed interval of this many milliseconds.
43const default_update_interval_ms = 500;
44
45/// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`.
46pub fn notifyUpdate(ws: *WebServer) void {
47 _ = ws.update_id.rmw(.Add, 1, .release);
48 std.Thread.Futex.wake(&ws.update_id, 16);
49}
50
51pub const Options = struct {
52 gpa: Allocator,
53 thread_pool: *std.Thread.Pool,
54 graph: *const std.Build.Graph,
55 all_steps: []const *Build.Step,
56 ttyconf: std.io.tty.Config,
57 root_prog_node: std.Progress.Node,
58 watch: bool,
59 listen_address: std.net.Address,
60};
61pub fn init(opts: Options) WebServer {
62 if (builtin.single_threaded) {
63 // The upcoming `std.Io` interface should allow us to use `Io.async` and `Io.concurrent`
64 // instead of threads, so that the web server can function in single-threaded builds.
65 std.process.fatal("--webui not yet implemented for single-threaded builds", .{});
66 }
67
68 if (builtin.os.tag == .windows) {
69 // At the time of writing, there are two bugs in the standard library which break this feature on Windows:
70 // * Reading from a socket on one thread while writing to it on another seems to deadlock.
71 // * Vectored writes to sockets currently trigger an infinite loop when a buffer has length 0.
72 //
73 // Both of these bugs are expected to be solved by changes which are currently in the unmerged
74 // 'wrangle-writer-buffering' branch. Until that makes it in, this must remain disabled.
75 std.process.fatal("--webui is currently disabled on Windows due to bugs", .{});
76 }
77
78 const all_steps = opts.all_steps;
79
80 const step_names_trailing = opts.gpa.alloc(u8, len: {
81 var name_bytes: usize = 0;
82 for (all_steps) |step| name_bytes += step.name.len;
83 break :len name_bytes + all_steps.len * 4;
84 }) catch @panic("out of memory");
85 {
86 const step_name_lens: []align(1) u32 = @ptrCast(step_names_trailing[0 .. all_steps.len * 4]);
87 var idx: usize = all_steps.len * 4;
88 for (all_steps, step_name_lens) |step, *name_len| {
89 name_len.* = @intCast(step.name.len);
90 @memcpy(step_names_trailing[idx..][0..step.name.len], step.name);
91 idx += step.name.len;
92 }
93 assert(idx == step_names_trailing.len);
94 }
95
96 const step_status_bits = opts.gpa.alloc(
97 u8,
98 std.math.divCeil(usize, all_steps.len, 4) catch unreachable,
99 ) catch @panic("out of memory");
100 @memset(step_status_bits, 0);
101
102 const time_reports_len: usize = if (opts.graph.time_report) all_steps.len else 0;
103 const time_report_msgs = opts.gpa.alloc([]u8, time_reports_len) catch @panic("out of memory");
104 const time_report_update_times = opts.gpa.alloc(i64, time_reports_len) catch @panic("out of memory");
105 @memset(time_report_msgs, &.{});
106 @memset(time_report_update_times, std.math.minInt(i64));
107
108 return .{
109 .gpa = opts.gpa,
110 .thread_pool = opts.thread_pool,
111 .graph = opts.graph,
112 .all_steps = all_steps,
113 .listen_address = opts.listen_address,
114 .ttyconf = opts.ttyconf,
115 .root_prog_node = opts.root_prog_node,
116 .watch = opts.watch,
117
118 .tcp_server = null,
119 .serve_thread = null,
120
121 .base_timestamp = std.time.nanoTimestamp(),
122 .step_names_trailing = step_names_trailing,
123
124 .step_status_bits = step_status_bits,
125
126 .fuzz = null,
127 .time_report_mutex = .{},
128 .time_report_msgs = time_report_msgs,
129 .time_report_update_times = time_report_update_times,
130
131 .build_status = .init(.idle),
132 .update_id = .init(0),
133
134 .runner_request_mutex = .{},
135 .runner_request_ready_cond = .{},
136 .runner_request_empty_cond = .{},
137 .runner_request = null,
138 };
139}
140pub fn deinit(ws: *WebServer) void {
141 const gpa = ws.gpa;
142
143 gpa.free(ws.step_names_trailing);
144 gpa.free(ws.step_status_bits);
145
146 if (ws.fuzz) |*f| f.deinit();
147 for (ws.time_report_msgs) |msg| gpa.free(msg);
148 gpa.free(ws.time_report_msgs);
149 gpa.free(ws.time_report_update_times);
150
151 if (ws.serve_thread) |t| {
152 if (ws.tcp_server) |*s| s.stream.close();
153 t.join();
154 }
155 if (ws.tcp_server) |*s| s.deinit();
156
157 gpa.free(ws.step_names_trailing);
158}
159pub fn start(ws: *WebServer) error{AlreadyReported}!void {
160 assert(ws.tcp_server == null);
161 assert(ws.serve_thread == null);
162
163 ws.tcp_server = ws.listen_address.listen(.{ .reuse_address = true }) catch |err| {
164 log.err("failed to listen to port {d}: {s}", .{ ws.listen_address.getPort(), @errorName(err) });
165 return error.AlreadyReported;
166 };
167 ws.serve_thread = std.Thread.spawn(.{}, serve, .{ws}) catch |err| {
168 log.err("unable to spawn web server thread: {s}", .{@errorName(err)});
169 ws.tcp_server.?.deinit();
170 ws.tcp_server = null;
171 return error.AlreadyReported;
172 };
173
174 log.info("web interface listening at http://{f}/", .{ws.tcp_server.?.listen_address});
175 if (ws.listen_address.getPort() == 0) {
176 log.info("hint: pass '--webui={f}' to use the same port next time", .{ws.tcp_server.?.listen_address});
177 }
178}
179fn serve(ws: *WebServer) void {
180 while (true) {
181 const connection = ws.tcp_server.?.accept() catch |err| {
182 log.err("failed to accept connection: {s}", .{@errorName(err)});
183 return;
184 };
185 _ = std.Thread.spawn(.{}, accept, .{ ws, connection }) catch |err| {
186 log.err("unable to spawn connection thread: {s}", .{@errorName(err)});
187 connection.stream.close();
188 continue;
189 };
190 }
191}
192
193pub fn startBuild(ws: *WebServer) void {
194 if (ws.fuzz) |*fuzz| {
195 fuzz.deinit();
196 ws.fuzz = null;
197 }
198 for (ws.step_status_bits) |*bits| @atomicStore(u8, bits, 0, .monotonic);
199 ws.build_status.store(.running, .monotonic);
200 ws.notifyUpdate();
201}
202
203pub fn updateStepStatus(ws: *WebServer, step: *Build.Step, new_status: abi.StepUpdate.Status) void {
204 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
205 if (s == step) break @intCast(i);
206 } else unreachable;
207 const ptr = &ws.step_status_bits[step_idx / 4];
208 const bit_offset: u3 = @intCast((step_idx % 4) * 2);
209 const old_bits: u2 = @truncate(@atomicLoad(u8, ptr, .monotonic) >> bit_offset);
210 const mask = @as(u8, @intFromEnum(new_status) ^ old_bits) << bit_offset;
211 _ = @atomicRmw(u8, ptr, .Xor, mask, .monotonic);
212 ws.notifyUpdate();
213}
214
215pub fn finishBuild(ws: *WebServer, opts: struct {
216 fuzz: bool,
217}) void {
218 if (opts.fuzz) {
219 switch (builtin.os.tag) {
220 // Current implementation depends on two things that need to be ported to Windows:
221 // * Memory-mapping to share data between the fuzzer and build runner.
222 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
223 // many addresses to source locations).
224 .windows => std.process.fatal("--fuzz not yet implemented for {s}", .{@tagName(builtin.os.tag)}),
225 else => {},
226 }
227 if (@bitSizeOf(usize) != 64) {
228 // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
229 // being compatible with `std.fs.getEndPos() u64`'s return value. This is not the case
230 // on 32-bit platforms.
231 // Affects or affected by issues #5185, #22523, and #22464.
232 std.process.fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
233 }
234 assert(ws.fuzz == null);
235
236 ws.build_status.store(.fuzz_init, .monotonic);
237 ws.notifyUpdate();
238
239 ws.fuzz = Fuzz.init(ws) catch |err| std.process.fatal("failed to start fuzzer: {s}", .{@errorName(err)});
240 ws.fuzz.?.start();
241 }
242
243 ws.build_status.store(if (ws.watch) .watching else .idle, .monotonic);
244 ws.notifyUpdate();
245}
246
247pub fn now(s: *const WebServer) i64 {
248 return @intCast(std.time.nanoTimestamp() - s.base_timestamp);
249}
250
251fn accept(ws: *WebServer, connection: std.net.Server.Connection) void {
252 defer connection.stream.close();
253
254 var read_buf: [0x4000]u8 = undefined;
255 var server: std.http.Server = .init(connection, &read_buf);
256
257 while (true) {
258 var request = server.receiveHead() catch |err| switch (err) {
259 error.HttpConnectionClosing => return,
260 else => {
261 log.err("failed to receive http request: {s}", .{@errorName(err)});
262 return;
263 },
264 };
265 var ws_send_buf: [0x4000]u8 = undefined;
266 var ws_recv_buf: [0x4000]u8 align(4) = undefined;
267 if (std.http.WebSocket.init(&request, &ws_send_buf, &ws_recv_buf) catch |err| {
268 log.err("failed to initialize websocket connection: {s}", .{@errorName(err)});
269 return;
270 }) |ws_init| {
271 var web_socket = ws_init;
272 ws.serveWebSocket(&web_socket) catch |err| {
273 log.err("failed to serve websocket: {s}", .{@errorName(err)});
274 return;
275 };
276 comptime unreachable;
277 } else {
278 ws.serveRequest(&request) catch |err| switch (err) {
279 error.AlreadyReported => return,
280 else => {
281 log.err("failed to serve '{s}': {s}", .{ request.head.target, @errorName(err) });
282 return;
283 },
284 };
285 }
286 }
287}
288
289fn makeIov(s: []const u8) std.posix.iovec_const {
290 return .{
291 .base = s.ptr,
292 .len = s.len,
293 };
294}
295fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn {
296 var prev_build_status = ws.build_status.load(.monotonic);
297
298 const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len);
299 defer ws.gpa.free(prev_step_status_bits);
300 for (prev_step_status_bits, ws.step_status_bits) |*copy, *shared| {
301 copy.* = @atomicLoad(u8, shared, .monotonic);
302 }
303
304 _ = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock });
305
306 {
307 const hello_header: abi.Hello = .{
308 .status = prev_build_status,
309 .flags = .{
310 .time_report = ws.graph.time_report,
311 },
312 .timestamp = ws.now(),
313 .steps_len = @intCast(ws.all_steps.len),
314 };
315 try sock.writeMessagev(&.{
316 makeIov(@ptrCast(&hello_header)),
317 makeIov(ws.step_names_trailing),
318 makeIov(prev_step_status_bits),
319 }, .binary);
320 }
321
322 var prev_fuzz: Fuzz.Previous = .init;
323 var prev_time: i64 = std.math.minInt(i64);
324 while (true) {
325 const start_time = ws.now();
326 const start_update_id = ws.update_id.load(.acquire);
327
328 if (ws.fuzz) |*fuzz| {
329 try fuzz.sendUpdate(sock, &prev_fuzz);
330 }
331
332 {
333 ws.time_report_mutex.lock();
334 defer ws.time_report_mutex.unlock();
335 for (ws.time_report_msgs, ws.time_report_update_times) |msg, update_time| {
336 if (update_time <= prev_time) continue;
337 // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so
338 // that we don't hold up the build system on the client accepting this packet.
339 const owned_msg = try ws.gpa.dupe(u8, msg);
340 defer ws.gpa.free(owned_msg);
341 // Temporarily unlock, then re-lock after the message is sent.
342 ws.time_report_mutex.unlock();
343 defer ws.time_report_mutex.lock();
344 try sock.writeMessage(msg, .binary);
345 }
346 }
347
348 {
349 const build_status = ws.build_status.load(.monotonic);
350 if (build_status != prev_build_status) {
351 prev_build_status = build_status;
352 const msg: abi.StatusUpdate = .{ .new = build_status };
353 try sock.writeMessage(@ptrCast(&msg), .binary);
354 }
355 }
356
357 for (prev_step_status_bits, ws.step_status_bits, 0..) |*prev_byte, *shared, byte_idx| {
358 const cur_byte = @atomicLoad(u8, shared, .monotonic);
359 if (prev_byte.* == cur_byte) continue;
360 const cur: [4]abi.StepUpdate.Status = .{
361 @enumFromInt(@as(u2, @truncate(cur_byte >> 0))),
362 @enumFromInt(@as(u2, @truncate(cur_byte >> 2))),
363 @enumFromInt(@as(u2, @truncate(cur_byte >> 4))),
364 @enumFromInt(@as(u2, @truncate(cur_byte >> 6))),
365 };
366 const prev: [4]abi.StepUpdate.Status = .{
367 @enumFromInt(@as(u2, @truncate(prev_byte.* >> 0))),
368 @enumFromInt(@as(u2, @truncate(prev_byte.* >> 2))),
369 @enumFromInt(@as(u2, @truncate(prev_byte.* >> 4))),
370 @enumFromInt(@as(u2, @truncate(prev_byte.* >> 6))),
371 };
372 for (cur, prev, byte_idx * 4..) |cur_status, prev_status, step_idx| {
373 const msg: abi.StepUpdate = .{ .step_idx = @intCast(step_idx), .bits = .{ .status = cur_status } };
374 if (cur_status != prev_status) try sock.writeMessage(@ptrCast(&msg), .binary);
375 }
376 prev_byte.* = cur_byte;
377 }
378
379 prev_time = start_time;
380 std.Thread.Futex.timedWait(&ws.update_id, start_update_id, std.time.ns_per_ms * default_update_interval_ms) catch {};
381 }
382}
383fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void {
384 while (true) {
385 const msg = sock.readSmallMessage() catch return;
386 if (msg.opcode != .binary) continue;
387 if (msg.data.len == 0) continue;
388 const tag: abi.ToServerTag = @enumFromInt(msg.data[0]);
389 switch (tag) {
390 _ => continue,
391 .rebuild => while (true) {
392 ws.runner_request_mutex.lock();
393 defer ws.runner_request_mutex.unlock();
394 if (ws.runner_request == null) {
395 ws.runner_request = .rebuild;
396 ws.runner_request_ready_cond.signal();
397 break;
398 }
399 ws.runner_request_empty_cond.wait(&ws.runner_request_mutex);
400 },
401 }
402 }
403}
404
405fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void {
406 // Strip an optional leading '/debug' component from the request.
407 const target: []const u8, const debug: bool = target: {
408 if (mem.eql(u8, req.head.target, "/debug")) break :target .{ "/", true };
409 if (mem.eql(u8, req.head.target, "/debug/")) break :target .{ "/", true };
410 if (mem.startsWith(u8, req.head.target, "/debug/")) break :target .{ req.head.target["/debug".len..], true };
411 break :target .{ req.head.target, false };
412 };
413
414 if (mem.eql(u8, target, "/")) return serveLibFile(ws, req, "build-web/index.html", "text/html");
415 if (mem.eql(u8, target, "/main.js")) return serveLibFile(ws, req, "build-web/main.js", "application/javascript");
416 if (mem.eql(u8, target, "/style.css")) return serveLibFile(ws, req, "build-web/style.css", "text/css");
417 if (mem.eql(u8, target, "/time_report.css")) return serveLibFile(ws, req, "build-web/time_report.css", "text/css");
418 if (mem.eql(u8, target, "/main.wasm")) return serveClientWasm(ws, req, if (debug) .Debug else .ReleaseFast);
419
420 if (ws.fuzz) |*fuzz| {
421 if (mem.eql(u8, target, "/sources.tar")) return fuzz.serveSourcesTar(req);
422 }
423
424 try req.respond("not found", .{
425 .status = .not_found,
426 .extra_headers = &.{
427 .{ .name = "Content-Type", .value = "text/plain" },
428 },
429 });
430}
431
432fn serveLibFile(
433 ws: *WebServer,
434 request: *std.http.Server.Request,
435 sub_path: []const u8,
436 content_type: []const u8,
437) !void {
438 return serveFile(ws, request, .{
439 .root_dir = ws.graph.zig_lib_directory,
440 .sub_path = sub_path,
441 }, content_type);
442}
443fn serveClientWasm(
444 ws: *WebServer,
445 req: *std.http.Server.Request,
446 optimize_mode: std.builtin.OptimizeMode,
447) !void {
448 var arena_state: std.heap.ArenaAllocator = .init(ws.gpa);
449 defer arena_state.deinit();
450 const arena = arena_state.allocator();
451
452 // We always rebuild the wasm on-the-fly, so that if it is edited the user can just refresh the page.
453 const bin_path = try buildClientWasm(ws, arena, optimize_mode);
454 return serveFile(ws, req, bin_path, "application/wasm");
455}
456
457pub fn serveFile(
458 ws: *WebServer,
459 request: *std.http.Server.Request,
460 path: Cache.Path,
461 content_type: []const u8,
462) !void {
463 const gpa = ws.gpa;
464 // The desired API is actually sendfile, which will require enhancing std.http.Server.
465 // We load the file with every request so that the user can make changes to the file
466 // and refresh the HTML page without restarting this server.
467 const file_contents = path.root_dir.handle.readFileAlloc(gpa, path.sub_path, 10 * 1024 * 1024) catch |err| {
468 log.err("failed to read '{f}': {s}", .{ path, @errorName(err) });
469 return error.AlreadyReported;
470 };
471 defer gpa.free(file_contents);
472 try request.respond(file_contents, .{
473 .extra_headers = &.{
474 .{ .name = "Content-Type", .value = content_type },
475 cache_control_header,
476 },
477 });
478}
479pub fn serveTarFile(
480 ws: *WebServer,
481 request: *std.http.Server.Request,
482 paths: []const Cache.Path,
483) !void {
484 const gpa = ws.gpa;
485
486 var send_buf: [0x4000]u8 = undefined;
487 var response = request.respondStreaming(.{
488 .send_buffer = &send_buf,
489 .respond_options = .{
490 .extra_headers = &.{
491 .{ .name = "Content-Type", .value = "application/x-tar" },
492 cache_control_header,
493 },
494 },
495 });
496
497 var cached_cwd_path: ?[]const u8 = null;
498 defer if (cached_cwd_path) |p| gpa.free(p);
499
500 var response_buf: [1024]u8 = undefined;
501 var adapter = response.writer().adaptToNewApi();
502 adapter.new_interface.buffer = &response_buf;
503 var archiver: std.tar.Writer = .{ .underlying_writer = &adapter.new_interface };
504
505 for (paths) |path| {
506 var file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| {
507 log.err("failed to open '{f}': {s}", .{ path, @errorName(err) });
508 continue;
509 };
510 defer file.close();
511 const stat = try file.stat();
512 var read_buffer: [1024]u8 = undefined;
513 var file_reader: std.fs.File.Reader = .initSize(file, &read_buffer, stat.size);
514
515 // TODO: this logic is completely bogus -- obviously so, because `path.root_dir.path` can
516 // be cwd-relative. This is also related to why linkification doesn't work in the fuzzer UI:
517 // it turns out the WASM treats the first path component as the module name, typically
518 // resulting in modules named "" and "src". The compiler needs to tell the build system
519 // about the module graph so that the build system can correctly encode this information in
520 // the tar file.
521 archiver.prefix = path.root_dir.path orelse cwd: {
522 if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa);
523 break :cwd cached_cwd_path.?;
524 };
525 try archiver.writeFile(path.sub_path, &file_reader, stat.mtime);
526 }
527
528 // intentionally not calling `archiver.finishPedantically`
529 try adapter.new_interface.flush();
530 try response.end();
531}
532
533fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path {
534 const root_name = "build-web";
535 const arch_os_abi = "wasm32-freestanding";
536 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
537
538 const gpa = ws.gpa;
539 const graph = ws.graph;
540
541 const main_src_path: Cache.Path = .{
542 .root_dir = graph.zig_lib_directory,
543 .sub_path = "build-web/main.zig",
544 };
545 const walk_src_path: Cache.Path = .{
546 .root_dir = graph.zig_lib_directory,
547 .sub_path = "docs/wasm/Walk.zig",
548 };
549 const html_render_src_path: Cache.Path = .{
550 .root_dir = graph.zig_lib_directory,
551 .sub_path = "docs/wasm/html_render.zig",
552 };
553
554 var argv: std.ArrayListUnmanaged([]const u8) = .empty;
555
556 try argv.appendSlice(arena, &.{
557 graph.zig_exe, "build-exe", //
558 "-fno-entry", //
559 "-O", @tagName(optimize), //
560 "-target", arch_os_abi, //
561 "-mcpu", cpu_features, //
562 "--cache-dir", graph.global_cache_root.path orelse ".", //
563 "--global-cache-dir", graph.global_cache_root.path orelse ".", //
564 "--zig-lib-dir", graph.zig_lib_directory.path orelse ".", //
565 "--name", root_name, //
566 "-rdynamic", //
567 "-fsingle-threaded", //
568 "--dep", "Walk", //
569 "--dep", "html_render", //
570 try std.fmt.allocPrint(arena, "-Mroot={f}", .{main_src_path}), //
571 try std.fmt.allocPrint(arena, "-MWalk={f}", .{walk_src_path}), //
572 "--dep", "Walk", //
573 try std.fmt.allocPrint(arena, "-Mhtml_render={f}", .{html_render_src_path}), //
574 "--listen=-",
575 });
576
577 var child: std.process.Child = .init(argv.items, gpa);
578 child.stdin_behavior = .Pipe;
579 child.stdout_behavior = .Pipe;
580 child.stderr_behavior = .Pipe;
581 try child.spawn();
582
583 var poller = std.io.poll(gpa, enum { stdout, stderr }, .{
584 .stdout = child.stdout.?,
585 .stderr = child.stderr.?,
586 });
587 defer poller.deinit();
588
589 try child.stdin.?.writeAll(@ptrCast(@as([]const std.zig.Client.Message.Header, &.{
590 .{ .tag = .update, .bytes_len = 0 },
591 .{ .tag = .exit, .bytes_len = 0 },
592 })));
593
594 const Header = std.zig.Server.Message.Header;
595 var result: ?Cache.Path = null;
596 var result_error_bundle = std.zig.ErrorBundle.empty;
597
598 const stdout = poller.reader(.stdout);
599
600 poll: while (true) {
601 while (stdout.buffered().len < @sizeOf(Header)) if (!(try poller.poll())) break :poll;
602 const header = stdout.takeStruct(Header, .little) catch unreachable;
603 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
604 const body = stdout.take(header.bytes_len) catch unreachable;
605
606 switch (header.tag) {
607 .zig_version => {
608 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
609 return error.ZigProtocolVersionMismatch;
610 }
611 },
612 .error_bundle => {
613 const EbHdr = std.zig.Server.Message.ErrorBundle;
614 const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body));
615 const extra_bytes =
616 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
617 const string_bytes =
618 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
619 const unaligned_extra: []align(1) const u32 = @ptrCast(extra_bytes);
620 const extra_array = try arena.alloc(u32, unaligned_extra.len);
621 @memcpy(extra_array, unaligned_extra);
622 result_error_bundle = .{
623 .string_bytes = try arena.dupe(u8, string_bytes),
624 .extra = extra_array,
625 };
626 },
627 .emit_digest => {
628 const EmitDigest = std.zig.Server.Message.EmitDigest;
629 const ebp_hdr: *align(1) const EmitDigest = @ptrCast(body);
630 if (!ebp_hdr.flags.cache_hit) {
631 log.info("source changes detected; rebuilt wasm component", .{});
632 }
633 const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
634 result = .{
635 .root_dir = graph.global_cache_root,
636 .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)),
637 };
638 },
639 else => {}, // ignore other messages
640 }
641 }
642
643 const stderr_contents = try poller.toOwnedSlice(.stderr);
644 if (stderr_contents.len > 0) {
645 std.debug.print("{s}", .{stderr_contents});
646 }
647
648 // Send EOF to stdin.
649 child.stdin.?.close();
650 child.stdin = null;
651
652 switch (try child.wait()) {
653 .Exited => |code| {
654 if (code != 0) {
655 log.err(
656 "the following command exited with error code {d}:\n{s}",
657 .{ code, try Build.Step.allocPrintCmd(arena, null, argv.items) },
658 );
659 return error.WasmCompilationFailed;
660 }
661 },
662 .Signal, .Stopped, .Unknown => {
663 log.err(
664 "the following command terminated unexpectedly:\n{s}",
665 .{try Build.Step.allocPrintCmd(arena, null, argv.items)},
666 );
667 return error.WasmCompilationFailed;
668 },
669 }
670
671 if (result_error_bundle.errorMessageCount() > 0) {
672 const color = std.zig.Color.auto;
673 result_error_bundle.renderToStdErr(color.renderOptions());
674 log.err("the following command failed with {d} compilation errors:\n{s}", .{
675 result_error_bundle.errorMessageCount(),
676 try Build.Step.allocPrintCmd(arena, null, argv.items),
677 });
678 return error.WasmCompilationFailed;
679 }
680
681 const base_path = result orelse {
682 log.err("child process failed to report result\n{s}", .{
683 try Build.Step.allocPrintCmd(arena, null, argv.items),
684 });
685 return error.WasmCompilationFailed;
686 };
687 const bin_name = try std.zig.binNameAlloc(arena, .{
688 .root_name = root_name,
689 .target = &(std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{
690 .arch_os_abi = arch_os_abi,
691 .cpu_features = cpu_features,
692 }) catch unreachable) catch unreachable),
693 .output_mode = .Exe,
694 });
695 return base_path.join(arena, bin_name);
696}
697
698pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
699 compile: *Build.Step.Compile,
700
701 use_llvm: bool,
702 stats: abi.time_report.CompileResult.Stats,
703 ns_total: u64,
704
705 llvm_pass_timings_len: u32,
706 files_len: u32,
707 decls_len: u32,
708
709 /// The trailing data of `abi.time_report.CompileResult`, except the step name.
710 trailing: []const u8,
711}) void {
712 const gpa = ws.gpa;
713
714 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
715 if (s == &opts.compile.step) break @intCast(i);
716 } else unreachable;
717
718 const old_buf = old: {
719 ws.time_report_mutex.lock();
720 defer ws.time_report_mutex.unlock();
721 const old = ws.time_report_msgs[step_idx];
722 ws.time_report_msgs[step_idx] = &.{};
723 break :old old;
724 };
725 const buf = gpa.realloc(old_buf, @sizeOf(abi.time_report.CompileResult) + opts.trailing.len) catch @panic("out of memory");
726
727 const out_header: *align(1) abi.time_report.CompileResult = @ptrCast(buf[0..@sizeOf(abi.time_report.CompileResult)]);
728 out_header.* = .{
729 .step_idx = step_idx,
730 .flags = .{
731 .use_llvm = opts.use_llvm,
732 },
733 .stats = opts.stats,
734 .ns_total = opts.ns_total,
735 .llvm_pass_timings_len = opts.llvm_pass_timings_len,
736 .files_len = opts.files_len,
737 .decls_len = opts.decls_len,
738 };
739 @memcpy(buf[@sizeOf(abi.time_report.CompileResult)..], opts.trailing);
740
741 {
742 ws.time_report_mutex.lock();
743 defer ws.time_report_mutex.unlock();
744 assert(ws.time_report_msgs[step_idx].len == 0);
745 ws.time_report_msgs[step_idx] = buf;
746 ws.time_report_update_times[step_idx] = ws.now();
747 }
748 ws.notifyUpdate();
749}
750
751pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) void {
752 const gpa = ws.gpa;
753
754 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
755 if (s == step) break @intCast(i);
756 } else unreachable;
757
758 const old_buf = old: {
759 ws.time_report_mutex.lock();
760 defer ws.time_report_mutex.unlock();
761 const old = ws.time_report_msgs[step_idx];
762 ws.time_report_msgs[step_idx] = &.{};
763 break :old old;
764 };
765 const buf = gpa.realloc(old_buf, @sizeOf(abi.time_report.GenericResult)) catch @panic("out of memory");
766 const out: *align(1) abi.time_report.GenericResult = @ptrCast(buf);
767 out.* = .{
768 .step_idx = step_idx,
769 .ns_total = ns_total,
770 };
771 {
772 ws.time_report_mutex.lock();
773 defer ws.time_report_mutex.unlock();
774 assert(ws.time_report_msgs[step_idx].len == 0);
775 ws.time_report_msgs[step_idx] = buf;
776 ws.time_report_update_times[step_idx] = ws.now();
777 }
778 ws.notifyUpdate();
779}
780
781const RunnerRequest = union(enum) {
782 rebuild,
783};
784pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
785 ws.runner_request_mutex.lock();
786 defer ws.runner_request_mutex.unlock();
787 if (ws.runner_request) |req| {
788 ws.runner_request = null;
789 ws.runner_request_empty_cond.signal();
790 return req;
791 }
792 return null;
793}
794pub fn wait(ws: *WebServer) RunnerRequest {
795 ws.runner_request_mutex.lock();
796 defer ws.runner_request_mutex.unlock();
797 while (true) {
798 if (ws.runner_request) |req| {
799 ws.runner_request = null;
800 ws.runner_request_empty_cond.signal();
801 return req;
802 }
803 ws.runner_request_ready_cond.wait(&ws.runner_request_mutex);
804 }
805}
806
807const cache_control_header: std.http.Header = .{
808 .name = "Cache-Control",
809 .value = "max-age=0, must-revalidate",
810};
811
812const builtin = @import("builtin");
813const std = @import("std");
814const assert = std.debug.assert;
815const mem = std.mem;
816const log = std.log.scoped(.web_server);
817const Allocator = std.mem.Allocator;
818const Build = std.Build;
819const Cache = Build.Cache;
820const Fuzz = Build.Fuzz;
821const abi = Build.abi;
822
823const WebServer = @This();
lib/std/Build/abi.zig created+313
...@@ -0,0 +1,313 @@
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
60 _,
61};
62
63/// Like `ToClientTag`, but for messages sent by the client to the server.
64pub const ToServerTag = enum(u8) {
65 rebuild,
66
67 _,
68};
69
70/// The current overall status of the build runner.
71/// Keep in sync with indices in web UI `main.js:updateBuildStatus`.
72pub const BuildStatus = enum(u8) {
73 idle,
74 watching,
75 running,
76 fuzz_init,
77};
78
79/// WebSocket server->client.
80///
81/// Sent by the server as the first message after a WebSocket connection opens to provide basic
82/// information about the server, the build graph, etc.
83///
84/// Trailing:
85/// * `step_name_len: u32` for each `steps_len`
86/// * `step_name: [step_name_len]u8` for each `step_name_len`
87/// * `step_status: u8` for every 4 `steps_len`; every 2 bits is a `StepUpdate.Status`, LSBs first
88pub const Hello = extern struct {
89 tag: ToClientTag = .hello,
90
91 status: BuildStatus,
92 flags: Flags,
93
94 /// Any message containing a timestamp represents it as a number of nanoseconds relative to when
95 /// the build began. This field is the current timestamp, represented in that form.
96 timestamp: i64 align(4),
97
98 /// The number of steps in the build graph which are reachable from the top-level step[s] being
99 /// run; in other words, the number of steps which will be executed by this build. The name of
100 /// each step trails this message.
101 steps_len: u32 align(1),
102
103 pub const Flags = packed struct(u16) {
104 /// Whether time reporting is enabled.
105 time_report: bool,
106 _: u15 = 0,
107 };
108};
109/// WebSocket server->client.
110///
111/// Indicates that the build status has changed.
112pub const StatusUpdate = extern struct {
113 tag: ToClientTag = .status_update,
114 new: BuildStatus,
115};
116/// WebSocket server->client.
117///
118/// Indicates a change in a step's status.
119pub const StepUpdate = extern struct {
120 tag: ToClientTag = .step_update,
121 step_idx: u32 align(1),
122 bits: packed struct(u8) {
123 status: Status,
124 _: u6 = 0,
125 },
126 /// Keep in sync with indices in web UI `main.js:updateStepStatus`.
127 pub const Status = enum(u2) {
128 pending,
129 wip,
130 success,
131 failure,
132 };
133};
134
135pub const Rebuild = extern struct {
136 tag: ToServerTag = .rebuild,
137};
138
139/// ABI bits specifically relating to the fuzzer interface.
140pub const fuzz = struct {
141 /// libfuzzer uses this and its usize is the one that counts. To match the ABI,
142 /// make the ints be the size of the target used with libfuzzer.
143 ///
144 /// Trailing:
145 /// * 1 bit per pc_addr, usize elements
146 /// * pc_addr: usize for each pcs_len
147 pub const SeenPcsHeader = extern struct {
148 n_runs: usize,
149 unique_runs: usize,
150 pcs_len: usize,
151
152 /// Used for comptime assertions. Provides a mechanism for strategically
153 /// causing compile errors.
154 pub const trailing = .{
155 .pc_bits_usize,
156 .pc_addr,
157 };
158
159 pub fn headerEnd(header: *const SeenPcsHeader) []const usize {
160 const ptr: [*]align(@alignOf(usize)) const u8 = @ptrCast(header);
161 const header_end_ptr: [*]const usize = @ptrCast(ptr + @sizeOf(SeenPcsHeader));
162 const pcs_len = header.pcs_len;
163 return header_end_ptr[0 .. pcs_len + seenElemsLen(pcs_len)];
164 }
165
166 pub fn seenBits(header: *const SeenPcsHeader) []const usize {
167 return header.headerEnd()[0..seenElemsLen(header.pcs_len)];
168 }
169
170 pub fn seenElemsLen(pcs_len: usize) usize {
171 return (pcs_len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
172 }
173
174 pub fn pcAddrs(header: *const SeenPcsHeader) []const usize {
175 const pcs_len = header.pcs_len;
176 return header.headerEnd()[seenElemsLen(pcs_len)..][0..pcs_len];
177 }
178 };
179
180 /// WebSocket server->client.
181 ///
182 /// Sent once, when fuzzing starts, to indicate the available coverage data.
183 ///
184 /// Trailing:
185 /// * std.debug.Coverage.String for each directories_len
186 /// * std.debug.Coverage.File for each files_len
187 /// * std.debug.Coverage.SourceLocation for each source_locations_len
188 /// * u8 for each string_bytes_len
189 pub const SourceIndexHeader = extern struct {
190 tag: ToClientTag = .fuzz_source_index,
191 _: [3]u8 = @splat(0),
192 directories_len: u32,
193 files_len: u32,
194 source_locations_len: u32,
195 string_bytes_len: u32,
196 /// When, according to the server, fuzzing started.
197 start_timestamp: i64 align(4),
198 };
199
200 /// WebSocket server->client.
201 ///
202 /// Sent whenever the set of covered source locations is updated.
203 ///
204 /// Trailing:
205 /// * one bit per source_locations_len, contained in u64 elements
206 pub const CoverageUpdateHeader = extern struct {
207 tag: ToClientTag = .fuzz_coverage_update,
208 _: [7]u8 = @splat(0),
209 n_runs: u64,
210 unique_runs: u64,
211
212 pub const trailing = .{
213 .pc_bits_usize,
214 };
215 };
216
217 /// WebSocket server->client.
218 ///
219 /// Sent whenever the set of entry points is updated.
220 ///
221 /// Trailing:
222 /// * one u32 index of source_locations per locsLen()
223 pub const EntryPointHeader = extern struct {
224 tag: ToClientTag = .fuzz_entry_points,
225 locs_len_raw: [3]u8,
226
227 pub fn locsLen(hdr: EntryPointHeader) u24 {
228 return @bitCast(hdr.locs_len_raw);
229 }
230 pub fn init(locs_len: u24) EntryPointHeader {
231 return .{ .locs_len_raw = @bitCast(locs_len) };
232 }
233 };
234};
235
236/// ABI bits specifically relating to the time report interface.
237pub const time_report = struct {
238 /// WebSocket server->client.
239 ///
240 /// Sent after a `Step` finishes, providing the time taken to execute the step.
241 pub const GenericResult = extern struct {
242 tag: ToClientTag = .time_report_generic_result,
243 step_idx: u32 align(1),
244 ns_total: u64 align(1),
245 };
246
247 /// WebSocket server->client.
248 ///
249 /// Sent after a `Step.Compile` finishes, providing the step's time report.
250 ///
251 /// Trailing:
252 /// * `llvm_pass_timings: [llvm_pass_timings_len]u8` (ASCII-encoded)
253 /// * for each `files_len`:
254 /// * `name` (null-terminated UTF-8 string)
255 /// * for each `decls_len`:
256 /// * `name` (null-terminated UTF-8 string)
257 /// * `file: u32` (index of file this decl is in)
258 /// * `sema_ns: u64` (nanoseconds spent semantically analyzing this decl)
259 /// * `codegen_ns: u64` (nanoseconds spent semantically analyzing this decl)
260 /// * `link_ns: u64` (nanoseconds spent semantically analyzing this decl)
261 pub const CompileResult = extern struct {
262 tag: ToClientTag = .time_report_compile_result,
263
264 step_idx: u32 align(1),
265
266 flags: Flags,
267 stats: Stats align(1),
268 ns_total: u64 align(1),
269
270 llvm_pass_timings_len: u32 align(1),
271 files_len: u32 align(1),
272 decls_len: u32 align(1),
273
274 pub const Flags = packed struct(u8) {
275 use_llvm: bool,
276 _: u7 = 0,
277 };
278
279 pub const Stats = extern struct {
280 n_reachable_files: u32,
281 n_imported_files: u32,
282 n_generic_instances: u32,
283 n_inline_calls: u32,
284
285 cpu_ns_parse: u64,
286 cpu_ns_astgen: u64,
287 cpu_ns_sema: u64,
288 cpu_ns_codegen: u64,
289 cpu_ns_link: u64,
290
291 real_ns_files: u64,
292 real_ns_decls: u64,
293 real_ns_llvm_emit: u64,
294 real_ns_link_flush: u64,
295
296 pub const init: Stats = .{
297 .n_reachable_files = 0,
298 .n_imported_files = 0,
299 .n_generic_instances = 0,
300 .n_inline_calls = 0,
301 .cpu_ns_parse = 0,
302 .cpu_ns_astgen = 0,
303 .cpu_ns_sema = 0,
304 .cpu_ns_codegen = 0,
305 .cpu_ns_link = 0,
306 .real_ns_files = 0,
307 .real_ns_decls = 0,
308 .real_ns_llvm_emit = 0,
309 .real_ns_link_flush = 0,
310 };
311 };
312 };
313};
lib/std/Io/Writer.zig+1-1
...@@ -2353,7 +2353,7 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -2353,7 +2353,7 @@ pub fn Hashed(comptime Hasher: type) type {
2353 this.hasher.update(slice);2353 this.hasher.update(slice);
2354 }2354 }
2355 const pattern = data[data.len - 1];2355 const pattern = data[data.len - 1];
2356 assert(remaining == splat * pattern.len);2356 assert(remaining <= splat * pattern.len);
2357 switch (pattern.len) {2357 switch (pattern.len) {
2358 0 => {2358 0 => {
2359 assert(remaining == 0);2359 assert(remaining == 0);
lib/std/compress/flate/Decompress.zig+21-18
...@@ -10,8 +10,8 @@ const Decompress = @This();...@@ -10,8 +10,8 @@ const Decompress = @This();
10const Token = @import("Token.zig");10const Token = @import("Token.zig");
1111
12input: *Reader,12input: *Reader,
13next_bits: usize,13next_bits: Bits,
14remaining_bits: std.math.Log2Int(usize),14remaining_bits: std.math.Log2Int(Bits),
1515
16reader: Reader,16reader: Reader,
1717
...@@ -25,6 +25,9 @@ state: State,...@@ -25,6 +25,9 @@ state: State,
2525
26err: ?Error,26err: ?Error,
2727
28/// TODO: change this to usize
29const Bits = u64;
30
28const BlockType = enum(u2) {31const BlockType = enum(u2) {
29 stored = 0,32 stored = 0,
30 fixed = 1,33 fixed = 1,
...@@ -498,14 +501,14 @@ fn takeBits(d: *Decompress, comptime U: type) !U {...@@ -498,14 +501,14 @@ fn takeBits(d: *Decompress, comptime U: type) !U {
498 return u;501 return u;
499 }502 }
500 const in = d.input;503 const in = d.input;
501 const next_int = in.takeInt(usize, .little) catch |err| switch (err) {504 const next_int = in.takeInt(Bits, .little) catch |err| switch (err) {
502 error.ReadFailed => return error.ReadFailed,505 error.ReadFailed => return error.ReadFailed,
503 error.EndOfStream => return takeBitsEnding(d, U),506 error.EndOfStream => return takeBitsEnding(d, U),
504 };507 };
505 const needed_bits = @bitSizeOf(U) - remaining_bits;508 const needed_bits = @bitSizeOf(U) - remaining_bits;
506 const u: U = @intCast(((next_int & ((@as(usize, 1) << needed_bits) - 1)) << remaining_bits) | next_bits);509 const u: U = @intCast(((next_int & ((@as(Bits, 1) << needed_bits) - 1)) << remaining_bits) | next_bits);
507 d.next_bits = next_int >> needed_bits;510 d.next_bits = next_int >> needed_bits;
508 d.remaining_bits = @intCast(@bitSizeOf(usize) - @as(usize, needed_bits));511 d.remaining_bits = @intCast(@bitSizeOf(Bits) - @as(usize, needed_bits));
509 return u;512 return u;
510}513}
511514
...@@ -514,14 +517,14 @@ fn takeBitsEnding(d: *Decompress, comptime U: type) !U {...@@ -514,14 +517,14 @@ fn takeBitsEnding(d: *Decompress, comptime U: type) !U {
514 const next_bits = d.next_bits;517 const next_bits = d.next_bits;
515 const in = d.input;518 const in = d.input;
516 const n = in.bufferedLen();519 const n = in.bufferedLen();
517 assert(n < @sizeOf(usize));520 assert(n < @sizeOf(Bits));
518 const needed_bits = @bitSizeOf(U) - remaining_bits;521 const needed_bits = @bitSizeOf(U) - remaining_bits;
519 if (n * 8 < needed_bits) return error.EndOfStream;522 if (n * 8 < needed_bits) return error.EndOfStream;
520 const next_int = in.takeVarInt(usize, .little, n) catch |err| switch (err) {523 const next_int = in.takeVarInt(Bits, .little, n) catch |err| switch (err) {
521 error.ReadFailed => return error.ReadFailed,524 error.ReadFailed => return error.ReadFailed,
522 error.EndOfStream => unreachable,525 error.EndOfStream => unreachable,
523 };526 };
524 const u: U = @intCast(((next_int & ((@as(usize, 1) << needed_bits) - 1)) << remaining_bits) | next_bits);527 const u: U = @intCast(((next_int & ((@as(Bits, 1) << needed_bits) - 1)) << remaining_bits) | next_bits);
525 d.next_bits = next_int >> needed_bits;528 d.next_bits = next_int >> needed_bits;
526 d.remaining_bits = @intCast(n * 8 - @as(usize, needed_bits));529 d.remaining_bits = @intCast(n * 8 - @as(usize, needed_bits));
527 return u;530 return u;
...@@ -532,37 +535,37 @@ fn peekBits(d: *Decompress, comptime U: type) !U {...@@ -532,37 +535,37 @@ fn peekBits(d: *Decompress, comptime U: type) !U {
532 const next_bits = d.next_bits;535 const next_bits = d.next_bits;
533 if (remaining_bits >= @bitSizeOf(U)) return @truncate(next_bits);536 if (remaining_bits >= @bitSizeOf(U)) return @truncate(next_bits);
534 const in = d.input;537 const in = d.input;
535 const next_int = in.peekInt(usize, .little) catch |err| switch (err) {538 const next_int = in.peekInt(Bits, .little) catch |err| switch (err) {
536 error.ReadFailed => return error.ReadFailed,539 error.ReadFailed => return error.ReadFailed,
537 error.EndOfStream => return peekBitsEnding(d, U),540 error.EndOfStream => return peekBitsEnding(d, U),
538 };541 };
539 const needed_bits = @bitSizeOf(U) - remaining_bits;542 const needed_bits = @bitSizeOf(U) - remaining_bits;
540 return @intCast(((next_int & ((@as(usize, 1) << needed_bits) - 1)) << remaining_bits) | next_bits);543 return @intCast(((next_int & ((@as(Bits, 1) << needed_bits) - 1)) << remaining_bits) | next_bits);
541}544}
542545
543fn peekBitsEnding(d: *Decompress, comptime U: type) !U {546fn peekBitsEnding(d: *Decompress, comptime U: type) !U {
544 const remaining_bits = d.remaining_bits;547 const remaining_bits = d.remaining_bits;
545 const next_bits = d.next_bits;548 const next_bits = d.next_bits;
546 const in = d.input;549 const in = d.input;
547 var u: usize = 0;550 var u: Bits = 0;
548 var remaining_needed_bits = @bitSizeOf(U) - remaining_bits;551 var remaining_needed_bits = @bitSizeOf(U) - remaining_bits;
549 var i: usize = 0;552 var i: usize = 0;
550 while (remaining_needed_bits >= 8) {553 while (remaining_needed_bits >= 8) {
551 const byte = try specialPeek(in, next_bits, i);554 const byte = try specialPeek(in, next_bits, i);
552 u |= @as(usize, byte) << @intCast(i * 8);555 u |= @as(Bits, byte) << @intCast(i * 8);
553 remaining_needed_bits -= 8;556 remaining_needed_bits -= 8;
554 i += 1;557 i += 1;
555 }558 }
556 if (remaining_needed_bits != 0) {559 if (remaining_needed_bits != 0) {
557 const byte = try specialPeek(in, next_bits, i);560 const byte = try specialPeek(in, next_bits, i);
558 u |= @as(usize, byte) << @intCast((i * 8) + remaining_needed_bits);561 u |= @as(Bits, byte) << @intCast((i * 8) + remaining_needed_bits);
559 }562 }
560 return @truncate((u << remaining_bits) | next_bits);563 return @truncate((u << remaining_bits) | next_bits);
561}564}
562565
563/// If there is any unconsumed data, handles EndOfStream by pretending there566/// If there is any unconsumed data, handles EndOfStream by pretending there
564/// are zeroes afterwards.567/// are zeroes afterwards.
565fn specialPeek(in: *Reader, next_bits: usize, i: usize) Reader.Error!u8 {568fn specialPeek(in: *Reader, next_bits: Bits, i: usize) Reader.Error!u8 {
566 const peeked = in.peek(i + 1) catch |err| switch (err) {569 const peeked = in.peek(i + 1) catch |err| switch (err) {
567 error.ReadFailed => return error.ReadFailed,570 error.ReadFailed => return error.ReadFailed,
568 error.EndOfStream => if (next_bits == 0 and i == 0) return error.EndOfStream else return 0,571 error.EndOfStream => if (next_bits == 0 and i == 0) return error.EndOfStream else return 0,
...@@ -578,13 +581,13 @@ fn tossBits(d: *Decompress, n: u4) !void {...@@ -578,13 +581,13 @@ fn tossBits(d: *Decompress, n: u4) !void {
578 d.remaining_bits = remaining_bits - n;581 d.remaining_bits = remaining_bits - n;
579 } else {582 } else {
580 const in = d.input;583 const in = d.input;
581 const next_int = in.takeInt(usize, .little) catch |err| switch (err) {584 const next_int = in.takeInt(Bits, .little) catch |err| switch (err) {
582 error.ReadFailed => return error.ReadFailed,585 error.ReadFailed => return error.ReadFailed,
583 error.EndOfStream => return tossBitsEnding(d, n),586 error.EndOfStream => return tossBitsEnding(d, n),
584 };587 };
585 const needed_bits = n - remaining_bits;588 const needed_bits = n - remaining_bits;
586 d.next_bits = next_int >> needed_bits;589 d.next_bits = next_int >> needed_bits;
587 d.remaining_bits = @intCast(@bitSizeOf(usize) - @as(usize, needed_bits));590 d.remaining_bits = @intCast(@bitSizeOf(Bits) - @as(usize, needed_bits));
588 }591 }
589}592}
590593
...@@ -593,9 +596,9 @@ fn tossBitsEnding(d: *Decompress, n: u4) !void {...@@ -593,9 +596,9 @@ fn tossBitsEnding(d: *Decompress, n: u4) !void {
593 const in = d.input;596 const in = d.input;
594 const buffered_n = in.bufferedLen();597 const buffered_n = in.bufferedLen();
595 if (buffered_n == 0) return error.EndOfStream;598 if (buffered_n == 0) return error.EndOfStream;
596 assert(buffered_n < @sizeOf(usize));599 assert(buffered_n < @sizeOf(Bits));
597 const needed_bits = n - remaining_bits;600 const needed_bits = n - remaining_bits;
598 const next_int = in.takeVarInt(usize, .little, buffered_n) catch |err| switch (err) {601 const next_int = in.takeVarInt(Bits, .little, buffered_n) catch |err| switch (err) {
599 error.ReadFailed => return error.ReadFailed,602 error.ReadFailed => return error.ReadFailed,
600 error.EndOfStream => unreachable,603 error.EndOfStream => unreachable,
601 };604 };
lib/std/net.zig+41
...@@ -42,6 +42,47 @@ pub const Address = extern union {...@@ -42,6 +42,47 @@ pub const Address = extern union {
42 in6: Ip6Address,42 in6: Ip6Address,
43 un: if (has_unix_sockets) posix.sockaddr.un else void,43 un: if (has_unix_sockets) posix.sockaddr.un else void,
4444
45 /// Parse an IP address which may include a port. For IPv4, this is just written `address:port`.
46 /// For IPv6, RFC 3986 defines this as an "IP literal", and the port is differentiated from the
47 /// address by surrounding the address part in brackets '[addr]:port'. Even if the port is not
48 /// given, the brackets are mandatory.
49 pub fn parseIpAndPort(str: []const u8) error{ InvalidAddress, InvalidPort }!Address {
50 if (str.len == 0) return error.InvalidAddress;
51 if (str[0] == '[') {
52 const addr_end = std.mem.indexOfScalar(u8, str, ']') orelse
53 return error.InvalidAddress;
54 const addr_str = str[1..addr_end];
55 const port: u16 = p: {
56 if (addr_end == str.len - 1) break :p 0;
57 if (str[addr_end + 1] != ':') return error.InvalidAddress;
58 break :p parsePort(str[addr_end + 2 ..]) orelse return error.InvalidPort;
59 };
60 return parseIp6(addr_str, port) catch error.InvalidAddress;
61 } else {
62 if (std.mem.indexOfScalar(u8, str, ':')) |idx| {
63 // hold off on `error.InvalidPort` since `error.InvalidAddress` might make more sense
64 const port: ?u16 = parsePort(str[idx + 1 ..]);
65 const addr = parseIp4(str[0..idx], port orelse 0) catch return error.InvalidAddress;
66 if (port == null) return error.InvalidPort;
67 return addr;
68 } else {
69 return parseIp4(str, 0) catch error.InvalidAddress;
70 }
71 }
72 }
73 fn parsePort(str: []const u8) ?u16 {
74 var p: u16 = 0;
75 for (str) |c| switch (c) {
76 '0'...'9' => {
77 const shifted = std.math.mul(u16, p, 10) catch return null;
78 p = std.math.add(u16, shifted, c - '0') catch return null;
79 },
80 else => return null,
81 };
82 if (p == 0) return null;
83 return p;
84 }
85
45 /// Parse the given IP address string into an Address value.86 /// Parse the given IP address string into an Address value.
46 /// It is recommended to use `resolveIp` instead, to handle87 /// It is recommended to use `resolveIp` instead, to handle
47 /// IPv6 link-local unix addresses.88 /// IPv6 link-local unix addresses.
lib/std/zig/Server.zig+15
...@@ -50,6 +50,8 @@ pub const Message = struct {...@@ -50,6 +50,8 @@ pub const Message = struct {
50 /// address of the fuzz unit test. This is used to provide a starting50 /// address of the fuzz unit test. This is used to provide a starting
51 /// point to view coverage.51 /// point to view coverage.
52 fuzz_start_addr,52 fuzz_start_addr,
53 /// Body is a TimeReport.
54 time_report,
5355
54 _,56 _,
55 };57 };
...@@ -95,6 +97,19 @@ pub const Message = struct {...@@ -95,6 +97,19 @@ pub const Message = struct {
95 };97 };
96 };98 };
9799
100 /// Trailing is the same as in `std.Build.abi.time_report.CompileResult`, excluding `step_name`.
101 pub const TimeReport = extern struct {
102 stats: std.Build.abi.time_report.CompileResult.Stats align(4),
103 llvm_pass_timings_len: u32,
104 files_len: u32,
105 decls_len: u32,
106 flags: Flags,
107 pub const Flags = packed struct(u32) {
108 use_llvm: bool,
109 _: u31 = 0,
110 };
111 };
112
98 /// Trailing:113 /// Trailing:
99 /// * the hex digest of the cache directory within the /o/ subdirectory.114 /// * the hex digest of the cache directory within the /o/ subdirectory.
100 pub const EmitDigest = extern struct {115 pub const EmitDigest = extern struct {
src/Compilation.zig+191-5
...@@ -173,7 +173,6 @@ verbose_cimport: bool,...@@ -173,7 +173,6 @@ verbose_cimport: bool,
173verbose_llvm_cpu_features: bool,173verbose_llvm_cpu_features: bool,
174verbose_link: bool,174verbose_link: bool,
175disable_c_depfile: bool,175disable_c_depfile: bool,
176time_report: bool,
177stack_report: bool,176stack_report: bool,
178debug_compiler_runtime_libs: bool,177debug_compiler_runtime_libs: bool,
179debug_compile_errors: bool,178debug_compile_errors: bool,
...@@ -263,6 +262,8 @@ link_prog_node: std.Progress.Node = std.Progress.Node.none,...@@ -263,6 +262,8 @@ link_prog_node: std.Progress.Node = std.Progress.Node.none,
263262
264llvm_opt_bisect_limit: c_int,263llvm_opt_bisect_limit: c_int,
265264
265time_report: ?TimeReport,
266
266file_system_inputs: ?*std.ArrayListUnmanaged(u8),267file_system_inputs: ?*std.ArrayListUnmanaged(u8),
267268
268/// This is the digest of the cache for the current compilation.269/// This is the digest of the cache for the current compilation.
...@@ -322,6 +323,72 @@ const QueuedJobs = struct {...@@ -322,6 +323,72 @@ const QueuedJobs = struct {
322 zigc_lib: bool = false,323 zigc_lib: bool = false,
323};324};
324325
326pub const Timer = union(enum) {
327 unused,
328 active: struct {
329 start: std.time.Instant,
330 saved_ns: u64,
331 },
332 paused: u64,
333 stopped,
334
335 pub fn pause(t: *Timer) void {
336 switch (t.*) {
337 .unused => return,
338 .active => |a| {
339 const current = std.time.Instant.now() catch unreachable;
340 const new_ns = switch (current.order(a.start)) {
341 .lt, .eq => 0,
342 .gt => current.since(a.start),
343 };
344 t.* = .{ .paused = a.saved_ns + new_ns };
345 },
346 .paused => unreachable,
347 .stopped => unreachable,
348 }
349 }
350 pub fn @"resume"(t: *Timer) void {
351 switch (t.*) {
352 .unused => return,
353 .active => unreachable,
354 .paused => |saved_ns| t.* = .{ .active = .{
355 .start = std.time.Instant.now() catch unreachable,
356 .saved_ns = saved_ns,
357 } },
358 .stopped => unreachable,
359 }
360 }
361 pub fn finish(t: *Timer) ?u64 {
362 defer t.* = .stopped;
363 switch (t.*) {
364 .unused => return null,
365 .active => |a| {
366 const current = std.time.Instant.now() catch unreachable;
367 const new_ns = switch (current.order(a.start)) {
368 .lt, .eq => 0,
369 .gt => current.since(a.start),
370 };
371 return a.saved_ns + new_ns;
372 },
373 .paused => |ns| return ns,
374 .stopped => unreachable,
375 }
376 }
377};
378
379/// Starts a timer for measuring a `--time-report` value. If `comp.time_report` is `null`, the
380/// returned timer does nothing. When the thing being timed is done, call `Timer.finish`. If that
381/// function returns non-`null`, then the value is a number of nanoseconds, and `comp.time_report`
382/// is set.
383pub fn startTimer(comp: *Compilation) Timer {
384 if (comp.time_report == null) return .unused;
385 const now = std.time.Instant.now() catch @panic("std.time.Timer unsupported; cannot emit time report");
386 return .{ .active = .{
387 .start = now,
388 .saved_ns = 0,
389 } };
390}
391
325/// A filesystem path, represented relative to one of a few specific directories where possible.392/// A filesystem path, represented relative to one of a few specific directories where possible.
326/// Every path (considering symlinks as distinct paths) has a canonical representation in this form.393/// Every path (considering symlinks as distinct paths) has a canonical representation in this form.
327/// This abstraction allows us to:394/// This abstraction allows us to:
...@@ -787,6 +854,58 @@ pub inline fn debugIncremental(comp: *const Compilation) bool {...@@ -787,6 +854,58 @@ pub inline fn debugIncremental(comp: *const Compilation) bool {
787 return comp.debug_incremental;854 return comp.debug_incremental;
788}855}
789856
857pub const TimeReport = struct {
858 stats: std.Build.abi.time_report.CompileResult.Stats,
859
860 /// Allocated into `gpa`. The pass time statistics emitted by LLVM's "time-passes" option.
861 /// LLVM provides this data in ASCII form as a table, which can be directly shown to users.
862 ///
863 /// Ideally, we would be able to use `printAllJSONValues` to get *structured* data which we can
864 /// then display more nicely. Unfortunately, that function seems to trip an assertion on one of
865 /// the pass timer names at the time of writing.
866 llvm_pass_timings: []u8,
867
868 /// Key is a ZIR `declaration` instruction; value is the number of nanoseconds spent analyzing
869 /// it. This is the total across all instances of the generic parent namespace, and (if this is
870 /// a function) all generic instances of this function. It also includes time spent analyzing
871 /// function bodies if this is a function (generic or otherwise).
872 /// An entry not existing means the declaration has not been analyzed (so far).
873 decl_sema_info: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, struct {
874 ns: u64,
875 count: u32,
876 }),
877
878 /// Key is a ZIR `declaration` instruction which is a function or test; value is the number of
879 /// nanoseconds spent running codegen on it. As above, this is the total across all generic
880 /// instances, both of this function itself and of its parent namespace.
881 /// An entry not existing means the declaration has not been codegenned (so far).
882 /// Every key in `decl_codegen_ns` is also in `decl_sema_ns`.
883 decl_codegen_ns: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, u64),
884
885 /// Key is a ZIR `declaration` instruction which is anything other than a `comptime` decl; value
886 /// is the number of nanoseconds spent linking it into the binary. As above, this is the total
887 /// across all generic instances.
888 /// An entry not existing means the declaration has not been linked (so far).
889 /// Every key in `decl_link_ns` is also in `decl_sema_ns`.
890 decl_link_ns: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, u64),
891
892 pub fn deinit(tr: *TimeReport, gpa: Allocator) void {
893 tr.stats = undefined;
894 gpa.free(tr.llvm_pass_timings);
895 tr.decl_sema_info.deinit(gpa);
896 tr.decl_codegen_ns.deinit(gpa);
897 tr.decl_link_ns.deinit(gpa);
898 }
899
900 pub const init: TimeReport = .{
901 .stats = .init,
902 .llvm_pass_timings = &.{},
903 .decl_sema_info = .empty,
904 .decl_codegen_ns = .empty,
905 .decl_link_ns = .empty,
906 };
907};
908
790pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;909pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
791pub const SemaError = Zcu.SemaError;910pub const SemaError = Zcu.SemaError;
792911
...@@ -2027,7 +2146,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2027,7 +2146,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2027 .verbose_link = options.verbose_link,2146 .verbose_link = options.verbose_link,
2028 .disable_c_depfile = options.disable_c_depfile,2147 .disable_c_depfile = options.disable_c_depfile,
2029 .reference_trace = options.reference_trace,2148 .reference_trace = options.reference_trace,
2030 .time_report = options.time_report,2149 .time_report = if (options.time_report) .init else null,
2031 .stack_report = options.stack_report,2150 .stack_report = options.stack_report,
2032 .test_filters = options.test_filters,2151 .test_filters = options.test_filters,
2033 .test_name_prefix = options.test_name_prefix,2152 .test_name_prefix = options.test_name_prefix,
...@@ -2561,6 +2680,8 @@ pub fn destroy(comp: *Compilation) void {...@@ -2561,6 +2680,8 @@ pub fn destroy(comp: *Compilation) void {
2561 }2680 }
2562 comp.failed_win32_resources.deinit(gpa);2681 comp.failed_win32_resources.deinit(gpa);
25632682
2683 if (comp.time_report) |*tr| tr.deinit(gpa);
2684
2564 comp.link_diags.deinit();2685 comp.link_diags.deinit();
25652686
2566 comp.clearMiscFailures();2687 comp.clearMiscFailures();
...@@ -2657,6 +2778,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2657,6 +2778,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
26572778
2658 comp.clearMiscFailures();2779 comp.clearMiscFailures();
2659 comp.last_update_was_cache_hit = false;2780 comp.last_update_was_cache_hit = false;
2781 if (comp.time_report) |*tr| {
2782 tr.deinit(gpa); // this is information about an old update
2783 tr.* = .init;
2784 }
26602785
2661 var tmp_dir_rand_int: u64 = undefined;2786 var tmp_dir_rand_int: u64 = undefined;
2662 var man: Cache.Manifest = undefined;2787 var man: Cache.Manifest = undefined;
...@@ -2688,6 +2813,14 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2688,6 +2813,14 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2688 whole.cache_manifest = &man;2813 whole.cache_manifest = &man;
2689 try addNonIncrementalStuffToCacheManifest(comp, arena, &man);2814 try addNonIncrementalStuffToCacheManifest(comp, arena, &man);
26902815
2816 // Under `--time-report`, ignore cache hits; do the work anyway for those juicy numbers.
2817 const ignore_hit = comp.time_report != null;
2818
2819 if (ignore_hit) {
2820 // We're going to do the work regardless of whether this is a hit or a miss.
2821 man.want_shared_lock = false;
2822 }
2823
2691 const is_hit = man.hit() catch |err| switch (err) {2824 const is_hit = man.hit() catch |err| switch (err) {
2692 error.CacheCheckFailed => switch (man.diagnostic) {2825 error.CacheCheckFailed => switch (man.diagnostic) {
2693 .none => unreachable,2826 .none => unreachable,
...@@ -2713,7 +2846,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2713,7 +2846,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2713 .{},2846 .{},
2714 ),2847 ),
2715 };2848 };
2716 if (is_hit) {2849 if (is_hit and !ignore_hit) {
2717 // In this case the cache hit contains the full set of file system inputs. Nice!2850 // In this case the cache hit contains the full set of file system inputs. Nice!
2718 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);2851 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
2719 if (comp.parent_whole_cache) |pwc| {2852 if (comp.parent_whole_cache) |pwc| {
...@@ -2734,6 +2867,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2734,6 +2867,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2734 }2867 }
2735 log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});2868 log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});
27362869
2870 if (ignore_hit) {
2871 // Okay, now set this back so that `writeManifest` will downgrade our lock later.
2872 man.want_shared_lock = true;
2873 }
2874
2737 // Compile the artifacts to a temporary directory.2875 // Compile the artifacts to a temporary directory.
2738 whole.tmp_artifact_directory = d: {2876 whole.tmp_artifact_directory = d: {
2739 tmp_dir_rand_int = std.crypto.random.int(u64);2877 tmp_dir_rand_int = std.crypto.random.int(u64);
...@@ -2786,6 +2924,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2786,6 +2924,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2786 const pt: Zcu.PerThread = .activate(zcu, .main);2924 const pt: Zcu.PerThread = .activate(zcu, .main);
2787 defer pt.deactivate();2925 defer pt.deactivate();
27882926
2927 assert(zcu.cur_analysis_timer == null);
2928
2789 zcu.skip_analysis_this_update = false;2929 zcu.skip_analysis_this_update = false;
27902930
2791 // TODO: doing this in `resolveReferences` later could avoid adding inputs for dead embedfiles. Investigate!2931 // TODO: doing this in `resolveReferences` later could avoid adding inputs for dead embedfiles. Investigate!
...@@ -2829,6 +2969,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2829,6 +2969,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2829 const pt: Zcu.PerThread = .activate(zcu, .main);2969 const pt: Zcu.PerThread = .activate(zcu, .main);
2830 defer pt.deactivate();2970 defer pt.deactivate();
28312971
2972 assert(zcu.cur_analysis_timer == null);
2973
2832 if (!zcu.skip_analysis_this_update) {2974 if (!zcu.skip_analysis_this_update) {
2833 if (comp.config.is_test) {2975 if (comp.config.is_test) {
2834 // The `test_functions` decl has been intentionally postponed until now,2976 // The `test_functions` decl has been intentionally postponed until now,
...@@ -3040,11 +3182,22 @@ fn flush(...@@ -3040,11 +3182,22 @@ fn flush(
3040) !void {3182) !void {
3041 if (comp.zcu) |zcu| {3183 if (comp.zcu) |zcu| {
3042 if (zcu.llvm_object) |llvm_object| {3184 if (zcu.llvm_object) |llvm_object| {
3185 const pt: Zcu.PerThread = .activate(zcu, tid);
3186 defer pt.deactivate();
3187
3043 // Emit the ZCU object from LLVM now; it's required to flush the output file.3188 // Emit the ZCU object from LLVM now; it's required to flush the output file.
3044 // If there's an output file, it wants to decide where the LLVM object goes!3189 // If there's an output file, it wants to decide where the LLVM object goes!
3045 const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0);3190 const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0);
3046 defer sub_prog_node.end();3191 defer sub_prog_node.end();
3047 try llvm_object.emit(.{ .zcu = zcu, .tid = tid }, .{3192
3193 var timer = comp.startTimer();
3194 defer if (timer.finish()) |ns| {
3195 comp.mutex.lock();
3196 defer comp.mutex.unlock();
3197 comp.time_report.?.stats.real_ns_llvm_emit = ns;
3198 };
3199
3200 try llvm_object.emit(pt, .{
3048 .pre_ir_path = comp.verbose_llvm_ir,3201 .pre_ir_path = comp.verbose_llvm_ir,
3049 .pre_bc_path = comp.verbose_llvm_bc,3202 .pre_bc_path = comp.verbose_llvm_bc,
30503203
...@@ -3071,7 +3224,7 @@ fn flush(...@@ -3071,7 +3224,7 @@ fn flush(
30713224
3072 .is_debug = comp.root_mod.optimize_mode == .Debug,3225 .is_debug = comp.root_mod.optimize_mode == .Debug,
3073 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,3226 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
3074 .time_report = comp.time_report,3227 .time_report = if (comp.time_report) |*p| p else null,
3075 .sanitize_thread = comp.config.any_sanitize_thread,3228 .sanitize_thread = comp.config.any_sanitize_thread,
3076 .fuzz = comp.config.any_fuzz,3229 .fuzz = comp.config.any_fuzz,
3077 .lto = comp.config.lto,3230 .lto = comp.config.lto,
...@@ -3079,6 +3232,12 @@ fn flush(...@@ -3079,6 +3232,12 @@ fn flush(
3079 }3232 }
3080 }3233 }
3081 if (comp.bin_file) |lf| {3234 if (comp.bin_file) |lf| {
3235 var timer = comp.startTimer();
3236 defer if (timer.finish()) |ns| {
3237 comp.mutex.lock();
3238 defer comp.mutex.unlock();
3239 comp.time_report.?.stats.real_ns_link_flush = ns;
3240 };
3082 // This is needed before reading the error flags.3241 // This is needed before reading the error flags.
3083 lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) {3242 lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) {
3084 error.LinkFailure => {}, // Already reported.3243 error.LinkFailure => {}, // Already reported.
...@@ -4223,6 +4382,17 @@ fn performAllTheWork(...@@ -4223,6 +4382,17 @@ fn performAllTheWork(
4223 zcu.generation += 1;4382 zcu.generation += 1;
4224 };4383 };
42254384
4385 // This is awkward: we don't want to start the timer until later, but we won't want to stop it
4386 // until the wait groups finish. That means we need do do this.
4387 var decl_work_timer: ?Timer = null;
4388 defer commit_timer: {
4389 const t = &(decl_work_timer orelse break :commit_timer);
4390 const ns = t.finish() orelse break :commit_timer;
4391 comp.mutex.lock();
4392 defer comp.mutex.unlock();
4393 comp.time_report.?.stats.real_ns_decls = ns;
4394 }
4395
4226 // Here we queue up all the AstGen tasks first, followed by C object compilation.4396 // Here we queue up all the AstGen tasks first, followed by C object compilation.
4227 // We wait until the AstGen tasks are all completed before proceeding to the4397 // We wait until the AstGen tasks are all completed before proceeding to the
4228 // (at least for now) single-threaded main work queue. However, C object compilation4398 // (at least for now) single-threaded main work queue. However, C object compilation
...@@ -4431,6 +4601,13 @@ fn performAllTheWork(...@@ -4431,6 +4601,13 @@ fn performAllTheWork(
4431 const zir_prog_node = main_progress_node.start("AST Lowering", 0);4601 const zir_prog_node = main_progress_node.start("AST Lowering", 0);
4432 defer zir_prog_node.end();4602 defer zir_prog_node.end();
44334603
4604 var timer = comp.startTimer();
4605 defer if (timer.finish()) |ns| {
4606 comp.mutex.lock();
4607 defer comp.mutex.unlock();
4608 comp.time_report.?.stats.real_ns_files = ns;
4609 };
4610
4434 var astgen_wait_group: WaitGroup = .{};4611 var astgen_wait_group: WaitGroup = .{};
4435 defer astgen_wait_group.wait();4612 defer astgen_wait_group.wait();
44364613
...@@ -4556,6 +4733,10 @@ fn performAllTheWork(...@@ -4556,6 +4733,10 @@ fn performAllTheWork(
4556 return;4733 return;
4557 }4734 }
45584735
4736 if (comp.time_report) |*tr| {
4737 tr.stats.n_reachable_files = @intCast(zcu.alive_files.count());
4738 }
4739
4559 if (comp.incremental) {4740 if (comp.incremental) {
4560 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);4741 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
4561 defer update_zir_refs_node.end();4742 defer update_zir_refs_node.end();
...@@ -4599,6 +4780,11 @@ fn performAllTheWork(...@@ -4599,6 +4780,11 @@ fn performAllTheWork(
4599 }4780 }
4600 }4781 }
46014782
4783 if (comp.zcu != null) {
4784 // Start the timer for the "decls" part of the pipeline (Sema, CodeGen, link).
4785 decl_work_timer = comp.startTimer();
4786 }
4787
4602 work: while (true) {4788 work: while (true) {
4603 for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {4789 for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {
4604 try processOneJob(@intFromEnum(Zcu.PerThread.Id.main), comp, job);4790 try processOneJob(@intFromEnum(Zcu.PerThread.Id.main), comp, job);
src/Sema.zig+25-15
...@@ -3246,21 +3246,25 @@ fn zirEnumDecl(...@@ -3246,21 +3246,25 @@ fn zirEnumDecl(
3246 wip_ty.prepare(ip, new_namespace_index);3246 wip_ty.prepare(ip, new_namespace_index);
3247 done = true;3247 done = true;
32483248
3249 try Sema.resolveDeclaredEnum(3249 {
3250 pt,3250 const tracked_unit = zcu.trackUnitSema(type_name.name.toSlice(ip), null);
3251 wip_ty,3251 defer tracked_unit.end(zcu);
3252 inst,3252 try Sema.resolveDeclaredEnum(
3253 tracked_inst,3253 pt,
3254 new_namespace_index,3254 wip_ty,
3255 type_name.name,3255 inst,
3256 small,3256 tracked_inst,
3257 body,3257 new_namespace_index,
3258 tag_type_ref,3258 type_name.name,
3259 any_values,3259 small,
3260 fields_len,3260 body,
3261 sema.code,3261 tag_type_ref,
3262 body_end,3262 any_values,
3263 );3263 fields_len,
3264 sema.code,
3265 body_end,
3266 );
3267 }
32643268
3265 codegen_type: {3269 codegen_type: {
3266 if (zcu.comp.config.use_llvm) break :codegen_type;3270 if (zcu.comp.config.use_llvm) break :codegen_type;
...@@ -7577,6 +7581,12 @@ fn analyzeCall(...@@ -7577,6 +7581,12 @@ fn analyzeCall(
75777581
7578 // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.7582 // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.
75797583
7584 if (zcu.comp.time_report) |*tr| {
7585 if (!block.isComptime()) {
7586 tr.stats.n_inline_calls += 1;
7587 }
7588 }
7589
7580 if (func_ty_info.is_noinline and !block.isComptime()) {7590 if (func_ty_info.is_noinline and !block.isComptime()) {
7581 return sema.fail(block, call_src, "inline call of noinline function", .{});7591 return sema.fail(block, call_src, "inline call of noinline function", .{});
7582 }7592 }
src/Type.zig+6
...@@ -3794,6 +3794,9 @@ fn resolveStructInner(...@@ -3794,6 +3794,9 @@ fn resolveStructInner(
3794 return error.AnalysisFail;3794 return error.AnalysisFail;
3795 }3795 }
37963796
3797 const tracked_unit = zcu.trackUnitSema(struct_obj.name.toSlice(&zcu.intern_pool), null);
3798 defer tracked_unit.end(zcu);
3799
3797 if (zcu.comp.debugIncremental()) {3800 if (zcu.comp.debugIncremental()) {
3798 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner);3801 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner);
3799 info.last_update_gen = zcu.generation;3802 info.last_update_gen = zcu.generation;
...@@ -3853,6 +3856,9 @@ fn resolveUnionInner(...@@ -3853,6 +3856,9 @@ fn resolveUnionInner(
3853 return error.AnalysisFail;3856 return error.AnalysisFail;
3854 }3857 }
38553858
3859 const tracked_unit = zcu.trackUnitSema(union_obj.name.toSlice(&zcu.intern_pool), null);
3860 defer tracked_unit.end(zcu);
3861
3856 if (zcu.comp.debugIncremental()) {3862 if (zcu.comp.debugIncremental()) {
3857 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner);3863 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner);
3858 info.last_update_gen = zcu.generation;3864 info.last_update_gen = zcu.generation;
src/Zcu.zig+44-10
...@@ -312,6 +312,10 @@ builtin_decl_values: BuiltinDecl.Memoized = .initFill(.none),...@@ -312,6 +312,10 @@ builtin_decl_values: BuiltinDecl.Memoized = .initFill(.none),
312incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void =312incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void =
313 if (build_options.enable_debug_extensions) .init else {},313 if (build_options.enable_debug_extensions) .init else {},
314314
315/// Times semantic analysis of the current `AnalUnit`. When we pause to analyze a different unit,
316/// this timer must be temporarily paused and resumed later.
317cur_analysis_timer: ?Compilation.Timer = null,
318
315generation: u32 = 0,319generation: u32 = 0,
316320
317pub const IncrementalDebugState = struct {321pub const IncrementalDebugState = struct {
...@@ -4683,26 +4687,56 @@ fn explainWhyFileIsInModule(...@@ -4683,26 +4687,56 @@ fn explainWhyFileIsInModule(
4683 }4687 }
4684}4688}
46854689
4686const SemaProgNode = struct {4690const TrackedUnitSema = struct {
4687 /// `null` means we created the node, so should end it.4691 /// `null` means we created the node, so should end it.
4688 old_name: ?[std.Progress.Node.max_name_len]u8,4692 old_name: ?[std.Progress.Node.max_name_len]u8,
4689 pub fn end(spn: SemaProgNode, zcu: *Zcu) void {4693 old_analysis_timer: ?Compilation.Timer,
4690 if (spn.old_name) |old_name| {4694 analysis_timer_decl: ?InternPool.TrackedInst.Index,
4695 pub fn end(tus: TrackedUnitSema, zcu: *Zcu) void {
4696 const comp = zcu.comp;
4697 if (tus.old_name) |old_name| {
4691 zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion4698 zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion
4692 zcu.cur_sema_prog_node.setName(&old_name);4699 zcu.cur_sema_prog_node.setName(&old_name);
4693 } else {4700 } else {
4694 zcu.cur_sema_prog_node.end();4701 zcu.cur_sema_prog_node.end();
4695 zcu.cur_sema_prog_node = .none;4702 zcu.cur_sema_prog_node = .none;
4696 }4703 }
4704 report_time: {
4705 const sema_ns = zcu.cur_analysis_timer.?.finish() orelse break :report_time;
4706 const zir_decl = tus.analysis_timer_decl orelse break :report_time;
4707 comp.mutex.lock();
4708 defer comp.mutex.unlock();
4709 comp.time_report.?.stats.cpu_ns_sema += sema_ns;
4710 const gop = comp.time_report.?.decl_sema_info.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) {
4711 error.OutOfMemory => {
4712 comp.setAllocFailure();
4713 break :report_time;
4714 },
4715 };
4716 if (!gop.found_existing) gop.value_ptr.* = .{ .ns = 0, .count = 0 };
4717 gop.value_ptr.ns += sema_ns;
4718 gop.value_ptr.count += 1;
4719 }
4720 zcu.cur_analysis_timer = tus.old_analysis_timer;
4721 if (zcu.cur_analysis_timer) |*t| t.@"resume"();
4697 }4722 }
4698};4723};
4699pub fn startSemaProgNode(zcu: *Zcu, name: []const u8) SemaProgNode {4724pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedInst.Index) TrackedUnitSema {
4700 if (zcu.cur_sema_prog_node.index != .none) {4725 if (zcu.cur_analysis_timer) |*t| t.pause();
4726 const old_analysis_timer = zcu.cur_analysis_timer;
4727 zcu.cur_analysis_timer = zcu.comp.startTimer();
4728 const old_name: ?[std.Progress.Node.max_name_len]u8 = old_name: {
4729 if (zcu.cur_sema_prog_node.index == .none) {
4730 zcu.cur_sema_prog_node = zcu.sema_prog_node.start(name, 0);
4731 break :old_name null;
4732 }
4701 const old_name = zcu.cur_sema_prog_node.getName();4733 const old_name = zcu.cur_sema_prog_node.getName();
4702 zcu.cur_sema_prog_node.setName(name);4734 zcu.cur_sema_prog_node.setName(name);
4703 return .{ .old_name = old_name };4735 break :old_name old_name;
4704 } else {4736 };
4705 zcu.cur_sema_prog_node = zcu.sema_prog_node.start(name, 0);4737 return .{
4706 return .{ .old_name = null };4738 .old_name = old_name,
4707 }4739 .old_analysis_timer = old_analysis_timer,
4740 .analysis_timer_decl = zir_inst,
4741 };
4708}4742}
src/Zcu/PerThread.zig+69-9
...@@ -215,12 +215,15 @@ pub fn updateFile(...@@ -215,12 +215,15 @@ pub fn updateFile(
215 };215 };
216 defer cache_file.close();216 defer cache_file.close();
217217
218 // Under `--time-report`, ignore cache hits; do the work anyway for those juicy numbers.
219 const ignore_hit = comp.time_report != null;
220
218 const need_update = while (true) {221 const need_update = while (true) {
219 const result = switch (file.getMode()) {222 const result = switch (file.getMode()) {
220 inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode),223 inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode),
221 };224 };
222 switch (result) {225 switch (result) {
223 .success => {226 .success => if (!ignore_hit) {
224 log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)});227 log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)});
225 break false;228 break false;
226 },229 },
...@@ -260,9 +263,16 @@ pub fn updateFile(...@@ -260,9 +263,16 @@ pub fn updateFile(
260263
261 file.source = source;264 file.source = source;
262265
266 var timer = comp.startTimer();
263 // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.267 // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.
264 file.tree = try Ast.parse(gpa, source, file.getMode());268 file.tree = try Ast.parse(gpa, source, file.getMode());
269 if (timer.finish()) |ns_parse| {
270 comp.mutex.lock();
271 defer comp.mutex.unlock();
272 comp.time_report.?.stats.cpu_ns_parse += ns_parse;
273 }
265274
275 timer = comp.startTimer();
266 switch (file.getMode()) {276 switch (file.getMode()) {
267 .zig => {277 .zig => {
268 file.zir = try AstGen.generate(gpa, file.tree.?);278 file.zir = try AstGen.generate(gpa, file.tree.?);
...@@ -282,6 +292,11 @@ pub fn updateFile(...@@ -282,6 +292,11 @@ pub fn updateFile(
282 };292 };
283 },293 },
284 }294 }
295 if (timer.finish()) |ns_astgen| {
296 comp.mutex.lock();
297 defer comp.mutex.unlock();
298 comp.time_report.?.stats.cpu_ns_astgen += ns_astgen;
299 }
285300
286 log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)});301 log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)});
287 }302 }
...@@ -801,8 +816,11 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU...@@ -801,8 +816,11 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
801 info.deps.clearRetainingCapacity();816 info.deps.clearRetainingCapacity();
802 }817 }
803818
804 const unit_prog_node = zcu.startSemaProgNode("comptime");819 const unit_tracking = zcu.trackUnitSema(
805 defer unit_prog_node.end(zcu);820 "comptime",
821 zcu.intern_pool.getComptimeUnit(cu_id).zir_index,
822 );
823 defer unit_tracking.end(zcu);
806824
807 return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) {825 return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) {
808 error.AnalysisFail => {826 error.AnalysisFail => {
...@@ -981,8 +999,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu...@@ -981,8 +999,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
981 info.deps.clearRetainingCapacity();999 info.deps.clearRetainingCapacity();
982 }1000 }
9831001
984 const unit_prog_node = zcu.startSemaProgNode(nav.fqn.toSlice(ip));1002 const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip));
985 defer unit_prog_node.end(zcu);1003 defer unit_tracking.end(zcu);
9861004
987 const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {1005 const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
988 break :res .{1006 break :res .{
...@@ -1381,8 +1399,8 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc...@@ -1381,8 +1399,8 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
1381 info.deps.clearRetainingCapacity();1399 info.deps.clearRetainingCapacity();
1382 }1400 }
13831401
1384 const unit_prog_node = zcu.startSemaProgNode(nav.fqn.toSlice(ip));1402 const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip));
1385 defer unit_prog_node.end(zcu);1403 defer unit_tracking.end(zcu);
13861404
1387 const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: {1405 const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: {
1388 break :res .{1406 break :res .{
...@@ -1601,8 +1619,12 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter...@@ -1601,8 +1619,12 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
1601 info.deps.clearRetainingCapacity();1619 info.deps.clearRetainingCapacity();
1602 }1620 }
16031621
1604 const func_prog_node = zcu.startSemaProgNode(ip.getNav(func.owner_nav).fqn.toSlice(ip));1622 const owner_nav = ip.getNav(func.owner_nav);
1605 defer func_prog_node.end(zcu);1623 const unit_tracking = zcu.trackUnitSema(
1624 owner_nav.fqn.toSlice(ip),
1625 owner_nav.srcInst(ip),
1626 );
1627 defer unit_tracking.end(zcu);
16061628
1607 const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result|1629 const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result|
1608 .{ prev_failed or result.ies_outdated, false }1630 .{ prev_failed or result.ies_outdated, false }
...@@ -1847,6 +1869,10 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {...@@ -1847,6 +1869,10 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
1847 });1869 });
1848 const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);1870 const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
1849 errdefer zcu.intern_pool.remove(pt.tid, struct_ty);1871 errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
1872
1873 if (zcu.comp.time_report) |*tr| {
1874 tr.stats.n_imported_files += 1;
1875 }
1850}1876}
18511877
1852/// Called by AstGen worker threads when an import is seen. If `new_file` is returned, the caller is1878/// Called by AstGen worker threads when an import is seen. If `new_file` is returned, the caller is
...@@ -2520,6 +2546,12 @@ pub fn scanNamespace(...@@ -2520,6 +2546,12 @@ pub fn scanNamespace(
2520 const gpa = zcu.gpa;2546 const gpa = zcu.gpa;
2521 const namespace = zcu.namespacePtr(namespace_index);2547 const namespace = zcu.namespacePtr(namespace_index);
25222548
2549 const tracked_unit = zcu.trackUnitSema(
2550 Type.fromInterned(namespace.owner_type).containerTypeName(ip).toSlice(ip),
2551 null,
2552 );
2553 defer tracked_unit.end(zcu);
2554
2523 // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather2555 // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather
2524 // than their name. We'll build an efficient mapping now, then discard the current `decls`.2556 // than their name. We'll build an efficient mapping now, then discard the current `decls`.
2525 // We map to the `AnalUnit`, since not every declaration has a `Nav`.2557 // We map to the `AnalUnit`, since not every declaration has a `Nav`.
...@@ -2755,6 +2787,12 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE...@@ -2755,6 +2787,12 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
2755 func.setResolvedErrorSet(ip, .none);2787 func.setResolvedErrorSet(ip, .none);
2756 }2788 }
27572789
2790 if (zcu.comp.time_report) |*tr| {
2791 if (func.generic_owner != .none) {
2792 tr.stats.n_generic_instances += 1;
2793 }
2794 }
2795
2758 // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from.2796 // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from.
2759 const decl_nav = ip.getNav(if (func.generic_owner == .none)2797 const decl_nav = ip.getNav(if (func.generic_owner == .none)
2760 func.owner_nav2798 func.owner_nav
...@@ -4307,6 +4345,9 @@ pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dep...@@ -4307,6 +4345,9 @@ pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dep
4307/// codegen thread, depending on whether the backend supports `Zcu.Feature.separate_thread`.4345/// codegen thread, depending on whether the backend supports `Zcu.Feature.separate_thread`.
4308pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, out: *@import("../link.zig").ZcuTask.LinkFunc.SharedMir) void {4346pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, out: *@import("../link.zig").ZcuTask.LinkFunc.SharedMir) void {
4309 const zcu = pt.zcu;4347 const zcu = pt.zcu;
4348
4349 var timer = zcu.comp.startTimer();
4350
4310 const success: bool = if (runCodegenInner(pt, func_index, air)) |mir| success: {4351 const success: bool = if (runCodegenInner(pt, func_index, air)) |mir| success: {
4311 out.value = mir;4352 out.value = mir;
4312 break :success true;4353 break :success true;
...@@ -4327,6 +4368,25 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou...@@ -4327,6 +4368,25 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou
4327 }4368 }
4328 break :success false;4369 break :success false;
4329 };4370 };
4371
4372 if (timer.finish()) |ns_codegen| report_time: {
4373 const ip = &zcu.intern_pool;
4374 const nav = ip.indexToKey(func_index).func.owner_nav;
4375 const zir_decl = ip.getNav(nav).srcInst(ip);
4376 zcu.comp.mutex.lock();
4377 defer zcu.comp.mutex.unlock();
4378 const tr = &zcu.comp.time_report.?;
4379 tr.stats.cpu_ns_codegen += ns_codegen;
4380 const gop = tr.decl_codegen_ns.getOrPut(zcu.gpa, zir_decl) catch |err| switch (err) {
4381 error.OutOfMemory => {
4382 zcu.comp.setAllocFailure();
4383 break :report_time;
4384 },
4385 };
4386 if (!gop.found_existing) gop.value_ptr.* = 0;
4387 gop.value_ptr.* += ns_codegen;
4388 }
4389
4330 // release `out.value` with this store; synchronizes with acquire loads in `link`4390 // release `out.value` with this store; synchronizes with acquire loads in `link`
4331 out.status.store(if (success) .ready else .failed, .release);4391 out.status.store(if (success) .ready else .failed, .release);
4332 zcu.comp.link_task_queue.mirReady(zcu.comp, func_index, out);4392 zcu.comp.link_task_queue.mirReady(zcu.comp, func_index, out);
src/codegen/llvm.zig+13-2
...@@ -764,7 +764,7 @@ pub const Object = struct {...@@ -764,7 +764,7 @@ pub const Object = struct {
764764
765 is_debug: bool,765 is_debug: bool,
766 is_small: bool,766 is_small: bool,
767 time_report: bool,767 time_report: ?*Compilation.TimeReport,
768 sanitize_thread: bool,768 sanitize_thread: bool,
769 fuzz: bool,769 fuzz: bool,
770 lto: std.zig.LtoMode,770 lto: std.zig.LtoMode,
...@@ -1063,7 +1063,7 @@ pub const Object = struct {...@@ -1063,7 +1063,7 @@ pub const Object = struct {
1063 var lowered_options: llvm.TargetMachine.EmitOptions = .{1063 var lowered_options: llvm.TargetMachine.EmitOptions = .{
1064 .is_debug = options.is_debug,1064 .is_debug = options.is_debug,
1065 .is_small = options.is_small,1065 .is_small = options.is_small,
1066 .time_report = options.time_report,1066 .time_report_out = null, // set below to make sure it's only set for a single `emitToFile`
1067 .tsan = options.sanitize_thread,1067 .tsan = options.sanitize_thread,
1068 .lto = switch (options.lto) {1068 .lto = switch (options.lto) {
1069 .none => .None,1069 .none => .None,
...@@ -1118,6 +1118,11 @@ pub const Object = struct {...@@ -1118,6 +1118,11 @@ pub const Object = struct {
1118 lowered_options.llvm_ir_filename = null;1118 lowered_options.llvm_ir_filename = null;
1119 }1119 }
11201120
1121 var time_report_c_str: [*:0]u8 = undefined;
1122 if (options.time_report != null) {
1123 lowered_options.time_report_out = &time_report_c_str;
1124 }
1125
1121 lowered_options.asm_filename = options.asm_path;1126 lowered_options.asm_filename = options.asm_path;
1122 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {1127 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {
1123 defer llvm.disposeMessage(error_message);1128 defer llvm.disposeMessage(error_message);
...@@ -1125,6 +1130,12 @@ pub const Object = struct {...@@ -1125,6 +1130,12 @@ pub const Object = struct {
1125 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message,1130 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message,
1126 });1131 });
1127 }1132 }
1133 if (options.time_report) |tr| {
1134 defer std.c.free(time_report_c_str);
1135 const time_report_data = std.mem.span(time_report_c_str);
1136 assert(tr.llvm_pass_timings.len == 0);
1137 tr.llvm_pass_timings = try comp.gpa.dupe(u8, time_report_data);
1138 }
1128 }1139 }
11291140
1130 pub fn updateFunc(1141 pub fn updateFunc(
src/codegen/llvm/bindings.zig+1-1
...@@ -88,7 +88,7 @@ pub const TargetMachine = opaque {...@@ -88,7 +88,7 @@ pub const TargetMachine = opaque {
88 pub const EmitOptions = extern struct {88 pub const EmitOptions = extern struct {
89 is_debug: bool,89 is_debug: bool,
90 is_small: bool,90 is_small: bool,
91 time_report: bool,91 time_report_out: ?*[*:0]u8,
92 tsan: bool,92 tsan: bool,
93 sancov: bool,93 sancov: bool,
94 lto: LtoPhase,94 lto: LtoPhase,
src/link.zig+33
...@@ -1311,6 +1311,14 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1311,6 +1311,14 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1311 comp.link_prog_node.completeOne();1311 comp.link_prog_node.completeOne();
1312 return;1312 return;
1313 };1313 };
1314
1315 var timer = comp.startTimer();
1316 defer if (timer.finish()) |ns| {
1317 comp.mutex.lock();
1318 defer comp.mutex.unlock();
1319 comp.time_report.?.stats.cpu_ns_link += ns;
1320 };
1321
1314 switch (task) {1322 switch (task) {
1315 .load_explicitly_provided => {1323 .load_explicitly_provided => {
1316 const prog_node = comp.link_prog_node.start("Parse Inputs", comp.link_inputs.len);1324 const prog_node = comp.link_prog_node.start("Parse Inputs", comp.link_inputs.len);
...@@ -1437,6 +1445,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {...@@ -1437,6 +1445,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
1437 const ip = &zcu.intern_pool;1445 const ip = &zcu.intern_pool;
1438 const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));1446 const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
1439 defer pt.deactivate();1447 defer pt.deactivate();
1448
1449 var timer = comp.startTimer();
1450
1440 switch (task) {1451 switch (task) {
1441 .link_nav => |nav_index| {1452 .link_nav => |nav_index| {
1442 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);1453 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
...@@ -1511,6 +1522,28 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {...@@ -1511,6 +1522,28 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
1511 }1522 }
1512 },1523 },
1513 }1524 }
1525
1526 if (timer.finish()) |ns_link| report_time: {
1527 const zir_decl: ?InternPool.TrackedInst.Index = switch (task) {
1528 .link_type, .update_line_number => null,
1529 .link_nav => |nav| ip.getNav(nav).srcInst(ip),
1530 .link_func => |f| ip.getNav(ip.indexToKey(f.func).func.owner_nav).srcInst(ip),
1531 };
1532 comp.mutex.lock();
1533 defer comp.mutex.unlock();
1534 const tr = &zcu.comp.time_report.?;
1535 tr.stats.cpu_ns_link += ns_link;
1536 if (zir_decl) |inst| {
1537 const gop = tr.decl_link_ns.getOrPut(zcu.gpa, inst) catch |err| switch (err) {
1538 error.OutOfMemory => {
1539 zcu.comp.setAllocFailure();
1540 break :report_time;
1541 },
1542 };
1543 if (!gop.found_existing) gop.value_ptr.* = 0;
1544 gop.value_ptr.* += ns_link;
1545 }
1546 }
1514}1547}
1515/// After the main pipeline is done, but before flush, the compilation may need to link one final1548/// After the main pipeline is done, but before flush, the compilation may need to link one final
1516/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running1549/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running
src/main.zig+87-3
...@@ -484,6 +484,7 @@ const usage_build_generic =...@@ -484,6 +484,7 @@ const usage_build_generic =
484 \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow484 \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow
485 \\ -mexec-model=[value] (WASI) Execution model485 \\ -mexec-model=[value] (WASI) Execution model
486 \\ -municode (Windows) Use wmain/wWinMain as entry point486 \\ -municode (Windows) Use wmain/wWinMain as entry point
487 \\ --time-report Send timing diagnostics to '--listen' clients
487 \\488 \\
488 \\Per-Module Compile Options:489 \\Per-Module Compile Options:
489 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command490 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
...@@ -678,7 +679,6 @@ const usage_build_generic =...@@ -678,7 +679,6 @@ const usage_build_generic =
678 \\679 \\
679 \\Debug Options (Zig Compiler Development):680 \\Debug Options (Zig Compiler Development):
680 \\ -fopt-bisect-limit=[limit] Only run [limit] first LLVM optimization passes681 \\ -fopt-bisect-limit=[limit] Only run [limit] first LLVM optimization passes
681 \\ -ftime-report Print timing diagnostics
682 \\ -fstack-report Print stack size diagnostics682 \\ -fstack-report Print stack size diagnostics
683 \\ --verbose-link Display linker invocations683 \\ --verbose-link Display linker invocations
684 \\ --verbose-cc Display C compiler invocations684 \\ --verbose-cc Display C compiler invocations
...@@ -1403,7 +1403,7 @@ fn buildOutputType(...@@ -1403,7 +1403,7 @@ fn buildOutputType(
1403 try test_exec_args.append(arena, null);1403 try test_exec_args.append(arena, null);
1404 } else if (mem.eql(u8, arg, "--test-no-exec")) {1404 } else if (mem.eql(u8, arg, "--test-no-exec")) {
1405 test_no_exec = true;1405 test_no_exec = true;
1406 } else if (mem.eql(u8, arg, "-ftime-report")) {1406 } else if (mem.eql(u8, arg, "--time-report")) {
1407 time_report = true;1407 time_report = true;
1408 } else if (mem.eql(u8, arg, "-fstack-report")) {1408 } else if (mem.eql(u8, arg, "-fstack-report")) {
1409 stack_report = true;1409 stack_report = true;
...@@ -2899,6 +2899,10 @@ fn buildOutputType(...@@ -2899,6 +2899,10 @@ fn buildOutputType(
2899 fatal("test-obj requires --test-no-exec", .{});2899 fatal("test-obj requires --test-no-exec", .{});
2900 }2900 }
29012901
2902 if (time_report and listen == .none) {
2903 fatal("--time-report requires --listen", .{});
2904 }
2905
2902 if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) {2906 if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) {
2903 fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len});2907 fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len});
2904 }2908 }
...@@ -4208,6 +4212,84 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {...@@ -4208,6 +4212,84 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
4208 }4212 }
4209 }4213 }
42104214
4215 if (comp.time_report) |*tr| {
4216 var decls_len: u32 = 0;
4217
4218 var file_name_bytes: std.ArrayListUnmanaged(u8) = .empty;
4219 defer file_name_bytes.deinit(gpa);
4220 var files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, void) = .empty;
4221 defer files.deinit(gpa);
4222 var decl_data: std.ArrayListUnmanaged(u8) = .empty;
4223 defer decl_data.deinit(gpa);
4224
4225 // Each decl needs at least 34 bytes:
4226 // * 2 for 1-byte name plus null terminator
4227 // * 4 for `file`
4228 // * 4 for `sema_count`
4229 // * 8 for `sema_ns`
4230 // * 8 for `codegen_ns`
4231 // * 8 for `link_ns`
4232 // Most, if not all, decls in `tr.decl_sema_ns` are valid, so we have a good size estimate.
4233 try decl_data.ensureUnusedCapacity(gpa, tr.decl_sema_info.count() * 34);
4234
4235 for (tr.decl_sema_info.keys(), tr.decl_sema_info.values()) |tracked_inst, sema_info| {
4236 const resolved = tracked_inst.resolveFull(&comp.zcu.?.intern_pool) orelse continue;
4237 const file = comp.zcu.?.fileByIndex(resolved.file);
4238 const zir = file.zir orelse continue;
4239 const decl_name = zir.nullTerminatedString(zir.getDeclaration(resolved.inst).name);
4240
4241 const gop = try files.getOrPut(gpa, resolved.file);
4242 if (!gop.found_existing) try file_name_bytes.writer(gpa).print("{f}\x00", .{file.path.fmt(comp)});
4243
4244 const codegen_ns = tr.decl_codegen_ns.get(tracked_inst) orelse 0;
4245 const link_ns = tr.decl_link_ns.get(tracked_inst) orelse 0;
4246
4247 decls_len += 1;
4248
4249 try decl_data.ensureUnusedCapacity(gpa, 33 + decl_name.len);
4250 decl_data.appendSliceAssumeCapacity(decl_name);
4251 decl_data.appendAssumeCapacity(0);
4252
4253 const out_file = decl_data.addManyAsArrayAssumeCapacity(4);
4254 const out_sema_count = decl_data.addManyAsArrayAssumeCapacity(4);
4255 const out_sema_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4256 const out_codegen_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4257 const out_link_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4258 std.mem.writeInt(u32, out_file, @intCast(gop.index), .little);
4259 std.mem.writeInt(u32, out_sema_count, sema_info.count, .little);
4260 std.mem.writeInt(u64, out_sema_ns, sema_info.ns, .little);
4261 std.mem.writeInt(u64, out_codegen_ns, codegen_ns, .little);
4262 std.mem.writeInt(u64, out_link_ns, link_ns, .little);
4263 }
4264
4265 const header: std.zig.Server.Message.TimeReport = .{
4266 .stats = tr.stats,
4267 .llvm_pass_timings_len = @intCast(tr.llvm_pass_timings.len),
4268 .files_len = @intCast(files.count()),
4269 .decls_len = decls_len,
4270 .flags = .{
4271 .use_llvm = comp.zcu != null and comp.zcu.?.llvm_object != null,
4272 },
4273 };
4274
4275 var slices: [4][]const u8 = .{
4276 @ptrCast(&header),
4277 tr.llvm_pass_timings,
4278 file_name_bytes.items,
4279 decl_data.items,
4280 };
4281 try s.serveMessageHeader(.{
4282 .tag = .time_report,
4283 .bytes_len = len: {
4284 var len: u32 = 0;
4285 for (slices) |slice| len += @intCast(slice.len);
4286 break :len len;
4287 },
4288 });
4289 try s.out.writeVecAll(&slices);
4290 try s.out.flush();
4291 }
4292
4211 if (error_bundle.errorMessageCount() > 0) {4293 if (error_bundle.errorMessageCount() > 0) {
4212 try s.serveErrorBundle(error_bundle);4294 try s.serveErrorBundle(error_bundle);
4213 return;4295 return;
...@@ -5277,7 +5359,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5277,7 +5359,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5277 var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty;5359 var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty;
52785360
5279 if (resolved_target.result.os.tag == .windows) {5361 if (resolved_target.result.os.tag == .windows) {
5280 try windows_libs.put(arena, "advapi32", {});5362 try windows_libs.ensureUnusedCapacity(arena, 2);
5363 windows_libs.putAssumeCapacity("advapi32", {});
5364 windows_libs.putAssumeCapacity("ws2_32", {}); // for `--listen` (web interface)
5281 }5365 }
52825366
5283 const comp = Compilation.create(gpa, arena, .{5367 const comp = Compilation.create(gpa, arena, .{
src/zig_llvm.cpp+11-4
...@@ -220,7 +220,7 @@ static SanitizerCoverageOptions getSanCovOptions(ZigLLVMCoverageOptions z) {...@@ -220,7 +220,7 @@ static SanitizerCoverageOptions getSanCovOptions(ZigLLVMCoverageOptions z) {
220ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,220ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
221 char **error_message, const ZigLLVMEmitOptions *options)221 char **error_message, const ZigLLVMEmitOptions *options)
222{222{
223 TimePassesIsEnabled = options->time_report;223 TimePassesIsEnabled = options->time_report_out != nullptr;
224224
225 raw_fd_ostream *dest_asm_ptr = nullptr;225 raw_fd_ostream *dest_asm_ptr = nullptr;
226 raw_fd_ostream *dest_bin_ptr = nullptr;226 raw_fd_ostream *dest_bin_ptr = nullptr;
...@@ -418,10 +418,17 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi...@@ -418,10 +418,17 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi
418 WriteBitcodeToFile(llvm_module, *dest_bitcode);418 WriteBitcodeToFile(llvm_module, *dest_bitcode);
419 }419 }
420420
421 if (options->time_report) {421 // This must only happen once we know we've succeeded and will be returning `false`, because
422 TimerGroup::printAll(errs());422 // this code `malloc`s memory which will become owned by the caller (in Zig code).
423 if (options->time_report_out != nullptr) {
424 std::string out_str;
425 auto os = raw_string_ostream(out_str);
426 TimerGroup::printAll(os);
427 TimerGroup::clearAll();
428 auto c_str = (char *)malloc(out_str.length() + 1);
429 strcpy(c_str, out_str.c_str());
430 *options->time_report_out = c_str;
423 }431 }
424
425 return false;432 return false;
426}433}
427434
src/zig_llvm.h+4-1
...@@ -66,7 +66,10 @@ enum ZigLLVMThinOrFullLTOPhase {...@@ -66,7 +66,10 @@ enum ZigLLVMThinOrFullLTOPhase {
66struct ZigLLVMEmitOptions {66struct ZigLLVMEmitOptions {
67 bool is_debug;67 bool is_debug;
68 bool is_small;68 bool is_small;
69 bool time_report;69 // If not null, and `ZigLLVMTargetMachineEmitToFile` returns `false` indicating success, this
70 // `char *` will be populated with a `malloc`-allocated string containing the serialized (as
71 // JSON) time report data. The caller is responsible for freeing that memory.
72 char **time_report_out;
70 bool tsan;73 bool tsan;
71 bool sancov;74 bool sancov;
72 ZigLLVMThinOrFullLTOPhase lto;75 ZigLLVMThinOrFullLTOPhase lto;
tools/dump-cov.zig+1-1
...@@ -5,7 +5,7 @@ const std = @import("std");...@@ -5,7 +5,7 @@ const std = @import("std");
5const fatal = std.process.fatal;5const fatal = std.process.fatal;
6const Path = std.Build.Cache.Path;6const Path = std.Build.Cache.Path;
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const SeenPcsHeader = std.Build.Fuzz.abi.SeenPcsHeader;8const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader;
99
10pub fn main() !void {10pub fn main() !void {
11 var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;11 var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;