authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-13 00:43:34-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-13 01:30:14-07:00
log6707a5efeea1ab973c3274495bb0a5640e4f568b
treee3f815ac7949a0b33ad997e386ccb4cdd5a81bcc
parent4ad665d3c8cd6a9d4f6b0e2f065098436142b450

Arena allocates text


7 files changed, 109 insertions(+), 71 deletions(-)

lib/std/debug.zig+39-38
......@@ -39,7 +39,7 @@ pub const cpu_context = @import("debug/cpu_context.zig");
3939/// pub fn deinit(si: *SelfInfo, io: Io) void;
4040///
4141/// /// Appends the symbols for the instruction at `address` to `symbols`.
42/// pub fn getSymbols(si: *SelfInfo, io: Io, gpa: Allocator, address: usize, include_inline_callers: bool, symbols: *std.ArrayList(Symbol)) SelfInfoError!void;
42/// 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;
4343/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
4444/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
4545/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
......@@ -229,11 +229,6 @@ pub const Symbol = struct {
229229 .compile_unit_name = null,
230230 .source_location = null,
231231 };
232
233 pub fn deinit(self: *Symbol, gpa: Allocator) void {
234 if (self.source_location) |sl| gpa.free(sl.file_name);
235 self.* = undefined;
236 }
237232};
238233
239234/// Deprecated because it returns the optimization mode of the standard
......@@ -699,6 +694,10 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:
699694/// See `captureCurrentStackTrace` to capture the trace addresses into a buffer instead of printing.
700695pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Terminal) Writer.Error!void {
701696 const writer = t.writer;
697
698 var text_arena: std.heap.ArenaAllocator = .init(getDebugInfoAllocator());
699 defer text_arena.deinit();
700
702701 if (!std.options.allow_stack_tracing) {
703702 t.setColor(.dim) catch {};
704703 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
776775 }
777776 // `ret_addr` is the return address, which is *after* the function call.
778777 // Subtract 1 to get an address *in* the function call for a better source location.
779 try printSourceAtAddress(io, di, t, .{
778 try printSourceAtAddress(io, &text_arena, di, t, .{
780779 .address = ret_addr -| StackIterator.ra_call_offset,
781780 .resolve_inline_callers = true,
782781 });
......@@ -832,6 +831,9 @@ fn writeTrace(
832831 t: Io.Terminal,
833832 resolve_inline_callers: bool,
834833) Writer.Error!void {
834 var text_arena: std.heap.ArenaAllocator = .init(getDebugInfoAllocator());
835 defer text_arena.deinit();
836
835837 const writer = t.writer;
836838 if (!std.options.allow_stack_tracing) {
837839 t.setColor(.dim) catch {};
......@@ -853,7 +855,7 @@ fn writeTrace(
853855 for (addresses) |addr| {
854856 // `addr` is the return address, which is *after* the function call.
855857 // Subtract 1 to get an address *in* the function call for a better source location.
856 try printSourceAtAddress(io, di, t, .{
858 try printSourceAtAddress(io, &text_arena, di, t, .{
857859 .address = addr -| StackIterator.ra_call_offset,
858860 .resolve_inline_callers = resolve_inline_callers,
859861 });
......@@ -1186,21 +1188,28 @@ const PrintSourceAddressOptions = struct {
11861188
11871189fn printSourceAtAddress(
11881190 io: Io,
1191 text_arena: *std.heap.ArenaAllocator,
11891192 debug_info: *SelfInfo,
11901193 t: Io.Terminal,
11911194 options: PrintSourceAddressOptions,
11921195) Writer.Error!void {
1193 // In the common case where there's only one symbol, allocate it on the stack. Reserve enough
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| {
1196 defer _ = text_arena.reset(.retain_capacity);
1197
1198 // Initialize the symbol array with space for at least one element, allocating this on the stack
1199 // in the common case where only one element is needed
1200 var symbol_fallback_allocator = std.heap.stackFallback(@sizeOf(Symbol) + @alignOf(Symbol) - 1, getDebugInfoAllocator());
1201 const symbol_allocator = symbol_fallback_allocator.get();
1202 var symbols = std.ArrayList(Symbol).initCapacity(symbol_allocator, 1) catch unreachable;
1203 defer symbols.deinit(symbol_allocator);
1204
1205 debug_info.getSymbols(
1206 io,
1207 symbol_allocator,
1208 text_arena.allocator(),
1209 options.address,
1210 options.resolve_inline_callers,
1211 &symbols,
1212 ) catch |err| {
12041213 t.setColor(.dim) catch {};
12051214 defer t.setColor(.reset) catch {};
12061215 switch (err) {
......@@ -1219,35 +1228,25 @@ fn printSourceAtAddress(
12191228 }
12201229 };
12211230
1222 // If we failed to get any symbols, append the unknown symbol. We initialized with a capacity of
1223 // one using a stack fallback allocator so this can't fail.
1231 // If we failed to write any symbols, at least write the unknown symbol. Can't fail since we
1232 // initialized with a capacity of 1.
12241233 if (symbols.items.len == 0) symbols.appendAssumeCapacity(.unknown);
12251234
12261235 for (symbols.items) |symbol| {
1227 try printLineInfo(
1228 io,
1229 t,
1230 debug_info,
1231 symbol.source_location,
1232 options.address,
1233 symbol.name,
1234 symbol.compile_unit_name,
1235 );
1236 try printLineInfo(io, t, debug_info, options.address, symbol);
12361237 }
12371238}
12381239fn printLineInfo(
12391240 io: Io,
12401241 t: Io.Terminal,
12411242 debug_info: *SelfInfo,
1242 source_location: ?SourceLocation,
12431243 address: usize,
1244 symbol_name: ?[]const u8,
1245 compile_unit_name: ?[]const u8,
1244 symbol: Symbol,
12461245) Writer.Error!void {
12471246 const writer = t.writer;
12481247 t.setColor(.bold) catch {};
12491248
1250 if (source_location) |*sl| {
1249 if (symbol.source_location) |*sl| {
12511250 if (sl.column == 0) {
12521251 try writer.print("{s}:{d}", .{ sl.file_name, sl.line });
12531252 } else {
......@@ -1262,14 +1261,14 @@ fn printLineInfo(
12621261 t.setColor(.dim) catch {};
12631262 try writer.print("0x{x} in {s} ({s})", .{
12641263 address,
1265 symbol_name orelse "???",
1266 compile_unit_name orelse debug_info.getModuleName(io, address) catch "???",
1264 symbol.name orelse "???",
1265 symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???",
12671266 });
12681267 t.setColor(.reset) catch {};
12691268 try writer.writeAll("\n");
12701269
12711270 // Show the matching source code line if possible
1272 if (source_location) |sl| {
1271 if (symbol.source_location) |sl| {
12731272 if (printLineFromFile(io, writer, sl)) {
12741273 if (sl.column > 0) {
12751274 // The caret already takes one char
......@@ -1708,7 +1707,9 @@ test "manage resources correctly" {
17081707 var di: SelfInfo = .init;
17091708 defer di.deinit(io);
17101709 const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color };
1711 try printSourceAtAddress(io, &di, t, .{
1710 var text_arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
1711 defer text_arena.deinit();
1712 try printSourceAtAddress(io, &text_arena, &di, t, .{
17121713 .address = S.showMyTrace(),
17131714 .resolve_inline_callers = true,
17141715 });
lib/std/debug/Dwarf.zig+7-4
......@@ -1220,6 +1220,7 @@ pub fn populateSrcLocCache(d: *Dwarf, gpa: Allocator, endian: Endian, cu: *Compi
12201220pub fn getLineNumberInfo(
12211221 d: *Dwarf,
12221222 gpa: Allocator,
1223 text_arena: Allocator,
12231224 endian: Endian,
12241225 compile_unit: *CompileUnit,
12251226 target_address: u64,
......@@ -1232,7 +1233,7 @@ pub fn getLineNumberInfo(
12321233 const file_entry = &slc.files[file_index];
12331234 if (file_entry.dir_index >= slc.directories.len) return bad();
12341235 const dir_name = slc.directories[file_entry.dir_index].path;
1235 const file_name = try std.fs.path.join(gpa, &.{ dir_name, file_entry.path });
1236 const file_name = try std.fs.path.join(text_arena, &.{ dir_name, file_entry.path });
12361237 return .{
12371238 .line = entry.line,
12381239 .column = entry.column,
......@@ -1547,25 +1548,27 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
15471548
15481549pub fn getSymbols(
15491550 di: *Dwarf,
1550 gpa: Allocator,
1551 symbol_allocator: Allocator,
1552 text_arena: Allocator,
15511553 endian: Endian,
15521554 address: u64,
15531555 resolve_inline_callers: bool,
15541556 symbols: *std.ArrayList(std.debug.Symbol),
15551557) std.debug.SelfInfoError!void {
15561558 _ = resolve_inline_callers;
1559 const gpa = std.debug.getDebugInfoAllocator();
15571560
15581561 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
15591562 error.EndOfStream => return error.MissingDebugInfo,
15601563 error.Overflow => return error.InvalidDebugInfo,
15611564 error.ReadFailed, error.InvalidDebugInfo, error.MissingDebugInfo => |e| return e,
15621565 };
1563 try symbols.append(gpa, .{
1566 try symbols.append(symbol_allocator, .{
15641567 .name = di.getSymbolName(address),
15651568 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
15661569 error.MissingDebugInfo, error.InvalidDebugInfo => null,
15671570 },
1568 .source_location = di.getLineNumberInfo(gpa, endian, compile_unit, address) catch |err| switch (err) {
1571 .source_location = di.getLineNumberInfo(gpa, text_arena, endian, compile_unit, address) catch |err| switch (err) {
15691572 error.MissingDebugInfo, error.InvalidDebugInfo => null,
15701573 error.ReadFailed,
15711574 error.EndOfStream,
lib/std/debug/Pdb.zig+6-6
......@@ -617,6 +617,7 @@ pub fn getBinaryAnnotations(self: *Pdb, module: *Module, site: *align(1) const p
617617
618618pub fn getInlineSiteSourceLocation(
619619 self: *Pdb,
620 gpa: Allocator,
620621 mod: *Module,
621622 site: *align(1) const pdb.InlineSiteSym,
622623 inlinee_src_line: *align(1) const pdb.InlineeSourceLine,
......@@ -627,7 +628,7 @@ pub fn getInlineSiteSourceLocation(
627628 if (!range.contains(offset_in_func)) continue;
628629
629630 const file_id = range.file_id orelse inlinee_src_line.file_id;
630 const file_name = try self.getFileName(mod, file_id);
631 const file_name = try self.getFileName(gpa, mod, file_id);
631632 errdefer self.allocator.free(file_name);
632633
633634 return .{
......@@ -640,14 +641,14 @@ pub fn getInlineSiteSourceLocation(
640641 return null;
641642}
642643
643pub fn getFileName(self: *Pdb, mod: *Module, file_id: u32) ![]const u8 {
644pub fn getFileName(self: *Pdb, gpa: Allocator, mod: *Module, file_id: u32) ![]const u8 {
644645 const checksum_offset = mod.checksum_offset orelse return error.MissingDebugInfo;
645646 const subsect_index = checksum_offset + file_id;
646647 const chksum_hdr: *align(1) pdb.FileChecksumEntryHeader = @ptrCast(&mod.subsect_info[subsect_index]);
647648 const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset;
648649 self.string_table.?.seekTo(strtab_offset) catch return error.InvalidDebugInfo;
649650 const string_reader = &self.string_table.?.interface;
650 var source_file_name: Io.Writer.Allocating = .init(self.allocator);
651 var source_file_name: Io.Writer.Allocating = .init(gpa);
651652 defer source_file_name.deinit();
652653 _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024));
653654 assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API
......@@ -716,10 +717,9 @@ pub fn getInlineeSourceLines(
716717 return mod.inlinee_source_lines[begin..end];
717718}
718719
719pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation {
720pub fn getLineNumberInfo(self: *Pdb, gpa: Allocator, module: *Module, address: u64) !std.debug.SourceLocation {
720721 std.debug.assert(module.populated);
721722 const subsect_info = module.subsect_info;
722 const gpa = self.allocator;
723723
724724 var sect_offset: usize = 0;
725725 var skip_len: usize = undefined;
......@@ -769,7 +769,7 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S
769769
770770 // line_i == 0 would mean that no matching pdb.LineNumberEntry was found.
771771 if (line_i > 0) {
772 const file_name = try self.getFileName(module, block_hdr.name_index);
772 const file_name = try self.getFileName(gpa, module, block_hdr.name_index);
773773 errdefer gpa.free(file_name);
774774
775775 const line_entry_idx = line_i - 1;
lib/std/debug/SelfInfo/Elf.zig+12-3
......@@ -33,11 +33,13 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
3333pub fn getSymbols(
3434 si: *SelfInfo,
3535 io: Io,
36 gpa: Allocator,
36 symbol_allocator: Allocator,
37 text_arena: Allocator,
3738 address: usize,
3839 resolve_inline_callers: bool,
3940 symbols: *std.ArrayList(std.debug.Symbol),
4041) Error!void {
42 const gpa = std.debug.getDebugInfoAllocator();
4143 const module = try si.findModule(gpa, io, address, .exclusive);
4244 defer si.rwlock.unlock(io);
4345
......@@ -59,10 +61,17 @@ pub fn getSymbols(
5961 };
6062 loaded_elf.scanned_dwarf = true;
6163 }
62 return dwarf.getSymbols(gpa, native_endian, vaddr, resolve_inline_callers, symbols);
64 return dwarf.getSymbols(
65 symbol_allocator,
66 text_arena,
67 native_endian,
68 vaddr,
69 resolve_inline_callers,
70 symbols,
71 );
6372 }
6473 // When DWARF is unavailable, fall back to searching the symtab.
65 try symbols.append(gpa, loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
74 try symbols.append(symbol_allocator, loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
6675 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
6776 error.BadSymtab => return error.InvalidDebugInfo,
6877 error.OutOfMemory => |e| return e,
lib/std/debug/SelfInfo/MachO.zig+7-4
......@@ -25,12 +25,14 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
2525pub fn getSymbols(
2626 si: *SelfInfo,
2727 io: Io,
28 gpa: Allocator,
28 symbol_allocator: Allocator,
29 text_arena: Allocator,
2930 address: usize,
3031 resolve_inline_callers: bool,
3132 symbols: *std.ArrayList(std.debug.Symbol),
3233) Error!void {
3334 _ = resolve_inline_callers;
35 const gpa = std.debug.getDebugInfoAllocator();
3436
3537 const module = try si.findModule(gpa, io, address);
3638 defer si.mutex.unlock(io);
......@@ -51,7 +53,7 @@ pub fn getSymbols(
5153
5254 const ofile_dwarf, const ofile_vaddr = file.getDwarfForAddress(gpa, io, vaddr) catch {
5355 // Return at least the symbol name if available.
54 return symbols.append(gpa, .{
56 return symbols.append(symbol_allocator, .{
5557 .name = try file.lookupSymbolName(vaddr),
5658 .compile_unit_name = null,
5759 .source_location = null,
......@@ -60,14 +62,14 @@ pub fn getSymbols(
6062
6163 const compile_unit = ofile_dwarf.findCompileUnit(native_endian, ofile_vaddr) catch {
6264 // Return at least the symbol name if available.
63 return symbols.append(gpa, .{
65 return symbols.append(symbol_allocator, .{
6466 .name = try file.lookupSymbolName(vaddr),
6567 .compile_unit_name = null,
6668 .source_location = null,
6769 });
6870 };
6971
70 try symbols.append(gpa, .{
72 try symbols.append(symbol_allocator, .{
7173 .name = ofile_dwarf.getSymbolName(ofile_vaddr) orelse
7274 try file.lookupSymbolName(vaddr),
7375 .compile_unit_name = compile_unit.die.getAttrString(
......@@ -81,6 +83,7 @@ pub fn getSymbols(
8183 },
8284 .source_location = ofile_dwarf.getLineNumberInfo(
8385 gpa,
86 text_arena,
8487 native_endian,
8588 compile_unit,
8689 ofile_vaddr,
lib/std/debug/SelfInfo/Windows.zig+19-7
......@@ -28,17 +28,20 @@ pub fn deinit(si: *SelfInfo, io: Io) void {
2828pub fn getSymbols(
2929 si: *SelfInfo,
3030 io: Io,
31 gpa: Allocator,
31 symbol_allocator: Allocator,
32 text_arena: Allocator,
3233 address: usize,
3334 resolve_inline_callers: bool,
3435 symbols: *std.ArrayList(std.debug.Symbol),
3536) Error!void {
37 const gpa = std.debug.getDebugInfoAllocator();
3638 try si.lock.lockShared(io);
3739 defer si.lock.unlockShared(io);
3840 const module = try si.findModule(gpa, address);
3941 const di = try module.getDebugInfo(gpa, io);
4042 return di.getSymbols(
41 gpa,
43 symbol_allocator,
44 text_arena,
4245 address - @intFromPtr(module.entry.DllBase),
4346 resolve_inline_callers,
4447 symbols,
......@@ -254,7 +257,8 @@ const Module = struct {
254257
255258 fn getSymbols(
256259 di: *DebugInfo,
257 gpa: Allocator,
260 symbol_allocator: Allocator,
261 text_arena: Allocator,
258262 vaddr: usize,
259263 resolve_inline_callers: bool,
260264 symbols: *std.ArrayList(std.debug.Symbol),
......@@ -312,6 +316,7 @@ const Module = struct {
312316 inline_site.inlinee,
313317 )) |inlinee_src_line| {
314318 const maybe_loc = pdb.getInlineSiteSourceLocation(
319 text_arena,
315320 module,
316321 inline_site,
317322 inlinee_src_line.info,
......@@ -333,7 +338,7 @@ const Module = struct {
333338 else
334339 null;
335340
336 try symbols.append(gpa, .{
341 try symbols.append(symbol_allocator, .{
337342 .name = name,
338343 .compile_unit_name = compile_unit_name,
339344 .source_location = loc,
......@@ -359,10 +364,10 @@ const Module = struct {
359364
360365 // If there's room for another symbol, add the actual proc
361366 if (resolve_inline_callers or symbols.items.len == 0) {
362 try symbols.append(gpa, .{
367 try symbols.append(symbol_allocator, .{
363368 .name = if (maybe_proc) |proc| pdb.getSymbolName(proc) else null,
364369 .compile_unit_name = compile_unit_name,
365 .source_location = pdb.getLineNumberInfo(module, addr) catch null,
370 .source_location = pdb.getLineNumberInfo(text_arena, module, addr) catch null,
366371 });
367372 }
368373
......@@ -372,7 +377,14 @@ const Module = struct {
372377 dwarf: {
373378 const dwarf = &(di.dwarf orelse break :dwarf);
374379 const addr = vaddr + di.coff_image_base;
375 return dwarf.getSymbols(gpa, native_endian, addr, resolve_inline_callers, symbols);
380 return dwarf.getSymbols(
381 symbol_allocator,
382 text_arena,
383 native_endian,
384 addr,
385 resolve_inline_callers,
386 symbols,
387 );
376388 }
377389
378390 return error.MissingDebugInfo;
test/standalone/coff_dwarf/main.zig+19-9
......@@ -12,16 +12,26 @@ pub fn main(init: std.process.Init) void {
1212 var add_addr: usize = undefined;
1313 _ = add(1, 2, &add_addr);
1414
15 const symbols = di.getSymbols(io, add_addr, false) catch |err| fatal("failed to get symbol: {t}", .{err});
1615 const debug_gpa = std.debug.getDebugInfoAllocator();
17 defer for (symbols) |symbol| {
18 if (symbol.source_location) |sl| {
19 debug_gpa.free(sl.file_name);
20 }
21 };
22
23 if (symbols.len != 1) fatal("expected 1 symbol, found {}", .{symbols.len});
24 const symbol = symbols[0];
16 const symbol_allocator = debug_gpa;
17
18 var symbols: std.ArrayList(std.debug.Symbol) = .empty;
19 defer symbols.deinit(symbol_allocator);
20
21 var text_arena: std.heap.ArenaAllocator = .init(debug_gpa);
22 defer text_arena.deinit();
23
24 di.getSymbols(
25 io,
26 symbol_allocator,
27 text_arena.allocator(),
28 add_addr,
29 false,
30 &symbols,
31 ) catch |err| fatal("failed to get symbol: {t}", .{err});
32
33 if (symbols.items.len != 1) fatal("expected 1 symbol, found {}", .{symbols.items.len});
34 const symbol = symbols.items[0];
2535
2636 if (symbol.name == null) fatal("failed to resolve symbol name", .{});
2737 if (symbol.compile_unit_name == null) fatal("failed to resolve compile unit", .{});