diff --git a/lib/std/debug.zig b/lib/std/debug.zig index e4a91a6fc5e82df20cf1fb1835d9f2e63fa9dc77..3dac7229f782d5ee834ae0224929b8cd1c805c50 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -39,7 +39,7 @@ pub const cpu_context = @import("debug/cpu_context.zig"); /// pub fn deinit(si: *SelfInfo, io: Io) void; /// /// /// 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; +/// pub fn getSymbols(si: *SelfInfo, io: Io, symbol_allocator: Allocator, text_arena: 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; @@ -229,11 +229,6 @@ 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 @@ -699,6 +694,10 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: /// See `captureCurrentStackTrace` to capture the trace addresses into a buffer instead of printing. pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Terminal) Writer.Error!void { const writer = t.writer; + + var text_arena: std.heap.ArenaAllocator = .init(getDebugInfoAllocator()); + defer text_arena.deinit(); + if (!std.options.allow_stack_tracing) { t.setColor(.dim) catch {}; try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{}); @@ -776,7 +775,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin } // `ret_addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(io, di, t, .{ + try printSourceAtAddress(io, &text_arena, di, t, .{ .address = ret_addr -| StackIterator.ra_call_offset, .resolve_inline_callers = true, }); @@ -832,6 +831,9 @@ fn writeTrace( t: Io.Terminal, resolve_inline_callers: bool, ) Writer.Error!void { + var text_arena: std.heap.ArenaAllocator = .init(getDebugInfoAllocator()); + defer text_arena.deinit(); + const writer = t.writer; if (!std.options.allow_stack_tracing) { t.setColor(.dim) catch {}; @@ -853,7 +855,7 @@ fn writeTrace( for (addresses) |addr| { // `addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(io, di, t, .{ + try printSourceAtAddress(io, &text_arena, di, t, .{ .address = addr -| StackIterator.ra_call_offset, .resolve_inline_callers = resolve_inline_callers, }); @@ -1186,21 +1188,28 @@ const PrintSourceAddressOptions = struct { fn printSourceAtAddress( io: Io, + text_arena: *std.heap.ArenaAllocator, debug_info: *SelfInfo, t: Io.Terminal, options: PrintSourceAddressOptions, ) Writer.Error!void { - // 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); - } + defer _ = text_arena.reset(.retain_capacity); - debug_info.getSymbols(io, sfa, options.address, options.resolve_inline_callers, &symbols) catch |err| { + // Initialize the symbol array with space for at least one element, allocating this on the stack + // in the common case where only one element is needed + var symbol_fallback_allocator = std.heap.stackFallback(@sizeOf(Symbol) + @alignOf(Symbol) - 1, getDebugInfoAllocator()); + const symbol_allocator = symbol_fallback_allocator.get(); + var symbols = std.ArrayList(Symbol).initCapacity(symbol_allocator, 1) catch unreachable; + defer symbols.deinit(symbol_allocator); + + debug_info.getSymbols( + io, + symbol_allocator, + text_arena.allocator(), + options.address, + options.resolve_inline_callers, + &symbols, + ) catch |err| { t.setColor(.dim) catch {}; defer t.setColor(.reset) catch {}; switch (err) { @@ -1219,35 +1228,25 @@ fn printSourceAtAddress( } }; - // 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 we failed to write any symbols, at least write the unknown symbol. Can't fail since we + // initialized with a capacity of 1. if (symbols.items.len == 0) symbols.appendAssumeCapacity(.unknown); for (symbols.items) |symbol| { - try printLineInfo( - io, - t, - debug_info, - symbol.source_location, - options.address, - symbol.name, - symbol.compile_unit_name, - ); + try printLineInfo(io, t, debug_info, options.address, symbol); } } fn printLineInfo( io: Io, t: Io.Terminal, debug_info: *SelfInfo, - source_location: ?SourceLocation, address: usize, - symbol_name: ?[]const u8, - compile_unit_name: ?[]const u8, + symbol: Symbol, ) Writer.Error!void { const writer = t.writer; t.setColor(.bold) catch {}; - if (source_location) |*sl| { + if (symbol.source_location) |*sl| { if (sl.column == 0) { try writer.print("{s}:{d}", .{ sl.file_name, sl.line }); } else { @@ -1262,14 +1261,14 @@ fn printLineInfo( t.setColor(.dim) catch {}; try writer.print("0x{x} in {s} ({s})", .{ address, - symbol_name orelse "???", - compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", + symbol.name orelse "???", + symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", }); t.setColor(.reset) catch {}; try writer.writeAll("\n"); // Show the matching source code line if possible - if (source_location) |sl| { + if (symbol.source_location) |sl| { if (printLineFromFile(io, writer, sl)) { if (sl.column > 0) { // The caret already takes one char @@ -1708,7 +1707,9 @@ test "manage resources correctly" { var di: SelfInfo = .init; defer di.deinit(io); const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color }; - try printSourceAtAddress(io, &di, t, .{ + var text_arena: std.heap.ArenaAllocator = .init(std.testing.allocator); + defer text_arena.deinit(); + try printSourceAtAddress(io, &text_arena, &di, t, .{ .address = S.showMyTrace(), .resolve_inline_callers = true, }); diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 599597759b499b54c711b3662ed73991de2676b8..fe28baf8bffbab92b58276f3973726aeeb4876ba 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1220,6 +1220,7 @@ pub fn populateSrcLocCache(d: *Dwarf, gpa: Allocator, endian: Endian, cu: *Compi pub fn getLineNumberInfo( d: *Dwarf, gpa: Allocator, + text_arena: Allocator, endian: Endian, compile_unit: *CompileUnit, target_address: u64, @@ -1232,7 +1233,7 @@ pub fn getLineNumberInfo( const file_entry = &slc.files[file_index]; if (file_entry.dir_index >= slc.directories.len) return bad(); const dir_name = slc.directories[file_entry.dir_index].path; - const file_name = try std.fs.path.join(gpa, &.{ dir_name, file_entry.path }); + const file_name = try std.fs.path.join(text_arena, &.{ dir_name, file_entry.path }); return .{ .line = entry.line, .column = entry.column, @@ -1547,25 +1548,27 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 { pub fn getSymbols( di: *Dwarf, - gpa: Allocator, + symbol_allocator: Allocator, + text_arena: Allocator, endian: Endian, address: u64, resolve_inline_callers: bool, symbols: *std.ArrayList(std.debug.Symbol), ) std.debug.SelfInfoError!void { _ = resolve_inline_callers; + const gpa = std.debug.getDebugInfoAllocator(); const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) { error.EndOfStream => return error.MissingDebugInfo, error.Overflow => return error.InvalidDebugInfo, error.ReadFailed, error.InvalidDebugInfo, error.MissingDebugInfo => |e| return e, }; - try symbols.append(gpa, .{ + try symbols.append(symbol_allocator, .{ .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, }, - .source_location = di.getLineNumberInfo(gpa, endian, compile_unit, address) catch |err| switch (err) { + .source_location = di.getLineNumberInfo(gpa, text_arena, endian, compile_unit, address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => null, error.ReadFailed, error.EndOfStream, diff --git a/lib/std/debug/Pdb.zig b/lib/std/debug/Pdb.zig index 59a16968ff95e95ce86c0f4e1aca972677a3a26d..6c48d10dcd68fc7bab3c0122e4908006a8d65ac2 100644 --- a/lib/std/debug/Pdb.zig +++ b/lib/std/debug/Pdb.zig @@ -617,6 +617,7 @@ pub fn getBinaryAnnotations(self: *Pdb, module: *Module, site: *align(1) const p pub fn getInlineSiteSourceLocation( self: *Pdb, + gpa: Allocator, mod: *Module, site: *align(1) const pdb.InlineSiteSym, inlinee_src_line: *align(1) const pdb.InlineeSourceLine, @@ -627,7 +628,7 @@ pub fn getInlineSiteSourceLocation( if (!range.contains(offset_in_func)) continue; const file_id = range.file_id orelse inlinee_src_line.file_id; - const file_name = try self.getFileName(mod, file_id); + const file_name = try self.getFileName(gpa, mod, file_id); errdefer self.allocator.free(file_name); return .{ @@ -640,14 +641,14 @@ pub fn getInlineSiteSourceLocation( return null; } -pub fn getFileName(self: *Pdb, mod: *Module, file_id: u32) ![]const u8 { +pub fn getFileName(self: *Pdb, gpa: Allocator, mod: *Module, file_id: u32) ![]const u8 { const checksum_offset = mod.checksum_offset orelse return error.MissingDebugInfo; const subsect_index = checksum_offset + file_id; const chksum_hdr: *align(1) pdb.FileChecksumEntryHeader = @ptrCast(&mod.subsect_info[subsect_index]); const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset; self.string_table.?.seekTo(strtab_offset) catch return error.InvalidDebugInfo; const string_reader = &self.string_table.?.interface; - var source_file_name: Io.Writer.Allocating = .init(self.allocator); + var source_file_name: Io.Writer.Allocating = .init(gpa); defer source_file_name.deinit(); _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API @@ -716,10 +717,9 @@ pub fn getInlineeSourceLines( return mod.inlinee_source_lines[begin..end]; } -pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation { +pub fn getLineNumberInfo(self: *Pdb, gpa: Allocator, module: *Module, address: u64) !std.debug.SourceLocation { std.debug.assert(module.populated); const subsect_info = module.subsect_info; - const gpa = self.allocator; var sect_offset: usize = 0; var skip_len: usize = undefined; @@ -769,7 +769,7 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S // line_i == 0 would mean that no matching pdb.LineNumberEntry was found. if (line_i > 0) { - const file_name = try self.getFileName(module, block_hdr.name_index); + const file_name = try self.getFileName(gpa, module, block_hdr.name_index); errdefer gpa.free(file_name); const line_entry_idx = line_i - 1; diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index bc607569f8193c3c83e643c54eb4e621d2113c76..b56e32983f78bf52d89b5a604ba3b6aa169767b3 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -33,11 +33,13 @@ pub fn deinit(si: *SelfInfo, io: Io) void { pub fn getSymbols( si: *SelfInfo, io: Io, - gpa: Allocator, + symbol_allocator: Allocator, + text_arena: Allocator, address: usize, resolve_inline_callers: bool, symbols: *std.ArrayList(std.debug.Symbol), ) Error!void { + const gpa = std.debug.getDebugInfoAllocator(); const module = try si.findModule(gpa, io, address, .exclusive); defer si.rwlock.unlock(io); @@ -59,10 +61,17 @@ pub fn getSymbols( }; loaded_elf.scanned_dwarf = true; } - return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers, symbols); + return dwarf.getSymbols( + symbol_allocator, + text_arena, + native_endian, + vaddr, + resolve_inline_callers, + symbols, + ); } // When DWARF is unavailable, fall back to searching the symtab. - try symbols.append(gpa, loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { + try symbols.append(symbol_allocator, 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, diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index e53e39f8e576fd724999befdf36643e79d339526..91c8cd41dc8268673bb2c742f0d505c2569a7fa0 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -25,12 +25,14 @@ pub fn deinit(si: *SelfInfo, io: Io) void { pub fn getSymbols( si: *SelfInfo, io: Io, - gpa: Allocator, + symbol_allocator: Allocator, + text_arena: 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); @@ -51,7 +53,7 @@ pub fn getSymbols( const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch { // Return at least the symbol name if available. - return symbols.append(gpa, .{ + return symbols.append(symbol_allocator, .{ .name = try file.lookupSymbolName(vaddr), .compile_unit_name = null, .source_location = null, @@ -60,14 +62,14 @@ pub fn getSymbols( const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch { // Return at least the symbol name if available. - return symbols.append(gpa, .{ + return symbols.append(symbol_allocator, .{ .name = try file.lookupSymbolName(vaddr), .compile_unit_name = null, .source_location = null, }); }; - try symbols.append(gpa, .{ + try symbols.append(symbol_allocator, .{ .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse try file.lookupSymbolName(vaddr), .compile_unit_name = compile_unit.die.getAttrString( @@ -81,6 +83,7 @@ pub fn getSymbols( }, .source_location = ofile_dwarf.getLineNumberInfo( gpa, + text_arena, native_endian, compile_unit, ofile_vaddr, diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index 686dec41706601e3cd41675d7da1d137fcadd750..50684e9b2a8d6288dcfba85a170420784756d935 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -28,17 +28,20 @@ pub fn deinit(si: *SelfInfo, io: Io) void { pub fn getSymbols( si: *SelfInfo, io: Io, - gpa: Allocator, + symbol_allocator: Allocator, + text_arena: Allocator, address: usize, resolve_inline_callers: bool, symbols: *std.ArrayList(std.debug.Symbol), ) Error!void { + 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, + symbol_allocator, + text_arena, address - @intFromPtr(module.entry.DllBase), resolve_inline_callers, symbols, @@ -254,7 +257,8 @@ const Module = struct { fn getSymbols( di: *DebugInfo, - gpa: Allocator, + symbol_allocator: Allocator, + text_arena: Allocator, vaddr: usize, resolve_inline_callers: bool, symbols: *std.ArrayList(std.debug.Symbol), @@ -312,6 +316,7 @@ const Module = struct { inline_site.inlinee, )) |inlinee_src_line| { const maybe_loc = pdb.getInlineSiteSourceLocation( + text_arena, module, inline_site, inlinee_src_line.info, @@ -333,7 +338,7 @@ const Module = struct { else null; - try symbols.append(gpa, .{ + try symbols.append(symbol_allocator, .{ .name = name, .compile_unit_name = compile_unit_name, .source_location = loc, @@ -359,10 +364,10 @@ const Module = struct { // If there's room for another symbol, add the actual proc if (resolve_inline_callers or symbols.items.len == 0) { - try symbols.append(gpa, .{ + try symbols.append(symbol_allocator, .{ .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null, .compile_unit_name = compile_unit_name, - .source_location = pdb.getLineNumberInfo(module, addr) catch null, + .source_location = pdb.getLineNumberInfo(text_arena, module, addr) catch null, }); } @@ -372,7 +377,14 @@ 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, resolve_inline_callers, symbols); + return dwarf.getSymbols( + symbol_allocator, + text_arena, + native_endian, + addr, + resolve_inline_callers, + symbols, + ); } return error.MissingDebugInfo; diff --git a/test/standalone/coff_dwarf/main.zig b/test/standalone/coff_dwarf/main.zig index 66b7e3656b344563a3227a780a12e26232ea52e6..9dbee9ec787a87c3b35a8d3e5485dd719bd85b43 100644 --- a/test/standalone/coff_dwarf/main.zig +++ b/test/standalone/coff_dwarf/main.zig @@ -12,16 +12,26 @@ pub fn main(init: std.process.Init) void { var add_addr: usize = undefined; _ = add(1, 2, &add_addr); - const symbols = di.getSymbols(io, add_addr, false) catch |err| fatal("failed to get symbol: {t}", .{err}); const debug_gpa = std.debug.getDebugInfoAllocator(); - defer for (symbols) |symbol| { - if (symbol.source_location) |sl| { - debug_gpa.free(sl.file_name); - } - }; - - if (symbols.len != 1) fatal("expected 1 symbol, found {}", .{symbols.len}); - const symbol = symbols[0]; + const symbol_allocator = debug_gpa; + + var symbols: std.ArrayList(std.debug.Symbol) = .empty; + defer symbols.deinit(symbol_allocator); + + var text_arena: std.heap.ArenaAllocator = .init(debug_gpa); + defer text_arena.deinit(); + + di.getSymbols( + io, + symbol_allocator, + text_arena.allocator(), + add_addr, + false, + &symbols, + ) catch |err| fatal("failed to get symbol: {t}", .{err}); + + if (symbols.items.len != 1) fatal("expected 1 symbol, found {}", .{symbols.items.len}); + const symbol = symbols.items[0]; if (symbol.name == null) fatal("failed to resolve symbol name", .{}); if (symbol.compile_unit_name == null) fatal("failed to resolve compile unit", .{});