mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
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
This commit is contained in:
parent
4e32edbff5
commit
f6f1ecf0f9
@ -213,11 +213,12 @@ const Fuzzer = struct {
|
|||||||
.truncate = false,
|
.truncate = false,
|
||||||
});
|
});
|
||||||
defer coverage_file.close();
|
defer coverage_file.close();
|
||||||
const n_bitset_elems = (flagged_pcs.len + 7) / 8;
|
const n_bitset_elems = (flagged_pcs.len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
|
||||||
comptime assert(SeenPcsHeader.trailing[0] == .pc_addr);
|
comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
|
||||||
comptime assert(SeenPcsHeader.trailing[1][0] == .pc_bits);
|
comptime assert(SeenPcsHeader.trailing[1] == .pc_addr);
|
||||||
comptime assert(SeenPcsHeader.trailing[1][1] == u8);
|
const bytes_len = @sizeOf(SeenPcsHeader) +
|
||||||
const bytes_len = @sizeOf(SeenPcsHeader) + flagged_pcs.len * @sizeOf(usize) + n_bitset_elems;
|
n_bitset_elems * @sizeOf(usize) +
|
||||||
|
flagged_pcs.len * @sizeOf(usize);
|
||||||
const existing_len = coverage_file.getEndPos() catch |err| {
|
const existing_len = coverage_file.getEndPos() catch |err| {
|
||||||
fatal("unable to check len of coverage file: {s}", .{@errorName(err)});
|
fatal("unable to check len of coverage file: {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
@ -232,7 +233,7 @@ const Fuzzer = struct {
|
|||||||
fatal("unable to init coverage memory map: {s}", .{@errorName(err)});
|
fatal("unable to init coverage memory map: {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
if (existing_len != 0) {
|
if (existing_len != 0) {
|
||||||
const existing_pcs_bytes = f.seen_pcs.items[@sizeOf(SeenPcsHeader)..][0 .. flagged_pcs.len * @sizeOf(usize)];
|
const existing_pcs_bytes = f.seen_pcs.items[@sizeOf(SeenPcsHeader) + @sizeOf(usize) * n_bitset_elems ..][0 .. flagged_pcs.len * @sizeOf(usize)];
|
||||||
const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);
|
const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);
|
||||||
for (existing_pcs, flagged_pcs, 0..) |old, new, i| {
|
for (existing_pcs, flagged_pcs, 0..) |old, new, i| {
|
||||||
if (old != new.addr) {
|
if (old != new.addr) {
|
||||||
@ -249,10 +250,10 @@ const Fuzzer = struct {
|
|||||||
.lowest_stack = std.math.maxInt(usize),
|
.lowest_stack = std.math.maxInt(usize),
|
||||||
};
|
};
|
||||||
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));
|
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));
|
||||||
|
f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize));
|
||||||
for (flagged_pcs) |flagged_pc| {
|
for (flagged_pcs) |flagged_pc| {
|
||||||
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&flagged_pc.addr));
|
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&flagged_pc.addr));
|
||||||
}
|
}
|
||||||
f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,26 +303,30 @@ const Fuzzer = struct {
|
|||||||
.score = analysis.score,
|
.score = analysis.score,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Track code coverage from all runs.
|
|
||||||
{
|
{
|
||||||
comptime assert(SeenPcsHeader.trailing[0] == .pc_addr);
|
// Track code coverage from all runs.
|
||||||
comptime assert(SeenPcsHeader.trailing[1][0] == .pc_bits);
|
comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
|
||||||
comptime assert(SeenPcsHeader.trailing[1][1] == u8);
|
const header_end_ptr: [*]volatile usize = @ptrCast(f.seen_pcs.items[@sizeOf(SeenPcsHeader)..]);
|
||||||
|
const remainder = f.flagged_pcs.len % @bitSizeOf(usize);
|
||||||
|
const aligned_len = f.flagged_pcs.len - remainder;
|
||||||
|
const seen_pcs = header_end_ptr[0..aligned_len];
|
||||||
|
const pc_counters = std.mem.bytesAsSlice([@bitSizeOf(usize)]u8, f.pc_counters[0..aligned_len]);
|
||||||
|
const V = @Vector(@bitSizeOf(usize), u8);
|
||||||
|
const zero_v: V = @splat(0);
|
||||||
|
|
||||||
const seen_pcs = f.seen_pcs.items[@sizeOf(SeenPcsHeader) + f.flagged_pcs.len * @sizeOf(usize) ..];
|
for (header_end_ptr[0..pc_counters.len], pc_counters) |*elem, *array| {
|
||||||
for (seen_pcs, 0..) |*elem, i| {
|
const v: V = array.*;
|
||||||
const byte_i = i * 8;
|
const mask: usize = @bitCast(v != zero_v);
|
||||||
const mask: u8 =
|
_ = @atomicRmw(usize, elem, .Or, mask, .monotonic);
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 0] != 0)) << 0) |
|
}
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 1] != 0)) << 1) |
|
if (remainder > 0) {
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 2] != 0)) << 2) |
|
const i = pc_counters.len;
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 3] != 0)) << 3) |
|
const elem = &seen_pcs[i];
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 4] != 0)) << 4) |
|
var mask: usize = 0;
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 5] != 0)) << 5) |
|
for (f.pc_counters[i * @bitSizeOf(usize) ..][0..remainder], 0..) |byte, bit_index| {
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 6] != 0)) << 6) |
|
mask |= @as(usize, @intFromBool(byte != 0)) << @intCast(bit_index);
|
||||||
(@as(u8, @intFromBool(f.pc_counters.ptr[byte_i + 7] != 0)) << 7);
|
}
|
||||||
|
_ = @atomicRmw(usize, elem, .Or, mask, .monotonic);
|
||||||
_ = @atomicRmw(u8, elem, .Or, mask, .monotonic);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -125,12 +125,12 @@ export fn coveredSourceLocations() usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export fn totalRuns() u64 {
|
export fn totalRuns() u64 {
|
||||||
const header: *abi.CoverageUpdateHeader = @ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]);
|
const header: *abi.CoverageUpdateHeader = @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
|
||||||
return header.n_runs;
|
return header.n_runs;
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn uniqueRuns() u64 {
|
export fn uniqueRuns() u64 {
|
||||||
const header: *abi.CoverageUpdateHeader = @ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]);
|
const header: *abi.CoverageUpdateHeader = @alignCast(@ptrCast(recent_coverage_update.items[0..@sizeOf(abi.CoverageUpdateHeader)]));
|
||||||
return header.unique_runs;
|
return header.unique_runs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ fn computeSourceAnnotations(
|
|||||||
if (next_loc_index >= locs.items.len) return;
|
if (next_loc_index >= locs.items.len) return;
|
||||||
const next_sli = locs.items[next_loc_index];
|
const next_sli = locs.items[next_loc_index];
|
||||||
const next_sl = next_sli.ptr();
|
const next_sl = next_sli.ptr();
|
||||||
if (next_sl.line > line or (next_sl.line == line and next_sl.column > column)) break;
|
if (next_sl.line > line or (next_sl.line == line and next_sl.column >= column)) break;
|
||||||
try annotations.append(gpa, .{
|
try annotations.append(gpa, .{
|
||||||
.file_byte_offset = offset,
|
.file_byte_offset = offset,
|
||||||
.dom_id = @intFromEnum(next_sli),
|
.dom_id = @intFromEnum(next_sli),
|
||||||
@ -349,7 +349,7 @@ var coverage = Coverage.init;
|
|||||||
/// Index of type `SourceLocationIndex`.
|
/// Index of type `SourceLocationIndex`.
|
||||||
var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{};
|
var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{};
|
||||||
/// Contains the most recent coverage update message, unmodified.
|
/// Contains the most recent coverage update message, unmodified.
|
||||||
var recent_coverage_update: std.ArrayListUnmanaged(u8) = .{};
|
var recent_coverage_update: std.ArrayListAlignedUnmanaged(u8, @alignOf(u64)) = .{};
|
||||||
|
|
||||||
fn updateCoverage(
|
fn updateCoverage(
|
||||||
directories: []const Coverage.String,
|
directories: []const Coverage.String,
|
||||||
@ -406,19 +406,23 @@ export fn sourceLocationFileCoveredList(sli_file: SourceLocationIndex) Slice(Sou
|
|||||||
};
|
};
|
||||||
const want_file = sli_file.ptr().file;
|
const want_file = sli_file.ptr().file;
|
||||||
global.result.clearRetainingCapacity();
|
global.result.clearRetainingCapacity();
|
||||||
const covered_bits = recent_coverage_update.items[@sizeOf(abi.CoverageUpdateHeader)..];
|
|
||||||
|
// This code assumes 64-bit elements, which is incorrect if the executable
|
||||||
|
// being fuzzed is not a 64-bit CPU. It also assumes little-endian which
|
||||||
|
// can also be incorrect.
|
||||||
|
comptime assert(abi.CoverageUpdateHeader.trailing[0] == .pc_bits_usize);
|
||||||
|
const n_bitset_elems = (coverage_source_locations.items.len + @bitSizeOf(u64) - 1) / @bitSizeOf(u64);
|
||||||
|
const covered_bits = std.mem.bytesAsSlice(
|
||||||
|
u64,
|
||||||
|
recent_coverage_update.items[@sizeOf(abi.CoverageUpdateHeader)..][0 .. n_bitset_elems * @sizeOf(u64)],
|
||||||
|
);
|
||||||
var sli: u32 = 0;
|
var sli: u32 = 0;
|
||||||
for (covered_bits) |byte| {
|
for (covered_bits) |elem| {
|
||||||
global.result.ensureUnusedCapacity(gpa, 8) catch @panic("OOM");
|
global.result.ensureUnusedCapacity(gpa, 64) catch @panic("OOM");
|
||||||
if ((byte & 0b0000_0001) != 0) global.add(sli + 0, want_file);
|
for (0..@bitSizeOf(u64)) |i| {
|
||||||
if ((byte & 0b0000_0010) != 0) global.add(sli + 1, want_file);
|
if ((elem & (@as(u64, 1) << @intCast(i))) != 0) global.add(sli, want_file);
|
||||||
if ((byte & 0b0000_0100) != 0) global.add(sli + 2, want_file);
|
sli += 1;
|
||||||
if ((byte & 0b0000_1000) != 0) global.add(sli + 3, want_file);
|
}
|
||||||
if ((byte & 0b0001_0000) != 0) global.add(sli + 4, want_file);
|
|
||||||
if ((byte & 0b0010_0000) != 0) global.add(sli + 5, want_file);
|
|
||||||
if ((byte & 0b0100_0000) != 0) global.add(sli + 6, want_file);
|
|
||||||
if ((byte & 0b1000_0000) != 0) global.add(sli + 7, want_file);
|
|
||||||
sli += 8;
|
|
||||||
}
|
}
|
||||||
return Slice(SourceLocationIndex).init(global.result.items);
|
return Slice(SourceLocationIndex).init(global.result.items);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -384,10 +384,7 @@ fn sendCoverageContext(
|
|||||||
// TODO: make each events URL correspond to one coverage map
|
// TODO: make each events URL correspond to one coverage map
|
||||||
const coverage_map = &coverage_maps[0];
|
const coverage_map = &coverage_maps[0];
|
||||||
const cov_header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
|
const cov_header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
|
||||||
comptime assert(abi.SeenPcsHeader.trailing[0] == .pc_addr);
|
const seen_pcs = cov_header.seenBits();
|
||||||
const seen_pcs = coverage_map.mapped_memory[@sizeOf(abi.SeenPcsHeader) + coverage_map.source_locations.len * @sizeOf(usize) ..];
|
|
||||||
comptime assert(abi.SeenPcsHeader.trailing[1][0] == .pc_bits);
|
|
||||||
comptime assert(abi.SeenPcsHeader.trailing[1][1] == u8);
|
|
||||||
const n_runs = @atomicLoad(usize, &cov_header.n_runs, .monotonic);
|
const n_runs = @atomicLoad(usize, &cov_header.n_runs, .monotonic);
|
||||||
const unique_runs = @atomicLoad(usize, &cov_header.unique_runs, .monotonic);
|
const unique_runs = @atomicLoad(usize, &cov_header.unique_runs, .monotonic);
|
||||||
const lowest_stack = @atomicLoad(usize, &cov_header.lowest_stack, .monotonic);
|
const lowest_stack = @atomicLoad(usize, &cov_header.lowest_stack, .monotonic);
|
||||||
@ -419,7 +416,7 @@ fn sendCoverageContext(
|
|||||||
};
|
};
|
||||||
const iovecs: [2]std.posix.iovec_const = .{
|
const iovecs: [2]std.posix.iovec_const = .{
|
||||||
makeIov(std.mem.asBytes(&header)),
|
makeIov(std.mem.asBytes(&header)),
|
||||||
makeIov(seen_pcs),
|
makeIov(std.mem.sliceAsBytes(seen_pcs)),
|
||||||
};
|
};
|
||||||
try web_socket.writeMessagev(&iovecs, .binary);
|
try web_socket.writeMessagev(&iovecs, .binary);
|
||||||
|
|
||||||
@ -634,9 +631,7 @@ fn prepareTables(
|
|||||||
gop.value_ptr.mapped_memory = mapped_memory;
|
gop.value_ptr.mapped_memory = mapped_memory;
|
||||||
|
|
||||||
const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
|
const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
|
||||||
comptime assert(abi.SeenPcsHeader.trailing[0] == .pc_addr);
|
const pcs = header.pcAddrs();
|
||||||
const pcs_bytes = mapped_memory[@sizeOf(abi.SeenPcsHeader)..][0 .. header.pcs_len * @sizeOf(usize)];
|
|
||||||
const pcs = std.mem.bytesAsSlice(usize, pcs_bytes);
|
|
||||||
const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
|
const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
|
||||||
errdefer gpa.free(source_locations);
|
errdefer gpa.free(source_locations);
|
||||||
debug_info.resolveAddresses(gpa, pcs, source_locations) catch |err| {
|
debug_info.resolveAddresses(gpa, pcs, source_locations) catch |err| {
|
||||||
@ -653,10 +648,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo
|
|||||||
defer ws.coverage_mutex.unlock();
|
defer ws.coverage_mutex.unlock();
|
||||||
|
|
||||||
const coverage_map = ws.coverage_files.getPtr(coverage_id).?;
|
const coverage_map = ws.coverage_files.getPtr(coverage_id).?;
|
||||||
const ptr = coverage_map.mapped_memory;
|
const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
|
||||||
comptime assert(abi.SeenPcsHeader.trailing[0] == .pc_addr);
|
const pcs = header.pcAddrs();
|
||||||
const pcs_bytes = ptr[@sizeOf(abi.SeenPcsHeader)..][0 .. coverage_map.source_locations.len * @sizeOf(usize)];
|
|
||||||
const pcs: []const usize = @alignCast(std.mem.bytesAsSlice(usize, pcs_bytes));
|
|
||||||
const index = std.sort.upperBound(usize, pcs, addr, struct {
|
const index = std.sort.upperBound(usize, pcs, addr, struct {
|
||||||
fn order(context: usize, item: usize) std.math.Order {
|
fn order(context: usize, item: usize) std.math.Order {
|
||||||
return std.math.order(item, context);
|
return std.math.order(item, context);
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
/// make the ints be the size of the target used with libfuzzer.
|
/// make the ints be the size of the target used with libfuzzer.
|
||||||
///
|
///
|
||||||
/// Trailing:
|
/// Trailing:
|
||||||
|
/// * 1 bit per pc_addr, usize elements
|
||||||
/// * pc_addr: usize for each pcs_len
|
/// * pc_addr: usize for each pcs_len
|
||||||
/// * 1 bit per pc_addr, u8 elements
|
|
||||||
pub const SeenPcsHeader = extern struct {
|
pub const SeenPcsHeader = extern struct {
|
||||||
n_runs: usize,
|
n_runs: usize,
|
||||||
unique_runs: usize,
|
unique_runs: usize,
|
||||||
@ -18,9 +18,29 @@ pub const SeenPcsHeader = extern struct {
|
|||||||
/// Used for comptime assertions. Provides a mechanism for strategically
|
/// Used for comptime assertions. Provides a mechanism for strategically
|
||||||
/// causing compile errors.
|
/// causing compile errors.
|
||||||
pub const trailing = .{
|
pub const trailing = .{
|
||||||
|
.pc_bits_usize,
|
||||||
.pc_addr,
|
.pc_addr,
|
||||||
.{ .pc_bits, u8 },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn headerEnd(header: *const SeenPcsHeader) []const usize {
|
||||||
|
const ptr: [*]align(@alignOf(usize)) const u8 = @ptrCast(header);
|
||||||
|
const header_end_ptr: [*]const usize = @ptrCast(ptr + @sizeOf(SeenPcsHeader));
|
||||||
|
const pcs_len = header.pcs_len;
|
||||||
|
return header_end_ptr[0 .. pcs_len + seenElemsLen(pcs_len)];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seenBits(header: *const SeenPcsHeader) []const usize {
|
||||||
|
return header.headerEnd()[0..seenElemsLen(header.pcs_len)];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seenElemsLen(pcs_len: usize) usize {
|
||||||
|
return (pcs_len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pcAddrs(header: *const SeenPcsHeader) []const usize {
|
||||||
|
const pcs_len = header.pcs_len;
|
||||||
|
return header.headerEnd()[seenElemsLen(pcs_len)..][0..pcs_len];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ToClientTag = enum(u8) {
|
pub const ToClientTag = enum(u8) {
|
||||||
@ -54,12 +74,21 @@ pub const SourceIndexHeader = extern struct {
|
|||||||
/// changes.
|
/// changes.
|
||||||
///
|
///
|
||||||
/// Trailing:
|
/// Trailing:
|
||||||
/// * one bit per source_locations_len, contained in u8 elements
|
/// * one bit per source_locations_len, contained in u64 elements
|
||||||
pub const CoverageUpdateHeader = extern struct {
|
pub const CoverageUpdateHeader = extern struct {
|
||||||
tag: ToClientTag = .coverage_update,
|
flags: Flags = .{},
|
||||||
n_runs: u64 align(1),
|
n_runs: u64,
|
||||||
unique_runs: u64 align(1),
|
unique_runs: u64,
|
||||||
lowest_stack: u64 align(1),
|
lowest_stack: u64,
|
||||||
|
|
||||||
|
pub const Flags = packed struct(u64) {
|
||||||
|
tag: ToClientTag = .coverage_update,
|
||||||
|
_: u56 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const trailing = .{
|
||||||
|
.pc_bits_usize,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sent to the fuzzer web client when the set of entry points is updated.
|
/// Sent to the fuzzer web client when the set of entry points is updated.
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const std = @import("std");
|
|||||||
const fatal = std.process.fatal;
|
const fatal = std.process.fatal;
|
||||||
const Path = std.Build.Cache.Path;
|
const Path = std.Build.Cache.Path;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
const SeenPcsHeader = std.Build.Fuzz.abi.SeenPcsHeader;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
|
var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
|
||||||
@ -36,24 +37,29 @@ pub fn main() !void {
|
|||||||
};
|
};
|
||||||
defer debug_info.deinit(gpa);
|
defer debug_info.deinit(gpa);
|
||||||
|
|
||||||
const cov_bytes = cov_path.root_dir.handle.readFileAlloc(arena, cov_path.sub_path, 1 << 30) catch |err| {
|
const cov_bytes = cov_path.root_dir.handle.readFileAllocOptions(
|
||||||
|
arena,
|
||||||
|
cov_path.sub_path,
|
||||||
|
1 << 30,
|
||||||
|
null,
|
||||||
|
@alignOf(SeenPcsHeader),
|
||||||
|
null,
|
||||||
|
) catch |err| {
|
||||||
fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) });
|
fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) });
|
||||||
};
|
};
|
||||||
|
|
||||||
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
|
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
|
||||||
const stdout = bw.writer();
|
const stdout = bw.writer();
|
||||||
|
|
||||||
const header: *align(1) SeenPcsHeader = @ptrCast(cov_bytes);
|
const header: *SeenPcsHeader = @ptrCast(cov_bytes);
|
||||||
try stdout.print("{any}\n", .{header.*});
|
try stdout.print("{any}\n", .{header.*});
|
||||||
//const n_bitset_elems = (header.pcs_len + 7) / 8;
|
const pcs = header.pcAddrs();
|
||||||
const pcs_bytes = cov_bytes[@sizeOf(SeenPcsHeader)..][0 .. header.pcs_len * @sizeOf(usize)];
|
for (0.., pcs[0 .. pcs.len - 1], pcs[1..]) |i, a, b| {
|
||||||
const pcs = try arena.alloc(usize, header.pcs_len);
|
if (a > b) std.log.err("{d}: 0x{x} > 0x{x}", .{ i, a, b });
|
||||||
for (0..pcs_bytes.len / @sizeOf(usize), pcs) |i, *pc| {
|
|
||||||
pc.* = std.mem.readInt(usize, pcs_bytes[i * @sizeOf(usize) ..][0..@sizeOf(usize)], .little);
|
|
||||||
}
|
}
|
||||||
assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize)));
|
assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize)));
|
||||||
|
|
||||||
const seen_pcs = cov_bytes[@sizeOf(SeenPcsHeader) + pcs.len * @sizeOf(usize) ..];
|
const seen_pcs = header.seenBits();
|
||||||
|
|
||||||
const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, pcs.len);
|
const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, pcs.len);
|
||||||
try debug_info.resolveAddresses(gpa, pcs, source_locations);
|
try debug_info.resolveAddresses(gpa, pcs, source_locations);
|
||||||
@ -62,7 +68,7 @@ pub fn main() !void {
|
|||||||
const file = debug_info.coverage.fileAt(sl.file);
|
const file = debug_info.coverage.fileAt(sl.file);
|
||||||
const dir_name = debug_info.coverage.directories.keys()[file.directory_index];
|
const dir_name = debug_info.coverage.directories.keys()[file.directory_index];
|
||||||
const dir_name_slice = debug_info.coverage.stringAt(dir_name);
|
const dir_name_slice = debug_info.coverage.stringAt(dir_name);
|
||||||
const hit: u1 = @truncate(seen_pcs[i / 8] >> @intCast(i % 8));
|
const hit: u1 = @truncate(seen_pcs[i / @bitSizeOf(usize)] >> @intCast(i % @bitSizeOf(usize)));
|
||||||
try stdout.print("{c}{x}: {s}/{s}:{d}:{d}\n", .{
|
try stdout.print("{c}{x}: {s}/{s}:{d}:{d}\n", .{
|
||||||
"-+"[hit], pc, dir_name_slice, debug_info.coverage.stringAt(file.basename), sl.line, sl.column,
|
"-+"[hit], pc, dir_name_slice, debug_info.coverage.stringAt(file.basename), sl.line, sl.column,
|
||||||
});
|
});
|
||||||
@ -70,10 +76,3 @@ pub fn main() !void {
|
|||||||
|
|
||||||
try bw.flush();
|
try bw.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
const SeenPcsHeader = extern struct {
|
|
||||||
n_runs: usize,
|
|
||||||
deduplicated_runs: usize,
|
|
||||||
pcs_len: usize,
|
|
||||||
lowest_stack: usize,
|
|
||||||
};
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user