authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 20:14:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 20:14:34-07:00
logf3dd10d40fb9b6b94ccf7729c728c80ebd608ba7
treed31b760b66be3120ce552b991890cdb21e1dd6a7
parenta9e0eb5340bb60547fb8badc3591d9ffca415ac7

Maker: add --debug-maker-leaks flag and fix some leaks


3 files changed, 41 insertions(+), 12 deletions(-)

lib/compiler/Maker.zig+26-9
......@@ -50,6 +50,7 @@ memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
5050/// Allocated into `gpa`.
5151step_stack: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
5252pkg_config: PkgConfig,
53debug_maker_leaks: bool,
5354
5455error_style: ErrorStyle,
5556multiline_errors: MultilineErrors,
......@@ -73,6 +74,7 @@ pub fn main(init: process.Init.Minimal) !void {
7374 var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
7475 defer arena_instance.deinit();
7576 const arena = arena_instance.allocator();
77 defer log.info("used {Bi} of arena", .{arena_instance.queryCapacity()});
7678
7779 const args = try init.args.toSlice(arena);
7880
......@@ -150,7 +152,8 @@ pub fn main(init: process.Init.Minimal) !void {
150152 var fuzz: ?Fuzz.Mode = null;
151153 var debounce_interval_ms: u16 = 50;
152154 var webui_listen: ?Io.net.IpAddress = null;
153 var debug_pkg_config: bool = false;
155 var debug_pkg_config = false;
156 var debug_maker_leaks = false;
154157 var run_args: ?[]const []const u8 = null;
155158
156159 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 {
297300 } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {
298301 graph.debug_compiler_runtime_libs = std.meta.stringToEnum(std.builtin.OptimizeMode, rest) orelse
299302 fatal("unrecognized optimization mode: {s}", .{rest});
303 } else if (mem.eql(u8, arg, "--debug-maker-leaks")) {
304 debug_maker_leaks = true;
300305 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
301306 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.
302307 graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);
......@@ -538,6 +543,7 @@ pub fn main(init: process.Init.Minimal) !void {
538543 .memory_blocked_steps = .empty,
539544 .step_stack = .empty,
540545 .pkg_config = .{ .debug = debug_pkg_config },
546 .debug_maker_leaks = debug_maker_leaks,
541547
542548 .error_style = error_style,
543549 .multiline_errors = multiline_errors,
......@@ -603,10 +609,10 @@ pub fn main(init: process.Init.Minimal) !void {
603609 web_server.finishBuild(.{ .fuzz = fuzz != null });
604610 }
605611
606 if (maker.web_server) |*ws| {
612 if (maker.web_server) |*web_server| {
607613 const c = &scanned_config.configuration;
608614 assert(!watch); // fatal error after CLI parsing
609 while (true) switch (try ws.wait()) {
615 while (true) switch (try web_server.wait()) {
610616 .rebuild => {
611617 for (maker.step_stack.keys()) |step_index| {
612618 const step = maker.stepByIndex(step_index);
......@@ -620,6 +626,8 @@ pub fn main(init: process.Init.Minimal) !void {
620626 };
621627 }
622628
629 if (!maker.watch) return;
630
623631 // Comptime-known guard to prevent including the logic below when `!Watch.have_impl`.
624632 if (!Watch.have_impl) unreachable;
625633
......@@ -996,21 +1004,29 @@ fn makeStepNames(
9961004
9971005 if (maker.watch or maker.web_server != null) return;
9981006
999 // Perhaps in the future there could be an Advanced Options flag such as
1000 // --debug-build-runner-leaks which would make this code return instead of
1001 // calling exit.
1002
10031007 const code: u8 = code: {
10041008 if (failure_count == 0) break :code 0; // success
10051009 if (maker.error_style.verboseContext()) break :code 1; // failure; print build command
10061010 break :code 2; // failure; do not print build command
10071011 };
1008 if (code == 0) removePoisonedConfiguration(io, maker.scanned_config);
1012 if (code == 0) {
1013 removePoisonedConfiguration(io, maker.scanned_config);
1014 if (builtin.mode == .Debug and maker.debug_maker_leaks) return deinit(maker);
1015 }
10091016 cleanup_task.await(io); // There is a defer above but an exit below.
10101017 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
10111018 process.exit(code);
10121019}
10131020
1021fn deinit(maker: *Maker) void {
1022 const gpa = maker.gpa;
1023 for (maker.steps) |*step| {
1024 step.clearFailedCommand(gpa);
1025 step.clearErrorBundle(gpa);
1026 step.inputs.deinit(gpa);
1027 }
1028}
1029
10141030fn stepReady(
10151031 maker: *Maker,
10161032 group: *Io.Group,
......@@ -1457,6 +1473,7 @@ fn constructGraphAndCheckForDependencyLoop(
14571473) error{ DependencyLoopDetected, OutOfMemory }!void {
14581474 const c = &maker.scanned_config.configuration;
14591475 const gpa = maker.gpa;
1476 const arena = maker.graph.arena;
14601477 const make_step = maker.stepByIndex(step_index);
14611478 switch (make_step.state) {
14621479 .precheck_started => {
......@@ -1480,7 +1497,7 @@ fn constructGraphAndCheckForDependencyLoop(
14801497 for (deps) |dep| {
14811498 const dep_step = maker.stepByIndex(dep);
14821499 try step_stack.put(gpa, dep, {});
1483 try dep_step.dependants.append(gpa, step_index);
1500 try dep_step.dependants.append(arena, step_index);
14841501 constructGraphAndCheckForDependencyLoop(maker, dep, step_stack, rand) catch |err| switch (err) {
14851502 error.DependencyLoopDetected => {
14861503 log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});
lib/compiler/Maker/Step.zig+11-3
......@@ -190,6 +190,11 @@ pub const Inputs = struct {
190190 for (inputs.table.values()) |*files| files.deinit(gpa);
191191 inputs.table.clearRetainingCapacity();
192192 }
193
194 pub fn deinit(inputs: *Inputs, gpa: Allocator) void {
195 clear(inputs, gpa);
196 inputs.table.deinit(gpa);
197 }
193198};
194199
195200pub const TestResults = struct {
......@@ -313,9 +318,7 @@ pub fn reset(step: *Step, maker: *Maker) void {
313318 step.result_peak_rss = 0;
314319 step.test_results = .{};
315320 clearWatchInputs(step, maker);
316
317 step.result_error_bundle.deinit(gpa);
318 step.result_error_bundle = std.zig.ErrorBundle.empty;
321 clearErrorBundle(step, gpa);
319322}
320323
321324pub const CaptureChildProcessError = error{
......@@ -360,6 +363,11 @@ pub fn captureChildProcess(s: *Step, maker: *Maker, options: CaptureChildProcess
360363 return result;
361364}
362365
366pub fn clearErrorBundle(s: *Step, gpa: Allocator) void {
367 s.result_error_bundle.deinit(gpa);
368 s.result_error_bundle = .empty;
369}
370
363371pub fn clearFailedCommand(s: *Step, gpa: Allocator) void {
364372 if (s.result_failed_command) |cmd| {
365373 gpa.free(cmd);
lib/compiler/Maker/Step/Run.zig+4
......@@ -49,7 +49,10 @@ pub fn make(
4949 const arena = arena_allocator.allocator();
5050
5151 var argv_list: std.ArrayList([]const u8) = .empty;
52 defer argv_list.deinit(gpa);
53
5254 var output_placeholders: std.ArrayList(IndexedOutput) = .empty;
55 defer output_placeholders.deinit(gpa);
5356
5457 var man = graph.cache.obtain();
5558 defer man.deinit();
......@@ -1523,6 +1526,7 @@ pub fn rerunInFuzzMode(
15231526 const arena = arena_allocator.allocator();
15241527
15251528 var argv_list: std.ArrayList([]const u8) = .empty;
1529 defer argv_list.deinit(gpa);
15261530
15271531 for (conf_run.args.slice) |arg_index| {
15281532 const arg = arg_index.get(conf);