commit 4ad665d3c8cd6a9d4f6b0e2f065098436142b450 (tree)
parent e968e6d00473a0fb9de86ae99400503e4e40f0ef
Author: Mason Remaley <mason@gamesbymason.com>
Date: Sun, 12 Apr 2026 14:50:34 -0700
Writes symbols to array list argument
Diffstat:
5 files changed, 79 insertions(+), 61 deletions(-)
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
@@ -38,8 +38,8 @@ pub const cpu_context = @import("debug/cpu_context.zig");
/// pub const init: SelfInfo;
/// pub fn deinit(si: *SelfInfo, io: Io) void;
///
-/// /// Returns the the symbols and source locations of the instruction at `address`.
-/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, include_inline_callers: bool) SelfInfoError![]Symbol;
+/// /// Appends the symbols for the instruction at `address` to `symbols`.
+/// pub fn getSymbols(si: *SelfInfo, io: Io, gpa: Allocator, address: usize, include_inline_callers: bool, symbols: *std.ArrayList(Symbol)) SelfInfoError!void;
/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
@@ -1190,8 +1190,17 @@ fn printSourceAtAddress(
t: Io.Terminal,
options: PrintSourceAddressOptions,
) Writer.Error!void {
- const gpa = getDebugInfoAllocator();
- const symbols: []Symbol = debug_info.getSymbols(io, options.address, options.resolve_inline_callers) catch |err| {
+ // In the common case where there's only one symbol, allocate it on the stack. Reserve enough
+ // space for one item regardless of alignment.
+ var stack_fallback = std.heap.stackFallback(@sizeOf(Symbol) + @alignOf(Symbol) - 1, getDebugInfoAllocator());
+ const sfa = stack_fallback.get();
+ var symbols = std.ArrayList(Symbol).initCapacity(sfa, 1) catch unreachable;
+ defer {
+ for (symbols.items) |*symbol| symbol.deinit(sfa);
+ symbols.deinit(sfa);
+ }
+
+ debug_info.getSymbols(io, sfa, options.address, options.resolve_inline_callers, &symbols) catch |err| {
t.setColor(.dim) catch {};
defer t.setColor(.reset) catch {};
switch (err) {
@@ -1208,13 +1217,13 @@ fn printSourceAtAddress(
t.setColor(.reset) catch {};
},
}
- return printLineInfo(io, t, debug_info, null, options.address, null, null);
};
- defer {
- for (symbols) |*symbol| symbol.deinit(gpa);
- gpa.free(symbols);
- }
- for (symbols) |symbol| {
+
+ // If we failed to get any symbols, append the unknown symbol. We initialized with a capacity of
+ // one using a stack fallback allocator so this can't fail.
+ if (symbols.items.len == 0) symbols.appendAssumeCapacity(.unknown);
+
+ for (symbols.items) |symbol| {
try printLineInfo(
io,
t,
diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig
@@ -1545,22 +1545,22 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
return str[casted_offset..last :0];
}
-pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64, resolve_inline_callers: bool) std.debug.SelfInfoError![]std.debug.Symbol {
+pub fn getSymbols(
+ di: *Dwarf,
+ gpa: Allocator,
+ endian: Endian,
+ address: u64,
+ resolve_inline_callers: bool,
+ symbols: *std.ArrayList(std.debug.Symbol),
+) std.debug.SelfInfoError!void {
_ = resolve_inline_callers;
- var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
- errdefer {
- for (symbols.items) |*symbol| symbol.deinit(gpa);
- symbols.deinit(gpa);
- }
const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
- error.EndOfStream, error.Overflow => {
- symbols.appendAssumeCapacity(.unknown);
- return symbols.toOwnedSlice(gpa);
- },
- else => |e| return e,
+ error.EndOfStream => return error.MissingDebugInfo,
+ error.Overflow => return error.InvalidDebugInfo,
+ error.ReadFailed, error.InvalidDebugInfo, error.MissingDebugInfo => |e| return e,
};
- symbols.appendAssumeCapacity(.{
+ try symbols.append(gpa, .{
.name = di.getSymbolName(address),
.compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
error.MissingDebugInfo, error.InvalidDebugInfo => null,
@@ -1575,7 +1575,6 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64, reso
else => |e| return e,
},
});
- return symbols.toOwnedSlice(gpa);
}
/// DWARF5 7.4: "In the 32-bit DWARF format, all values that represent lengths of DWARF sections and
diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig
@@ -30,8 +30,14 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
if (si.unwind_cache) |cache| gpa.free(cache);
}
-pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
- const gpa = std.debug.getDebugInfoAllocator();
+pub fn getSymbols(
+ si: *SelfInfo,
+ io: Io,
+ gpa: Allocator,
+ address: usize,
+ resolve_inline_callers: bool,
+ symbols: *std.ArrayList(std.debug.Symbol),
+) Error!void {
const module = try si.findModule(gpa, io, address, .exclusive);
defer si.rwlock.unlock(io);
@@ -53,20 +59,14 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:
};
loaded_elf.scanned_dwarf = true;
}
- return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers);
+ return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers, symbols);
}
// When DWARF is unavailable, fall back to searching the symtab.
- var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
- errdefer {
- for (symbols.items) |*symbol| symbol.deinit(gpa);
- symbols.deinit(gpa);
- }
- symbols.appendAssumeCapacity(loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
+ try symbols.append(gpa, loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
error.BadSymtab => return error.InvalidDebugInfo,
error.OutOfMemory => |e| return e,
});
- return symbols.toOwnedSlice(gpa);
}
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
const gpa = std.debug.getDebugInfoAllocator();
diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig
@@ -22,21 +22,21 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
si.modules.deinit(gpa);
}
-pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
+pub fn getSymbols(
+ si: *SelfInfo,
+ io: Io,
+ gpa: Allocator,
+ address: usize,
+ resolve_inline_callers: bool,
+ symbols: *std.ArrayList(std.debug.Symbol),
+) Error!void {
_ = resolve_inline_callers;
- const gpa = std.debug.getDebugInfoAllocator();
const module = try si.findModule(gpa, io, address);
defer si.mutex.unlock(io);
const file = try module.getFile(gpa, io);
- var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
- errdefer {
- for (symbols.items) |*symbol| symbol.deinit(gpa);
- symbols.deinit(gpa);
- }
-
// This is not necessarily the same as the vmaddr_slide that dyld would report. This is
// because the segments in the file on disk might differ from the ones in memory. Normally
// 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:
const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
// Return at least the symbol name if available.
- symbols.appendAssumeCapacity(.{
+ return symbols.append(gpa, .{
.name = try file.lookupSymbolName(vaddr),
.compile_unit_name = null,
.source_location = null,
});
- return symbols.toOwnedSlice(gpa);
};
const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
// Return at least the symbol name if available.
- symbols.appendAssumeCapacity(.{
+ return symbols.append(gpa, .{
.name = try file.lookupSymbolName(vaddr),
.compile_unit_name = null,
.source_location = null,
});
- return symbols.toOwnedSlice(gpa);
};
- symbols.appendAssumeCapacity(.{
+ try symbols.append(gpa, .{
.name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
try file.lookupSymbolName(vaddr),
.compile_unit_name = compile_unit.die.getAttrString(
@@ -88,7 +86,6 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers:
ofile_vaddr,
) catch null,
});
- return symbols.toOwnedSlice(gpa);
}
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
_ = si;
diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig
@@ -25,13 +25,24 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
si.modules.deinit(gpa);
}
-pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
- const gpa = std.debug.getDebugInfoAllocator();
+pub fn getSymbols(
+ si: *SelfInfo,
+ io: Io,
+ gpa: Allocator,
+ address: usize,
+ resolve_inline_callers: bool,
+ symbols: *std.ArrayList(std.debug.Symbol),
+) Error!void {
try si.lock.lockShared(io);
defer si.lock.unlockShared(io);
const module = try si.findModule(gpa, address);
const di = try module.getDebugInfo(gpa, io);
- return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase), resolve_inline_callers);
+ return di.getSymbols(
+ gpa,
+ address - @intFromPtr(module.entry.DllBase),
+ resolve_inline_callers,
+ symbols,
+ );
}
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
@@ -241,7 +252,13 @@ const Module = struct {
arena.deinit();
}
- fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol {
+ fn getSymbols(
+ di: *DebugInfo,
+ gpa: Allocator,
+ vaddr: usize,
+ resolve_inline_callers: bool,
+ symbols: *std.ArrayList(std.debug.Symbol),
+ ) Error!void {
pdb: {
const pdb = &(di.pdb orelse break :pdb);
var coff_section: *align(1) const coff.SectionHeader = undefined;
@@ -275,12 +292,7 @@ const Module = struct {
const addr = vaddr - coff_section.virtual_address;
const maybe_proc = pdb.getProcSym(module, addr);
const compile_unit_name = fs.path.basename(module.obj_file_name);
- var symbols: std.ArrayList(std.debug.Symbol) = try .initCapacity(gpa, 1);
- errdefer {
- for (symbols.items) |*symbol| symbol.deinit(gpa);
- symbols.deinit(gpa);
- }
-
+ const symbols_top = symbols.items.len;
if (maybe_proc) |proc| {
const offset_in_func = addr - proc.code_offset;
var last_inlinee: ?u32 = null;
@@ -308,9 +320,10 @@ const Module = struct {
const loc = maybe_loc orelse continue;
// If we aren't trying to resolve inline callers, and we've matched a
- // new inline site, we want to overwrite the previous results.
+ // new inline site, we want to overwrite the previously appended
+ // results.
if (!resolve_inline_callers and inline_site.inlinee != last_inlinee) {
- symbols.items.len = 0;
+ symbols.items.len = symbols_top;
}
// Only resolve the name if we're resolving inline callers, otherwise
@@ -353,13 +366,13 @@ const Module = struct {
});
}
- return symbols.toOwnedSlice(gpa);
+ return;
}
dwarf: {
const dwarf = &(di.dwarf orelse break :dwarf);
const addr = vaddr + di.coff_image_base;
- return dwarf.getSymbols(gpa, native_endian, addr, resolve_inline_callers);
+ return dwarf.getSymbols(gpa, native_endian, addr, resolve_inline_callers, symbols);
}
return error.MissingDebugInfo;