commit f3dd10d40fb9b6b94ccf7729c728c80ebd608ba7 (tree)
parent a9e0eb5340bb60547fb8badc3591d9ffca415ac7
Author: Andrew Kelley <andrew@ziglang.org>
Date: Mon, 25 May 2026 20:14:34 -0700
Maker: add --debug-maker-leaks flag and fix some leaks
Diffstat:
3 files changed, 41 insertions(+), 12 deletions(-)
diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig
@@ -50,6 +50,7 @@ memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
/// Allocated into `gpa`.
step_stack: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
pkg_config: PkgConfig,
+debug_maker_leaks: bool,
error_style: ErrorStyle,
multiline_errors: MultilineErrors,
@@ -73,6 +74,7 @@ pub fn main(init: process.Init.Minimal) !void {
var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
defer arena_instance.deinit();
const arena = arena_instance.allocator();
+ defer log.info("used {Bi} of arena", .{arena_instance.queryCapacity()});
const args = try init.args.toSlice(arena);
@@ -150,7 +152,8 @@ pub fn main(init: process.Init.Minimal) !void {
var fuzz: ?Fuzz.Mode = null;
var debounce_interval_ms: u16 = 50;
var webui_listen: ?Io.net.IpAddress = null;
- var debug_pkg_config: bool = false;
+ var debug_pkg_config = false;
+ var debug_maker_leaks = false;
var run_args: ?[]const []const u8 = null;
if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {
@@ -297,6 +300,8 @@ pub fn main(init: process.Init.Minimal) !void {
} else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {
graph.debug_compiler_runtime_libs = std.meta.stringToEnum(std.builtin.OptimizeMode, rest) orelse
fatal("unrecognized optimization mode: {s}", .{rest});
+ } else if (mem.eql(u8, arg, "--debug-maker-leaks")) {
+ debug_maker_leaks = true;
} else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
// --glibc-runtimes was the old name of the flag; kept for compatibility for now.
graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);
@@ -538,6 +543,7 @@ pub fn main(init: process.Init.Minimal) !void {
.memory_blocked_steps = .empty,
.step_stack = .empty,
.pkg_config = .{ .debug = debug_pkg_config },
+ .debug_maker_leaks = debug_maker_leaks,
.error_style = error_style,
.multiline_errors = multiline_errors,
@@ -603,10 +609,10 @@ pub fn main(init: process.Init.Minimal) !void {
web_server.finishBuild(.{ .fuzz = fuzz != null });
}
- if (maker.web_server) |*ws| {
+ if (maker.web_server) |*web_server| {
const c = &scanned_config.configuration;
assert(!watch); // fatal error after CLI parsing
- while (true) switch (try ws.wait()) {
+ while (true) switch (try web_server.wait()) {
.rebuild => {
for (maker.step_stack.keys()) |step_index| {
const step = maker.stepByIndex(step_index);
@@ -620,6 +626,8 @@ pub fn main(init: process.Init.Minimal) !void {
};
}
+ if (!maker.watch) return;
+
// Comptime-known guard to prevent including the logic below when `!Watch.have_impl`.
if (!Watch.have_impl) unreachable;
@@ -996,21 +1004,29 @@ fn makeStepNames(
if (maker.watch or maker.web_server != null) return;
- // Perhaps in the future there could be an Advanced Options flag such as
- // --debug-build-runner-leaks which would make this code return instead of
- // calling exit.
-
const code: u8 = code: {
if (failure_count == 0) break :code 0; // success
if (maker.error_style.verboseContext()) break :code 1; // failure; print build command
break :code 2; // failure; do not print build command
};
- if (code == 0) removePoisonedConfiguration(io, maker.scanned_config);
+ if (code == 0) {
+ removePoisonedConfiguration(io, maker.scanned_config);
+ if (builtin.mode == .Debug and maker.debug_maker_leaks) return deinit(maker);
+ }
cleanup_task.await(io); // There is a defer above but an exit below.
_ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
process.exit(code);
}
+fn deinit(maker: *Maker) void {
+ const gpa = maker.gpa;
+ for (maker.steps) |*step| {
+ step.clearFailedCommand(gpa);
+ step.clearErrorBundle(gpa);
+ step.inputs.deinit(gpa);
+ }
+}
+
fn stepReady(
maker: *Maker,
group: *Io.Group,
@@ -1457,6 +1473,7 @@ fn constructGraphAndCheckForDependencyLoop(
) error{ DependencyLoopDetected, OutOfMemory }!void {
const c = &maker.scanned_config.configuration;
const gpa = maker.gpa;
+ const arena = maker.graph.arena;
const make_step = maker.stepByIndex(step_index);
switch (make_step.state) {
.precheck_started => {
@@ -1480,7 +1497,7 @@ fn constructGraphAndCheckForDependencyLoop(
for (deps) |dep| {
const dep_step = maker.stepByIndex(dep);
try step_stack.put(gpa, dep, {});
- try dep_step.dependants.append(gpa, step_index);
+ try dep_step.dependants.append(arena, step_index);
constructGraphAndCheckForDependencyLoop(maker, dep, step_stack, rand) catch |err| switch (err) {
error.DependencyLoopDetected => {
log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});
diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig
@@ -190,6 +190,11 @@ pub const Inputs = struct {
for (inputs.table.values()) |*files| files.deinit(gpa);
inputs.table.clearRetainingCapacity();
}
+
+ pub fn deinit(inputs: *Inputs, gpa: Allocator) void {
+ clear(inputs, gpa);
+ inputs.table.deinit(gpa);
+ }
};
pub const TestResults = struct {
@@ -313,9 +318,7 @@ pub fn reset(step: *Step, maker: *Maker) void {
step.result_peak_rss = 0;
step.test_results = .{};
clearWatchInputs(step, maker);
-
- step.result_error_bundle.deinit(gpa);
- step.result_error_bundle = std.zig.ErrorBundle.empty;
+ clearErrorBundle(step, gpa);
}
pub const CaptureChildProcessError = error{
@@ -360,6 +363,11 @@ pub fn captureChildProcess(s: *Step, maker: *Maker, options: CaptureChildProcess
return result;
}
+pub fn clearErrorBundle(s: *Step, gpa: Allocator) void {
+ s.result_error_bundle.deinit(gpa);
+ s.result_error_bundle = .empty;
+}
+
pub fn clearFailedCommand(s: *Step, gpa: Allocator) void {
if (s.result_failed_command) |cmd| {
gpa.free(cmd);
diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig
@@ -49,7 +49,10 @@ pub fn make(
const arena = arena_allocator.allocator();
var argv_list: std.ArrayList([]const u8) = .empty;
+ defer argv_list.deinit(gpa);
+
var output_placeholders: std.ArrayList(IndexedOutput) = .empty;
+ defer output_placeholders.deinit(gpa);
var man = graph.cache.obtain();
defer man.deinit();
@@ -1523,6 +1526,7 @@ pub fn rerunInFuzzMode(
const arena = arena_allocator.allocator();
var argv_list: std.ArrayList([]const u8) = .empty;
+ defer argv_list.deinit(gpa);
for (conf_run.args.slice) |arg_index| {
const arg = arg_index.get(conf);