authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-03 15:27:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-15 10:48:13-07:00
log41a5ad28c9a651efd2822f43486a71ca48ace726
treea2513ed3e16aa7bfbc1c0f8e89c34ed1c1d83f8e
parent8b054e190a977ccd27302debd5d0f95c28597005

std: child process API supports rusage data


4 files changed, 81 insertions(+), 3 deletions(-)

lib/std/c.zig+2-1
...@@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n...@@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n
153pub extern "c" fn unlink(path: [*:0]const u8) c_int;153pub extern "c" fn unlink(path: [*:0]const u8) c_int;
154pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int;154pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int;
155pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;155pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
156pub extern "c" fn waitpid(pid: c.pid_t, stat_loc: ?*c_int, options: c_int) c.pid_t;156pub extern "c" fn waitpid(pid: c.pid_t, status: ?*c_int, options: c_int) c.pid_t;
157pub extern "c" fn wait4(pid: c.pid_t, status: ?*c_int, options: c_int, ru: ?*c.rusage) c.pid_t;
157pub extern "c" fn fork() c_int;158pub extern "c" fn fork() c_int;
158pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;159pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;
159pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int;160pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int;
lib/std/child_process.zig+48-1
...@@ -17,6 +17,7 @@ const Os = std.builtin.Os;...@@ -17,6 +17,7 @@ const Os = std.builtin.Os;
17const TailQueue = std.TailQueue;17const TailQueue = std.TailQueue;
18const maxInt = std.math.maxInt;18const maxInt = std.math.maxInt;
19const assert = std.debug.assert;19const assert = std.debug.assert;
20const is_darwin = builtin.target.isDarwin();
2021
21pub const ChildProcess = struct {22pub const ChildProcess = struct {
22 pub const Id = switch (builtin.os.tag) {23 pub const Id = switch (builtin.os.tag) {
...@@ -70,6 +71,43 @@ pub const ChildProcess = struct {...@@ -70,6 +71,43 @@ pub const ChildProcess = struct {
70 /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent.71 /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent.
71 start_suspended: bool = false,72 start_suspended: bool = false,
7273
74 /// Set to true to obtain rusage information for the child process.
75 /// Depending on the target platform and implementation status, the
76 /// requested statistics may or may not be available. If they are
77 /// available, then the `resource_usage_statistics` field will be populated
78 /// after calling `wait`.
79 /// On Linux, this obtains rusage statistics from wait4().
80 request_resource_usage_statistics: bool = false,
81
82 /// This is available after calling wait if
83 /// `request_resource_usage_statistics` was set to `true` before calling
84 /// `spawn`.
85 resource_usage_statistics: ResourceUsageStatistics = .{},
86
87 pub const ResourceUsageStatistics = struct {
88 rusage: @TypeOf(rusage_init) = rusage_init,
89
90 /// Returns the peak resident set size of the child process, in bytes,
91 /// if available.
92 pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize {
93 switch (builtin.os.tag) {
94 .linux => {
95 if (rus.rusage) |ru| {
96 return @intCast(usize, ru.maxrss) * 1024;
97 } else {
98 return null;
99 }
100 },
101 else => return null,
102 }
103 }
104
105 const rusage_init = switch (builtin.os.tag) {
106 .linux => @as(?std.os.rusage, null),
107 else => {},
108 };
109 };
110
73 pub const Arg0Expand = os.Arg0Expand;111 pub const Arg0Expand = os.Arg0Expand;
74112
75 pub const SpawnError = error{113 pub const SpawnError = error{
...@@ -332,7 +370,16 @@ pub const ChildProcess = struct {...@@ -332,7 +370,16 @@ pub const ChildProcess = struct {
332 }370 }
333371
334 fn waitUnwrapped(self: *ChildProcess) !void {372 fn waitUnwrapped(self: *ChildProcess) !void {
335 const res: os.WaitPidResult = os.waitpid(self.id, 0);373 const res: os.WaitPidResult = res: {
374 if (builtin.os.tag == .linux and self.request_resource_usage_statistics) {
375 var ru: std.os.rusage = undefined;
376 const res = os.wait4(self.id, 0, &ru);
377 self.resource_usage_statistics.rusage = ru;
378 break :res res;
379 }
380
381 break :res os.waitpid(self.id, 0);
382 };
336 const status = res.status;383 const status = res.status;
337 self.cleanupStreams();384 self.cleanupStreams();
338 self.handleWaitResult(status);385 self.handleWaitResult(status);
lib/std/os.zig+21-1
...@@ -4000,8 +4000,28 @@ pub const WaitPidResult = struct {...@@ -4000,8 +4000,28 @@ pub const WaitPidResult = struct {
4000pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {4000pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
4001 const Status = if (builtin.link_libc) c_int else u32;4001 const Status = if (builtin.link_libc) c_int else u32;
4002 var status: Status = undefined;4002 var status: Status = undefined;
4003 const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags;
4003 while (true) {4004 while (true) {
4004 const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags);4005 const rc = system.waitpid(pid, &status, coerced_flags);
4006 switch (errno(rc)) {
4007 .SUCCESS => return .{
4008 .pid = @intCast(pid_t, rc),
4009 .status = @bitCast(u32, status),
4010 },
4011 .INTR => continue,
4012 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
4013 .INVAL => unreachable, // Invalid flags.
4014 else => unreachable,
4015 }
4016 }
4017}
4018
4019pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
4020 const Status = if (builtin.link_libc) c_int else u32;
4021 var status: Status = undefined;
4022 const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags;
4023 while (true) {
4024 const rc = system.wait4(pid, &status, coerced_flags, ru);
4005 switch (errno(rc)) {4025 switch (errno(rc)) {
4006 .SUCCESS => return .{4026 .SUCCESS => return .{
4007 .pid = @intCast(pid_t, rc),4027 .pid = @intCast(pid_t, rc),
lib/std/os/linux.zig+10
...@@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {...@@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {
944 return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);944 return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);
945}945}
946946
947pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize {
948 return syscall4(
949 .wait4,
950 @bitCast(usize, @as(isize, pid)),
951 @ptrToInt(status),
952 flags,
953 @ptrToInt(usage),
954 );
955}
956
947pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize {957pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize {
948 return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0);958 return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0);
949}959}