lib/sys/src/process/tracing/operations.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const builtin = @import("builtin");
 3 const linux = std.os.linux;
 4 const ptrace = linux.PTRACE;
 5 
 6 pub const Event = enum { fork, vfork, clone, exec, stop, signal, syscall };
 7 pub const Observation = struct {
 8     pid: i32,
 9     state: union(enum) {
10         ended: struct { code: ?u8, signal: ?u8 },
11         stopped: struct { event: Event, signal: u8 },
12     },
13 };
14 
15 pub fn attach(pid: i32, syscalls: bool) !void {
16     if (comptime builtin.os.tag != .linux) return error.UnsupportedPlatform;
17     const options = ptrace.O.TRACEFORK | ptrace.O.TRACEVFORK | ptrace.O.TRACECLONE |
18         ptrace.O.TRACEEXEC | ptrace.O.EXITKILL |
19         (if (syscalls) ptrace.O.TRACESYSGOOD else @as(u32, 0));
20     try operation(ptrace.SEIZE, pid, options);
21 }
22 
23 pub fn continueTask(pid: i32, signal: u8, syscalls: bool) !void {
24     try operation(if (syscalls) ptrace.SYSCALL else ptrace.CONT, pid, signal);
25 }
26 pub fn listen(pid: i32) !void {
27     try operation(ptrace.LISTEN, pid, 0);
28 }
29 pub fn terminate(pid: i32) void {
30     _ = linux.kill(pid, .KILL);
31 }
32 
33 pub fn message(pid: i32) !usize {
34     var result: usize = 0;
35     try operation(ptrace.GETEVENTMSG, pid, @intFromPtr(&result));
36     return result;
37 }
38 
39 pub fn wait() !Observation {
40     var status: i32 = 0;
41     const result = linux.waitpid(-1, &status, 0x40000000);
42     switch (linux.errno(result)) {
43         .SUCCESS => {},
44         .INTR => return error.Interrupted,
45         .CHILD => return error.NoChildren,
46         else => return error.TraceWaitFailed,
47     }
48     const pid: i32 = @intCast(result);
49     if ((status & 0xff) != 0x7f) {
50         const signal: ?u8 = if ((status & 0x7f) == 0) null else @intCast(status & 0x7f);
51         const code: ?u8 = if (signal == null) @intCast((status >> 8) & 0xff) else null;
52         return .{ .pid = pid, .state = .{ .ended = .{ .code = code, .signal = signal } } };
53     }
54     const event: Event = switch (status >> 16) {
55         ptrace.EVENT.FORK => .fork,
56         ptrace.EVENT.VFORK => .vfork,
57         ptrace.EVENT.CLONE => .clone,
58         ptrace.EVENT.EXEC => .exec,
59         ptrace.EVENT.STOP => .stop,
60         0 => if (((status >> 8) & 0xff) == 133) .syscall else .signal,
61         else => return error.UnexpectedTraceEvent,
62     };
63     return .{ .pid = pid, .state = .{ .stopped = .{
64         .event = event,
65         .signal = @intCast((status >> 8) & 0xff),
66     } } };
67 }
68 
69 fn operation(request: u32, pid: i32, data: usize) !void {
70     std.debug.assert(pid > 0);
71     const result = linux.ptrace(request, pid, 0, data, 0);
72     return switch (linux.errno(result)) {
73         .SUCCESS => {},
74         .PERM, .ACCES => error.TracingPermissionDenied,
75         else => error.TraceOperationFailed,
76     };
77 }