Want to capture and inspect every process that runs on your system — in real time?
In this week’s newsletter, we’re building a lightweight process monitor with eBPF and Go.
You’ll learn how to trace every process execution, extract metadata like the PID and command name, and forward it to a Go user-space application.
Whether you're new to tracing or just want a working example, this ones for you.
🚀 Special thanks to Satyam Dubey, Software Engineer at NgKore, for putting together this practical guest post for eBPFChirp!
To follow along, you are gonna need Linux Kernel 5.x or later (can be checked with uname -r
) and Golang 1.18+.
For readers just looking for the code — it’s available in the following GitHub repository.
Step 1: Installing Dependencies
In order to be able to compile and run the eBPF program, we are gonna need libbpf, bpftool, llvm, clang, linux headers and the bpf2go helper tool.
On Ubuntu/Debian you can easily install them using:
Step 2: Writing the eBPF Kernel Space Program
The eBPF project will consist of two parts — namely the Golang user space application and the eBPF kernel space program.
To start off create an empty code repository (on GitHub if you prefer) and inside it, create an ebpf
directory.
Then in this ebpf
directory place the following eBPF Program written in C:
Code comments should provide sufficient information, but in short the program:
Hooks into the
sys_enter_execve
tracepoint to capture process executions as they occur.Uses
bpf_get_current_task()
andbpf_get_current_comm()
to gather process context, namely:The process ID (PID)
The command name (i.e., the name of the executable)
Sends this data to Golang user space application using a perf event buffer (
bpf_perf_event_output
).
Step 3: Generating eBPF Bytecode Using bpf2go
Instead of manually compiling with clang
, we’ll use bpf2go to generate eBPF bindings for Go.
To achieve this we need to create a go_monitor_objects.go
file in the root of your repository with the following content:
Then by invoking go generate
at the root of your repository, this command (with the help of bpf2go directive) will:
Compile
ebpf_monitor.c
into BPF bytecodeGenerate Go bindings into
ebpf_monitor_bpf.go
that embed the bytecode fromebpf_monitor_bpf.o
and provide helper functions to load the eBPF kernel space program and attach it to the appropriate kernel hook points
By now you repository layout should be as follows:
Step 4: Writing the Go User Space Program
Now, in the final step, we just need to complete the Go user-space application that reads events from the ring buffer whenever the eBPF kernel program captures a process execution.
To do that, update your go_monitor_objects.go
file as follows:
Code comments should provide sufficient information, but in short the program:
Loads the compiled eBPF program and eBPF maps objects (generated using
bpf2go
).Attaches the eBPF program to the
sys_enter_execve
tracepoint, which triggers on every process execution.Creates a perf buffer “listener” to receive events emitted by the eBPF program from kernel space.
Listens for and decodes events in real time, extracting:
The process ID (
PID
)The command name (
comm
)
Prints the details of every process executed on the system as they occur.
Step 5: Compiling and Running the Program
Now, let’s build and run the eBPF process monitor using:
Now, every time a process executes, our eBPF program captures and logs its PID and command name in real-time.
And that’s it — you just created an eBPF-based process monitor.
⏪ Did you miss the previous issues? I'm sure you wouldn't, but JUST in case:
I hope you find this resource helpful. Keep an eye out for more updates and developments in eBPF in next week's newsletter.
Until then, keep 🐝-ing!
Warm regards, Teodor and Satyam