You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改说明

  1. BPF程序类型变更:将BPF_PROG_TYPE_KPROBE改为BPF_PROG_TYPE_TRACEPOINT,适配tracepoint的执行环境。
  2. 简化BPF逻辑:tracepoint不需要处理原函数的返回值,直接完成计数自增即可。
  3. Tracepoint绑定逻辑:
    • 通过读取/sys/kernel/debug/tracing/events/kmem/kmalloc/id获取tracepoint的唯一ID
    • 使用perf_event_open打开对应tracepoint的perf事件,关联BPF程序后启用
  4. 移除kprobe相关操作:不再操作kprobe_events文件,完全避开kprobe的限制

编译与运行

确保系统安装了libbpf依赖(或直接使用系统自带的bpf头文件),编译命令示例:

gcc kmalloc_counter_tracepoint.c -o kmalloc_counter -lbpf

运行时需要root权限:

sudo ./kmalloc_counter

内容的提问来源于stack exchange,提问作者wangt13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:28:10