authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 14:50:34-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 23:50:24-07:00
log4ad665d3c8cd6a9d4f6b0e2f065098436142b450
tree26e3f51669fd2a2c44286f2d912e47d4001b3cf9
parente968e6d00473a0fb9de86ae99400503e4e40f0ef

Writes symbols to array list argument


5 files changed, 79 insertions(+), 61 deletions(-)

lib/std/debug.zig+19-10
...@@ -38,8 +38,8 @@ pub const cpu_context = @import("debug/cpu_context.zig");...@@ -38,8 +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`.41/// /// Appends the symbols for the instruction at `address` to `symbols`.
42/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, include_inline_callers: bool) SelfInfoError![]Symbol;42/// pub fn getSymbols(si: *SelfInfo, io: Io, gpa: Allocator, address: usize, include_inline_callers: bool, symbols: *std.ArrayList(Symbol)) SelfInfoError!void;
43/// /// 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`.
44/// 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;
45/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;45/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
...@@ -1190,8 +1190,17 @@ fn printSourceAtAddress(...@@ -1190,8 +1190,17 @@ fn printSourceAtAddress(
1190 t: Io.Terminal,1190 t: Io.Terminal,
1191 options: PrintSourceAddressOptions,1191 options: PrintSourceAddressOptions,
1192) Writer.Error!void {1192) Writer.Error!void {
1193 const gpa = getDebugInfoAllocator();1193 // In the common case where there's only one symbol, allocate it on the stack. Reserve enough
1194 const symbols: []Symbol = debug_info.getSymbols(io, options.address, options.resolve_inline_callers) catch |err| {1194 // space for one item regardless of alignment.
1195 var stack_fallback = std.heap.stackFallback(@sizeOf(Symbol) + @alignOf(Symbol) - 1, getDebugInfoAllocator());
1196 const sfa = stack_fallback.get();
1197 var symbols = std.ArrayList(Symbol).initCapacity(sfa, 1) catch unreachable;
1198 defer {
1199 for (symbols.items) |*symbol| symbol.deinit(sfa);
1200 symbols.deinit(sfa);
1201 }
1202
1203 debug_info.getSymbols(io, sfa, options.address, options.resolve_inline_callers, &symbols) catch |err| {
1195 t.setColor(.dim) catch {};1204 t.setColor(.dim) catch {};
1196 defer t.setColor(.reset) catch {};1205 defer t.setColor(.reset) catch {};
1197 switch (err) {1206 switch (err) {
...@@ -1208,13 +1217,13 @@ fn printSourceAtAddress(...@@ -1208,13 +1217,13 @@ fn printSourceAtAddress(
1208 t.setColor(.reset) catch {};1217 t.setColor(.reset) catch {};
1209 },1218 },
1210 }1219 }
1211 return printLineInfo(io, t, debug_info, null, options.address, null, null);
1212 };1220 };
1213 defer {1221
1214 for (symbols) |*symbol| symbol.deinit(gpa);1222 // If we failed to get any symbols, append the unknown symbol. We initialized with a capacity of
1215 gpa.free(symbols);1223 // one using a stack fallback allocator so this can't fail.
1216 }1224 if (symbols.items.len == 0) symbols.appendAssumeCapacity(.unknown);
1217 for (symbols) |symbol| {1225
1226 for (symbols.items) |symbol| {
1218 try printLineInfo(1227 try printLineInfo(
1219 io,1228 io,
1220 t,1229 t,
lib/std/debug/Dwarf.zig+12-13
...@@ -1545,22 +1545,22 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {...@@ -1545,22 +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, resolve_inline_callers: bool) std.debug.SelfInfoError![]std.debug.Symbol {1548pub fn getSymbols(
1549 di: *Dwarf,
1550 gpa: Allocator,
1551 endian: Endian,
1552 address: u64,
1553 resolve_inline_callers: bool,
1554 symbols: *std.ArrayList(std.debug.Symbol),
1555) std.debug.SelfInfoError!void {
1549 _ = resolve_inline_callers;1556 _ = resolve_inline_callers;
15501557
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 }
1556 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {1558 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
1557 error.EndOfStream, error.Overflow => {1559 error.EndOfStream => return error.MissingDebugInfo,
1558 symbols.appendAssumeCapacity(.unknown);1560 error.Overflow => return error.InvalidDebugInfo,
1559 return symbols.toOwnedSlice(gpa);1561 error.ReadFailed, error.InvalidDebugInfo, error.MissingDebugInfo => |e| return e,
1560 },
1561 else => |e| return e,
1562 };1562 };
1563 symbols.appendAssumeCapacity(.{1563 try symbols.append(gpa, .{
1564 .name = di.getSymbolName(address),1564 .name = di.getSymbolName(address),
1565 .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) {
1566 error.MissingDebugInfo, error.InvalidDebugInfo => null,1566 error.MissingDebugInfo, error.InvalidDebugInfo => null,
...@@ -1575,7 +1575,6 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64, reso...@@ -1575,7 +1575,6 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64, reso
1575 else => |e| return e,1575 else => |e| return e,
1576 },1576 },
1577 });1577 });
1578 return symbols.toOwnedSlice(gpa);
1579}1578}
15801579
1581/// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and1580/// 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-10
...@@ -30,8 +30,14 @@ pub fn deinit(si: *SelfInfo, io: Io) void {...@@ -30,8 +30,14 @@ 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, resolve_inline_callers: bool) Error![]std.debug.Symbol {33pub fn getSymbols(
34 const gpa = std.debug.getDebugInfoAllocator();34 si: *SelfInfo,
35 io: Io,
36 gpa: Allocator,
37 address: usize,
38 resolve_inline_callers: bool,
39 symbols: *std.ArrayList(std.debug.Symbol),
40) Error!void {
35 const module = try si.findModule(gpa, io, address, .exclusive);41 const module = try si.findModule(gpa, io, address, .exclusive);
36 defer si.rwlock.unlock(io);42 defer si.rwlock.unlock(io);
3743
...@@ -53,20 +59,14 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:...@@ -53,20 +59,14 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:
53 };59 };
54 loaded_elf.scanned_dwarf = true;60 loaded_elf.scanned_dwarf = true;
55 }61 }
56 return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers);62 return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers, symbols);
57 }63 }
58 // When DWARF is unavailable, fall back to searching the symtab.64 // When DWARF is unavailable, fall back to searching the symtab.
59 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);65 try symbols.append(gpa, loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
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) {
65 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,66 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
66 error.BadSymtab => return error.InvalidDebugInfo,67 error.BadSymtab => return error.InvalidDebugInfo,
67 error.OutOfMemory => |e| return e,68 error.OutOfMemory => |e| return e,
68 });69 });
69 return symbols.toOwnedSlice(gpa);
70}70}
71pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {71pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
72 const gpa = std.debug.getDebugInfoAllocator();72 const gpa = std.debug.getDebugInfoAllocator();
lib/std/debug/SelfInfo/MachO.zig+11-14
...@@ -22,21 +22,21 @@ pub fn deinit(si: *SelfInfo, io: Io) void {...@@ -22,21 +22,21 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
22 si.modules.deinit(gpa);22 si.modules.deinit(gpa);
23}23}
2424
25pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {25pub fn getSymbols(
26 si: *SelfInfo,
27 io: Io,
28 gpa: Allocator,
29 address: usize,
30 resolve_inline_callers: bool,
31 symbols: *std.ArrayList(std.debug.Symbol),
32) Error!void {
26 _ = resolve_inline_callers;33 _ = resolve_inline_callers;
2734
28 const gpa = std.debug.getDebugInfoAllocator();
29 const module = try si.findModule(gpa, io, address);35 const module = try si.findModule(gpa, io, address);
30 defer si.mutex.unlock(io);36 defer si.mutex.unlock(io);
3137
32 const file = try module.getFile(gpa, io);38 const file = try module.getFile(gpa, io);
3339
34 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
35 errdefer {
36 for (symbols.items) |*symbol| symbol.deinit(gpa);
37 symbols.deinit(gpa);
38 }
39
40 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is40 // This is not necessarily the same as the vmaddr_slide that dyld would report. This is
41 // because the segments in the file on disk might differ from the ones in memory. Normally41 // because the segments in the file on disk might differ from the ones in memory. Normally
42 // we wouldn't necessarily expect that to work, but /usr/lib/dyld is incredibly annoying:42 // we wouldn't necessarily expect that to work, but /usr/lib/dyld is incredibly annoying:
...@@ -51,25 +51,23 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:...@@ -51,25 +51,23 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:
5151
52 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {52 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
53 // Return at least the symbol name if available.53 // Return at least the symbol name if available.
54 symbols.appendAssumeCapacity(.{54 return symbols.append(gpa, .{
55 .name = try file.lookupSymbolName(vaddr),55 .name = try file.lookupSymbolName(vaddr),
56 .compile_unit_name = null,56 .compile_unit_name = null,
57 .source_location = null,57 .source_location = null,
58 });58 });
59 return symbols.toOwnedSlice(gpa);
60 };59 };
6160
62 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {61 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
63 // Return at least the symbol name if available.62 // Return at least the symbol name if available.
64 symbols.appendAssumeCapacity(.{63 return symbols.append(gpa, .{
65 .name = try file.lookupSymbolName(vaddr),64 .name = try file.lookupSymbolName(vaddr),
66 .compile_unit_name = null,65 .compile_unit_name = null,
67 .source_location = null,66 .source_location = null,
68 });67 });
69 return symbols.toOwnedSlice(gpa);
70 };68 };
7169
72 symbols.appendAssumeCapacity(.{70 try symbols.append(gpa, .{
73 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse71 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
74 try file.lookupSymbolName(vaddr),72 try file.lookupSymbolName(vaddr),
75 .compile_unit_name = compile_unit.die.getAttrString(73 .compile_unit_name = compile_unit.die.getAttrString(
...@@ -88,7 +86,6 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:...@@ -88,7 +86,6 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:
88 ofile_vaddr,86 ofile_vaddr,
89 ) catch null,87 ) catch null,
90 });88 });
91 return symbols.toOwnedSlice(gpa);
92}89}
93pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {90pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
94 _ = si;91 _ = si;
lib/std/debug/SelfInfo/Windows.zig+27-14
...@@ -25,13 +25,24 @@ pub fn deinit(si: *SelfInfo, io: Io) void {...@@ -25,13 +25,24 @@ 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, resolve_inline_callers: bool) Error![]std.debug.Symbol {28pub fn getSymbols(
29 const gpa = std.debug.getDebugInfoAllocator();29 si: *SelfInfo,
30 io: Io,
31 gpa: Allocator,
32 address: usize,
33 resolve_inline_callers: bool,
34 symbols: *std.ArrayList(std.debug.Symbol),
35) Error!void {
30 try si.lock.lockShared(io);36 try si.lock.lockShared(io);
31 defer si.lock.unlockShared(io);37 defer si.lock.unlockShared(io);
32 const module = try si.findModule(gpa, address);38 const module = try si.findModule(gpa, address);
33 const di = try module.getDebugInfo(gpa, io);39 const di = try module.getDebugInfo(gpa, io);
34 return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase), resolve_inline_callers);40 return di.getSymbols(
41 gpa,
42 address - @intFromPtr(module.entry.DllBase),
43 resolve_inline_callers,
44 symbols,
45 );
35}46}
3647
37pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {48pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
...@@ -241,7 +252,13 @@ const Module = struct {...@@ -241,7 +252,13 @@ const Module = struct {
241 arena.deinit();252 arena.deinit();
242 }253 }
243254
244 fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {255 fn getSymbols(
256 di: *DebugInfo,
257 gpa: Allocator,
258 vaddr: usize,
259 resolve_inline_callers: bool,
260 symbols: *std.ArrayList(std.debug.Symbol),
261 ) Error!void {
245 pdb: {262 pdb: {
246 const pdb = &(di.pdb orelse break :pdb);263 const pdb = &(di.pdb orelse break :pdb);
247 var coff_section: *align(1) const coff.SectionHeader = undefined;264 var coff_section: *align(1) const coff.SectionHeader = undefined;
...@@ -275,12 +292,7 @@ const Module = struct {...@@ -275,12 +292,7 @@ const Module = struct {
275 const addr = vaddr - coff_section.virtual_address;292 const addr = vaddr - coff_section.virtual_address;
276 const maybe_proc = pdb.getProcSym(module, addr);293 const maybe_proc = pdb.getProcSym(module, addr);
277 const compile_unit_name = fs.path.basename(module.obj_file_name);294 const compile_unit_name = fs.path.basename(module.obj_file_name);
278 var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);295 const symbols_top = symbols.items.len;
279 errdefer {
280 for (symbols.items) |*symbol| symbol.deinit(gpa);
281 symbols.deinit(gpa);
282 }
283
284 if (maybe_proc) |proc| {296 if (maybe_proc) |proc| {
285 const offset_in_func = addr - proc.code_offset;297 const offset_in_func = addr - proc.code_offset;
286 var last_inlinee: ?u32 = null;298 var last_inlinee: ?u32 = null;
...@@ -308,9 +320,10 @@ const Module = struct {...@@ -308,9 +320,10 @@ const Module = struct {
308 const loc = maybe_loc orelse continue;320 const loc = maybe_loc orelse continue;
309321
310 // If we aren't trying to resolve inline callers, and we've matched a322 // If we aren't trying to resolve inline callers, and we've matched a
311 // new inline site, we want to overwrite the previous results.323 // new inline site, we want to overwrite the previously appended
324 // results.
312 if (!resolve_inline_callers and inline_site.inlinee != last_inlinee) {325 if (!resolve_inline_callers and inline_site.inlinee != last_inlinee) {
313 symbols.items.len = 0;326 symbols.items.len = symbols_top;
314 }327 }
315328
316 // Only resolve the name if we're resolving inline callers, otherwise329 // Only resolve the name if we're resolving inline callers, otherwise
...@@ -353,13 +366,13 @@ const Module = struct {...@@ -353,13 +366,13 @@ const Module = struct {
353 });366 });
354 }367 }
355368
356 return symbols.toOwnedSlice(gpa);369 return;
357 }370 }
358371
359 dwarf: {372 dwarf: {
360 const dwarf = &(di.dwarf orelse break :dwarf);373 const dwarf = &(di.dwarf orelse break :dwarf);
361 const addr = vaddr + di.coff_image_base;374 const addr = vaddr + di.coff_image_base;
362 return dwarf.getSymbols(gpa, native_endian, addr, resolve_inline_callers);375 return dwarf.getSymbols(gpa, native_endian, addr, resolve_inline_callers, symbols);
363 }376 }
364377
365 return error.MissingDebugInfo;378 return error.MissingDebugInfo;