Commit 078e330555

Sahnvour <sahnvour@pm.me>
2023-08-11 12:19:56
std.Build: make number of collected stack frames configurable
1 parent 0461a64
Changed files (2)
lib
lib/std/Build/Step.zig
@@ -39,7 +39,7 @@ test_results: TestResults,
 
 /// The return address associated with creation of this step that can be useful
 /// to print along with debugging messages.
-debug_stack_trace: [n_debug_stack_frames]usize,
+debug_stack_trace: []usize,
 
 pub const TestResults = struct {
     fail_count: u32 = 0,
@@ -58,8 +58,6 @@ pub const TestResults = struct {
 
 pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void;
 
-const n_debug_stack_frames = 4;
-
 pub const State = enum {
     precheck_unstarted,
     precheck_started,
@@ -140,14 +138,6 @@ pub const StepOptions = struct {
 pub fn init(options: StepOptions) Step {
     const arena = options.owner.allocator;
 
-    var addresses = [1]usize{0} ** n_debug_stack_frames;
-    const first_ret_addr = options.first_ret_addr orelse @returnAddress();
-    var stack_trace = std.builtin.StackTrace{
-        .instruction_addresses = &addresses,
-        .index = 0,
-    };
-    std.debug.captureStackTrace(first_ret_addr, &stack_trace);
-
     return .{
         .id = options.id,
         .name = arena.dupe(u8, options.name) catch @panic("OOM"),
@@ -157,7 +147,17 @@ pub fn init(options: StepOptions) Step {
         .dependants = .{},
         .state = .precheck_unstarted,
         .max_rss = options.max_rss,
-        .debug_stack_trace = addresses,
+        .debug_stack_trace = blk: {
+            const addresses = arena.alloc(usize, options.owner.debug_stack_frames_count) catch @panic("OOM");
+            @memset(addresses, 0);
+            const first_ret_addr = options.first_ret_addr orelse @returnAddress();
+            var stack_trace = std.builtin.StackTrace{
+                .instruction_addresses = addresses,
+                .index = 0,
+            };
+            std.debug.captureStackTrace(first_ret_addr, &stack_trace);
+            break :blk addresses;
+        },
         .result_error_msgs = .{},
         .result_error_bundle = std.zig.ErrorBundle.empty,
         .result_cached = false,
@@ -199,14 +199,14 @@ pub fn dependOn(self: *Step, other: *Step) void {
     self.dependencies.append(other) catch @panic("OOM");
 }
 
-pub fn getStackTrace(s: *Step) std.builtin.StackTrace {
-    const stack_addresses = &s.debug_stack_trace;
+pub fn getStackTrace(s: *Step) ?std.builtin.StackTrace {
     var len: usize = 0;
-    while (len < n_debug_stack_frames and stack_addresses[len] != 0) {
+    while (len < s.debug_stack_trace.len and s.debug_stack_trace[len] != 0) {
         len += 1;
     }
-    return .{
-        .instruction_addresses = stack_addresses,
+
+    return if (len == 0) null else .{
+        .instruction_addresses = s.debug_stack_trace,
         .index = len,
     };
 }
@@ -245,11 +245,19 @@ pub fn dump(step: *Step) void {
         return;
     };
     const ally = debug_info.allocator;
-    w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
-    std.debug.writeStackTrace(step.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
-        stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {};
-        return;
-    };
+    if (step.getStackTrace()) |stack_trace| {
+        w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
+        std.debug.writeStackTrace(stack_trace, w, ally, debug_info, tty_config) catch |err| {
+            stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {};
+            return;
+        };
+    } else {
+        const field = "debug_stack_frames_count";
+        comptime assert(@hasField(Build, field));
+        tty_config.setColor(w, .yellow) catch {};
+        w.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {};
+        tty_config.setColor(w, .reset) catch {};
+    }
 }
 
 const Step = @This();
lib/std/Build.zig
@@ -102,6 +102,10 @@ args: ?[][]const u8 = null,
 debug_log_scopes: []const []const u8 = &.{},
 debug_compile_errors: bool = false,
 debug_pkg_config: bool = false,
+/// Number of stack frames captured when a `StackTrace` is recorded for debug purposes,
+/// in particular at `Step` creation.
+/// Set to 0 to disable stack collection.
+debug_stack_frames_count: u8 = 8,
 
 /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
 enable_darling: bool = false,
@@ -1764,33 +1768,49 @@ pub fn dumpBadGetPathHelp(
     });
 
     const tty_config = std.io.tty.detectConfig(stderr);
-    tty_config.setColor(w, .red) catch {};
-    try stderr.writeAll("    The step was created by this stack trace:\n");
-    tty_config.setColor(w, .reset) catch {};
-
-    const debug_info = std.debug.getSelfDebugInfo() catch |err| {
-        try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
-        return;
-    };
-    const ally = debug_info.allocator;
-    std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
-        try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
-        return;
-    };
-    if (asking_step) |as| {
+    if (s.getStackTrace()) |stack_trace| {
         tty_config.setColor(w, .red) catch {};
-        try stderr.writeAll("    The step that is missing a dependency on the above step was created by this stack trace:\n");
+        try stderr.writeAll("    The step was created by this stack trace:\n");
         tty_config.setColor(w, .reset) catch {};
 
-        std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
+        const debug_info = std.debug.getSelfDebugInfo() catch |err| {
+            try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
+            return;
+        };
+        const ally = debug_info.allocator;
+
+        std.debug.writeStackTrace(stack_trace, w, ally, debug_info, tty_config) catch |err| {
             try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
             return;
         };
+        if (asking_step) |as| {
+            tty_config.setColor(w, .red) catch {};
+            try stderr.writer().print("    The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
+            tty_config.setColor(w, .reset) catch {};
+
+            if (as.getStackTrace()) |as_stack_trace| {
+                std.debug.writeStackTrace(as_stack_trace, w, ally, debug_info, tty_config) catch |err| {
+                    try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
+                    return;
+                };
+            } else {
+                const field = "debug_stack_frames_count";
+                comptime assert(@hasField(Build, field));
+                tty_config.setColor(w, .yellow) catch {};
+                try stderr.writer().print("no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{});
+                tty_config.setColor(w, .reset) catch {};
+            }
+        }
+        tty_config.setColor(w, .red) catch {};
+        try stderr.writeAll("    Hope that helps. Proceeding to panic.\n");
+        tty_config.setColor(w, .reset) catch {};
+    } else {
+        const field = "debug_stack_frames_count";
+        comptime assert(@hasField(Build, field));
+        tty_config.setColor(w, .yellow) catch {};
+        try stderr.writer().print("no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{});
+        tty_config.setColor(w, .reset) catch {};
     }
-
-    tty_config.setColor(w, .red) catch {};
-    try stderr.writeAll("    Hope that helps. Proceeding to panic.\n");
-    tty_config.setColor(w, .reset) catch {};
 }
 
 /// Allocates a new string for assigning a value to a named macro.