eBPF:raw_tracepoint 个参数
eBPF: raw_tracepoint arguments
我正在学习 eBPF 编程并想使用原始跟踪点,但我不太了解如何使用它们以及如何正确访问参数。对于文档化的任何帮助和提示,我将不胜感激。
我的问题:
- 如何使用 raw_tracepoint 而不是跟踪点从系统调用中获取参数?
- 顺便说一句:原始跟踪点的
uint16_t common_type;
是什么?
系统:Ubuntu 2004 内核 5.4 通用,x86_64
Explanation/Example:
我从“普通跟踪点”sys_enter_kill
开始,在这里我可以使用来自 sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
:
的参数创建结构
// sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
// name: sys_enter_kill
// ID: 184
// format:
// field:unsigned short common_type; offset:0; size:2; signed:0;
// field:unsigned char common_flags; offset:2; size:1; signed:0;
// field:unsigned char common_preempt_count; offset:3; size:1;signed:0;
// field:int common_pid; offset:4; size:4; signed:1;
// field:int __syscall_nr; offset:8; size:4; signed:1;
// field:pid_t pid; offset:16; size:8; signed:0;
// field:int sig; offset:24; size:8; signed:0;
struct syscalls_enter_kill_args
{
unsigned short common_type;
unsigned char common_flags;
unsigned char common_preempt_count;
int common_pid;
long syscall_nr;
long pid;
long sig;
};
SEC("tracepoint/xxx")
int main_entry(struct syscalls_enter_kill_args *ctx)
{
if(ctx->sig != 9)
return 0;
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid;
bpf_printk("Catched function call; PID = : %d.\n", pid);
return 0;
}
这个简单的 bpf 程序只在调用 kill 信号时输出一些文本。它只记录 SIGKILL,不记录 SIGINT、SIGQUIT、...
现在我想对原始跟踪点 sys_enter
.
执行相同的功能
// sudo cat /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/format
// name: sys_enter
// ID: 22
// format:
// field:unsigned short common_type; offset:0; size:2; signed:0;
// field:unsigned char common_flags; offset:2; size:1; signed:0;
// field:unsigned char common_preempt_count; offset:3; size:1;signed:0;
// field:int common_pid; offset:4; size:4; signed:1;
// field:long id; offset:8; size:8; signed:1;
// field:unsigned long args[6]; offset:16; size:48; signed:0;
struct sys_enter_args
{
uint16_t common_type;
uint8_t common_flags;
uint8_t common_preempt_count;
int32_t common_pid;
int64_t id;
uint64_t args[6]; // Je 4 Bytes
};
SEC("raw_tracepoint/xxx")
int main_entry_raw(struct sys_enter_args *ctx)
{
if(ctx->id != SYS_kill) // 62
return 0;
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid;
bpf_printk("Catched function call; PID = : %d.\n", pid);
bpf_printk(" type: %u\n", ctx->common_type);
bpf_printk(" id: %u\n", ctx->id);
uint64_t* args = ctx->args;
uint64_t arg3 = 0;
bpf_probe_read(&arg3, sizeof(uint64_t), args + 3);
bpf_printk(" Arg3: %u \n", arg3);
}
我想,我可能会收到信号 (SIGKILL/SIGINT/SIGQUIT/...)
field:int sig; offset:24; size:8; signed:0;
来自 args[]
:
偏移量=24 => 字节 3;大小 8 => 类型 u64 = unsigned long。
但是,这不会产生任何有用的值。
那么我如何获得信号的值,我可以在跟踪点以及 raw_tracepoint?
中访问它
感谢您的帮助!
我想我已经解决了,基于这个article。
raw_tracepoint 程序的 ctx 是 struct bpf_raw_tracepoint_args
。在 bpf.h 中定义为
struct bpf_raw_tracepoint_args {
__u64 args[0];
};
所以基本上只是一个 numbers/pointers 的数组。这些参数的含义取决于跟踪点原型的定义方式。在查看 tracepoint is defined 所在的源代码时,我们发现:
TRACE_EVENT_FN(sys_enter,
TP_PROTO(struct pt_regs *regs, long id),
TP_ARGS(regs, id),
TP_STRUCT__entry(
__field( long, id )
__array( unsigned long, args, 6 )
),
TP_fast_assign(
__entry->id = id;
syscall_get_arguments(current, regs, __entry->args);
),
TP_printk("NR %ld (%lx, %lx, %lx, %lx, %lx, %lx)",
__entry->id,
__entry->args[0], __entry->args[1], __entry->args[2],
__entry->args[3], __entry->args[4], __entry->args[5]),
syscall_regfunc, syscall_unregfunc
);
让我们关注 TP_PROTO(struct pt_regs *regs, long id)
,这意味着 args[0]
是 struct pt_regs *regs
,args[1]
是 long id
。 struct pt_regs
是调用 sys_enter
时 CPU 寄存器的副本。 id
是系统调用的 ID。
我们可以通过从 CPU 寄存器中提取参数来获取系统调用的参数。 System V ABI 指定哪些参数应该出现在哪些 CPU 寄存器中。为了让我们的生活更轻松,libbpf 定义了 PT_REGS_PARM{1..5} macros in bpf_tracing.h
所以,如果相信这应该是一个正确的程序:
SEC("raw_tracepoint/sys_enter")
int main_entry_raw(struct bpf_raw_tracepoint_args *ctx)
{
unsigned long syscall_id = ctx->args[1];
struct pt_regs *regs;
if(syscall_id != SYS_kill) // 62
return 0;
regs = (struct pt_regs *)ctx->args[0];
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid;
bpf_printk("Catched function call; PID = : %d.\n", pid);
bpf_printk(" id: %u\n", syscall_id);
uint64_t arg3 = 0;
bpf_probe_read(&arg3, sizeof(uint64_t), PT_REGS_PARM3(regs));
bpf_printk(" Arg3: %u \n", arg3);
}
我正在学习 eBPF 编程并想使用原始跟踪点,但我不太了解如何使用它们以及如何正确访问参数。对于文档化的任何帮助和提示,我将不胜感激。
我的问题:
- 如何使用 raw_tracepoint 而不是跟踪点从系统调用中获取参数?
- 顺便说一句:原始跟踪点的
uint16_t common_type;
是什么?
系统:Ubuntu 2004 内核 5.4 通用,x86_64
Explanation/Example:
我从“普通跟踪点”sys_enter_kill
开始,在这里我可以使用来自 sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
:
// sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
// name: sys_enter_kill
// ID: 184
// format:
// field:unsigned short common_type; offset:0; size:2; signed:0;
// field:unsigned char common_flags; offset:2; size:1; signed:0;
// field:unsigned char common_preempt_count; offset:3; size:1;signed:0;
// field:int common_pid; offset:4; size:4; signed:1;
// field:int __syscall_nr; offset:8; size:4; signed:1;
// field:pid_t pid; offset:16; size:8; signed:0;
// field:int sig; offset:24; size:8; signed:0;
struct syscalls_enter_kill_args
{
unsigned short common_type;
unsigned char common_flags;
unsigned char common_preempt_count;
int common_pid;
long syscall_nr;
long pid;
long sig;
};
SEC("tracepoint/xxx")
int main_entry(struct syscalls_enter_kill_args *ctx)
{
if(ctx->sig != 9)
return 0;
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid;
bpf_printk("Catched function call; PID = : %d.\n", pid);
return 0;
}
这个简单的 bpf 程序只在调用 kill 信号时输出一些文本。它只记录 SIGKILL,不记录 SIGINT、SIGQUIT、...
现在我想对原始跟踪点 sys_enter
.
// sudo cat /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/format
// name: sys_enter
// ID: 22
// format:
// field:unsigned short common_type; offset:0; size:2; signed:0;
// field:unsigned char common_flags; offset:2; size:1; signed:0;
// field:unsigned char common_preempt_count; offset:3; size:1;signed:0;
// field:int common_pid; offset:4; size:4; signed:1;
// field:long id; offset:8; size:8; signed:1;
// field:unsigned long args[6]; offset:16; size:48; signed:0;
struct sys_enter_args
{
uint16_t common_type;
uint8_t common_flags;
uint8_t common_preempt_count;
int32_t common_pid;
int64_t id;
uint64_t args[6]; // Je 4 Bytes
};
SEC("raw_tracepoint/xxx")
int main_entry_raw(struct sys_enter_args *ctx)
{
if(ctx->id != SYS_kill) // 62
return 0;
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid;
bpf_printk("Catched function call; PID = : %d.\n", pid);
bpf_printk(" type: %u\n", ctx->common_type);
bpf_printk(" id: %u\n", ctx->id);
uint64_t* args = ctx->args;
uint64_t arg3 = 0;
bpf_probe_read(&arg3, sizeof(uint64_t), args + 3);
bpf_printk(" Arg3: %u \n", arg3);
}
我想,我可能会收到信号 (SIGKILL/SIGINT/SIGQUIT/...)
field:int sig; offset:24; size:8; signed:0;
来自 args[]
:
偏移量=24 => 字节 3;大小 8 => 类型 u64 = unsigned long。
但是,这不会产生任何有用的值。
那么我如何获得信号的值,我可以在跟踪点以及 raw_tracepoint?
感谢您的帮助!
我想我已经解决了,基于这个article。
raw_tracepoint 程序的 ctx 是 struct bpf_raw_tracepoint_args
。在 bpf.h 中定义为
struct bpf_raw_tracepoint_args {
__u64 args[0];
};
所以基本上只是一个 numbers/pointers 的数组。这些参数的含义取决于跟踪点原型的定义方式。在查看 tracepoint is defined 所在的源代码时,我们发现:
TRACE_EVENT_FN(sys_enter,
TP_PROTO(struct pt_regs *regs, long id),
TP_ARGS(regs, id),
TP_STRUCT__entry(
__field( long, id )
__array( unsigned long, args, 6 )
),
TP_fast_assign(
__entry->id = id;
syscall_get_arguments(current, regs, __entry->args);
),
TP_printk("NR %ld (%lx, %lx, %lx, %lx, %lx, %lx)",
__entry->id,
__entry->args[0], __entry->args[1], __entry->args[2],
__entry->args[3], __entry->args[4], __entry->args[5]),
syscall_regfunc, syscall_unregfunc
);
让我们关注 TP_PROTO(struct pt_regs *regs, long id)
,这意味着 args[0]
是 struct pt_regs *regs
,args[1]
是 long id
。 struct pt_regs
是调用 sys_enter
时 CPU 寄存器的副本。 id
是系统调用的 ID。
我们可以通过从 CPU 寄存器中提取参数来获取系统调用的参数。 System V ABI 指定哪些参数应该出现在哪些 CPU 寄存器中。为了让我们的生活更轻松,libbpf 定义了 PT_REGS_PARM{1..5} macros in bpf_tracing.h
所以,如果相信这应该是一个正确的程序:
SEC("raw_tracepoint/sys_enter")
int main_entry_raw(struct bpf_raw_tracepoint_args *ctx)
{
unsigned long syscall_id = ctx->args[1];
struct pt_regs *regs;
if(syscall_id != SYS_kill) // 62
return 0;
regs = (struct pt_regs *)ctx->args[0];
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid;
bpf_printk("Catched function call; PID = : %d.\n", pid);
bpf_printk(" id: %u\n", syscall_id);
uint64_t arg3 = 0;
bpf_probe_read(&arg3, sizeof(uint64_t), PT_REGS_PARM3(regs));
bpf_printk(" Arg3: %u \n", arg3);
}