zig/tools/dump-cov.zig
Andrew Kelley f6f1ecf0f9 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
2024-08-08 21:46:36 -07:00

78 lines
2.8 KiB
Zig

//! Reads a Zig coverage file and prints human-readable information to stdout,
//! including file:line:column information for each PC.
const std = @import("std");
const fatal = std.process.fatal;
const Path = std.Build.Cache.Path;
const assert = std.debug.assert;
const SeenPcsHeader = std.Build.Fuzz.abi.SeenPcsHeader;
pub fn main() !void {
var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
defer _ = general_purpose_allocator.deinit();
const gpa = general_purpose_allocator.allocator();
var arena_instance = std.heap.ArenaAllocator.init(gpa);
defer arena_instance.deinit();
const arena = arena_instance.allocator();
const args = try std.process.argsAlloc(arena);
const exe_file_name = args[1];
const cov_file_name = args[2];
const exe_path: Path = .{
.root_dir = std.Build.Cache.Directory.cwd(),
.sub_path = exe_file_name,
};
const cov_path: Path = .{
.root_dir = std.Build.Cache.Directory.cwd(),
.sub_path = cov_file_name,
};
var coverage = std.debug.Coverage.init;
defer coverage.deinit(gpa);
var debug_info = std.debug.Info.load(gpa, exe_path, &coverage) catch |err| {
fatal("failed to load debug info for {}: {s}", .{ exe_path, @errorName(err) });
};
defer debug_info.deinit(gpa);
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) });
};
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
const stdout = bw.writer();
const header: *SeenPcsHeader = @ptrCast(cov_bytes);
try stdout.print("{any}\n", .{header.*});
const pcs = header.pcAddrs();
for (0.., pcs[0 .. pcs.len - 1], pcs[1..]) |i, a, b| {
if (a > b) std.log.err("{d}: 0x{x} > 0x{x}", .{ i, a, b });
}
assert(std.sort.isSorted(usize, pcs, {}, std.sort.asc(usize)));
const seen_pcs = header.seenBits();
const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, pcs.len);
try debug_info.resolveAddresses(gpa, pcs, source_locations);
for (pcs, source_locations, 0..) |pc, sl, i| {
const file = debug_info.coverage.fileAt(sl.file);
const dir_name = debug_info.coverage.directories.keys()[file.directory_index];
const dir_name_slice = debug_info.coverage.stringAt(dir_name);
const hit: u1 = @truncate(seen_pcs[i / @bitSizeOf(usize)] >> @intCast(i % @bitSizeOf(usize)));
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,
});
}
try bw.flush();
}