WSL2内核5.15.133下eBPF kprobe挂载失败,求kmalloc统计替代方案
替代kprobe统计kmalloc调用次数的方案(WSL2 5.15.133内核)
方案选择:内核tracepoint(kmem:kmalloc)
WSL2部分内核可能限制了kprobe的使用,但通常会保留tracepoint支持。内核从2.6.x版本开始就引入了kmem:kmalloc tracepoint,每次调用kmalloc时都会触发,正好满足计数需求。
修改后的代码实现
// kmalloc_counter_tracepoint.c #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <errno.h> #include <asm/unistd.h> #include <linux/bpf.h> #include <inttypes.h> #include <linux/version.h> #include <sys/ioctl.h> #include <linux/perf_event.h> #include <linux/hw_breakpoint.h> #include "bpf_insn.h" #ifndef __NR_bpf # define __NR_bpf 321 #endif static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size) { return syscall(__NR_bpf, cmd, attr, size); } static int create_map() { union bpf_attr attr = { .map_type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(int), .value_size = sizeof(long long), .max_entries = 1, }; return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); } static int load_prog(int map_fd) { // Tracepoint程序逻辑:直接对map中的计数做原子自增 struct bpf_insn prog[] = { BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0), // 栈上存key=0 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), // r2 = &key BPF_LD_MAP_FD(BPF_REG_1, map_fd), // r1 = map_fd BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), // 找不到map元素则退出 BPF_MOV64_IMM(BPF_REG_1, 1), // r1 = 1 BPF_RAW_INSN(BPF_STX|BPF_XADD|BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), // 原子自增计数 BPF_EXIT_INSN(), // 退出程序 }; union bpf_attr attr = { .prog_type = BPF_PROG_TYPE_TRACEPOINT, // 改为tracepoint类型 .insns = (__u64)prog, .insn_cnt = sizeof(prog)/sizeof(prog[0]), .license = (__u64)"GPL", .kern_version = LINUX_VERSION_CODE, }; return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); } // 获取tracepoint的ID static int get_tracepoint_id(const char *category, const char *name) { char path[256]; snprintf(path, sizeof(path), "/sys/kernel/debug/tracing/events/%s/%s/id", category, name); int fd = open(path, O_RDONLY); if (fd < 0) return -1; char buf[16]; int ret = read(fd, buf, sizeof(buf)); close(fd); if (ret <= 0) return -1; return atoi(buf); } // 挂载tracepoint程序 static int attach_tracepoint(int prog_fd, int tp_id) { struct perf_event_attr attr = { .type = PERF_TYPE_TRACEPOINT, .size = sizeof(attr), .config = tp_id, // 指定tracepoint ID .sample_period = 1, .sample_type = PERF_SAMPLE_RAW, .disabled = 1, // 先禁用,设置BPF后再启用 }; // 打开perf事件,绑定到所有CPU int pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC); if (pfd < 0) return -1; // 关联BPF程序 if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { close(pfd); return -1; } // 启用perf事件 if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { close(pfd); return -1; } return pfd; } int main() { int map_fd, prog_fd, pfd; int key = 0; long long value = 0; if ((map_fd = create_map()) < 0) { perror("map create failed"); return 1; } // 初始化map计数为0 union bpf_attr update_attr = { .map_fd = map_fd, .key = (__u64)&key, .value = (__u64)&value, .flags = BPF_ANY, }; sys_bpf(BPF_MAP_UPDATE_ELEM, &update_attr, sizeof(update_attr)); if ((prog_fd = load_prog(map_fd)) < 0) { perror("prog load failed"); return 1; } // 获取kmem:kmalloc的tracepoint ID int tp_id = get_tracepoint_id("kmem", "kmalloc"); if (tp_id < 0) { perror("get tracepoint id failed"); return 1; } if ((pfd = attach_tracepoint(prog_fd, tp_id)) < 0) { perror("tracepoint attach failed"); return 1; } printf("Monitoring kmalloc calls via tracepoint...\n"); while (1) { union bpf_attr lookup_attr = { .map_fd = map_fd, .key = (__u64)&key, .value = (__u64)&value, }; if (sys_bpf(BPF_MAP_LOOKUP_ELEM, &lookup_attr, sizeof(lookup_attr)) == 0) { printf("kmalloc calls: %lld\n", value); } sleep(1); } close(pfd); close(prog_fd); close(map_fd); return 0; }
关键修改说明
- BPF程序类型变更:将
BPF_PROG_TYPE_KPROBE改为BPF_PROG_TYPE_TRACEPOINT,适配tracepoint的执行环境。 - 简化BPF逻辑:tracepoint不需要处理原函数的返回值,直接完成计数自增即可。
- Tracepoint绑定逻辑:
- 通过读取
/sys/kernel/debug/tracing/events/kmem/kmalloc/id获取tracepoint的唯一ID - 使用
perf_event_open打开对应tracepoint的perf事件,关联BPF程序后启用
- 通过读取
- 移除kprobe相关操作:不再操作
kprobe_events文件,完全避开kprobe的限制
编译与运行
确保系统安装了libbpf依赖(或直接使用系统自带的bpf头文件),编译命令示例:
gcc kmalloc_counter_tracepoint.c -o kmalloc_counter -lbpf
运行时需要root权限:
sudo ./kmalloc_counter
内容的提问来源于stack exchange,提问作者wangt13
相关产品推荐
相关产品推荐

