【问题标题】:How to get cgroup path of task in an eBPF program?如何在 eBPF 程序中获取任务的 cgroup 路径?
【发布时间】:2020-09-23 02:24:04
【问题描述】:

我一直在尝试使用 Brendan Gregg 的 tcptop BCC 工具来了解有关 eBPF 程序如何工作的更多信息。我正在尝试让它打印任务的 CGROUP 路径。

凭借我对 Linux 系统编程的生疏知识,我认为我可以使用来自 linux/cgroup.h 的函数,尤其是 task_cgroup_path() looked promising,因为我可以将当​​前的 task_struct *(从 bpf_get_current_task() 获得)传递给它。我正在使用带有4.19.59 内核的CentOS7 机器。

但是,当我尝试执行修改后的 tcptop 时,验证程序失败并显示 last insn is not an exit or jmp 错误消息。我试图理解为什么会这样。

这是修改后的tcptop 的差异:patch

这里是验证者output

【问题讨论】:

  • 在你打补丁之前,tcptop 是否首先在你的系统上工作?
  • 如果未打补丁的 tcptop 可以正常工作,您是否尝试将您的更改一一引入,看看验证者抱怨的是哪一个?
  • @Qeole 是的,密件抄送工具工作得很好。 @pchaigno 是的,如果我删除对 task_cgroup_path() 的调用,它就可以正常工作。

标签: linux-kernel cgroups ebpf bcc-bpf


【解决方案1】:

我想我可以使用linux/cgroup.h中的函数

不,你不能。

可以从 eBPF 程序调用的唯一函数是:

  • 在您的 eBPF 代码中定义的其他函数(它们可以内联,但不再需要,eBPF 支持函数调用),
  • eBPF 助手,它们是专门暴露给 eBPF 程序的内核函数(请参阅 their documentation in the sourceas a man page),
  • 编译器内置,例如__builtin_memcpy()

其他内核函数,即使符号被导出并暴露给内核模块,或者标准库中的用户函数,也不能从 eBPF 程序调用(它们将是可跟踪 eBPF,但这是不同的)。

关于您的用例,我不确定从其 id 获取 cgroup 路径的最佳方法是什么。您可以在用户空间中执行此操作,尽管我不知道如何从 id 获取路径。我知道反过来是可能的(从路径获取 id,这个 can be donename_to_handle_at() 系统调用),所以最坏的情况是你可以迭代所有现有路径并获取 id。如果你有一个强大的用例并且需要一段时间,一个长期的解决方案是提交一个新的 eBPF 助手,它会调用task_cgroup_path()

[编辑] 关于验证器返回的错误信息(last insn is not an exit or jmp):BPF 程序支持函数调用,因此您的程序中可以有多个函数(“子程序”)。这些子程序中的每一个都是指令列表,可能包含向前或向后(在某些条件下)跳转。从验证者的角度来看,程序的执行必须exit 或无条件向后跳转指令(子程序的“返回”)结束,即to avoid fall-through from one subprog into another。在函数结束时不做任何特定的事情(从整个程序返回或退出)是没有意义的,而且会很危险(一旦 JIT-ed,程序在内存中不一定是连续的,所以你不会失败安全,即使这是有道理的)。

现在,如果我们看一下 clang 是如何编译您的程序的,我们会看到这是对 task_cgroup_path() 的调用:

; task_cgroup_path(t, (char *) &cgpath, sizeof(cgpath)); // Line  50
  20:   bf 01 00 00 00 00 00 00     r1 = r0
  21:   b7 03 00 00 10 00 00 00     r3 = 16
  22:   85 10 00 00 ff ff ff ff     call -1

call -1 是对 BPF 函数的调用(不是内核帮助程序,而是预期在您的程序中找到的函数,您可以通过将源寄存器设置为 1 (BPF_PSEUDO_CALL) 在第二个指令的字节)。所以验证者认为这次跳转的目标是一个子程序。除了因为task_cgroup_path() 不是BPF 子程序,clang 找不到它来设置相关偏移,而是使用-1,将call -1 标记为该子程序的第一条指令。但是call -1 之前的最后一条指令既不是exit 也不是jump,因此验证者最终会发现程序有问题,并拒绝它。所有这些逻辑都发生在函数check_subprogs()中。

【讨论】:

  • 感谢您的指导。我也有兴趣知道为什么验证者会抛出last insn is not an exit or jmp。根据评论来自github.com/torvalds/linux/commit/…the last insn of the subprog should be either exit or unconditional jump back。我试图理解这意味着什么。
  • 非常感谢您的详细解释!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-25
  • 2011-01-14
  • 2011-05-16
  • 2014-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多