如何在 eBPF 程序中获取任务的 cgroup 路径?
How to get cgroup path of task in an eBPF program?
我一直在尝试使用 Brendan Gregg 的 tcptop BCC 工具来了解更多关于 eBPF 程序如何工作的信息。我试图让它打印任务的 CGROUP 路径。
凭借我对 Linux 系统编程的生疏知识,我认为我可以使用 linux/cgroup.h
中的函数,尤其是 task_cgroup_path()
looked promising,因为我可以传递当前 [=12] =](从bpf_get_current_task()
获得)。我正在使用带有 4.19.59
内核的 CentOS7 机器。
然而,当我尝试执行修改后的 tcptop 时,验证程序失败并显示 last insn is not an exit or jmp
错误消息。我试图理解为什么会这样。
这是修改后的 tcptop
的差异:patch
这里是验证者output
I thought I could use functions from linux/cgroup.h
不,你不能。
唯一可以从 eBPF 程序调用的函数是:
- 在您的 eBPF 代码中定义的其他函数(它们可以内联,但这不再是必需的,eBPF 支持函数调用),
- eBPF 助手,它们是专门暴露给 eBPF 程序的内核函数(参见 their documentation in the source or as a man page),
- 编译器内置,例如
__builtin_memcpy()
。
其他内核函数,即使符号被导出并暴露给内核模块,或者与此相关的标准库中的用户函数,也不能从 eBPF 程序调用(它们将是 可追踪的 与 eBPF,但这是不同的)。
关于您的用例,我不确定从其 id 获取 cgroup 路径的最佳方法是什么。您可以在用户 space 中执行此操作,但我不知道如何从 ID 获取路径。我知道反过来是可能的(从路径获取 id,这个 can be done 和 name_to_handle_at()
系统调用),所以最坏的情况是你可以迭代所有现有路径并获取 id。如果你有一个强大的用例并且提前一段时间,一个长期的解决方案是提交一个新的 eBPF 助手,它将调用 task_cgroup_path()
.
[编辑]关于验证者return编辑的错误信息(last insn is not an exit or jmp
):BPF程序支持函数调用,所以可以有多个程序中的函数(“子程序”)。这些子程序中的每一个都是指令列表,可能包含向前或向后(在某些条件下)跳转。从验证者的角度来看,程序的执行必须以exit
或无条件向后跳转指令(来自子程序的“return”)结束,这是 to avoid fall-through from one subprog into another
。在函数末尾不执行任何特定操作(return 或退出整个程序)是没有意义的,而且会很危险(一旦进行了 JIT,程序在内存中不一定是连续的,因此您不能'安全地通过,即使这是有道理的)。
现在如果我们看看 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()
.
中
我一直在尝试使用 Brendan Gregg 的 tcptop BCC 工具来了解更多关于 eBPF 程序如何工作的信息。我试图让它打印任务的 CGROUP 路径。
凭借我对 Linux 系统编程的生疏知识,我认为我可以使用 linux/cgroup.h
中的函数,尤其是 task_cgroup_path()
looked promising,因为我可以传递当前 [=12] =](从bpf_get_current_task()
获得)。我正在使用带有 4.19.59
内核的 CentOS7 机器。
然而,当我尝试执行修改后的 tcptop 时,验证程序失败并显示 last insn is not an exit or jmp
错误消息。我试图理解为什么会这样。
这是修改后的 tcptop
的差异:patch
这里是验证者output
I thought I could use functions from
linux/cgroup.h
不,你不能。
唯一可以从 eBPF 程序调用的函数是:
- 在您的 eBPF 代码中定义的其他函数(它们可以内联,但这不再是必需的,eBPF 支持函数调用),
- eBPF 助手,它们是专门暴露给 eBPF 程序的内核函数(参见 their documentation in the source or as a man page),
- 编译器内置,例如
__builtin_memcpy()
。
其他内核函数,即使符号被导出并暴露给内核模块,或者与此相关的标准库中的用户函数,也不能从 eBPF 程序调用(它们将是 可追踪的 与 eBPF,但这是不同的)。
关于您的用例,我不确定从其 id 获取 cgroup 路径的最佳方法是什么。您可以在用户 space 中执行此操作,但我不知道如何从 ID 获取路径。我知道反过来是可能的(从路径获取 id,这个 can be done 和 name_to_handle_at()
系统调用),所以最坏的情况是你可以迭代所有现有路径并获取 id。如果你有一个强大的用例并且提前一段时间,一个长期的解决方案是提交一个新的 eBPF 助手,它将调用 task_cgroup_path()
.
[编辑]关于验证者return编辑的错误信息(last insn is not an exit or jmp
):BPF程序支持函数调用,所以可以有多个程序中的函数(“子程序”)。这些子程序中的每一个都是指令列表,可能包含向前或向后(在某些条件下)跳转。从验证者的角度来看,程序的执行必须以exit
或无条件向后跳转指令(来自子程序的“return”)结束,这是 to avoid fall-through from one subprog into another
。在函数末尾不执行任何特定操作(return 或退出整个程序)是没有意义的,而且会很危险(一旦进行了 JIT,程序在内存中不一定是连续的,因此您不能'安全地通过,即使这是有道理的)。
现在如果我们看看 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()
.