aboutsummaryrefslogtreecommitdiff
path: root/tools/dump-cov.zig
blob: 249783b927411747e342b19c9426225229bb15e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! 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.abi.fuzz.SeenPcsHeader;

pub fn main() !void {
    var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
    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 {f}: {s}", .{ exe_path, @errorName(err) });
    };
    defer debug_info.deinit(gpa);

    const cov_bytes = cov_path.root_dir.handle.readFileAllocOptions(
        cov_path.sub_path,
        arena,
        .limited(1 << 30),
        .of(SeenPcsHeader),
        null,
    ) catch |err| {
        fatal("failed to load coverage file {f}: {s}", .{ cov_path, @errorName(err) });
    };

    var stdout_buffer: [4000]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
    const stdout = &stdout_writer.interface;

    const header: *SeenPcsHeader = @ptrCast(cov_bytes);
    try stdout.print("{any}\n", .{header.*});
    const pcs = header.pcAddrs();

    var indexed_pcs: std.AutoArrayHashMapUnmanaged(usize, void) = .empty;
    try indexed_pcs.entries.resize(arena, pcs.len);
    @memcpy(indexed_pcs.entries.items(.key), pcs);
    try indexed_pcs.reIndex(arena);

    const sorted_pcs = try arena.dupe(usize, pcs);
    std.mem.sortUnstable(usize, sorted_pcs, {}, std.sort.asc(usize));

    const source_locations = try arena.alloc(std.debug.Coverage.SourceLocation, sorted_pcs.len);
    try debug_info.resolveAddresses(gpa, sorted_pcs, source_locations);

    const seen_pcs = header.seenBits();

    for (sorted_pcs, source_locations) |pc, sl| {
        if (sl.file == .invalid) {
            try stdout.print(" {x}: invalid\n", .{pc});
            continue;
        }
        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 seen_i = indexed_pcs.getIndex(pc).?;
        const hit: u1 = @truncate(seen_pcs[seen_i / @bitSizeOf(usize)] >> @intCast(seen_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 stdout.flush();
}