Monitor the CPU loading using ebpf to reduce system call overhead
A lightweight CPU utilization monitor built with eBPF + libbpf.
It hooks into the kernel scheduler (sched_switch tracepoint) to measure per-CPU
active time with nanosecond precision — more accurate than polling-based tools like
top, and with lower overhead at high process counts.
How it works
Kernel (eBPF) User space (C + libbpf)
----------------------- -------------------------
sched_switch tracepoint poll BPF maps every 1 s
-> record on-CPU time per core -> compute active % and idle %
-> accumulate in BPF Array map print per-core + average
The sched_switch tracepoint context structure is defined manually in
cpu_monitor_bpf.c.
Output
The monitor displays per-core active/idle percentages, with the idle column
matching top's id field for easy comparison:
CPU Core | Active % | Idle % (=top id) ------------------------------------------- CPU 0 | 0.34% | 99.66% CPU 1 | 21.89% | 78.11% CPU 2 | 0.15% | 99.85% CPU 3 | 0.03% | 99.97% ------------------------------------------- TOTAL AVG | 5.60% | 94.40%
To compare with top, press 1 in top to show per-core stats, then compare
the id column with the Idle % column above.
Prerequisites
Host (build machine)
Package
Purpose
clang + llvm version 21
Compile BPF C source to BPF ELF
bpftool
Generate BFP skeleton headers
aarch64 Poky Toolchain
Corss-compile the user-space binary
Install on Ubuntu/Debian:
wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh ./llvm.sh 21 apt update apt install -y llvm-21 clang-21 lld-21 lldb-21 update-alternatives --install /usr/bin/llvm-link llvm-link /usr/bin/llvm-link-21 100 update-alternatives --install /usr/bin/clang clang /usr/bin/clang-21 100 update-alternatives --install /usr/bin/llc llc /usr/bin/llc-21 100 llvm-link --version clang --version # install bpftool apt install linux-tools-$(uname -r)
Target board
Tested Linux kernel == 6.18.20-2.0.0
Tested image == imx-image-full
Most BPF configurations are enabled by default. In addition, you need CONFIG_FTRACE=y to enable tracepoint support (required for the sched_switch hook).
Build
Build the project on your host machine:
TOOLCHAIN_PATH=/opt/fsl-imx-internal-wayland/6.18-whinlatter/environment-setup-armv8a-poky-linux ./build_and_deploy.sh
The script will:
Source the Poky toolchain environment
Run make clean && make all (compiles BPF object, generates skeleton,
cross-compiles user-space binary)
Install the binary to ./board_deploy/cpu_monitor
Deploy and run
# Copy binary to the board scp board_deploy/cpu_monitor root@<board-ip>:/usr/bin/ # Run on the board (root required for eBPF) ssh root@<board-ip> ./cpu_monitor
Press Ctrl+C to exit.
Benchmarking: eBPF vs top
A scheduler stress test script is included to compare monitoring overhead
between cpu_monitor and top under high scheduling pressure.
Run the stress test
On the target board, open three terminals:
# Terminal 1: start scheduler pressure (200 threads, 30 seconds) ./stress_sched.sh -t 200 -d 30 # Terminal 2: measure eBPF monitoring overhead perf stat -e cpu-clock,task-clock,context-switches,cpu-migrations,instructions timeout -s KILL 10 ./cpu_monitor # Terminal 3: measure top monitoring overhead perf stat -e cpu-clock,task-clock,context-switches,cpu-migrations,instructions top -b -d 1 -n 10
Compare task-clock, instructions, and context-switches from perf stat
output. Try different thread counts to observe scaling behavior:
./stress_sched.sh -t 50 -d 30 # low pressure ./stress_sched.sh -t 200 -d 30 # medium pressure ./stress_sched.sh -t 500 -d 30 # high pressure
Result
Test environment: i.MX943 (4x Cortex-A55), Linux 6.18.20, 10-second measurement window.
Comparison results in free system load:
Indicator
eBPF cpu_monitor
top
delta
task-clock (CPU timing)
12.0 ms
263.9 ms
22x
CPU usage
0.1%
2.8%
28x
instructions
3.38M
197M
58x
context-switches
12
17
sys time (kernel time)
5.0ms
199.5ms
40x
user time
0
62.1ms
Comparison results in high scheduling pressure (200 threads):
Indicator
eBPF cpu_monitor
top
delta
task-clock (CPU timing)
12.6ms
690ms
54x
CPU usage
0.1%
6.1%
61x
instructions
4.8M
538M
112x
context-switches
122
9019
75x
sys time (kernel time)
4ms
461ms
115x
user time
0
206ms
Project structure
ebpf_cpu_usage/ ├── cpu_monitor_bpf.c # Kernel-side eBPF program (C, compiled to BPF) ├── cpu_monitor.c # User-space program (C, cross-compiled to aarch64) ├── Makefile # Build rules ├── build_and_deploy.sh # One-shot build + package script ├── stress_sched.sh # Scheduler stress test for benchmarking └── README.md
A lightweight CPU utilization monitor built with eBPF + libbpf.
It hooks into the kernel scheduler (sched_switch tracepoint) to measure per-CPU
active time with nanosecond precision — more accurate than polling-based tools like
top, and with lower overhead at high process counts.
i.MX Processors
記事全体を表示