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");...@@ -38,12 +38,8 @@ 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 the the symbols and source locations of the instruction at `address`. Often this41/// /// Returns the the symbols and source locations of the instruction at `address`.
42/// /// will return a single result, but in the case of inlines it may return multiple. When42/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, include_inline_callers: bool) SelfInfoError![]Symbol;
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;
47/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.43/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
48/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;44/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
49/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;45/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
...@@ -233,6 +229,11 @@ pub const Symbol = struct {...@@ -233,6 +229,11 @@ pub const Symbol = struct {
233 .compile_unit_name = null,229 .compile_unit_name = null,
234 .source_location = null,230 .source_location = null,
235 };231 };
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 }
236};237};
237238
238/// Deprecated because it returns the optimization mode of the standard239/// Deprecated because it returns the optimization mode of the standard
...@@ -1192,7 +1193,8 @@ fn printSourceAtAddress(...@@ -1192,7 +1193,8 @@ fn printSourceAtAddress(
1192 t: Io.Terminal,1193 t: Io.Terminal,
1193 options: PrintSourceAddressOptions,1194 options: PrintSourceAddressOptions,
1194) Writer.Error!void {1195) 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| {
1196 t.setColor(.dim) catch {};1198 t.setColor(.dim) catch {};
1197 defer t.setColor(.reset) catch {};1199 defer t.setColor(.reset) catch {};
1198 switch (err) {1200 switch (err) {
...@@ -1211,7 +1213,10 @@ fn printSourceAtAddress(...@@ -1211,7 +1213,10 @@ fn printSourceAtAddress(
1211 }1213 }
1212 return printLineInfo(io, t, debug_info, null, options.address, null, null);1214 return printLineInfo(io, t, debug_info, null, options.address, null, null);
1213 };1215 };
1214 defer debug_info.freeSymbols(symbols);1216 defer {
1217 for (symbols) |*symbol| symbol.deinit(gpa);
1218 gpa.free(symbols);
1219 }
1215 for (symbols) |symbol| {1220 for (symbols) |symbol| {
1216 try printLineInfo(1221 try printLineInfo(
1217 io,1222 io,
...@@ -1222,7 +1227,6 @@ fn printSourceAtAddress(...@@ -1222,7 +1227,6 @@ fn printSourceAtAddress(
1222 symbol.name,1227 symbol.name,
1223 symbol.compile_unit_name,1228 symbol.compile_unit_name,
1224 );1229 );
1225 if (!options.resolve_inline_callers) break;
1226 }1230 }
1227}1231}
1228fn printLineInfo(1232fn printLineInfo(
lib/std/debug/Dwarf.zig+13-8
...@@ -1545,17 +1545,22 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {...@@ -1545,17 +1545,22 @@ 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 fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std.debug.SelfInfoError![]const std.debug.Symbol {1548pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64, resolve_inline_callers: bool) std.debug.SelfInfoError![]std.debug.Symbol {
1549 const symbol = try gpa.create(std.debug.Symbol);1549 _ = resolve_inline_callers;
1550 errdefer gpa.destroy(symbol);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 }
1551 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {1556 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
1552 error.EndOfStream, error.Overflow => {1557 error.EndOfStream, error.Overflow => {
1553 symbol.* = .unknown;1558 symbols.appendAssumeCapacity(.unknown);
1554 return symbol[0..1];1559 return symbols.toOwnedSlice(gpa);
1555 },1560 },
1556 else => |e| return e,1561 else => |e| return e,
1557 };1562 };
1558 symbol.* = .{1563 symbols.appendAssumeCapacity(.{
1559 .name = di.getSymbolName(address),1564 .name = di.getSymbolName(address),
1560 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {1565 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
1561 error.MissingDebugInfo, error.InvalidDebugInfo => null,1566 error.MissingDebugInfo, error.InvalidDebugInfo => null,
...@@ -1569,8 +1574,8 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std....@@ -1569,8 +1574,8 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std.
1569 => return error.InvalidDebugInfo,1574 => return error.InvalidDebugInfo,
1570 else => |e| return e,1575 else => |e| return e,
1571 },1576 },
1572 };1577 });
1573 return symbol[0..1];1578 return symbols.toOwnedSlice(gpa);
1574}1579}
15751580
1576/// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and1581/// 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 {...@@ -30,7 +30,7 @@ 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 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 {
34 const gpa = std.debug.getDebugInfoAllocator();34 const gpa = std.debug.getDebugInfoAllocator();
35 const module = try si.findModule(gpa, io, address, .exclusive);35 const module = try si.findModule(gpa, io, address, .exclusive);
36 defer si.rwlock.unlock(io);36 defer si.rwlock.unlock(io);
...@@ -53,27 +53,20 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug...@@ -53,27 +53,20 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug
53 };53 };
54 loaded_elf.scanned_dwarf = true;54 loaded_elf.scanned_dwarf = true;
55 }55 }
56 return dwarf.getSymbols(gpa, native_endian, vaddr);56 return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers);
57 }57 }
58 // When DWARF is unavailable, fall back to searching the symtab.58 // When DWARF is unavailable, fall back to searching the symtab.
59 const symbol = try gpa.create(std.debug.Symbol);59 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
60 errdefer gpa.destroy(symbol);60 errdefer {
61 symbol.* = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {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) {
62 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,65 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
63 error.BadSymtab => return error.InvalidDebugInfo,66 error.BadSymtab => return error.InvalidDebugInfo,
64 error.OutOfMemory => |e| return e,67 error.OutOfMemory => |e| return e,
65 };68 });
66 return symbol[0..1];69 return symbols.toOwnedSlice(gpa);
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);
77}70}
78pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {71pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
79 const gpa = std.debug.getDebugInfoAllocator();72 const gpa = std.debug.getDebugInfoAllocator();
lib/std/debug/SelfInfo/MachO.zig+17-22
...@@ -36,15 +36,20 @@ pub const SymbolIterator = struct {...@@ -36,15 +36,20 @@ pub const SymbolIterator = struct {
36 }36 }
37};37};
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
40 const gpa = std.debug.getDebugInfoAllocator();42 const gpa = std.debug.getDebugInfoAllocator();
41 const module = try si.findModule(gpa, io, address);43 const module = try si.findModule(gpa, io, address);
42 defer si.mutex.unlock(io);44 defer si.mutex.unlock(io);
4345
44 const file = try module.getFile(gpa, io);46 const file = try module.getFile(gpa, io);
4547
46 const symbol = try gpa.create(std.debug.Symbol);48 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
47 errdefer gpa.destroy(symbol);49 errdefer {
50 for (symbols.items) |*symbol| symbol.deinit(gpa);
51 symbols.deinit(gpa);
52 }
4853
49 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is54 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is
50 // because the segments in the file on disk might differ from the ones in memory. Normally55 // 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...@@ -60,25 +65,25 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug
6065
61 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {66 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
62 // Return at least the symbol name if available.67 // Return at least the symbol name if available.
63 symbol.* = .{68 symbols.appendAssumeCapacity(.{
64 .name = try file.lookupSymbolName(vaddr),69 .name = try file.lookupSymbolName(vaddr),
65 .compile_unit_name = null,70 .compile_unit_name = null,
66 .source_location = null,71 .source_location = null,
67 };72 });
68 return symbol[0..1];73 return symbols.toOwnedSlice(gpa);
69 };74 };
7075
71 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {76 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
72 // Return at least the symbol name if available.77 // Return at least the symbol name if available.
73 symbol.* = .{78 symbols.appendAssumeCapacity(.{
74 .name = try file.lookupSymbolName(vaddr),79 .name = try file.lookupSymbolName(vaddr),
75 .compile_unit_name = null,80 .compile_unit_name = null,
76 .source_location = null,81 .source_location = null,
77 };82 });
78 return symbol[0..1];83 return symbols.toOwnedSlice(gpa);
79 };84 };
8085
81 symbol.* = .{86 symbols.appendAssumeCapacity(.{
82 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse87 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
83 try file.lookupSymbolName(vaddr),88 try file.lookupSymbolName(vaddr),
84 .compile_unit_name = compile_unit.die.getAttrString(89 .compile_unit_name = compile_unit.die.getAttrString(
...@@ -96,18 +101,8 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug...@@ -96,18 +101,8 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug
96 compile_unit,101 compile_unit,
97 ofile_vaddr,102 ofile_vaddr,
98 ) catch null,103 ) catch null,
99 };104 });
100 return symbol[0..1];105 return symbols.toOwnedSlice(gpa);
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);
111}106}
112pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {107pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
113 _ = si;108 _ = si;
lib/std/debug/SelfInfo/Windows.zig+40-29
...@@ -25,24 +25,13 @@ pub fn deinit(si: *SelfInfo, io: Io) void {...@@ -25,24 +25,13 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
25 si.modules.deinit(gpa);25 si.modules.deinit(gpa);
26}26}
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 {
29 const gpa = std.debug.getDebugInfoAllocator();29 const gpa = std.debug.getDebugInfoAllocator();
30 try si.lock.lockShared(io);30 try si.lock.lockShared(io);
31 defer si.lock.unlockShared(io);31 defer si.lock.unlockShared(io);
32 const module = try si.findModule(gpa, address);32 const module = try si.findModule(gpa, address);
33 const di = try module.getDebugInfo(gpa, io);33 const di = try module.getDebugInfo(gpa, io);
34 return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase));34 return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase), resolve_inline_callers);
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);
46}35}
4736
48pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {37pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
...@@ -252,7 +241,7 @@ const Module = struct {...@@ -252,7 +241,7 @@ const Module = struct {
252 arena.deinit();241 arena.deinit();
253 }242 }
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 {
256 pdb: {245 pdb: {
257 const pdb = &(di.pdb orelse break :pdb);246 const pdb = &(di.pdb orelse break :pdb);
258 var coff_section: *align(1) const coff.SectionHeader = undefined;247 var coff_section: *align(1) const coff.SectionHeader = undefined;
...@@ -285,8 +274,12 @@ const Module = struct {...@@ -285,8 +274,12 @@ const Module = struct {
285274
286 const addr = vaddr - coff_section.virtual_address;275 const addr = vaddr - coff_section.virtual_address;
287 const maybe_proc = pdb.getProcSym(module, addr);276 const maybe_proc = pdb.getProcSym(module, addr);
288 var symbols: std.ArrayList(std.debug.Symbol) = .empty;277 const compile_unit_name = fs.path.basename(module.obj_file_name);
289 errdefer symbols.deinit(gpa);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
291 if (maybe_proc) |proc| {284 if (maybe_proc) |proc| {
292 const offset_in_func = addr - proc.code_offset;285 const offset_in_func = addr - proc.code_offset;
...@@ -315,25 +308,43 @@ const Module = struct {...@@ -315,25 +308,43 @@ const Module = struct {
315 if (inline_site.inlinee == last_inlinee) continue;308 if (inline_site.inlinee == last_inlinee) continue;
316 last_inlinee = inline_site.inlinee;309 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
318 try symbols.append(gpa, .{321 try symbols.append(gpa, .{
319 .name = pdb.findInlineeName(inline_site.inlinee),322 .name = name,
320 .compile_unit_name = fs.path.basename(module.obj_file_name),323 .compile_unit_name = compile_unit_name,
321 .source_location = loc,324 .source_location = loc,
322 });325 });
323 }326 }
324327
325 // Inline sites are stored in the pdb in reverse order, so we reverse the328 if (resolve_inline_callers) {
326 // matching sites here. We could alternatively use the parent fields to329 // Inline sites are stored in the pdb in reverse order, so we reverse the
327 // determine the order, but this would introduce seemingly unecessary330 // matching sites here. We could alternatively use the parent fields to
328 // complexity.331 // determine the order, but this would introduce seemingly unecessary
329 std.mem.reverse(std.debug.Symbol, symbols.items);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 }
330 }338 }
331339
332 try symbols.append(gpa, .{340 // If there's room for another symbol, add the actual proc
333 .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null,341 if (resolve_inline_callers or symbols.items.len == 0) {
334 .compile_unit_name = fs.path.basename(module.obj_file_name),342 try symbols.append(gpa, .{
335 .source_location = pdb.getLineNumberInfo(module, addr) catch null,343 .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null,
336 });344 .compile_unit_name = compile_unit_name,
345 .source_location = pdb.getLineNumberInfo(module, addr) catch null,
346 });
347 }
337348
338 return symbols.toOwnedSlice(gpa);349 return symbols.toOwnedSlice(gpa);
339 }350 }
...@@ -341,7 +352,7 @@ const Module = struct {...@@ -341,7 +352,7 @@ const Module = struct {
341 dwarf: {352 dwarf: {
342 const dwarf = &(di.dwarf orelse break :dwarf);353 const dwarf = &(di.dwarf orelse break :dwarf);
343 const addr = vaddr + di.coff_image_base;354 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);
345 }356 }
346357
347 return error.MissingDebugInfo;358 return error.MissingDebugInfo;