aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug/Info.zig
blob: 74119a3ea40ce86cf2707c971c4bd89ebc2d59f5 (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
//! Cross-platform abstraction for loading debug information into an in-memory
//! format that supports queries such as "what is the source location of this
//! virtual memory address?"
//!
//! Unlike `std.debug.SelfInfo`, this API does not assume the debug information
//! in question happens to match the host CPU architecture, OS, or other target
//! properties.

const std = @import("../std.zig");
const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path;
const ElfFile = std.debug.ElfFile;
const assert = std.debug.assert;
const Coverage = std.debug.Coverage;
const SourceLocation = std.debug.Coverage.SourceLocation;

const Info = @This();

/// Sorted by key, ascending.
address_map: std.AutoArrayHashMapUnmanaged(u64, ElfFile),
/// Externally managed, outlives this `Info` instance.
coverage: *Coverage,

pub const LoadError = std.fs.File.OpenError || ElfFile.LoadError || std.debug.Dwarf.ScanError || error{MissingDebugInfo};

pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {
    var file = try path.root_dir.handle.openFile(path.sub_path, .{});
    defer file.close();

    var elf_file: ElfFile = try .load(gpa, file, null, &.none);
    errdefer elf_file.deinit(gpa);

    if (elf_file.dwarf == null) return error.MissingDebugInfo;
    try elf_file.dwarf.?.open(gpa, elf_file.endian);
    try elf_file.dwarf.?.populateRanges(gpa, elf_file.endian);

    var info: Info = .{
        .address_map = .{},
        .coverage = coverage,
    };
    try info.address_map.put(gpa, 0, elf_file);
    errdefer comptime unreachable; // elf_file is owned by the map now
    return info;
}

pub fn deinit(info: *Info, gpa: Allocator) void {
    for (info.address_map.values()) |*elf_file| {
        elf_file.dwarf.?.deinit(gpa);
    }
    info.address_map.deinit(gpa);
    info.* = undefined;
}

pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError;

/// Given an array of virtual memory addresses, sorted ascending, outputs a
/// corresponding array of source locations.
pub fn resolveAddresses(
    info: *Info,
    gpa: Allocator,
    /// Asserts the addresses are in ascending order.
    sorted_pc_addrs: []const u64,
    /// Asserts its length equals length of `sorted_pc_addrs`.
    output: []SourceLocation,
) ResolveAddressesError!void {
    assert(sorted_pc_addrs.len == output.len);
    if (info.address_map.entries.len != 1) @panic("TODO");
    const elf_file = &info.address_map.values()[0];
    return info.coverage.resolveAddressesDwarf(gpa, elf_file.endian, sorted_pc_addrs, output, &elf_file.dwarf.?);
}