【问题标题】:Take name of called function in eBPF在 eBPF 中取被调用函数的名称
【发布时间】:2018-11-19 11:43:56
【问题描述】:

我想跟踪特定 PID 的函数并收集一些统计信息(总调用次数、总时间等),但我并不完全清楚如何使用 funcname+my_struct 对创建 BPF_HASH。

有什么方法可以获取BPF程序中被调用函数的名字吗?

我想我应该使用“PT_REGS_IP(ctx)”读取 IP 寄存器,但我不完全理解如何将值转换为人类可读的字符串。

目前 BPF 程序如下所示:

#include <uapi/linux/ptrace.h>
#include <linux/sched.h>

struct data_t {
    u32 pid;
    u64 delta;
    u64 start;
} __attribute__((packed));

BPF_HASH(faddr, u64, struct data_t);
BPF_PERF_OUTPUT(events);

int do_entry(struct pt_regs *ctx) {
    struct data_t *data;
    data->start = bpf_ktime_get_ns();
    u64 ip = PT_REGS_IP(ctx);
    faddr.update(&ip, data);

    return 0;
}

int do_return(struct pt_regs *ctx) {
    struct data_t *data;
    u64 ip = PT_REGS_IP(ctx);
    data = faddr.lookup(&ip);

    if (data->start == 0)
        return 0;       // missed start

    data->delta = bpf_ktime_get_ns() - data->start;
    data->pid = bpf_get_current_pid_tgid();

    events.perf_submit(ctx, &data, sizeof(data));
    faddr.delete(&ip);

    return 0;
}

但在启动时我得到了:

error: <unknown>:0:0: in function do_entry i32 (%struct.pt_regs*): A call to built-in function 'abort' is not supported.

【问题讨论】:

  • 能否请您提供用于编译、加载和附加程序的命令?
  • 抱歉不完整。完整的代码示例可以在我之前的question 中找到,其中有跟踪的 C 程序示例和主 Go 程序示例。 C程序应该用调试符号编译(gcc -g),go程序用默认编译(go build)
  • 最好在每个问题中提供一个最小的、可重现的示例程序,即使这意味着问题之间有一些重复的内容。用户空间代码是运行 BPF 程序所必需的,想要重现的人可能没有阅读您之前的问题。
  • 好的,接下来的问题会做。

标签: trace bpf ebpf


【解决方案1】:

您的do_entry 函数中有错误。您正在尝试取消引用空指针:

struct data_t *data;
data->start = bpf_ktime_get_ns();

以下应该会更好:

int do_entry(struct pt_regs *ctx) {
    struct data_t data = {}; // initializes data with zeros.
    data.start = bpf_ktime_get_ns();
    u64 ip = PT_REGS_IP(ctx);
    faddr.update(&ip, &data);
    return 0;
}

我不明白为什么错误消息提到abort。我去问问。


如何将内存地址翻译成函数名称取决于您使用的用户空间库。如果您使用密件抄送,可以使用ksym 方法。我不知道gobpf中是否有等价物。


do_return 中至少还有一个错误:

data = faddr.lookup(&ip);
if (data->start == 0)
    return 0;       // missed start

在取消引用之前,您需要检查 data 是否为空。否则,验证者将拒绝您的程序。

data = faddr.lookup(&ip);
if (!data || data->start == 0)
    return 0;       // missed start

【讨论】:

  • 我已经替换了指向变量的指针并修复了 do_return() 中的错误,但得到了一些我以前从未见过的奇怪的东西:$ go build timings.go $ sudo ./timings bpf: Failed to load program: Permission denied 0: (bf) r6 = r1 1: (79) r1 = *(u64 *)(r6 +128) 2: (7b) *(u64 *)(r10 -16) = r1 3: (18) r1 = 0xffff8fcad7999800 ... a lot of similar messages here ... 96: (b7) r5 = 8 97: (85) call bpf_perf_event_output#25 invalid indirect read from stack off -8+0 size 8 Failed to load do_return: error loading BPF program: permission denied
  • ameba_events.perf_submit(ctx, &amp;data, sizeof(data));影响的错误
  • 将其更改为 ameba_events.perf_submit(ctx, data, sizeof(data)); 修复它。
  • 无论如何,通过 do_entry 和 do_return 中的 PT_REGS_IP(ctx) 读取,返回不同的值,我不能将这些值用作 BPF_HASH 中的键。也许可以在 BPF 程序中将地址转换为函数名并使用字符串作为哈希键?
  • 我不确定我是否看到了要跟踪多个函数的问题。只要您确定在do_entry 之后立即调用do_return,您就可以从映射中检索IP 值而不会出错。也许我们可以通过聊天(IRC?)或另一个 SO 问题中的示例来讨论这个问题?
猜你喜欢
  • 2023-03-31
  • 2018-08-25
  • 2018-06-25
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-21
  • 2012-12-19
相关资源
最近更新 更多