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");...@@ -11,6 +11,9 @@ pub const assembly = @import("builtin/assembly.zig");
11pub const StackTrace = struct {11pub const StackTrace = struct {
12 index: usize,12 index: usize,
13 instruction_addresses: []usize,13 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,
14};17};
1518
16/// This data structure is used by the Zig language code generation and19/// 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 {...@@ -626,7 +626,11 @@ pub const StackUnwindOptions = struct {
626///626///
627/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.627/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.
628pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {628pub 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 };
630 if (!std.options.allow_stack_tracing) return empty_trace;634 if (!std.options.allow_stack_tracing) return empty_trace;
631 var it: StackIterator = .init(options.context);635 var it: StackIterator = .init(options.context);
632 defer it.deinit();636 defer it.deinit();
...@@ -661,6 +665,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:...@@ -661,6 +665,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:
661 return .{665 return .{
662 .index = index,666 .index = index,
663 .instruction_addresses = addr_buf[0..index],667 .instruction_addresses = addr_buf[0..index],
668 .includes_inlined_frames = false,
664 };669 };
665}670}
666/// Write the current stack trace to `writer`, annotated with source locations.671/// Write the current stack trace to `writer`, annotated with source locations.
...@@ -745,7 +750,10 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin...@@ -745,7 +750,10 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
745 }750 }
746 // `ret_addr` is the return address, which is *after* the function call.751 // `ret_addr` is the return address, which is *after* the function call.
747 // Subtract 1 to get an address *in* the function call for a better source location.752 // 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 });
749 printed_any_frame = true;757 printed_any_frame = true;
750 },758 },
751 };759 };
...@@ -805,7 +813,10 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void...@@ -805,7 +813,10 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void
805 for (st.instruction_addresses[0..captured_frames]) |ret_addr| {813 for (st.instruction_addresses[0..captured_frames]) |ret_addr| {
806 // `ret_addr` is the return address, which is *after* the function call.814 // `ret_addr` is the return address, which is *after* the function call.
807 // Subtract 1 to get an address *in* the function call for a better source location.815 // 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 });
809 }820 }
810 if (n_frames > captured_frames) {821 if (n_frames > captured_frames) {
811 t.setColor(.bold) catch {};822 t.setColor(.bold) catch {};
...@@ -1111,8 +1122,18 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {...@@ -1111,8 +1122,18 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
1111 return ptr;1122 return ptr;
1112}1123}
11131124
1114fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void {1125const PrintSourceAddressOptions = struct {
1115 var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, address);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);
1116 defer symbols.deinit(io);1137 defer symbols.deinit(io);
1117 while (symbols.next()) |curr| {1138 while (symbols.next()) |curr| {
1118 const symbol: Symbol = curr catch |err| switch (err) {1139 const symbol: Symbol = curr catch |err| switch (err) {
...@@ -1138,10 +1159,11 @@ fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address:...@@ -1138,10 +1159,11 @@ fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address:
1138 io,1159 io,
1139 t,1160 t,
1140 symbol.source_location,1161 symbol.source_location,
1141 address,1162 options.address,
1142 symbol.name orelse "???",1163 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 "???",
1144 );1165 );
1166 if (!options.print_inlines) break;
1145 }1167 }
1146}1168}
1147fn printLineInfo(1169fn printLineInfo(
...@@ -1608,7 +1630,10 @@ test "manage resources correctly" {...@@ -1608,7 +1630,10 @@ test "manage resources correctly" {
1608 var di: SelfInfo = .init;1630 var di: SelfInfo = .init;
1609 defer di.deinit(io);1631 defer di.deinit(io);
1610 const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color };1632 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 });
1612}1637}
16131638
1614/// This API helps you track where a value originated and where it was mutated,1639/// 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...@@ -1679,6 +1704,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
1679 const stack_trace: StackTrace = .{1704 const stack_trace: StackTrace = .{
1680 .index = frames.len,1705 .index = frames.len,
1681 .instruction_addresses = frames,1706 .instruction_addresses = frames,
1707 .includes_inlined_frames = false,
1682 };1708 };
1683 writeStackTrace(&stack_trace, stderr) catch return;1709 writeStackTrace(&stack_trace, stderr) catch return;
1684 }1710 }
lib/std/heap/debug_allocator.zig+2
...@@ -239,6 +239,7 @@ pub fn DebugAllocator(comptime config: Config) type {...@@ -239,6 +239,7 @@ pub fn DebugAllocator(comptime config: Config) type {
239 return .{239 return .{
240 .instruction_addresses = stack_addresses,240 .instruction_addresses = stack_addresses,
241 .index = len,241 .index = len,
242 .includes_inlined_frames = false,
242 };243 };
243 }244 }
244245
...@@ -341,6 +342,7 @@ pub fn DebugAllocator(comptime config: Config) type {...@@ -341,6 +342,7 @@ pub fn DebugAllocator(comptime config: Config) type {
341 return .{342 return .{
342 .instruction_addresses = stack_addresses,343 .instruction_addresses = stack_addresses,
343 .index = len,344 .index = len,
345 .includes_inlined_frames = false,
344 };346 };
345 }347 }
346348