authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-17 23:52:52+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:55+01:00
log3a9c680ad7b88ed6d9684626be9138ffc44b73b7
treed8d9363fc60b59a966c94b8ed69e37edaf8315d9
parentabb2b1e2daffdadca7edc26634631215df901950
signaturelock-open Commit is signed but in an unrecognized format.

std: allow disabling stack tracing

This option disables both capturing and printing stack traces. The default is to disable if debug info is stripped.

3 files changed, 60 insertions(+), 6 deletions(-)

lib/std/debug.zig+16-6
...@@ -567,13 +567,11 @@ pub const StackUnwindOptions = struct {...@@ -567,13 +567,11 @@ pub const StackUnwindOptions = struct {
567///567///
568/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.568/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.
569pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) std.builtin.StackTrace {569pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) std.builtin.StackTrace {
570 var it = StackIterator.init(options.context) catch {570 const empty_trace: std.builtin.StackTrace = .{ .index = 0, .instruction_addresses = &.{} };
571 return .{ .index = 0, .instruction_addresses = &.{} };571 if (!std.options.allow_stack_tracing) return empty_trace;
572 };572 var it = StackIterator.init(options.context) catch return empty_trace;
573 defer it.deinit();573 defer it.deinit();
574 if (!it.stratOk(options.allow_unsafe_unwind)) {574 if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace;
575 return .{ .index = 0, .instruction_addresses = &.{} };
576 }
577 var frame_idx: usize = 0;575 var frame_idx: usize = 0;
578 var wait_for = options.first_address;576 var wait_for = options.first_address;
579 while (true) switch (it.next()) {577 while (true) switch (it.next()) {
...@@ -599,6 +597,12 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize)...@@ -599,6 +597,12 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize)
599///597///
600/// See `captureCurrentStackTrace` to capture the trace addresses into a buffer instead of printing.598/// See `captureCurrentStackTrace` to capture the trace addresses into a buffer instead of printing.
601pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_config: tty.Config) Writer.Error!void {599pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_config: tty.Config) Writer.Error!void {
600 if (!std.options.allow_stack_tracing) {
601 tty_config.setColor(writer, .dim) catch {};
602 try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{});
603 tty_config.setColor(writer, .reset) catch {};
604 return;
605 }
602 const di_gpa = getDebugInfoAllocator();606 const di_gpa = getDebugInfoAllocator();
603 const di = getSelfDebugInfo() catch |err| switch (err) {607 const di = getSelfDebugInfo() catch |err| switch (err) {
604 error.UnsupportedTarget => {608 error.UnsupportedTarget => {
...@@ -688,6 +692,12 @@ pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void {...@@ -688,6 +692,12 @@ pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void {
688692
689/// Write a previously captured stack trace to `writer`, annotated with source locations.693/// Write a previously captured stack trace to `writer`, annotated with source locations.
690pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_config: tty.Config) Writer.Error!void {694pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_config: tty.Config) Writer.Error!void {
695 if (!std.options.allow_stack_tracing) {
696 tty_config.setColor(writer, .dim) catch {};
697 try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{});
698 tty_config.setColor(writer, .reset) catch {};
699 return;
700 }
691 // Fetch `st.index` straight away. Aside from avoiding redundant loads, this prevents issues if701 // Fetch `st.index` straight away. Aside from avoiding redundant loads, this prevents issues if
692 // `st` is `@errorReturnTrace()` and errors are encountered while writing the stack trace.702 // `st` is `@errorReturnTrace()` and errors are encountered while writing the stack trace.
693 const n_frames = st.index;703 const n_frames = st.index;
lib/std/std.zig+16
...@@ -171,6 +171,22 @@ pub const Options = struct {...@@ -171,6 +171,22 @@ pub const Options = struct {
171 http_enable_ssl_key_log_file: bool = @import("builtin").mode == .Debug,171 http_enable_ssl_key_log_file: bool = @import("builtin").mode == .Debug,
172172
173 side_channels_mitigations: crypto.SideChannelsMitigations = crypto.default_side_channels_mitigations,173 side_channels_mitigations: crypto.SideChannelsMitigations = crypto.default_side_channels_mitigations,
174
175 /// Whether to allow capturing and writing stack traces. This affects the following functions:
176 /// * `debug.captureCurrentStackTrace`
177 /// * `debug.writeCurrentStackTrace`
178 /// * `debug.dumpCurrentStackTrace`
179 /// * `debug.writeStackTrace`
180 /// * `debug.dumpStackTrace`
181 ///
182 /// Stack traces can generally be collected and printed when debug info is stripped, but are
183 /// often less useful since they usually cannot be mapped to source locations and/or have bad
184 /// source locations. The stack tracing logic can also be quite large, which may be undesirable,
185 /// particularly in ReleaseSmall.
186 ///
187 /// If this is `false`, then captured stack traces will always be empty, and attempts to write
188 /// stack traces will just print an error to the relevant `Io.Writer` and return.
189 allow_stack_tracing: bool = !@import("builtin").strip_debug_info,
174};190};
175191
176// This forces the start.zig file to be imported, and the comptime logic inside that192// This forces the start.zig file to be imported, and the comptime logic inside that
test/cases/disable_stack_tracing.zig created+28
...@@ -0,0 +1,28 @@
1pub const std_options: std.Options = .{
2 .allow_stack_tracing = false,
3};
4
5pub fn main() !void {
6 var st_buf: [8]usize = undefined;
7 var buf: [1024]u8 = undefined;
8 var stdout = std.fs.File.stdout().writer(&buf);
9
10 const captured_st = try foo(&stdout.interface, &st_buf);
11 try std.debug.writeStackTrace(&captured_st, &stdout.interface, .no_color);
12 try stdout.interface.print("stack trace index: {d}\n", .{captured_st.index});
13
14 try stdout.interface.flush();
15}
16fn foo(w: *std.Io.Writer, st_buf: []usize) !std.builtin.StackTrace {
17 try std.debug.writeCurrentStackTrace(.{}, w, .no_color);
18 return std.debug.captureCurrentStackTrace(.{}, st_buf);
19}
20
21const std = @import("std");
22
23// run
24//
25// Cannot print stack trace: stack tracing is disabled
26// Cannot print stack trace: stack tracing is disabled
27// stack trace index: 0
28//