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),...@@ -50,6 +50,7 @@ memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
50/// Allocated into `gpa`.50/// Allocated into `gpa`.
51step_stack: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),51step_stack: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
52pkg_config: PkgConfig,52pkg_config: PkgConfig,
53debug_maker_leaks: bool,
5354
54error_style: ErrorStyle,55error_style: ErrorStyle,
55multiline_errors: MultilineErrors,56multiline_errors: MultilineErrors,
...@@ -73,6 +74,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -73,6 +74,7 @@ pub fn main(init: process.Init.Minimal) !void {
73 var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);74 var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
74 defer arena_instance.deinit();75 defer arena_instance.deinit();
75 const arena = arena_instance.allocator();76 const arena = arena_instance.allocator();
77 defer log.info("used {Bi} of arena", .{arena_instance.queryCapacity()});
7678
77 const args = try init.args.toSlice(arena);79 const args = try init.args.toSlice(arena);
7880
...@@ -150,7 +152,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -150,7 +152,8 @@ pub fn main(init: process.Init.Minimal) !void {
150 var fuzz: ?Fuzz.Mode = null;152 var fuzz: ?Fuzz.Mode = null;
151 var debounce_interval_ms: u16 = 50;153 var debounce_interval_ms: u16 = 50;
152 var webui_listen: ?Io.net.IpAddress = null;154 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;
154 var run_args: ?[]const []const u8 = null;157 var run_args: ?[]const []const u8 = null;
155158
156 if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {159 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 {...@@ -297,6 +300,8 @@ pub fn main(init: process.Init.Minimal) !void {
297 } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {300 } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {
298 graph.debug_compiler_runtime_libs = std.meta.stringToEnum(std.builtin.OptimizeMode, rest) orelse301 graph.debug_compiler_runtime_libs = std.meta.stringToEnum(std.builtin.OptimizeMode, rest) orelse
299 fatal("unrecognized optimization mode: {s}", .{rest});302 fatal("unrecognized optimization mode: {s}", .{rest});
303 } else if (mem.eql(u8, arg, "--debug-maker-leaks")) {
304 debug_maker_leaks = true;
300 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {305 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
301 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.306 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.
302 graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);307 graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);
...@@ -538,6 +543,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -538,6 +543,7 @@ pub fn main(init: process.Init.Minimal) !void {
538 .memory_blocked_steps = .empty,543 .memory_blocked_steps = .empty,
539 .step_stack = .empty,544 .step_stack = .empty,
540 .pkg_config = .{ .debug = debug_pkg_config },545 .pkg_config = .{ .debug = debug_pkg_config },
546 .debug_maker_leaks = debug_maker_leaks,
541547
542 .error_style = error_style,548 .error_style = error_style,
543 .multiline_errors = multiline_errors,549 .multiline_errors = multiline_errors,
...@@ -603,10 +609,10 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -603,10 +609,10 @@ pub fn main(init: process.Init.Minimal) !void {
603 web_server.finishBuild(.{ .fuzz = fuzz != null });609 web_server.finishBuild(.{ .fuzz = fuzz != null });
604 }610 }
605611
606 if (maker.web_server) |*ws| {612 if (maker.web_server) |*web_server| {
607 const c = &scanned_config.configuration;613 const c = &scanned_config.configuration;
608 assert(!watch); // fatal error after CLI parsing614 assert(!watch); // fatal error after CLI parsing
609 while (true) switch (try ws.wait()) {615 while (true) switch (try web_server.wait()) {
610 .rebuild => {616 .rebuild => {
611 for (maker.step_stack.keys()) |step_index| {617 for (maker.step_stack.keys()) |step_index| {
612 const step = maker.stepByIndex(step_index);618 const step = maker.stepByIndex(step_index);
...@@ -620,6 +626,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -620,6 +626,8 @@ pub fn main(init: process.Init.Minimal) !void {
620 };626 };
621 }627 }
622628
629 if (!maker.watch) return;
630
623 // Comptime-known guard to prevent including the logic below when `!Watch.have_impl`.631 // Comptime-known guard to prevent including the logic below when `!Watch.have_impl`.
624 if (!Watch.have_impl) unreachable;632 if (!Watch.have_impl) unreachable;
625633
...@@ -996,21 +1004,29 @@ fn makeStepNames(...@@ -996,21 +1004,29 @@ fn makeStepNames(
9961004
997 if (maker.watch or maker.web_server != null) return;1005 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
1003 const code: u8 = code: {1007 const code: u8 = code: {
1004 if (failure_count == 0) break :code 0; // success1008 if (failure_count == 0) break :code 0; // success
1005 if (maker.error_style.verboseContext()) break :code 1; // failure; print build command1009 if (maker.error_style.verboseContext()) break :code 1; // failure; print build command
1006 break :code 2; // failure; do not print build command1010 break :code 2; // failure; do not print build command
1007 };1011 };
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 }
1009 cleanup_task.await(io); // There is a defer above but an exit below.1016 cleanup_task.await(io); // There is a defer above but an exit below.
1010 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};1017 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
1011 process.exit(code);1018 process.exit(code);
1012}1019}
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
1014fn stepReady(1030fn stepReady(
1015 maker: *Maker,1031 maker: *Maker,
1016 group: *Io.Group,1032 group: *Io.Group,
...@@ -1457,6 +1473,7 @@ fn constructGraphAndCheckForDependencyLoop(...@@ -1457,6 +1473,7 @@ fn constructGraphAndCheckForDependencyLoop(
1457) error{ DependencyLoopDetected, OutOfMemory }!void {1473) error{ DependencyLoopDetected, OutOfMemory }!void {
1458 const c = &maker.scanned_config.configuration;1474 const c = &maker.scanned_config.configuration;
1459 const gpa = maker.gpa;1475 const gpa = maker.gpa;
1476 const arena = maker.graph.arena;
1460 const make_step = maker.stepByIndex(step_index);1477 const make_step = maker.stepByIndex(step_index);
1461 switch (make_step.state) {1478 switch (make_step.state) {
1462 .precheck_started => {1479 .precheck_started => {
...@@ -1480,7 +1497,7 @@ fn constructGraphAndCheckForDependencyLoop(...@@ -1480,7 +1497,7 @@ fn constructGraphAndCheckForDependencyLoop(
1480 for (deps) |dep| {1497 for (deps) |dep| {
1481 const dep_step = maker.stepByIndex(dep);1498 const dep_step = maker.stepByIndex(dep);
1482 try step_stack.put(gpa, dep, {});1499 try step_stack.put(gpa, dep, {});
1483 try dep_step.dependants.append(gpa, step_index);1500 try dep_step.dependants.append(arena, step_index);
1484 constructGraphAndCheckForDependencyLoop(maker, dep, step_stack, rand) catch |err| switch (err) {1501 constructGraphAndCheckForDependencyLoop(maker, dep, step_stack, rand) catch |err| switch (err) {
1485 error.DependencyLoopDetected => {1502 error.DependencyLoopDetected => {
1486 log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});1503 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 {...@@ -190,6 +190,11 @@ pub const Inputs = struct {
190 for (inputs.table.values()) |*files| files.deinit(gpa);190 for (inputs.table.values()) |*files| files.deinit(gpa);
191 inputs.table.clearRetainingCapacity();191 inputs.table.clearRetainingCapacity();
192 }192 }
193
194 pub fn deinit(inputs: *Inputs, gpa: Allocator) void {
195 clear(inputs, gpa);
196 inputs.table.deinit(gpa);
197 }
193};198};
194199
195pub const TestResults = struct {200pub const TestResults = struct {
...@@ -313,9 +318,7 @@ pub fn reset(step: *Step, maker: *Maker) void {...@@ -313,9 +318,7 @@ pub fn reset(step: *Step, maker: *Maker) void {
313 step.result_peak_rss = 0;318 step.result_peak_rss = 0;
314 step.test_results = .{};319 step.test_results = .{};
315 clearWatchInputs(step, maker);320 clearWatchInputs(step, maker);
316321 clearErrorBundle(step, gpa);
317 step.result_error_bundle.deinit(gpa);
318 step.result_error_bundle = std.zig.ErrorBundle.empty;
319}322}
320323
321pub const CaptureChildProcessError = error{324pub const CaptureChildProcessError = error{
...@@ -360,6 +363,11 @@ pub fn captureChildProcess(s: *Step, maker: *Maker, options: CaptureChildProcess...@@ -360,6 +363,11 @@ pub fn captureChildProcess(s: *Step, maker: *Maker, options: CaptureChildProcess
360 return result;363 return result;
361}364}
362365
366pub fn clearErrorBundle(s: *Step, gpa: Allocator) void {
367 s.result_error_bundle.deinit(gpa);
368 s.result_error_bundle = .empty;
369}
370
363pub fn clearFailedCommand(s: *Step, gpa: Allocator) void {371pub fn clearFailedCommand(s: *Step, gpa: Allocator) void {
364 if (s.result_failed_command) |cmd| {372 if (s.result_failed_command) |cmd| {
365 gpa.free(cmd);373 gpa.free(cmd);
lib/compiler/Maker/Step/Run.zig+4
...@@ -49,7 +49,10 @@ pub fn make(...@@ -49,7 +49,10 @@ pub fn make(
49 const arena = arena_allocator.allocator();49 const arena = arena_allocator.allocator();
5050
51 var argv_list: std.ArrayList([]const u8) = .empty;51 var argv_list: std.ArrayList([]const u8) = .empty;
52 defer argv_list.deinit(gpa);
53
52 var output_placeholders: std.ArrayList(IndexedOutput) = .empty;54 var output_placeholders: std.ArrayList(IndexedOutput) = .empty;
55 defer output_placeholders.deinit(gpa);
5356
54 var man = graph.cache.obtain();57 var man = graph.cache.obtain();
55 defer man.deinit();58 defer man.deinit();
...@@ -1523,6 +1526,7 @@ pub fn rerunInFuzzMode(...@@ -1523,6 +1526,7 @@ pub fn rerunInFuzzMode(
1523 const arena = arena_allocator.allocator();1526 const arena = arena_allocator.allocator();
15241527
1525 var argv_list: std.ArrayList([]const u8) = .empty;1528 var argv_list: std.ArrayList([]const u8) = .empty;
1529 defer argv_list.deinit(gpa);
15261530
1527 for (conf_run.args.slice) |arg_index| {1531 for (conf_run.args.slice) |arg_index| {
1528 const arg = arg_index.get(conf);1532 const arg = arg_index.get(conf);