From 4efbb27aa2b841357c03d6ab158418ccad022415 Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sat, 11 Apr 2026 00:28:48 -0700 Subject: [PATCH] Don't bother resolving symbol names that won't be used Also fixes some memory management issues --- lib/std/debug.zig | 22 ++++++---- lib/std/debug/Dwarf.zig | 21 +++++---- lib/std/debug/SelfInfo/Elf.zig | 27 +++++------- lib/std/debug/SelfInfo/MachO.zig | 39 ++++++++--------- lib/std/debug/SelfInfo/Windows.zig | 69 +++++++++++++++++------------- 5 files changed, 93 insertions(+), 85 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 9ff9601478..1cda6b5018 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -38,12 +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`. Often this -/// /// will return a single result, but in the case of inlines it may return multiple. When -/// /// multiple results are returned, they are sorted from innermost to outermost. -/// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const Symbol; -/// /// Frees symbols returned from `getSymbols`. -/// pub fn freeSymbols(si: *SelfInfo, symbols: []const Symbol) 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; /// /// 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; @@ -233,6 +229,11 @@ pub const Symbol = struct { .compile_unit_name = null, .source_location = null, }; + + pub fn deinit(self: *Symbol, gpa: Allocator) void { + if (self.source_location) |sl| gpa.free(sl.file_name); + self.* = undefined; + } }; /// Deprecated because it returns the optimization mode of the standard @@ -1192,7 +1193,8 @@ fn printSourceAtAddress( t: Io.Terminal, options: PrintSourceAddressOptions, ) Writer.Error!void { - const symbols: []const Symbol = debug_info.getSymbols(io, options.address) catch |err| { + const gpa = getDebugInfoAllocator(); + const symbols: []Symbol = debug_info.getSymbols(io, options.address, options.resolve_inline_callers) catch |err| { t.setColor(.dim) catch {}; defer t.setColor(.reset) catch {}; switch (err) { @@ -1211,7 +1213,10 @@ fn printSourceAtAddress( } return printLineInfo(io, t, debug_info, null, options.address, null, null); }; - defer debug_info.freeSymbols(symbols); + defer { + for (symbols) |*symbol| symbol.deinit(gpa); + gpa.free(symbols); + } for (symbols) |symbol| { try printLineInfo( io, @@ -1222,7 +1227,6 @@ fn printSourceAtAddress( symbol.name, symbol.compile_unit_name, ); - if (!options.resolve_inline_callers) break; } } fn printLineInfo( diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 24288a515e..8ffddf4b39 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1545,17 +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) std.debug.SelfInfoError![]const std.debug.Symbol { - const symbol = try gpa.create(std.debug.Symbol); - errdefer gpa.destroy(symbol); +pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64, resolve_inline_callers: bool) std.debug.SelfInfoError![]std.debug.Symbol { + _ = 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 => { - symbol.* = .unknown; - return symbol[0..1]; + symbols.appendAssumeCapacity(.unknown); + return symbols.toOwnedSlice(gpa); }, else => |e| return e, }; - symbol.* = .{ + symbols.appendAssumeCapacity(.{ .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, @@ -1569,8 +1574,8 @@ pub fn getSymbols(di: *Dwarf, gpa: Allocator, endian: Endian, address: u64) std. => return error.InvalidDebugInfo, else => |e| return e, }, - }; - return symbol[0..1]; + }); + 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 71f172200f..bd045126ff 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -30,7 +30,7 @@ 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) Error![]const std.debug.Symbol { +pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol { const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, address, .exclusive); defer si.rwlock.unlock(io); @@ -53,27 +53,20 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug }; loaded_elf.scanned_dwarf = true; } - return dwarf.getSymbols(gpa, native_endian, vaddr); + return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers); } // When DWARF is unavailable, fall back to searching the symtab. - const symbol = try gpa.create(std.debug.Symbol); - errdefer gpa.destroy(symbol); - symbol.* = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { + 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) { error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo, error.BadSymtab => return error.InvalidDebugInfo, error.OutOfMemory => |e| return e, - }; - return symbol[0..1]; -} -pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void { - _ = si; - const gpa = std.debug.getDebugInfoAllocator(); - for (symbols) |symbol| { - if (symbol.source_location) |source_location| { - gpa.free(source_location.file_name); - } - } - gpa.free(symbols); + }); + 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 d8f73976d0..894a4163da 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -36,15 +36,20 @@ pub const SymbolIterator = struct { } }; -pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol { +pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol { + _ = 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); - const symbol = try gpa.create(std.debug.Symbol); - errdefer gpa.destroy(symbol); + 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 @@ -60,25 +65,25 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch { // Return at least the symbol name if available. - symbol.* = .{ + symbols.appendAssumeCapacity(.{ .name = try file.lookupSymbolName(vaddr), .compile_unit_name = null, .source_location = null, - }; - return symbol[0..1]; + }); + return symbols.toOwnedSlice(gpa); }; const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch { // Return at least the symbol name if available. - symbol.* = .{ + symbols.appendAssumeCapacity(.{ .name = try file.lookupSymbolName(vaddr), .compile_unit_name = null, .source_location = null, - }; - return symbol[0..1]; + }); + return symbols.toOwnedSlice(gpa); }; - symbol.* = .{ + symbols.appendAssumeCapacity(.{ .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse try file.lookupSymbolName(vaddr), .compile_unit_name = compile_unit.die.getAttrString( @@ -96,18 +101,8 @@ pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug compile_unit, ofile_vaddr, ) catch null, - }; - return symbol[0..1]; -} -pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void { - _ = si; - const gpa = std.debug.getDebugInfoAllocator(); - for (symbols) |symbol| { - if (symbol.source_location) |source_location| { - gpa.free(source_location.file_name); - } - } - gpa.free(symbols); + }); + 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 d2a28803b1..3a4d7b6878 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -25,24 +25,13 @@ pub fn deinit(si: *SelfInfo, io: Io) void { si.modules.deinit(gpa); } -pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) Error![]const std.debug.Symbol { +pub fn getSymbols(si: *SelfInfo, io: Io, address: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol { const gpa = std.debug.getDebugInfoAllocator(); 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)); -} - -pub fn freeSymbols(si: *SelfInfo, symbols: []const std.debug.Symbol) void { - _ = si; - const gpa = std.debug.getDebugInfoAllocator(); - for (symbols) |symbol| { - if (symbol.source_location) |source_location| { - gpa.free(source_location.file_name); - } - } - gpa.free(symbols); + return di.getSymbols(gpa, address - @intFromPtr(module.entry.DllBase), resolve_inline_callers); } pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { @@ -252,7 +241,7 @@ const Module = struct { arena.deinit(); } - fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize) Error![]const std.debug.Symbol { + fn getSymbols(di: *DebugInfo, gpa: Allocator, vaddr: usize, resolve_inline_callers: bool) Error![]std.debug.Symbol { pdb: { const pdb = &(di.pdb orelse break :pdb); var coff_section: *align(1) const coff.SectionHeader = undefined; @@ -285,8 +274,12 @@ const Module = struct { const addr = vaddr - coff_section.virtual_address; const maybe_proc = pdb.getProcSym(module, addr); - var symbols: std.ArrayList(std.debug.Symbol) = .empty; - errdefer symbols.deinit(gpa); + 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); + } if (maybe_proc) |proc| { const offset_in_func = addr - proc.code_offset; @@ -315,25 +308,43 @@ const Module = struct { if (inline_site.inlinee == last_inlinee) continue; last_inlinee = inline_site.inlinee; + // If we're appending this symbol, resolve the name. If we're replacing the + // last symbol, clear the previous symbols and wait to resolve the name + // until we've reached the last symbol to avoid doing work and then + // throwing it out. + const name = b: { + if (resolve_inline_callers) break :b pdb.findInlineeName(inline_site.inlinee); + symbols.items.len = 0; + break :b null; + }; + try symbols.append(gpa, .{ - .name = pdb.findInlineeName(inline_site.inlinee), - .compile_unit_name = fs.path.basename(module.obj_file_name), + .name = name, + .compile_unit_name = compile_unit_name, .source_location = loc, }); } - // Inline sites are stored in the pdb in reverse order, so we reverse the - // matching sites here. We could alternatively use the parent fields to - // determine the order, but this would introduce seemingly unecessary - // complexity. - std.mem.reverse(std.debug.Symbol, symbols.items); + if (resolve_inline_callers) { + // Inline sites are stored in the pdb in reverse order, so we reverse the + // matching sites here. We could alternatively use the parent fields to + // determine the order, but this would introduce seemingly unecessary + // complexity. + std.mem.reverse(std.debug.Symbol, symbols.items); + } else if (last_inlinee) |inlinee| { + // If we haven't resolved the name yet, resolve it now + symbols.items[symbols.items.len - 1].name = pdb.findInlineeName(inlinee); + } } - try symbols.append(gpa, .{ - .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null, - .compile_unit_name = fs.path.basename(module.obj_file_name), - .source_location = pdb.getLineNumberInfo(module, addr) catch null, - }); + // If there's room for another symbol, add the actual proc + if (resolve_inline_callers or symbols.items.len == 0) { + try symbols.append(gpa, .{ + .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null, + .compile_unit_name = compile_unit_name, + .source_location = pdb.getLineNumberInfo(module, addr) catch null, + }); + } return symbols.toOwnedSlice(gpa); } @@ -341,7 +352,7 @@ const Module = struct { dwarf: { const dwarf = &(di.dwarf orelse break :dwarf); const addr = vaddr + di.coff_image_base; - return dwarf.getSymbols(gpa, native_endian, addr); + return dwarf.getSymbols(gpa, native_endian, addr, resolve_inline_callers); } return error.MissingDebugInfo;