在Linux系统中,perf是一个强大的性能分析工具,它允许开发者深入测量和分析内核及用户空间程序的运行状况,并获取调用堆栈信息。通过利用硬件计数器和软件事件,perf能够高效收集性能数据。更多关于Linux系统运维和性能调优的知识,可以在云栈社区的运维/DevOps板块找到。
eBPF程序通过与Linux的perf子系统结合,能以极低的开销对系统、内核或应用程序进行采样分析。这为实时性能监控和问题诊断提供了强大支持。eBPF技术在现代云原生架构中应用广泛,相关实践可参考云栈社区的云原生/IaaS板块。
以下是一个基于libbpf-bootstrap示例的eBPF程序代码,展示了如何实现profile功能:
SEC("perf_event")
int profile(void *ctx)
{
int pid = bpf_get_current_pid_tgid() >> 32;
int cpu_id = bpf_get_smp_processor_id();
struct stacktrace_event *event;
int cp;
event = bpf_ringbuf_reserve(&events, sizeof(*event), 0);
if (!event)
return 1;
event->pid = pid;
event->cpu_id = cpu_id;
if (bpf_get_current_comm(event->comm, sizeof(event->comm)))
event->comm[0] = 0;
event->kstack_sz = bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0);
event->ustack_sz = bpf_get_stack(ctx, event->ustack, sizeof(event->ustack), BPF_F_USER_STACK);
bpf_ringbuf_submit(event, 0);
return 0;
}
SEC("perf_event")是libbpf加载器的一个“标签”或“指令”,它标识这是一个BPF_PROG_TYPE_PERF_EVENT类型的程序,并将作为回调函数挂载到Linux内核的Perf Event子系统上。需要注意的是,SEC("perf_event")本身并不指定具体监控的事件类型(如CPU周期或缓存未命中),事件配置由用户态代码完成。
在以上代码中,bpf_get_stack函数用于将当前的调用栈直接拷贝到event->kstack数组中,支持内核栈和用户栈的捕获。
用户态代码负责配置和挂载事件。以下是一个关键的用户态代码片段:
memset(&attr, 0, sizeof(attr));
// 如果有 -s 参数,使用软件事件 (PERF_TYPE_SOFTWARE)
// 否则默认使用硬件事件 (PERF_TYPE_HARDWARE)
attr.type = sw_event ? PERF_TYPE_SOFTWARE : PERF_TYPE_HARDWARE;
// 具体的事件配置:
// 软件事件 -> CPU_CLOCK (基于时间的采样,不依赖硬件计数器)
// 硬件事件 -> CPU_CYCLES (基于 CPU 周期的采样,更精确但虚拟化环境可能不支持)
attr.config = sw_event ? PERF_COUNT_SW_CPU_CLOCK : PERF_COUNT_HW_CPU_CYCLES;
attr.sample_freq = freq; // 设置采样频率,比如 99Hz
attr.freq = 1; // 告诉内核 attr.sample_freq 是频率而不是周期
挂载过程针对每个在线CPU进行,以确保全系统监控:
for (cpu = 0; cpu < num_cpus; cpu++) {
// 1. 跳过离线 CPU
if (cpu >= num_online_cpus || !online_mask[cpu])
continue;
/* Set up performance monitoring on a CPU/Core */
// 2. 关键系统调用:perf_event_open
// pid = -1: 监控该 CPU 上运行的所有进程
// cpu = cpu: 指定监控第几个 CPU 核
pefd = perf_event_open(&attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC);
// ... 错误处理 ...
/* Attach a BPF program on a CPU */
// 3. 将 BPF 程序挂载到这个 perf event 文件描述符 (pefd) 上
links[cpu] = bpf_program__attach_perf_event(skel->progs.profile, pefd);
}
这种设计使得perf_event类型的eBPF程序能够实现对系统中所有在线CPU的全面监控,为性能分析和优化提供精细数据。