authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-06 13:01:27-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 04:01:29-07:00
log156f54d8f0165fb772d538382a6c3248e2ee30be
treec528b14bc6780ddac6408e210b2895ccb06dac95
parent22f9592dc7db869b2a618a72560fd0cf25b62d42

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!

3 files changed, 39 insertions(+), 8 deletions(-)

lib/std/builtin.zig+3
......@@ -11,6 +11,9 @@ pub const assembly = @import("builtin/assembly.zig");
1111pub const StackTrace = struct {
1212 index: usize,
1313 instruction_addresses: []usize,
14 /// Set to true if inlined frames are given their own entries in `instruction_addresses`,
15 /// otherwise set to false.
16 includes_inlined_frames: bool,
1417};
1518
1619/// This data structure is used by the Zig language code generation and
lib/std/debug.zig+34-8
......@@ -626,7 +626,11 @@ pub const StackUnwindOptions = struct {
626626///
627627/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.
628628pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {
629 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };
629 const empty_trace: StackTrace = .{
630 .index = 0,
631 .instruction_addresses = &.{},
632 .includes_inlined_frames = false,
633 };
630634 if (!std.options.allow_stack_tracing) return empty_trace;
631635 var it: StackIterator = .init(options.context);
632636 defer it.deinit();
......@@ -661,6 +665,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:
661665 return .{
662666 .index = index,
663667 .instruction_addresses = addr_buf[0..index],
668 .includes_inlined_frames = false,
664669 };
665670}
666671/// Write the current stack trace to `writer`, annotated with source locations.
......@@ -745,7 +750,10 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
745750 }
746751 // `ret_addr` is the return address, which is *after* the function call.
747752 // Subtract 1 to get an address *in* the function call for a better source location.
748 try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset);
753 try printSourceAtAddress(io, di, t, .{
754 .address = ret_addr -| StackIterator.ra_call_offset,
755 .print_inlines = true,
756 });
749757 printed_any_frame = true;
750758 },
751759 };
......@@ -805,7 +813,10 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void
805813 for (st.instruction_addresses[0..captured_frames]) |ret_addr| {
806814 // `ret_addr` is the return address, which is *after* the function call.
807815 // Subtract 1 to get an address *in* the function call for a better source location.
808 try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset);
816 try printSourceAtAddress(io, di, t, .{
817 .address = ret_addr -| StackIterator.ra_call_offset,
818 .print_inlines = !st.includes_inlined_frames,
819 });
809820 }
810821 if (n_frames > captured_frames) {
811822 t.setColor(.bold) catch {};
......@@ -1111,8 +1122,18 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
11111122 return ptr;
11121123}
11131124
1114fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void {
1115 var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, address);
1125const PrintSourceAddressOptions = struct {
1126 address: usize,
1127 print_inlines: bool,
1128};
1129
1130fn printSourceAtAddress(
1131 io: Io,
1132 debug_info: *SelfInfo,
1133 t: Io.Terminal,
1134 options: PrintSourceAddressOptions,
1135) Writer.Error!void {
1136 var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, options.address);
11161137 defer symbols.deinit(io);
11171138 while (symbols.next()) |curr| {
11181139 const symbol: Symbol = curr catch |err| switch (err) {
......@@ -1138,10 +1159,11 @@ fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address:
11381159 io,
11391160 t,
11401161 symbol.source_location,
1141 address,
1162 options.address,
11421163 symbol.name orelse "???",
1143 symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???",
1164 symbol.compile_unit_name orelse debug_info.getModuleName(io, options.address) catch "???",
11441165 );
1166 if (!options.print_inlines) break;
11451167 }
11461168}
11471169fn printLineInfo(
......@@ -1608,7 +1630,10 @@ test "manage resources correctly" {
16081630 var di: SelfInfo = .init;
16091631 defer di.deinit(io);
16101632 const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color };
1611 try printSourceAtAddress(io, &di, t, S.showMyTrace());
1633 try printSourceAtAddress(io, &di, t, .{
1634 .address = S.showMyTrace(),
1635 .inlines = true,
1636 });
16121637}
16131638
16141639/// 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
16791704 const stack_trace: StackTrace = .{
16801705 .index = frames.len,
16811706 .instruction_addresses = frames,
1707 .includes_inlined_frames = false,
16821708 };
16831709 writeStackTrace(&stack_trace, stderr) catch return;
16841710 }
lib/std/heap/debug_allocator.zig+2
......@@ -239,6 +239,7 @@ pub fn DebugAllocator(comptime config: Config) type {
239239 return .{
240240 .instruction_addresses = stack_addresses,
241241 .index = len,
242 .includes_inlined_frames = false,
242243 };
243244 }
244245
......@@ -341,6 +342,7 @@ pub fn DebugAllocator(comptime config: Config) type {
341342 return .{
342343 .instruction_addresses = stack_addresses,
343344 .index = len,
345 .includes_inlined_frames = false,
344346 };
345347 }
346348