From 156f54d8f0165fb772d538382a6c3248e2ee30be Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 6 Apr 2026 13:01:27 -0700 Subject: [PATCH] Adds includes_inlined_frames option to builtin.StackTrace This will be relevant once #31605 is merged. In general, stack traces do *not* contain unique addresses for inlined frames, but for error return traces, they will after the above PR. This bool indicates that code printing the trace should not try to resolve inline frames since they're explicitly encoded into the instruction addresses. This is set as state on stack trace rather than passed into the formatting methods as an argument, as it's not really a formatting option--whether or not it's correct to resolve inlines is decided at the time of capture! --- lib/std/builtin.zig | 3 +++ lib/std/debug.zig | 42 ++++++++++++++++++++++++++------ lib/std/heap/debug_allocator.zig | 2 ++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 00ae6199d1d9cdf96e8202a29b3f0b7385b5d387..addfa166b3bc8043e1ac45a55373d40fcf237b75 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -11,6 +11,9 @@ pub const assembly = @import("builtin/assembly.zig"); pub const StackTrace = struct { index: usize, instruction_addresses: []usize, + /// Set to true if inlined frames are given their own entries in `instruction_addresses`, + /// otherwise set to false. + includes_inlined_frames: bool, }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 78e1b8df23ae775503e655e5901460dd4601f6d1..a7c2c68d0e0baedc7a91688dc5c6c750828a7069 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -626,7 +626,11 @@ pub const StackUnwindOptions = struct { /// /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace { - const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} }; + const empty_trace: StackTrace = .{ + .index = 0, + .instruction_addresses = &.{}, + .includes_inlined_frames = false, + }; if (!std.options.allow_stack_tracing) return empty_trace; var it: StackIterator = .init(options.context); defer it.deinit(); @@ -661,6 +665,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: return .{ .index = index, .instruction_addresses = addr_buf[0..index], + .includes_inlined_frames = false, }; } /// Write the current stack trace to `writer`, annotated with source locations. @@ -745,7 +750,10 @@ 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, ret_addr -| StackIterator.ra_call_offset); + try printSourceAtAddress(io, di, t, .{ + .address = ret_addr -| StackIterator.ra_call_offset, + .print_inlines = true, + }); printed_any_frame = true; }, }; @@ -805,7 +813,10 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void for (st.instruction_addresses[0..captured_frames]) |ret_addr| { // `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, ret_addr -| StackIterator.ra_call_offset); + try printSourceAtAddress(io, di, t, .{ + .address = ret_addr -| StackIterator.ra_call_offset, + .print_inlines = !st.includes_inlined_frames, + }); } if (n_frames > captured_frames) { t.setColor(.bold) catch {}; @@ -1111,8 +1122,18 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { return ptr; } -fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void { - var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, address); +const PrintSourceAddressOptions = struct { + address: usize, + print_inlines: bool, +}; + +fn printSourceAtAddress( + io: Io, + debug_info: *SelfInfo, + t: Io.Terminal, + options: PrintSourceAddressOptions, +) Writer.Error!void { + var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, options.address); defer symbols.deinit(io); while (symbols.next()) |curr| { const symbol: Symbol = curr catch |err| switch (err) { @@ -1138,10 +1159,11 @@ fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: io, t, symbol.source_location, - address, + options.address, symbol.name orelse "???", - symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", + symbol.compile_unit_name orelse debug_info.getModuleName(io, options.address) catch "???", ); + if (!options.print_inlines) break; } } fn printLineInfo( @@ -1608,7 +1630,10 @@ 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, S.showMyTrace()); + try printSourceAtAddress(io, &di, t, .{ + .address = S.showMyTrace(), + .inlines = true, + }); } /// This API helps you track where a value originated and where it was mutated, @@ -1679,6 +1704,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize const stack_trace: StackTrace = .{ .index = frames.len, .instruction_addresses = frames, + .includes_inlined_frames = false, }; writeStackTrace(&stack_trace, stderr) catch return; } diff --git a/lib/std/heap/debug_allocator.zig b/lib/std/heap/debug_allocator.zig index d6f2bf179653582d1d0d02faa1236e38e7fa5ab0..987bb4e6eaaca055953b8f2a4149b08b052db1b1 100644 --- a/lib/std/heap/debug_allocator.zig +++ b/lib/std/heap/debug_allocator.zig @@ -239,6 +239,7 @@ pub fn DebugAllocator(comptime config: Config) type { return .{ .instruction_addresses = stack_addresses, .index = len, + .includes_inlined_frames = false, }; } @@ -341,6 +342,7 @@ pub fn DebugAllocator(comptime config: Config) type { return .{ .instruction_addresses = stack_addresses, .index = len, + .includes_inlined_frames = false, }; } -- 2.54.0