# System calls In order to track process lifecycle we need to know: - When a new process is created - When an existing process starts executing a new program - When an existing process terminates If you're not familiar with how that works, here's the short version: - The `fork` system call (syscall) creates a copy of the running program. - The `exec` syscall tells the current process to start executing a different program. - The `exit` syscall tells the current process to terminate. It turns out that it's more complicated than that, so let's get into the details. ## Fork Under the hood a process and a thread are roughly the same thing: a "task". A process is modeled as a group of tasks. Confusingly, each task has a "PID", and a process has a "thread group ID" (`tgid`). It follows that since there's a more generic primitive underlying both threads and processes, there's a more generic syscall for creating them. Actually there's two: - `clone` - `clone3` The `fork` system call is actually implemented via `clone` (`kernel_clone` in the kernel, to be precise). In playing around with a previous iteration of `proctrace` that was based on `bpftrace` I found that instrumenting the `fork` syscall didn't collect all events that I'd expect, but instrumenting both `clone` and `clone3` did. This is why we inspect these two syscalls instead of `fork`. However, since the `clone` syscalls are also used to create threads, if you collect all `clone` syscalls you'll end up collecting events that are really just new threads being created. Luckily, both `clone` syscalls take a `flags` argument in one way or another, and one of these flags is `CLONE_THREAD`, which indicates whether the call is creating a new thread or not. In the program that calls `fork` the return value is the PID (`tgid`) of the newly created process. In the newly created process, execution proceeds from where `fork` was called, and the return value of `fork` is 0. You can use this return value to track the process tree. ## Exec There's actually a number of `exec`-like syscalls or `libc` functions, but in terms of tracepoints we instrument both `execve` and `execveat`, the difference being that `execve` takes a path to the program to execute, and `execveat` takes a file descriptor and a path relative to that file descriptor that points at the program to execute. We care about the filename and the arguments to the program. ## Exit Again, there's actually more than one `exit`-like syscall: - `exit` - `exit_group` Calling `exit` will terminate the thread in which it's called. If that thread is the main thread, it will also terminate the process. Calling `exit_group` will terminate the process regardless of which thread calls it. For this reason we record *all* `exit_group` calls, and only the `exit` calls that come from the main thread.