authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-04-11 00:59:11+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-04-11 00:59:11+02:00
logd6f43caadf30dce2ad6f8fa938482855e08e6700
treebd69fea5940f040ba272c4c98cfc11300d30c4e7
parent8111d3d63cf3662b5aecbd0ddd10567e025694a7
parent4a1383d987130978bc66ba7cd7172c398684978d

Merge pull request 'audit: handle process.Child.Term exhaustively and give useful exit information on process exit' (#31018) from murtaza/zig:child.term-audit into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31018 Reviewed-by: Alex Rønne Petersen <alex@alexrp.com>

20 files changed, 140 insertions(+), 72 deletions(-)

lib/compiler/reduce.zig+5-1
......@@ -291,7 +291,11 @@ fn termToInteresting(term: std.process.Child.Term) Interestingness {
291291 std.debug.print("interestingness check terminated with signal {t}\n", .{sig});
292292 return .boring;
293293 },
294 else => {
294 .stopped => |sig| {
295 std.debug.print("interestingness check stopped with signal {t}\n", .{sig});
296 return .boring;
297 },
298 .unknown => {
295299 std.debug.print("interestingness check aborted unexpectedly\n", .{});
296300 return .boring;
297301 },
lib/compiler/std-docs.zig+8-1
......@@ -423,7 +423,14 @@ fn buildWasmBinary(
423423 );
424424 return error.WasmCompilationFailed;
425425 },
426 .stopped, .unknown => {
426 .stopped => |sig| {
427 std.log.err(
428 "the following command stopped unexpectedly with signal {t}:\n{s}",
429 .{ sig, try std.Build.Step.allocPrintCmd(arena, .inherit, null, argv.items) },
430 );
431 return error.WasmCompilationFailed;
432 },
433 .unknown => {
427434 std.log.err(
428435 "the following command terminated unexpectedly:\n{s}",
429436 .{try std.Build.Step.allocPrintCmd(arena, .inherit, null, argv.items)},
lib/std/Build.zig+2-2
......@@ -1899,11 +1899,11 @@ pub fn runAllowFail(
18991899 }
19001900 return stdout;
19011901 },
1902 .signal => |sig| {
1902 .signal, .stopped => |sig| {
19031903 out_code.* = @as(u8, @truncate(@intFromEnum(sig)));
19041904 return error.ProcessTerminated;
19051905 },
1906 .stopped, .unknown => |code| {
1906 .unknown => |code| {
19071907 out_code.* = @as(u8, @truncate(code));
19081908 return error.ProcessTerminated;
19091909 },
lib/std/Build/Step.zig+6-13
......@@ -725,19 +725,12 @@ pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFaile
725725/// Asserts that the caller has already populated `s.result_failed_command`.
726726pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void {
727727 assert(s.result_failed_command != null);
728 switch (term) {
729 .exited => |code| {
730 if (code != 0) {
731 return s.fail("process exited with error code {d}", .{code});
732 }
733 },
734 .signal => |sig| {
735 return s.fail("process terminated with signal {t}", .{sig});
736 },
737 .stopped, .unknown => {
738 return s.fail("process terminated unexpectedly", .{});
739 },
740 }
728 return switch (term) {
729 .exited => |code| if (code != 0) s.fail("process exited with error code {d}", .{code}),
730 .signal => |sig| s.fail("process terminated with signal {t}", .{sig}),
731 .stopped => |sig| s.fail("process stopped with signal {t}", .{sig}),
732 .unknown => s.fail("process terminated unexpectedly", .{}),
733 };
741734}
742735
743736pub fn allocPrintCmd(
lib/std/Build/Step/Run.zig+3-3
......@@ -1173,7 +1173,7 @@ fn formatTerm(term: ?process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error!
11731173 if (term) |t| switch (t) {
11741174 .exited => |code| try w.print("exited with code {d}", .{code}),
11751175 .signal => |sig| try w.print("terminated with signal {t}", .{sig}),
1176 .stopped => |sig| try w.print("stopped with signal {d}", .{sig}),
1176 .stopped => |sig| try w.print("stopped with signal {t}", .{sig}),
11771177 .unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}),
11781178 } else {
11791179 try w.writeAll("exited with any code");
......@@ -2804,8 +2804,8 @@ fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void {
28042804 .expect_term => |term| {
28052805 hh.add(@as(std.meta.Tag(process.Child.Term), term));
28062806 switch (term) {
2807 inline .exited, .signal => |x| hh.add(x),
2808 .stopped, .unknown => |x| hh.add(x),
2807 inline .exited, .signal, .stopped => |x| hh.add(x),
2808 .unknown => |x| hh.add(x),
28092809 }
28102810 },
28112811 }
lib/std/Build/WebServer.zig+8-1
......@@ -681,7 +681,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
681681 );
682682 return error.WasmCompilationFailed;
683683 },
684 .stopped, .unknown => {
684 .stopped => |sig| {
685 log.err(
686 "the following command stopped unexpectedly with signal {t}:\n{s}",
687 .{ sig, try Build.Step.allocPrintCmd(arena, .inherit, null, argv.items) },
688 );
689 return error.WasmCompilationFailed;
690 },
691 .unknown => {
685692 log.err(
686693 "the following command terminated unexpectedly:\n{s}",
687694 .{try Build.Step.allocPrintCmd(arena, .inherit, null, argv.items)},
lib/std/Io/Threaded.zig+1-1
......@@ -15281,7 +15281,7 @@ fn childWaitPosix(child: *process.Child) process.Child.WaitError!process.Child.T
1528115281 return switch (code) {
1528215282 .EXITED => .{ .exited = @truncate(status) },
1528315283 .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) },
15284 .TRAPPED, .STOPPED => .{ .stopped = status },
15284 .TRAPPED, .STOPPED => .{ .stopped = @enumFromInt(status) },
1528515285 _, .CONTINUED => .{ .unknown = status },
1528615286 };
1528715287 },
lib/std/Io/Uring.zig+1-1
......@@ -4729,7 +4729,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr
47294729 return switch (code) {
47304730 .EXITED => .{ .exited = @truncate(status) },
47314731 .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) },
4732 .TRAPPED, .STOPPED => .{ .stopped = status },
4732 .TRAPPED, .STOPPED => .{ .stopped = @enumFromInt(status) },
47334733 _, .CONTINUED => .{ .unknown = status },
47344734 };
47354735 },
lib/std/c.zig+17-17
......@@ -3721,14 +3721,14 @@ pub const W = switch (native_os) {
37213721 pub fn TERMSIG(x: u32) SIG {
37223722 return @enumFromInt(status(x));
37233723 }
3724 pub fn STOPSIG(x: u32) u32 {
3725 return x >> 8;
3724 pub fn STOPSIG(x: u32) SIG {
3725 return @enumFromInt(x >> 8);
37263726 }
37273727 pub fn IFEXITED(x: u32) bool {
37283728 return status(x) == 0;
37293729 }
37303730 pub fn IFSTOPPED(x: u32) bool {
3731 return status(x) == stopped and STOPSIG(x) != 0x13;
3731 return status(x) == stopped and @as(u32, @intFromEnum(STOPSIG(x))) != 0x13;
37323732 }
37333733 pub fn IFSIGNALED(x: u32) bool {
37343734 return status(x) != stopped and status(x) != 0;
......@@ -3754,8 +3754,8 @@ pub const W = switch (native_os) {
37543754 pub fn TERMSIG(s: u32) SIG {
37553755 return @enumFromInt(s & 0x7f);
37563756 }
3757 pub fn STOPSIG(s: u32) u32 {
3758 return EXITSTATUS(s);
3757 pub fn STOPSIG(s: u32) SIG {
3758 return @enumFromInt(EXITSTATUS(s));
37593759 }
37603760 pub fn IFEXITED(s: u32) bool {
37613761 return (s & 0x7f) == 0;
......@@ -3782,8 +3782,8 @@ pub const W = switch (native_os) {
37823782 pub fn TERMSIG(s: u32) SIG {
37833783 return @enumFromInt(s & 0x7f);
37843784 }
3785 pub fn STOPSIG(s: u32) u32 {
3786 return EXITSTATUS(s);
3785 pub fn STOPSIG(s: u32) SIG {
3786 return @enumFromInt(EXITSTATUS(s));
37873787 }
37883788 pub fn IFEXITED(s: u32) bool {
37893789 return (s & 0x7f) == 0;
......@@ -3816,8 +3816,8 @@ pub const W = switch (native_os) {
38163816 pub fn TERMSIG(s: u32) SIG {
38173817 return @enumFromInt(s & 0x7f);
38183818 }
3819 pub fn STOPSIG(s: u32) u32 {
3820 return EXITSTATUS(s);
3819 pub fn STOPSIG(s: u32) SIG {
3820 return @enumFromInt(EXITSTATUS(s));
38213821 }
38223822 pub fn IFEXITED(s: u32) bool {
38233823 return (s & 0x7f) == 0;
......@@ -3850,8 +3850,8 @@ pub const W = switch (native_os) {
38503850 pub fn TERMSIG(s: u32) SIG {
38513851 return @enumFromInt(s & 0x7f);
38523852 }
3853 pub fn STOPSIG(s: u32) u32 {
3854 return EXITSTATUS(s);
3853 pub fn STOPSIG(s: u32) SIG {
3854 return @enumFromInt(EXITSTATUS(s));
38553855 }
38563856 pub fn IFEXITED(s: u32) bool {
38573857 return (s & 0x7f) == 0;
......@@ -3879,8 +3879,8 @@ pub const W = switch (native_os) {
38793879 return @enumFromInt((s >> 8) & 0xff);
38803880 }
38813881
3882 pub fn STOPSIG(s: u32) u32 {
3883 return (s >> 16) & 0xff;
3882 pub fn STOPSIG(s: u32) SIG {
3883 return @enumFromInt((s >> 16) & 0xff);
38843884 }
38853885
38863886 pub fn IFEXITED(s: u32) bool {
......@@ -3906,8 +3906,8 @@ pub const W = switch (native_os) {
39063906 pub fn TERMSIG(s: u32) SIG {
39073907 return @enumFromInt(s & 0x7f);
39083908 }
3909 pub fn STOPSIG(s: u32) u32 {
3910 return EXITSTATUS(s);
3909 pub fn STOPSIG(s: u32) SIG {
3910 return @enumFromInt(EXITSTATUS(s));
39113911 }
39123912 pub fn IFEXITED(s: u32) bool {
39133913 return (s & 0x7f) == 0;
......@@ -3938,8 +3938,8 @@ pub const W = switch (native_os) {
39383938 return @intCast((s & 0xff00) >> 8);
39393939 }
39403940
3941 pub fn STOPSIG(s: u32) u32 {
3942 return EXITSTATUS(s);
3941 pub fn STOPSIG(s: u32) SIG {
3942 return @enumFromInt(EXITSTATUS(s));
39433943 }
39443944
39453945 pub fn TERMSIG(s: u32) SIG {
lib/std/os/emscripten.zig+1-1
......@@ -212,7 +212,7 @@ pub const W = struct {
212212 return @enumFromInt(s & 0x7f);
213213 }
214214 pub fn STOPSIG(s: u32) u32 {
215 return EXITSTATUS(s);
215 return @enumFromInt(EXITSTATUS(s));
216216 }
217217 pub fn IFEXITED(s: u32) bool {
218218 return (s & 0x7f) == 0;
lib/std/os/linux.zig+2-2
......@@ -3883,8 +3883,8 @@ pub const W = struct {
38833883 pub fn TERMSIG(s: u32) SIG {
38843884 return @enumFromInt(s & 0x7f);
38853885 }
3886 pub fn STOPSIG(s: u32) u32 {
3887 return EXITSTATUS(s);
3886 pub fn STOPSIG(s: u32) SIG {
3887 return @enumFromInt(EXITSTATUS(s));
38883888 }
38893889 pub fn IFEXITED(s: u32) bool {
38903890 return (s & 0x7f) == 0;
lib/std/process/Child.zig+1-1
......@@ -94,7 +94,7 @@ pub const ResourceUsageStatistics = struct {
9494pub const Term = union(enum) {
9595 exited: u8,
9696 signal: std.posix.SIG,
97 stopped: u32,
97 stopped: std.posix.SIG,
9898 unknown: u32,
9999};
100100
lib/std/zig/system.zig+18-4
......@@ -1185,11 +1185,25 @@ fn detectAndroidApiLevel(io: Io) !u32 {
11851185 return error.ApiLevelQueryFailed;
11861186 };
11871187
1188 const term = try child.wait(io);
1189 if (term != .exited or term.exited != 0) {
1190 std.log.err("getprop terminated abnormally: {}", .{term});
1191 return error.ApiLevelQueryFailed;
1188 switch (try child.wait(io)) {
1189 .exited => |code| if (code != 0) {
1190 std.log.err("getprop terminated abnormally with exit code: {d}", .{code});
1191 return error.ApiLevelQueryFailed;
1192 },
1193 .signal => |sig| {
1194 std.log.err("getprop terminated abnormally with signal: {t}", .{sig});
1195 return error.ApiLevelQueryFailed;
1196 },
1197 .stopped => |sig| {
1198 std.log.err("getprop stopped abnormally with signal: {t}", .{sig});
1199 return error.ApiLevelQueryFailed;
1200 },
1201 .unknown => {
1202 std.log.err("getprop terminated abnormally", .{});
1203 return error.ApiLevelQueryFailed;
1204 },
11921205 }
1206
11931207 return api_level;
11941208}
11951209
src/Compilation.zig+10-2
......@@ -5870,7 +5870,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
58705870 log.err("clang failed with stderr: {s}", .{stderr});
58715871 return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig});
58725872 },
5873 else => {
5873 .stopped => |sig| {
5874 log.err("clang failed with stderr: {s}", .{stderr});
5875 return comp.failCObj(c_object, "clang stopped with signal {t}", .{sig});
5876 },
5877 .unknown => {
58745878 log.err("clang terminated with stderr: {s}", .{stderr});
58755879 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
58765880 },
......@@ -6297,7 +6301,11 @@ fn spawnZigRc(
62976301 log.err("zig rc signaled {t} with stderr:\n{s}", .{ sig, stderr });
62986302 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
62996303 },
6300 else => {
6304 .stopped => |sig| {
6305 log.err("zig rc stopped {t} with stderr:\n{s}", .{ sig, stderr });
6306 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
6307 },
6308 .unknown => {
63016309 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
63026310 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
63036311 },
src/link/Lld.zig+1-1
......@@ -1733,7 +1733,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
17331733 },
17341734 .stopped => |sig| {
17351735 if (comp.clang_passthrough_mode) std.process.abort();
1736 return diags.fail("{s} stopped with signal {d} and stderr:\n{s}", .{ argv[0], sig, stderr });
1736 return diags.fail("{s} stopped with signal {t} and stderr:\n{s}", .{ argv[0], sig, stderr });
17371737 },
17381738 .unknown => |code| {
17391739 if (comp.clang_passthrough_mode) std.process.abort();
src/main.zig+15-3
......@@ -4580,7 +4580,11 @@ fn runOrTest(
45804580 const cmd = try std.mem.join(arena, " ", argv.items);
45814581 fatal("the following command terminated with signal {t}:\n{s}", .{ sig, cmd });
45824582 },
4583 else => {
4583 .stopped => |sig| {
4584 const cmd = try std.mem.join(arena, " ", argv.items);
4585 fatal("the following command stopped with signal {t}:\n{s}", .{ sig, cmd });
4586 },
4587 .unknown => {
45844588 process.exit(1);
45854589 },
45864590 }
......@@ -5630,7 +5634,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
56305634 const cmd = try std.mem.join(arena, " ", child_argv.items);
56315635 fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd });
56325636 },
5633 else => {
5637 .stopped => |sig| {
5638 const cmd = try std.mem.join(arena, " ", child_argv.items);
5639 fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd });
5640 },
5641 .unknown => {
56345642 const cmd = try std.mem.join(arena, " ", child_argv.items);
56355643 fatal("the following build command crashed:\n{s}", .{cmd});
56365644 },
......@@ -5932,7 +5940,11 @@ fn jitCmdInner(
59325940 const cmd = try std.mem.join(arena, " ", child_argv.items);
59335941 fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd });
59345942 },
5935 else => {
5943 .stopped => |sig| {
5944 const cmd = try std.mem.join(arena, " ", child_argv.items);
5945 fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd });
5946 },
5947 .unknown => {
59365948 const cmd = try std.mem.join(arena, " ", child_argv.items);
59375949 fatal("the following build command crashed:\n{s}", .{cmd});
59385950 },
tools/doctest.zig+6-1
......@@ -1141,7 +1141,12 @@ fn run(
11411141 dumpArgs(args);
11421142 return error.ChildCrashed;
11431143 },
1144 else => {
1144 .stopped => |sig| {
1145 std.debug.print("{s}\nThe following command stopped with signal {t}:\n", .{ result.stderr, sig });
1146 dumpArgs(args);
1147 return error.ChildCrashed;
1148 },
1149 .unknown => {
11451150 std.debug.print("{s}\nThe following command crashed:\n", .{result.stderr});
11461151 dumpArgs(args);
11471152 return error.ChildCrashed;
tools/incr-check.zig+16-14
......@@ -568,7 +568,10 @@ const Eval = struct {
568568 .signal => |sig| {
569569 eval.fatal("generated executable '{s}' terminated with signal {t}", .{ binary_path, sig });
570570 },
571 .stopped, .unknown => {
571 .stopped => |sig| {
572 eval.fatal("generated executable '{s}' stopped with signal {t}", .{ binary_path, sig });
573 },
574 .unknown => {
572575 eval.fatal("generated executable '{s}' terminated unexpectedly", .{binary_path});
573576 },
574577 }
......@@ -627,19 +630,17 @@ const Eval = struct {
627630 }) catch |err| {
628631 eval.fatal("failed to spawn zig cc for '{s}': {t}", .{ c_path, err });
629632 };
633
634 if (result.term == .exited and result.term.exited == 0) return;
635
636 if (result.stderr.len != 0) {
637 std.log.err("zig cc stderr:\n{s}", .{result.stderr});
638 }
630639 switch (result.term) {
631 .exited => |code| if (code != 0) {
632 if (result.stderr.len != 0) {
633 std.log.err("zig cc stderr:\n{s}", .{result.stderr});
634 }
635 eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code });
636 },
637 .signal, .stopped, .unknown => {
638 if (result.stderr.len != 0) {
639 std.log.err("zig cc stderr:\n{s}", .{result.stderr});
640 }
641 eval.fatal("zig cc for '{s}' terminated unexpectedly", .{c_path});
642 },
640 .exited => |code| eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code }),
641 .signal => |sig| eval.fatal("zig cc for '{s}' terminated unexpectedly with signal {t}", .{ c_path, sig }),
642 .stopped => |sig| eval.fatal("zig cc for '{s}' stopped unexpectedly with signal {t}", .{ c_path, sig }),
643 .unknown => eval.fatal("zig cc for '{s}' terminated unexpectedly", .{c_path}),
643644 }
644645 }
645646
......@@ -918,7 +919,8 @@ fn waitChild(child: *std.process.Child, eval: *Eval) void {
918919 switch (term) {
919920 .exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}),
920921 .signal => |sig| eval.fatal("compiler terminated with signal {t}", .{sig}),
921 .stopped, .unknown => eval.fatal("compiler terminated unexpectedly", .{}),
922 .stopped => |sig| eval.fatal("compiler stopped unexpectedly with signal {t}", .{sig}),
923 .unknown => eval.fatal("compiler terminated unexpectedly", .{}),
922924 }
923925}
924926
tools/update_clang_options.zig+10-2
......@@ -688,9 +688,17 @@ pub fn main(init: std.process.Init) !void {
688688
689689 const json_text = switch (child_result.term) {
690690 .exited => |code| if (code == 0) child_result.stdout else {
691 fatal("llvm-tblgen exited with code {d}", .{code});
691 fatal("llvm-tblgen exited with code {d}\n", .{code});
692 },
693 .signal => |sig| {
694 fatal("llvm-tblgen terminated with signal {t}\n", .{sig});
695 },
696 .stopped => |sig| {
697 fatal("llvm-tblgen stopped with signal {d}\n", .{sig});
698 },
699 .unknown => {
700 fatal("llvm-tblgen crashed\n", .{});
692701 },
693 else => fatal("llvm-tblgen crashed", .{}),
694702 };
695703
696704 const parsed = try json.parseFromSlice(json.Value, arena, json_text, .{});
tools/update_cpu_features.zig+9-1
......@@ -2012,7 +2012,15 @@ fn processOneTarget(io: Io, job: Job) void {
20122012 std.debug.print("llvm-tblgen exited with code {d}\n", .{code});
20132013 std.process.exit(1);
20142014 },
2015 else => {
2015 .signal => |sig| {
2016 std.debug.print("llvm-tblgen terminated with signal {t}\n", .{sig});
2017 std.process.exit(1);
2018 },
2019 .stopped => |sig| {
2020 std.debug.print("llvm-tblgen stopped with signal {t}\n", .{sig});
2021 std.process.exit(1);
2022 },
2023 .unknown => {
20162024 std.debug.print("llvm-tblgen crashed\n", .{});
20172025 std.process.exit(1);
20182026 },