Skip to content

Commit

Permalink
Add ebpf lost event counter. Issue #38
Browse files Browse the repository at this point in the history
This commit includes the pending PR: elastic/ebpf#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.
  • Loading branch information
haesbaert committed Jul 2, 2024
1 parent b171c6c commit 5fd023d
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 37 deletions.
31 changes: 25 additions & 6 deletions bpf_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
{
Expand Down
47 changes: 27 additions & 20 deletions elastic-ebpf/GPL/Events/EbpfEventProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
21 changes: 21 additions & 0 deletions elastic-ebpf/GPL/Events/Helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions elastic-ebpf/GPL/Events/Process/Probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion elastic-ebpf/GPL/Events/Varlen.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion elastic-ebpf/commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ba15ef679e3bbdc784d18fb2cd42d3687b7f1d83
2550cdd9431444e19887f0c8437f32a5c56ca140
15 changes: 12 additions & 3 deletions kprobe_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand Down Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions quark.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions quark.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
};

Expand Down

0 comments on commit 5fd023d

Please sign in to comment.