记一次报错:prog failed to find kernel BTF type ID of -3 && 动态挂载fentry和fexit

在eBPF中,fentry和fexit通常用于监控和追踪内核函数的入口和出口。fentryfexit,类似于kprobekretprobe但是fentry和fexit的性能要比kprobe及kretprobe要好。

示例程序(有问题版)

bpf.c

...
    
static int entry(void *ctx){
	// is your code
}

SEC("fentry/dummy_fentry")
int BPF_PROG(dummy_fentry)
{
	entry(ctx);
	return 0;
}

static int exit(void *ctx){
	// is your code
}

SEC("fexit/dummy_fexit")
int BPF_PROG(dummy_fexit)
{
	exit(ctx);
	return 0;
}

用户态.c

static bool try_fentry(struct probe_bpf *skel, std::string func)
{
    long err;
	// 检查是否可以使用 fentry 附加到指定的内核函数上
    if (!fentry_can_attach(func.c_str(), NULL))
    {
        return false;
    }
     // 尝试将 dummy_fentry 程序附加到指定的内核函数func
    err = bpf_program__set_attach_target(skel->progs.dummy_fentry, 0, func.c_str());
    if (err)
    {
        // 如果附加失败,禁用 dummy_fentry 和 dummy_fexit 程序的自动加载
        bpf_program__set_autoload(skel->progs.dummy_fentry, false);
        bpf_program__set_autoload(skel->progs.dummy_fexit, false);
        return false;
    }
    
    // 尝试将 dummy_fexit 程序附加到指定的内核函数退出点
    err = bpf_program__set_attach_target(skel->progs.dummy_fexit, 0, func.c_str());
    if (err)
    {
        // 如果附加失败,禁用 dummy_fentry 和 dummy_fexit 程序的自动加载
        bpf_program__set_autoload(skel->progs.dummy_fentry, false);
        bpf_program__set_autoload(skel->progs.dummy_fexit, false);

        return false;
    }
	// 如果 fentry 和 fexit 成功附加,禁用 dummy_kprobe 和 dummy_kretprobe 程序的自动加载
    bpf_program__set_autoload(skel->progs.dummy_kprobe, false);
    bpf_program__set_autoload(skel->progs.dummy_kretprobe, false);
    return true;
}

static int attach_kprobes(struct probe_bpf *skel, std::string func)
{
    skel->links.dummy_kprobe =
        bpf_program__attach_kprobe(skel->progs.dummy_kprobe, false, func.c_str());
    CHECK_ERR(!skel->links.dummy_kprobe, "Fail to attach kprobe");
    skel->links.dummy_kretprobe =
        bpf_program__attach_kprobe(skel->progs.dummy_kretprobe, true, func.c_str());
    CHECK_ERR(!skel->links.dummy_kretprobe, "Fail to attach ketprobe");
    return 0;
}


//load 前调用
bool tryf = try_fentry(skel, str);

//attach 部分调用
if (!tryf)
{
    err = attach_kprobes(skel, func);
    return 0;
}

运行结果

make编译之后,运行,效果如下:

在这里插入图片描述

动态挂载fentry及fexit

下面主要说一下如何动态挂载fentryfexit在梳理一遍我上面的用户态代码流程,发现是我只在加载前设置了bpf程序要加载到哪个函数,但是没attach。如下代码,我在attach部分,先判断可以用fentryfexit指定bpf程序到对应的内核函数上吗,如果不可以则用attach kprobekretprobe。但如果可以呢?我没有attach

//load 前调用
bool tryf = try_fentry(skel, str);

//attach 部分调用
if (!tryf)
{
    err = attach_kprobes(skel, func);
    return 0;
}

用到的API

1.bpf_program__set_attach_target

bpf_program__set_attach_target 是一个用于设置 BPF 程序附加目标的函数。它通常用于附加 eBPF 程序到特定的内核函数上,特别是在使用 fentryfexit 类型的 BPF 程序时。

注意:该函数设置附加目标时必须在bpf程序load之前。

函数原型:bpf_program__set_attach_target(struct  bpf_program *prog,int attach_prog_fd, const char *attach_func_name)

