Reputation: 330
I am trying to get into eBPF CO:RE with libbpf. My program uses a tracepoint SEC("tracepoint/syscalls/sys_enter_kill")) and I wonder, how to get the arguments and why they are not included in vmlinux.h. Additionally, the define BPF_F_CURRENT_CPU seems to be missing. Or is it possible to combine vmlinux.h and uapi/linux/bpf.h?
My setup:
I know there is already this question (Read eBPF tracepoint argument) on how to get the arguments. However, this does not answer my question (why is it not in vmlinux.h? What else is the sense of vmlinux.h?) and I have another issue, the layout the kernel shows seems not to be correct (see below). Also, the define BPF_F_CURRENT_CPU seems to be missing.
My code:
common.h: Only a small struct to transport data to userspace
struct dataStruct {
int pid;
};
main.bpf.c:
// Compiling with: clang -target bpf -S -D __BPF_TRACING__ -Wall -Werror -O2 -emit-llvm -c -g main.bpf.c
// and then: llc -march=bpf -filetype=obj -o main.bpf.o main.bpf.ll
#include "vmlinux.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf_helpers.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf_tracing.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf_core_read.h"
#include "common.h"
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
} pb SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, int);;
__type(value, struct dataStruct);
} heap SEC(".maps");
// 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;
// print fmt: "pid: 0x%08lx, sig: 0x%08lx", ((unsigned long)(REC->pid)), ((unsigned long)(REC->sig))
// >> How to obtain this structurecorrectly?
struct syscalls_enter_kill_args
{
unsigned short common_type;
unsigned char common_flags;
unsigned char common_preempt_count;
int common_pid;
// int syscall_nr; // From sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
// pid_t pid;
// int sig;
long syscall_nr; // From https://hed.am/papers/2021-EBPF.pdf
long pid;
long sig;
};
enum { // from #include "../90_lib/libbpf/include/uapi/linux/bpf.h"
BPF_F_INDEX_MASK = 0xffffffffULL,
BPF_F_CURRENT_CPU = BPF_F_INDEX_MASK,
/* BPF_FUNC_perf_event_output for sk_buff input context. */
BPF_F_CTXLEN_MASK = (0xfffffULL << 32),
};
SEC("tracepoint/syscalls/sys_enter_kill")
int kill_example(struct syscalls_enter_kill_args *ctx)
{
if(ctx->sig != 9)
return 0;
// int s = BPF_CORE_READ(ctx, sig); // Does not work
// if(s != 9)
// return 0;
char fmt[] = "BPF handle\n";
bpf_trace_printk(fmt, sizeof(fmt));
struct dataStruct *e;
int zero = 0;
e = bpf_map_lookup_elem(&heap, &zero);
if (!e) /* can't happen */
return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
bpf_perf_event_output(ctx, &pb, BPF_F_CURRENT_CPU, e, sizeof(*e));
return 0;
}
char _license[] SEC("license") = "GPL";
main.cc:
// Compile loader
// >> g++ -I../90_lib/libbpf/src/root/usr/include/ -L../90_lib/libbpf/src/ -o ebpf main.cc -lbpf -lelf -Wl,-R../90_lib/libbpf/src/
# include <stdio.h>
#include <iostream>
# include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <poll.h>
#include <sys/select.h>
#include </usr/include/asm-generic/errno-base.h>
// Should be defined in up-to-date linux/bpf.h, but is not (and other linux/bpf.h conflicts with a lot of definitions)
/* type for BPF_ENABLE_STATS */
enum bpf_stats_type {
/* enabled run_time_ns and run_cnt */
BPF_STATS_RUN_TIME = 0,
};
#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/libbpf.h"
#include "common.h"
void bump_memlock_rlimit(void)
{
struct rlimit rlim_new = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) {
fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n");
exit(1);
}
}
using namespace std;
void handle_event(void *ctx, int cpu, void *data, unsigned int data_sz)
{
cout << "perfBuffer - Event!; got " << data_sz << " Bytes? data." << endl;
struct dataStruct* d = static_cast<struct dataStruct*>(data);
cout << " PID: " << d->pid << endl;
}
int main (int argc , char ** argv )
{
int prog_fd ;
struct bpf_object *obj;
bump_memlock_rlimit();
if(bpf_prog_load("main.bpf.o", BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd) != 0)
{
printf (" eBPF program not loaded \n");
return -1;
}
// Check that we got a file descriptor for the loaded object file.
if(prog_fd < 1)
{
printf (" Error creating prog_fd \n");
return -2;
}
// Attach the eBPF program by it 's function name
struct bpf_program * prog = bpf_object__find_program_by_name(obj, "kill_example");
bpf_program__attach(prog);
int numMaps = 0;
struct bpf_map * map;
struct bpf_map* map_pb;
struct bpf_map* maps[10];
for (map = bpf_map__next(NULL, (obj)); \
map != NULL; \
map = bpf_map__next(map, (obj)))
{
++numMaps;
cout << "Found map, name: '" << bpf_map__name(map) << "'" << endl;
if(strcmp(bpf_map__name(map), "pb") == 0)
{
cout << " Found map 'pb'; fileDescriptor: " << bpf_map__fd(map) << endl;
map_pb = map;
}
}
cout << "Found " << numMaps << " Maps." << endl;
int map_pb_fd = bpf_map__fd(map_pb);
cout << "Got Map File Descriptor: " << map_pb_fd << endl;
uint32_t map_key_cnt = 0;
uint64_t val = 0;
uint64_t val_last = val;
struct perf_buffer *pb = NULL;
struct perf_buffer_opts pb_opts = {};
pb_opts.sample_cb = handle_event;
pb = perf_buffer__new(map_pb_fd, 8 /* 32KB per CPU */, &pb_opts);
if (libbpf_get_error(pb)) {
fprintf(stderr, "Failed to create perf buffer\n");
}
int res = 0;
cout << "Entering main loop." << endl;
while(1)
{
res = perf_buffer__poll(pb, -1);
if(res == -EINTR)
{
cout << "-EINTR" << endl;
return 0;
}
}
return 0;
}
This works with the structure
struct syscalls_enter_kill_args
{
unsigned short common_type;
unsigned char common_flags;
unsigned char common_preempt_count;
int common_pid;
long syscall_nr; // From https://hed.am/papers/2021-EBPF.pdf
long pid;
long sig;
};
but NOT when I change the structure to
struct syscalls_enter_kill_args
{
unsigned short common_type;
unsigned char common_flags;
unsigned char common_preempt_count;
int common_pid;
int syscall_nr; // From sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
pid_t pid;
int sig;
};
which seems strange. Does anybody have an idea on what is wrong? Thanks in advance! :)
Upvotes: 0
Views: 1363
Reputation: 2517
Only reason its not working because format of variable and size doesn't match in this case. check this file for format of sys_kill_enter /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
although field of pid is pid_t and for sig its int.
but if you check size of pid and sig both are 8.
so long
is working where as pid_t
didn't.
Upvotes: 1