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 }