Linux eBPF for WordPress Performance Tracing: bpftrace and BCC

eBPF (extended Berkeley Packet Filter) lets you attach lightweight programs to kernel and userspace tracepoints with zero application code changes. For WordPress performance work, bpftrace and the BCC toolkit can trace every PHP function call, MySQL query, or file I/O event in real time — far more granular than APM tools and without the overhead of Xdebug.

Problem: A WordPress server shows occasional CPU spikes, high page load times, and slow database queries — but standard profilers only show application-level metrics and cannot attribute latency to specific kernel events or system calls.

Solution: Use eBPF-based tools to trace the system at the kernel level without modifying application code. Run bpftrace -e 'tracepoint:syscalls:sys_enter_read { @[comm] = count(); }' to see which processes are making the most read syscalls. Use bpftrace scripts or BCC tools like biolatency and tcplife to trace disk I/O and TCP connection lifecycles in real time.


The one-liners and scripts below trace PHP function latency, count MySQL queries per second, profile Nginx connection handling, and measure WordPress page-generation time from the kernel level.


# Prerequisites: Linux 5.8+, bpftrace, BCC tools
apt install bpftrace bpfcc-tools linux-headers-$(uname -r)

# ── 1. Trace all PHP function calls slower than 5ms ──────────────────────
bpftrace -e '
usdt:/usr/bin/php8.2:php:function__entry {
    @start[tid] = nsecs;
    @fname[tid]  = str(arg0);
}
usdt:/usr/bin/php8.2:php:function__return /nsecs - @start[tid] > 5000000/ {
    printf("SLOW: %s  %.2f ms\n", @fname[tid], (nsecs - @start[tid]) / 1e6);
    delete(@start[tid]); delete(@fname[tid]);
}'

# ── 2. Count MySQL queries per second (via tcp sendmsg to port 3306) ──────
bpftrace -e '
kprobe:tcp_sendmsg /((struct sock *)arg0)->__sk_common.skc_dport == 3306/ {
    @queries = count();
}
interval:s:1 {
    print(@queries); clear(@queries);
}'

# ── 3. Histogram of PHP page-generation time (entry to exit of main script) 
bpftrace -e '
usdt:/usr/bin/php8.2:php:request__startup  { @start[tid] = nsecs; }
usdt:/usr/bin/php8.2:php:request__shutdown /@start[tid]/ {
    @ms = hist( (nsecs - @start[tid]) / 1000000 );
    delete(@start[tid]);
}'

# ── 4. Trace file opens in /var/www (find which files WordPress reads most)
bpftrace -e '
tracepoint:syscalls:sys_enter_openat
/str(args->filename) != "" && strncmp("/var/www", str(args->filename), 8) == 0/ {
    @files[str(args->filename)] = count();
}
END { print(@files, 20); }'

# ── 5. MySQL slow-query tracing with BCC (execsnoop-style for mysqld)
/usr/share/bcc/tools/funclatency -u -p $(pgrep mysqld) \
    'mysqld:*dispatch_command*' 2>/dev/null | head -30


NOTE: PHP USDT probes (usdt:...php:function__entry) require PHP compiled with --enable-dtrace — most distribution packages do not include this; compile PHP from source with --enable-dtrace on your tracing host, or use the php-probe BCC tool which uses uprobes and does not require DTRACE-enabled PHP binaries.