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");
3838/// pub const init: SelfInfo;
3939/// pub fn deinit(si: *SelfInfo, io: Io) void;
4040///
41/// /// Returns an iterator over the symbols and source locations of the instruction at `address`.
42/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfo.SymbolIterator;
41/// /// Returns the the symbols and source locations of the instruction at `address`. Often this
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;
4347/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
4448/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
4549/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
......@@ -60,11 +64,6 @@ pub const cpu_context = @import("debug/cpu_context.zig");
6064/// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's
6165/// /// return address, or 0 if the end of the stack has been reached.
6266/// 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/// };
6867/// ```
6968pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))
7069 root.debug.SelfInfo
......@@ -1193,35 +1192,35 @@ fn printSourceAtAddress(
11931192 t: Io.Terminal,
11941193 options: PrintSourceAddressOptions,
11951194) Writer.Error!void {
1196 var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, options.address);
1197 defer symbols.deinit(io);
1198 while (symbols.next()) |curr| {
1199 const symbol: Symbol = curr catch |err| switch (err) {
1195 const symbols: []const Symbol = debug_info.getSymbols(io, options.address) catch |err| {
1196 t.setColor(.dim) catch {};
1197 defer t.setColor(.reset) catch {};
1198 switch (err) {
12001199 error.MissingDebugInfo,
12011200 error.UnsupportedDebugInfo,
12021201 error.InvalidDebugInfo,
1203 => .unknown,
1204 error.ReadFailed, error.Unexpected, error.Canceled => s: {
1205 t.setColor(.dim) catch {};
1202 => {},
1203 error.ReadFailed, error.Unexpected, error.Canceled => {
12061204 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;
12091205 },
1210 error.OutOfMemory => s: {
1206 error.OutOfMemory => {
12111207 t.setColor(.dim) catch {};
12121208 try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{});
12131209 t.setColor(.reset) catch {};
1214 break :s .unknown;
12151210 },
1216 };
1217 defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name);
1211 }
1212 return printLineInfo(io, t, debug_info, null, options.address, null, null);
1213 };
1214 defer debug_info.freeSymbols(symbols);
1215 for (symbols) |symbol| {
12181216 try printLineInfo(
12191217 io,
12201218 t,
1219 debug_info,
12211220 symbol.source_location,
12221221 options.address,
1223 symbol.name orelse "???",
1224 symbol.compile_unit_name orelse debug_info.getModuleName(io, options.address) catch "???",
1222 symbol.name,
1223 symbol.compile_unit_name,
12251224 );
12261225 if (!options.resolve_inline_callers) break;
12271226 }
......@@ -1229,10 +1228,11 @@ fn printSourceAtAddress(
12291228fn printLineInfo(
12301229 io: Io,
12311230 t: Io.Terminal,
1231 debug_info: *SelfInfo,
12321232 source_location: ?SourceLocation,
12331233 address: usize,
1234 symbol_name: []const u8,
1235 compile_unit_name: []const u8,
1234 symbol_name: ?[]const u8,
1235 compile_unit_name: ?[]const u8,
12361236) Writer.Error!void {
12371237 const writer = t.writer;
12381238 t.setColor(.bold) catch {};
......@@ -1250,7 +1250,11 @@ fn printLineInfo(
12501250 t.setColor(.reset) catch {};
12511251 try writer.writeAll(": ");
12521252 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 });
12541258 t.setColor(.reset) catch {};
12551259 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 {
15451545 return str[casted_offset..last :0];
15461546}
15471547
1548pub const SymbolIterator = struct {
1549 curr: ?std.debug.SelfInfoError!std.debug.Symbol,
1550
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 {
1548pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std.debug.SelfInfoError![]const std.debug.Symbol {
1549 const symbol = try gpa.create(std.debug.Symbol);
1550 errdefer gpa.destroy(symbol);
15631551 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
1564 error.EndOfStream, error.Overflow => return .{ .curr = error.InvalidDebugInfo },
1565 else => |e| return .{ .curr = e },
1552 error.EndOfStream, error.Overflow => {
1553 symbol.* = .unknown;
1554 return symbol[0..1];
1555 },
1556 else => |e| return e,
15661557 };
1567 return .{ .curr = .{
1558 symbol.* = .{
15681559 .name = di.getSymbolName(address),
15691560 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
15701561 error.MissingDebugInfo, error.InvalidDebugInfo => null,
......@@ -1575,10 +1566,11 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) Symb
15751566 error.EndOfStream,
15761567 error.Overflow,
15771568 error.StreamTooLong,
1578 => return .{ .curr = error.InvalidDebugInfo },
1579 else => |e| return .{ .curr = e },
1569 => return error.InvalidDebugInfo,
1570 else => |e| return e,
15801571 },
1581 } };
1572 };
1573 return symbol[0..1];
15821574}
15831575
15841576/// 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 {
3030 if (si.unwind_cache) |cache| gpa.free(cache);
3131}
3232
33pub const SymbolIterator = std.debug.Dwarf.SymbolIterator;
34
35pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
33pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
3634 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);
3836 defer si.rwlock.unlock(io);
3937
4038 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);
4341 if (loaded_elf.file.dwarf) |*dwarf| {
4442 if (!loaded_elf.scanned_dwarf) {
4543 dwarf.open(gpa, native_endian) catch |err| switch (err) {
4644 error.InvalidDebugInfo,
4745 error.MissingDebugInfo,
4846 error.OutOfMemory,
49 => |e| return .{ .curr = e },
47 => |e| return e,
5048 error.EndOfStream,
5149 error.Overflow,
5250 error.ReadFailed,
5351 error.StreamTooLong,
54 => return .{ .curr = error.InvalidDebugInfo },
52 => return error.InvalidDebugInfo,
5553 };
5654 loaded_elf.scanned_dwarf = true;
5755 }
5856 return dwarf.getSymbols(gpa, native_endian, vaddr);
5957 }
6058 // When DWARF is unavailable, fall back to searching the symtab.
61 const symbol = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
62 error.NoSymtab, error.NoStrtab => return .{ .curr = error.MissingDebugInfo },
63 error.BadSymtab => return .{ .curr = error.InvalidDebugInfo },
64 error.OutOfMemory => |e| return .{ .curr = e },
59 const symbol = try gpa.create(std.debug.Symbol);
60 errdefer gpa.destroy(symbol);
61 symbol.* = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
62 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
63 error.BadSymtab => return error.InvalidDebugInfo,
64 error.OutOfMemory => |e| return e,
6565 };
66
67 return .{ .curr = symbol };
66 return symbol[0..1];
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);
6877}
6978pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
7079 const gpa = std.debug.getDebugInfoAllocator();
lib/std/debug/SelfInfo/MachO.zig+28-13
......@@ -36,12 +36,15 @@ pub const SymbolIterator = struct {
3636 }
3737};
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 {
4040 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);
4242 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
4649 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is
4750 // 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 {
5760
5861 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
5962 // Return at least the symbol name if available.
60 return .{ .curr = .{
61 .name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err },
63 symbol.* = .{
64 .name = try file.lookupSymbolName(vaddr),
6265 .compile_unit_name = null,
6366 .source_location = null,
64 } };
67 };
68 return symbol[0..1];
6569 };
6670
6771 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
6872 // Return at least the symbol name if available.
69 return .{ .curr = .{
70 .name = file.lookupSymbolName(vaddr) catch |err| return .{ .curr = err },
73 symbol.* = .{
74 .name = try file.lookupSymbolName(vaddr),
7175 .compile_unit_name = null,
7276 .source_location = null,
73 } };
77 };
78 return symbol[0..1];
7479 };
7580
76 return .{ .curr = .{
81 symbol.* = .{
7782 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
78 file.lookupSymbolName(vaddr) catch |err|
79 return .{ .curr = err },
83 try file.lookupSymbolName(vaddr),
8084 .compile_unit_name = compile_unit.die.getAttrString(
8185 ofile_dwarf,
8286 native_endian,
......@@ -92,7 +96,18 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator {
9296 compile_unit,
9397 ofile_vaddr,
9498 ) 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);
96111}
97112pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
98113 _ = si;
lib/std/debug/SelfInfo/Windows.zig+101-176
......@@ -25,107 +25,26 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
2525 si.modules.deinit(gpa);
2626}
2727
28pub const SymbolIterator = struct {
29 err: Error!void = {},
30 lock: ?*Io.RwLock,
31 module: *Module,
32 symbols: Module.DebugInfo.Symbols,
33
34 pub fn deinit(self: *SymbolIterator, io: Io) void {
35 if (self.lock) |lock| lock.unlockShared(io);
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 }
28pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
29 const gpa = std.debug.getDebugInfoAllocator();
30 try si.lock.lockShared(io);
31 defer si.lock.unlockShared(io);
32 const module = try si.findModule(gpa, address);
33 const di = try module.getDebugInfo(gpa, io);
34 return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase));
35}
9936
100 // Return the main symbol and end the iterator
101 defer self.symbols = .none;
102 return .{
103 .name = if (info.proc) |proc| pdb.getSymbolName(proc) else null,
104 .compile_unit_name = fs.path.basename(info.module.obj_file_name),
105 .source_location = pdb.getLineNumberInfo(info.module, info.addr) catch null,
106 };
107 },
108 .dwarf => |*info| return info.next(),
109 .none => return null,
37pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void {
38 _ = si;
39 const gpa = std.debug.getDebugInfoAllocator();
40 for (symbols) |symbol| {
41 if (symbol.source_location) |source_location| {
42 gpa.free(source_location.file_name);
11043 }
11144 }
112};
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 };
45 gpa.free(symbols);
12846}
47
12948pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
13049 const gpa = std.debug.getDebugInfoAllocator();
13150 try si.lock.lockShared(io);
......@@ -333,94 +252,100 @@ const Module = struct {
333252 arena.deinit();
334253 }
335254
336 pub const Symbols = union(enum) {
337 pdb: struct {
338 module: *Pdb.Module,
339 proc: ?*align(1) const std.pdb.ProcSym,
340 addr: usize,
341 /// Inline sites are stored in the pdb in reverse order, so we build up a list of up
342 /// front so that our iterator can return them in the correct order without doing an
343 /// n^2 search. We don't try to filter inline sites based on address until the user
344 /// calls `next` as this requires parsing binary annotations, and this is work we
345 /// may be able to elide if the caller chooses to early out before finishing
346 /// iteration, e.g. because they only wanted the topmost call.
347 inline_sites: std.ArrayList(*align(1) const std.pdb.InlineSiteSym),
348 },
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 }
255 fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize) Error![]const std.debug.Symbol {
256 pdb: {
257 const pdb = &(di.pdb orelse break :pdb);
258 var coff_section: *align(1) const coff.SectionHeader = undefined;
259 const mod_index = for (pdb.sect_contribs) |sect_contrib| {
260 if (sect_contrib.section > di.coff_section_headers.len) continue;
261 // Remember that SectionContribEntry.Section is 1-based.
262 coff_section = &di.coff_section_headers[sect_contrib.section - 1];
263
264 const vaddr_start = coff_section.virtual_address + sect_contrib.offset;
265 const vaddr_end = vaddr_start + sect_contrib.size;
266 if (vaddr >= vaddr_start and vaddr < vaddr_end) {
267 break sect_contrib.module_index;
394268 }
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 = .{
397 .module = module,
398 .proc = maybe_proc,
399 .addr = addr,
400 .inline_sites = inline_sites,
401 } };
402 }
286 const addr = vaddr - coff_section.virtual_address;
287 const maybe_proc = pdb.getProcSym(module, addr);
288 var symbols: std.ArrayList(std.debug.Symbol) = .empty;
289 errdefer symbols.deinit(gpa);
290
291 if (maybe_proc) |proc| {
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 // Dwarf
405 dwarf: {
406 const dwarf = &(di.dwarf orelse break :dwarf);
407 const addr = vaddr + di.coff_image_base;
408 return .{ .dwarf = dwarf.getSymbols(gpa, native_endian, addr) };
325 // Inline sites are stored in the pdb in reverse order, so we reverse the
326 // matching sites here. We could alternatively use the parent fields to
327 // determine the order, but this would introduce seemingly unecessary
328 // complexity.
329 std.mem.reverse(std.debug.Symbol, symbols.items);
409330 }
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);
412339 }
413340
414 fn deinit(self: *Symbols, io: Io) void {
415 const gpa = std.debug.getDebugInfoAllocator();
416 switch (self.*) {
417 .pdb => |*info| info.inline_sites.deinit(gpa),
418 .dwarf => |*info| info.deinit(io),
419 .none => {},
420 }
341 dwarf: {
342 const dwarf = &(di.dwarf orelse break :dwarf);
343 const addr = vaddr + di.coff_image_base;
344 return dwarf.getSymbols(gpa, native_endian, addr);
421345 }
422 };
423346
347 return error.MissingDebugInfo;
348 }
424349 };
425350
426351 fn deinit(module: *Module, gpa: Allocator, io: Io) void {