| author | |
| committer | |
| log | 5667435bc4eacb36b4c33470784f1662637341b4 |
| tree | 2a135ee5158625e3772a4aae511fdcb82c605ade |
| parent | c5231a8fb4463f7087c6090c562531065a1869f9 |
| parent | 742956865c0c55b6650d7b89d923b63ea11cde4f |
119 files changed, 4794 insertions(+), 2622 deletions(-)
lib/build-web/fuzz.zig created+377| ... | ... | @@ -0,0 +1,377 @@ |
| 1 | // Server timestamp. | |
| 2 | var start_fuzzing_timestamp: i64 = undefined; | |
| 3 | ||
| 4 | const 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 | ||
| 14 | pub 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 | ||
| 43 | var coverage = Coverage.init; | |
| 44 | /// Index of type `SourceLocationIndex`. | |
| 45 | var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .empty; | |
| 46 | /// Contains the most recent coverage update message, unmodified. | |
| 47 | var recent_coverage_update: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty; | |
| 48 | ||
| 49 | fn 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 | ||
| 72 | pub 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 | ||
| 79 | var entry_points: std.ArrayListUnmanaged(SourceLocationIndex) = .empty; | |
| 80 | ||
| 81 | pub 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`. | |
| 91 | const 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 | ||
| 153 | fn 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 | ||
| 207 | export 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 | ||
| 216 | fn 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 | ||
| 254 | fn 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 | ||
| 296 | fn 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 | ||
| 307 | fn 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 | ||
| 339 | fn 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 | ||
| 351 | var 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`. | |
| 355 | export 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 | ||
| 363 | const std = @import("std"); | |
| 364 | const Allocator = std.mem.Allocator; | |
| 365 | const Coverage = std.debug.Coverage; | |
| 366 | const abi = std.Build.abi; | |
| 367 | const assert = std.debug.assert; | |
| 368 | const gpa = std.heap.wasm_allocator; | |
| 369 | ||
| 370 | const Walk = @import("Walk"); | |
| 371 | const html_render = @import("html_render"); | |
| 372 | ||
| 373 | const nsSince = @import("main.zig").nsSince; | |
| 374 | const Slice = @import("main.zig").Slice; | |
| 375 | const fatal = @import("main.zig").fatal; | |
| 376 | const log = std.log; | |
| 377 | const 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"> | |
| 164 | If you are using Firefox and <code>zig build --listen</code> is definitely running, you may be experiencing an unreasonably aggressive exponential | |
| 165 | backoff for WebSocket connection attempts, which is enabled by default and can block connection attempts for up to a minute. To disable this limit, | |
| 166 | open <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 @@ |
| 1 | const domConnectionStatus = document.getElementById("connectionStatus"); | |
| 2 | const domFirefoxWebSocketBullshitExplainer = document.getElementById("firefoxWebSocketBullshitExplainer"); | |
| 3 | ||
| 4 | const domMain = document.getElementsByTagName("main")[0]; | |
| 5 | const domSummary = { | |
| 6 | stepCount: document.getElementById("summaryStepCount"), | |
| 7 | status: document.getElementById("summaryStatus"), | |
| 8 | }; | |
| 9 | const domButtonRebuild = document.getElementById("buttonRebuild"); | |
| 10 | const domStepList = document.getElementById("stepList"); | |
| 11 | let domSteps = []; | |
| 12 | ||
| 13 | let wasm_promise = fetch("main.wasm"); | |
| 14 | let wasm_exports = null; | |
| 15 | ||
| 16 | const text_decoder = new TextDecoder(); | |
| 17 | const text_encoder = new TextEncoder(); | |
| 18 | ||
| 19 | domButtonRebuild.addEventListener("click", () => wasm_exports.rebuild()); | |
| 20 | ||
| 21 | setConnectionStatus("Loading WebAssembly...", false); | |
| 22 | WebAssembly.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 | ||
| 60 | function 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 | } | |
| 77 | function onWebSocketOpen() { | |
| 78 | setConnectionStatus("Waiting for data...", false); | |
| 79 | } | |
| 80 | function 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 | } | |
| 87 | function 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 | ||
| 97 | function 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 | ||
| 113 | function 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 | ||
| 142 | function 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 | } | |
| 179 | function 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 | ||
| 196 | function decodeString(ptr, len) { | |
| 197 | if (len === 0) return ""; | |
| 198 | return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len)); | |
| 199 | } | |
| 200 | function getU32Array(ptr, len) { | |
| 201 | if (len === 0) return new Uint32Array(); | |
| 202 | return new Uint32Array(wasm_exports.memory.buffer, ptr, len); | |
| 203 | } | |
| 204 | function unwrapString(bigint) { | |
| 205 | const ptr = Number(bigint & 0xffffffffn); | |
| 206 | const len = Number(bigint >> 32n); | |
| 207 | return decodeString(ptr, len); | |
| 208 | } | |
| 209 | ||
| 210 | const time_report_entry_template = document.getElementById("timeReportEntryTemplate").content; | |
| 211 | const domTimeReport = document.getElementById("timeReport"); | |
| 212 | const domTimeReportList = document.getElementById("timeReportList"); | |
| 213 | function 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 | } | |
| 230 | function 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 | } | |
| 257 | function 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 | ||
| 271 | const fuzz_entry_template = document.getElementById("fuzzEntryTemplate").content; | |
| 272 | const domFuzz = document.getElementById("fuzz"); | |
| 273 | const domFuzzStatus = document.getElementById("fuzzStatus"); | |
| 274 | const domFuzzEntries = document.getElementById("fuzzEntries"); | |
| 275 | let domFuzzInstance = null; | |
| 276 | function 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 | } | |
| 294 | function 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 | } | |
| 307 | function fuzzReset() { | |
| 308 | domFuzz.classList.add("hidden"); | |
| 309 | domFuzzEntries.replaceChildren(); | |
| 310 | domFuzzInstance = null; | |
| 311 | } | |
| 312 | function 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 | } | |
| 318 | function 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 | } | |
| 324 | function 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 | } | |
| 331 | function 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const abi = std.Build.abi; | |
| 4 | const gpa = std.heap.wasm_allocator; | |
| 5 | const log = std.log; | |
| 6 | const Allocator = std.mem.Allocator; | |
| 7 | ||
| 8 | const fuzz = @import("fuzz.zig"); | |
| 9 | const time_report = @import("time_report.zig"); | |
| 10 | ||
| 11 | /// Nanoseconds. | |
| 12 | var server_base_timestamp: i64 = 0; | |
| 13 | /// Milliseconds. | |
| 14 | var client_base_timestamp: i64 = 0; | |
| 15 | ||
| 16 | pub var step_list: []Step = &.{}; | |
| 17 | /// Not accessed after initialization, but must be freed alongside `step_list`. | |
| 18 | pub var step_list_data: []u8 = &.{}; | |
| 19 | ||
| 20 | const Step = struct { | |
| 21 | name: []const u8, | |
| 22 | status: abi.StepUpdate.Status, | |
| 23 | }; | |
| 24 | ||
| 25 | const 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 | ||
| 39 | pub const std_options: std.Options = .{ | |
| 40 | .logFn = logFn, | |
| 41 | }; | |
| 42 | ||
| 43 | pub 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 | ||
| 50 | fn 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 | ||
| 66 | export fn alloc(n: usize) [*]u8 { | |
| 67 | const slice = gpa.alloc(u8, n) catch @panic("OOM"); | |
| 68 | return slice.ptr; | |
| 69 | } | |
| 70 | ||
| 71 | var 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. | |
| 75 | export fn message_begin(len: usize) [*]u8 { | |
| 76 | message_buffer.resize(gpa, len) catch @panic("OOM"); | |
| 77 | return message_buffer.items.ptr; | |
| 78 | } | |
| 79 | ||
| 80 | export 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 | ||
| 100 | const String = Slice(u8); | |
| 101 | ||
| 102 | pub 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 | ||
| 116 | pub 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 | ||
| 125 | fn 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 | } | |
| 165 | fn 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 | } | |
| 170 | fn 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 | ||
| 178 | export fn stepName(idx: usize) String { | |
| 179 | return .init(step_list[idx].name); | |
| 180 | } | |
| 181 | export fn stepStatus(idx: usize) u8 { | |
| 182 | return @intFromEnum(step_list[idx].status); | |
| 183 | } | |
| 184 | ||
| 185 | export 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. | |
| 192 | pub 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 | ||
| 198 | pub fn fmtEscapeHtml(unescaped: []const u8) HtmlEscaper { | |
| 199 | return .{ .unescaped = unescaped }; | |
| 200 | } | |
| 201 | const 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 @@ |
| 1 | body { | |
| 2 | font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif; | |
| 3 | color: #000000; | |
| 4 | padding: 1em 10%; | |
| 5 | } | |
| 6 | ul.no-marker { | |
| 7 | list-style-type: none; | |
| 8 | padding-left: 0; | |
| 9 | } | |
| 10 | hr { | |
| 11 | margin: 2em 0; | |
| 12 | } | |
| 13 | .hidden { | |
| 14 | display: none; | |
| 15 | } | |
| 16 | .empty-cell { | |
| 17 | background: #ccc; | |
| 18 | } | |
| 19 | table.time-stats > tbody > tr > th { | |
| 20 | text-align: left; | |
| 21 | } | |
| 22 | table.time-stats > tbody > tr > td { | |
| 23 | text-align: right; | |
| 24 | } | |
| 25 | details > 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 | } | |
| 48 | table { | |
| 49 | margin: 1.0em auto 1.5em 0; | |
| 50 | border-collapse: collapse; | |
| 51 | } | |
| 52 | th, td { | |
| 53 | padding: 0.5em 1em 0.5em 1em; | |
| 54 | border: 1px solid; | |
| 55 | border-color: black; | |
| 56 | } | |
| 57 | a, button { | |
| 58 | color: #2A6286; | |
| 59 | } | |
| 60 | button { | |
| 61 | background: #eee; | |
| 62 | cursor: pointer; | |
| 63 | border: none; | |
| 64 | border-radius: 3px; | |
| 65 | padding: 0.2em 0.5em; | |
| 66 | } | |
| 67 | button.big-btn { | |
| 68 | font-size: 1.3em; | |
| 69 | } | |
| 70 | button.linkish { | |
| 71 | background: none; | |
| 72 | text-decoration: underline; | |
| 73 | padding: 0; | |
| 74 | } | |
| 75 | button:disabled { | |
| 76 | color: #888; | |
| 77 | cursor: not-allowed; | |
| 78 | } | |
| 79 | pre { | |
| 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 | } | |
| 90 | code { | |
| 91 | font-family: "Source Code Pro", monospace; | |
| 92 | font-size: 0.9em; | |
| 93 | } | |
| 94 | code a { | |
| 95 | color: #000000; | |
| 96 | } | |
| 97 | kbd { | |
| 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 | } | |
| 19 | details.section { | |
| 20 | margin: 1.0em 0 0 0; | |
| 21 | } | |
| 22 | details.section > summary { | |
| 23 | font-weight: bold; | |
| 24 | } | |
| 25 | details.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 | } | |
| 40 | th { | |
| 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const gpa = std.heap.wasm_allocator; | |
| 3 | const abi = std.Build.abi.time_report; | |
| 4 | const fmtEscapeHtml = @import("root").fmtEscapeHtml; | |
| 5 | const step_list = &@import("root").step_list; | |
| 6 | ||
| 7 | const 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 | ||
| 32 | pub 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 | ||
| 47 | pub 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 | 9 | const File = std.fs.File; |
| 10 | 10 | const Step = std.Build.Step; |
| 11 | 11 | const Watch = std.Build.Watch; |
| 12 | const Fuzz = std.Build.Fuzz; | |
| 12 | const WebServer = std.Build.WebServer; | |
| 13 | 13 | const Allocator = std.mem.Allocator; |
| 14 | 14 | const fatal = std.process.fatal; |
| 15 | 15 | const Writer = std.io.Writer; |
| ... | ... | @@ -25,15 +25,16 @@ pub const std_options: std.Options = .{ |
| 25 | 25 | }; |
| 26 | 26 | |
| 27 | 27 | pub fn main() !void { |
| 28 | // Here we use an ArenaAllocator backed by a page allocator because a build is a short-lived, | |
| 29 | // one shot program. We don't need to waste time freeing memory and finding places to squish | |
| 30 | // bytes into. So we free everything all at once at the very end. | |
| 31 | var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | |
| 28 | // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not | |
| 29 | // always the case. So, we do need a true gpa for some things. | |
| 30 | var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init; | |
| 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 | 36 | defer single_threaded_arena.deinit(); |
| 33 | ||
| 34 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ | |
| 35 | .child_allocator = single_threaded_arena.allocator(), | |
| 36 | }; | |
| 37 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ .child_allocator = single_threaded_arena.allocator() }; | |
| 37 | 38 | const arena = thread_safe_arena.allocator(); |
| 38 | 39 | |
| 39 | 40 | const args = try process.argsAlloc(arena); |
| ... | ... | @@ -81,6 +82,7 @@ pub fn main() !void { |
| 81 | 82 | .query = .{}, |
| 82 | 83 | .result = try std.zig.system.resolveTargetQuery(.{}), |
| 83 | 84 | }, |
| 85 | .time_report = false, | |
| 84 | 86 | }; |
| 85 | 87 | |
| 86 | 88 | graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); |
| ... | ... | @@ -113,7 +115,7 @@ pub fn main() !void { |
| 113 | 115 | var watch = false; |
| 114 | 116 | var fuzz = false; |
| 115 | 117 | var debounce_interval_ms: u16 = 50; |
| 116 | var listen_port: u16 = 0; | |
| 118 | var webui_listen: ?std.net.Address = null; | |
| 117 | 119 | |
| 118 | 120 | while (nextArg(args, &arg_idx)) |arg| { |
| 119 | 121 | if (mem.startsWith(u8, arg, "-Z")) { |
| ... | ... | @@ -220,13 +222,13 @@ pub fn main() !void { |
| 220 | 222 | next_arg, @errorName(err), |
| 221 | 223 | }); |
| 222 | 224 | }; |
| 223 | } else if (mem.eql(u8, arg, "--port")) { | |
| 224 | const next_arg = nextArg(args, &arg_idx) orelse | |
| 225 | fatalWithHint("expected u16 after '{s}'", .{arg}); | |
| 226 | listen_port = std.fmt.parseUnsigned(u16, next_arg, 10) catch |err| { | |
| 227 | fatal("unable to parse port '{s}' as unsigned 16-bit integer: {s}\n", .{ | |
| 228 | next_arg, @errorName(err), | |
| 229 | }); | |
| 225 | } else if (mem.eql(u8, arg, "--webui")) { | |
| 226 | webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable; | |
| 227 | } else if (mem.startsWith(u8, arg, "--webui=")) { | |
| 228 | const addr_str = arg["--webui=".len..]; | |
| 229 | if (std.mem.eql(u8, addr_str, "-")) fatal("web interface cannot listen on stdio", .{}); | |
| 230 | webui_listen = std.net.Address.parseIpAndPort(addr_str) catch |err| { | |
| 231 | fatal("invalid web UI address '{s}': {s}", .{ addr_str, @errorName(err) }); | |
| 230 | 232 | }; |
| 231 | 233 | } else if (mem.eql(u8, arg, "--debug-log")) { |
| 232 | 234 | const next_arg = nextArgOrFatal(args, &arg_idx); |
| ... | ... | @@ -267,8 +269,16 @@ pub fn main() !void { |
| 267 | 269 | prominent_compile_errors = true; |
| 268 | 270 | } else if (mem.eql(u8, arg, "--watch")) { |
| 269 | 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 | 277 | } else if (mem.eql(u8, arg, "--fuzz")) { |
| 271 | 278 | fuzz = true; |
| 279 | if (webui_listen == null) { | |
| 280 | webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable; | |
| 281 | } | |
| 272 | 282 | } else if (mem.eql(u8, arg, "-fincremental")) { |
| 273 | 283 | graph.incremental = true; |
| 274 | 284 | } else if (mem.eql(u8, arg, "-fno-incremental")) { |
| ... | ... | @@ -331,6 +341,10 @@ pub fn main() !void { |
| 331 | 341 | } |
| 332 | 342 | } |
| 333 | 343 | |
| 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 | 348 | const stderr: std.fs.File = .stderr(); |
| 335 | 349 | const ttyconf = get_tty_conf(color, stderr); |
| 336 | 350 | switch (ttyconf) { |
| ... | ... | @@ -394,14 +408,16 @@ pub fn main() !void { |
| 394 | 408 | } |
| 395 | 409 | |
| 396 | 410 | var run: Run = .{ |
| 411 | .gpa = gpa, | |
| 412 | ||
| 397 | 413 | .max_rss = max_rss, |
| 398 | 414 | .max_rss_is_default = false, |
| 399 | 415 | .max_rss_mutex = .{}, |
| 400 | 416 | .skip_oom_steps = skip_oom_steps, |
| 401 | 417 | .watch = watch, |
| 402 | .fuzz = fuzz, | |
| 403 | .memory_blocked_steps = std.ArrayList(*Step).init(arena), | |
| 404 | .step_stack = .{}, | |
| 418 | .web_server = undefined, // set after `prepare` | |
| 419 | .memory_blocked_steps = .empty, | |
| 420 | .step_stack = .empty, | |
| 405 | 421 | .prominent_compile_errors = prominent_compile_errors, |
| 406 | 422 | |
| 407 | 423 | .claimed_rss = 0, |
| ... | ... | @@ -410,74 +426,81 @@ pub fn main() !void { |
| 410 | 426 | .stderr = stderr, |
| 411 | 427 | .thread_pool = undefined, |
| 412 | 428 | }; |
| 429 | defer { | |
| 430 | run.memory_blocked_steps.deinit(gpa); | |
| 431 | run.step_stack.deinit(gpa); | |
| 432 | } | |
| 413 | 433 | |
| 414 | 434 | if (run.max_rss == 0) { |
| 415 | 435 | run.max_rss = process.totalSystemMemory() catch std.math.maxInt(u64); |
| 416 | 436 | run.max_rss_is_default = true; |
| 417 | 437 | } |
| 418 | 438 | |
| 419 | const gpa = arena; | |
| 420 | prepare(gpa, arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) { | |
| 439 | prepare(arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) { | |
| 421 | 440 | error.UncleanExit => process.exit(1), |
| 422 | 441 | else => return err, |
| 423 | 442 | }; |
| 424 | 443 | |
| 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 | }; | |
| 426 | 449 | |
| 427 | 450 | try run.thread_pool.init(thread_pool_options); |
| 428 | 451 | defer run.thread_pool.deinit(); |
| 429 | 452 | |
| 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 | 468 | rebuild: while (true) { |
| 469 | if (run.web_server) |*ws| ws.startBuild(); | |
| 470 | ||
| 431 | 471 | runStepNames( |
| 432 | gpa, | |
| 433 | 472 | builder, |
| 434 | 473 | targets.items, |
| 435 | 474 | main_progress_node, |
| 436 | 475 | &run, |
| 437 | 476 | ) catch |err| switch (err) { |
| 438 | 477 | error.UncleanExit => { |
| 439 | assert(!run.watch); | |
| 478 | assert(!run.watch and run.web_server == null); | |
| 440 | 479 | process.exit(1); |
| 441 | 480 | }, |
| 442 | 481 | else => return err, |
| 443 | 482 | }; |
| 444 | if (fuzz) { | |
| 445 | if (builtin.single_threaded) { | |
| 446 | fatal("--fuzz not yet implemented for single-threaded builds", .{}); | |
| 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 | ); | |
| 483 | ||
| 484 | if (run.web_server) |*web_server| { | |
| 485 | web_server.finishBuild(.{ .fuzz = fuzz }); | |
| 476 | 486 | } |
| 477 | 487 | |
| 478 | if (!watch) return cleanExit(); | |
| 488 | if (!watch and run.web_server == null) { | |
| 489 | return cleanExit(); | |
| 490 | } | |
| 479 | 491 | |
| 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 | } | |
| 481 | 504 | |
| 482 | 505 | try w.update(gpa, run.step_stack.keys()); |
| 483 | 506 | |
| ... | ... | @@ -491,15 +514,16 @@ pub fn main() !void { |
| 491 | 514 | w.dir_table.entries.len, countSubProcesses(run.step_stack.keys()), |
| 492 | 515 | }) catch &caption_buf; |
| 493 | 516 | var debouncing_node = main_progress_node.start(caption, 0); |
| 494 | var debounce_timeout: Watch.Timeout = .none; | |
| 495 | while (true) switch (try w.wait(gpa, debounce_timeout)) { | |
| 517 | var in_debounce = false; | |
| 518 | while (true) switch (try w.wait(gpa, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) { | |
| 496 | 519 | .timeout => { |
| 520 | assert(in_debounce); | |
| 497 | 521 | debouncing_node.end(); |
| 498 | 522 | markFailedStepsDirty(gpa, run.step_stack.keys()); |
| 499 | 523 | continue :rebuild; |
| 500 | 524 | }, |
| 501 | .dirty => if (debounce_timeout == .none) { | |
| 502 | debounce_timeout = .{ .ms = debounce_interval_ms }; | |
| 525 | .dirty => if (!in_debounce) { | |
| 526 | in_debounce = true; | |
| 503 | 527 | debouncing_node.end(); |
| 504 | 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 | 554 | } |
| 531 | 555 | |
| 532 | 556 | const Run = struct { |
| 557 | gpa: Allocator, | |
| 533 | 558 | max_rss: u64, |
| 534 | 559 | max_rss_is_default: bool, |
| 535 | 560 | max_rss_mutex: std.Thread.Mutex, |
| 536 | 561 | skip_oom_steps: bool, |
| 537 | 562 | watch: bool, |
| 538 | fuzz: bool, | |
| 539 | memory_blocked_steps: std.ArrayList(*Step), | |
| 563 | web_server: ?WebServer, | |
| 564 | /// Allocated into `gpa`. | |
| 565 | memory_blocked_steps: std.ArrayListUnmanaged(*Step), | |
| 566 | /// Allocated into `gpa`. | |
| 540 | 567 | step_stack: std.AutoArrayHashMapUnmanaged(*Step, void), |
| 541 | 568 | prominent_compile_errors: bool, |
| 542 | 569 | thread_pool: std.Thread.Pool, |
| ... | ... | @@ -547,19 +574,19 @@ const Run = struct { |
| 547 | 574 | stderr: File, |
| 548 | 575 | |
| 549 | 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 | 578 | return runner.cleanExit(); |
| 552 | 579 | } |
| 553 | 580 | }; |
| 554 | 581 | |
| 555 | 582 | fn prepare( |
| 556 | gpa: Allocator, | |
| 557 | 583 | arena: Allocator, |
| 558 | 584 | b: *std.Build, |
| 559 | 585 | step_names: []const []const u8, |
| 560 | 586 | run: *Run, |
| 561 | 587 | seed: u32, |
| 562 | 588 | ) !void { |
| 589 | const gpa = run.gpa; | |
| 563 | 590 | const step_stack = &run.step_stack; |
| 564 | 591 | |
| 565 | 592 | if (step_names.len == 0) { |
| ... | ... | @@ -583,7 +610,7 @@ fn prepare( |
| 583 | 610 | rand.shuffle(*Step, starting_steps); |
| 584 | 611 | |
| 585 | 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 | 614 | error.DependencyLoopDetected => return uncleanExit(), |
| 588 | 615 | else => |e| return e, |
| 589 | 616 | }; |
| ... | ... | @@ -614,12 +641,12 @@ fn prepare( |
| 614 | 641 | } |
| 615 | 642 | |
| 616 | 643 | fn runStepNames( |
| 617 | gpa: Allocator, | |
| 618 | 644 | b: *std.Build, |
| 619 | 645 | step_names: []const []const u8, |
| 620 | 646 | parent_prog_node: std.Progress.Node, |
| 621 | 647 | run: *Run, |
| 622 | 648 | ) !void { |
| 649 | const gpa = run.gpa; | |
| 623 | 650 | const step_stack = &run.step_stack; |
| 624 | 651 | const thread_pool = &run.thread_pool; |
| 625 | 652 | |
| ... | ... | @@ -675,6 +702,7 @@ fn runStepNames( |
| 675 | 702 | // B will be marked as dependency_failure, while A may never be queued, and thus |
| 676 | 703 | // remain in the initial state of precheck_done. |
| 677 | 704 | s.state = .dependency_failure; |
| 705 | if (run.web_server) |*ws| ws.updateStepStatus(s, .failure); | |
| 678 | 706 | pending_count += 1; |
| 679 | 707 | }, |
| 680 | 708 | .dependency_failure => pending_count += 1, |
| ... | ... | @@ -768,7 +796,7 @@ fn runStepNames( |
| 768 | 796 | } |
| 769 | 797 | } |
| 770 | 798 | |
| 771 | if (!run.watch) { | |
| 799 | if (!run.watch and run.web_server == null) { | |
| 772 | 800 | // Signal to parent process that we have printed compile errors. The |
| 773 | 801 | // parent process may choose to omit the "following command failed" |
| 774 | 802 | // line in this case. |
| ... | ... | @@ -777,7 +805,7 @@ fn runStepNames( |
| 777 | 805 | } |
| 778 | 806 | } |
| 779 | 807 | |
| 780 | if (!run.watch) return uncleanExit(); | |
| 808 | if (!run.watch and run.web_server == null) return uncleanExit(); | |
| 781 | 809 | } |
| 782 | 810 | |
| 783 | 811 | const PrintNode = struct { |
| ... | ... | @@ -1022,6 +1050,7 @@ fn printTreeStep( |
| 1022 | 1050 | /// when it finishes executing in `workerMakeOneStep`, it spawns next steps |
| 1023 | 1051 | /// to run in random order |
| 1024 | 1052 | fn constructGraphAndCheckForDependencyLoop( |
| 1053 | gpa: Allocator, | |
| 1025 | 1054 | b: *std.Build, |
| 1026 | 1055 | s: *Step, |
| 1027 | 1056 | step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), |
| ... | ... | @@ -1035,17 +1064,19 @@ fn constructGraphAndCheckForDependencyLoop( |
| 1035 | 1064 | .precheck_unstarted => { |
| 1036 | 1065 | s.state = .precheck_started; |
| 1037 | 1066 | |
| 1038 | try step_stack.ensureUnusedCapacity(b.allocator, s.dependencies.items.len); | |
| 1067 | try step_stack.ensureUnusedCapacity(gpa, s.dependencies.items.len); | |
| 1039 | 1068 | |
| 1040 | 1069 | // We dupe to avoid shuffling the steps in the summary, it depends |
| 1041 | 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 | 1074 | rand.shuffle(*Step, deps); |
| 1044 | 1075 | |
| 1045 | 1076 | for (deps) |dep| { |
| 1046 | try step_stack.put(b.allocator, dep, {}); | |
| 1077 | try step_stack.put(gpa, dep, {}); | |
| 1047 | 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 | 1080 | if (err == error.DependencyLoopDetected) { |
| 1050 | 1081 | std.debug.print(" {s}\n", .{s.name}); |
| 1051 | 1082 | } |
| ... | ... | @@ -1084,6 +1115,7 @@ fn workerMakeOneStep( |
| 1084 | 1115 | .success, .skipped => continue, |
| 1085 | 1116 | .failure, .dependency_failure, .skipped_oom => { |
| 1086 | 1117 | @atomicStore(Step.State, &s.state, .dependency_failure, .seq_cst); |
| 1118 | if (run.web_server) |*ws| ws.updateStepStatus(s, .failure); | |
| 1087 | 1119 | return; |
| 1088 | 1120 | }, |
| 1089 | 1121 | .precheck_done, .running => { |
| ... | ... | @@ -1109,7 +1141,7 @@ fn workerMakeOneStep( |
| 1109 | 1141 | if (new_claimed_rss > run.max_rss) { |
| 1110 | 1142 | // Running this step right now could possibly exceed the allotted RSS. |
| 1111 | 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 | 1145 | return; |
| 1114 | 1146 | } |
| 1115 | 1147 | |
| ... | ... | @@ -1126,10 +1158,14 @@ fn workerMakeOneStep( |
| 1126 | 1158 | const sub_prog_node = prog_node.start(s.name, 0); |
| 1127 | 1159 | defer sub_prog_node.end(); |
| 1128 | 1160 | |
| 1161 | if (run.web_server) |*ws| ws.updateStepStatus(s, .wip); | |
| 1162 | ||
| 1129 | 1163 | const make_result = s.make(.{ |
| 1130 | 1164 | .progress_node = sub_prog_node, |
| 1131 | 1165 | .thread_pool = thread_pool, |
| 1132 | 1166 | .watch = run.watch, |
| 1167 | .web_server = if (run.web_server) |*ws| ws else null, | |
| 1168 | .gpa = run.gpa, | |
| 1133 | 1169 | }); |
| 1134 | 1170 | |
| 1135 | 1171 | // No matter the result, we want to display error/warning messages. |
| ... | ... | @@ -1141,21 +1177,24 @@ fn workerMakeOneStep( |
| 1141 | 1177 | if (show_error_msgs or show_compile_errors or show_stderr) { |
| 1142 | 1178 | const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation); |
| 1143 | 1179 | defer std.debug.unlockStderrWriter(); |
| 1144 | ||
| 1145 | const gpa = b.allocator; | |
| 1146 | printErrorMessages(gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.prominent_compile_errors) catch {}; | |
| 1180 | printErrorMessages(run.gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.prominent_compile_errors) catch {}; | |
| 1147 | 1181 | } |
| 1148 | 1182 | |
| 1149 | 1183 | handle_result: { |
| 1150 | 1184 | if (make_result) |_| { |
| 1151 | 1185 | @atomicStore(Step.State, &s.state, .success, .seq_cst); |
| 1186 | if (run.web_server) |*ws| ws.updateStepStatus(s, .success); | |
| 1152 | 1187 | } else |err| switch (err) { |
| 1153 | 1188 | error.MakeFailed => { |
| 1154 | 1189 | @atomicStore(Step.State, &s.state, .failure, .seq_cst); |
| 1190 | if (run.web_server) |*ws| ws.updateStepStatus(s, .failure); | |
| 1155 | 1191 | std.Progress.setStatus(.failure_working); |
| 1156 | 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 | } |
| 1160 | 1199 | |
| 1161 | 1200 | // Successful completion of a step, so we queue up its dependants as well. |
| ... | ... | @@ -1255,10 +1294,10 @@ pub fn printErrorMessages( |
| 1255 | 1294 | } |
| 1256 | 1295 | |
| 1257 | 1296 | fn printSteps(builder: *std.Build, w: *Writer) !void { |
| 1258 | const allocator = builder.allocator; | |
| 1297 | const arena = builder.graph.arena; | |
| 1259 | 1298 | for (builder.top_level_steps.values()) |top_level_step| { |
| 1260 | 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 | 1301 | else |
| 1263 | 1302 | top_level_step.step.name; |
| 1264 | 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 | 1358 | \\ needed (Default) Lazy dependencies are fetched as needed |
| 1320 | 1359 | \\ all Lazy dependencies are always fetched |
| 1321 | 1360 | \\ --watch Continuously rebuild when source files are modified |
| 1322 | \\ --fuzz Continuously search for unit test failures | |
| 1323 | 1361 | \\ --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 | 1366 | \\ -fincremental Enable incremental compilation |
| 1325 | 1367 | \\ -fno-incremental Disable incremental compilation |
| 1326 | 1368 | \\ |
| ... | ... | @@ -1328,7 +1370,7 @@ fn printUsage(b: *std.Build, w: *Writer) !void { |
| 1328 | 1370 | \\ |
| 1329 | 1371 | ); |
| 1330 | 1372 | |
| 1331 | const arena = b.allocator; | |
| 1373 | const arena = b.graph.arena; | |
| 1332 | 1374 | if (b.available_options_list.items.len == 0) { |
| 1333 | 1375 | try w.print(" (none)\n", .{}); |
| 1334 | 1376 | } else { |
lib/fuzzer.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ const std = @import("std"); |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const fatal = std.process.fatal; |
| 6 | const SeenPcsHeader = std.Build.Fuzz.abi.SeenPcsHeader; | |
| 6 | const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader; | |
| 7 | 7 | |
| 8 | 8 | pub const std_options = std.Options{ |
| 9 | 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const abi = std.Build.Fuzz.abi; | |
| 4 | const gpa = std.heap.wasm_allocator; | |
| 5 | const log = std.log; | |
| 6 | const Coverage = std.debug.Coverage; | |
| 7 | const Allocator = std.mem.Allocator; | |
| 8 | ||
| 9 | const Walk = @import("Walk"); | |
| 10 | const Decl = Walk.Decl; | |
| 11 | const html_render = @import("html_render"); | |
| 12 | ||
| 13 | /// Nanoseconds. | |
| 14 | var server_base_timestamp: i64 = 0; | |
| 15 | /// Milliseconds. | |
| 16 | var client_base_timestamp: i64 = 0; | |
| 17 | /// Relative to `server_base_timestamp`. | |
| 18 | var start_fuzzing_timestamp: i64 = undefined; | |
| 19 | ||
| 20 | const 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 | ||
| 29 | pub const std_options: std.Options = .{ | |
| 30 | .logFn = logFn, | |
| 31 | }; | |
| 32 | ||
| 33 | pub 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 | ||
| 40 | fn 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 | ||
| 56 | export fn alloc(n: usize) [*]u8 { | |
| 57 | const slice = gpa.alloc(u8, n) catch @panic("OOM"); | |
| 58 | return slice.ptr; | |
| 59 | } | |
| 60 | ||
| 61 | var 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. | |
| 65 | export fn message_begin(len: usize) [*]u8 { | |
| 66 | message_buffer.resize(gpa, len) catch @panic("OOM"); | |
| 67 | return message_buffer.items.ptr; | |
| 68 | } | |
| 69 | ||
| 70 | export 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 | ||
| 83 | export 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`. | |
| 93 | var input_string: std.ArrayListUnmanaged(u8) = .empty; | |
| 94 | var string_result: std.ArrayListUnmanaged(u8) = .empty; | |
| 95 | ||
| 96 | export 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`. | |
| 103 | export 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 | ||
| 108 | export 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 | ||
| 118 | export fn totalSourceLocations() usize { | |
| 119 | return coverage_source_locations.items.len; | |
| 120 | } | |
| 121 | ||
| 122 | export 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 | ||
| 129 | fn getCoverageUpdateHeader() *abi.CoverageUpdateHeader { | |
| 130 | return @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)])); | |
| 131 | } | |
| 132 | ||
| 133 | export fn totalRuns() u64 { | |
| 134 | const header = getCoverageUpdateHeader(); | |
| 135 | return header.n_runs; | |
| 136 | } | |
| 137 | ||
| 138 | export fn uniqueRuns() u64 { | |
| 139 | const header = getCoverageUpdateHeader(); | |
| 140 | return header.unique_runs; | |
| 141 | } | |
| 142 | ||
| 143 | export fn totalRunsPerSecond() f64 { | |
| 144 | @setFloatMode(.optimized); | |
| 145 | const header = getCoverageUpdateHeader(); | |
| 146 | const ns_elapsed: f64 = @floatFromInt(nsSince(start_fuzzing_timestamp)); | |
| 147 | const n_runs: f64 = @floatFromInt(header.n_runs); | |
| 148 | return n_runs / (ns_elapsed / std.time.ns_per_s); | |
| 149 | } | |
| 150 | ||
| 151 | const String = Slice(u8); | |
| 152 | ||
| 153 | fn 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 | ||
| 167 | fn 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 | ||
| 204 | fn 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 | ||
| 213 | fn currentTimeMessage(msg_bytes: []u8) void { | |
| 214 | client_base_timestamp = js.timestamp(); | |
| 215 | server_base_timestamp = @bitCast(msg_bytes[1..][0..8].*); | |
| 216 | } | |
| 217 | ||
| 218 | /// Nanoseconds passed since a server timestamp. | |
| 219 | fn nsSince(server_timestamp: i64) i64 { | |
| 220 | const ms_passed = js.timestamp() - client_base_timestamp; | |
| 221 | const ns_passed = server_base_timestamp - server_timestamp; | |
| 222 | return ns_passed + ms_passed * std.time.ns_per_ms; | |
| 223 | } | |
| 224 | ||
| 225 | fn 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 | ||
| 246 | fn 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 | ||
| 252 | var entry_points: std.ArrayListUnmanaged(u32) = .empty; | |
| 253 | ||
| 254 | fn 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 | ||
| 261 | export fn entryPoints() Slice(u32) { | |
| 262 | return Slice(u32).init(entry_points.items); | |
| 263 | } | |
| 264 | ||
| 265 | /// Index into `coverage_source_locations`. | |
| 266 | const 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 | ||
| 321 | fn 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 | ||
| 375 | var coverage = Coverage.init; | |
| 376 | /// Index of type `SourceLocationIndex`. | |
| 377 | var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .empty; | |
| 378 | /// Contains the most recent coverage update message, unmodified. | |
| 379 | var recent_coverage_update: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty; | |
| 380 | ||
| 381 | fn 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 | ||
| 404 | export 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. | |
| 411 | export 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 | ||
| 417 | export 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 | ||
| 426 | export 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 | 837 | #define NT_ARM_ZT	0x40d		/* ARM SME ZT registers. */ |
| 838 | 838 | #define NT_ARM_FPMR	0x40e		/* ARM floating point mode register. */ |
| 839 | 839 | #define NT_ARM_POE	0x40f		/* ARM POE registers. */ |
| 840 | #define NT_ARM_GCS	0x410		/* ARM GCS state. */ | |
| 840 | 841 | #define NT_VMCOREDD	0x700		/* Vmcore Device Dump Note. */ |
| 841 | 842 | #define NT_MIPS_DSP	0x800		/* MIPS DSP ASE registers. */ |
| 842 | 843 | #define NT_MIPS_FP_MODE	0x801		/* MIPS floating-point mode. */ |
| 843 | 844 | #define NT_MIPS_MSA	0x802		/* MIPS SIMD registers. */ |
| 844 | 845 | #define NT_RISCV_CSR	0x900		/* RISC-V Control and Status Registers */ |
| 845 | 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 | 849 | #define NT_LOONGARCH_CPUCFG	0xa00	/* LoongArch CPU config registers. */ |
| 847 | 850 | #define NT_LOONGARCH_CSR	0xa01	/* LoongArch control and |
| 848 | 851 | 					 status registers. */ |
| ... | ... | @@ -2906,19 +2909,6 @@ enum |
| 2906 | 2909 | |
| 2907 | 2910 | #define R_AARCH64_NONE 0	/* No relocation. */ |
| 2908 | 2911 | |
| 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 | 2912 | #define R_AARCH64_ABS64 257	/* Direct 64 bit. */ |
| 2923 | 2913 | #define R_AARCH64_ABS32 258	/* Direct 32 bit. */ |
| 2924 | 2914 | #define R_AARCH64_ABS16		259	/* Direct 16-bit. */ |
| ... | ... | @@ -4091,6 +4081,7 @@ enum |
| 4091 | 4081 | #define R_RISCV_TLS_DTPREL64	 9 |
| 4092 | 4082 | #define R_RISCV_TLS_TPREL32	10 |
| 4093 | 4083 | #define R_RISCV_TLS_TPREL64	11 |
| 4084 | #define R_RISCV_TLSDESC		12 | |
| 4094 | 4085 | #define R_RISCV_BRANCH		16 |
| 4095 | 4086 | #define R_RISCV_JAL		17 |
| 4096 | 4087 | #define R_RISCV_CALL		18 |
| ... | ... | @@ -4116,16 +4107,10 @@ enum |
| 4116 | 4107 | #define R_RISCV_SUB16		38 |
| 4117 | 4108 | #define R_RISCV_SUB32		39 |
| 4118 | 4109 | #define R_RISCV_SUB64		40 |
| 4119 | #define R_RISCV_GNU_VTINHERIT	41 | |
| 4120 | #define R_RISCV_GNU_VTENTRY	42 | |
| 4110 | #define R_RISCV_GOT32_PCREL	41 | |
| 4121 | 4111 | #define R_RISCV_ALIGN		43 |
| 4122 | 4112 | #define R_RISCV_RVC_BRANCH	44 |
| 4123 | 4113 | #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 | 4114 | #define R_RISCV_RELAX		51 |
| 4130 | 4115 | #define R_RISCV_SUB6		52 |
| 4131 | 4116 | #define R_RISCV_SET6		53 |
| ... | ... | @@ -4137,8 +4122,12 @@ enum |
| 4137 | 4122 | #define R_RISCV_PLT32		59 |
| 4138 | 4123 | #define R_RISCV_SET_ULEB128	60 |
| 4139 | 4124 | #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 | |
| 4140 | 4129 | |
| 4141 | #define R_RISCV_NUM		62 | |
| 4130 | #define R_RISCV_NUM		66 | |
| 4142 | 4131 | |
| 4143 | 4132 | /* RISC-V specific values for the st_other field. */ |
| 4144 | 4133 | #define STO_RISCV_VARIANT_CC	0x80	/* Function uses variant calling |
| ... | ... | @@ -4147,7 +4136,7 @@ enum |
| 4147 | 4136 | /* RISC-V specific values for the sh_type field. */ |
| 4148 | 4137 | #define SHT_RISCV_ATTRIBUTES	(SHT_LOPROC + 3) |
| 4149 | 4138 | |
| 4150 | /* RISC-V specific values for the p_type field. */ | |
| 4139 | /* RISC-V specific values for the p_type field (deprecated). */ | |
| 4151 | 4140 | #define PT_RISCV_ATTRIBUTES	(PT_LOPROC + 3) |
| 4152 | 4141 | |
| 4153 | 4142 | /* RISC-V specific values for the d_tag field. */ |
lib/libc/glibc/include/elf.h+13| ... | ... | @@ -15,6 +15,19 @@ |
| 15 | 15 | # define ELF_NOTE_NEXT_OFFSET(namesz, descsz, align) \ |
| 16 | 16 | ALIGN_UP (ELF_NOTE_DESC_OFFSET ((namesz), (align)) + (descsz), (align)) |
| 17 | 17 | |
| 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 | 31 | /* Some information which is not meant for the public and therefore not |
| 19 | 32 | in <elf.h>. */ |
| 20 | 33 | # include <dl-dtprocnum.h> |
lib/libc/glibc/include/libc-symbols.h+4-4| ... | ... | @@ -155,7 +155,7 @@ |
| 155 | 155 | extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))) \ |
| 156 | 156 | __attribute_copy__ (name); |
| 157 | 157 | |
| 158 | /* Zig patch. weak_hidden_alias was removed from glibc v2.36 (v2.37?), Zig | |
| 158 | /* zig patch: weak_hidden_alias was removed from glibc v2.36 (v2.37?), Zig | |
| 159 | 159 | needs it for the v2.32 and earlier {f,l,}stat wrappers, so only include |
| 160 | 160 | in this header for 2.32 and earlier. */ |
| 161 | 161 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 32) || __GLIBC__ < 2 |
| ... | ... | @@ -220,7 +220,7 @@ |
| 220 | 220 | #define __make_section_unallocated(section_string)	\ |
| 221 | 221 | asm (".section " section_string "\n\t.previous"); |
| 222 | 222 | |
| 223 | /* Tacking on "\n\t#" to the section name makes gcc put it's bogus | |
| 223 | /* Tacking on "\n\t#" to the section name makes gcc put its bogus | |
| 224 | 224 | section attributes on what looks like a comment to the assembler. */ |
| 225 | 225 | #ifdef HAVE_SECTION_QUOTES |
| 226 | 226 | # define __sec_comment "\"\n\t#\"" |
| ... | ... | @@ -280,7 +280,7 @@ for linking") |
| 280 | 280 | |
| 281 | 281 | |
| 282 | 282 | /* |
| 283 | ||
| 283 | ||
| 284 | 284 | */ |
| 285 | 285 | |
| 286 | 286 | #ifdef HAVE_GNU_RETAIN |
| ... | ... | @@ -807,7 +807,7 @@ for linking") |
| 807 | 807 | #define libm_ifunc_init() |
| 808 | 808 | #define libm_ifunc(name, expr)				\ |
| 809 | 809 | __ifunc (name, name, expr, void, libm_ifunc_init) |
| 810 | ||
| 810 | ||
| 811 | 811 | /* These macros facilitate sharing source files with gnulib. |
| 812 | 812 | |
| 813 | 813 | 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 | 368 | extern struct abort_msg_s *__abort_msg; |
| 369 | 369 | libc_hidden_proto (__abort_msg) |
| 370 | 370 | |
| 371 | enum readonly_error_type | |
| 372 | { | |
| 373 | readonly_noerror, | |
| 374 | readonly_area_writable, | |
| 375 | readonly_procfs_inaccessible, | |
| 376 | readonly_procfs_open_fail, | |
| 377 | }; | |
| 378 | ||
| 379 | extern enum readonly_error_type __readonly_area (const void *ptr, | |
| 380 | 						 size_t size) | |
| 381 | attribute_hidden; | |
| 382 | extern enum readonly_error_type __readonly_area_fallback (const void *ptr, | |
| 383 | 							 size_t size) | |
| 384 | attribute_hidden; | |
| 385 | ||
| 371 | 386 | # if IS_IN (rtld) |
| 372 | 387 | extern __typeof (unsetenv) unsetenv attribute_hidden; |
| 373 | 388 | extern __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 | 168 | #endif |
| 169 | 169 | |
| 170 | 170 | |
| 171 | /* fcntl was a simple symbol until glibc 2.27 inclusive. glibc 2.28 onwards | |
| 171 | /* zig patch: fcntl was a simple symbol until glibc 2.27 inclusive. glibc 2.28 onwards | |
| 172 | 172 | * re-defines it to fcntl64 (via #define) if _FILE_OFFSET_BITS == 64. */ |
| 173 | 173 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2 |
| 174 | 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 | 288 | # define F_TEST 3	/* Test a region for other processes locks. */ |
| 289 | 289 | |
| 290 | 290 | # ifndef __USE_FILE_OFFSET64 |
| 291 | extern int lockf (int __fd, int __cmd, off_t __len); | |
| 291 | extern int lockf (int __fd, int __cmd, off_t __len) __wur; | |
| 292 | 292 | # else |
| 293 | 293 | # ifdef __REDIRECT |
| 294 | extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len), lockf64); | |
| 294 | extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len), | |
| 295 | lockf64) __wur; | |
| 295 | 296 | # else |
| 296 | 297 | # define lockf lockf64 |
| 297 | 298 | # endif |
| 298 | 299 | # endif |
| 299 | 300 | # ifdef __USE_LARGEFILE64 |
| 300 | extern int lockf64 (int __fd, int __cmd, off64_t __len); | |
| 301 | extern int lockf64 (int __fd, int __cmd, off64_t __len) __wur; | |
| 301 | 302 | # endif |
| 302 | 303 | #endif |
| 303 | 304 |
lib/libc/glibc/posix/bits/types.h+1-1| ... | ... | @@ -217,7 +217,7 @@ typedef int __sig_atomic_t; |
| 217 | 217 | /* Seconds since the Epoch, visible to user code when time_t is too |
| 218 | 218 | narrow only for consistency with the old way of widening too-narrow |
| 219 | 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 | 221 | #if __TIMESIZE == 64 |
| 222 | 222 | # define __time64_t __time_t |
| 223 | 223 | #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 | 985 | __THROW __attribute__ ((__const__)) __wur; |
| 986 | 986 | #endif |
| 987 | 987 | |
| 988 | #if __GLIBC_USE (ISOC2Y) | |
| 989 | extern unsigned int uabs (int __x) __THROW __attribute__ ((__const__)) __wur; | |
| 990 | extern 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 | |
| 988 | 994 | |
| 989 | 995 | /* Return the `div_t', `ldiv_t' or `lldiv_t' representation |
| 990 | 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 | 21 | |
| 22 | 22 | #include <bits/endian.h> |
| 23 | 23 | |
| 24 | #ifdef __ILP32__ | |
| 25 | # define __SIZEOF_PTHREAD_ATTR_T 32 | |
| 26 | # define __SIZEOF_PTHREAD_MUTEX_T 32 | |
| 27 | # define __SIZEOF_PTHREAD_MUTEXATTR_T 4 | |
| 28 | # define __SIZEOF_PTHREAD_CONDATTR_T 4 | |
| 29 | # define __SIZEOF_PTHREAD_RWLOCK_T 48 | |
| 30 | # define __SIZEOF_PTHREAD_BARRIER_T 20 | |
| 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 | |
| 24 | #define __SIZEOF_PTHREAD_ATTR_T		64 | |
| 25 | #define __SIZEOF_PTHREAD_MUTEX_T	48 | |
| 26 | #define __SIZEOF_PTHREAD_MUTEXATTR_T	8 | |
| 27 | #define __SIZEOF_PTHREAD_CONDATTR_T	8 | |
| 28 | #define __SIZEOF_PTHREAD_RWLOCK_T	56 | |
| 29 | #define __SIZEOF_PTHREAD_BARRIER_T	32 | |
| 30 | #define __SIZEOF_PTHREAD_BARRIERATTR_T 8 | |
| 41 | 31 | #define __SIZEOF_PTHREAD_COND_T 48 |
| 42 | 32 | #define __SIZEOF_PTHREAD_RWLOCKATTR_T	8 |
| 43 | 33 |
lib/libc/glibc/sysdeps/aarch64/start-2.33.S+17-8| ... | ... | @@ -54,8 +54,8 @@ _start: |
| 54 | 54 | 	mov	x5, x0 |
| 55 | 55 | |
| 56 | 56 | 	/* Load argc and a pointer to argv */ |
| 57 | 	ldr	PTR_REG (1), [sp, #0] | |
| 58 | 	add	x2, sp, #PTR_SIZE | |
| 57 | 	ldr	x1, [sp, #0] | |
| 58 | 	add	x2, sp, 8 | |
| 59 | 59 | |
| 60 | 60 | 	/* Setup stack limit in argument register */ |
| 61 | 61 | 	mov	x6, sp |
| ... | ... | @@ -63,13 +63,13 @@ _start: |
| 63 | 63 | #ifdef PIC |
| 64 | 64 | # ifdef SHARED |
| 65 | 65 | adrp x0, :got:main |
| 66 | 	ldr PTR_REG (0), [x0, #:got_lo12:main] | |
| 66 | 	ldr x0, [x0, #:got_lo12:main] | |
| 67 | 67 | |
| 68 | 68 | 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] | |
| 70 | 70 | |
| 71 | 71 | 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 | 73 | # else |
| 74 | 74 | 	adrp	x0, __wrap_main |
| 75 | 75 | 	add	x0, x0, :lo12:__wrap_main |
| ... | ... | @@ -80,9 +80,18 @@ _start: |
| 80 | 80 | # endif |
| 81 | 81 | #else |
| 82 | 82 | 	/* Set up the other arguments in registers */ |
| 83 | 	MOVL (0, main) | |
| 84 | 	MOVL (3, __libc_csu_init) | |
| 85 | 	MOVL (4, __libc_csu_fini) | |
| 83 | 	movz x0, :abs_g3:main | |
| 84 | 	movk x0, :abs_g2_nc:main | |
| 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 | 95 | #endif |
| 87 | 96 | |
| 88 | 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 | 70 | 	mov	x5, x0 |
| 71 | 71 | |
| 72 | 72 | 	/* Load argc and a pointer to argv */ |
| 73 | 	ldr	PTR_REG (1), [sp, #0] | |
| 74 | 	add	x2, sp, #PTR_SIZE | |
| 73 | 	ldr	x1, [sp, #0] | |
| 74 | 	add	x2, sp, 8 | |
| 75 | 75 | |
| 76 | 76 | 	/* Setup stack limit in argument register */ |
| 77 | 77 | 	mov	x6, sp |
| ... | ... | @@ -79,14 +79,16 @@ ENTRY(_start) |
| 79 | 79 | #ifdef PIC |
| 80 | 80 | # ifdef SHARED |
| 81 | 81 | adrp x0, :got:main |
| 82 | 	ldr PTR_REG (0), [x0, #:got_lo12:main] | |
| 82 | 	ldr x0, [x0, #:got_lo12:main] | |
| 83 | 83 | # else |
| 84 | 84 | 	adrp	x0, __wrap_main |
| 85 | 85 | 	add	x0, x0, :lo12:__wrap_main |
| 86 | 86 | # endif |
| 87 | 87 | #else |
| 88 | 	/* Set up the other arguments in registers */ | |
| 89 | 	MOVL (0, main) | |
| 88 | 	movz x0, :abs_g3:main | |
| 89 | 	movk x0, :abs_g2_nc:main | |
| 90 | 	movk x0, :abs_g1_nc:main | |
| 91 | 	movk x0, :abs_g0_nc:main | |
| 90 | 92 | #endif |
| 91 | 93 | 	mov	x3, #0		/* Used to be init. */ |
| 92 | 94 | 	mov	x4, #0		/* Used to be fini. */ |
| ... | ... | @@ -106,7 +108,7 @@ ENTRY(_start) |
| 106 | 108 | 	 because crt1.o and rcrt1.o share code and the later must avoid the |
| 107 | 109 | 	 use of GOT relocations before __libc_start_main is called. */ |
| 108 | 110 | __wrap_main: |
| 109 | 	BTI_C | |
| 111 | 	bti	c | |
| 110 | 112 | 	b	main |
| 111 | 113 | #endif |
| 112 | 114 | END(_start) |
lib/libc/glibc/sysdeps/aarch64/sysdep.h+6-81| ... | ... | @@ -21,59 +21,15 @@ |
| 21 | 21 | |
| 22 | 22 | #include <sysdeps/generic/sysdep.h> |
| 23 | 23 | |
| 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. */ | |
| 42 | static inline void * | |
| 43 | strip_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 | 24 | #ifdef	__ASSEMBLER__ |
| 59 | 25 | |
| 26 | /* CFI directive for return address. */ | |
| 27 | #define cfi_negate_ra_state	.cfi_negate_ra_state | |
| 28 | ||
| 60 | 29 | /* Syntactic details of assembler. */ |
| 61 | 30 | |
| 62 | 31 | #define ASM_SIZE_DIRECTIVE(name) .size name,.-name |
| 63 | 32 | |
| 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 | 33 | /* Guarded Control Stack support. */ |
| 78 | 34 | #define CHKFEAT_X16	hint	40 |
| 79 | 35 | #define MRS_GCSPR(x)	mrs	x, s3_3_c2_c5_1 |
| ... | ... | @@ -103,11 +59,7 @@ strip_pac (void *p) |
| 103 | 59 | |
| 104 | 60 | /* Add GNU property note with the supported features to all asm code |
| 105 | 61 | where sysdep.h is included. */ |
| 106 | #if HAVE_AARCH64_BTI && HAVE_AARCH64_PAC_RET | |
| 107 | 62 | GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_PAC|FEATURE_1_GCS) |
| 108 | #elif HAVE_AARCH64_BTI | |
| 109 | GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS) | |
| 110 | #endif | |
| 111 | 63 | |
| 112 | 64 | /* Define an entry point visible from C. */ |
| 113 | 65 | #define ENTRY(name)						\ |
| ... | ... | @@ -116,7 +68,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS) |
| 116 | 68 | .p2align 6;							\ |
| 117 | 69 | C_LABEL(name)							\ |
| 118 | 70 | cfi_startproc;						\ |
| 119 | BTI_C;							\ | |
| 71 | bti	c;							\ | |
| 120 | 72 | CALL_MCOUNT |
| 121 | 73 | |
| 122 | 74 | /* Define an entry point visible from C. */ |
| ... | ... | @@ -126,7 +78,7 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS) |
| 126 | 78 | .p2align align;						\ |
| 127 | 79 | C_LABEL(name)							\ |
| 128 | 80 | cfi_startproc;						\ |
| 129 | BTI_C;							\ | |
| 81 | bti	c;							\ | |
| 130 | 82 | CALL_MCOUNT |
| 131 | 83 | |
| 132 | 84 | /* 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 | 95 | .endr;							\ |
| 144 | 96 | C_LABEL(name)							\ |
| 145 | 97 | cfi_startproc;						\ |
| 146 | BTI_C;							\ | |
| 98 | bti	c;							\ | |
| 147 | 99 | CALL_MCOUNT |
| 148 | 100 | |
| 149 | 101 | #undef	END |
| ... | ... | @@ -195,33 +147,6 @@ GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_GCS) |
| 195 | 147 | # define L(name) .L##name |
| 196 | 148 | #endif |
| 197 | 149 | |
| 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 | 150 | /* Since C identifiers are not normally prefixed with an underscore |
| 226 | 151 | on this system, the asm identifier `syscall_error' intrudes on the |
| 227 | 152 | C name space. Make sure we use an innocuous name. */ |
lib/libc/glibc/sysdeps/generic/sysdep.h+3| ... | ... | @@ -45,6 +45,7 @@ |
| 45 | 45 | # define cfi_adjust_cfa_offset(off)	.cfi_adjust_cfa_offset off |
| 46 | 46 | # define cfi_offset(reg, off)		.cfi_offset reg, off |
| 47 | 47 | # define cfi_rel_offset(reg, off)	.cfi_rel_offset reg, off |
| 48 | # define cfi_val_offset(reg, off)	.cfi_val_offset reg, off | |
| 48 | 49 | # define cfi_register(r1, r2)		.cfi_register r1, r2 |
| 49 | 50 | # define cfi_return_column(reg)	.cfi_return_column reg |
| 50 | 51 | # define cfi_restore(reg)		.cfi_restore reg |
| ... | ... | @@ -74,6 +75,8 @@ |
| 74 | 75 | ".cfi_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off) |
| 75 | 76 | # define CFI_REL_OFFSET(reg, off) \ |
| 76 | 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 | 80 | # define CFI_REGISTER(r1, r2) \ |
| 78 | 81 | ".cfi_register " CFI_STRINGIFY(r1) "," CFI_STRINGIFY(r2) |
| 79 | 82 | # define CFI_RETURN_COLUMN(reg) \ |
lib/libc/glibc/sysdeps/htl/libc-lockP.h+10-39| ... | ... | @@ -75,7 +75,6 @@ |
| 75 | 75 | |
| 76 | 76 | extern int __pthread_mutex_init (pthread_mutex_t *__mutex, |
| 77 | 77 | 				 const pthread_mutexattr_t *__mutex_attr); |
| 78 | ||
| 79 | 78 | extern int __pthread_mutex_destroy (pthread_mutex_t *__mutex); |
| 80 | 79 | libc_hidden_proto (__pthread_mutex_destroy) |
| 81 | 80 | |
| ... | ... | @@ -91,75 +90,47 @@ libc_hidden_proto (__pthread_mutexattr_init) |
| 91 | 90 | extern int __pthread_mutexattr_destroy (pthread_mutexattr_t *__attr); |
| 92 | 91 | libc_hidden_proto (__pthread_mutexattr_destroy) |
| 93 | 92 | |
| 94 | extern int __pthread_mutexattr_settype (pthread_mutexattr_t *__attr, | |
| 95 | 					int __kind); | |
| 96 | ||
| 97 | 93 | extern int __pthread_rwlock_init (pthread_rwlock_t *__rwlock, |
| 98 | 94 | 				 const pthread_rwlockattr_t *__attr); |
| 95 | libc_hidden_proto (__pthread_rwlock_init) | |
| 99 | 96 | |
| 100 | 97 | extern int __pthread_rwlock_destroy (pthread_rwlock_t *__rwlock); |
| 98 | libc_hidden_proto (__pthread_rwlock_destroy) | |
| 101 | 99 | |
| 102 | 100 | extern int __pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock); |
| 101 | libc_hidden_proto (__pthread_rwlock_rdlock) | |
| 103 | 102 | |
| 104 | 103 | extern int __pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock); |
| 104 | libc_hidden_proto (__pthread_rwlock_tryrdlock) | |
| 105 | 105 | |
| 106 | 106 | extern int __pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock); |
| 107 | libc_hidden_proto (__pthread_rwlock_wrlock) | |
| 107 | 108 | |
| 108 | 109 | extern int __pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock); |
| 110 | libc_hidden_proto (__pthread_rwlock_trywrlock) | |
| 109 | 111 | |
| 110 | 112 | extern int __pthread_rwlock_unlock (pthread_rwlock_t *__rwlock); |
| 113 | libc_hidden_proto (__pthread_rwlock_unlock) | |
| 111 | 114 | |
| 112 | 115 | extern int __pthread_once (pthread_once_t *__once_control, |
| 113 | 116 | 			 void (*__init_routine) (void)); |
| 117 | libc_hidden_proto (__pthread_once); | |
| 114 | 118 | |
| 115 | 119 | extern int __pthread_atfork (void (*__prepare) (void), |
| 116 | 120 | 			 void (*__parent) (void), |
| 117 | 121 | 			 void (*__child) (void)); |
| 118 | 122 | |
| 123 | extern int __pthread_setcancelstate (int state, int *oldstate); | |
| 124 | libc_hidden_proto (__pthread_setcancelstate) | |
| 119 | 125 | /* Make the pthread functions weak so that we can elide them from |
| 120 | 126 | single-threaded processes. */ |
| 121 | 127 | #if !defined(__NO_WEAK_PTHREAD_ALIASES) && !IS_IN (libpthread) |
| 122 | 128 | # ifdef weak_extern |
| 123 | weak_extern (__pthread_mutex_init) | |
| 124 | weak_extern (__pthread_mutex_destroy) | |
| 125 | weak_extern (__pthread_mutex_lock) | |
| 126 | weak_extern (__pthread_mutex_trylock) | |
| 127 | weak_extern (__pthread_mutex_unlock) | |
| 128 | weak_extern (__pthread_mutexattr_settype) | |
| 129 | weak_extern (__pthread_rwlock_init) | |
| 130 | weak_extern (__pthread_rwlock_destroy) | |
| 131 | weak_extern (__pthread_rwlock_rdlock) | |
| 132 | weak_extern (__pthread_rwlock_tryrdlock) | |
| 133 | weak_extern (__pthread_rwlock_wrlock) | |
| 134 | weak_extern (__pthread_rwlock_trywrlock) | |
| 135 | weak_extern (__pthread_rwlock_unlock) | |
| 136 | weak_extern (__pthread_key_create) | |
| 137 | weak_extern (__pthread_setspecific) | |
| 138 | weak_extern (__pthread_getspecific) | |
| 139 | weak_extern (__pthread_once) | |
| 140 | 129 | weak_extern (__pthread_initialize) |
| 141 | 130 | weak_extern (__pthread_atfork) |
| 142 | weak_extern (__pthread_setcancelstate) | |
| 143 | 131 | # 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 | 132 | # pragma weak __pthread_initialize |
| 161 | 133 | # pragma weak __pthread_atfork |
| 162 | # pragma weak __pthread_setcancelstate | |
| 163 | 134 | # endif |
| 164 | 135 | #endif |
| 165 | 136 |
lib/libc/glibc/sysdeps/mach/sysdep.h+5| ... | ... | @@ -20,6 +20,11 @@ |
| 20 | 20 | /* Get the Mach definitions of ENTRY and kernel_trap. */ |
| 21 | 21 | #include <mach/machine/syscall_sw.h> |
| 22 | 22 | |
| 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 | 28 | /* The Mach definitions assume underscores should be prepended to |
| 24 | 29 | symbol names. Redefine them to do so only when appropriate. */ |
| 25 | 30 | #undef EXT |
lib/libc/glibc/sysdeps/nptl/pthread.h+5| ... | ... | @@ -1317,6 +1317,11 @@ extern int pthread_getcpuclockid (pthread_t __thread_id, |
| 1317 | 1317 | __THROW __nonnull ((2)); |
| 1318 | 1318 | #endif |
| 1319 | 1319 | |
| 1320 | #ifdef __USE_GNU | |
| 1321 | /* Return the Linux TID for THREAD_ID. Returns -1 on failure. */ | |
| 1322 | extern pid_t pthread_gettid_np (pthread_t __thread_id); | |
| 1323 | #endif | |
| 1324 | ||
| 1320 | 1325 | |
| 1321 | 1326 | /* Install handlers to be called when a new process is created with FORK. |
| 1322 | 1327 | 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 | 35 | |
| 36 | 36 | #include <sysdep.h> |
| 37 | 37 | |
| 38 | #define FRAME_SIZE 104 | |
| 38 | 39 | |
| 39 | 40 | 	.section ".text" |
| 40 | 41 | 	.align 4 |
| ... | ... | @@ -48,12 +49,12 @@ _start: |
| 48 | 49 | /* Terminate the stack frame, and reserve space for functions to |
| 49 | 50 | drop their arguments. */ |
| 50 | 51 | 	mov	%g0, %fp |
| 51 | 	sub	%sp, 6*4, %sp | |
| 52 | 	sub	%sp, FRAME_SIZE, %sp | |
| 52 | 53 | |
| 53 | 54 | /* Extract the arguments and environment as encoded on the stack. The |
| 54 | 55 | argument info starts after one register window (16 words) past the SP. */ |
| 55 | 	ld	[%sp+22*4], %o1 | |
| 56 | 	add	%sp, 23*4, %o2 | |
| 56 | 	ld	[%sp+168], %o1 | |
| 57 | 	add	%sp, 172, %o2 | |
| 57 | 58 | |
| 58 | 59 | /* Load the addresses of the user entry points. */ |
| 59 | 60 | #ifndef PIC |
| ... | ... | @@ -73,6 +74,10 @@ _start: |
| 73 | 74 | be NULL. */ |
| 74 | 75 | 	mov	%g1, %o5 |
| 75 | 76 | |
| 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 | 81 | /* Let libc do the rest of the initialization, and call main. */ |
| 77 | 82 | 	call	__libc_start_main |
| 78 | 83 | 	 nop |
lib/libc/glibc/sysdeps/sparc/sparc64/start.S+4| ... | ... | @@ -74,6 +74,10 @@ _start: |
| 74 | 74 | be NULL. */ |
| 75 | 75 | 	mov %g1, %o5 |
| 76 | 76 | |
| 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 | 81 | /* Let libc do the rest of the initialization, and call main. */ |
| 78 | 82 | 	call __libc_start_main |
| 79 | 83 | 	 nop |
lib/libc/glibc/sysdeps/unix/sysv/linux/aarch64/sysdep.h+2-7| ... | ... | @@ -152,13 +152,8 @@ |
| 152 | 152 | |
| 153 | 153 | #else /* not __ASSEMBLER__ */ |
| 154 | 154 | |
| 155 | # ifdef __LP64__ | |
| 156 | # define VDSO_NAME "LINUX_2.6.39" | |
| 157 | # define VDSO_HASH 123718537 | |
| 158 | # else | |
| 159 | # define VDSO_NAME "LINUX_4.9" | |
| 160 | # define VDSO_HASH 61765625 | |
| 161 | # endif | |
| 155 | # define VDSO_NAME "LINUX_2.6.39" | |
| 156 | # define VDSO_HASH 123718537 | |
| 162 | 157 | |
| 163 | 158 | /* List of system calls which are supported as vsyscalls. */ |
| 164 | 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 | 54 | configurations). */ |
| 55 | 55 | #define __ASSUME_SET_ROBUST_LIST	1 |
| 56 | 56 | |
| 57 | /* The termios2 interface was introduced across all architectures except | |
| 58 | Alpha in kernel 2.6.22. */ | |
| 59 | #define __ASSUME_TERMIOS2	1 | |
| 60 | ||
| 57 | 61 | /* Support for various CLOEXEC and NONBLOCK flags was added in |
| 58 | 62 | 2.6.27. */ |
| 59 | 63 | #define __ASSUME_IN_NONBLOCK	1 |
lib/libc/glibc/sysdeps/unix/sysv/linux/riscv/sysdep.h+2-1| ... | ... | @@ -145,11 +145,12 @@ |
| 145 | 145 | # define HAVE_CLOCK_GETRES64_VSYSCALL	"__vdso_clock_getres" |
| 146 | 146 | # define HAVE_CLOCK_GETTIME64_VSYSCALL	"__vdso_clock_gettime" |
| 147 | 147 | # define HAVE_GETTIMEOFDAY_VSYSCALL	"__vdso_gettimeofday" |
| 148 | # define HAVE_GETRANDOM_VSYSCALL	"__vdso_getrandom" | |
| 148 | 149 | # else |
| 149 | 150 | # define VDSO_NAME	"LINUX_5.4" |
| 150 | 151 | # define VDSO_HASH	61765876 |
| 151 | 152 | |
| 152 | /* RV32 does not support the gettime VDSO syscalls. */ | |
| 153 | /* RV32 does not support the gettime and getrandom VDSO syscalls. */ | |
| 153 | 154 | # endif |
| 154 | 155 | # define HAVE_CLONE3_WRAPPER		1 |
| 155 | 156 |
lib/libc/glibc/sysdeps/x86/sysdep.h+29| ... | ... | @@ -102,6 +102,9 @@ |
| 102 | 102 | | (1 << X86_XSTATE_ZMM_ID)		\ |
| 103 | 103 | | (1 << X86_XSTATE_APX_F_ID)) |
| 104 | 104 | |
| 105 | /* The maximum supported xstate ID. */ | |
| 106 | # define X86_XSTATE_MAX_ID	X86_XSTATE_APX_F_ID | |
| 107 | ||
| 105 | 108 | /* AMX state mask. */ |
| 106 | 109 | # define AMX_STATE_SAVE_MASK		\ |
| 107 | 110 | ((1 << X86_XSTATE_TILECFG_ID) | (1 << X86_XSTATE_TILEDATA_ID)) |
| ... | ... | @@ -123,6 +126,9 @@ |
| 123 | 126 | | (1 << X86_XSTATE_K_ID)		\ |
| 124 | 127 | | (1 << X86_XSTATE_ZMM_H_ID)) |
| 125 | 128 | |
| 129 | /* The maximum supported xstate ID. */ | |
| 130 | # define X86_XSTATE_MAX_ID	X86_XSTATE_ZMM_H_ID | |
| 131 | ||
| 126 | 132 | /* States to be included in xsave_state_size. */ |
| 127 | 133 | # define FULL_STATE_SAVE_MASK		STATE_SAVE_MASK |
| 128 | 134 | #endif |
| ... | ... | @@ -177,6 +183,29 @@ |
| 177 | 183 | |
| 178 | 184 | #define atom_text_section .section ".text.atom", "ax" |
| 179 | 185 | |
| 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 | 209 | #endif	/* __ASSEMBLER__ */ |
| 181 | 210 | |
| 182 | 211 | #endif	/* _X86_SYSDEP_H */ |
lib/libc/include/aarch64-linux-gnu/bits/fcntl.h+4-10| ... | ... | @@ -25,17 +25,11 @@ |
| 25 | 25 | #define __O_NOFOLLOW	0100000 |
| 26 | 26 | #define __O_DIRECT	0200000 |
| 27 | 27 | |
| 28 | #ifdef __ILP32__ | |
| 29 | # define __O_LARGEFILE	0400000 | |
| 30 | #else | |
| 31 | # define __O_LARGEFILE	0 | |
| 32 | #endif | |
| 28 | #define __O_LARGEFILE	0 | |
| 33 | 29 | |
| 34 | #ifdef __LP64__ | |
| 35 | # define F_GETLK64	5 | |
| 36 | # define F_SETLK64	6 | |
| 37 | # define F_SETLKW64	7 | |
| 38 | #endif | |
| 30 | #define F_GETLK64	5 | |
| 31 | #define F_SETLK64	6 | |
| 32 | #define F_SETLKW64	7 | |
| 39 | 33 | |
| 40 | 34 | struct flock |
| 41 | 35 | { |
lib/libc/include/aarch64-linux-gnu/bits/math-vector.h+32| ... | ... | @@ -37,6 +37,10 @@ |
| 37 | 37 | # define __DECL_SIMD_acosh __DECL_SIMD_aarch64 |
| 38 | 38 | # undef __DECL_SIMD_acoshf |
| 39 | 39 | # 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 | 44 | # undef __DECL_SIMD_asin |
| 41 | 45 | # define __DECL_SIMD_asin __DECL_SIMD_aarch64 |
| 42 | 46 | # undef __DECL_SIMD_asinf |
| ... | ... | @@ -45,6 +49,10 @@ |
| 45 | 49 | # define __DECL_SIMD_asinh __DECL_SIMD_aarch64 |
| 46 | 50 | # undef __DECL_SIMD_asinhf |
| 47 | 51 | # 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 | 56 | # undef __DECL_SIMD_atan |
| 49 | 57 | # define __DECL_SIMD_atan __DECL_SIMD_aarch64 |
| 50 | 58 | # undef __DECL_SIMD_atanf |
| ... | ... | @@ -53,10 +61,18 @@ |
| 53 | 61 | # define __DECL_SIMD_atanh __DECL_SIMD_aarch64 |
| 54 | 62 | # undef __DECL_SIMD_atanhf |
| 55 | 63 | # 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 | 68 | # undef __DECL_SIMD_atan2 |
| 57 | 69 | # define __DECL_SIMD_atan2 __DECL_SIMD_aarch64 |
| 58 | 70 | # undef __DECL_SIMD_atan2f |
| 59 | 71 | # 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 | 76 | # undef __DECL_SIMD_cbrt |
| 61 | 77 | # define __DECL_SIMD_cbrt __DECL_SIMD_aarch64 |
| 62 | 78 | # undef __DECL_SIMD_cbrtf |
| ... | ... | @@ -176,12 +192,16 @@ typedef __SVBool_t __sv_bool_t; |
| 176 | 192 | # define __vpcs __attribute__ ((__aarch64_vector_pcs__)) |
| 177 | 193 | |
| 178 | 194 | __vpcs __f32x4_t _ZGVnN4vv_atan2f (__f32x4_t, __f32x4_t); |
| 195 | __vpcs __f32x4_t _ZGVnN4vv_atan2pif (__f32x4_t, __f32x4_t); | |
| 179 | 196 | __vpcs __f32x4_t _ZGVnN4v_acosf (__f32x4_t); |
| 180 | 197 | __vpcs __f32x4_t _ZGVnN4v_acoshf (__f32x4_t); |
| 198 | __vpcs __f32x4_t _ZGVnN4v_acospif (__f32x4_t); | |
| 181 | 199 | __vpcs __f32x4_t _ZGVnN4v_asinf (__f32x4_t); |
| 182 | 200 | __vpcs __f32x4_t _ZGVnN4v_asinhf (__f32x4_t); |
| 201 | __vpcs __f32x4_t _ZGVnN4v_asinpif (__f32x4_t); | |
| 183 | 202 | __vpcs __f32x4_t _ZGVnN4v_atanf (__f32x4_t); |
| 184 | 203 | __vpcs __f32x4_t _ZGVnN4v_atanhf (__f32x4_t); |
| 204 | __vpcs __f32x4_t _ZGVnN4v_atanpif (__f32x4_t); | |
| 185 | 205 | __vpcs __f32x4_t _ZGVnN4v_cbrtf (__f32x4_t); |
| 186 | 206 | __vpcs __f32x4_t _ZGVnN4v_cosf (__f32x4_t); |
| 187 | 207 | __vpcs __f32x4_t _ZGVnN4v_coshf (__f32x4_t); |
| ... | ... | @@ -207,12 +227,16 @@ __vpcs __f32x4_t _ZGVnN4v_tanhf (__f32x4_t); |
| 207 | 227 | __vpcs __f32x4_t _ZGVnN4v_tanpif (__f32x4_t); |
| 208 | 228 | |
| 209 | 229 | __vpcs __f64x2_t _ZGVnN2vv_atan2 (__f64x2_t, __f64x2_t); |
| 230 | __vpcs __f64x2_t _ZGVnN2vv_atan2pi (__f64x2_t, __f64x2_t); | |
| 210 | 231 | __vpcs __f64x2_t _ZGVnN2v_acos (__f64x2_t); |
| 211 | 232 | __vpcs __f64x2_t _ZGVnN2v_acosh (__f64x2_t); |
| 233 | __vpcs __f64x2_t _ZGVnN2v_acospi (__f64x2_t); | |
| 212 | 234 | __vpcs __f64x2_t _ZGVnN2v_asin (__f64x2_t); |
| 213 | 235 | __vpcs __f64x2_t _ZGVnN2v_asinh (__f64x2_t); |
| 236 | __vpcs __f64x2_t _ZGVnN2v_asinpi (__f64x2_t); | |
| 214 | 237 | __vpcs __f64x2_t _ZGVnN2v_atan (__f64x2_t); |
| 215 | 238 | __vpcs __f64x2_t _ZGVnN2v_atanh (__f64x2_t); |
| 239 | __vpcs __f64x2_t _ZGVnN2v_atanpi (__f64x2_t); | |
| 216 | 240 | __vpcs __f64x2_t _ZGVnN2v_cbrt (__f64x2_t); |
| 217 | 241 | __vpcs __f64x2_t _ZGVnN2v_cos (__f64x2_t); |
| 218 | 242 | __vpcs __f64x2_t _ZGVnN2v_cosh (__f64x2_t); |
| ... | ... | @@ -243,12 +267,16 @@ __vpcs __f64x2_t _ZGVnN2v_tanpi (__f64x2_t); |
| 243 | 267 | #ifdef __SVE_VEC_MATH_SUPPORTED |
| 244 | 268 | |
| 245 | 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 | 271 | __sv_f32_t _ZGVsMxv_acosf (__sv_f32_t, __sv_bool_t); |
| 247 | 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 | 274 | __sv_f32_t _ZGVsMxv_asinf (__sv_f32_t, __sv_bool_t); |
| 249 | 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 | 277 | __sv_f32_t _ZGVsMxv_atanf (__sv_f32_t, __sv_bool_t); |
| 251 | 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 | 280 | __sv_f32_t _ZGVsMxv_cbrtf (__sv_f32_t, __sv_bool_t); |
| 253 | 281 | __sv_f32_t _ZGVsMxv_cosf (__sv_f32_t, __sv_bool_t); |
| 254 | 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 | 302 | __sv_f32_t _ZGVsMxv_tanpif (__sv_f32_t, __sv_bool_t); |
| 275 | 303 | |
| 276 | 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 | 306 | __sv_f64_t _ZGVsMxv_acos (__sv_f64_t, __sv_bool_t); |
| 278 | 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 | 309 | __sv_f64_t _ZGVsMxv_asin (__sv_f64_t, __sv_bool_t); |
| 280 | 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 | 312 | __sv_f64_t _ZGVsMxv_atan (__sv_f64_t, __sv_bool_t); |
| 282 | 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 | 315 | __sv_f64_t _ZGVsMxv_cbrt (__sv_f64_t, __sv_bool_t); |
| 284 | 316 | __sv_f64_t _ZGVsMxv_cos (__sv_f64_t, __sv_bool_t); |
| 285 | 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 | 21 | |
| 22 | 22 | #include <bits/endian.h> |
| 23 | 23 | |
| 24 | #ifdef __ILP32__ | |
| 25 | # define __SIZEOF_PTHREAD_ATTR_T 32 | |
| 26 | # define __SIZEOF_PTHREAD_MUTEX_T 32 | |
| 27 | # define __SIZEOF_PTHREAD_MUTEXATTR_T 4 | |
| 28 | # define __SIZEOF_PTHREAD_CONDATTR_T 4 | |
| 29 | # define __SIZEOF_PTHREAD_RWLOCK_T 48 | |
| 30 | # define __SIZEOF_PTHREAD_BARRIER_T 20 | |
| 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 | |
| 24 | #define __SIZEOF_PTHREAD_ATTR_T		64 | |
| 25 | #define __SIZEOF_PTHREAD_MUTEX_T	48 | |
| 26 | #define __SIZEOF_PTHREAD_MUTEXATTR_T	8 | |
| 27 | #define __SIZEOF_PTHREAD_CONDATTR_T	8 | |
| 28 | #define __SIZEOF_PTHREAD_RWLOCK_T	56 | |
| 29 | #define __SIZEOF_PTHREAD_BARRIER_T	32 | |
| 30 | #define __SIZEOF_PTHREAD_BARRIERATTR_T 8 | |
| 41 | 31 | #define __SIZEOF_PTHREAD_COND_T 48 |
| 42 | 32 | #define __SIZEOF_PTHREAD_RWLOCKATTR_T	8 |
| 43 | 33 |
lib/libc/include/aarch64-linux-gnu/bits/semaphore.h+1-7| ... | ... | @@ -20,13 +20,7 @@ |
| 20 | 20 | # error "Never use <bits/semaphore.h> directly; include <semaphore.h> instead." |
| 21 | 21 | #endif |
| 22 | 22 | |
| 23 | ||
| 24 | #ifdef __ILP32__ | |
| 25 | # define __SIZEOF_SEM_T	16 | |
| 26 | #else | |
| 27 | # define __SIZEOF_SEM_T	32 | |
| 28 | #endif | |
| 29 | ||
| 23 | #define __SIZEOF_SEM_T	32 | |
| 30 | 24 | |
| 31 | 25 | /* Value returned if `sem_open' failed. */ |
| 32 | 26 | #define SEM_FAILED ((sem_t *) 0) |
lib/libc/include/aarch64-linux-gnu/bits/wordsize.h+1-8| ... | ... | @@ -17,12 +17,5 @@ |
| 17 | 17 | License along with the GNU C Library; if not, see |
| 18 | 18 | <https://www.gnu.org/licenses/>. */ |
| 19 | 19 | |
| 20 | #ifdef __LP64__ | |
| 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 | ||
| 20 | #define __WORDSIZE			64 | |
| 28 | 21 | #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 | 101 | 			 char *__buf) __THROW; |
| 102 | 102 | #endif |
| 103 | 103 | |
| 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 | 109 | __END_DECLS |
| 105 | 110 | |
| 106 | 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 | 379 | 						identity and may not |
| 380 | 380 | 						be usable to |
| 381 | 381 | 						open_by_handle_at. */ |
| 382 | # define AT_HANDLE_MNT_ID_UNIQUE 1 /* Return the 64-bit unique mount | |
| 383 | 				 ID. */ | |
| 382 | 384 | #endif |
| 383 | 385 | |
| 384 | 386 | __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 | ||
| 26 | extern const char *__inet_ntop_chk (int, const void *, char *, socklen_t, size_t); | |
| 27 | ||
| 28 | extern const char *__REDIRECT_FORTIFY_NTH (__inet_ntop_alias, | |
| 29 | 					 (int, const void *, char *, socklen_t), inet_ntop); | |
| 30 | extern 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 | ||
| 35 | extern int __inet_pton_chk (int, const char *, void *, size_t); | |
| 36 | ||
| 37 | extern int __REDIRECT_FORTIFY_NTH (__inet_pton_alias, | |
| 38 | 				 (int, const char *, void *), inet_pton); | |
| 39 | extern 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 | 32 | unsigned short int ws_ypixel; |
| 33 | 33 | }; |
| 34 | 34 | |
| 35 | #define NCC 8 | |
| 36 | struct 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 | 35 | /* modem lines */ |
| 47 | 36 | #define TIOCM_LE	0x001 |
| 48 | 37 | #define TIOCM_DTR	0x002 |
lib/libc/include/generic-glibc/bits/ioctls.h+1-84| ... | ... | @@ -22,87 +22,4 @@ |
| 22 | 22 | /* Use the definitions from the kernel header files. */ |
| 23 | 23 | #include <asm/ioctls.h> |
| 24 | 24 | |
| 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 | 373 | #define __DECL_SIMD_tanpif32x |
| 374 | 374 | #define __DECL_SIMD_tanpif64x |
| 375 | 375 | #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 | 420 | #endif |
| \ No newline at end of file |
lib/libc/include/generic-glibc/bits/mathcalls-macros.h+1-1| ... | ... | @@ -34,7 +34,7 @@ |
| 34 | 34 | #define __MATHCALLX(function,suffix, args, attrib)	\ |
| 35 | 35 | __MATHDECLX (_Mdouble_,function,suffix, args, attrib) |
| 36 | 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 | 38 | #define __MATHDECL_1_IMPL(type, function, suffix, args) \ |
| 39 | 39 | extern type __MATH_PRECNAME(function,suffix) args __THROW |
| 40 | 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 | 68 | #if __GLIBC_USE (IEC_60559_FUNCS_EXT_C23) |
| 69 | 69 | /* Arc cosine of X, divided by pi. */ |
| 70 | 70 | __MATHCALL (acospi,, (_Mdouble_ __x)); |
| 71 | __MATHCALL_VEC (acospi,, (_Mdouble_ __x)); | |
| 71 | 72 | /* Arc sine of X, divided by pi. */ |
| 72 | 73 | __MATHCALL (asinpi,, (_Mdouble_ __x)); |
| 74 | __MATHCALL_VEC (asinpi,, (_Mdouble_ __x)); | |
| 73 | 75 | /* Arc tangent of X, divided by pi. */ |
| 74 | 76 | __MATHCALL (atanpi,, (_Mdouble_ __x)); |
| 77 | __MATHCALL_VEC (atanpi,, (_Mdouble_ __x)); | |
| 75 | 78 | /* Arc tangent of Y/X, divided by pi. */ |
| 76 | 79 | __MATHCALL (atan2pi,, (_Mdouble_ __y, _Mdouble_ __x)); |
| 80 | __MATHCALL_VEC (atan2pi,, (_Mdouble_ __y, _Mdouble_ __x)); | |
| 77 | 81 | |
| 78 | 82 | /* Cosine of pi * X. */ |
| 79 | 83 | __MATHCALL_VEC (cospi,, (_Mdouble_ __x)); |
| ... | ... | @@ -185,6 +189,23 @@ __MATHCALL_VEC (hypot,, (_Mdouble_ __x, _Mdouble_ __y)); |
| 185 | 189 | __MATHCALL_VEC (cbrt,, (_Mdouble_ __x)); |
| 186 | 190 | #endif |
| 187 | 191 | |
| 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 | ||
| 188 | 209 | |
| 189 | 210 | /* Nearest integer, absolute value, and remainder functions. */ |
| 190 | 211 |
lib/libc/include/generic-glibc/bits/mman-linux.h+2| ... | ... | @@ -113,6 +113,8 @@ |
| 113 | 113 | 				 locked pages too. */ |
| 114 | 114 | # define MADV_COLLAPSE 25	/* Synchronous hugepage collapse. */ |
| 115 | 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 | 118 | #endif |
| 117 | 119 | |
| 118 | 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 | 43 | # endif |
| 44 | 44 | |
| 45 | 45 | /* Access restrictions for pkey_alloc. */ |
| 46 | # ifndef PKEY_DISABLE_ACCESS | |
| 47 | # define PKEY_DISABLE_ACCESS 0x1 | |
| 48 | # define PKEY_DISABLE_WRITE 0x2 | |
| 49 | # endif | |
| 46 | # define PKEY_UNRESTRICTED 0x0 | |
| 47 | # define PKEY_DISABLE_ACCESS 0x1 | |
| 48 | # define PKEY_DISABLE_WRITE 0x2 | |
| 50 | 49 | |
| 51 | 50 | __BEGIN_DECLS |
| 52 | 51 |
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 | 152 | store it in *ATTR. */ |
| 153 | 153 | int sched_getattr (pid_t tid, struct sched_attr *attr, unsigned int size, |
| 154 | 154 | 		 unsigned int flags) |
| 155 | __THROW __nonnull ((2)) __attr_access ((__write_only__, 2, 3)); | |
| 155 | __THROW __nonnull ((2)); | |
| 156 | 156 | |
| 157 | 157 | #endif |
| 158 | 158 |
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 | 151 | } |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | * strlcpy and strlcat introduced in glibc 2.38 | |
| 154 | * zig patch: strlcpy and strlcat introduced in glibc 2.38 | |
| 155 | 155 | * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da |
| 156 | 156 | */ |
| 157 | 157 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2 |
lib/libc/include/generic-glibc/bits/syscall.h+22-2| ... | ... | @@ -1,11 +1,11 @@ |
| 1 | 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. */ | |
| 3 | 3 | |
| 4 | 4 | #ifndef _SYSCALL_H |
| 5 | 5 | # error "Never use <bits/syscall.h> directly; include <sys/syscall.h> instead." |
| 6 | 6 | #endif |
| 7 | 7 | |
| 8 | #define __GLIBC_LINUX_VERSION_CODE 396288 | |
| 8 | #define __GLIBC_LINUX_VERSION_CODE 397056 | |
| 9 | 9 | |
| 10 | 10 | #ifdef __NR_FAST_atomic_update |
| 11 | 11 | # define SYS_FAST_atomic_update __NR_FAST_atomic_update |
| ... | ... | @@ -703,6 +703,10 @@ |
| 703 | 703 | # define SYS_getxattr __NR_getxattr |
| 704 | 704 | #endif |
| 705 | 705 | |
| 706 | #ifdef __NR_getxattrat | |
| 707 | # define SYS_getxattrat __NR_getxattrat | |
| 708 | #endif | |
| 709 | ||
| 706 | 710 | #ifdef __NR_getxgid |
| 707 | 711 | # define SYS_getxgid __NR_getxgid |
| 708 | 712 | #endif |
| ... | ... | @@ -875,6 +879,10 @@ |
| 875 | 879 | # define SYS_listxattr __NR_listxattr |
| 876 | 880 | #endif |
| 877 | 881 | |
| 882 | #ifdef __NR_listxattrat | |
| 883 | # define SYS_listxattrat __NR_listxattrat | |
| 884 | #endif | |
| 885 | ||
| 878 | 886 | #ifdef __NR_llistxattr |
| 879 | 887 | # define SYS_llistxattr __NR_llistxattr |
| 880 | 888 | #endif |
| ... | ... | @@ -1167,6 +1175,10 @@ |
| 1167 | 1175 | # define SYS_open_tree __NR_open_tree |
| 1168 | 1176 | #endif |
| 1169 | 1177 | |
| 1178 | #ifdef __NR_open_tree_attr | |
| 1179 | # define SYS_open_tree_attr __NR_open_tree_attr | |
| 1180 | #endif | |
| 1181 | ||
| 1170 | 1182 | #ifdef __NR_openat |
| 1171 | 1183 | # define SYS_openat __NR_openat |
| 1172 | 1184 | #endif |
| ... | ... | @@ -1839,6 +1851,10 @@ |
| 1839 | 1851 | # define SYS_removexattr __NR_removexattr |
| 1840 | 1852 | #endif |
| 1841 | 1853 | |
| 1854 | #ifdef __NR_removexattrat | |
| 1855 | # define SYS_removexattrat __NR_removexattrat | |
| 1856 | #endif | |
| 1857 | ||
| 1842 | 1858 | #ifdef __NR_rename |
| 1843 | 1859 | # define SYS_rename __NR_rename |
| 1844 | 1860 | #endif |
| ... | ... | @@ -2199,6 +2215,10 @@ |
| 2199 | 2215 | # define SYS_setxattr __NR_setxattr |
| 2200 | 2216 | #endif |
| 2201 | 2217 | |
| 2218 | #ifdef __NR_setxattrat | |
| 2219 | # define SYS_setxattrat __NR_setxattrat | |
| 2220 | #endif | |
| 2221 | ||
| 2202 | 2222 | #ifdef __NR_sgetmask |
| 2203 | 2223 | # define SYS_sgetmask __NR_sgetmask |
| 2204 | 2224 | #endif |
lib/libc/include/generic-glibc/bits/termios-baud.h+52-25| ... | ... | @@ -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 | 2 | Copyright (C) 2019-2025 Free Software Foundation, Inc. |
| 3 | 3 | This file is part of the GNU C Library. |
| 4 | 4 | |
| ... | ... | @@ -20,29 +20,56 @@ |
| 20 | 20 | # error "Never include <bits/termios-baud.h> directly; use <termios.h> instead." |
| 21 | 21 | #endif |
| 22 | 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 (not used). */ | |
| 28 | # define CMSPAR 010000000000 /* Mark or space (stick) parity. */ | |
| 29 | # define CRTSCTS 020000000000 /* Flow control. */ | |
| 23 | /* POSIX required baud rates */ | |
| 24 | #define B0		 0U		/* Hang up or ispeed == ospeed */ | |
| 25 | #define B50		 50U | |
| 26 | #define B75		 75U | |
| 27 | #define B110		 110U | |
| 28 | #define B134		 134U		/* Really 134.5 baud by POSIX spec */ | |
| 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 | 43 | #endif |
| 31 | 44 | |
| 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 | 34 | #define CLOCAL	0004000 |
| 35 | 35 | |
| 36 | 36 | #ifdef __USE_MISC |
| 37 | # define ADDRB 04000000000 | |
| 37 | # define ADDRB 04000000000 | |
| 38 | # define CMSPAR 010000000000 /* Mark or space (stick) parity. */ | |
| 39 | # define CRTSCTS 020000000000 /* Flow control. */ | |
| 38 | 40 | #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 | 29 | tcflag_t c_lflag;		/* local mode flags */ |
| 30 | 30 | cc_t c_line;			/* line discipline */ |
| 31 | 31 | cc_t c_cc[NCCS];		/* control characters */ |
| 32 | speed_t c_ispeed;		/* input speed */ | |
| 33 | speed_t c_ospeed;		/* output speed */ | |
| 32 | /* Input and output baud rates. */ | |
| 33 | __extension__ union { | |
| 34 | speed_t __ispeed; | |
| 35 | speed_t c_ispeed; | |
| 36 | }; | |
| 34 | 37 | #define _HAVE_STRUCT_TERMIOS_C_ISPEED 1 |
| 38 | __extension__ union { | |
| 39 | speed_t __ospeed; | |
| 40 | speed_t c_ospeed; | |
| 41 | }; | |
| 35 | 42 | #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 | 24 | typedef unsigned int	speed_t; |
| 25 | 25 | typedef unsigned int	tcflag_t; |
| 26 | 26 | |
| 27 | #include <bits/termios-struct.h> | |
| 27 | #ifdef _TERMIOS_H | |
| 28 | # include <bits/termios-struct.h> | |
| 29 | #endif | |
| 30 | ||
| 28 | 31 | #include <bits/termios-c_cc.h> |
| 29 | 32 | #include <bits/termios-c_iflag.h> |
| 30 | 33 | #include <bits/termios-c_oflag.h> |
| 31 | 34 | |
| 32 | 35 | /* c_cflag bit meaning */ |
| 33 | #define B0	0000000		/* hang up */ | |
| 34 | #define B50	0000001 | |
| 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 | |
| 36 | #include <bits/termios-c_cflag.h> | |
| 37 | ||
| 49 | 38 | #ifdef __USE_MISC |
| 50 | # define EXTA B19200 | |
| 51 | # define EXTB B38400 | |
| 39 | #define __B0	 0000000	/* hang up */ | |
| 40 | #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 | 60 | #endif |
| 53 | #include <bits/termios-baud.h> | |
| 54 | 61 | |
| 55 | #include <bits/termios-c_cflag.h> | |
| 56 | 62 | #include <bits/termios-c_lflag.h> |
| 57 | 63 | |
| 58 | 64 | #ifdef __USE_MISC |
| ... | ... | @@ -73,4 +79,6 @@ typedef unsigned int	tcflag_t; |
| 73 | 79 | |
| 74 | 80 | #include <bits/termios-tcflow.h> |
| 75 | 81 | |
| 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 | 32 | #endif |
| 33 | 33 | |
| 34 | 34 | #include <bits/types.h> |
| 35 | #include <bits/wordsize.h> | |
| 35 | 36 | |
| 36 | 37 | struct _IO_FILE; |
| 37 | 38 | struct _IO_marker; |
| ... | ... | @@ -97,8 +98,15 @@ struct _IO_FILE_complete |
| 97 | 98 | void *_freeres_buf; |
| 98 | 99 | struct _IO_FILE **_prevchain; |
| 99 | 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 | 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 | }; |
| 103 | 111 | |
| 104 | 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 | 217 | int dlfo_eh_count;		/* Number of exception handling entries. */ |
| 218 | 218 | unsigned int __dlfo_eh_count_pad; |
| 219 | 219 | # 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 | }; |
| 222 | 226 | |
| 223 | 227 | /* If ADDRESS is found in an object, fill in *RESULT and return 0. |
| 224 | 228 | Otherwise, return -1. */ |
| 225 | 229 | int _dl_find_object (void *__address, struct dl_find_object *__result) __THROW; |
| 226 | 230 | |
| 227 | #endif /* __USE_GNU */ | |
| 231 | /* SFrame stack trace data is valid. */ | |
| 232 | #define DLFO_FLAG_SFRAME (1ULL << 0) | |
| 228 | 233 | |
| 234 | #endif /* __USE_GNU */ | |
| 229 | 235 | |
| 230 | 236 | __END_DECLS |
| 231 | 237 |
lib/libc/include/generic-glibc/elf.h+11-22| ... | ... | @@ -837,12 +837,15 @@ typedef struct |
| 837 | 837 | #define NT_ARM_ZT	0x40d		/* ARM SME ZT registers. */ |
| 838 | 838 | #define NT_ARM_FPMR	0x40e		/* ARM floating point mode register. */ |
| 839 | 839 | #define NT_ARM_POE	0x40f		/* ARM POE registers. */ |
| 840 | #define NT_ARM_GCS	0x410		/* ARM GCS state. */ | |
| 840 | 841 | #define NT_VMCOREDD	0x700		/* Vmcore Device Dump Note. */ |
| 841 | 842 | #define NT_MIPS_DSP	0x800		/* MIPS DSP ASE registers. */ |
| 842 | 843 | #define NT_MIPS_FP_MODE	0x801		/* MIPS floating-point mode. */ |
| 843 | 844 | #define NT_MIPS_MSA	0x802		/* MIPS SIMD registers. */ |
| 844 | 845 | #define NT_RISCV_CSR	0x900		/* RISC-V Control and Status Registers */ |
| 845 | 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 | 849 | #define NT_LOONGARCH_CPUCFG	0xa00	/* LoongArch CPU config registers. */ |
| 847 | 850 | #define NT_LOONGARCH_CSR	0xa01	/* LoongArch control and |
| 848 | 851 | 					 status registers. */ |
| ... | ... | @@ -2906,19 +2909,6 @@ enum |
| 2906 | 2909 | |
| 2907 | 2910 | #define R_AARCH64_NONE 0	/* No relocation. */ |
| 2908 | 2911 | |
| 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 | 2912 | #define R_AARCH64_ABS64 257	/* Direct 64 bit. */ |
| 2923 | 2913 | #define R_AARCH64_ABS32 258	/* Direct 32 bit. */ |
| 2924 | 2914 | #define R_AARCH64_ABS16		259	/* Direct 16-bit. */ |
| ... | ... | @@ -4091,6 +4081,7 @@ enum |
| 4091 | 4081 | #define R_RISCV_TLS_DTPREL64	 9 |
| 4092 | 4082 | #define R_RISCV_TLS_TPREL32	10 |
| 4093 | 4083 | #define R_RISCV_TLS_TPREL64	11 |
| 4084 | #define R_RISCV_TLSDESC		12 | |
| 4094 | 4085 | #define R_RISCV_BRANCH		16 |
| 4095 | 4086 | #define R_RISCV_JAL		17 |
| 4096 | 4087 | #define R_RISCV_CALL		18 |
| ... | ... | @@ -4116,16 +4107,10 @@ enum |
| 4116 | 4107 | #define R_RISCV_SUB16		38 |
| 4117 | 4108 | #define R_RISCV_SUB32		39 |
| 4118 | 4109 | #define R_RISCV_SUB64		40 |
| 4119 | #define R_RISCV_GNU_VTINHERIT	41 | |
| 4120 | #define R_RISCV_GNU_VTENTRY	42 | |
| 4110 | #define R_RISCV_GOT32_PCREL	41 | |
| 4121 | 4111 | #define R_RISCV_ALIGN		43 |
| 4122 | 4112 | #define R_RISCV_RVC_BRANCH	44 |
| 4123 | 4113 | #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 | 4114 | #define R_RISCV_RELAX		51 |
| 4130 | 4115 | #define R_RISCV_SUB6		52 |
| 4131 | 4116 | #define R_RISCV_SET6		53 |
| ... | ... | @@ -4137,8 +4122,12 @@ enum |
| 4137 | 4122 | #define R_RISCV_PLT32		59 |
| 4138 | 4123 | #define R_RISCV_SET_ULEB128	60 |
| 4139 | 4124 | #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 | |
| 4140 | 4129 | |
| 4141 | #define R_RISCV_NUM		62 | |
| 4130 | #define R_RISCV_NUM		66 | |
| 4142 | 4131 | |
| 4143 | 4132 | /* RISC-V specific values for the st_other field. */ |
| 4144 | 4133 | #define STO_RISCV_VARIANT_CC	0x80	/* Function uses variant calling |
| ... | ... | @@ -4147,7 +4136,7 @@ enum |
| 4147 | 4136 | /* RISC-V specific values for the sh_type field. */ |
| 4148 | 4137 | #define SHT_RISCV_ATTRIBUTES	(SHT_LOPROC + 3) |
| 4149 | 4138 | |
| 4150 | /* RISC-V specific values for the p_type field. */ | |
| 4139 | /* RISC-V specific values for the p_type field (deprecated). */ | |
| 4151 | 4140 | #define PT_RISCV_ATTRIBUTES	(PT_LOPROC + 3) |
| 4152 | 4141 | |
| 4153 | 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 | 168 | #endif |
| 169 | 169 | |
| 170 | 170 | |
| 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 | 172 | * glibc 2.28 onwards converted it to a macro when compiled with |
| 173 | 173 | * USE_LARGEFILE64. */ |
| 174 | 174 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 28) || __GLIBC__ > 2 |
| ... | ... | @@ -289,16 +289,17 @@ extern int creat64 (const char *__file, mode_t __mode) __nonnull ((1)); |
| 289 | 289 | # define F_TEST 3	/* Test a region for other processes locks. */ |
| 290 | 290 | |
| 291 | 291 | # ifndef __USE_FILE_OFFSET64 |
| 292 | extern int lockf (int __fd, int __cmd, off_t __len); | |
| 292 | extern int lockf (int __fd, int __cmd, off_t __len) __wur; | |
| 293 | 293 | # else |
| 294 | 294 | # ifdef __REDIRECT |
| 295 | extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len), lockf64); | |
| 295 | extern int __REDIRECT (lockf, (int __fd, int __cmd, __off64_t __len), | |
| 296 | lockf64) __wur; | |
| 296 | 297 | # else |
| 297 | 298 | # define lockf lockf64 |
| 298 | 299 | # endif |
| 299 | 300 | # endif |
| 300 | 301 | # ifdef __USE_LARGEFILE64 |
| 301 | extern int lockf64 (int __fd, int __cmd, off64_t __len); | |
| 302 | extern int lockf64 (int __fd, int __cmd, off64_t __len) __wur; | |
| 302 | 303 | # endif |
| 303 | 304 | #endif |
| 304 | 305 | |
| ... | ... | @@ -351,4 +352,4 @@ extern int posix_fallocate64 (int __fd, off64_t __offset, off64_t __len); |
| 351 | 352 | |
| 352 | 353 | __END_DECLS |
| 353 | 354 | |
| 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 | 491 | or without -D_GNU_SOURCE, but -std=c89 -D_GNU_SOURCE will have the |
| 492 | 492 | old extension. */ |
| 493 | 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 | 495 | # define __GLIBC_USE_DEPRECATED_SCANF 1 |
| 496 | 496 | #elif (defined __USE_GNU							\ |
| 497 | 497 | && (defined __cplusplus						\ |
| ... | ... | @@ -503,7 +503,7 @@ |
| 503 | 503 | #endif |
| 504 | 504 | |
| 505 | 505 | |
| 506 | /* support for ISO C2X strtol was added in 2.38 | |
| 506 | /* zig patch: support for ISO C2X strtol was added in 2.38 | |
| 507 | 507 | * glibc commit 64924422a99690d147a166b4de3103f3bf3eaf6c |
| 508 | 508 | */ |
| 509 | 509 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2 |
| ... | ... | @@ -564,4 +564,4 @@ |
| 564 | 564 | #include <gnu/stubs.h> |
| 565 | 565 | |
| 566 | 566 | |
| 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 | 195 | |
| 196 | 196 | This function is not part of the interface specified by POSIX.2 |
| 197 | 197 | but several programs want to use it. */ |
| 198 | extern int glob_pattern_p (const char *__pattern, int __quote) __THROW; | |
| 198 | extern int glob_pattern_p (const char *__pattern, int __quote) __THROW | |
| 199 | 	__nonnull ((1)); | |
| 199 | 200 | #endif |
| 200 | 201 | |
| 201 | 202 | __END_DECLS |
lib/libc/include/generic-glibc/inttypes.h+5| ... | ... | @@ -350,6 +350,11 @@ typedef struct |
| 350 | 350 | /* Compute absolute value of N. */ |
| 351 | 351 | extern intmax_t imaxabs (intmax_t __n) __THROW __attribute__ ((__const__)); |
| 352 | 352 | |
| 353 | ||
| 354 | #if __GLIBC_USE (ISOC2Y) | |
| 355 | extern uintmax_t uimaxabs (intmax_t __n) __THROW __attribute__ ((__const__)); | |
| 356 | #endif | |
| 357 | ||
| 353 | 358 | /* Return the `imaxdiv_t' representation of the value of NUMER over DENOM. */ |
| 354 | 359 | extern imaxdiv_t imaxdiv (intmax_t __numer, intmax_t __denom) |
| 355 | 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 | 52 | __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2)); |
| 53 | 53 | |
| 54 | 54 | /* |
| 55 | * reallocarray introduced in glibc 2.26 | |
| 55 | * zig patch: reallocarray introduced in glibc 2.26 | |
| 56 | 56 | * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da |
| 57 | 57 | */ |
| 58 | 58 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26) || __GLIBC__ > 2 |
| ... | ... | @@ -164,4 +164,4 @@ extern void malloc_stats (void) __THROW; |
| 164 | 164 | extern int malloc_info (int __options, FILE *__fp) __THROW; |
| 165 | 165 | |
| 166 | 166 | __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 | 212 | # define TCPI_OPT_ECN		8 /* ECN was negotiated at TCP session init */ |
| 213 | 213 | # define TCPI_OPT_ECN_SEEN	16 /* we received at least one packet with ECT */ |
| 214 | 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 | ||
| 215 | 218 | |
| 216 | 219 | /* Values for tcpi_state. */ |
| 217 | 220 | enum tcp_ca_state |
lib/libc/include/generic-glibc/pthread.h+5| ... | ... | @@ -1317,6 +1317,11 @@ extern int pthread_getcpuclockid (pthread_t __thread_id, |
| 1317 | 1317 | __THROW __nonnull ((2)); |
| 1318 | 1318 | #endif |
| 1319 | 1319 | |
| 1320 | #ifdef __USE_GNU | |
| 1321 | /* Return the Linux TID for THREAD_ID. Returns -1 on failure. */ | |
| 1322 | extern pid_t pthread_gettid_np (pthread_t __thread_id); | |
| 1323 | #endif | |
| 1324 | ||
| 1320 | 1325 | |
| 1321 | 1326 | /* Install handlers to be called when a new process is created with FORK. |
| 1322 | 1327 | 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 | 171 | #define res_init		__res_init |
| 172 | 172 | #define res_isourserver		__res_isourserver |
| 173 | 173 | |
| 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 | 175 | * res_querydomain, res_nquerydomain, dn_skipname, dn_comp, dn_expand were |
| 176 | 176 | * #define'd to __res_search, __res_nsearch, etc. glibc 2.34 onwards removes |
| 177 | 177 | * the macros and exposes the symbols directly. New glibc exposes compat |
| ... | ... | @@ -336,4 +336,4 @@ void		res_nclose (res_state) __THROW; |
| 336 | 336 | |
| 337 | 337 | __END_DECLS |
| 338 | 338 | |
| 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 | 168 | #ifdef __USE_GNU |
| 169 | 169 | /* Flags for renameat2. */ |
| 170 | 170 | # define RENAME_NOREPLACE (1 << 0) |
| 171 | # define AT_RENAME_NOREPLACE RENAME_NOREPLACE | |
| 171 | 172 | # define RENAME_EXCHANGE (1 << 1) |
| 173 | # define AT_RENAME_EXCHANGE RENAME_EXCHANGE | |
| 172 | 174 | # define RENAME_WHITEOUT (1 << 2) |
| 175 | # define AT_RENAME_WHITEOUT RENAME_WHITEOUT | |
| 173 | 176 | |
| 174 | 177 | /* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with |
| 175 | 178 | additional flags. */ |
| ... | ... | @@ -604,9 +607,6 @@ extern int fgetc_unlocked (FILE *__stream) __nonnull ((1)); |
| 604 | 607 | /* Write a character to STREAM. |
| 605 | 608 | |
| 606 | 609 | 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 | 610 | marked with __THROW. */ |
| 611 | 611 | extern int fputc (int __c, FILE *__stream) __nonnull ((2)); |
| 612 | 612 | extern 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 | |
| 44 | 44 | /* Return the size of the buffer of FP in bytes currently in use by |
| 45 | 45 | the given stream. */ |
| 46 | extern size_t __fbufsize (FILE *__fp) __THROW; | |
| 46 | extern size_t __fbufsize (FILE *__fp) __THROW __nonnull ((1)); | |
| 47 | 47 | |
| 48 | 48 | |
| 49 | 49 | /* Return non-zero value iff the stream FP is opened readonly, or if the |
| 50 | 50 | last operation on the stream was a read operation. */ |
| 51 | extern int __freading (FILE *__fp) __THROW; | |
| 51 | extern int __freading (FILE *__fp) __THROW __nonnull ((1)); | |
| 52 | 52 | |
| 53 | 53 | /* Return non-zero value iff the stream FP is opened write-only or |
| 54 | 54 | append-only, or if the last operation on the stream was a write |
| 55 | 55 | operation. */ |
| 56 | extern int __fwriting (FILE *__fp) __THROW; | |
| 56 | extern int __fwriting (FILE *__fp) __THROW __nonnull ((1)); | |
| 57 | 57 | |
| 58 | 58 | |
| 59 | 59 | /* Return non-zero value iff stream FP is not opened write-only or |
| 60 | 60 | append-only. */ |
| 61 | extern int __freadable (FILE *__fp) __THROW; | |
| 61 | extern int __freadable (FILE *__fp) __THROW __nonnull ((1)); | |
| 62 | 62 | |
| 63 | 63 | /* Return non-zero value iff stream FP is not opened read-only. */ |
| 64 | extern int __fwritable (FILE *__fp) __THROW; | |
| 64 | extern int __fwritable (FILE *__fp) __THROW __nonnull ((1)); | |
| 65 | 65 | |
| 66 | 66 | |
| 67 | 67 | /* Return non-zero value iff the stream FP is line-buffered. */ |
| 68 | extern int __flbf (FILE *__fp) __THROW; | |
| 68 | extern int __flbf (FILE *__fp) __THROW __nonnull ((1)); | |
| 69 | 69 | |
| 70 | 70 | |
| 71 | 71 | /* Discard all pending buffered I/O on the stream FP. */ |
| 72 | extern void __fpurge (FILE *__fp) __THROW; | |
| 72 | extern void __fpurge (FILE *__fp) __THROW __nonnull ((1)); | |
| 73 | 73 | |
| 74 | 74 | /* Return amount of output in bytes pending on a stream FP. */ |
| 75 | extern size_t __fpending (FILE *__fp) __THROW; | |
| 75 | extern size_t __fpending (FILE *__fp) __THROW __nonnull ((1)); | |
| 76 | 76 | |
| 77 | 77 | /* Flush all line-buffered files. */ |
| 78 | 78 | extern void _flushlbf (void); |
| 79 | 79 | |
| 80 | 80 | |
| 81 | 81 | /* Set locking status of stream FP to TYPE. */ |
| 82 | extern int __fsetlocking (FILE *__fp, int __type) __THROW; | |
| 82 | extern int __fsetlocking (FILE *__fp, int __type) __THROW __nonnull ((1)); | |
| 83 | 83 | |
| 84 | 84 | __END_DECLS |
| 85 | 85 |
lib/libc/include/generic-glibc/stdlib.h+9-3| ... | ... | @@ -654,7 +654,7 @@ extern int lcong48_r (unsigned short int __param[7], |
| 654 | 654 | __THROW __nonnull ((1, 2)); |
| 655 | 655 | |
| 656 | 656 | /* |
| 657 | * arc4random* symbols introduced in glibc 2.36: | |
| 657 | * zig patch: arc4random* symbols introduced in glibc 2.36: | |
| 658 | 658 | * https://sourceware.org/git/?p=glibc.git;a=blob;f=NEWS;h=8420a65cd06874ee09518366b8fba746a557212a;hb=6f4e0fcfa2d2b0915816a3a3a1d48b4763a7dee2 |
| 659 | 659 | */ |
| 660 | 660 | # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 36) || __GLIBC__ > 2 |
| ... | ... | @@ -693,7 +693,7 @@ extern void *realloc (void *__ptr, size_t __size) |
| 693 | 693 | extern void free (void *__ptr) __THROW; |
| 694 | 694 | |
| 695 | 695 | /* |
| 696 | * reallocarray introduced in glibc 2.26 | |
| 696 | * zig patch: reallocarray introduced in glibc 2.26 | |
| 697 | 697 | * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da |
| 698 | 698 | */ |
| 699 | 699 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26) || __GLIBC__ > 2 |
| ... | ... | @@ -997,6 +997,12 @@ __extension__ extern long long int llabs (long long int __x) |
| 997 | 997 | __THROW __attribute__ ((__const__)) __wur; |
| 998 | 998 | #endif |
| 999 | 999 | |
| 1000 | #if __GLIBC_USE (ISOC2Y) | |
| 1001 | extern unsigned int uabs (int __x) __THROW __attribute__ ((__const__)) __wur; | |
| 1002 | extern 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 | |
| 1000 | 1006 | |
| 1001 | 1007 | /* Return the `div_t', `ldiv_t' or `lldiv_t' representation |
| 1002 | 1008 | of the value of NUMER over DENOM. */ |
| ... | ... | @@ -1178,4 +1184,4 @@ extern int ttyslot (void) __THROW; |
| 1178 | 1184 | |
| 1179 | 1185 | __END_DECLS |
| 1180 | 1186 | |
| 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 | 502 | #endif |
| 503 | 503 | |
| 504 | 504 | /* |
| 505 | * strlcpy and strlcat introduced in glibc 2.38 | |
| 505 | * zig patch: strlcpy and strlcat introduced in glibc 2.38 | |
| 506 | 506 | * https://sourceware.org/git/?p=glibc.git;a=commit;h=2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da |
| 507 | 507 | */ |
| 508 | 508 | #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2 |
| ... | ... | @@ -557,4 +557,4 @@ extern char *basename (const char *__filename) __THROW __nonnull ((1)); |
| 557 | 557 | |
| 558 | 558 | __END_DECLS |
| 559 | 559 | |
| 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 | 21 | #define _SYS_HWPROBE_H 1 |
| 22 | 22 | |
| 23 | 23 | #include <features.h> |
| 24 | #include <sched.h> | |
| 24 | 25 | #include <stddef.h> |
| 25 | 26 | #include <errno.h> |
| 26 | 27 | #ifdef __has_include |
| ... | ... | @@ -63,22 +64,39 @@ struct riscv_hwprobe { |
| 63 | 64 | |
| 64 | 65 | __BEGIN_DECLS |
| 65 | 66 | |
| 66 | extern int __riscv_hwprobe (struct riscv_hwprobe *__pairs, size_t __pair_count, | |
| 67 | 			 size_t __cpu_count, unsigned long int *__cpus, | |
| 67 | #if defined __cplusplus || !__GNUC_PREREQ (2, 7) | |
| 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 *. */ | |
| 79 | typedef 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 | ||
| 86 | extern int __riscv_hwprobe (struct riscv_hwprobe *__pairs, | |
| 87 | 			 size_t __pair_count, size_t __cpusetsize, | |
| 88 | 			 __RISCV_HWPROBE_CPUS_TYPE __cpus, | |
| 68 | 89 | 			 unsigned int __flags) |
| 69 | __nonnull ((1)) __wur | |
| 70 | __fortified_attr_access (__read_write__, 1, 2) | |
| 71 | __fortified_attr_access (__read_only__, 4, 3); | |
| 90 | __THROW __nonnull ((1)) __attr_access ((__read_write__, 1, 2)); | |
| 72 | 91 | |
| 73 | /* A pointer to the __riscv_hwprobe vDSO function is passed as the second | |
| 92 | /* A pointer to the __riscv_hwprobe function is passed as the second | |
| 74 | 93 | argument to ifunc selector routines. Include a function pointer type for |
| 75 | 94 | convenience in calling the function in those settings. */ |
| 76 | typedef int (*__riscv_hwprobe_t) (struct riscv_hwprobe *__pairs, size_t __pair_count, | |
| 77 | 				 size_t __cpu_count, unsigned long int *__cpus, | |
| 95 | typedef int (*__riscv_hwprobe_t) (struct riscv_hwprobe *__pairs, | |
| 96 | 				 size_t __pair_count, size_t __cpusetsize, | |
| 97 | 				 __RISCV_HWPROBE_CPUS_TYPE __cpus, | |
| 78 | 98 | 				 unsigned int __flags) |
| 79 | __nonnull ((1)) __wur | |
| 80 | __fortified_attr_access (__read_write__, 1, 2) | |
| 81 | __fortified_attr_access (__read_only__, 4, 3); | |
| 99 | __nonnull ((1)) __attr_access ((__read_write__, 1, 2)); | |
| 82 | 100 | |
| 83 | 101 | /* Helper function usable from ifunc selectors that probes a single key. */ |
| 84 | 102 | static __inline int |
lib/libc/include/generic-glibc/sys/ifunc.h+59-6| ... | ... | @@ -19,24 +19,77 @@ |
| 19 | 19 | #ifndef _SYS_IFUNC_H |
| 20 | 20 | #define _SYS_IFUNC_H |
| 21 | 21 | |
| 22 | #include <sys/cdefs.h> | |
| 23 | ||
| 22 | 24 | /* A second argument is passed to the ifunc resolver. */ |
| 23 | 25 | #define _IFUNC_ARG_HWCAP	(1ULL << 62) |
| 24 | 26 | |
| 25 | /* The prototype of a gnu indirect function resolver on AArch64 is | |
| 27 | /* 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: | |
| 26 | 35 | |
| 27 | 36 | ElfW(Addr) ifunc_resolver (uint64_t, const __ifunc_arg_t *); |
| 28 | 37 | |
| 29 | the first argument should have the _IFUNC_ARG_HWCAP bit set and | |
| 30 | the remaining bits should match the AT_HWCAP settings. */ | |
| 38 | The first argument might have the _IFUNC_ARG_HWCAP bit set and | |
| 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. */ | |
| 31 | 54 | |
| 32 | /* Second argument to an ifunc resolver. */ | |
| 33 | 55 | struct __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 | 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 | }; |
| 39 | 63 | |
| 40 | 64 | typedef struct __ifunc_arg_t __ifunc_arg_t; |
| 41 | 65 | |
| 66 | /* Constants for IDs of HWCAP elements to be used with the | |
| 67 | __ifunc_hwcap function below. */ | |
| 68 | enum | |
| 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. */ | |
| 82 | static __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 | 95 | #endif |
| \ No newline at end of file |
lib/libc/include/generic-glibc/sys/mount.h+1-1| ... | ... | @@ -121,7 +121,7 @@ enum |
| 121 | 121 | MS_ACTIVE = 1 << 30, |
| 122 | 122 | #define MS_ACTIVE	MS_ACTIVE |
| 123 | 123 | #undef MS_NOUSER |
| 124 | MS_NOUSER = 1 << 31 | |
| 124 | MS_NOUSER = 1U << 31 | |
| 125 | 125 | #define MS_NOUSER	MS_NOUSER |
| 126 | 126 | }; |
| 127 | 127 |
lib/libc/include/generic-glibc/sys/ttychars.h-4| ... | ... | @@ -54,8 +54,4 @@ struct ttychars { |
| 54 | 54 | 	char	tc_lnextc;	/* literal next character */ |
| 55 | 55 | }; |
| 56 | 56 | |
| 57 | #ifdef __USE_OLD_TTY | |
| 58 | #include <sys/ttydefaults.h>	/* to pick up character defaults */ | |
| 59 | #endif | |
| 60 | ||
| 61 | 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 | 61 | extern int cfsetspeed (struct termios *__termios_p, speed_t __speed) __THROW; |
| 62 | 62 | #endif |
| 63 | 63 | |
| 64 | #ifdef __USE_GNU | |
| 65 | /* Interfaces that are explicitly numeric representations of baud rates */ | |
| 66 | typedef speed_t baud_t; | |
| 67 | #define BAUD_MAX SPEED_MAX | |
| 68 | ||
| 69 | /* Return the output baud rate stored in *TERMIOS_P. */ | |
| 70 | extern baud_t cfgetobaud (const struct termios *__termios_p) __THROW; | |
| 71 | ||
| 72 | /* Return the input baud rate stored in *TERMIOS_P. */ | |
| 73 | extern baud_t cfgetibaud (const struct termios *__termios_p) __THROW; | |
| 74 | ||
| 75 | /* Set the output baud rate stored in *TERMIOS_P to BAUD. */ | |
| 76 | extern int cfsetobaud (struct termios *__termios_p, baud_t __baud) __THROW; | |
| 77 | ||
| 78 | /* Set the input baud rate stored in *TERMIOS_P to BAUD. */ | |
| 79 | extern 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. */ | |
| 82 | extern int cfsetbaud (struct termios *__termios_p, baud_t __baud) __THROW; | |
| 83 | #endif | |
| 64 | 84 | |
| 65 | 85 | /* Put the state of FD into *TERMIOS_P. */ |
| 66 | 86 | extern int tcgetattr (int __fd, struct termios *__termios_p) __THROW; |
lib/libc/include/generic-glibc/tgmath.h+18| ... | ... | @@ -923,6 +923,24 @@ |
| 923 | 923 | /* Return the cube root of X. */ |
| 924 | 924 | #define cbrt(Val) __TGMATH_UNARY_REAL_ONLY (Val, cbrt) |
| 925 | 925 | |
| 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 | ||
| 926 | 944 | |
| 927 | 945 | /* Nearest integer, absolute value, and remainder functions. */ |
| 928 | 946 |
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 | 1231 | |
| 1232 | 1232 | __END_DECLS |
| 1233 | 1233 | |
| 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 | 31 | unsigned short int ws_ypixel; |
| 32 | 32 | }; |
| 33 | 33 | |
| 34 | #define NCC	8 | |
| 35 | struct 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 | 34 | /* modem lines */ |
| 47 | 35 | #define TIOCM_LE	0x001		/* line enable */ |
| 48 | 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 | |
| 24 | struct 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 | 32 | unsigned short int ws_ypixel; |
| 33 | 33 | }; |
| 34 | 34 | |
| 35 | #define NCC 10 | |
| 36 | struct 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 | 35 | /* modem lines */ |
| 47 | 36 | #define TIOCM_LE	0x001 |
| 48 | 37 | #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 | 35 | #define CLOCAL	00100000 |
| 36 | 36 | |
| 37 | 37 | #ifdef __USE_MISC |
| 38 | # define ADDRB 04000000000 | |
| 38 | # define ADDRB 04000000000 | |
| 39 | # define CMSPAR 010000000000 /* Mark or space (stick) parity. */ | |
| 40 | # define CRTSCTS 020000000000 /* Flow control. */ | |
| 39 | 41 | #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 | |
| 24 | struct 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 | 25 | floating-point type with the IEEE 754 binary128 format, and this |
| 26 | 26 | glibc includes corresponding *f128 interfaces for it. The required |
| 27 | 27 | 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 | 31 | #if (defined __x86_64__							\ |
| 30 | 32 | ? __GNUC_PREREQ (4, 3)						\ |
| 31 | 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 | 37 | # define __HAVE_FLOAT128 1 |
| 34 | 38 | #else |
| 35 | 39 | # define __HAVE_FLOAT128 0 |
| ... | ... | @@ -89,7 +93,7 @@ typedef _Complex float __cfloat128 __attribute__ ((__mode__ (__TC__))); |
| 89 | 93 | /* The type _Float128 exists only since GCC 7.0. */ |
| 90 | 94 | # if !__GNUC_PREREQ (7, 0) \ |
| 91 | 95 | || (defined __cplusplus && !__GNUC_PREREQ (13, 0)) \ |
| 92 | || __glibc_clang_prereq (3, 4) | |
| 96 | || __glibc_clang_prereq (3, 9) | |
| 93 | 97 | typedef __float128 _Float128; |
| 94 | 98 | # endif |
| 95 | 99 |
lib/std/Build.zig+3| ... | ... | @@ -22,6 +22,8 @@ pub const Step = @import("Build/Step.zig"); |
| 22 | 22 | pub const Module = @import("Build/Module.zig"); |
| 23 | 23 | pub const Watch = @import("Build/Watch.zig"); |
| 24 | 24 | pub const Fuzz = @import("Build/Fuzz.zig"); |
| 25 | pub const WebServer = @import("Build/WebServer.zig"); | |
| 26 | pub const abi = @import("Build/abi.zig"); | |
| 25 | 27 | |
| 26 | 28 | /// Shared state among all Build instances. |
| 27 | 29 | graph: *Graph, |
| ... | ... | @@ -125,6 +127,7 @@ pub const Graph = struct { |
| 125 | 127 | random_seed: u32 = 0, |
| 126 | 128 | dependency_cache: InitializedDepMap = .empty, |
| 127 | 129 | allow_so_scripts: ?bool = null, |
| 130 | time_report: bool, | |
| 128 | 131 | }; |
| 129 | 132 | |
| 130 | 133 | const AvailableDeps = []const struct { []const u8, []const u8 }; |
lib/std/Build/Fuzz.zig+370-81| ... | ... | @@ -1,108 +1,134 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | 1 | const std = @import("../std.zig"); |
| 3 | 2 | const Build = std.Build; |
| 3 | const Cache = Build.Cache; | |
| 4 | 4 | const Step = std.Build.Step; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const fatal = std.process.fatal; |
| 7 | 7 | const Allocator = std.mem.Allocator; |
| 8 | 8 | const log = std.log; |
| 9 | const Coverage = std.debug.Coverage; | |
| 10 | const abi = Build.abi.fuzz; | |
| 9 | 11 | |
| 10 | 12 | const Fuzz = @This(); |
| 11 | 13 | const build_runner = @import("root"); |
| 12 | 14 | |
| 13 | pub const WebServer = @import("Fuzz/WebServer.zig"); | |
| 14 | pub const abi = @import("Fuzz/abi.zig"); | |
| 15 | ||
| 16 | pub fn start( | |
| 17 | gpa: Allocator, | |
| 18 | arena: Allocator, | |
| 19 | global_cache_directory: Build.Cache.Directory, | |
| 20 | zig_lib_directory: Build.Cache.Directory, | |
| 21 | zig_exe_path: []const u8, | |
| 22 | thread_pool: *std.Thread.Pool, | |
| 23 | all_steps: []const *Step, | |
| 24 | ttyconf: std.io.tty.Config, | |
| 25 | listen_address: std.net.Address, | |
| 26 | prog_node: std.Progress.Node, | |
| 27 | ) Allocator.Error!void { | |
| 28 | const fuzz_run_steps = block: { | |
| 29 | const rebuild_node = prog_node.start("Rebuilding Unit Tests", 0); | |
| 15 | ws: *Build.WebServer, | |
| 16 | ||
| 17 | /// Allocated into `ws.gpa`. | |
| 18 | run_steps: []const *Step.Run, | |
| 19 | ||
| 20 | wait_group: std.Thread.WaitGroup, | |
| 21 | prog_node: std.Progress.Node, | |
| 22 | ||
| 23 | /// Protects `coverage_files`. | |
| 24 | coverage_mutex: std.Thread.Mutex, | |
| 25 | coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap), | |
| 26 | ||
| 27 | queue_mutex: std.Thread.Mutex, | |
| 28 | queue_cond: std.Thread.Condition, | |
| 29 | msg_queue: std.ArrayListUnmanaged(Msg), | |
| 30 | ||
| 31 | const 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 | ||
| 42 | const 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 | ||
| 57 | pub 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 | 64 | defer rebuild_node.end(); |
| 31 | var wait_group: std.Thread.WaitGroup = .{}; | |
| 32 | defer wait_group.wait(); | |
| 33 | var fuzz_run_steps: std.ArrayListUnmanaged(*Step.Run) = .empty; | |
| 34 | defer fuzz_run_steps.deinit(gpa); | |
| 35 | for (all_steps) |step| { | |
| 65 | var rebuild_wg: std.Thread.WaitGroup = .{}; | |
| 66 | defer rebuild_wg.wait(); | |
| 67 | ||
| 68 | for (ws.all_steps) |step| { | |
| 36 | 69 | const run = step.cast(Step.Run) orelse continue; |
| 37 | if (run.fuzz_tests.items.len > 0 and run.producer != null) { | |
| 38 | thread_pool.spawnWg(&wait_group, rebuildTestsWorkerRun, .{ run, ttyconf, rebuild_node }); | |
| 39 | try fuzz_run_steps.append(gpa, run); | |
| 40 | } | |
| 70 | if (run.producer == null) continue; | |
| 71 | if (run.fuzz_tests.items.len == 0) continue; | |
| 72 | try steps.append(gpa, run); | |
| 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", .{}); | |
| 43 | rebuild_node.setEstimatedTotalItems(fuzz_run_steps.items.len); | |
| 44 | break :block try arena.dupe(*Step.Run, fuzz_run_steps.items); | |
| 75 | ||
| 76 | if (steps.items.len == 0) fatal("no fuzz tests found", .{}); | |
| 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); | |
| 46 | 81 | |
| 47 | // Detect failure. | |
| 48 | for (fuzz_run_steps) |run| { | |
| 82 | for (run_steps) |run| { | |
| 49 | 83 | assert(run.fuzz_tests.items.len > 0); |
| 50 | 84 | if (run.rebuilt_executable == null) |
| 51 | 85 | fatal("one or more unit tests failed to be rebuilt in fuzz mode", .{}); |
| 52 | 86 | } |
| 53 | 87 | |
| 54 | var web_server: WebServer = .{ | |
| 55 | .gpa = gpa, | |
| 56 | .global_cache_directory = global_cache_directory, | |
| 57 | .zig_lib_directory = zig_lib_directory, | |
| 58 | .zig_exe_path = zig_exe_path, | |
| 59 | .listen_address = listen_address, | |
| 60 | .fuzz_run_steps = fuzz_run_steps, | |
| 61 | ||
| 62 | .msg_queue = .{}, | |
| 63 | .mutex = .{}, | |
| 64 | .condition = .{}, | |
| 65 | ||
| 66 | .coverage_files = .{}, | |
| 88 | return .{ | |
| 89 | .ws = ws, | |
| 90 | .run_steps = run_steps, | |
| 91 | .wait_group = .{}, | |
| 92 | .prog_node = .none, | |
| 93 | .coverage_files = .empty, | |
| 67 | 94 | .coverage_mutex = .{}, |
| 68 | .coverage_condition = .{}, | |
| 69 | ||
| 70 | .base_timestamp = std.time.nanoTimestamp(), | |
| 95 | .queue_mutex = .{}, | |
| 96 | .queue_cond = .{}, | |
| 97 | .msg_queue = .empty, | |
| 71 | 98 | }; |
| 99 | } | |
| 72 | 100 | |
| 73 | // For accepting HTTP connections. | |
| 74 | const web_server_thread = std.Thread.spawn(.{}, WebServer.run, .{&web_server}) catch |err| { | |
| 75 | fatal("unable to spawn web server thread: {s}", .{@errorName(err)}); | |
| 76 | }; | |
| 77 | defer web_server_thread.join(); | |
| 101 | pub fn start(fuzz: *Fuzz) void { | |
| 102 | const ws = fuzz.ws; | |
| 103 | fuzz.prog_node = ws.root_prog_node.start("Fuzzing", fuzz.run_steps.len); | |
| 78 | 104 | |
| 79 | 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 | 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(); | |
| 90 | 111 | |
| 91 | for (fuzz_run_steps) |run| { | |
| 92 | for (run.fuzz_tests.items) |unit_test_index| { | |
| 93 | assert(run.rebuilt_executable != null); | |
| 94 | thread_pool.spawnWg(&wait_group, fuzzWorkerRun, .{ | |
| 95 | run, &web_server, unit_test_index, ttyconf, fuzz_node, | |
| 96 | }); | |
| 97 | } | |
| 112 | for (fuzz.run_steps) |run| { | |
| 113 | for (run.fuzz_tests.items) |unit_test_index| { | |
| 114 | assert(run.rebuilt_executable != null); | |
| 115 | ws.thread_pool.spawnWg(&fuzz.wait_group, fuzzWorkerRun, .{ | |
| 116 | fuzz, run, unit_test_index, | |
| 117 | }); | |
| 98 | 118 | } |
| 99 | 119 | } |
| 120 | } | |
| 121 | pub fn deinit(fuzz: *Fuzz) void { | |
| 122 | if (true) @panic("TODO: terminate the fuzzer processes"); | |
| 123 | fuzz.wait_group.wait(); | |
| 124 | fuzz.prog_node.end(); | |
| 100 | 125 | |
| 101 | log.err("all fuzz workers crashed", .{}); | |
| 126 | const gpa = fuzz.ws.gpa; | |
| 127 | gpa.free(fuzz.run_steps); | |
| 102 | 128 | } |
| 103 | 129 | |
| 104 | fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void { | |
| 105 | rebuildTestsWorkerRunFallible(run, ttyconf, parent_prog_node) catch |err| { | |
| 130 | fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void { | |
| 131 | rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| { | |
| 106 | 132 | const compile = run.producer.?; |
| 107 | 133 | log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{ |
| 108 | 134 | compile.step.name, @errorName(err), |
| ... | ... | @@ -110,14 +136,12 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog |
| 110 | 136 | }; |
| 111 | 137 | } |
| 112 | 138 | |
| 113 | fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void { | |
| 114 | const gpa = run.step.owner.allocator; | |
| 115 | ||
| 139 | fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void { | |
| 116 | 140 | const compile = run.producer.?; |
| 117 | 141 | const prog_node = parent_prog_node.start(compile.step.name, 0); |
| 118 | 142 | defer prog_node.end(); |
| 119 | 143 | |
| 120 | const result = compile.rebuildInFuzzMode(prog_node); | |
| 144 | const result = compile.rebuildInFuzzMode(gpa, prog_node); | |
| 121 | 145 | |
| 122 | 146 | const show_compile_errors = compile.step.result_error_bundle.errorMessageCount() > 0; |
| 123 | 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 | 162 | } |
| 139 | 163 | |
| 140 | 164 | fn fuzzWorkerRun( |
| 165 | fuzz: *Fuzz, | |
| 141 | 166 | run: *Step.Run, |
| 142 | web_server: *WebServer, | |
| 143 | 167 | unit_test_index: u32, |
| 144 | ttyconf: std.io.tty.Config, | |
| 145 | parent_prog_node: std.Progress.Node, | |
| 146 | 168 | ) void { |
| 147 | 169 | const gpa = run.step.owner.allocator; |
| 148 | 170 | const test_name = run.cached_test_metadata.?.testName(unit_test_index); |
| 149 | 171 | |
| 150 | const prog_node = parent_prog_node.start(test_name, 0); | |
| 172 | const prog_node = fuzz.prog_node.start(test_name, 0); | |
| 151 | 173 | defer prog_node.end(); |
| 152 | 174 | |
| 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 | 176 | error.MakeFailed => { |
| 155 | 177 | var buf: [256]u8 = undefined; |
| 156 | 178 | const w = std.debug.lockStderrWriter(&buf); |
| 157 | 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 | 181 | return; |
| 160 | 182 | }, |
| 161 | 183 | else => { |
| ... | ... | @@ -166,3 +188,270 @@ fn fuzzWorkerRun( |
| 166 | 188 | }, |
| 167 | 189 | }; |
| 168 | 190 | } |
| 191 | ||
| 192 | pub 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 | ||
| 230 | pub const Previous = struct { | |
| 231 | unique_runs: usize, | |
| 232 | entry_points: usize, | |
| 233 | pub const init: Previous = .{ .unique_runs = 0, .entry_points = 0 }; | |
| 234 | }; | |
| 235 | pub 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 | ||
| 301 | fn 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 | } | |
| 322 | fn 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 | } | |
| 418 | fn 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 | ||
| 452 | fn 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 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | ||
| 3 | const std = @import("../../std.zig"); | |
| 4 | const Allocator = std.mem.Allocator; | |
| 5 | const Build = std.Build; | |
| 6 | const Step = std.Build.Step; | |
| 7 | const Coverage = std.debug.Coverage; | |
| 8 | const abi = std.Build.Fuzz.abi; | |
| 9 | const log = std.log; | |
| 10 | const assert = std.debug.assert; | |
| 11 | const Cache = std.Build.Cache; | |
| 12 | const Path = Cache.Path; | |
| 13 | ||
| 14 | const WebServer = @This(); | |
| 15 | ||
| 16 | gpa: Allocator, | |
| 17 | global_cache_directory: Build.Cache.Directory, | |
| 18 | zig_lib_directory: Build.Cache.Directory, | |
| 19 | zig_exe_path: []const u8, | |
| 20 | listen_address: std.net.Address, | |
| 21 | fuzz_run_steps: []const *Step.Run, | |
| 22 | ||
| 23 | /// Messages from fuzz workers. Protected by mutex. | |
| 24 | msg_queue: std.ArrayListUnmanaged(Msg), | |
| 25 | /// Protects `msg_queue` only. | |
| 26 | mutex: std.Thread.Mutex, | |
| 27 | /// Signaled when there is a message in `msg_queue`. | |
| 28 | condition: std.Thread.Condition, | |
| 29 | ||
| 30 | coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap), | |
| 31 | /// Protects `coverage_files` only. | |
| 32 | coverage_mutex: std.Thread.Mutex, | |
| 33 | /// Signaled when `coverage_files` changes. | |
| 34 | coverage_condition: std.Thread.Condition, | |
| 35 | ||
| 36 | /// Time at initialization of WebServer. | |
| 37 | base_timestamp: i128, | |
| 38 | ||
| 39 | const fuzzer_bin_name = "fuzzer"; | |
| 40 | const fuzzer_arch_os_abi = "wasm32-freestanding"; | |
| 41 | const fuzzer_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; | |
| 42 | ||
| 43 | const 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 | ||
| 58 | const 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 | ||
| 69 | pub 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 | ||
| 94 | fn now(s: *const WebServer) i64 { | |
| 95 | return @intCast(std.time.nanoTimestamp() - s.base_timestamp); | |
| 96 | } | |
| 97 | ||
| 98 | fn 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 | ||
| 140 | fn 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 | ||
| 168 | fn 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 | ||
| 191 | fn 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 | ||
| 225 | fn 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 | ||
| 378 | fn 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 | ||
| 386 | fn 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 | ||
| 408 | fn 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 | ||
| 473 | fn 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 | ||
| 540 | fn 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 | ||
| 547 | const cache_control_header: std.http.Header = .{ | |
| 548 | .name = "cache-control", | |
| 549 | .value = "max-age=0, must-revalidate", | |
| 550 | }; | |
| 551 | ||
| 552 | pub 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 | ||
| 572 | fn 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 | ||
| 672 | fn 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 | ||
| 706 | fn 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 | |
| 12 | pub 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 | ||
| 45 | pub const ToClientTag = enum(u8) { | |
| 46 | current_time, | |
| 47 | source_index, | |
| 48 | coverage_update, | |
| 49 | entry_points, | |
| 50 | _, | |
| 51 | }; | |
| 52 | ||
| 53 | pub const CurrentTime = extern struct { | |
| 54 | tag: ToClientTag = .current_time, | |
| 55 | /// Number of nanoseconds that all other timestamps are in reference to. | |
| 56 | base: i64 align(1), | |
| 57 | }; | |
| 58 | ||
| 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 | |
| 66 | pub 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 | |
| 86 | pub 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 | |
| 105 | pub 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 | 72 | progress_node: std.Progress.Node, |
| 73 | 73 | thread_pool: *std.Thread.Pool, |
| 74 | 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 | }; |
| 76 | 84 | |
| 77 | 85 | pub const MakeFn = *const fn (step: *Step, options: MakeOptions) anyerror!void; |
| ... | ... | @@ -229,7 +237,17 @@ pub fn init(options: StepOptions) Step { |
| 229 | 237 | pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void { |
| 230 | 238 | const arena = s.owner.allocator; |
| 231 | 239 | |
| 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 | 251 | error.MakeFailed => return error.MakeFailed, |
| 234 | 252 | error.MakeSkipped => return error.MakeSkipped, |
| 235 | 253 | else => { |
| ... | ... | @@ -372,18 +390,20 @@ pub fn evalZigProcess( |
| 372 | 390 | argv: []const []const u8, |
| 373 | 391 | prog_node: std.Progress.Node, |
| 374 | 392 | watch: bool, |
| 393 | web_server: ?*Build.WebServer, | |
| 394 | gpa: Allocator, | |
| 375 | 395 | ) !?Path { |
| 376 | 396 | if (s.getZigProcess()) |zp| update: { |
| 377 | 397 | assert(watch); |
| 378 | 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 | 400 | error.BrokenPipe => { |
| 381 | 401 | // Process restart required. |
| 382 | 402 | const term = zp.child.wait() catch |e| { |
| 383 | 403 | return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) }); |
| 384 | 404 | }; |
| 385 | 405 | _ = term; |
| 386 | s.clearZigProcess(); | |
| 406 | s.clearZigProcess(gpa); | |
| 387 | 407 | break :update; |
| 388 | 408 | }, |
| 389 | 409 | else => |e| return e, |
| ... | ... | @@ -398,7 +418,7 @@ pub fn evalZigProcess( |
| 398 | 418 | return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) }); |
| 399 | 419 | }; |
| 400 | 420 | s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0; |
| 401 | s.clearZigProcess(); | |
| 421 | s.clearZigProcess(gpa); | |
| 402 | 422 | try handleChildProcessTerm(s, term, null, argv); |
| 403 | 423 | return error.MakeFailed; |
| 404 | 424 | } |
| ... | ... | @@ -408,7 +428,6 @@ pub fn evalZigProcess( |
| 408 | 428 | assert(argv.len != 0); |
| 409 | 429 | const b = s.owner; |
| 410 | 430 | const arena = b.allocator; |
| 411 | const gpa = arena; | |
| 412 | 431 | |
| 413 | 432 | try handleChildProcUnsupported(s, null, argv); |
| 414 | 433 | try handleVerbose(s.owner, null, argv); |
| ... | ... | @@ -435,9 +454,12 @@ pub fn evalZigProcess( |
| 435 | 454 | .progress_ipc_fd = if (std.Progress.have_ipc) child.progress_node.getIpcFd() else {}, |
| 436 | 455 | }; |
| 437 | 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 | }; | |
| 439 | 461 | |
| 440 | const result = try zigProcessUpdate(s, zp, watch); | |
| 462 | const result = try zigProcessUpdate(s, zp, watch, web_server, gpa); | |
| 441 | 463 | |
| 442 | 464 | if (!watch) { |
| 443 | 465 | // Send EOF to stdin. |
| ... | ... | @@ -499,7 +521,7 @@ pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus { |
| 499 | 521 | }; |
| 500 | 522 | } |
| 501 | 523 | |
| 502 | fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { | |
| 524 | fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.WebServer, gpa: Allocator) !?Path { | |
| 503 | 525 | const b = s.owner; |
| 504 | 526 | const arena = b.allocator; |
| 505 | 527 | |
| ... | ... | @@ -537,12 +559,14 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 537 | 559 | body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; |
| 538 | 560 | // TODO: use @ptrCast when the compiler supports it |
| 539 | 561 | const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); |
| 540 | const extra_array = try arena.alloc(u32, unaligned_extra.len); | |
| 541 | @memcpy(extra_array, unaligned_extra); | |
| 542 | s.result_error_bundle = .{ | |
| 543 | .string_bytes = try arena.dupe(u8, string_bytes), | |
| 544 | .extra = extra_array, | |
| 545 | }; | |
| 562 | { | |
| 563 | s.result_error_bundle = .{ .string_bytes = &.{}, .extra = &.{} }; | |
| 564 | errdefer s.result_error_bundle.deinit(gpa); | |
| 565 | s.result_error_bundle.string_bytes = try gpa.dupe(u8, string_bytes); | |
| 566 | const extra = try gpa.alloc(u32, unaligned_extra.len); | |
| 567 | @memcpy(extra, unaligned_extra); | |
| 568 | s.result_error_bundle.extra = extra; | |
| 569 | } | |
| 546 | 570 | // This message indicates the end of the update. |
| 547 | 571 | if (watch) break :poll; |
| 548 | 572 | }, |
| ... | ... | @@ -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 | 643 | else => {}, // ignore other messages |
| 606 | 644 | } |
| 607 | 645 | } |
| ... | ... | @@ -630,8 +668,7 @@ fn setZigProcess(s: *Step, zp: *ZigProcess) void { |
| 630 | 668 | } |
| 631 | 669 | } |
| 632 | 670 | |
| 633 | fn clearZigProcess(s: *Step) void { | |
| 634 | const gpa = s.owner.allocator; | |
| 671 | fn clearZigProcess(s: *Step, gpa: Allocator) void { | |
| 635 | 672 | switch (s.id) { |
| 636 | 673 | .compile => { |
| 637 | 674 | const compile = s.cast(Compile).?; |
| ... | ... | @@ -947,7 +984,8 @@ fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const |
| 947 | 984 | try gop.value_ptr.append(gpa, basename); |
| 948 | 985 | } |
| 949 | 986 | |
| 950 | fn reset(step: *Step, gpa: Allocator) void { | |
| 987 | /// Implementation detail of file watching and forced rebuilds. Prepares the step for being re-evaluated. | |
| 988 | pub fn reset(step: *Step, gpa: Allocator) void { | |
| 951 | 989 | assert(step.state == .precheck_done); |
| 952 | 990 | |
| 953 | 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 | 1491 | if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link"); |
| 1492 | 1492 | if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc"); |
| 1493 | 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"); | |
| 1494 | 1495 | |
| 1495 | 1496 | if (compile.generated_asm != null) try zig_args.append("-femit-asm"); |
| 1496 | 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 | 1852 | zig_args, |
| 1852 | 1853 | options.progress_node, |
| 1853 | 1854 | (b.graph.incremental == true) and options.watch, |
| 1855 | options.web_server, | |
| 1856 | options.gpa, | |
| 1854 | 1857 | ) catch |err| switch (err) { |
| 1855 | 1858 | error.NeedCompileErrorCheck => { |
| 1856 | 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 | 1908 | return out_dir.joinString(arena, name) catch @panic("OOM"); |
| 1906 | 1909 | } |
| 1907 | 1910 | |
| 1908 | pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path { | |
| 1909 | const gpa = c.step.owner.allocator; | |
| 1910 | ||
| 1911 | pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progress.Node) !Path { | |
| 1911 | 1912 | c.step.result_error_msgs.clearRetainingCapacity(); |
| 1912 | 1913 | c.step.result_stderr = ""; |
| 1913 | 1914 | |
| ... | ... | @@ -1915,7 +1916,7 @@ pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path { |
| 1915 | 1916 | c.step.result_error_bundle = std.zig.ErrorBundle.empty; |
| 1916 | 1917 | |
| 1917 | 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 | 1920 | return maybe_output_bin_path.?; |
| 1920 | 1921 | } |
| 1921 | 1922 |
lib/std/Build/Step/ObjCopy.zig+1-1| ... | ... | @@ -236,7 +236,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 236 | 236 | try argv.appendSlice(&.{ full_src_path, full_dest_path }); |
| 237 | 237 | |
| 238 | 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); | |
| 240 | 240 | |
| 241 | 241 | objcopy.output_file.path = full_dest_path; |
| 242 | 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 | 549 | .result = try std.zig.system.resolveTargetQuery(.{}), |
| 550 | 550 | }, |
| 551 | 551 | .zig_lib_directory = std.Build.Cache.Directory.cwd(), |
| 552 | .time_report = false, | |
| 552 | 553 | }; |
| 553 | 554 | |
| 554 | 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 | 944 | |
| 945 | 945 | pub fn rerunInFuzzMode( |
| 946 | 946 | run: *Run, |
| 947 | web_server: *std.Build.Fuzz.WebServer, | |
| 947 | fuzz: *std.Build.Fuzz, | |
| 948 | 948 | unit_test_index: u32, |
| 949 | 949 | prog_node: std.Progress.Node, |
| 950 | 950 | ) !void { |
| ... | ... | @@ -984,7 +984,7 @@ pub fn rerunInFuzzMode( |
| 984 | 984 | const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); |
| 985 | 985 | try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, .{ |
| 986 | 986 | .unit_test_index = unit_test_index, |
| 987 | .web_server = web_server, | |
| 987 | .fuzz = fuzz, | |
| 988 | 988 | }); |
| 989 | 989 | } |
| 990 | 990 | |
| ... | ... | @@ -1054,7 +1054,7 @@ fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term |
| 1054 | 1054 | } |
| 1055 | 1055 | |
| 1056 | 1056 | const FuzzContext = struct { |
| 1057 | web_server: *std.Build.Fuzz.WebServer, | |
| 1057 | fuzz: *std.Build.Fuzz, | |
| 1058 | 1058 | unit_test_index: u32, |
| 1059 | 1059 | }; |
| 1060 | 1060 | |
| ... | ... | @@ -1638,31 +1638,31 @@ fn evalZigTest( |
| 1638 | 1638 | }; |
| 1639 | 1639 | }, |
| 1640 | 1640 | .coverage_id => { |
| 1641 | const web_server = fuzz_context.?.web_server; | |
| 1641 | const fuzz = fuzz_context.?.fuzz; | |
| 1642 | 1642 | const msg_ptr: *align(1) const u64 = @ptrCast(body); |
| 1643 | 1643 | coverage_id = msg_ptr.*; |
| 1644 | 1644 | { |
| 1645 | web_server.mutex.lock(); | |
| 1646 | defer web_server.mutex.unlock(); | |
| 1647 | try web_server.msg_queue.append(web_server.gpa, .{ .coverage = .{ | |
| 1645 | fuzz.queue_mutex.lock(); | |
| 1646 | defer fuzz.queue_mutex.unlock(); | |
| 1647 | try fuzz.msg_queue.append(fuzz.ws.gpa, .{ .coverage = .{ | |
| 1648 | 1648 | .id = coverage_id.?, |
| 1649 | 1649 | .run = run, |
| 1650 | 1650 | } }); |
| 1651 | web_server.condition.signal(); | |
| 1651 | fuzz.queue_cond.signal(); | |
| 1652 | 1652 | } |
| 1653 | 1653 | }, |
| 1654 | 1654 | .fuzz_start_addr => { |
| 1655 | const web_server = fuzz_context.?.web_server; | |
| 1655 | const fuzz = fuzz_context.?.fuzz; | |
| 1656 | 1656 | const msg_ptr: *align(1) const u64 = @ptrCast(body); |
| 1657 | 1657 | const addr = msg_ptr.*; |
| 1658 | 1658 | { |
| 1659 | web_server.mutex.lock(); | |
| 1660 | defer web_server.mutex.unlock(); | |
| 1661 | try web_server.msg_queue.append(web_server.gpa, .{ .entry_point = .{ | |
| 1659 | fuzz.queue_mutex.lock(); | |
| 1660 | defer fuzz.queue_mutex.unlock(); | |
| 1661 | try fuzz.msg_queue.append(fuzz.ws.gpa, .{ .entry_point = .{ | |
| 1662 | 1662 | .addr = addr, |
| 1663 | 1663 | .coverage_id = coverage_id.?, |
| 1664 | 1664 | } }); |
| 1665 | web_server.condition.signal(); | |
| 1665 | fuzz.queue_cond.signal(); | |
| 1666 | 1666 | } |
| 1667 | 1667 | }, |
| 1668 | 1668 | 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 | 187 | const c_source_path = translate_c.source.getPath2(b, step); |
| 188 | 188 | try argv_list.append(c_source_path); |
| 189 | 189 | |
| 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); | |
| 191 | 191 | |
| 192 | 192 | const basename = std.fs.path.stem(std.fs.path.basename(c_source_path)); |
| 193 | 193 | translate_c.out_basename = b.fmt("{s}.zig", .{basename}); |
lib/std/Build/WebServer.zig created+823| ... | ... | @@ -0,0 +1,823 @@ |
| 1 | gpa: Allocator, | |
| 2 | thread_pool: *std.Thread.Pool, | |
| 3 | graph: *const Build.Graph, | |
| 4 | all_steps: []const *Build.Step, | |
| 5 | listen_address: std.net.Address, | |
| 6 | ttyconf: std.io.tty.Config, | |
| 7 | root_prog_node: std.Progress.Node, | |
| 8 | watch: bool, | |
| 9 | ||
| 10 | tcp_server: ?std.net.Server, | |
| 11 | serve_thread: ?std.Thread, | |
| 12 | ||
| 13 | base_timestamp: i128, | |
| 14 | /// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`. | |
| 15 | step_names_trailing: []u8, | |
| 16 | ||
| 17 | /// The bit-packed "step status" data. Values are `abi.StepUpdate.Status`. LSBs are earlier steps. | |
| 18 | /// Accessed atomically. | |
| 19 | step_status_bits: []u8, | |
| 20 | ||
| 21 | fuzz: ?Fuzz, | |
| 22 | time_report_mutex: std.Thread.Mutex, | |
| 23 | time_report_msgs: [][]u8, | |
| 24 | time_report_update_times: []i64, | |
| 25 | ||
| 26 | build_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. | |
| 34 | update_id: std.atomic.Value(u32), | |
| 35 | ||
| 36 | runner_request_mutex: std.Thread.Mutex, | |
| 37 | runner_request_ready_cond: std.Thread.Condition, | |
| 38 | runner_request_empty_cond: std.Thread.Condition, | |
| 39 | runner_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. | |
| 43 | const default_update_interval_ms = 500; | |
| 44 | ||
| 45 | /// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`. | |
| 46 | pub fn notifyUpdate(ws: *WebServer) void { | |
| 47 | _ = ws.update_id.rmw(.Add, 1, .release); | |
| 48 | std.Thread.Futex.wake(&ws.update_id, 16); | |
| 49 | } | |
| 50 | ||
| 51 | pub 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 | }; | |
| 61 | pub 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 | } | |
| 140 | pub 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 | } | |
| 159 | pub 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 | } | |
| 179 | fn 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 | ||
| 193 | pub 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 | ||
| 203 | pub 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 | ||
| 215 | pub 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 | ||
| 247 | pub fn now(s: *const WebServer) i64 { | |
| 248 | return @intCast(std.time.nanoTimestamp() - s.base_timestamp); | |
| 249 | } | |
| 250 | ||
| 251 | fn 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 | ||
| 289 | fn makeIov(s: []const u8) std.posix.iovec_const { | |
| 290 | return .{ | |
| 291 | .base = s.ptr, | |
| 292 | .len = s.len, | |
| 293 | }; | |
| 294 | } | |
| 295 | fn 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 | } | |
| 383 | fn 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 | ||
| 405 | fn 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 | ||
| 432 | fn 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 | } | |
| 443 | fn 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 | ||
| 457 | pub 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 | } | |
| 479 | pub 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 | ||
| 533 | fn 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 | ||
| 698 | pub 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 | ||
| 751 | pub 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 | ||
| 781 | const RunnerRequest = union(enum) { | |
| 782 | rebuild, | |
| 783 | }; | |
| 784 | pub 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 | } | |
| 794 | pub 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 | ||
| 807 | const cache_control_header: std.http.Header = .{ | |
| 808 | .name = "Cache-Control", | |
| 809 | .value = "max-age=0, must-revalidate", | |
| 810 | }; | |
| 811 | ||
| 812 | const builtin = @import("builtin"); | |
| 813 | const std = @import("std"); | |
| 814 | const assert = std.debug.assert; | |
| 815 | const mem = std.mem; | |
| 816 | const log = std.log.scoped(.web_server); | |
| 817 | const Allocator = std.mem.Allocator; | |
| 818 | const Build = std.Build; | |
| 819 | const Cache = Build.Cache; | |
| 820 | const Fuzz = Build.Fuzz; | |
| 821 | const abi = Build.abi; | |
| 822 | ||
| 823 | const 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. | |
| 13 | comptime { | |
| 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. | |
| 46 | pub 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. | |
| 64 | pub 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`. | |
| 72 | pub 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 | |
| 88 | pub 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. | |
| 112 | pub 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. | |
| 119 | pub 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 | ||
| 135 | pub const Rebuild = extern struct { | |
| 136 | tag: ToServerTag = .rebuild, | |
| 137 | }; | |
| 138 | ||
| 139 | /// ABI bits specifically relating to the fuzzer interface. | |
| 140 | pub 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. | |
| 237 | pub 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 | 2353 | this.hasher.update(slice); |
| 2354 | 2354 | } |
| 2355 | 2355 | const pattern = data[data.len - 1]; |
| 2356 | assert(remaining == splat * pattern.len); | |
| 2356 | assert(remaining <= splat * pattern.len); | |
| 2357 | 2357 | switch (pattern.len) { |
| 2358 | 2358 | 0 => { |
| 2359 | 2359 | assert(remaining == 0); |
lib/std/compress/flate/Decompress.zig+21-18| ... | ... | @@ -10,8 +10,8 @@ const Decompress = @This(); |
| 10 | 10 | const Token = @import("Token.zig"); |
| 11 | 11 | |
| 12 | 12 | input: *Reader, |
| 13 | next_bits: usize, | |
| 14 | remaining_bits: std.math.Log2Int(usize), | |
| 13 | next_bits: Bits, | |
| 14 | remaining_bits: std.math.Log2Int(Bits), | |
| 15 | 15 | |
| 16 | 16 | reader: Reader, |
| 17 | 17 | |
| ... | ... | @@ -25,6 +25,9 @@ state: State, |
| 25 | 25 | |
| 26 | 26 | err: ?Error, |
| 27 | 27 | |
| 28 | /// TODO: change this to usize | |
| 29 | const Bits = u64; | |
| 30 | ||
| 28 | 31 | const BlockType = enum(u2) { |
| 29 | 32 | stored = 0, |
| 30 | 33 | fixed = 1, |
| ... | ... | @@ -498,14 +501,14 @@ fn takeBits(d: *Decompress, comptime U: type) !U { |
| 498 | 501 | return u; |
| 499 | 502 | } |
| 500 | 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 | 505 | error.ReadFailed => return error.ReadFailed, |
| 503 | 506 | error.EndOfStream => return takeBitsEnding(d, U), |
| 504 | 507 | }; |
| 505 | 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 | 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 | 512 | return u; |
| 510 | 513 | } |
| 511 | 514 | |
| ... | ... | @@ -514,14 +517,14 @@ fn takeBitsEnding(d: *Decompress, comptime U: type) !U { |
| 514 | 517 | const next_bits = d.next_bits; |
| 515 | 518 | const in = d.input; |
| 516 | 519 | const n = in.bufferedLen(); |
| 517 | assert(n < @sizeOf(usize)); | |
| 520 | assert(n < @sizeOf(Bits)); | |
| 518 | 521 | const needed_bits = @bitSizeOf(U) - remaining_bits; |
| 519 | 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 | 524 | error.ReadFailed => return error.ReadFailed, |
| 522 | 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 | 528 | d.next_bits = next_int >> needed_bits; |
| 526 | 529 | d.remaining_bits = @intCast(n * 8 - @as(usize, needed_bits)); |
| 527 | 530 | return u; |
| ... | ... | @@ -532,37 +535,37 @@ fn peekBits(d: *Decompress, comptime U: type) !U { |
| 532 | 535 | const next_bits = d.next_bits; |
| 533 | 536 | if (remaining_bits >= @bitSizeOf(U)) return @truncate(next_bits); |
| 534 | 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 | 539 | error.ReadFailed => return error.ReadFailed, |
| 537 | 540 | error.EndOfStream => return peekBitsEnding(d, U), |
| 538 | 541 | }; |
| 539 | 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 | } |
| 542 | 545 | |
| 543 | 546 | fn peekBitsEnding(d: *Decompress, comptime U: type) !U { |
| 544 | 547 | const remaining_bits = d.remaining_bits; |
| 545 | 548 | const next_bits = d.next_bits; |
| 546 | 549 | const in = d.input; |
| 547 | var u: usize = 0; | |
| 550 | var u: Bits = 0; | |
| 548 | 551 | var remaining_needed_bits = @bitSizeOf(U) - remaining_bits; |
| 549 | 552 | var i: usize = 0; |
| 550 | 553 | while (remaining_needed_bits >= 8) { |
| 551 | 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 | 556 | remaining_needed_bits -= 8; |
| 554 | 557 | i += 1; |
| 555 | 558 | } |
| 556 | 559 | if (remaining_needed_bits != 0) { |
| 557 | 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 | 563 | return @truncate((u << remaining_bits) | next_bits); |
| 561 | 564 | } |
| 562 | 565 | |
| 563 | 566 | /// If there is any unconsumed data, handles EndOfStream by pretending there |
| 564 | 567 | /// are zeroes afterwards. |
| 565 | fn specialPeek(in: *Reader, next_bits: usize, i: usize) Reader.Error!u8 { | |
| 568 | fn specialPeek(in: *Reader, next_bits: Bits, i: usize) Reader.Error!u8 { | |
| 566 | 569 | const peeked = in.peek(i + 1) catch |err| switch (err) { |
| 567 | 570 | error.ReadFailed => return error.ReadFailed, |
| 568 | 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 | 581 | d.remaining_bits = remaining_bits - n; |
| 579 | 582 | } else { |
| 580 | 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 | 585 | error.ReadFailed => return error.ReadFailed, |
| 583 | 586 | error.EndOfStream => return tossBitsEnding(d, n), |
| 584 | 587 | }; |
| 585 | 588 | const needed_bits = n - remaining_bits; |
| 586 | 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 | } |
| 590 | 593 | |
| ... | ... | @@ -593,9 +596,9 @@ fn tossBitsEnding(d: *Decompress, n: u4) !void { |
| 593 | 596 | const in = d.input; |
| 594 | 597 | const buffered_n = in.bufferedLen(); |
| 595 | 598 | if (buffered_n == 0) return error.EndOfStream; |
| 596 | assert(buffered_n < @sizeOf(usize)); | |
| 599 | assert(buffered_n < @sizeOf(Bits)); | |
| 597 | 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 | 602 | error.ReadFailed => return error.ReadFailed, |
| 600 | 603 | error.EndOfStream => unreachable, |
| 601 | 604 | }; |
lib/std/net.zig+41| ... | ... | @@ -42,6 +42,47 @@ pub const Address = extern union { |
| 42 | 42 | in6: Ip6Address, |
| 43 | 43 | un: if (has_unix_sockets) posix.sockaddr.un else void, |
| 44 | 44 | |
| 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 | 86 | /// Parse the given IP address string into an Address value. |
| 46 | 87 | /// It is recommended to use `resolveIp` instead, to handle |
| 47 | 88 | /// IPv6 link-local unix addresses. |
lib/std/zig/Server.zig+15| ... | ... | @@ -50,6 +50,8 @@ pub const Message = struct { |
| 50 | 50 | /// address of the fuzz unit test. This is used to provide a starting |
| 51 | 51 | /// point to view coverage. |
| 52 | 52 | fuzz_start_addr, |
| 53 | /// Body is a TimeReport. | |
| 54 | time_report, | |
| 53 | 55 | |
| 54 | 56 | _, |
| 55 | 57 | }; |
| ... | ... | @@ -95,6 +97,19 @@ pub const Message = struct { |
| 95 | 97 | }; |
| 96 | 98 | }; |
| 97 | 99 | |
| 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 | 113 | /// Trailing: |
| 99 | 114 | /// * the hex digest of the cache directory within the /o/ subdirectory. |
| 100 | 115 | pub const EmitDigest = extern struct { |
src/Compilation.zig+191-5| ... | ... | @@ -173,7 +173,6 @@ verbose_cimport: bool, |
| 173 | 173 | verbose_llvm_cpu_features: bool, |
| 174 | 174 | verbose_link: bool, |
| 175 | 175 | disable_c_depfile: bool, |
| 176 | time_report: bool, | |
| 177 | 176 | stack_report: bool, |
| 178 | 177 | debug_compiler_runtime_libs: bool, |
| 179 | 178 | debug_compile_errors: bool, |
| ... | ... | @@ -263,6 +262,8 @@ link_prog_node: std.Progress.Node = std.Progress.Node.none, |
| 263 | 262 | |
| 264 | 263 | llvm_opt_bisect_limit: c_int, |
| 265 | 264 | |
| 265 | time_report: ?TimeReport, | |
| 266 | ||
| 266 | 267 | file_system_inputs: ?*std.ArrayListUnmanaged(u8), |
| 267 | 268 | |
| 268 | 269 | /// This is the digest of the cache for the current compilation. |
| ... | ... | @@ -322,6 +323,72 @@ const QueuedJobs = struct { |
| 322 | 323 | zigc_lib: bool = false, |
| 323 | 324 | }; |
| 324 | 325 | |
| 326 | pub 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. | |
| 383 | pub 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 | 392 | /// A filesystem path, represented relative to one of a few specific directories where possible. |
| 326 | 393 | /// Every path (considering symlinks as distinct paths) has a canonical representation in this form. |
| 327 | 394 | /// This abstraction allows us to: |
| ... | ... | @@ -787,6 +854,58 @@ pub inline fn debugIncremental(comp: *const Compilation) bool { |
| 787 | 854 | return comp.debug_incremental; |
| 788 | 855 | } |
| 789 | 856 | |
| 857 | pub 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 | ||
| 790 | 909 | pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; |
| 791 | 910 | pub const SemaError = Zcu.SemaError; |
| 792 | 911 | |
| ... | ... | @@ -2027,7 +2146,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2027 | 2146 | .verbose_link = options.verbose_link, |
| 2028 | 2147 | .disable_c_depfile = options.disable_c_depfile, |
| 2029 | 2148 | .reference_trace = options.reference_trace, |
| 2030 | .time_report = options.time_report, | |
| 2149 | .time_report = if (options.time_report) .init else null, | |
| 2031 | 2150 | .stack_report = options.stack_report, |
| 2032 | 2151 | .test_filters = options.test_filters, |
| 2033 | 2152 | .test_name_prefix = options.test_name_prefix, |
| ... | ... | @@ -2561,6 +2680,8 @@ pub fn destroy(comp: *Compilation) void { |
| 2561 | 2680 | } |
| 2562 | 2681 | comp.failed_win32_resources.deinit(gpa); |
| 2563 | 2682 | |
| 2683 | if (comp.time_report) |*tr| tr.deinit(gpa); | |
| 2684 | ||
| 2564 | 2685 | comp.link_diags.deinit(); |
| 2565 | 2686 | |
| 2566 | 2687 | comp.clearMiscFailures(); |
| ... | ... | @@ -2657,6 +2778,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2657 | 2778 | |
| 2658 | 2779 | comp.clearMiscFailures(); |
| 2659 | 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 | } | |
| 2660 | 2785 | |
| 2661 | 2786 | var tmp_dir_rand_int: u64 = undefined; |
| 2662 | 2787 | var man: Cache.Manifest = undefined; |
| ... | ... | @@ -2688,6 +2813,14 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2688 | 2813 | whole.cache_manifest = &man; |
| 2689 | 2814 | try addNonIncrementalStuffToCacheManifest(comp, arena, &man); |
| 2690 | 2815 | |
| 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 | 2824 | const is_hit = man.hit() catch |err| switch (err) { |
| 2692 | 2825 | error.CacheCheckFailed => switch (man.diagnostic) { |
| 2693 | 2826 | .none => unreachable, |
| ... | ... | @@ -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 | 2850 | // In this case the cache hit contains the full set of file system inputs. Nice! |
| 2718 | 2851 | if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf); |
| 2719 | 2852 | if (comp.parent_whole_cache) |pwc| { |
| ... | ... | @@ -2734,6 +2867,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2734 | 2867 | } |
| 2735 | 2868 | log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name}); |
| 2736 | 2869 | |
| 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 | 2875 | // Compile the artifacts to a temporary directory. |
| 2738 | 2876 | whole.tmp_artifact_directory = d: { |
| 2739 | 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 | 2924 | const pt: Zcu.PerThread = .activate(zcu, .main); |
| 2787 | 2925 | defer pt.deactivate(); |
| 2788 | 2926 | |
| 2927 | assert(zcu.cur_analysis_timer == null); | |
| 2928 | ||
| 2789 | 2929 | zcu.skip_analysis_this_update = false; |
| 2790 | 2930 | |
| 2791 | 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 | 2969 | const pt: Zcu.PerThread = .activate(zcu, .main); |
| 2830 | 2970 | defer pt.deactivate(); |
| 2831 | 2971 | |
| 2972 | assert(zcu.cur_analysis_timer == null); | |
| 2973 | ||
| 2832 | 2974 | if (!zcu.skip_analysis_this_update) { |
| 2833 | 2975 | if (comp.config.is_test) { |
| 2834 | 2976 | // The `test_functions` decl has been intentionally postponed until now, |
| ... | ... | @@ -3040,11 +3182,22 @@ fn flush( |
| 3040 | 3182 | ) !void { |
| 3041 | 3183 | if (comp.zcu) |zcu| { |
| 3042 | 3184 | if (zcu.llvm_object) |llvm_object| { |
| 3185 | const pt: Zcu.PerThread = .activate(zcu, tid); | |
| 3186 | defer pt.deactivate(); | |
| 3187 | ||
| 3043 | 3188 | // Emit the ZCU object from LLVM now; it's required to flush the output file. |
| 3044 | 3189 | // If there's an output file, it wants to decide where the LLVM object goes! |
| 3045 | 3190 | const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0); |
| 3046 | 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 | 3201 | .pre_ir_path = comp.verbose_llvm_ir, |
| 3049 | 3202 | .pre_bc_path = comp.verbose_llvm_bc, |
| 3050 | 3203 | |
| ... | ... | @@ -3071,7 +3224,7 @@ fn flush( |
| 3071 | 3224 | |
| 3072 | 3225 | .is_debug = comp.root_mod.optimize_mode == .Debug, |
| 3073 | 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 | 3228 | .sanitize_thread = comp.config.any_sanitize_thread, |
| 3076 | 3229 | .fuzz = comp.config.any_fuzz, |
| 3077 | 3230 | .lto = comp.config.lto, |
| ... | ... | @@ -3079,6 +3232,12 @@ fn flush( |
| 3079 | 3232 | } |
| 3080 | 3233 | } |
| 3081 | 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 | 3241 | // This is needed before reading the error flags. |
| 3083 | 3242 | lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { |
| 3084 | 3243 | error.LinkFailure => {}, // Already reported. |
| ... | ... | @@ -4223,6 +4382,17 @@ fn performAllTheWork( |
| 4223 | 4382 | zcu.generation += 1; |
| 4224 | 4383 | }; |
| 4225 | 4384 | |
| 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 | 4396 | // Here we queue up all the AstGen tasks first, followed by C object compilation. |
| 4227 | 4397 | // We wait until the AstGen tasks are all completed before proceeding to the |
| 4228 | 4398 | // (at least for now) single-threaded main work queue. However, C object compilation |
| ... | ... | @@ -4431,6 +4601,13 @@ fn performAllTheWork( |
| 4431 | 4601 | const zir_prog_node = main_progress_node.start("AST Lowering", 0); |
| 4432 | 4602 | defer zir_prog_node.end(); |
| 4433 | 4603 | |
| 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 | 4611 | var astgen_wait_group: WaitGroup = .{}; |
| 4435 | 4612 | defer astgen_wait_group.wait(); |
| 4436 | 4613 | |
| ... | ... | @@ -4556,6 +4733,10 @@ fn performAllTheWork( |
| 4556 | 4733 | return; |
| 4557 | 4734 | } |
| 4558 | 4735 | |
| 4736 | if (comp.time_report) |*tr| { | |
| 4737 | tr.stats.n_reachable_files = @intCast(zcu.alive_files.count()); | |
| 4738 | } | |
| 4739 | ||
| 4559 | 4740 | if (comp.incremental) { |
| 4560 | 4741 | const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0); |
| 4561 | 4742 | defer update_zir_refs_node.end(); |
| ... | ... | @@ -4599,6 +4780,11 @@ fn performAllTheWork( |
| 4599 | 4780 | } |
| 4600 | 4781 | } |
| 4601 | 4782 | |
| 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 | 4788 | work: while (true) { |
| 4603 | 4789 | for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| { |
| 4604 | 4790 | try processOneJob(@intFromEnum(Zcu.PerThread.Id.main), comp, job); |
src/Sema.zig+25-15| ... | ... | @@ -3246,21 +3246,25 @@ fn zirEnumDecl( |
| 3246 | 3246 | wip_ty.prepare(ip, new_namespace_index); |
| 3247 | 3247 | done = true; |
| 3248 | 3248 | |
| 3249 | try Sema.resolveDeclaredEnum( | |
| 3250 | pt, | |
| 3251 | wip_ty, | |
| 3252 | inst, | |
| 3253 | tracked_inst, | |
| 3254 | new_namespace_index, | |
| 3255 | type_name.name, | |
| 3256 | small, | |
| 3257 | body, | |
| 3258 | tag_type_ref, | |
| 3259 | any_values, | |
| 3260 | fields_len, | |
| 3261 | sema.code, | |
| 3262 | body_end, | |
| 3263 | ); | |
| 3249 | { | |
| 3250 | const tracked_unit = zcu.trackUnitSema(type_name.name.toSlice(ip), null); | |
| 3251 | defer tracked_unit.end(zcu); | |
| 3252 | try Sema.resolveDeclaredEnum( | |
| 3253 | pt, | |
| 3254 | wip_ty, | |
| 3255 | inst, | |
| 3256 | tracked_inst, | |
| 3257 | new_namespace_index, | |
| 3258 | type_name.name, | |
| 3259 | small, | |
| 3260 | body, | |
| 3261 | tag_type_ref, | |
| 3262 | any_values, | |
| 3263 | fields_len, | |
| 3264 | sema.code, | |
| 3265 | body_end, | |
| 3266 | ); | |
| 3267 | } | |
| 3264 | 3268 | |
| 3265 | 3269 | codegen_type: { |
| 3266 | 3270 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| ... | ... | @@ -7577,6 +7581,12 @@ fn analyzeCall( |
| 7577 | 7581 | |
| 7578 | 7582 | // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`. |
| 7579 | 7583 | |
| 7584 | if (zcu.comp.time_report) |*tr| { | |
| 7585 | if (!block.isComptime()) { | |
| 7586 | tr.stats.n_inline_calls += 1; | |
| 7587 | } | |
| 7588 | } | |
| 7589 | ||
| 7580 | 7590 | if (func_ty_info.is_noinline and !block.isComptime()) { |
| 7581 | 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 | 3794 | return error.AnalysisFail; |
| 3795 | 3795 | } |
| 3796 | 3796 | |
| 3797 | const tracked_unit = zcu.trackUnitSema(struct_obj.name.toSlice(&zcu.intern_pool), null); | |
| 3798 | defer tracked_unit.end(zcu); | |
| 3799 | ||
| 3797 | 3800 | if (zcu.comp.debugIncremental()) { |
| 3798 | 3801 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner); |
| 3799 | 3802 | info.last_update_gen = zcu.generation; |
| ... | ... | @@ -3853,6 +3856,9 @@ fn resolveUnionInner( |
| 3853 | 3856 | return error.AnalysisFail; |
| 3854 | 3857 | } |
| 3855 | 3858 | |
| 3859 | const tracked_unit = zcu.trackUnitSema(union_obj.name.toSlice(&zcu.intern_pool), null); | |
| 3860 | defer tracked_unit.end(zcu); | |
| 3861 | ||
| 3856 | 3862 | if (zcu.comp.debugIncremental()) { |
| 3857 | 3863 | const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner); |
| 3858 | 3864 | info.last_update_gen = zcu.generation; |
src/Zcu.zig+44-10| ... | ... | @@ -312,6 +312,10 @@ builtin_decl_values: BuiltinDecl.Memoized = .initFill(.none), |
| 312 | 312 | incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void = |
| 313 | 313 | if (build_options.enable_debug_extensions) .init else {}, |
| 314 | 314 | |
| 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. | |
| 317 | cur_analysis_timer: ?Compilation.Timer = null, | |
| 318 | ||
| 315 | 319 | generation: u32 = 0, |
| 316 | 320 | |
| 317 | 321 | pub const IncrementalDebugState = struct { |
| ... | ... | @@ -4683,26 +4687,56 @@ fn explainWhyFileIsInModule( |
| 4683 | 4687 | } |
| 4684 | 4688 | } |
| 4685 | 4689 | |
| 4686 | const SemaProgNode = struct { | |
| 4690 | const TrackedUnitSema = struct { | |
| 4687 | 4691 | /// `null` means we created the node, so should end it. |
| 4688 | 4692 | old_name: ?[std.Progress.Node.max_name_len]u8, |
| 4689 | pub fn end(spn: SemaProgNode, zcu: *Zcu) void { | |
| 4690 | if (spn.old_name) |old_name| { | |
| 4693 | old_analysis_timer: ?Compilation.Timer, | |
| 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 | 4698 | zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion |
| 4692 | 4699 | zcu.cur_sema_prog_node.setName(&old_name); |
| 4693 | 4700 | } else { |
| 4694 | 4701 | zcu.cur_sema_prog_node.end(); |
| 4695 | 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 | }; |
| 4699 | pub fn startSemaProgNode(zcu: *Zcu, name: []const u8) SemaProgNode { | |
| 4700 | if (zcu.cur_sema_prog_node.index != .none) { | |
| 4724 | pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedInst.Index) TrackedUnitSema { | |
| 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 | 4733 | const old_name = zcu.cur_sema_prog_node.getName(); |
| 4702 | 4734 | zcu.cur_sema_prog_node.setName(name); |
| 4703 | return .{ .old_name = old_name }; | |
| 4704 | } else { | |
| 4705 | zcu.cur_sema_prog_node = zcu.sema_prog_node.start(name, 0); | |
| 4706 | return .{ .old_name = null }; | |
| 4707 | } | |
| 4735 | break :old_name old_name; | |
| 4736 | }; | |
| 4737 | return .{ | |
| 4738 | .old_name = old_name, | |
| 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 | 215 | }; |
| 216 | 216 | defer cache_file.close(); |
| 217 | 217 | |
| 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 | 221 | const need_update = while (true) { |
| 219 | 222 | const result = switch (file.getMode()) { |
| 220 | 223 | inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode), |
| 221 | 224 | }; |
| 222 | 225 | switch (result) { |
| 223 | .success => { | |
| 226 | .success => if (!ignore_hit) { | |
| 224 | 227 | log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)}); |
| 225 | 228 | break false; |
| 226 | 229 | }, |
| ... | ... | @@ -260,9 +263,16 @@ pub fn updateFile( |
| 260 | 263 | |
| 261 | 264 | file.source = source; |
| 262 | 265 | |
| 266 | var timer = comp.startTimer(); | |
| 263 | 267 | // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen. |
| 264 | 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 | } | |
| 265 | 274 | |
| 275 | timer = comp.startTimer(); | |
| 266 | 276 | switch (file.getMode()) { |
| 267 | 277 | .zig => { |
| 268 | 278 | file.zir = try AstGen.generate(gpa, file.tree.?); |
| ... | ... | @@ -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 | } | |
| 285 | 300 | |
| 286 | 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 | 816 | info.deps.clearRetainingCapacity(); |
| 802 | 817 | } |
| 803 | 818 | |
| 804 | const unit_prog_node = zcu.startSemaProgNode("comptime"); | |
| 805 | defer unit_prog_node.end(zcu); | |
| 819 | const unit_tracking = zcu.trackUnitSema( | |
| 820 | "comptime", | |
| 821 | zcu.intern_pool.getComptimeUnit(cu_id).zir_index, | |
| 822 | ); | |
| 823 | defer unit_tracking.end(zcu); | |
| 806 | 824 | |
| 807 | 825 | return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) { |
| 808 | 826 | error.AnalysisFail => { |
| ... | ... | @@ -981,8 +999,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu |
| 981 | 999 | info.deps.clearRetainingCapacity(); |
| 982 | 1000 | } |
| 983 | 1001 | |
| 984 | const unit_prog_node = zcu.startSemaProgNode(nav.fqn.toSlice(ip)); | |
| 985 | defer unit_prog_node.end(zcu); | |
| 1002 | const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip)); | |
| 1003 | defer unit_tracking.end(zcu); | |
| 986 | 1004 | |
| 987 | 1005 | const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: { |
| 988 | 1006 | break :res .{ |
| ... | ... | @@ -1381,8 +1399,8 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc |
| 1381 | 1399 | info.deps.clearRetainingCapacity(); |
| 1382 | 1400 | } |
| 1383 | 1401 | |
| 1384 | const unit_prog_node = zcu.startSemaProgNode(nav.fqn.toSlice(ip)); | |
| 1385 | defer unit_prog_node.end(zcu); | |
| 1402 | const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip)); | |
| 1403 | defer unit_tracking.end(zcu); | |
| 1386 | 1404 | |
| 1387 | 1405 | const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: { |
| 1388 | 1406 | break :res .{ |
| ... | ... | @@ -1601,8 +1619,12 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 1601 | 1619 | info.deps.clearRetainingCapacity(); |
| 1602 | 1620 | } |
| 1603 | 1621 | |
| 1604 | const func_prog_node = zcu.startSemaProgNode(ip.getNav(func.owner_nav).fqn.toSlice(ip)); | |
| 1605 | defer func_prog_node.end(zcu); | |
| 1622 | const owner_nav = ip.getNav(func.owner_nav); | |
| 1623 | const unit_tracking = zcu.trackUnitSema( | |
| 1624 | owner_nav.fqn.toSlice(ip), | |
| 1625 | owner_nav.srcInst(ip), | |
| 1626 | ); | |
| 1627 | defer unit_tracking.end(zcu); | |
| 1606 | 1628 | |
| 1607 | 1629 | const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result| |
| 1608 | 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 | 1869 | }); |
| 1848 | 1870 | const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false); |
| 1849 | 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 | } |
| 1851 | 1877 | |
| 1852 | 1878 | /// 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 | 2546 | const gpa = zcu.gpa; |
| 2521 | 2547 | const namespace = zcu.namespacePtr(namespace_index); |
| 2522 | 2548 | |
| 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 | 2555 | // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather |
| 2524 | 2556 | // than their name. We'll build an efficient mapping now, then discard the current `decls`. |
| 2525 | 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 | 2787 | func.setResolvedErrorSet(ip, .none); |
| 2756 | 2788 | } |
| 2757 | 2789 | |
| 2790 | if (zcu.comp.time_report) |*tr| { | |
| 2791 | if (func.generic_owner != .none) { | |
| 2792 | tr.stats.n_generic_instances += 1; | |
| 2793 | } | |
| 2794 | } | |
| 2795 | ||
| 2758 | 2796 | // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from. |
| 2759 | 2797 | const decl_nav = ip.getNav(if (func.generic_owner == .none) |
| 2760 | 2798 | func.owner_nav |
| ... | ... | @@ -4307,6 +4345,9 @@ pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dep |
| 4307 | 4345 | /// codegen thread, depending on whether the backend supports `Zcu.Feature.separate_thread`. |
| 4308 | 4346 | pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, out: *@import("../link.zig").ZcuTask.LinkFunc.SharedMir) void { |
| 4309 | 4347 | const zcu = pt.zcu; |
| 4348 | ||
| 4349 | var timer = zcu.comp.startTimer(); | |
| 4350 | ||
| 4310 | 4351 | const success: bool = if (runCodegenInner(pt, func_index, air)) |mir| success: { |
| 4311 | 4352 | out.value = mir; |
| 4312 | 4353 | break :success true; |
| ... | ... | @@ -4327,6 +4368,25 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air, ou |
| 4327 | 4368 | } |
| 4328 | 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 | 4390 | // release `out.value` with this store; synchronizes with acquire loads in `link` |
| 4331 | 4391 | out.status.store(if (success) .ready else .failed, .release); |
| 4332 | 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 | 764 | |
| 765 | 765 | is_debug: bool, |
| 766 | 766 | is_small: bool, |
| 767 | time_report: bool, | |
| 767 | time_report: ?*Compilation.TimeReport, | |
| 768 | 768 | sanitize_thread: bool, |
| 769 | 769 | fuzz: bool, |
| 770 | 770 | lto: std.zig.LtoMode, |
| ... | ... | @@ -1063,7 +1063,7 @@ pub const Object = struct { |
| 1063 | 1063 | var lowered_options: llvm.TargetMachine.EmitOptions = .{ |
| 1064 | 1064 | .is_debug = options.is_debug, |
| 1065 | 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 | 1067 | .tsan = options.sanitize_thread, |
| 1068 | 1068 | .lto = switch (options.lto) { |
| 1069 | 1069 | .none => .None, |
| ... | ... | @@ -1118,6 +1118,11 @@ pub const Object = struct { |
| 1118 | 1118 | lowered_options.llvm_ir_filename = null; |
| 1119 | 1119 | } |
| 1120 | 1120 | |
| 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 | 1126 | lowered_options.asm_filename = options.asm_path; |
| 1122 | 1127 | if (target_machine.emitToFile(module, &error_message, &lowered_options)) { |
| 1123 | 1128 | defer llvm.disposeMessage(error_message); |
| ... | ... | @@ -1125,6 +1130,12 @@ pub const Object = struct { |
| 1125 | 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 | } |
| 1129 | 1140 | |
| 1130 | 1141 | pub fn updateFunc( |
src/codegen/llvm/bindings.zig+1-1| ... | ... | @@ -88,7 +88,7 @@ pub const TargetMachine = opaque { |
| 88 | 88 | pub const EmitOptions = extern struct { |
| 89 | 89 | is_debug: bool, |
| 90 | 90 | is_small: bool, |
| 91 | time_report: bool, | |
| 91 | time_report_out: ?*[*:0]u8, | |
| 92 | 92 | tsan: bool, |
| 93 | 93 | sancov: bool, |
| 94 | 94 | lto: LtoPhase, |
src/link.zig+33| ... | ... | @@ -1311,6 +1311,14 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { |
| 1311 | 1311 | comp.link_prog_node.completeOne(); |
| 1312 | 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 | 1322 | switch (task) { |
| 1315 | 1323 | .load_explicitly_provided => { |
| 1316 | 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 | 1445 | const ip = &zcu.intern_pool; |
| 1438 | 1446 | const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); |
| 1439 | 1447 | defer pt.deactivate(); |
| 1448 | ||
| 1449 | var timer = comp.startTimer(); | |
| 1450 | ||
| 1440 | 1451 | switch (task) { |
| 1441 | 1452 | .link_nav => |nav_index| { |
| 1442 | 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 | 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 | 1548 | /// After the main pipeline is done, but before flush, the compilation may need to link one final |
| 1516 | 1549 | /// `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 | 484 | \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow |
| 485 | 485 | \\ -mexec-model=[value] (WASI) Execution model |
| 486 | 486 | \\ -municode (Windows) Use wmain/wWinMain as entry point |
| 487 | \\ --time-report Send timing diagnostics to '--listen' clients | |
| 487 | 488 | \\ |
| 488 | 489 | \\Per-Module Compile Options: |
| 489 | 490 | \\ -target [name] <arch><sub>-<os>-<abi> see the targets command |
| ... | ... | @@ -678,7 +679,6 @@ const usage_build_generic = |
| 678 | 679 | \\ |
| 679 | 680 | \\Debug Options (Zig Compiler Development): |
| 680 | 681 | \\ -fopt-bisect-limit=[limit] Only run [limit] first LLVM optimization passes |
| 681 | \\ -ftime-report Print timing diagnostics | |
| 682 | 682 | \\ -fstack-report Print stack size diagnostics |
| 683 | 683 | \\ --verbose-link Display linker invocations |
| 684 | 684 | \\ --verbose-cc Display C compiler invocations |
| ... | ... | @@ -1403,7 +1403,7 @@ fn buildOutputType( |
| 1403 | 1403 | try test_exec_args.append(arena, null); |
| 1404 | 1404 | } else if (mem.eql(u8, arg, "--test-no-exec")) { |
| 1405 | 1405 | test_no_exec = true; |
| 1406 | } else if (mem.eql(u8, arg, "-ftime-report")) { | |
| 1406 | } else if (mem.eql(u8, arg, "--time-report")) { | |
| 1407 | 1407 | time_report = true; |
| 1408 | 1408 | } else if (mem.eql(u8, arg, "-fstack-report")) { |
| 1409 | 1409 | stack_report = true; |
| ... | ... | @@ -2899,6 +2899,10 @@ fn buildOutputType( |
| 2899 | 2899 | fatal("test-obj requires --test-no-exec", .{}); |
| 2900 | 2900 | } |
| 2901 | 2901 | |
| 2902 | if (time_report and listen == .none) { | |
| 2903 | fatal("--time-report requires --listen", .{}); | |
| 2904 | } | |
| 2905 | ||
| 2902 | 2906 | if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) { |
| 2903 | 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 | 4212 | } |
| 4209 | 4213 | } |
| 4210 | 4214 | |
| 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 | 4293 | if (error_bundle.errorMessageCount() > 0) { |
| 4212 | 4294 | try s.serveErrorBundle(error_bundle); |
| 4213 | 4295 | return; |
| ... | ... | @@ -5277,7 +5359,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5277 | 5359 | var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; |
| 5278 | 5360 | |
| 5279 | 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 | } |
| 5282 | 5366 | |
| 5283 | 5367 | const comp = Compilation.create(gpa, arena, .{ |
src/zig_llvm.cpp+11-4| ... | ... | @@ -220,7 +220,7 @@ static SanitizerCoverageOptions getSanCovOptions(ZigLLVMCoverageOptions z) { |
| 220 | 220 | ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, |
| 221 | 221 | char **error_message, const ZigLLVMEmitOptions *options) |
| 222 | 222 | { |
| 223 | TimePassesIsEnabled = options->time_report; | |
| 223 | TimePassesIsEnabled = options->time_report_out != nullptr; | |
| 224 | 224 | |
| 225 | 225 | raw_fd_ostream *dest_asm_ptr = nullptr; |
| 226 | 226 | raw_fd_ostream *dest_bin_ptr = nullptr; |
| ... | ... | @@ -418,10 +418,17 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi |
| 418 | 418 | WriteBitcodeToFile(llvm_module, *dest_bitcode); |
| 419 | 419 | } |
| 420 | 420 | |
| 421 | if (options->time_report) { | |
| 422 | TimerGroup::printAll(errs()); | |
| 421 | // This must only happen once we know we've succeeded and will be returning `false`, because | |
| 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 | 432 | return false; |
| 426 | 433 | } |
| 427 | 434 |
src/zig_llvm.h+4-1| ... | ... | @@ -66,7 +66,10 @@ enum ZigLLVMThinOrFullLTOPhase { |
| 66 | 66 | struct ZigLLVMEmitOptions { |
| 67 | 67 | bool is_debug; |
| 68 | 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 | 73 | bool tsan; |
| 71 | 74 | bool sancov; |
| 72 | 75 | ZigLLVMThinOrFullLTOPhase lto; |
tools/dump-cov.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ const std = @import("std"); |
| 5 | 5 | const fatal = std.process.fatal; |
| 6 | 6 | const Path = std.Build.Cache.Path; |
| 7 | 7 | const assert = std.debug.assert; |
| 8 | const SeenPcsHeader = std.Build.Fuzz.abi.SeenPcsHeader; | |
| 8 | const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader; | |
| 9 | 9 | |
| 10 | 10 | pub fn main() !void { |
| 11 | 11 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; |