blob: 7d932ef305914af17ade8f1553766d2bd10c560c [file] [log] [blame]
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2022 Hengqi Chen */
#include "compat.h"
#include "trace_helpers.h"
#include <stdlib.h>
#include <errno.h>
#include <bpf/libbpf.h>
#define PERF_BUFFER_PAGES 64
struct bpf_buffer {
struct bpf_map *events;
void *inner;
bpf_buffer_sample_fn fn;
void *ctx;
int type;
};
static void perfbuf_sample_fn(void *ctx, int cpu, void *data, __u32 size)
{
struct bpf_buffer *buffer = ctx;
bpf_buffer_sample_fn fn;
fn = buffer->fn;
if (!fn)
return;
(void)fn(buffer->ctx, data, size);
}
struct bpf_buffer *bpf_buffer__new(struct bpf_map *events, struct bpf_map *heap)
{
struct bpf_buffer *buffer;
bool use_ringbuf;
int type;
use_ringbuf = probe_ringbuf();
if (use_ringbuf) {
bpf_map__set_autocreate(heap, false);
type = BPF_MAP_TYPE_RINGBUF;
} else {
bpf_map__set_type(events, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
bpf_map__set_key_size(events, sizeof(int));
bpf_map__set_value_size(events, sizeof(int));
type = BPF_MAP_TYPE_PERF_EVENT_ARRAY;
}
buffer = calloc(1, sizeof(*buffer));
if (!buffer) {
errno = ENOMEM;
return NULL;
}
buffer->events = events;
buffer->type = type;
return buffer;
}
int bpf_buffer__open(struct bpf_buffer *buffer, bpf_buffer_sample_fn sample_cb,
bpf_buffer_lost_fn lost_cb, void *ctx)
{
int fd, type;
void *inner;
fd = bpf_map__fd(buffer->events);
type = buffer->type;
switch (type) {
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
buffer->fn = sample_cb;
buffer->ctx = ctx;
inner = perf_buffer__new(fd, PERF_BUFFER_PAGES, perfbuf_sample_fn, lost_cb, buffer, NULL);
break;
case BPF_MAP_TYPE_RINGBUF:
inner = ring_buffer__new(fd, sample_cb, ctx, NULL);
break;
default:
return 0;
}
if (!inner)
return -errno;
buffer->inner = inner;
return 0;
}
int bpf_buffer__poll(struct bpf_buffer *buffer, int timeout_ms)
{
switch (buffer->type) {
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
return perf_buffer__poll(buffer->inner, timeout_ms);
case BPF_MAP_TYPE_RINGBUF:
return ring_buffer__poll(buffer->inner, timeout_ms);
default:
return -EINVAL;
}
}
void bpf_buffer__free(struct bpf_buffer *buffer)
{
if (!buffer)
return;
switch (buffer->type) {
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
perf_buffer__free(buffer->inner);
break;
case BPF_MAP_TYPE_RINGBUF:
ring_buffer__free(buffer->inner);
break;
}
free(buffer);
}