Tracing Scripts
Writing Performance Tracing Scripts¶
Performance tracing with bpftrace involves crafting scripts that leverage eBPF programs to collect low-overhead metrics from the Linux kernel. This section demonstrates how to write scripts for measuring latency, CPU usage, and I/O operations using bpftrace. These examples focus on standard tracepoints and kprobes, ensuring compatibility across modern Linux kernels.
Latency Measurement¶
To measure latency for a specific function or system call, use kprobe (for entry) and kretprobe (for return). For example, trace the latency of the sys_read system call:
bpftrace -e '
kprobe:sys_read { $start = ns; }
kretprobe:sys_read {
printf("Read latency: %d ns\n", ns - $start);
trace("Read latency: %d ns", ns - $start);
}
'
Explanation:
- kprobe:sys_read captures the start time of the sys_read call.
- kretprobe:sys_read calculates the duration between entry and return.
- printf outputs the latency in nanoseconds.
For user-space functions, replace sys_read with the appropriate function name (e.g., do_read).
CPU Usage Tracking¶
Track CPU usage by aggregating time spent on each CPU core using the cpu:cpu_clock tracepoint. Note that cpu:cpu_clock returns clock cycles, not time units. To convert cycles to nanoseconds, use the CPU frequency (e.g., from /proc/cpuinfo or sysfs):
bpftrace -e '
BEGIN {
$cpu_freq = 3500000000; // Example: 3.5 GHz
timer:sleep(1s, @print_cpu_usage);
}
tracepoint:cpu:cpu_clock {
@cpu_time[args->cpu] += args->clock;
}
@print_cpu_usage {
printf("CPU %d time: %d cycles (%.2f ns)\n",
args->cpu, @cpu_time[args->cpu],
@cpu_time[args->cpu] * 1e9 / $cpu_freq);
timer:sleep(1s, @print_cpu_usage);
}
'
Explanation:
- cpu:cpu_clock provides the CPU clock cycles for each core.
- The script uses a hash map @cpu_time to track per-CPU cycles.
- To convert cycles to nanoseconds, use the formula:
nanoseconds = (cycles * 1e9) / cpu_frequency.
Example: A 3.5 GHz CPU has 1 cycle = 0.2857 ns.
For process-level CPU usage, use kprobe/kretprobe with pid tracking instead of relying on cpu:cpu_clock:
bpftrace -e '
kprobe:sys_read { $start[pid] = ns; }
kretprobe:sys_read {
printf("PID %d Read latency: %d ns\n", pid, ns - $start[pid]);
}
'
I/O Operation Monitoring¶
Monitor I/O operations by tracing relevant syscalls like read and write. Add metrics for bytes transferred and file descriptors:
bpftrace -e '
tracepoint:syscall:sys_enter_read {
$reads[pid]++;
$read_bytes[pid] += args->count;
printf("Read: %d bytes (fd: %d)\n", $read_bytes[pid], args->fd);
}
tracepoint:syscall:sys_enter_write {
$writes[pid]++;
$write_bytes[pid] += args->count;
printf("Write: %d bytes (fd: %d)\n", $write_bytes[pid], args->fd);
}
'
Explanation:
- sys_enter_read and sys_enter_write capture I/O syscall entries.
- The script counts and prints the number of read/write operations per process, along with bytes transferred and file descriptors.
For detailed timing, combine with latency tracking:
bpftrace -e '
kprobe:sys_read { $start = ns; }
kretprobe:sys_read {
printf("Read latency: %d ns\n", ns - $start);
}
'
Key takeaways¶
- Use
kprobe/kretprobefor precise latency measurements. - Track CPU usage with
cpu:cpu_clock(converted to nanoseconds) orsched:sched_switch. - Monitor I/O operations via syscall tracepoints like
sys_enter_read, including bytes and file descriptors. - Always run scripts with
sudofor kernel tracing privileges. - Leverage
printfand aggregation for actionable metrics.