From 4ad665d3c8cd6a9d4f6b0e2f065098436142b450 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sun, 12 Apr 2026 14:50:34 -0700 Subject: [PATCH] Writes symbols to array list argument --- lib/std/debug.zig | 29 +++++++++++++-------- lib/std/debug/Dwarf.zig | 25 +++++++++--------- lib/std/debug/SelfInfo/Elf.zig | 20 +++++++-------- lib/std/debug/SelfInfo/MachO.zig | 25 ++++++++---------- lib/std/debug/SelfInfo/Windows.zig | 41 ++++++++++++++++++++---------- 5 files changed, 79 insertions(+), 61 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 80dfb14459..e4a91a6fc5 100644 --- 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 index 8ffddf4b39..599597759b 100644 --- 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 index bd045126ff..bc607569f8 100644 --- 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 index 7618776434..e53e39f8e5 100644 --- 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 index 1a888e7205..686dec4170 100644 --- 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;