函数参数详解:
- struct  bpf_program *prog:这是一个指向 BPF 程序对象的指针,该参数指定了要附加到目标函数的 BPF 程序
- int attach_prog_fd:该文件描述符指定了要附加的目标程序或上下文
- const char *attach_func_name:该字符串表示 BPF 程序要附加的内核函数名

2.bpf_program__attach

bpf_program__attach 是一个用于将 BPF 程序附加到特定钩子(如内核函数、网络数据路径等)的函数。

函数原型:struct bpf_link *bpf_program__attach(const struct bpf_program *prog)

参数详解

- const struct bpf_program *prog:这是一个指向 BPF 程序对象的指针,指定了要附加的 BPF 程序

示例程序(正确版)

bpf.c

...
    
static int entry(void *ctx){
	// is your code
}

SEC("fentry/dummy_fentry")
int BPF_PROG(dummy_fentry)
{
	entry(ctx);
	return 0;
}

static int exit(void *ctx){
	// is your code
}

SEC("fexit/dummy_fexit")
int BPF_PROG(dummy_fexit)
{
	exit(ctx);
	return 0;
}

用户态.c

static bool try_fentry(struct probe_bpf *skel, std::string func)
{
    long err;
	// 检查是否可以使用 fentry 附加到指定的内核函数上
    if (!fentry_can_attach(func.c_str(), NULL))
    {
        return false;
    }
     // 尝试将 dummy_fentry 程序附加到指定的内核函数func
    err = bpf_program__set_attach_target(skel->progs.dummy_fentry, 0, func.c_str());
    if (err)
    {
        // 如果附加失败,禁用 dummy_fentry 和 dummy_fexit 程序的自动加载
        bpf_program__set_autoload(skel->progs.dummy_fentry, false);
        bpf_program__set_autoload(skel->progs.dummy_fexit, false);
        return false;
    }
    
    // 尝试将 dummy_fexit 程序附加到指定的内核函数退出点
    err = bpf_program__set_attach_target(skel->progs.dummy_fexit, 0, func.c_str());
    if (err)
    {
        // 如果附加失败,禁用 dummy_fentry 和 dummy_fexit 程序的自动加载
        bpf_program__set_autoload(skel->progs.dummy_fentry, false);
        bpf_program__set_autoload(skel->progs.dummy_fexit, false);

        return false;
    }
	// 如果 fentry 和 fexit 成功附加,禁用 dummy_kprobe 和 dummy_kretprobe 程序的自动加载
    bpf_program__set_autoload(skel->progs.dummy_kprobe, false);
    bpf_program__set_autoload(skel->progs.dummy_kretprobe, false);
    return true;
}

static int attach_kprobes(struct probe_bpf *skel, std::string func)
{
    skel->links.dummy_kprobe =
        bpf_program__attach_kprobe(skel->progs.dummy_kprobe, false, func.c_str());
    CHECK_ERR(!skel->links.dummy_kprobe, "Fail to attach kprobe");
    skel->links.dummy_kretprobe =
        bpf_program__attach_kprobe(skel->progs.dummy_kretprobe, true, func.c_str());
    CHECK_ERR(!skel->links.dummy_kretprobe, "Fail to attach ketprobe");
    return 0;
}

static int attach_fentry(struct probe_bpf *skel)
{
    skel->links.dummy_fentry =
        bpf_program__attach(skel->progs.dummy_fentry);
    CHECK_ERR(!skel->links.dummy_fentry, "Fail to attach fentry");
    skel->links.dummy_fexit =
        bpf_program__attach(skel->progs.dummy_fexit);
    CHECK_ERR(!skel->links.dummy_fexit, "Fail to attach fexit");
    return 0;
}

//load 前调用
bool tryf = try_fentry(skel, str);

//attach 部分调用
if (!tryf)
{
    err = attach_kprobes(skel, func);
    return 0;
}
else
{
    err = attach_fentry(skel);
    return 0;
}

函数 try_fentry 试图将一个 eBPF 程序附加到指定的内核函数上,首先尝试使用 fentry (函数入口点) 和 fexit (函数退出点) 方法。如果这些方法失败,则禁用自动加载对应的 eBPF 程序,然后使用kprobe、kretprobe来加载。

运行效果

统计时延:

在这里插入图片描述

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值