From 963d29fc904f4e1b42a8e5119dfcc5268f69dcf1 Mon Sep 17 00:00:00 2001 From: iretq Date: Mon, 29 Dec 2025 14:25:12 +0100 Subject: [PATCH] syscall log: support for selective syscall tracing --- kernel-src/arch/x86-64/archctl.c | 28 ++++++++++++ kernel-src/arch/x86-64/syscalllog.c | 8 ++++ kernel-src/include/kernel/proc.h | 3 ++ kernel-src/sys/syscalls/fork.c | 3 ++ patches/mlibc/jinx-working-patch.patch | 16 +++++++ recipes/systrace | 14 ++++++ source-recipes/systrace | 7 +++ tools/systrace/Makefile | 14 ++++++ tools/systrace/main.c | 62 ++++++++++++++++++++++++++ 9 files changed, 155 insertions(+) create mode 100644 recipes/systrace create mode 100644 source-recipes/systrace create mode 100644 tools/systrace/Makefile create mode 100644 tools/systrace/main.c diff --git a/kernel-src/arch/x86-64/archctl.c b/kernel-src/arch/x86-64/archctl.c index 51a04cd2..cc5cf127 100644 --- a/kernel-src/arch/x86-64/archctl.c +++ b/kernel-src/arch/x86-64/archctl.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,6 +7,11 @@ #define ARCH_CTL_SET_FSBASE 1 #define ARCH_CTL_GET_GSBASE 2 #define ARCH_CTL_GET_FSBASE 3 +#define ARCH_CTL_SET_SYSTRACE 4 + +#define ARCH_CTL_SYSTRACE_OFF 0 +#define ARCH_CTL_SYSTRACE_SELF 1 +#define ARCH_CTL_SYSTRACE_ALL 2 static inline int copy_u64_to_user(void *ptr, uint64_t v) { return usercopy_touser(ptr, &v, sizeof(v)); @@ -36,6 +42,28 @@ syscallret_t syscall_archctl(context_t *context, int func, void *arg) { case ARCH_CTL_GET_FSBASE: ret.errno = copy_u64_to_user(arg, rdmsr(MSR_FSBASE)); break; + case ARCH_CTL_SET_SYSTRACE: +#ifdef SYSCALL_LOGGING + switch ((uint64_t)arg) { + case ARCH_CTL_SYSTRACE_OFF: + current_thread()->proc->flags &= ~(PROC_FLAG_SYSTRACE | PROC_FLAG_SYSTRACE_SELF); + break; + case ARCH_CTL_SYSTRACE_SELF: + current_thread()->proc->flags |= PROC_FLAG_SYSTRACE | PROC_FLAG_SYSTRACE_SELF; + break; + case ARCH_CTL_SYSTRACE_ALL: + current_thread()->proc->flags |= PROC_FLAG_SYSTRACE; + current_thread()->proc->flags &= ~PROC_FLAG_SYSTRACE_SELF; + break; + default: + ret.errno = EINVAL; + break; + } +#else + // signal that syscall logging is not supported + ret.errno = ENOTSUP; +#endif + break; default: ret.errno = EINVAL; break; diff --git a/kernel-src/arch/x86-64/syscalllog.c b/kernel-src/arch/x86-64/syscalllog.c index d4545c04..c4bfc9a2 100644 --- a/kernel-src/arch/x86-64/syscalllog.c +++ b/kernel-src/arch/x86-64/syscalllog.c @@ -5,6 +5,9 @@ #ifdef SYSCALL_LOGGING +void arch_interrupt_disable(); +void arch_interrupt_enable(); + #define SYSCALL_COUNT 99 #define LOGSTR(x) arch_e9_puts(x) @@ -225,6 +228,9 @@ __attribute__((no_caller_saved_registers)) void arch_syscall_log(int syscall, ui thread_t *thread = current_thread(); proc_t *proc = thread->proc; + if (!(proc->flags & PROC_FLAG_SYSTRACE)) + return; + snprintf(argbuff, 768, syscall < SYSCALL_COUNT ? args[syscall] : "N/A", a1, a2, a3, a4, a5, a6); snprintf(printbuff, 1024, "\e[92msyscall: pid %d tid %d: %s: %s (%lu cached pages, %lu free pages)\n\e[0m", proc->pid, thread->tid, syscall < SYSCALL_COUNT ? name[syscall] : "invalid syscall", argbuff, vmmcache_cachedpages, freepagecount); @@ -242,6 +248,8 @@ __attribute__((no_caller_saved_registers)) void arch_syscall_log_return(uint64_t thread_t *thread = current_thread(); proc_t *proc = thread->proc; + if (!(proc->flags & PROC_FLAG_SYSTRACE)) + return; snprintf(printbuff, 1024, "\e[94msyscall return: pid %d tid %d: %lu %s (%lu cached pages, %lu free pages)\n\e[0m", proc->pid, thread->tid, ret, strerror(errno), vmmcache_cachedpages, freepagecount); arch_interrupt_disable(); diff --git a/kernel-src/include/kernel/proc.h b/kernel-src/include/kernel/proc.h index bb34e23b..e25535ff 100644 --- a/kernel-src/include/kernel/proc.h +++ b/kernel-src/include/kernel/proc.h @@ -16,6 +16,9 @@ #define PROC_STATE_NORMAL 0 #define PROC_STATE_ZOMBIE 1 +#define PROC_FLAG_SYSTRACE (1 << 0) +#define PROC_FLAG_SYSTRACE_SELF (1 << 1) + typedef struct proc_t { mutex_t mutex; int refcount; diff --git a/kernel-src/sys/syscalls/fork.c b/kernel-src/sys/syscalls/fork.c index 00eb8fda..d9687ec4 100644 --- a/kernel-src/sys/syscalls/fork.c +++ b/kernel-src/sys/syscalls/fork.c @@ -50,6 +50,9 @@ syscallret_t syscall_fork(context_t *ctx) { nproc->root = proc_get_root(); nproc->cwd = proc_get_cwd(); nproc->threadlist = nthread; + if (proc->flags & PROC_FLAG_SYSTRACE && !(proc->flags & PROC_FLAG_SYSTRACE_SELF)) + nproc->flags |= PROC_FLAG_SYSTRACE; + nthread->procnext = NULL; ARCH_CONTEXT_THREADSAVE(nthread, ctx); diff --git a/patches/mlibc/jinx-working-patch.patch b/patches/mlibc/jinx-working-patch.patch index 94a0e24a..4b6dae28 100644 --- a/patches/mlibc/jinx-working-patch.patch +++ b/patches/mlibc/jinx-working-patch.patch @@ -105,3 +105,19 @@ index 939925e..5ee7fad 100644 int sys_readv(int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_read) { long r; int error = syscall(SYSCALL_READV, &r, fd, (uint64_t)iovs, iovc); +diff --git mlibc-clean/sysdeps/astral/include/astral/archctl.h mlibc-workdir/sysdeps/astral/include/astral/archctl.h +index 7e4f2a1..a5f382d 100644 +--- mlibc-clean/sysdeps/astral/include/astral/archctl.h ++++ mlibc-workdir/sysdeps/astral/include/astral/archctl.h +@@ -5,6 +5,11 @@ + #define ARCH_CTL_SET_FSBASE 1 + #define ARCH_CTL_GET_GSBASE 2 + #define ARCH_CTL_GET_FSBASE 3 ++#define ARCH_CTL_SET_SYSTRACE 4 ++ ++#define ARCH_CTL_SYSTRACE_OFF 0 ++#define ARCH_CTL_SYSTRACE_SELF 1 ++#define ARCH_CTL_SYSTRACE_ALL 2 + + #ifdef __cplusplus + extern "C" { diff --git a/recipes/systrace b/recipes/systrace new file mode 100644 index 00000000..5e6c36c1 --- /dev/null +++ b/recipes/systrace @@ -0,0 +1,14 @@ +name=systrace +revision=1 +from_source=systrace +imagedeps="build-essential" +hostdeps="xbinutils xgcc" +deps="base" + +build() { + make -C ${source_dir} -j ${parallelism} +} + +package() { + make -C ${source_dir} install PREFIX=${prefix} DESTDIR=${dest_dir} +} diff --git a/source-recipes/systrace b/source-recipes/systrace new file mode 100644 index 00000000..86ff27b3 --- /dev/null +++ b/source-recipes/systrace @@ -0,0 +1,7 @@ +name=systrace +version=0 +source_dir="tools/systrace" + +prepare() { + true +} diff --git a/tools/systrace/Makefile b/tools/systrace/Makefile new file mode 100644 index 00000000..3fab5854 --- /dev/null +++ b/tools/systrace/Makefile @@ -0,0 +1,14 @@ +.phony: all install +CC=x86_64-astral-gcc +LD=x86_64-astral-gcc +all: systrace + +install: + mkdir -p "$(DESTDIR)/$(PREFIX)/sbin/" + cp systrace "$(DESTDIR)/$(PREFIX)/sbin/systrace" + +systrace: main.o + $(LD) $(LDFLAGS) -g -o $@ $^ + +main.o: main.c + $(CC) -c $(CFLAGS) -g -o $@ $< diff --git a/tools/systrace/main.c b/tools/systrace/main.c new file mode 100644 index 00000000..f8573ed1 --- /dev/null +++ b/tools/systrace/main.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +static int arch_ctl_systrace(uint64_t mode) { + return arch_ctl(ARCH_CTL_SET_SYSTRACE, (void *)mode); +} + +int main(int argc, char *argv[]) { + // make sure syscall tracing is supported + if (arch_ctl_systrace(ARCH_CTL_SYSTRACE_OFF) != 0) { + if (errno == ENOTSUP) { + fprintf(stderr, "%s: compile the kernel with syscall tracing enabled\n", argv[0]); + } else { + perror("arch_ctl failed"); + } + return 1; + } + + uint64_t mode = ARCH_CTL_SYSTRACE_SELF; + + // check for -ff flag + if (argc > 1 && strcmp(argv[1], "-ff") == 0) { + mode = ARCH_CTL_SYSTRACE_ALL; + argv++; + argc--; + } + + if (argc < 2) { + fprintf(stderr, "usage: %s [-ff] [args...]\n", argv[0]); + fprintf(stderr, "example: %s ls -l /\n", argv[0]); + return 1; + } + + int pid = fork(); + if (pid < 0) { + perror("fork failed"); + return 1; + } + + if (pid == 0) { + // enable syscall tracing for the child process + if (arch_ctl_systrace(mode) != 0) { + perror("failed to enable syscall tracing"); + return 1; + } + fprintf(stderr, "%s: tracee is pid %d\n", argv[0], getpid()); + if (execvp(argv[1], &argv[1]) != 0) { + arch_ctl_systrace(ARCH_CTL_SYSTRACE_OFF); + perror("execvp failed"); + return 1; + } + // not reached + } else { + int status; + waitpid(pid, &status, 0); + return WIFEXITED(status) ? WEXITSTATUS(status) : 1; + } +}