pub fn assert(ok: bool) voidInvokes detectable illegal behavior when ok is false.
In debug and safe modes, calls to this function are always generated, and the unreachable statement triggers a panic.
In fast and small modes, calls to this function are optimized away, and in fact the optimizer is able to use the assertion in its heuristics.
Inside a test block, it is best to use the testing module rather than this function, because this function may not detect a test failure in fast and small mode. Outside of a test block, this assert function is the correct function to use.
pub fn load(gpa: Allocator, io: Io, path: Path, coverage: *Coverage, format: std.Target.ObjectFormat, arch: std.Target.Cpu.Arch) LoadError!Infopub fn deinit(info: *Info, gpa: Allocator) voidpub fn resolveAddresses(info: *Info, gpa: Allocator, io: Io, /// Asserts the addresses are in ascending order. sorted_pc_addrs: []const u64, /// Asserts its length equals length of `sorted_pc_addrs`. output: []SourceLocation) ResolveAddressesError!voidGiven an array of virtual memory addresses, sorted ascending, outputs a corresponding array of source locations.
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124//! 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 Info = @This();
const std = @import("../std.zig");
const Io = std.Io;
const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path;
const assert = std.debug.assert;
const Coverage = std.debug.Coverage;
const SourceLocation = std.debug.Coverage.SourceLocation;
const ElfFile = std.debug.ElfFile;
const MachOFile = std.debug.MachOFile;
impl: union(enum) {
elf: ElfFile,
macho: MachOFile,
},
/// Externally managed, outlives this `Info` instance.
coverage: *Coverage,
pub const LoadError = error{
MissingDebugInfo,
UnsupportedDebugInfo,
} || Io.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError;
pub fn load(
gpa: Allocator,
io: Io,
path: Path,
coverage: *Coverage,
format: std.Target.ObjectFormat,
arch: std.Target.Cpu.Arch,
) LoadError!Info {
switch (format) {
.elf => {
var file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
defer file.close(io);
var elf_file: ElfFile = try .load(gpa, io, 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);
return .{
.impl = .{ .elf = elf_file },
.coverage = coverage,
};
},
.macho => {
const path_str = try path.toString(gpa);
defer gpa.free(path_str);
var macho_file: MachOFile = try .load(gpa, io, path_str, arch);
errdefer macho_file.deinit(gpa);
return .{
.impl = .{ .macho = macho_file },
.coverage = coverage,
};
},
else => return error.UnsupportedDebugInfo,
}
}
pub fn deinit(info: *Info, gpa: Allocator) void {
switch (info.impl) {
.elf => |*ef| ef.deinit(gpa),
.macho => |*mf| mf.deinit(gpa),
}
info.* = undefined;
}
pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError || error{UnsupportedDebugInfo};
/// Given an array of virtual memory addresses, sorted ascending, outputs a
/// corresponding array of source locations.
pub fn resolveAddresses(
info: *Info,
gpa: Allocator,
io: Io,
/// 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);
switch (info.impl) {
.elf => |*ef| return info.coverage.resolveAddressesDwarf(gpa, io, ef.endian, sorted_pc_addrs, output, &ef.dwarf.?),
.macho => |*mf| {
// Resolving all of the addresses at once unfortunately isn't so easy in Mach-O binaries
// due to split debug information. For now, we'll just resolve the addreses one by one.
for (sorted_pc_addrs, output) |pc_addr, *src_loc| {
const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, io, pc_addr) catch |err| switch (err) {
error.MissingDebugInfo => {
src_loc.* = .invalid;
continue;
},
error.InvalidMachO, error.InvalidDwarf => return error.InvalidDebugInfo,
else => |e| return e,
};
if (dwarf.ranges.items.len == 0) {
dwarf.populateRanges(gpa, .little) catch |err| switch (err) {
error.EndOfStream,
error.Overflow,
error.StreamTooLong,
error.ReadFailed,
=> return error.InvalidDebugInfo,
else => |e| return e,
};
}
try info.coverage.resolveAddressesDwarf(gpa, io, .little, &.{dwarf_pc_addr}, src_loc[0..1], dwarf);
}
},
}
}
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.