zig/lib/std / debug/Info.zig

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.

//! 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.

LoadError

Sorted by key, ascending.


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;

load()

Externally managed, outlives this Info instance.


const Info = @This();

deinit()

Given an array of virtual memory addresses, sorted ascending, outputs a corresponding array of source locations.


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

ResolveAddressesError

Asserts the addresses are in ascending order.


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

resolveAddresses()

Asserts its length equals length of sorted_pc_addrs.


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.?);
}