## eBPF 入门实践教程:编写 eBPF 程序 profile 进行性能分析 ### 背景 `profile` 是一款用户追踪程序执行调用流程的工具,类似于perf中的 -g 指令。但是相较于perf而言, `profile`的功能更为细化,它可以选择用户需要追踪的层面,比如在用户态层面进行追踪,或是在内核态进行追踪。 ### 实现原理 `profile` 的实现依赖于linux中的perf_event。在注入ebpf程序前,`profile` 工具会先将 perf_event 注册好。 ```c static int open_and_attach_perf_event(int freq, struct bpf_program *prog, struct bpf_link *links[]) { struct perf_event_attr attr = { .type = PERF_TYPE_SOFTWARE, .freq = env.freq, .sample_freq = env.sample_freq, .config = PERF_COUNT_SW_CPU_CLOCK, }; int i, fd; for (i = 0; i < nr_cpus; i++) { if (env.cpu != -1 && env.cpu != i) continue; fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0); if (fd < 0) { /* Ignore CPU that is offline */ if (errno == ENODEV) continue; fprintf(stderr, "failed to init perf sampling: %s\n", strerror(errno)); return -1; } links[i] = bpf_program__attach_perf_event(prog, fd); if (!links[i]) { fprintf(stderr, "failed to attach perf event on cpu: " "%d\n", i); links[i] = NULL; close(fd); return -1; } } return 0; } ``` 其ebpf程序实现逻辑是对程序的堆栈进行定时采样,从而捕获程序的执行流程。 ```c SEC("perf_event") int do_perf_event(struct bpf_perf_event_data *ctx) { __u64 id = bpf_get_current_pid_tgid(); __u32 pid = id >> 32; __u32 tid = id; __u64 *valp; static const __u64 zero; struct key_t key = {}; if (!include_idle && tid == 0) return 0; if (targ_pid != -1 && targ_pid != pid) return 0; if (targ_tid != -1 && targ_tid != tid) return 0; key.pid = pid; bpf_get_current_comm(&key.name, sizeof(key.name)); if (user_stacks_only) key.kern_stack_id = -1; else key.kern_stack_id = bpf_get_stackid(&ctx->regs, &stackmap, 0); if (kernel_stacks_only) key.user_stack_id = -1; else key.user_stack_id = bpf_get_stackid(&ctx->regs, &stackmap, BPF_F_USER_STACK); if (key.kern_stack_id >= 0) { // populate extras to fix the kernel stack __u64 ip = PT_REGS_IP(&ctx->regs); if (is_kernel_addr(ip)) { key.kernel_ip = ip; } } valp = bpf_map_lookup_or_try_init(&counts, &key, &zero); if (valp) __sync_fetch_and_add(valp, 1); return 0; } ``` 通过这种方式,它可以根据用户指令,简单的决定追踪用户态层面的执行流程或是内核态层面的执行流程。 ### Eunomia中使用方式 ### 总结 `profile` 实现了对程序执行流程的分析,在debug等操作中可以极大的帮助开发者提高效率。