authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-08 21:44:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-08 21:46:36-07:00
logf6f1ecf0f9227bee33b963a6ff5dfc3001ad2c46
tree0995711a5355f2526ab4455909892a8107e500be
parent4e32edbff5d34dfc779797d73bb61d32ffa02953

more optimized and correct management of 8-bit PC counters

* Upgrade from u8 to usize element types. - WebAssembly assumes u64. It should probably try to be target-aware instead. * Move the covered PC bits to after the header so it goes on the same page with the other rapidly changing memory (the header stats). depends on the semantics of accepted proposal #19755 closes #20994

5 files changed, 107 insertions(+), 77 deletions(-)

lib/fuzzer.zig+31-26
...@@ -213,11 +213,12 @@ const Fuzzer = struct {...@@ -213,11 +213,12 @@ const Fuzzer = struct {
213 .truncate = false,213 .truncate = false,
214 });214 });
215 defer coverage_file.close();215 defer coverage_file.close();
216 const n_bitset_elems = (flagged_pcs.len + 7) / 8;216 const n_bitset_elems = (flagged_pcs.len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
217 comptime assert(SeenPcsHeader.trailing[0] == .pc_addr);217 comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
218 comptime assert(SeenPcsHeader.trailing[1][0] == .pc_bits);218 comptime assert(SeenPcsHeader.trailing[1] == .pc_addr);
219 comptime assert(SeenPcsHeader.trailing[1][1] == u8);219 const bytes_len = @sizeOf(SeenPcsHeader) +
220 const bytes_len = @sizeOf(SeenPcsHeader) + flagged_pcs.len * @sizeOf(usize) + n_bitset_elems;220 n_bitset_elems * @sizeOf(usize) +
221 flagged_pcs.len * @sizeOf(usize);
221 const existing_len = coverage_file.getEndPos() catch |err| {222 const existing_len = coverage_file.getEndPos() catch |err| {
222 fatal("unable to check len of coverage file: {s}", .{@errorName(err)});223 fatal("unable to check len of coverage file: {s}", .{@errorName(err)});
223 };224 };
...@@ -232,7 +233,7 @@ const Fuzzer = struct {...@@ -232,7 +233,7 @@ const Fuzzer = struct {
232 fatal("unable to init coverage memory map: {s}", .{@errorName(err)});233 fatal("unable to init coverage memory map: {s}", .{@errorName(err)});
233 };234 };
234 if (existing_len != 0) {235 if (existing_len != 0) {
235 const existing_pcs_bytes = f.seen_pcs.items[@sizeOf(SeenPcsHeader)..][0 .. flagged_pcs.len * @sizeOf(usize)];236 const existing_pcs_bytes = f.seen_pcs.items[@sizeOf(SeenPcsHeader) + @sizeOf(usize) * n_bitset_elems ..][0 .. flagged_pcs.len * @sizeOf(usize)];
236 const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);237 const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);
237 for (existing_pcs, flagged_pcs, 0..) |old, new, i| {238 for (existing_pcs, flagged_pcs, 0..) |old, new, i| {
238 if (old != new.addr) {239 if (old != new.addr) {
...@@ -249,10 +250,10 @@ const Fuzzer = struct {...@@ -249,10 +250,10 @@ const Fuzzer = struct {
249 .lowest_stack = std.math.maxInt(usize),250 .lowest_stack = std.math.maxInt(usize),
250 };251 };
251 f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));252 f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));
253 f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize));
252 for (flagged_pcs) |flagged_pc| {254 for (flagged_pcs) |flagged_pc| {
253 f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&flagged_pc.addr));255 f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&flagged_pc.addr));
254 }256 }
255 f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems);
256 }257 }
257 }258 }
258259
...@@ -302,26 +303,30 @@ const Fuzzer = struct {...@@ -302,26 +303,30 @@ const Fuzzer = struct {
302 .score = analysis.score,303 .score = analysis.score,
303 };304 };
304305
305 // Track code coverage from all runs.
306 {306 {
307 comptime assert(SeenPcsHeader.trailing[0] == .pc_addr);307 // Track code coverage from all runs.
308 comptime assert(SeenPcsHeader.trailing[1][0] == .pc_bits);308 comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
309 comptime assert(SeenPcsHeader.trailing[1][1] == u8);309 const header_end_ptr: [*]volatile usize = @ptrCast(f.seen_pcs.items[@sizeOf(SeenPcsHeader)..]);
310310 const remainder = f.flagged_pcs.len % @bitSizeOf(usize);
311 const seen_pcs = f.seen_pcs.items[@sizeOf(SeenPcsHeader) + f.flagged_pcs.len * @sizeOf(usize) ..];311 const aligned_len = f.flagged_pcs.len - remainder;
312 for (seen_pcs, 0..) |*elem, i| {312 const seen_pcs = header_end_ptr[0..aligned_len];
313 const byte_i = i * 8;313 const pc_counters = std.mem.bytesAsSlice([@bitSizeOf(usize)]u8, f.pc_counters[0..aligned_len]);
314 const mask: u8 =314 const V = @Vector(@bitSizeOf(usize), u8);
315 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 0] != 0)) << 0) |315 const zero_v: V = @splat(0);
316 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 1] != 0)) << 1) |316
317 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 2] != 0)) << 2) |317 for (header_end_ptr[0..pc_counters.len], pc_counters) |*elem, *array| {
318 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 3] != 0)) << 3) |318 const v: V = array.*;
319 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 4] != 0)) << 4) |319 const mask: usize = @bitCast(v != zero_v);
320 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 5] != 0)) << 5) |320 _ = @atomicRmw(usize, elem, .Or, mask, .monotonic);
321 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 6] != 0)) << 6) |321 }
322 (@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 7] != 0)) << 7);322 if (remainder > 0) {
323323 const i = pc_counters.len;
324 _ = @atomicRmw(u8, elem, .Or, mask, .monotonic);324 const elem = &seen_pcs[i];
325 var mask: usize = 0;
326 for (f.pc_counters[i * @bitSizeOf(usize) ..][0..remainder], 0..) |byte, bit_index| {
327 mask |= @as(usize, @intFromBool(byte != 0)) << @intCast(bit_index);
328 }
329 _ = @atomicRmw(usize, elem, .Or, mask, .monotonic);
325 }330 }
326 }331 }
327332
lib/fuzzer/wasm/main.zig+20-16
...@@ -125,12 +125,12 @@ export fn coveredSourceLocations() usize {...@@ -125,12 +125,12 @@ export fn coveredSourceLocations() usize {
125}125}
126126
127export fn totalRuns() u64 {127export fn totalRuns() u64 {
128 const header: *abi.CoverageUpdateHeader = @ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]);128 const header: *abi.CoverageUpdateHeader = @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
129 return header.n_runs;129 return header.n_runs;
130}130}
131131
132export fn uniqueRuns() u64 {132export fn uniqueRuns() u64 {
133 const header: *abi.CoverageUpdateHeader = @ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]);133 const header: *abi.CoverageUpdateHeader = @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
134 return header.unique_runs;134 return header.unique_runs;
135}135}
136136
...@@ -335,7 +335,7 @@ fn computeSourceAnnotations(...@@ -335,7 +335,7 @@ fn computeSourceAnnotations(
335 if (next_loc_index >= locs.items.len) return;335 if (next_loc_index >= locs.items.len) return;
336 const next_sli = locs.items[next_loc_index];336 const next_sli = locs.items[next_loc_index];
337 const next_sl = next_sli.ptr();337 const next_sl = next_sli.ptr();
338 if (next_sl.line > line or (next_sl.line == line and next_sl.column > column)) break;338 if (next_sl.line > line or (next_sl.line == line and next_sl.column >= column)) break;
339 try annotations.append(gpa, .{339 try annotations.append(gpa, .{
340 .file_byte_offset = offset,340 .file_byte_offset = offset,
341 .dom_id = @intFromEnum(next_sli),341 .dom_id = @intFromEnum(next_sli),
...@@ -349,7 +349,7 @@ var coverage = Coverage.init;...@@ -349,7 +349,7 @@ var coverage = Coverage.init;
349/// Index of type `SourceLocationIndex`.349/// Index of type `SourceLocationIndex`.
350var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{};350var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{};
351/// Contains the most recent coverage update message, unmodified.351/// Contains the most recent coverage update message, unmodified.
352var recent_coverage_update: std.ArrayListUnmanaged(u8) = .{};352var recent_coverage_update: std.ArrayListAlignedUnmanaged(u8, @alignOf(u64)) = .{};
353353
354fn updateCoverage(354fn updateCoverage(
355 directories: []const Coverage.String,355 directories: []const Coverage.String,
...@@ -406,19 +406,23 @@ export fn sourceLocationFileCoveredList(sli_file: SourceLocationIndex) Slice(Sou...@@ -406,19 +406,23 @@ export fn sourceLocationFileCoveredList(sli_file: SourceLocationIndex) Slice(Sou
406 };406 };
407 const want_file = sli_file.ptr().file;407 const want_file = sli_file.ptr().file;
408 global.result.clearRetainingCapacity();408 global.result.clearRetainingCapacity();
409 const covered_bits = recent_coverage_update.items[@sizeOf(abi.CoverageUpdateHeader)..];409
410 // This code assumes 64-bit elements, which is incorrect if the executable
411 // being fuzzed is not a 64-bit CPU. It also assumes little-endian which
412 // can also be incorrect.
413 comptime assert(abi.CoverageUpdateHeader.trailing[0] == .pc_bits_usize);
414 const n_bitset_elems = (coverage_source_locations.items.len + @bitSizeOf(u64) - 1) / @bitSizeOf(u64);
415 const covered_bits = std.mem.bytesAsSlice(
416 u64,
417 recent_coverage_update.items[@sizeOf(abi.CoverageUpdateHeader)..][0 .. n_bitset_elems * @sizeOf(u64)],
418 );
410 var sli: u32 = 0;419 var sli: u32 = 0;
411 for (covered_bits) |byte| {420 for (covered_bits) |elem| {
412 global.result.ensureUnusedCapacity(gpa, 8) catch @panic("OOM");421 global.result.ensureUnusedCapacity(gpa, 64) catch @panic("OOM");
413 if ((byte & 0b0000_0001) != 0) global.add(sli + 0, want_file);422 for (0..@bitSizeOf(u64)) |i| {
414 if ((byte & 0b0000_0010) != 0) global.add(sli + 1, want_file);423 if ((elem & (@as(u64, 1) << @intCast(i))) != 0) global.add(sli, want_file);
415 if ((byte & 0b0000_0100) != 0) global.add(sli + 2, want_file);424 sli += 1;
416 if ((byte & 0b0000_1000) != 0) global.add(sli + 3, want_file);425 }
417 if ((byte & 0b0001_0000) != 0) global.add(sli + 4, want_file);
418 if ((byte & 0b0010_0000) != 0) global.add(sli + 5, want_file);
419 if ((byte & 0b0100_0000) != 0) global.add(sli + 6, want_file);
420 if ((byte & 0b1000_0000) != 0) global.add(sli + 7, want_file);
421 sli += 8;
422 }426 }
423 return Slice(SourceLocationIndex).init(global.result.items);427 return Slice(SourceLocationIndex).init(global.result.items);
424}428}
lib/std/Build/Fuzz/WebServer.zig+5-12
...@@ -384,10 +384,7 @@ fn sendCoverageContext(...@@ -384,10 +384,7 @@ fn sendCoverageContext(
384 // TODO: make each events URL correspond to one coverage map384 // TODO: make each events URL correspond to one coverage map
385 const coverage_map = &coverage_maps[0];385 const coverage_map = &coverage_maps[0];
386 const cov_header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);386 const cov_header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
387 comptime assert(abi.SeenPcsHeader.trailing[0] == .pc_addr);387 const seen_pcs = cov_header.seenBits();
388 const seen_pcs = coverage_map.mapped_memory[@sizeOf(abi.SeenPcsHeader) + coverage_map.source_locations.len * @sizeOf(usize) ..];
389 comptime assert(abi.SeenPcsHeader.trailing[1][0] == .pc_bits);
390 comptime assert(abi.SeenPcsHeader.trailing[1][1] == u8);
391 const n_runs = @atomicLoad(usize, &cov_header.n_runs, .monotonic);388 const n_runs = @atomicLoad(usize, &cov_header.n_runs, .monotonic);
392 const unique_runs = @atomicLoad(usize, &cov_header.unique_runs, .monotonic);389 const unique_runs = @atomicLoad(usize, &cov_header.unique_runs, .monotonic);
393 const lowest_stack = @atomicLoad(usize, &cov_header.lowest_stack, .monotonic);390 const lowest_stack = @atomicLoad(usize, &cov_header.lowest_stack, .monotonic);
...@@ -419,7 +416,7 @@ fn sendCoverageContext(...@@ -419,7 +416,7 @@ fn sendCoverageContext(
419 };416 };
420 const iovecs: [2]std.posix.iovec_const = .{417 const iovecs: [2]std.posix.iovec_const = .{
421 makeIov(std.mem.asBytes(&header)),418 makeIov(std.mem.asBytes(&header)),
422 makeIov(seen_pcs),419 makeIov(std.mem.sliceAsBytes(seen_pcs)),
423 };420 };
424 try web_socket.writeMessagev(&iovecs, .binary);421 try web_socket.writeMessagev(&iovecs, .binary);
425422
...@@ -634,9 +631,7 @@ fn prepareTables(...@@ -634,9 +631,7 @@ fn prepareTables(
634 gop.value_ptr.mapped_memory = mapped_memory;631 gop.value_ptr.mapped_memory = mapped_memory;
635632
636 const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);633 const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
637 comptime assert(abi.SeenPcsHeader.trailing[0] == .pc_addr);634 const pcs = header.pcAddrs();
638 const pcs_bytes = mapped_memory[@sizeOf(abi.SeenPcsHeader)..][0 .. header.pcs_len * @sizeOf(usize)];
639 const pcs = std.mem.bytesAsSlice(usize, pcs_bytes);
640 const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);635 const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
641 errdefer gpa.free(source_locations);636 errdefer gpa.free(source_locations);
642 debug_info.resolveAddresses(gpa, pcs, source_locations) catch |err| {637 debug_info.resolveAddresses(gpa, pcs, source_locations) catch |err| {
...@@ -653,10 +648,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo...@@ -653,10 +648,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo
653 defer ws.coverage_mutex.unlock();648 defer ws.coverage_mutex.unlock();
654649
655 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;650 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;
656 const ptr = coverage_map.mapped_memory;651 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
657 comptime assert(abi.SeenPcsHeader.trailing[0] == .pc_addr);652 const pcs = header.pcAddrs();
658 const pcs_bytes = ptr[@sizeOf(abi.SeenPcsHeader)..][0 .. coverage_map.source_locations.len * @sizeOf(usize)];
659 const pcs: []const usize = @alignCast(std.mem.bytesAsSlice(usize, pcs_bytes));
660 const index = std.sort.upperBound(usize, pcs, addr, struct {653 const index = std.sort.upperBound(usize, pcs, addr, struct {
661 fn order(context: usize, item: usize) std.math.Order {654 fn order(context: usize, item: usize) std.math.Order {
662 return std.math.order(item, context);655 return std.math.order(item, context);
lib/std/Build/Fuzz/abi.zig+36-7
...@@ -7,8 +7,8 @@...@@ -7,8 +7,8 @@
7/// make the ints be the size of the target used with libfuzzer.7/// make the ints be the size of the target used with libfuzzer.
8///8///
9/// Trailing:9/// Trailing:
10/// * 1 bit per pc_addr, usize elements
10/// * pc_addr: usize for each pcs_len11/// * pc_addr: usize for each pcs_len
11/// * 1 bit per pc_addr, u8 elements
12pub const SeenPcsHeader = extern struct {12pub const SeenPcsHeader = extern struct {
13 n_runs: usize,13 n_runs: usize,
14 unique_runs: usize,14 unique_runs: usize,
...@@ -18,9 +18,29 @@ pub const SeenPcsHeader = extern struct {...@@ -18,9 +18,29 @@ pub const SeenPcsHeader = extern struct {
18 /// Used for comptime assertions. Provides a mechanism for strategically18 /// Used for comptime assertions. Provides a mechanism for strategically
19 /// causing compile errors.19 /// causing compile errors.
20 pub const trailing = .{20 pub const trailing = .{
21 .pc_bits_usize,
21 .pc_addr,22 .pc_addr,
22 .{ .pc_bits, u8 },
23 };23 };
24
25 pub fn headerEnd(header: *const SeenPcsHeader) []const usize {
26 const ptr: [*]align(@alignOf(usize)) const u8 = @ptrCast(header);
27 const header_end_ptr: [*]const usize = @ptrCast(ptr + @sizeOf(SeenPcsHeader));
28 const pcs_len = header.pcs_len;
29 return header_end_ptr[0 .. pcs_len + seenElemsLen(pcs_len)];
30 }
31
32 pub fn seenBits(header: *const SeenPcsHeader) []const usize {
33 return header.headerEnd()[0..seenElemsLen(header.pcs_len)];
34 }
35
36 pub fn seenElemsLen(pcs_len: usize) usize {
37 return (pcs_len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
38 }
39
40 pub fn pcAddrs(header: *const SeenPcsHeader) []const usize {
41 const pcs_len = header.pcs_len;
42 return header.headerEnd()[seenElemsLen(pcs_len)..][0..pcs_len];
43 }
24};44};
2545
26pub const ToClientTag = enum(u8) {46pub const ToClientTag = enum(u8) {
...@@ -54,12 +74,21 @@ pub const SourceIndexHeader = extern struct {...@@ -54,12 +74,21 @@ pub const SourceIndexHeader = extern struct {
54/// changes.74/// changes.
55///75///
56/// Trailing:76/// Trailing:
57/// * one bit per source_locations_len, contained in u8 elements77/// * one bit per source_locations_len, contained in u64 elements
58pub const CoverageUpdateHeader = extern struct {78pub const CoverageUpdateHeader = extern struct {
59 tag: ToClientTag = .coverage_update,79 flags: Flags = .{},
60 n_runs: u64 align(1),80 n_runs: u64,
61 unique_runs: u64 align(1),81 unique_runs: u64,
62 lowest_stack: u64 align(1),82 lowest_stack: u64,
83
84 pub const Flags = packed struct(u64) {
85 tag: ToClientTag = .coverage_update,
86 _: u56 = 0,
87 };
88
89 pub const trailing = .{
90 .pc_bits_usize,
91 };
63};92};
6493
65/// Sent to the fuzzer web client when the set of entry points is updated.94/// Sent to the fuzzer web client when the set of entry points is updated.
tools/dump-cov.zig+15-16
...@@ -5,6 +5,7 @@ const std = @import("std");...@@ -5,6 +5,7 @@ const std = @import("std");
5const fatal = std.process.fatal;5const fatal = std.process.fatal;
6const Path = std.Build.Cache.Path;6const Path = std.Build.Cache.Path;
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const SeenPcsHeader = std.Build.Fuzz.abi.SeenPcsHeader;
89
9pub fn main() !void {10pub fn main() !void {
10 var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};11 var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
...@@ -36,24 +37,29 @@ pub fn main() !void {...@@ -36,24 +37,29 @@ pub fn main() !void {
36 };37 };
37 defer debug_info.deinit(gpa);38 defer debug_info.deinit(gpa);
3839
39 const cov_bytes = cov_path.root_dir.handle.readFileAlloc(arena, cov_path.sub_path, 1 << 30) catch |err| {40 const cov_bytes = cov_path.root_dir.handle.readFileAllocOptions(
41 arena,
42 cov_path.sub_path,
43 1 << 30,
44 null,
45 @alignOf(SeenPcsHeader),
46 null,
47 ) catch |err| {
40 fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) });48 fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) });
41 };49 };
4250
43 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());51 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
44 const stdout = bw.writer();52 const stdout = bw.writer();
4553
46 const header: *align(1) SeenPcsHeader = @ptrCast(cov_bytes);54 const header: *SeenPcsHeader = @ptrCast(cov_bytes);
47 try stdout.print("{any}\n", .{header.*});55 try stdout.print("{any}\n", .{header.*});
48 //const n_bitset_elems = (header.pcs_len + 7) / 8;56 const pcs = header.pcAddrs();
49 const pcs_bytes = cov_bytes[@sizeOf(SeenPcsHeader)..][0 .. header.pcs_len * @sizeOf(usize)];57 for (0.., pcs[0 .. pcs.len - 1], pcs[1..]) |i, a, b| {
50 const pcs = try arena.alloc(usize, header.pcs_len);58 if (a > b) std.log.err("{d}: 0x{x} > 0x{x}", .{ i, a, b });
51 for (0..pcs_bytes.len / @sizeOf(usize), pcs) |i, *pc| {
52 pc.* = std.mem.readInt(usize, pcs_bytes[i * @sizeOf(usize) ..][0..@sizeOf(usize)], .little);
53 }59 }
54 assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize)));60 assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize)));
5561
56 const seen_pcs = cov_bytes[@sizeOf(SeenPcsHeader) + pcs.len * @sizeOf(usize) ..];62 const seen_pcs = header.seenBits();
5763
58 const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, pcs.len);64 const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, pcs.len);
59 try debug_info.resolveAddresses(gpa, pcs, source_locations);65 try debug_info.resolveAddresses(gpa, pcs, source_locations);
...@@ -62,7 +68,7 @@ pub fn main() !void {...@@ -62,7 +68,7 @@ pub fn main() !void {
62 const file = debug_info.coverage.fileAt(sl.file);68 const file = debug_info.coverage.fileAt(sl.file);
63 const dir_name = debug_info.coverage.directories.keys()[file.directory_index];69 const dir_name = debug_info.coverage.directories.keys()[file.directory_index];
64 const dir_name_slice = debug_info.coverage.stringAt(dir_name);70 const dir_name_slice = debug_info.coverage.stringAt(dir_name);
65 const hit: u1 = @truncate(seen_pcs[i / 8] >> @intCast(i % 8));71 const hit: u1 = @truncate(seen_pcs[i / @bitSizeOf(usize)] >> @intCast(i % @bitSizeOf(usize)));
66 try stdout.print("{c}{x}: {s}/{s}:{d}:{d}\n", .{72 try stdout.print("{c}{x}: {s}/{s}:{d}:{d}\n", .{
67 "-+"[hit], pc, dir_name_slice, debug_info.coverage.stringAt(file.basename), sl.line, sl.column,73 "-+"[hit], pc, dir_name_slice, debug_info.coverage.stringAt(file.basename), sl.line, sl.column,
68 });74 });
...@@ -70,10 +76,3 @@ pub fn main() !void {...@@ -70,10 +76,3 @@ pub fn main() !void {
7076
71 try bw.flush();77 try bw.flush();
72}78}
73
74const SeenPcsHeader = extern struct {
75 n_runs: usize,
76 deduplicated_runs: usize,
77 pcs_len: usize,
78 lowest_stack: usize,
79};