Skip to content

Commit 97581e4

Browse files
authored
Basic ringbuf event statistics (#198)
Basic ringbuf event statistics This adds a new structure `ebpf_event_stats` where we accumulate stats, right now it only counts lost events vs non-lost events, but we can grow this in the future, like adding per type events if it's interesting enough. The print in EventsTrace is quite primitive as I believe this will only be used for debugging there, so heh. Choosing a name for `ebpf_ringbuf_write` was a bit tricky, I wanted to keep the same parameters but it had to be "different enough" that we could spot it with a naked eye. While here, adjust the enum of ebpf events to not skip that precious first bit.
1 parent ba15ef6 commit 97581e4

File tree

8 files changed

+102
-33
lines changed

8 files changed

+102
-33
lines changed

GPL/Events/EbpfEventProto.h

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,27 @@
1919
#endif
2020

2121
enum ebpf_event_type {
22-
EBPF_EVENT_PROCESS_FORK = (1 << 1),
23-
EBPF_EVENT_PROCESS_EXEC = (1 << 2),
24-
EBPF_EVENT_PROCESS_EXIT = (1 << 3),
25-
EBPF_EVENT_PROCESS_SETSID = (1 << 4),
26-
EBPF_EVENT_PROCESS_SETUID = (1 << 5),
27-
EBPF_EVENT_PROCESS_SETGID = (1 << 6),
28-
EBPF_EVENT_PROCESS_TTY_WRITE = (1 << 7),
29-
EBPF_EVENT_FILE_DELETE = (1 << 8),
30-
EBPF_EVENT_FILE_CREATE = (1 << 9),
31-
EBPF_EVENT_FILE_RENAME = (1 << 10),
32-
EBPF_EVENT_FILE_MODIFY = (1 << 11),
33-
EBPF_EVENT_FILE_MEMFD_OPEN = (1 << 12),
34-
EBPF_EVENT_FILE_SHMEM_OPEN = (1 << 13),
35-
EBPF_EVENT_NETWORK_CONNECTION_ACCEPTED = (1 << 14),
36-
EBPF_EVENT_NETWORK_CONNECTION_ATTEMPTED = (1 << 15),
37-
EBPF_EVENT_NETWORK_CONNECTION_CLOSED = (1 << 16),
38-
EBPF_EVENT_PROCESS_MEMFD_CREATE = (1 << 17),
39-
EBPF_EVENT_PROCESS_SHMGET = (1 << 18),
40-
EBPF_EVENT_PROCESS_PTRACE = (1 << 19),
41-
EBPF_EVENT_PROCESS_LOAD_MODULE = (1 << 20),
22+
EBPF_EVENT_PROCESS_INVALID = 0,
23+
EBPF_EVENT_PROCESS_FORK = (1 << 0),
24+
EBPF_EVENT_PROCESS_EXEC = (1 << 1),
25+
EBPF_EVENT_PROCESS_EXIT = (1 << 2),
26+
EBPF_EVENT_PROCESS_SETSID = (1 << 3),
27+
EBPF_EVENT_PROCESS_SETUID = (1 << 4),
28+
EBPF_EVENT_PROCESS_SETGID = (1 << 5),
29+
EBPF_EVENT_PROCESS_TTY_WRITE = (1 << 6),
30+
EBPF_EVENT_FILE_DELETE = (1 << 7),
31+
EBPF_EVENT_FILE_CREATE = (1 << 8),
32+
EBPF_EVENT_FILE_RENAME = (1 << 9),
33+
EBPF_EVENT_FILE_MODIFY = (1 << 10),
34+
EBPF_EVENT_FILE_MEMFD_OPEN = (1 << 11),
35+
EBPF_EVENT_FILE_SHMEM_OPEN = (1 << 12),
36+
EBPF_EVENT_NETWORK_CONNECTION_ACCEPTED = (1 << 13),
37+
EBPF_EVENT_NETWORK_CONNECTION_ATTEMPTED = (1 << 14),
38+
EBPF_EVENT_NETWORK_CONNECTION_CLOSED = (1 << 15),
39+
EBPF_EVENT_PROCESS_MEMFD_CREATE = (1 << 16),
40+
EBPF_EVENT_PROCESS_SHMGET = (1 << 17),
41+
EBPF_EVENT_PROCESS_PTRACE = (1 << 18),
42+
EBPF_EVENT_PROCESS_LOAD_MODULE = (1 << 19),
4243
};
4344

4445
struct ebpf_event_header {
@@ -378,4 +379,10 @@ struct ebpf_net_event {
378379
char comm[TASK_COMM_LEN];
379380
} __attribute__((packed));
380381

382+
// Basic event statistics
383+
struct ebpf_event_stats {
384+
uint64_t lost; // lost events due to a full ringbuffer
385+
uint64_t sent; // events sent through the ringbuffer
386+
};
387+
381388
#endif // EBPF_EVENTPROBE_EBPFEVENTPROTO_H

GPL/Events/File/Probe.bpf.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ static int vfs_unlink__exit(int ret)
155155
size = ebpf_resolve_pids_ss_cgroup_path_to_string(field->data, task);
156156
ebpf_vl_field__set_size(&event->vl_fields, field, size);
157157

158-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
158+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
159159

160160
// Certain filesystems (eg. overlayfs) call vfs_unlink twice during the same
161161
// execution context.
@@ -263,10 +263,10 @@ static void prepare_and_send_file_event(struct file *f,
263263
if (path_prefix) {
264264
if ((path_prefix_len > 0) && (size >= path_prefix_len)) {
265265
if (is_equal_prefix(field->data, path_prefix, path_prefix_len))
266-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
266+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
267267
}
268268
} else {
269-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
269+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
270270
}
271271
}
272272

@@ -516,7 +516,7 @@ static int vfs_rename__exit(int ret)
516516
size = ebpf_resolve_pids_ss_cgroup_path_to_string(field->data, task);
517517
ebpf_vl_field__set_size(&event->vl_fields, field, size);
518518

519-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
519+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
520520

521521
// Certain filesystems (eg. overlayfs) call vfs_rename twice during the same
522522
// execution context.
@@ -588,7 +588,7 @@ static void file_modify_event__emit(enum ebpf_file_change_type typ, struct path
588588
size = ebpf_resolve_pids_ss_cgroup_path_to_string(field->data, task);
589589
ebpf_vl_field__set_size(&event->vl_fields, field, size);
590590

591-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
591+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
592592

593593
out:
594594
return;

GPL/Events/Helpers.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,27 @@ static void ebpf_comm__fill(char *comm, size_t len, const struct task_struct *ta
295295
read_kernel_str_or_empty_str(comm, len, BPF_CORE_READ(task, comm));
296296
}
297297

298+
struct {
299+
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
300+
__type(key, u32);
301+
__type(value, struct ebpf_event_stats);
302+
__uint(max_entries, 1);
303+
} ringbuf_stats SEC(".maps");
304+
305+
static long ebpf_ringbuf_write(void *ringbuf, void *data, u64 size, u64 flags)
306+
{
307+
long r;
308+
struct ebpf_event_stats *ees;
309+
u32 zero = 0;
310+
311+
r = bpf_ringbuf_output(ringbuf, data, size, flags);
312+
ees = bpf_map_lookup_elem(&ringbuf_stats, &zero);
313+
if (ees != NULL)
314+
r == 0 ? ees->sent++ : ees->lost++;
315+
316+
return (r);
317+
}
318+
298319
static bool is_kernel_thread(const struct task_struct *task)
299320
{
300321
// All kernel threads are children of kthreadd, which always has pid 2

GPL/Events/Process/Probe.bpf.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ int BPF_PROG(sched_process_fork, const struct task_struct *parent, const struct
7575
size = ebpf_resolve_path_to_string(field->data, &child->fs->pwd, child);
7676
ebpf_vl_field__set_size(&event->vl_fields, field, size);
7777

78-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
78+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
7979

8080
out:
8181
return 0;
@@ -165,7 +165,7 @@ int BPF_PROG(sched_process_exec,
165165
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, binprm->filename);
166166
ebpf_vl_field__set_size(&event->vl_fields, field, size);
167167

168-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
168+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
169169

170170
out:
171171
return 0;
@@ -219,7 +219,7 @@ static int taskstats_exit__enter(const struct task_struct *task, int group_dead)
219219
size = ebpf_resolve_pids_ss_cgroup_path_to_string(field->data, task);
220220
ebpf_vl_field__set_size(&event->vl_fields, field, size);
221221

222-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
222+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
223223

224224
out:
225225
return 0;
@@ -315,7 +315,7 @@ int BPF_PROG(module_load, struct module *mod)
315315
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, mod->srcversion);
316316
ebpf_vl_field__set_size(&event->vl_fields, field, size);
317317

318-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
318+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
319319

320320
out:
321321
return 0;
@@ -448,7 +448,7 @@ int tracepoint_syscalls_sys_enter_memfd_create(struct trace_event_raw_sys_enter
448448
goto out;
449449
ebpf_vl_field__set_size(&event->vl_fields, field, size);
450450

451-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
451+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
452452

453453
out:
454454
return 0;
@@ -562,7 +562,7 @@ static int output_tty_event(struct ebpf_tty_dev *slave, const void *base, size_t
562562
}
563563

564564
ebpf_vl_field__set_size(&event->vl_fields, field, len_cap);
565-
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
565+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
566566
out:
567567
return ret;
568568
}

GPL/Events/Varlen.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
// We can't use the ringbuf reserve/commit API if we want to output an event
1818
// with variable length fields as we won't know the event size in advance, so
1919
// we create events on the event_buffer_map if this is the case and output them
20-
// with bpf_ringbuf_output.
20+
// with ebpf_ringbuf_write.
2121
//
2222
// If the event has no variable length parameters (i.e. is always a fixed
2323
// size). bpf_ringbuf_reserve/bpf_ringbuf_submit should be used instead to

non-GPL/Events/EventsTrace/EventsTrace.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const char argp_program_doc[] =
3535
"[--process-setgid] [--process-tty-write] [--process-memfd_create] [--process-shmget] "
3636
"[--process-ptrace] [--process-load_module]\n"
3737
"[--net-conn-accept] [--net-conn-attempt] [--net-conn-closed]\n"
38-
"[--print-features-on-init] [--unbuffer-stdout] [--libbpf-verbose]\n";
38+
"[--print-features-on-init] [--stats|-s] [--unbuffer-stdout] [--libbpf-verbose]\n";
3939

4040
// Somewhat kludgy way of ensuring argp doesn't print the EBPF_* constants that
4141
// happen to be valid ASCII values as short options. We pass these enum values
@@ -120,6 +120,7 @@ static const struct argp_option opts[] = {
120120
"Print network connection closed events", 0},
121121
{"print-features-on-init", 'i', NULL, false,
122122
"Print a message with feature information when probes have been successfully loaded", 1},
123+
{"stats", 's', NULL, false, "Print event statistics", 0},
123124
{"unbuffer-stdout", 'u', NULL, false, "Disable userspace stdout buffering", 2},
124125
{"libbpf-verbose", 'v', NULL, false, "Log verbose libbpf logs to stderr", 2},
125126
{},
@@ -132,6 +133,7 @@ bool g_print_features_init = 0;
132133
bool g_features_printed = 0;
133134
bool g_unbuffer_stdout = 0;
134135
bool g_libbpf_verbose = 0;
136+
bool g_stats = 0;
135137

136138
static error_t parse_arg(int key, char *arg, struct argp_state *state)
137139
{
@@ -148,6 +150,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
148150
case 'a':
149151
g_events_env = UINT64_MAX;
150152
break;
153+
case 's':
154+
g_stats = 1;
155+
break;
151156
case FILE_DELETE:
152157
case FILE_CREATE:
153158
case FILE_RENAME:
@@ -1179,6 +1184,14 @@ int main(int argc, char **argv)
11791184
fprintf(stderr, "Failed to poll event context %d: %s\n", err, strerror(-err));
11801185
break;
11811186
}
1187+
if (g_stats) {
1188+
struct ebpf_event_stats ees;
1189+
1190+
if (ebpf_event_ctx__read_stats(ctx, &ees) == 0)
1191+
printf("sent %lu lost %lu\n", ees.sent, ees.lost);
1192+
else
1193+
fprintf(stderr, "Failed to read stats: %s\n", strerror(errno));
1194+
}
11821195
}
11831196

11841197
ebpf_event_ctx__destroy(&ctx);

non-GPL/Events/Lib/EbpfEvents.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,28 @@ int ebpf_event_ctx__poll(struct ebpf_event_ctx *ctx, int timeout)
797797
return ring_buffer__poll(ctx->ringbuf, timeout);
798798
}
799799

800+
int ebpf_event_ctx__read_stats(struct ebpf_event_ctx *ctx, struct ebpf_event_stats *ees)
801+
{
802+
struct ebpf_event_stats pcpu_ees[libbpf_num_possible_cpus()];
803+
uint32_t zero = 0;
804+
int i;
805+
806+
if (!ctx || !ees)
807+
return -1;
808+
if (bpf_map__lookup_elem(ctx->probe->maps.ringbuf_stats, &zero, sizeof(zero), pcpu_ees,
809+
sizeof(pcpu_ees), 0) != 0) {
810+
return -1;
811+
}
812+
813+
memset(ees, 0, sizeof(*ees));
814+
for (i = 0; i < libbpf_num_possible_cpus(); i++) {
815+
ees->lost += pcpu_ees[i].lost;
816+
ees->sent += pcpu_ees[i].sent;
817+
}
818+
819+
return 0;
820+
}
821+
800822
int ebpf_event_ctx__consume(struct ebpf_event_ctx *ctx)
801823
{
802824
if (!ctx)

non-GPL/Events/Lib/EbpfEvents.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ int ebpf_event_ctx__next(struct ebpf_event_ctx *ctx, int timeout);
5454
*/
5555
int ebpf_event_ctx__poll(struct ebpf_event_ctx *ctx, int timeout);
5656

57+
/* Read event statistics into ees.
58+
*
59+
* returns 0 on success or less than 0 on failure.
60+
*/
61+
int ebpf_event_ctx__read_stats(struct ebpf_event_ctx *ctx, struct ebpf_event_stats *ees);
62+
5763
/* Consumes as many events as possible from the event context and returns the
5864
* number consumed. Does not poll. This is good if you are polling outside
5965
* this library.

0 commit comments

Comments
 (0)