eBPF性能分析实战

发布时间:2026/5/24 20:53:26

eBPF性能分析实战 eBPF性能分析实战一、eBPF概述eBPFextended Berkeley Packet Filter是Linux内核的一项革命性技术支持在运行时动态加载程序。1.1 eBPF架构┌─────────────────────────────────────────────────────────────┐ │ 用户空间 │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ bpftool │ │ BCC/CLang │ │ 用户程序 │ │ │ │ 工具链 │ │ 编译器 │ │ │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ ├─────────┼─────────────────┼─────────────────┼─────────────┤ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ Linux 内核 │ │ │ │ ┌────────────────────────────────────────────┐ │ │ │ │ │ eBPF 虚拟机 │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ │ │ Map │ │ Program│ │ Verifier│ │ │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ └────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ tracepoint kprobe kretprobe │ │ │ │ perf_event uprobe raw_tracepoint │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘1.2 eBPF程序类型类型说明用途kprobe内核函数入口探测监控内核函数调用kretprobe内核函数返回探测获取函数返回值uprobe用户函数入口探测监控用户态程序tracepoint静态追踪点稳定的内核追踪perf_event性能事件采样分析二、环境准备2.1 检查内核版本# 检查内核版本需要4.15 uname -r # 检查eBPF支持 cat /proc/sys/kernel/bpf_enabled # 安装依赖 apt-get install -y bpfcc-tools linux-tools-common linux-tools-$(uname -r)2.2 安装BCC工具# Ubuntu/Debian apt-get install -y bcc bcc-tools libbcc-examples # CentOS/RHEL yum install -y bcc bcc-tools # 验证安装 bpftrace -e BEGIN { printf(eBPF works!\n); exit(); }三、eBPF程序开发3.1 使用BCC编写eBPF程序from bcc import BPF # 定义eBPF程序 bpf_text #include uapi/linux/ptrace.h BPF_HASH(counter); TRACEPOINT_PROBE(syscalls, sys_enter_openat) { u64 pid bpf_get_current_pid_tgid() 32; counter.increment(pid); return 0; } # 加载并运行 b BPF(textbpf_text) # 打印结果 while True: try: for k, v in b[counter].items(): print(fPID {k.value}: {v.value} openat calls) b[counter].clear() sleep(1) except KeyboardInterrupt: exit()3.2 使用bpftrace编写脚本#!/usr/bin/env bpftrace # 追踪execve系统调用 tracepoint:syscalls:sys_enter_execve { printf(PID %d called execve: %s\n, pid, str(args-filename)); } # 统计进程系统调用次数 profile:hz:99 { [comm] count(); } END { print(); }3.3 C语言编写eBPF程序#include vmlinux.h #include bpf/bpf_helpers.h struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, u64); __type(value, u64); } counter SEC(.maps); SEC(tracepoint/syscalls/sys_enter_openat) int trace_openat(struct trace_event_raw_sys_enter *ctx) { u64 pid bpf_get_current_pid_tgid() 32; u64 *count bpf_map_lookup_elem(counter, pid); if (count) { (*count); } else { u64 init 1; bpf_map_update_elem(counter, pid, init, BPF_ANY); } return 0; } char _license[] SEC(license) GPL;四、性能分析实战4.1 CPU分析# 查看CPU使用率最高的进程 bpftrace -e profile:hz:99 { [comm, pid] count(); } END { sort(); } # 分析内核函数调用 bpftrace -e kprobe:do_sys_open { [func] count(); } # 分析用户态函数 bpftrace -e uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc { [malloc calls] count(); }4.2 内存分析# 追踪内存分配 bpftrace -e kprobe:kmalloc { [size] count(); } # 分析内存泄漏 bpftrace -e kprobe:kmalloc { alloc[pid, comm] sum(args-size); } kretprobe:kfree { free[pid, comm] sum(args-size); } # 查看slab分配 bpftrace -e tracepoint:slab_alloc { [name] count(); }4.3 网络分析# 追踪TCP连接 bpftrace -e tracepoint:tcp:tcp_connect { printf(Connect: %s:%d - %s:%d\n, args-saddr, args-sport, args-daddr, args-dport); } # 分析数据包 bpftrace -e tracepoint:net:netif_receive_skb { [args-name] count(); } # 查看网络延迟 bpftrace -e kprobe:tcp_sendmsg { start[pid] nsecs; } kretprobe:tcp_sendmsg { latency hist(nsecs - start[pid]); delete(start[pid]); }4.4 文件系统分析# 追踪文件打开 bpftrace -e tracepoint:syscalls:sys_enter_openat { [str(args-filename)] count(); } # 分析磁盘I/O bpftrace -e tracepoint:block:block_rq_issue { [rwbs] count(); } # 查看文件读写大小分布 bpftrace -e tracepoint:syscalls:sys_enter_read { read hist(args-count); } tracepoint:syscalls:sys_enter_write { write hist(args-count); }五、高级应用5.1 动态追踪应用启动from bcc import BPF import time bpf_text #include uapi/linux/ptrace.h BPF_PERF_OUTPUT(events); struct event { u32 pid; char comm[16]; char filename[256]; }; TRACEPOINT_PROBE(syscalls, sys_enter_execve) { struct event e {}; e.pid bpf_get_current_pid_tgid() 32; bpf_get_current_comm(e.comm, sizeof(e.comm)); bpf_probe_read_user_str(e.filename, sizeof(e.filename), (void *)args-filename); events.perf_submit(args, e, sizeof(e)); return 0; } b BPF(textbpf_text) def print_event(cpu, data, size): event b[events].event(data) print(fPID {event.pid} ({event.comm.decode()}): execve({event.filename.decode()})) b[events].open_perf_buffer(print_event) while True: b.perf_buffer_poll()5.2 性能计数器#!/usr/bin/env bpftrace profile:hz:99 { cpu[cpu] count(); pid[pid, comm] count(); } tracepoint:sched:sched_switch { switches count(); } END { printf(CPU distribution:\n); print(cpu); printf(\nTop processes:\n); sort(pid, -1); print(pid, 10); printf(\nContext switches: %d\n, switches); }5.3 错误追踪#!/usr/bin/env bpftrace tracepoint:syscalls:sys_exit_openat /args-ret 0/ { errors[-args-ret] count(); } tracepoint:syscalls:sys_exit_socket /args-ret 0/ { socket_errors[-args-ret] count(); } END { printf(Open errors:\n); print(errors); printf(\nSocket errors:\n); print(socket_errors); }六、工具链对比工具语言适用场景优点缺点bpftrace脚本快速原型简单易用功能有限BCCPython/C复杂分析功能强大学习曲线libbpfC生产环境高性能开发复杂七、最佳实践7.1 性能优化建议减少数据收集只收集必要的数据使用哈希表合理使用BPF_MAP_TYPE_HASH批量输出减少用户态和内核态交互避免复杂计算在用户态处理复杂逻辑7.2 安全注意事项权限控制eBPF需要CAP_BPF权限程序验证内核会验证eBPF程序安全性资源限制设置合理的内存和指令限制7.3 调试技巧# 查看eBPF程序 bpftool prog list # 查看eBPF map bpftool map list # 查看tracepoint cat /sys/kernel/debug/tracing/available_events # 查看kprobe可用函数 cat /proc/kallsyms | head -20八、总结eBPF是性能分析的利器无侵入式无需修改内核或应用代码高性能在内核态执行低开销灵活强大支持多种追踪方式安全可靠内核验证确保安全通过eBPF可以深入了解系统内部运行机制定位性能瓶颈。

相关新闻