authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-10 19:29:43-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 04:01:30-07:00
logf6a3a0ca723325b2b84e67b1f7ef78a8f69df650
tree48d52b5a735047e850e548cbfa032b7b2939ed6f
parent5a4b5c8b94236429263ef25d9287b6c5cc829bde

Replaces the inline symbol iterator with an array of symbols

The intention behind the iterator was to avoid needing to allocate the symbols, but in practice we need to allocate them anyway since we need to reverse their order and don't have random access. The alternative would be an N^2 algorithm. In practice this isn't that bad, because even if the allocation fails, we'll still end up printing the address, so the user still ends up with the necessary information to reconstruct the crash. I don't think it's worth it to try to set up some kind of ring buffer or return partial results on failure, but may revisit this.

5 files changed, 193 insertions(+), 248 deletions(-)

lib/std/debug.zig+29-25
...@@ -38,8 +38,12 @@ pub const cpu_context = @import("debug/cpu_context.zig");...@@ -38,8 +38,12 @@ pub const cpu_context = @import("debug/cpu_context.zig");
38/// pub const init: SelfInfo;38/// pub const init: SelfInfo;
39/// pub fn deinit(si: *SelfInfo, io: Io) void;39/// pub fn deinit(si: *SelfInfo, io: Io) void;
40///40///
41/// /// Returns an iterator over the symbols and source locations of the instruction at `address`.41/// /// Returns the the symbols and source locations of the instruction at `address`. Often this
42/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfo.SymbolIterator;42/// /// will return a single result, but in the case of inlines it may return multiple. When
43/// /// multiple results are returned, they are sorted from innermost to outermost.
44/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const Symbol;
45/// /// Frees symbols returned from `getSymbols`.
46/// pub fn freeSymbols(si: *SelfInfo, symbols: []const Symbol) void;
43/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.47/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
44/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;48/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
45/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;49/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
...@@ -60,11 +64,6 @@ pub const cpu_context = @import("debug/cpu_context.zig");...@@ -60,11 +64,6 @@ pub const cpu_context = @import("debug/cpu_context.zig");
60/// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's64/// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's
61/// /// return address, or 0 if the end of the stack has been reached.65/// /// return address, or 0 if the end of the stack has been reached.
62/// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize;66/// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize;
63/// /// Iterates symbols found at an address.
64/// pub const SymbolIterator = struct {
65/// pub fn deinit(Self: *SymbolIterator, io: Io) void;
66/// pub fn next(self: *SymbolIterator) ?SelfInfoError!Symbol;
67/// };
68/// ```67/// ```
69pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))68pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))
70 root.debug.SelfInfo69 root.debug.SelfInfo
...@@ -1193,35 +1192,35 @@ fn printSourceAtAddress(...@@ -1193,35 +1192,35 @@ fn printSourceAtAddress(
1193 t: Io.Terminal,1192 t: Io.Terminal,
1194 options: PrintSourceAddressOptions,1193 options: PrintSourceAddressOptions,
1195) Writer.Error!void {1194) Writer.Error!void {
1196 var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, options.address);1195 const symbols: []const Symbol = debug_info.getSymbols(io, options.address) catch |err| {
1197 defer symbols.deinit(io);1196 t.setColor(.dim) catch {};
1198 while (symbols.next()) |curr| {1197 defer t.setColor(.reset) catch {};
1199 const symbol: Symbol = curr catch |err| switch (err) {1198 switch (err) {
1200 error.MissingDebugInfo,1199 error.MissingDebugInfo,
1201 error.UnsupportedDebugInfo,1200 error.UnsupportedDebugInfo,
1202 error.InvalidDebugInfo,1201 error.InvalidDebugInfo,
1203 => .unknown,1202 => {},
1204 error.ReadFailed, error.Unexpected, error.Canceled => s: {1203 error.ReadFailed, error.Unexpected, error.Canceled => {
1205 t.setColor(.dim) catch {};
1206 try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{});1204 try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{});
1207 t.setColor(.reset) catch {};
1208 break :s .unknown;
1209 },1205 },
1210 error.OutOfMemory => s: {1206 error.OutOfMemory => {
1211 t.setColor(.dim) catch {};1207 t.setColor(.dim) catch {};
1212 try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{});1208 try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{});
1213 t.setColor(.reset) catch {};1209 t.setColor(.reset) catch {};
1214 break :s .unknown;
1215 },1210 },
1216 };1211 }
1217 defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name);1212 return printLineInfo(io, t, debug_info, null, options.address, null, null);
1213 };
1214 defer debug_info.freeSymbols(symbols);
1215 for (symbols) |symbol| {
1218 try printLineInfo(1216 try printLineInfo(
1219 io,1217 io,
1220 t,1218 t,
1219 debug_info,
1221 symbol.source_location,1220 symbol.source_location,
1222 options.address,1221 options.address,
1223 symbol.name orelse "???",1222 symbol.name,
1224 symbol.compile_unit_name orelse debug_info.getModuleName(io, options.address) catch "???",1223 symbol.compile_unit_name,
1225 );1224 );
1226 if (!options.resolve_inline_callers) break;1225 if (!options.resolve_inline_callers) break;
1227 }1226 }
...@@ -1229,10 +1228,11 @@ fn printSourceAtAddress(...@@ -1229,10 +1228,11 @@ fn printSourceAtAddress(
1229fn printLineInfo(1228fn printLineInfo(
1230 io: Io,1229 io: Io,
1231 t: Io.Terminal,1230 t: Io.Terminal,
1231 debug_info: *SelfInfo,
1232 source_location: ?SourceLocation,1232 source_location: ?SourceLocation,
1233 address: usize,1233 address: usize,
1234 symbol_name: []const u8,1234 symbol_name: ?[]const u8,
1235 compile_unit_name: []const u8,1235 compile_unit_name: ?[]const u8,
1236) Writer.Error!void {1236) Writer.Error!void {
1237 const writer = t.writer;1237 const writer = t.writer;
1238 t.setColor(.bold) catch {};1238 t.setColor(.bold) catch {};
...@@ -1250,7 +1250,11 @@ fn printLineInfo(...@@ -1250,7 +1250,11 @@ fn printLineInfo(
1250 t.setColor(.reset) catch {};1250 t.setColor(.reset) catch {};
1251 try writer.writeAll(": ");1251 try writer.writeAll(": ");
1252 t.setColor(.dim) catch {};1252 t.setColor(.dim) catch {};
1253 try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name });1253 try writer.print("0x{x} in {s} ({s})", .{
1254 address,
1255 symbol_name orelse "???",
1256 compile_unit_name orelse debug_info.getModuleName(io, address) catch "???",
1257 });
1254 t.setColor(.reset) catch {};1258 t.setColor(.reset) catch {};
1255 try writer.writeAll("\n");1259 try writer.writeAll("\n");
12561260
lib/std/debug/Dwarf.zig+13-21
...@@ -1545,26 +1545,17 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {...@@ -1545,26 +1545,17 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
1545 return str[casted_offset..last :0];1545 return str[casted_offset..last :0];
1546}1546}
15471547
1548pub const SymbolIterator = struct {1548pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std.debug.SelfInfoError![]const std.debug.Symbol {
1549 curr: ?std.debug.SelfInfoError!std.debug.Symbol,1549 const symbol = try gpa.create(std.debug.Symbol);
15501550 errdefer gpa.destroy(symbol);
1551 pub fn deinit(self: *SymbolIterator, _: Io) void {
1552 self.* = undefined;
1553 }
1554
1555 pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol {
1556 const result = self.curr;
1557 self.curr = null;
1558 return result;
1559 }
1560};
1561
1562pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) SymbolIterator {
1563 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {1551 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
1564 error.EndOfStream, error.Overflow => return .{ .curr = error.InvalidDebugInfo },1552 error.EndOfStream, error.Overflow => {
1565 else => |e| return .{ .curr = e },1553 symbol.* = .unknown;
1554 return symbol[0..1];
1555 },
1556 else => |e| return e,
1566 };1557 };
1567 return .{ .curr = .{1558 symbol.* = .{
1568 .name = di.getSymbolName(address),1559 .name = di.getSymbolName(address),
1569 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {1560 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
1570 error.MissingDebugInfo, error.InvalidDebugInfo => null,1561 error.MissingDebugInfo, error.InvalidDebugInfo => null,
...@@ -1575,10 +1566,11 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) Symb...@@ -1575,10 +1566,11 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) Symb
1575 error.EndOfStream,1566 error.EndOfStream,
1576 error.Overflow,1567 error.Overflow,
1577 error.StreamTooLong,1568 error.StreamTooLong,
1578 => return .{ .curr = error.InvalidDebugInfo },1569 => return error.InvalidDebugInfo,
1579 else => |e| return .{ .curr = e },1570 else => |e| return e,
1580 },1571 },
1581 } };1572 };
1573 return symbol[0..1];
1582}1574}
15831575
1584/// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and1576/// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and
lib/std/debug/SelfInfo/Elf.zig+22-13
...@@ -30,41 +30,50 @@ pub fn deinit(si: *SelfInfo, io: Io) void {...@@ -30,41 +30,50 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
30 if (si.unwind_cache) |cache| gpa.free(cache);30 if (si.unwind_cache) |cache| gpa.free(cache);
31}31}
3232
33pub const SymbolIterator = std.debug.Dwarf.SymbolIterator;33pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
34
35pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
36 const gpa = std.debug.getDebugInfoAllocator();34 const gpa = std.debug.getDebugInfoAllocator();
37 const module = si.findModule(gpa, io, address, .exclusive) catch |err| return .{ .curr = err };35 const module = try si.findModule(gpa, io, address, .exclusive);
38 defer si.rwlock.unlock(io);36 defer si.rwlock.unlock(io);
3937
40 const vaddr = address - module.load_offset;38 const vaddr = address - module.load_offset;
4139
42 const loaded_elf = module.getLoadedElf(gpa, io) catch |err| return .{ .curr = err };40 const loaded_elf = try module.getLoadedElf(gpa, io);
43 if (loaded_elf.file.dwarf) |*dwarf| {41 if (loaded_elf.file.dwarf) |*dwarf| {
44 if (!loaded_elf.scanned_dwarf) {42 if (!loaded_elf.scanned_dwarf) {
45 dwarf.open(gpa, native_endian) catch |err| switch (err) {43 dwarf.open(gpa, native_endian) catch |err| switch (err) {
46 error.InvalidDebugInfo,44 error.InvalidDebugInfo,
47 error.MissingDebugInfo,45 error.MissingDebugInfo,
48 error.OutOfMemory,46 error.OutOfMemory,
49 => |e| return .{ .curr = e },47 => |e| return e,
50 error.EndOfStream,48 error.EndOfStream,
51 error.Overflow,49 error.Overflow,
52 error.ReadFailed,50 error.ReadFailed,
53 error.StreamTooLong,51 error.StreamTooLong,
54 => return .{ .curr = error.InvalidDebugInfo },52 => return error.InvalidDebugInfo,
55 };53 };
56 loaded_elf.scanned_dwarf = true;54 loaded_elf.scanned_dwarf = true;
57 }55 }
58 return dwarf.getSymbols(gpa, native_endian, vaddr);56 return dwarf.getSymbols(gpa, native_endian, vaddr);
59 }57 }
60 // When DWARF is unavailable, fall back to searching the symtab.58 // When DWARF is unavailable, fall back to searching the symtab.
61 const symbol = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {59 const symbol = try gpa.create(std.debug.Symbol);
62 error.NoSymtab, error.NoStrtab => return .{ .curr = error.MissingDebugInfo },60 errdefer gpa.destroy(symbol);
63 error.BadSymtab => return .{ .curr = error.InvalidDebugInfo },61 symbol.* = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
64 error.OutOfMemory => |e| return .{ .curr = e },62 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
63 error.BadSymtab => return error.InvalidDebugInfo,
64 error.OutOfMemory => |e| return e,
65 };65 };
6666 return symbol[0..1];
67 return .{ .curr = symbol };67}
68pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void {
69 _ = si;
70 const gpa = std.debug.getDebugInfoAllocator();
71 for (symbols) |symbol| {
72 if (symbol.source_location) |source_location| {
73 gpa.free(source_location.file_name);
74 }
75 }
76 gpa.free(symbols);
68}77}
69pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {78pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
70 const gpa = std.debug.getDebugInfoAllocator();79 const gpa = std.debug.getDebugInfoAllocator();
lib/std/debug/SelfInfo/MachO.zig+28-13
...@@ -36,12 +36,15 @@ pub const SymbolIterator = struct {...@@ -36,12 +36,15 @@ pub const SymbolIterator = struct {
36 }36 }
37};37};
3838
39pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {39pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
40 const gpa = std.debug.getDebugInfoAllocator();40 const gpa = std.debug.getDebugInfoAllocator();
41 const module = si.findModule(gpa, io, address) catch |err| return .{ .curr = err };41 const module = try si.findModule(gpa, io, address);
42 defer si.mutex.unlock(io);42 defer si.mutex.unlock(io);
4343
44 const file = module.getFile(gpa, io) catch |err| return .{ .curr = err };44 const file = try module.getFile(gpa, io);
45
46 const symbol = try gpa.create(std.debug.Symbol);
47 errdefer gpa.destroy(symbol);
4548
46 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is49 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is
47 // because the segments in the file on disk might differ from the ones in memory. Normally50 // because the segments in the file on disk might differ from the ones in memory. Normally
...@@ -57,26 +60,27 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {...@@ -57,26 +60,27 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
5760
58 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {61 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
59 // Return at least the symbol name if available.62 // Return at least the symbol name if available.
60 return .{ .curr = .{63 symbol.* = .{
61 .name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err },64 .name = try file.lookupSymbolName(vaddr),
62 .compile_unit_name = null,65 .compile_unit_name = null,
63 .source_location = null,66 .source_location = null,
64 } };67 };
68 return symbol[0..1];
65 };69 };
6670
67 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {71 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
68 // Return at least the symbol name if available.72 // Return at least the symbol name if available.
69 return .{ .curr = .{73 symbol.* = .{
70 .name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err },74 .name = try file.lookupSymbolName(vaddr),
71 .compile_unit_name = null,75 .compile_unit_name = null,
72 .source_location = null,76 .source_location = null,
73 } };77 };
78 return symbol[0..1];
74 };79 };
7580
76 return .{ .curr = .{81 symbol.* = .{
77 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse82 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
78 file.lookupSymbolName(vaddr) catch |err|83 try file.lookupSymbolName(vaddr),
79 return .{ .curr = err },
80 .compile_unit_name = compile_unit.die.getAttrString(84 .compile_unit_name = compile_unit.die.getAttrString(
81 ofile_dwarf,85 ofile_dwarf,
82 native_endian,86 native_endian,
...@@ -92,7 +96,18 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {...@@ -92,7 +96,18 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
92 compile_unit,96 compile_unit,
93 ofile_vaddr,97 ofile_vaddr,
94 ) catch null,98 ) catch null,
95 } };99 };
100 return symbol[0..1];
101}
102pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void {
103 _ = si;
104 const gpa = std.debug.getDebugInfoAllocator();
105 for (symbols) |symbol| {
106 if (symbol.source_location) |source_location| {
107 gpa.free(source_location.file_name);
108 }
109 }
110 gpa.free(symbols);
96}111}
97pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {112pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
98 _ = si;113 _ = si;
lib/std/debug/SelfInfo/Windows.zig+101-176
...@@ -25,107 +25,26 @@ pub fn deinit(si: *SelfInfo, io: Io) void {...@@ -25,107 +25,26 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
25 si.modules.deinit(gpa);25 si.modules.deinit(gpa);
26}26}
2727
28pub const SymbolIterator = struct {28pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
29 err: Error!void = {},29 const gpa = std.debug.getDebugInfoAllocator();
30 lock: ?*Io.RwLock,30 try si.lock.lockShared(io);
31 module: *Module,31 defer si.lock.unlockShared(io);
32 symbols: Module.DebugInfo.Symbols,32 const module = try si.findModule(gpa, address);
3333 const di = try module.getDebugInfo(gpa, io);
34 pub fn deinit(self: *SymbolIterator, io: Io) void {34 return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase));
35 if (self.lock) |lock| lock.unlockShared(io);35}
36 self.symbols.deinit(io);
37 self.* = undefined;
38 }
39
40 fn failing(err: Error) SymbolIterator {
41 return .{
42 .err = err,
43 .lock = null,
44 .module = undefined,
45 .symbols = .none,
46 };
47 }
48
49 pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol {
50 // Check for errors
51 self.err catch |err| {
52 self.err = {};
53 self.symbols = .none;
54 return err;
55 };
56
57 // Return the next symbol for the debug info type
58 switch (self.symbols) {
59 .pdb => |*info| {
60 // The failure cases are unreachable because we only set the pdb field if these are
61 // set
62 const di = if (self.module.di.?) |*di| di else |_| unreachable;
63 const pdb = if (di.pdb) |*pdb| pdb else unreachable;
64
65 // Get the next inlinee if it exists
66 if (info.proc) |proc| {
67 const offset_in_func = info.addr - proc.code_offset;
68 while (info.inline_sites.pop()) |site| {
69 // If our address points into this site, get the source location it points
70 // at
71 const inlinee_src_line = pdb.getInlineeSourceLine(
72 info.module,
73 site.inlinee,
74 ) orelse continue;
75 const maybe_loc = pdb.getInlineSiteSourceLocation(
76 info.module,
77 site,
78 inlinee_src_line.info,
79 offset_in_func,
80 ) catch continue;
81 const loc = maybe_loc orelse continue;
82
83 // If we've found a match, filter out any duplicates that might follow.
84 // Tools like llvm-addr2line output duplicate sites in the same cases as us,
85 // implying that they exist in the underlying data and are not indicative of
86 // a parser bug.
87 while (info.inline_sites.getLastOrNull()) |top| {
88 if (top.inlinee != site.inlinee) break;
89 _ = info.inline_sites.pop();
90 }
91
92 return .{
93 .name = pdb.findInlineeName(site.inlinee),
94 .compile_unit_name = fs.path.basename(info.module.obj_file_name),
95 .source_location = loc,
96 };
97 }
98 }
9936
100 // Return the main symbol and end the iterator37pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void {
101 defer self.symbols = .none;38 _ = si;
102 return .{39 const gpa = std.debug.getDebugInfoAllocator();
103 .name = if (info.proc) |proc| pdb.getSymbolName(proc) else null,40 for (symbols) |symbol| {
104 .compile_unit_name = fs.path.basename(info.module.obj_file_name),41 if (symbol.source_location) |source_location| {
105 .source_location = pdb.getLineNumberInfo(info.module, info.addr) catch null,42 gpa.free(source_location.file_name);
106 };
107 },
108 .dwarf => |*info| return info.next(),
109 .none => return null,
110 }43 }
111 }44 }
112};45 gpa.free(symbols);
113
114pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
115 const gpa = std.debug.getDebugInfoAllocator();
116 si.lock.lockShared(io) catch |err| return .failing(err);
117 errdefer si.lock.unlockShared(io);
118 const module = si.findModule(gpa, address) catch |err| return .failing(err);
119 const di = module.getDebugInfo(gpa, io) catch |err| return .failing(err);
120 const symbols = Module.DebugInfo.Symbols.init(di, address - @intFromPtr(module.entry.DllBase))
121 catch |err| return .failing(err);
122 errdefer comptime unreachable;
123 return .{
124 .lock = &si.lock,
125 .module = module,
126 .symbols = symbols,
127 };
128}46}
47
129pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {48pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
130 const gpa = std.debug.getDebugInfoAllocator();49 const gpa = std.debug.getDebugInfoAllocator();
131 try si.lock.lockShared(io);50 try si.lock.lockShared(io);
...@@ -333,94 +252,100 @@ const Module = struct {...@@ -333,94 +252,100 @@ const Module = struct {
333 arena.deinit();252 arena.deinit();
334 }253 }
335254
336 pub const Symbols = union(enum) {255 fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize) Error![]const std.debug.Symbol {
337 pdb: struct {256 pdb: {
338 module: *Pdb.Module,257 const pdb = &(di.pdb orelse break :pdb);
339 proc: ?*align(1) const std.pdb.ProcSym,258 var coff_section: *align(1) const coff.SectionHeader = undefined;
340 addr: usize,259 const mod_index = for (pdb.sect_contribs) |sect_contrib| {
341 /// Inline sites are stored in the pdb in reverse order, so we build up a list of up260 if (sect_contrib.section > di.coff_section_headers.len) continue;
342 /// front so that our iterator can return them in the correct order without doing an261 // Remember that SectionContribEntry.Section is 1-based.
343 /// n^2 search. We don't try to filter inline sites based on address until the user262 coff_section = &di.coff_section_headers[sect_contrib.section - 1];
344 /// calls `next` as this requires parsing binary annotations, and this is work we263
345 /// may be able to elide if the caller chooses to early out before finishing264 const vaddr_start = coff_section.virtual_address + sect_contrib.offset;
346 /// iteration, e.g. because they only wanted the topmost call.265 const vaddr_end = vaddr_start + sect_contrib.size;
347 inline_sites: std.ArrayList(*align(1) const std.pdb.InlineSiteSym),266 if (vaddr >= vaddr_start and vaddr < vaddr_end) {
348 },267 break sect_contrib.module_index;
349 dwarf: std.debug.Dwarf.SymbolIterator,
350 none: void,
351
352 fn init(di: *DebugInfo, vaddr: usize) Error!Symbols {
353 const gpa = std.debug.getDebugInfoAllocator();
354
355 pdb: {
356 const pdb = &(di.pdb orelse break :pdb);
357 var coff_section: *align(1) const coff.SectionHeader = undefined;
358 const mod_index = for (pdb.sect_contribs) |sect_contrib| {
359 if (sect_contrib.section > di.coff_section_headers.len) continue;
360 // Remember that SectionContribEntry.Section is 1-based.
361 coff_section = &di.coff_section_headers[sect_contrib.section - 1];
362
363 const vaddr_start = coff_section.virtual_address + sect_contrib.offset;
364 const vaddr_end = vaddr_start + sect_contrib.size;
365 if (vaddr >= vaddr_start and vaddr < vaddr_end) {
366 break sect_contrib.module_index;
367 }
368 } else {
369 // we have no information to add to the address
370 break :pdb;
371 };
372 const module = pdb.getModule(mod_index) catch |err| switch (err) {
373 error.InvalidDebugInfo,
374 error.MissingDebugInfo,
375 error.OutOfMemory,
376 => |e| return e,
377
378 error.ReadFailed,
379 error.EndOfStream,
380 => return error.InvalidDebugInfo,
381 } orelse {
382 return error.InvalidDebugInfo; // bad module index
383 };
384
385 const addr = vaddr - coff_section.virtual_address;
386 const maybe_proc = pdb.getProcSym(module, addr);
387
388 var inline_sites: std.ArrayList(*align(1) const std.pdb.InlineSiteSym) = .empty;
389 if (maybe_proc) |proc| {
390 var iter = pdb.getInlinees(module, proc);
391 while (iter.next(module)) |inline_site| {
392 try inline_sites.append(gpa, inline_site);
393 }
394 }268 }
269 } else {
270 // we have no information to add to the address
271 break :pdb;
272 };
273 const module = pdb.getModule(mod_index) catch |err| switch (err) {
274 error.InvalidDebugInfo,
275 error.MissingDebugInfo,
276 error.OutOfMemory,
277 => |e| return e,
278
279 error.ReadFailed,
280 error.EndOfStream,
281 => return error.InvalidDebugInfo,
282 } orelse {
283 return error.InvalidDebugInfo; // bad module index
284 };
395285
396 return .{ .pdb = .{286 const addr = vaddr - coff_section.virtual_address;
397 .module = module,287 const maybe_proc = pdb.getProcSym(module, addr);
398 .proc = maybe_proc,288 var symbols: std.ArrayList(std.debug.Symbol) = .empty;
399 .addr = addr,289 errdefer symbols.deinit(gpa);
400 .inline_sites = inline_sites,290
401 } };291 if (maybe_proc) |proc| {
402 }292 const offset_in_func = addr - proc.code_offset;
293 var last_inlinee: ?u32 = null;
294 var iter = pdb.getInlinees(module, proc);
295 while (iter.next(module)) |inline_site| {
296 // If our address points into this site, get the source location it
297 // points at
298 const inlinee_src_line = pdb.getInlineeSourceLine(
299 module,
300 inline_site.inlinee,
301 ) orelse continue;
302 const maybe_loc = pdb.getInlineSiteSourceLocation(
303 module,
304 inline_site,
305 inlinee_src_line.info,
306 offset_in_func,
307 ) catch continue;
308 const loc = maybe_loc orelse continue;
309
310 // Filter out duplicate inline sites. Tools like llvm-addr2line output
311 // duplicate sites in the same cases as us if we elide this check,
312 // implying that they exist in the underlying data and are not
313 // indicative of a parser bug. No useful information is lost here since an
314 // inline site can't actually reference itself.
315 if (inline_site.inlinee == last_inlinee) continue;
316 last_inlinee = inline_site.inlinee;
317
318 try symbols.append(gpa, .{
319 .name = pdb.findInlineeName(inline_site.inlinee),
320 .compile_unit_name = fs.path.basename(module.obj_file_name),
321 .source_location = loc,
322 });
323 }
403324
404 // Dwarf325 // Inline sites are stored in the pdb in reverse order, so we reverse the
405 dwarf: {326 // matching sites here. We could alternatively use the parent fields to
406 const dwarf = &(di.dwarf orelse break :dwarf);327 // determine the order, but this would introduce seemingly unecessary
407 const addr = vaddr + di.coff_image_base;328 // complexity.
408 return .{ .dwarf = dwarf.getSymbols(gpa, native_endian, addr) };329 std.mem.reverse(std.debug.Symbol, symbols.items);
409 }330 }
410331
411 return error.MissingDebugInfo;332 try symbols.append(gpa, .{
333 .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null,
334 .compile_unit_name = fs.path.basename(module.obj_file_name),
335 .source_location = pdb.getLineNumberInfo(module, addr) catch null,
336 });
337
338 return symbols.toOwnedSlice(gpa);
412 }339 }
413340
414 fn deinit(self: *Symbols, io: Io) void {341 dwarf: {
415 const gpa = std.debug.getDebugInfoAllocator();342 const dwarf = &(di.dwarf orelse break :dwarf);
416 switch (self.*) {343 const addr = vaddr + di.coff_image_base;
417 .pdb => |*info| info.inline_sites.deinit(gpa),344 return dwarf.getSymbols(gpa, native_endian, addr);
418 .dwarf => |*info| info.deinit(io),
419 .none => {},
420 }
421 }345 }
422 };
423346
347 return error.MissingDebugInfo;
348 }
424 };349 };
425350
426 fn deinit(module: *Module, gpa: Allocator, io: Io) void {351 fn deinit(module: *Module, gpa: Allocator, io: Io) void {