package generate import "strings" type TracepointKind int const ( KindNone TracepointKind = iota KindFd KindOpen KindMqOpen KindExec KindPathname KindName KindRet KindFcntl KindNull KindDup3 KindOpenByHandleAt KindSocket KindSocketpair KindAccept KindPipe KindEventfd KindPidfd KindEpollCtl KindTwoFd KindPoll KindMem KindSleep KindKeyctl KindPtrace KindPerfOpen KindSeccomp KindModule KindSysVId KindSysVOp KindProc KindBpf KindFutex KindPrctl KindTimerObj ) func (k TracepointKind) MetadataName() string { switch k { case KindFd: return "fd" case KindOpen: return "open" case KindMqOpen: return "mq-open" case KindExec: return "exec" case KindPathname: return "pathname" case KindName: return "name" case KindRet: return "ret" case KindFcntl: return "fcntl" case KindNull: return "null" case KindDup3: return "dup3" case KindOpenByHandleAt: return "open-by-handle-at" case KindSocket: return "socket" case KindSocketpair: return "socketpair" case KindAccept: return "accept" case KindPipe: return "pipe" case KindEventfd: return "eventfd" case KindPidfd: return "pidfd" case KindEpollCtl: return "epoll-ctl" case KindTwoFd: return "two-fd" case KindPoll: return "poll" case KindMem: return "mem" case KindSleep: return "sleep" case KindKeyctl: return "keyctl" case KindPtrace: return "ptrace" case KindPerfOpen: return "perf-open" case KindSeccomp: return "seccomp" case KindModule: return "module" case KindSysVId: return "sysv-id" case KindSysVOp: return "sysv-op" case KindProc: return "proc" case KindBpf: return "bpf" case KindFutex: return "futex" case KindPrctl: return "prctl" case KindTimerObj: return "timer-obj" default: return "none" } } type RetClassification string const ( Unclassified RetClassification = "UNCLASSIFIED" ReadClassified RetClassification = "READ_CLASSIFIED" WriteClassified RetClassification = "WRITE_CLASSIFIED" TransferClassified RetClassification = "TRANSFER_CLASSIFIED" ) type ClassificationResult struct { Kind TracepointKind PathnameField string // for KindPathname: e.g. "pathname", "path", "filename", "name", "u_name" } // ClassifyFormat determines the tracepoint kind for a parsed format section. // It mirrors the Raku multi-dispatch: name-based ignores take priority, // then name-only mappings, then each external field is tried in order until // one matches a name+field or generic field pattern. func ClassifyFormat(f *Format) ClassificationResult { if len(f.ExternalFields) == 0 { return ClassificationResult{Kind: KindNone} } if r, ok := classifyNameOnly(f.Name); ok { return r } for _, field := range f.ExternalFields { if field.Name == "__syscall_nr" { continue } if r, ok := classifyNameAndField(f.Name, field.Type, field.Name); ok { return r } if r, ok := classifyByField(field.Type, field.Name); ok { return r } } return ClassificationResult{Kind: KindNone} } // classifyNameOnly handles tracepoints classified by name alone, // independent of any field. // // Keep newly-added syscall expansion mappings in this table first to reduce // switch churn and merge conflicts across incremental tracing phases. var nameOnlyKindsTable = map[string]TracepointKind{ "sys_enter_open_by_handle_at": KindOpenByHandleAt, "sys_enter_io_uring_enter": KindFd, "sys_enter_io_uring_register": KindFd, "sys_enter_fcntl": KindFcntl, "sys_enter_syslog": KindNull, "sys_enter_sync": KindNull, "sys_enter_msync": KindNull, "sys_enter_getcwd": KindNull, "sys_enter_socket": KindSocket, "sys_enter_socketpair": KindSocketpair, "sys_exit_socketpair": KindSocketpair, "sys_enter_accept": KindAccept, "sys_exit_accept": KindAccept, "sys_enter_accept4": KindAccept, "sys_exit_accept4": KindAccept, "sys_enter_pipe": KindPipe, "sys_exit_pipe": KindPipe, "sys_enter_pipe2": KindPipe, "sys_exit_pipe2": KindPipe, "sys_enter_eventfd": KindEventfd, "sys_exit_eventfd": KindEventfd, "sys_enter_eventfd2": KindEventfd, "sys_exit_eventfd2": KindEventfd, "sys_enter_memfd_create": KindEventfd, "sys_exit_memfd_create": KindEventfd, "sys_enter_memfd_secret": KindEventfd, "sys_exit_memfd_secret": KindEventfd, "sys_enter_userfaultfd": KindEventfd, "sys_exit_userfaultfd": KindEventfd, "sys_enter_signalfd": KindEventfd, "sys_exit_signalfd": KindEventfd, "sys_enter_signalfd4": KindEventfd, "sys_exit_signalfd4": KindEventfd, "sys_enter_timerfd_create": KindEventfd, "sys_exit_timerfd_create": KindEventfd, "sys_enter_epoll_create": KindEventfd, "sys_exit_epoll_create": KindEventfd, "sys_enter_epoll_create1": KindEventfd, "sys_exit_epoll_create1": KindEventfd, "sys_enter_inotify_init": KindEventfd, "sys_exit_inotify_init": KindEventfd, "sys_enter_inotify_init1": KindEventfd, "sys_exit_inotify_init1": KindEventfd, "sys_enter_fanotify_init": KindEventfd, "sys_exit_fanotify_init": KindEventfd, "sys_enter_landlock_create_ruleset": KindEventfd, "sys_exit_landlock_create_ruleset": KindEventfd, "sys_enter_landlock_add_rule": KindFd, "sys_enter_landlock_restrict_self": KindFd, "sys_enter_fsopen": KindEventfd, "sys_exit_fsopen": KindEventfd, "sys_enter_fsmount": KindEventfd, "sys_exit_fsmount": KindEventfd, "sys_enter_pidfd_open": KindPidfd, "sys_exit_pidfd_open": KindPidfd, "sys_enter_bind": KindFd, "sys_enter_connect": KindFd, "sys_enter_listen": KindFd, "sys_enter_shutdown": KindFd, "sys_enter_getsockname": KindFd, "sys_enter_getpeername": KindFd, "sys_enter_getsockopt": KindFd, "sys_enter_setsockopt": KindFd, "sys_enter_epoll_wait": KindFd, "sys_enter_epoll_pwait": KindFd, "sys_enter_epoll_pwait2": KindFd, "sys_enter_epoll_ctl": KindEpollCtl, "sys_enter_move_mount": KindTwoFd, // close_range(first, last, flags) needs all three arguments, so it is a // two_fd_event (fd_a=first, fd_b=last, extra=flags) rather than a single-fd // fd_event. This lets the runtime honour the upper bound and the // CLOSE_RANGE_CLOEXEC flag instead of closing every fd >= first. "sys_enter_close_range": KindTwoFd, // sendfile64(out_fd, in_fd, offset, count) transfers bytes between two file // descriptors inside the kernel and returns the number of bytes written to // out_fd (TransferClassified, see retClassifications). Its tracepoint fields // (out_fd, in_fd, offset, count) carry no field literally named "fd", so // without an explicit override it would fall through to KindNull and capture // no descriptor at all — unlike its sibling copy_file_range, which is a // KindFd event. Capture out_fd (args[0], the destination the bytes are // written to) so sendfile64 attributes its transfer to a concrete fd, matching // the single-fd KindFd convention used for copy_file_range and the // read/write/sendto/recvfrom families. "sys_enter_sendfile64": KindFd, "sys_enter_statmount": KindNull, "sys_enter_listmount": KindNull, "sys_enter_listns": KindNull, "sys_enter_poll": KindPoll, "sys_enter_ppoll": KindPoll, "sys_enter_select": KindPoll, "sys_enter_pselect6": KindPoll, "sys_enter_msgget": KindSysVId, "sys_enter_semget": KindSysVId, "sys_enter_shmget": KindSysVId, "sys_enter_msgsnd": KindSysVOp, "sys_enter_msgrcv": KindSysVOp, "sys_enter_msgctl": KindSysVOp, "sys_enter_semop": KindSysVOp, "sys_enter_semtimedop": KindSysVOp, "sys_enter_semctl": KindSysVOp, "sys_enter_shmat": KindSysVOp, "sys_enter_shmdt": KindSysVOp, "sys_enter_shmctl": KindSysVOp, "sys_enter_clone": KindProc, "sys_enter_clone3": KindProc, "sys_enter_fork": KindProc, "sys_enter_vfork": KindProc, "sys_enter_wait4": KindProc, "sys_enter_waitid": KindProc, "sys_enter_bpf": KindBpf, "sys_enter_mprotect": KindMem, "sys_enter_madvise": KindMem, "sys_enter_pkey_mprotect": KindMem, "sys_enter_brk": KindMem, "sys_enter_munmap": KindMem, "sys_enter_mremap": KindMem, "sys_enter_mincore": KindMem, "sys_enter_remap_file_pages": KindMem, "sys_enter_mlock": KindMem, "sys_enter_mlock2": KindMem, "sys_enter_munlock": KindMem, "sys_enter_mseal": KindMem, "sys_enter_map_shadow_stack": KindMem, "sys_enter_pkey_alloc": KindNull, "sys_enter_pkey_free": KindNull, "sys_enter_mbind": KindNull, "sys_enter_set_mempolicy": KindNull, "sys_enter_get_mempolicy": KindNull, "sys_enter_set_mempolicy_home_node": KindNull, "sys_enter_migrate_pages": KindNull, "sys_enter_move_pages": KindNull, "sys_enter_mlockall": KindNull, "sys_enter_munlockall": KindNull, "sys_enter_process_madvise": KindFd, "sys_enter_process_mrelease": KindFd, "sys_enter_pidfd_send_signal": KindFd, "sys_enter_kexec_file_load": KindFd, "sys_enter_kcmp": KindTwoFd, "sys_enter_mq_timedsend": KindFd, "sys_enter_mq_timedreceive": KindFd, "sys_enter_mq_notify": KindFd, "sys_enter_mq_getsetattr": KindFd, "sys_enter_execve": KindExec, "sys_enter_execveat": KindExec, "sys_enter_exit": KindNull, "sys_enter_exit_group": KindNull, "sys_enter_rt_sigaction": KindNull, "sys_enter_rt_sigprocmask": KindNull, "sys_enter_rt_sigpending": KindNull, "sys_enter_rt_sigsuspend": KindNull, "sys_enter_rt_sigtimedwait": KindNull, "sys_enter_rt_sigreturn": KindNull, "sys_enter_sigaltstack": KindNull, "sys_enter_pause": KindNull, "sys_enter_rt_sigqueueinfo": KindNull, "sys_enter_rt_tgsigqueueinfo": KindNull, "sys_enter_futex": KindFutex, "sys_enter_futex_wait": KindFutex, "sys_enter_futex_wake": KindFutex, "sys_enter_futex_requeue": KindFutex, "sys_enter_futex_waitv": KindFutex, "sys_enter_kill": KindNull, "sys_enter_prctl": KindPrctl, "sys_enter_setns": KindFd, "sys_enter_unshare": KindNull, "sys_enter_nanosleep": KindSleep, "sys_enter_clock_nanosleep": KindSleep, "sys_enter_clock_gettime": KindNull, "sys_enter_clock_settime": KindNull, "sys_enter_clock_getres": KindNull, "sys_enter_clock_adjtime": KindNull, "sys_enter_gettimeofday": KindNull, "sys_enter_settimeofday": KindNull, "sys_enter_time": KindNull, "sys_enter_times": KindNull, "sys_enter_adjtimex": KindNull, "sys_enter_alarm": KindNull, "sys_enter_getitimer": KindNull, "sys_enter_setitimer": KindNull, "sys_enter_timer_create": KindTimerObj, "sys_enter_timer_settime": KindTimerObj, "sys_enter_timer_gettime": KindTimerObj, "sys_enter_timer_getoverrun": KindTimerObj, "sys_enter_timer_delete": KindTimerObj, "sys_enter_keyctl": KindKeyctl, "sys_enter_add_key": KindKeyctl, "sys_enter_request_key": KindKeyctl, "sys_enter_ptrace": KindPtrace, "sys_enter_perf_event_open": KindPerfOpen, "sys_enter_seccomp": KindSeccomp, "sys_exit_seccomp": KindSeccomp, "sys_enter_init_module": KindModule, "sys_exit_init_module": KindModule, "sys_enter_delete_module": KindModule, "sys_exit_delete_module": KindModule, "sys_enter_getpid": KindNull, "sys_enter_gettid": KindNull, "sys_enter_getppid": KindNull, "sys_enter_getuid": KindNull, "sys_enter_geteuid": KindNull, "sys_enter_getgid": KindNull, "sys_enter_getegid": KindNull, "sys_enter_getresuid": KindNull, "sys_enter_getresgid": KindNull, "sys_enter_getgroups": KindNull, "sys_enter_setuid": KindNull, "sys_enter_seteuid": KindNull, "sys_enter_setgid": KindNull, "sys_enter_setegid": KindNull, "sys_enter_setresuid": KindNull, "sys_enter_setresgid": KindNull, "sys_enter_setreuid": KindNull, "sys_enter_setregid": KindNull, "sys_enter_setfsuid": KindNull, "sys_enter_setfsgid": KindNull, "sys_enter_setgroups": KindNull, "sys_enter_umask": KindNull, "sys_enter_setsid": KindNull, "sys_enter_getsid": KindNull, "sys_enter_setpgid": KindNull, "sys_enter_getpgid": KindNull, "sys_enter_getpgrp": KindNull, "sys_enter_set_tid_address": KindNull, "sys_enter_sched_yield": KindNull, "sys_enter_sched_setaffinity": KindNull, "sys_enter_sched_getaffinity": KindNull, "sys_enter_sched_setparam": KindNull, "sys_enter_sched_getparam": KindNull, "sys_enter_sched_setscheduler": KindNull, "sys_enter_sched_getscheduler": KindNull, "sys_enter_sched_setattr": KindNull, "sys_enter_sched_getattr": KindNull, "sys_enter_sched_get_priority_max": KindNull, "sys_enter_sched_get_priority_min": KindNull, "sys_enter_sched_rr_get_interval": KindNull, "sys_enter_getcpu": KindNull, "sys_enter_getrusage": KindNull, "sys_enter_getrlimit": KindNull, "sys_enter_setrlimit": KindNull, "sys_enter_prlimit64": KindNull, "sys_enter_getpriority": KindNull, "sys_enter_setpriority": KindNull, "sys_enter_membarrier": KindNull, "sys_enter_rseq": KindNull, "sys_enter_set_robust_list": KindNull, "sys_enter_get_robust_list": KindNull, "sys_enter_mmap2": KindNull, "sys_enter_kexec_load": KindNull, "sys_enter_sysinfo": KindNull, "sys_enter_sysfs": KindNull, "sys_enter_ustat": KindNull, "sys_enter_newuname": KindNull, "sys_enter_sethostname": KindNull, "sys_enter_setdomainname": KindNull, "sys_enter_capget": KindNull, "sys_enter_capset": KindNull, "sys_enter_personality": KindNull, "sys_enter_reboot": KindNull, "sys_enter_restart_syscall": KindNull, "sys_enter_vhangup": KindNull, "sys_enter_arch_prctl": KindNull, "sys_enter_ioperm": KindNull, "sys_enter_iopl": KindNull, "sys_enter_modify_ldt": KindNull, "sys_enter_lsm_get_self_attr": KindNull, "sys_enter_lsm_set_self_attr": KindNull, "sys_enter_lsm_list_modules": KindNull, } var nameOnlyPrefixKinds = []struct { prefix string kind TracepointKind }{ {prefix: "sys_enter_io_", kind: KindNull}, } func classifyNameOnly(name string) (ClassificationResult, bool) { if kind, ok := nameOnlyKindsTable[name]; ok { return ClassificationResult{Kind: kind}, true } for _, prefixKind := range nameOnlyPrefixKinds { if strings.HasPrefix(name, prefixKind.prefix) { return ClassificationResult{Kind: prefixKind.kind}, true } } return ClassificationResult{}, false } // classifyNameAndField handles tracepoints that need both the name and // a specific field to classify. func classifyNameAndField(name, fieldType, fieldName string) (ClassificationResult, bool) { switch name { case "sys_enter_dup": if fieldType == "unsigned int" && fieldName == "fildes" { return ClassificationResult{Kind: KindFd}, true } case "sys_enter_dup2": if fieldType == "unsigned int" && fieldName == "oldfd" { return ClassificationResult{Kind: KindFd}, true } case "sys_enter_dup3": if fieldType == "unsigned int" && fieldName == "oldfd" { return ClassificationResult{Kind: KindDup3}, true } case "sys_enter_name_to_handle_at": if isCStringPtrType(fieldType) && fieldName == "name" { return ClassificationResult{Kind: KindPathname, PathnameField: "name"}, true } case "sys_enter_copy_file_range": if isFdType(fieldType) && fieldName == "fd_in" { return ClassificationResult{Kind: KindFd}, true } case "sys_enter_mount": if isCStringPtrType(fieldType) && fieldName == "dir_name" { return ClassificationResult{Kind: KindPathname, PathnameField: "dir_name"}, true } case "sys_enter_umount": if isCStringPtrType(fieldType) && fieldName == "name" { return ClassificationResult{Kind: KindPathname, PathnameField: "name"}, true } case "sys_enter_acct": if isCStringPtrType(fieldType) && fieldName == "name" { return ClassificationResult{Kind: KindPathname, PathnameField: "name"}, true } case "sys_enter_pivot_root": if isCStringPtrType(fieldType) && fieldName == "new_root" { return ClassificationResult{Kind: KindPathname, PathnameField: "new_root"}, true } case "sys_enter_quotactl": if isCStringPtrType(fieldType) && fieldName == "special" { return ClassificationResult{Kind: KindPathname, PathnameField: "special"}, true } case "sys_enter_swapon": if isCStringPtrType(fieldType) && fieldName == "specialfile" { return ClassificationResult{Kind: KindPathname, PathnameField: "specialfile"}, true } case "sys_enter_swapoff": if isCStringPtrType(fieldType) && fieldName == "specialfile" { return ClassificationResult{Kind: KindPathname, PathnameField: "specialfile"}, true } case "sys_enter_mq_open": if isCStringPtrType(fieldType) && fieldName == "u_name" { return ClassificationResult{Kind: KindMqOpen}, true } case "sys_enter_mq_unlink": if isCStringPtrType(fieldType) && fieldName == "u_name" { return ClassificationResult{Kind: KindPathname, PathnameField: "u_name"}, true } } if strings.HasPrefix(name, "sys_enter") && strings.Contains(name, "open") && isCStringPtrType(fieldType) && fieldName == "filename" { return ClassificationResult{Kind: KindOpen}, true } return ClassificationResult{}, false } func classifyByField(fieldType, fieldName string) (ClassificationResult, bool) { switch { case fieldName == "fd" && isFdType(fieldType): return ClassificationResult{Kind: KindFd}, true case isCStringPtrType(fieldType) && fieldName == "newname": return ClassificationResult{Kind: KindName}, true case isCStringPtrType(fieldType) && fieldName == "pathname": return ClassificationResult{Kind: KindPathname, PathnameField: "pathname"}, true case isCStringPtrType(fieldType) && fieldName == "path": return ClassificationResult{Kind: KindPathname, PathnameField: "path"}, true case isCStringPtrType(fieldType) && fieldName == "filename": return ClassificationResult{Kind: KindPathname, PathnameField: "filename"}, true case fieldType == "long" && fieldName == "ret": return ClassificationResult{Kind: KindRet}, true } return ClassificationResult{}, false } func isFdType(t string) bool { return t == "unsigned int" || t == "unsigned long" || t == "int" } func isCStringPtrType(t string) bool { return t == "const char *" || t == "char *" } // ClassifyRet returns the RetClassification for a syscall exit name. func ClassifyRet(name string) RetClassification { syscall := strings.ToLower(strings.TrimPrefix(name, "sys_exit_")) if c, ok := retClassifications[syscall]; ok { return c } return Unclassified } var retClassifications = map[string]RetClassification{ "fgetxattr": ReadClassified, "flistxattr": ReadClassified, "getdents": ReadClassified, "getdents64": ReadClassified, "getxattr": ReadClassified, // getxattrat (Linux 6.13+) returns the size in bytes of the xattr value, // exactly like getxattr/lgetxattr/fgetxattr, so it is a read byte-count. "getxattrat": ReadClassified, "lgetxattr": ReadClassified, "listxattr": ReadClassified, // listxattrat (Linux 6.13+) returns the size in bytes of the list of // extended attribute names, exactly like listxattr/llistxattr/flistxattr, // so it is a read byte-count. "listxattrat": ReadClassified, "llistxattr": ReadClassified, "pread64": ReadClassified, "preadv": ReadClassified, "preadv2": ReadClassified, "process_vm_readv": ReadClassified, "read": ReadClassified, "readlink": ReadClassified, "readlinkat": ReadClassified, "readv": ReadClassified, "recvmsg": ReadClassified, "recvfrom": ReadClassified, "msgrcv": ReadClassified, "getrandom": ReadClassified, "syslog": ReadClassified, "mq_timedreceive": ReadClassified, "copy_file_range": TransferClassified, "sendfile64": TransferClassified, "splice": TransferClassified, "tee": TransferClassified, "vmsplice": TransferClassified, "process_vm_writev": WriteClassified, "pwrite64": WriteClassified, "pwritev": WriteClassified, "pwritev2": WriteClassified, "sendmsg": WriteClassified, "sendto": WriteClassified, // msgsnd is deliberately NOT listed here: msgsnd(2) returns 0 on success or // -1 on error — it is NOT a byte count (the payload size msgsz is an INPUT // arg, never the return). Like its SysV IPC siblings (msgrcv excepted, which // genuinely returns a received byte count), msgsnd's int status must stay // UNCLASSIFIED so the stats engine never treats the return as bytes written. "write": WriteClassified, "writev": WriteClassified, // mq_timedsend is deliberately NOT listed here: mq_timedsend(2)/mq_send(3) // return 0 on success or -1 on error — NOT a byte count (msg_len is an // INPUT arg, never the return). Listing it as WriteClassified made // bytesFromRet attribute its 0 return as "bytes written". Like its POSIX mq // sibling mq_timedreceive (which genuinely returns the received byte count // and stays ReadClassified), mq_timedsend's int status must stay // UNCLASSIFIED. This mirrors the SysV IPC msgsnd vs msgrcv asymmetry. }