From 5fd023df32ca5d4b0f0ec1788bf37e183f61344b Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 8 May 2024 15:40:12 +0200 Subject: [PATCH] Add ebpf lost event counter. Issue #38 This commit includes the pending PR: https://github.com/elastic/ebpf/pull/198 Once that is merged I'll rebase this, so ignore the elastic-ebpf/* bits. Pretty straighforward, contrary to kprobes which we get the counter on the data path, with ebpf we have to actually read it, so add a new ops for updating the counter, we should caution users to not hammer the reading, as it's real syscall. Tested by hacking quark-mon away. --- bpf_queue.c | 31 +++++++++++--- elastic-ebpf/GPL/Events/EbpfEventProto.h | 47 ++++++++++++--------- elastic-ebpf/GPL/Events/Helpers.h | 21 +++++++++ elastic-ebpf/GPL/Events/Process/Probe.bpf.c | 12 +++--- elastic-ebpf/GPL/Events/Varlen.h | 2 +- elastic-ebpf/commit | 2 +- kprobe_queue.c | 15 +++++-- quark.c | 1 + quark.h | 1 + 9 files changed, 95 insertions(+), 37 deletions(-) diff --git a/bpf_queue.c b/bpf_queue.c index 02dfc4a..a62e596 100644 --- a/bpf_queue.c +++ b/bpf_queue.c @@ -11,17 +11,19 @@ #include "elastic-ebpf/GPL/Events/EbpfEventProto.h" struct bpf_queue { - struct bpf_prog *prog; - struct ring_buffer *ringbuf; + struct bpf_prog *prog; + struct ring_buffer *ringbuf; }; static int bpf_queue_populate(struct quark_queue *); +static int bpf_queue_update_stats(struct quark_queue *); static void bpf_queue_close(struct quark_queue *); struct quark_queue_ops queue_ops_bpf = { - .open = bpf_queue_open, - .populate = bpf_queue_populate, - .close = bpf_queue_close, + .open = bpf_queue_open, + .populate = bpf_queue_populate, + .update_stats = bpf_queue_update_stats, + .close = bpf_queue_close, }; static int @@ -262,7 +264,6 @@ bpf_queue_populate(struct quark_queue *qq) struct bpf_queue *bqq = qq->queue_be; int npop, space_left; - npop = 0; space_left = qq->length >= qq->max_length ? 0 : qq->max_length - qq->length; if (space_left == 0) @@ -273,6 +274,24 @@ bpf_queue_populate(struct quark_queue *qq) return (npop < 0 ? -1 : npop); } +static int +bpf_queue_update_stats(struct quark_queue *qq) +{ + struct bpf_queue *bqq = qq->queue_be; + struct ebpf_event_stats pcpu_ees[libbpf_num_possible_cpus()]; + u32 zero = 0; + int i; + + if (bpf_map__lookup_elem(bqq->prog->maps.ringbuf_stats, &zero, + sizeof(zero), pcpu_ees, sizeof(pcpu_ees), 0) != 0) + return (-1); + + for (i = 0; i < libbpf_num_possible_cpus(); i++) + qq->stats.lost = pcpu_ees[i].lost; + + return (0); +} + static void bpf_queue_close(struct quark_queue *qq) { diff --git a/elastic-ebpf/GPL/Events/EbpfEventProto.h b/elastic-ebpf/GPL/Events/EbpfEventProto.h index d858832..c3919c9 100644 --- a/elastic-ebpf/GPL/Events/EbpfEventProto.h +++ b/elastic-ebpf/GPL/Events/EbpfEventProto.h @@ -19,26 +19,27 @@ #endif enum ebpf_event_type { - EBPF_EVENT_PROCESS_FORK = (1 << 1), - EBPF_EVENT_PROCESS_EXEC = (1 << 2), - EBPF_EVENT_PROCESS_EXIT = (1 << 3), - EBPF_EVENT_PROCESS_SETSID = (1 << 4), - EBPF_EVENT_PROCESS_SETUID = (1 << 5), - EBPF_EVENT_PROCESS_SETGID = (1 << 6), - EBPF_EVENT_PROCESS_TTY_WRITE = (1 << 7), - EBPF_EVENT_FILE_DELETE = (1 << 8), - EBPF_EVENT_FILE_CREATE = (1 << 9), - EBPF_EVENT_FILE_RENAME = (1 << 10), - EBPF_EVENT_FILE_MODIFY = (1 << 11), - EBPF_EVENT_FILE_MEMFD_OPEN = (1 << 12), - EBPF_EVENT_FILE_SHMEM_OPEN = (1 << 13), - EBPF_EVENT_NETWORK_CONNECTION_ACCEPTED = (1 << 14), - EBPF_EVENT_NETWORK_CONNECTION_ATTEMPTED = (1 << 15), - EBPF_EVENT_NETWORK_CONNECTION_CLOSED = (1 << 16), - EBPF_EVENT_PROCESS_MEMFD_CREATE = (1 << 17), - EBPF_EVENT_PROCESS_SHMGET = (1 << 18), - EBPF_EVENT_PROCESS_PTRACE = (1 << 19), - EBPF_EVENT_PROCESS_LOAD_MODULE = (1 << 20), + EBPF_EVENT_PROCESS_INVALID = 0, + EBPF_EVENT_PROCESS_FORK = (1 << 0), + EBPF_EVENT_PROCESS_EXEC = (1 << 1), + EBPF_EVENT_PROCESS_EXIT = (1 << 2), + EBPF_EVENT_PROCESS_SETSID = (1 << 3), + EBPF_EVENT_PROCESS_SETUID = (1 << 4), + EBPF_EVENT_PROCESS_SETGID = (1 << 5), + EBPF_EVENT_PROCESS_TTY_WRITE = (1 << 6), + EBPF_EVENT_FILE_DELETE = (1 << 7), + EBPF_EVENT_FILE_CREATE = (1 << 8), + EBPF_EVENT_FILE_RENAME = (1 << 9), + EBPF_EVENT_FILE_MODIFY = (1 << 10), + EBPF_EVENT_FILE_MEMFD_OPEN = (1 << 11), + EBPF_EVENT_FILE_SHMEM_OPEN = (1 << 12), + EBPF_EVENT_NETWORK_CONNECTION_ACCEPTED = (1 << 13), + EBPF_EVENT_NETWORK_CONNECTION_ATTEMPTED = (1 << 14), + EBPF_EVENT_NETWORK_CONNECTION_CLOSED = (1 << 15), + EBPF_EVENT_PROCESS_MEMFD_CREATE = (1 << 16), + EBPF_EVENT_PROCESS_SHMGET = (1 << 17), + EBPF_EVENT_PROCESS_PTRACE = (1 << 18), + EBPF_EVENT_PROCESS_LOAD_MODULE = (1 << 19), }; struct ebpf_event_header { @@ -378,4 +379,10 @@ struct ebpf_net_event { char comm[TASK_COMM_LEN]; } __attribute__((packed)); +// Basic event statistics +struct ebpf_event_stats { + uint64_t lost; // lost events due to a full ringbuffer + uint64_t sent; // events sent through the ringbuffer +}; + #endif // EBPF_EVENTPROBE_EBPFEVENTPROTO_H diff --git a/elastic-ebpf/GPL/Events/Helpers.h b/elastic-ebpf/GPL/Events/Helpers.h index d5e7bcd..92e5df5 100644 --- a/elastic-ebpf/GPL/Events/Helpers.h +++ b/elastic-ebpf/GPL/Events/Helpers.h @@ -295,6 +295,27 @@ static void ebpf_comm__fill(char *comm, size_t len, const struct task_struct *ta read_kernel_str_or_empty_str(comm, len, BPF_CORE_READ(task, comm)); } +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, u32); + __type(value, struct ebpf_event_stats); + __uint(max_entries, 1); +} ringbuf_stats SEC(".maps"); + +static long ebpf_ringbuf_write(void *ringbuf, void *data, u64 size, u64 flags) +{ + long r; + struct ebpf_event_stats *ees; + u32 zero = 0; + + r = bpf_ringbuf_output(ringbuf, data, size, flags); + ees = bpf_map_lookup_elem(&ringbuf_stats, &zero); + if (ees != NULL) + r == 0 ? ees->sent++ : ees->lost++; + + return (r); +} + static bool is_kernel_thread(const struct task_struct *task) { // All kernel threads are children of kthreadd, which always has pid 2 diff --git a/elastic-ebpf/GPL/Events/Process/Probe.bpf.c b/elastic-ebpf/GPL/Events/Process/Probe.bpf.c index 00b5cb0..8970943 100644 --- a/elastic-ebpf/GPL/Events/Process/Probe.bpf.c +++ b/elastic-ebpf/GPL/Events/Process/Probe.bpf.c @@ -75,7 +75,7 @@ int BPF_PROG(sched_process_fork, const struct task_struct *parent, const struct size = ebpf_resolve_path_to_string(field->data, &child->fs->pwd, child); ebpf_vl_field__set_size(&event->vl_fields, field, size); - bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0); + ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0); out: return 0; @@ -165,7 +165,7 @@ int BPF_PROG(sched_process_exec, size = read_kernel_str_or_empty_str(field->data, PATH_MAX, binprm->filename); ebpf_vl_field__set_size(&event->vl_fields, field, size); - bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0); + ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0); out: return 0; @@ -219,7 +219,7 @@ static int taskstats_exit__enter(const struct task_struct *task, int group_dead) size = ebpf_resolve_pids_ss_cgroup_path_to_string(field->data, task); ebpf_vl_field__set_size(&event->vl_fields, field, size); - bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0); + ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0); out: return 0; @@ -315,7 +315,7 @@ int BPF_PROG(module_load, struct module *mod) size = read_kernel_str_or_empty_str(field->data, PATH_MAX, mod->srcversion); ebpf_vl_field__set_size(&event->vl_fields, field, size); - bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0); + ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0); out: return 0; @@ -448,7 +448,7 @@ int tracepoint_syscalls_sys_enter_memfd_create(struct trace_event_raw_sys_enter goto out; ebpf_vl_field__set_size(&event->vl_fields, field, size); - bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0); + ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0); out: return 0; @@ -562,7 +562,7 @@ static int output_tty_event(struct ebpf_tty_dev *slave, const void *base, size_t } ebpf_vl_field__set_size(&event->vl_fields, field, len_cap); - bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0); + ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0); out: return ret; } diff --git a/elastic-ebpf/GPL/Events/Varlen.h b/elastic-ebpf/GPL/Events/Varlen.h index 56dddea..f30de8b 100644 --- a/elastic-ebpf/GPL/Events/Varlen.h +++ b/elastic-ebpf/GPL/Events/Varlen.h @@ -17,7 +17,7 @@ // We can't use the ringbuf reserve/commit API if we want to output an event // with variable length fields as we won't know the event size in advance, so // we create events on the event_buffer_map if this is the case and output them -// with bpf_ringbuf_output. +// with ebpf_ringbuf_write. // // If the event has no variable length parameters (i.e. is always a fixed // size). bpf_ringbuf_reserve/bpf_ringbuf_submit should be used instead to diff --git a/elastic-ebpf/commit b/elastic-ebpf/commit index 4d72e6a..6380649 100644 --- a/elastic-ebpf/commit +++ b/elastic-ebpf/commit @@ -1 +1 @@ -ba15ef679e3bbdc784d18fb2cd42d3687b7f1d83 +2550cdd9431444e19887f0c8437f32a5c56ca140 diff --git a/kprobe_queue.c b/kprobe_queue.c index a111f94..0c3cd8f 100644 --- a/kprobe_queue.c +++ b/kprobe_queue.c @@ -266,12 +266,14 @@ struct kprobe_queue { }; static int kprobe_queue_populate(struct quark_queue *); +static int kprobe_queue_update_stats(struct quark_queue *); static void kprobe_queue_close(struct quark_queue *); struct quark_queue_ops queue_ops_kprobe = { - .open = kprobe_queue_open, - .populate = kprobe_queue_populate, - .close = kprobe_queue_close, + .open = kprobe_queue_open, + .populate = kprobe_queue_populate, + .update_stats = kprobe_queue_update_stats, + .close = kprobe_queue_close, }; static char * @@ -1298,6 +1300,13 @@ kprobe_queue_populate(struct quark_queue *qq) return (npop); } +static int +kprobe_queue_update_stats(struct quark_queue *qq) +{ + /* NADA */ + return (0); +} + static void kprobe_queue_close(struct quark_queue *qq) { diff --git a/quark.c b/quark.c index 9fdac60..24b6b09 100644 --- a/quark.c +++ b/quark.c @@ -1597,6 +1597,7 @@ quark_queue_get_epollfd(struct quark_queue *qq) void quark_queue_get_stats(struct quark_queue *qq, struct quark_queue_stats *qs) { + qq->queue_ops->update_stats(qq); *qs = qq->stats; } diff --git a/quark.h b/quark.h index 03d3a09..6d56c87 100644 --- a/quark.h +++ b/quark.h @@ -320,6 +320,7 @@ struct quark_queue_stats { struct quark_queue_ops { int (*open)(struct quark_queue *); int (*populate)(struct quark_queue *); + int (*update_stats)(struct quark_queue *); void (*close)(struct quark_queue *); };