Linux kernel 如何在eBPF程序中获取任务的cgroup路径?

Linux kernel 如何在eBPF程序中获取任务的cgroup路径?,linux-kernel,cgroups,ebpf,bcc-bpf,Linux Kernel,Cgroups,Ebpf,Bcc Bpf,我一直在尝试使用Brendan Gregg的BCC工具来了解更多关于eBPF程序如何工作的信息。我试图让它打印任务的CGROUP路径 凭借我对Linux系统编程的渊博知识,我认为我可以使用Linux/cgroup.h中的函数,尤其是task\u cgroup\u path(),因为我可以将当前的task\u struct*(从bpf\u get\u current\u task()传递给它。我使用的是带有4.19.59内核的CentOS7机器 但是,当我尝试执行修改后的tcptop时,验证器失败

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

凭借我对Linux系统编程的渊博知识,我认为我可以使用
Linux/cgroup.h
中的函数,尤其是
task\u cgroup\u path()
,因为我可以将当前的
task\u struct*
(从
bpf\u get\u current\u task()
传递给它。我使用的是带有
4.19.59
内核的CentOS7机器

但是,当我尝试执行修改后的tcptop时,验证器失败,并显示
last insn不是退出或jmp
错误消息。我试图理解为什么会发生这种情况

以下是修改后的
tcptop
的区别:

这是验证器

我想我可以使用
linux/cgroup.h

不,你不能

可从eBPF程序调用的功能只有:

  • eBPF代码中定义的其他函数(它们可以内联,但不再需要,eBPF支持函数调用)
  • eBPF助手,是专门暴露于eBPF程序的内核函数(请参阅或)
  • 编译器内置,例如
    \uuu builtin\u memcpy()
其他内核函数,即使这些符号被导出并公开给内核模块,或者是标准库中的用户函数,也不能从eBPF程序调用(它们可以通过eBPF进行跟踪,但这是不同的)

关于您的用例,我不确定从其id获取cgroup路径的最佳方法是什么。虽然我不知道如何从id中获取路径,但您可以在用户空间中执行此操作。我知道相反的操作是可能的(从路径获取id,使用
name\u to\u handle\u at()。如果您有一个强大的用例并且需要一段时间,一个长期的解决方案是提交一个新的eBPF帮助程序,该帮助程序将调用
task\u cgroup\u path()

[Edit]关于验证器返回的错误消息(
last insn不是出口或jmp
):BPF程序支持函数调用,因此程序中可以有多个函数(“子程序”)。这些子程序中的每一个都是指令列表,可能包含向前或向后(在某些条件下)跳转。从验证器的角度来看,程序的执行必须以
退出
或无条件向后跳转指令(子程序的“返回”)结束,这是
,以避免从一个子程序掉入另一个子程序。在函数结束时不做任何特定的操作(返回或退出整个程序)是没有意义的,而且是危险的(一旦JIT化,程序在内存中不一定是连续的,所以即使这样做有意义,也不能安全地通过)

现在,如果我们看一下clang是如何编译您的程序的,我们可以在调用
task\u cgroup\u path()
时看到这一点:


调用-1
是对BPF函数的调用(不是内核帮助程序,而是一个预期在程序中找到的函数,您可以通过将源寄存器设置为指令第二个字节的1(
BPF_伪_调用
)来判断)。所以验证器认为这个跳转的目标是一个子程序。除了因为
task\u cgroup\u path()
不是BPF子程序之外,clang找不到它来设置相关的偏移量,而是使用
-1
,将
调用-1
标记为该潜在子程序的第一条指令。但是
调用-1
之前的最后一条指令既不是
退出
也不是
跳转
,因此验证器最终发现程序有问题,并拒绝它。所有这些逻辑都发生在功能中。

在您修补系统之前,tcptop是否首先在您的系统上工作?如果未修补的tcptop正常工作,您是否尝试逐一介绍您的更改,以查看验证器抱怨的是哪一个?@Qeole是的,BCC工具工作正常@pchaigno是的,如果我删除对
task\u cgroup\u path()
的调用,它就可以正常工作。谢谢您的指导。我还想知道为什么验证器抛出
last insn不是出口或jmp
。根据注释,
子程序的最后一个insn应该是exit或无条件跳回
。我正在努力理解这意味着什么。非常感谢您的详细解释!
; 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