authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-11 00:28:48-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 04:01:30-07:00
log4efbb27aa2b841357c03d6ab158418ccad022415
tree2e61e2cdcb42580a7c892d120f6d2ac2effbfbab
parent9edbf00ddfb3f8316d82e191b7aca281a0939ac0

Don't bother resolving symbol names that won't be used

Also fixes some memory management issues

5 files changed, 93 insertions(+), 85 deletions(-)

lib/std/debug.zig+13-9
......@@ -38,12 +38,8 @@ 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 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;
41/// /// Returns the the symbols and source locations of the instruction at `address`.
42/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, include_inline_callers: bool) SelfInfoError![]Symbol;
4743/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
4844/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
4945/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
......@@ -233,6 +229,11 @@ pub const Symbol = struct {
233229 .compile_unit_name = null,
234230 .source_location = null,
235231 };
232
233 pub fn deinit(self: *Symbol, gpa: Allocator) void {
234 if (self.source_location) |sl| gpa.free(sl.file_name);
235 self.* = undefined;
236 }
236237};
237238
238239/// Deprecated because it returns the optimization mode of the standard
......@@ -1192,7 +1193,8 @@ fn printSourceAtAddress(
11921193 t: Io.Terminal,
11931194 options: PrintSourceAddressOptions,
11941195) Writer.Error!void {
1195 const symbols: []const Symbol = debug_info.getSymbols(io, options.address) catch |err| {
1196 const gpa = getDebugInfoAllocator();
1197 const symbols: []Symbol = debug_info.getSymbols(io, options.address, options.resolve_inline_callers) catch |err| {
11961198 t.setColor(.dim) catch {};
11971199 defer t.setColor(.reset) catch {};
11981200 switch (err) {
......@@ -1211,7 +1213,10 @@ fn printSourceAtAddress(
12111213 }
12121214 return printLineInfo(io, t, debug_info, null, options.address, null, null);
12131215 };
1214 defer debug_info.freeSymbols(symbols);
1216 defer {
1217 for (symbols) |*symbol| symbol.deinit(gpa);
1218 gpa.free(symbols);
1219 }
12151220 for (symbols) |symbol| {
12161221 try printLineInfo(
12171222 io,
......@@ -1222,7 +1227,6 @@ fn printSourceAtAddress(
12221227 symbol.name,
12231228 symbol.compile_unit_name,
12241229 );
1225 if (!options.resolve_inline_callers) break;
12261230 }
12271231}
12281232fn printLineInfo(
lib/std/debug/Dwarf.zig+13-8
......@@ -1545,17 +1545,22 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
15451545 return str[casted_offset..last :0];
15461546}
15471547
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);
1548pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64, resolve_inline_callers: bool) std.debug.SelfInfoError![]std.debug.Symbol {
1549 _ = resolve_inline_callers;
1550
1551 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
1552 errdefer {
1553 for (symbols.items) |*symbol| symbol.deinit(gpa);
1554 symbols.deinit(gpa);
1555 }
15511556 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
15521557 error.EndOfStream, error.Overflow => {
1553 symbol.* = .unknown;
1554 return symbol[0..1];
1558 symbols.appendAssumeCapacity(.unknown);
1559 return symbols.toOwnedSlice(gpa);
15551560 },
15561561 else => |e| return e,
15571562 };
1558 symbol.* = .{
1563 symbols.appendAssumeCapacity(.{
15591564 .name = di.getSymbolName(address),
15601565 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
15611566 error.MissingDebugInfo, error.InvalidDebugInfo => null,
......@@ -1569,8 +1574,8 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std.
15691574 => return error.InvalidDebugInfo,
15701575 else => |e| return e,
15711576 },
1572 };
1573 return symbol[0..1];
1577 });
1578 return symbols.toOwnedSlice(gpa);
15741579}
15751580
15761581/// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and
lib/std/debug/SelfInfo/Elf.zig+10-17
......@@ -30,7 +30,7 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
3030 if (si.unwind_cache) |cache| gpa.free(cache);
3131}
3232
33pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
33pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
3434 const gpa = std.debug.getDebugInfoAllocator();
3535 const module = try si.findModule(gpa, io, address, .exclusive);
3636 defer si.rwlock.unlock(io);
......@@ -53,27 +53,20 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug
5353 };
5454 loaded_elf.scanned_dwarf = true;
5555 }
56 return dwarf.getSymbols(gpa, native_endian, vaddr);
56 return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers);
5757 }
5858 // When DWARF is unavailable, fall back to searching the symtab.
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) {
59 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
60 errdefer {
61 for (symbols.items) |*symbol| symbol.deinit(gpa);
62 symbols.deinit(gpa);
63 }
64 symbols.appendAssumeCapacity(loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
6265 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
6366 error.BadSymtab => return error.InvalidDebugInfo,
6467 error.OutOfMemory => |e| return e,
65 };
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);
68 });
69 return symbols.toOwnedSlice(gpa);
7770}
7871pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
7972 const gpa = std.debug.getDebugInfoAllocator();
lib/std/debug/SelfInfo/MachO.zig+17-22
......@@ -36,15 +36,20 @@ pub const SymbolIterator = struct {
3636 }
3737};
3838
39pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
39pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
40 _ = resolve_inline_callers;
41
4042 const gpa = std.debug.getDebugInfoAllocator();
4143 const module = try si.findModule(gpa, io, address);
4244 defer si.mutex.unlock(io);
4345
4446 const file = try module.getFile(gpa, io);
4547
46 const symbol = try gpa.create(std.debug.Symbol);
47 errdefer gpa.destroy(symbol);
48 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
49 errdefer {
50 for (symbols.items) |*symbol| symbol.deinit(gpa);
51 symbols.deinit(gpa);
52 }
4853
4954 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is
5055 // because the segments in the file on disk might differ from the ones in memory. Normally
......@@ -60,25 +65,25 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug
6065
6166 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
6267 // Return at least the symbol name if available.
63 symbol.* = .{
68 symbols.appendAssumeCapacity(.{
6469 .name = try file.lookupSymbolName(vaddr),
6570 .compile_unit_name = null,
6671 .source_location = null,
67 };
68 return symbol[0..1];
72 });
73 return symbols.toOwnedSlice(gpa);
6974 };
7075
7176 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
7277 // Return at least the symbol name if available.
73 symbol.* = .{
78 symbols.appendAssumeCapacity(.{
7479 .name = try file.lookupSymbolName(vaddr),
7580 .compile_unit_name = null,
7681 .source_location = null,
77 };
78 return symbol[0..1];
82 });
83 return symbols.toOwnedSlice(gpa);
7984 };
8085
81 symbol.* = .{
86 symbols.appendAssumeCapacity(.{
8287 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
8388 try file.lookupSymbolName(vaddr),
8489 .compile_unit_name = compile_unit.die.getAttrString(
......@@ -96,18 +101,8 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug
96101 compile_unit,
97102 ofile_vaddr,
98103 ) catch null,
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);
104 });
105 return symbols.toOwnedSlice(gpa);
111106}
112107pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
113108 _ = si;
lib/std/debug/SelfInfo/Windows.zig+40-29
......@@ -25,24 +25,13 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
2525 si.modules.deinit(gpa);
2626}
2727
28pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol {
28pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
2929 const gpa = std.debug.getDebugInfoAllocator();
3030 try si.lock.lockShared(io);
3131 defer si.lock.unlockShared(io);
3232 const module = try si.findModule(gpa, address);
3333 const di = try module.getDebugInfo(gpa, io);
34 return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase));
35}
36
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);
43 }
44 }
45 gpa.free(symbols);
34 return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase), resolve_inline_callers);
4635}
4736
4837pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
......@@ -252,7 +241,7 @@ const Module = struct {
252241 arena.deinit();
253242 }
254243
255 fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize) Error![]const std.debug.Symbol {
244 fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
256245 pdb: {
257246 const pdb = &(di.pdb orelse break :pdb);
258247 var coff_section: *align(1) const coff.SectionHeader = undefined;
......@@ -285,8 +274,12 @@ const Module = struct {
285274
286275 const addr = vaddr - coff_section.virtual_address;
287276 const maybe_proc = pdb.getProcSym(module, addr);
288 var symbols: std.ArrayList(std.debug.Symbol) = .empty;
289 errdefer symbols.deinit(gpa);
277 const compile_unit_name = fs.path.basename(module.obj_file_name);
278 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
279 errdefer {
280 for (symbols.items) |*symbol| symbol.deinit(gpa);
281 symbols.deinit(gpa);
282 }
290283
291284 if (maybe_proc) |proc| {
292285 const offset_in_func = addr - proc.code_offset;
......@@ -315,25 +308,43 @@ const Module = struct {
315308 if (inline_site.inlinee == last_inlinee) continue;
316309 last_inlinee = inline_site.inlinee;
317310
311 // If we're appending this symbol, resolve the name. If we're replacing the
312 // last symbol, clear the previous symbols and wait to resolve the name
313 // until we've reached the last symbol to avoid doing work and then
314 // throwing it out.
315 const name = b: {
316 if (resolve_inline_callers) break :b pdb.findInlineeName(inline_site.inlinee);
317 symbols.items.len = 0;
318 break :b null;
319 };
320
318321 try symbols.append(gpa, .{
319 .name = pdb.findInlineeName(inline_site.inlinee),
320 .compile_unit_name = fs.path.basename(module.obj_file_name),
322 .name = name,
323 .compile_unit_name = compile_unit_name,
321324 .source_location = loc,
322325 });
323326 }
324327
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);
328 if (resolve_inline_callers) {
329 // Inline sites are stored in the pdb in reverse order, so we reverse the
330 // matching sites here. We could alternatively use the parent fields to
331 // determine the order, but this would introduce seemingly unecessary
332 // complexity.
333 std.mem.reverse(std.debug.Symbol, symbols.items);
334 } else if (last_inlinee) |inlinee| {
335 // If we haven't resolved the name yet, resolve it now
336 symbols.items[symbols.items.len - 1].name = pdb.findInlineeName(inlinee);
337 }
330338 }
331339
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 });
340 // If there's room for another symbol, add the actual proc
341 if (resolve_inline_callers or symbols.items.len == 0) {
342 try symbols.append(gpa, .{
343 .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null,
344 .compile_unit_name = compile_unit_name,
345 .source_location = pdb.getLineNumberInfo(module, addr) catch null,
346 });
347 }
337348
338349 return symbols.toOwnedSlice(gpa);
339350 }
......@@ -341,7 +352,7 @@ const Module = struct {
341352 dwarf: {
342353 const dwarf = &(di.dwarf orelse break :dwarf);
343354 const addr = vaddr + di.coff_image_base;
344 return dwarf.getSymbols(gpa, native_endian, addr);
355 return dwarf.getSymbols(gpa, native_endian, addr, resolve_inline_callers);
345356 }
346357
347358 return error.MissingDebugInfo;