From 4602557478dbc73e295bfaecb26f180df3dd598f Mon Sep 17 00:00:00 2001 From: yunwei37 <1067852565@qq.com> Date: Fri, 20 Oct 2023 13:28:33 +0000 Subject: [PATCH] add user_ring_buffer example --- src/35-user-ringbuf/.gitignore | 8 ++ src/35-user-ringbuf/Makefile | 141 +++++++++++++++++++++++ src/35-user-ringbuf/user_ringbuf.bpf.c | 55 +++++++++ src/35-user-ringbuf/user_ringbuf.c | 151 +++++++++++++++++++++++++ src/35-user-ringbuf/user_ringbuf.h | 19 ++++ 5 files changed, 374 insertions(+) create mode 100644 src/35-user-ringbuf/.gitignore create mode 100644 src/35-user-ringbuf/Makefile create mode 100644 src/35-user-ringbuf/user_ringbuf.bpf.c create mode 100644 src/35-user-ringbuf/user_ringbuf.c create mode 100644 src/35-user-ringbuf/user_ringbuf.h diff --git a/src/35-user-ringbuf/.gitignore b/src/35-user-ringbuf/.gitignore new file mode 100644 index 0000000..d501860 --- /dev/null +++ b/src/35-user-ringbuf/.gitignore @@ -0,0 +1,8 @@ +.vscode +package.json +*.o +*.skel.json +*.skel.yaml +package.yaml +ecli +user_ringbuf diff --git a/src/35-user-ringbuf/Makefile b/src/35-user-ringbuf/Makefile new file mode 100644 index 0000000..c64958d --- /dev/null +++ b/src/35-user-ringbuf/Makefile @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +OUTPUT := .output +CLANG ?= clang +LIBBPF_SRC := $(abspath ../third_party/libbpf/src) +BPFTOOL_SRC := $(abspath ../third_party/bpftool/src) +LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool +LIBBLAZESYM_SRC := $(abspath ../third_party/blazesym/) +LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a) +LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h) +ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ + | sed 's/arm.*/arm/' \ + | sed 's/aarch64/arm64/' \ + | sed 's/ppc64le/powerpc/' \ + | sed 's/mips.*/mips/' \ + | sed 's/riscv64/riscv/' \ + | sed 's/loongarch64/loongarch/') +VMLINUX := ../third_party/vmlinux/$(ARCH)/vmlinux.h +# Use our own libbpf API headers and Linux UAPI headers distributed with +# libbpf to avoid dependency on system-wide headers, which could be missing or +# outdated +INCLUDES := -I$(OUTPUT) -I../third_party/libbpf/include/uapi -I$(dir $(VMLINUX)) +CFLAGS := -g -Wall +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS = user_ringbuf # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall + +CARGO ?= $(shell which cargo) +ifeq ($(strip $(CARGO)),) +BZS_APPS := +else +BZS_APPS := # profile +APPS += $(BZS_APPS) +# Required by libblazesym +ALL_LDFLAGS += -lrt -ldl -lpthread -lm +endif + +# Get Clang's default includes on this system. We'll explicitly add these dirs +# to the includes list when compiling with `-target bpf` because otherwise some +# architecture-specific dirs will be "missing" on some architectures/distros - +# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, +# sys/cdefs.h etc. might be missing. +# +# Use '-idirafter': Don't interfere with include mechanics except where the +# build would have failed anyways. +CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - &1 \ + | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') + +ifeq ($(V),1) + Q = + msg = +else + Q = @ + msg = @printf ' %-8s %s%s\n' \ + "$(1)" \ + "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ + "$(if $(3), $(3))"; + MAKEFLAGS += --no-print-directory +endif + +define allow-override + $(if $(or $(findstring environment,$(origin $(1))),\ + $(findstring command line,$(origin $(1)))),,\ + $(eval $(1) = $(2))) +endef + +$(call allow-override,CC,$(CROSS_COMPILE)cc) +$(call allow-override,LD,$(CROSS_COMPILE)ld) + +.PHONY: all +all: $(APPS) + +.PHONY: clean +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) $(APPS) + +$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): + $(call msg,MKDIR,$@) + $(Q)mkdir -p $@ + +# Build libbpf +$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf + $(call msg,LIB,$@) + $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ + OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ + INCLUDEDIR= LIBDIR= UAPIDIR= \ + install + +# Build bpftool +$(BPFTOOL): | $(BPFTOOL_OUTPUT) + $(call msg,BPFTOOL,$@) + $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap + + +$(LIBBLAZESYM_SRC)/target/release/libblazesym.a:: + $(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release + +$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) + $(call msg,LIB, $@) + $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@ + +$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) + $(call msg,LIB,$@) + $(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@ + +# Build BPF code +$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) + $(call msg,BPF,$@) + $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ + $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ + -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + +# Generate BPF skeletons +$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton $< > $@ + +# Build user-space code +$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h + +$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ + +$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER) + +$(BZS_APPS): $(LIBBLAZESYM_OBJ) + +# Build application binary +$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + +# delete failed targets +.DELETE_ON_ERROR: + +# keep intermediate (.skel.h, .bpf.o, etc) targets +.SECONDARY: diff --git a/src/35-user-ringbuf/user_ringbuf.bpf.c b/src/35-user-ringbuf/user_ringbuf.bpf.c new file mode 100644 index 0000000..b24be57 --- /dev/null +++ b/src/35-user-ringbuf/user_ringbuf.bpf.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include +#include +#include "user_ringbuf.h" + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_USER_RINGBUF); + __uint(max_entries, 256 * 1024); +} user_ringbuf SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} kernel_ringbuf SEC(".maps"); + +int read = 0; + +static long +do_nothing_cb(struct bpf_dynptr *dynptr, void *context) +{ + struct event *e; + pid_t pid; + /* get PID and TID of exiting thread/process */ + pid = bpf_get_current_pid_tgid() >> 32; + + /* reserve sample from BPF ringbuf */ + e = bpf_ringbuf_reserve(&kernel_ringbuf, sizeof(*e), 0); + if (!e) + return 0; + + e->pid = pid; + bpf_get_current_comm(&e->comm, sizeof(e->comm)); + + /* send data to user-space for post-processing */ + bpf_ringbuf_submit(e, 0); + __sync_fetch_and_add(&read, 1); + return 0; +} + +SEC("tracepoint/syscalls/sys_exit_kill") +int kill_exit(struct trace_event_raw_sys_exit *ctx) +{ + long num_samples; + int err = 0; + + num_samples = bpf_user_ringbuf_drain(&user_ringbuf, do_nothing_cb, NULL, 0); + + return 0; +} \ No newline at end of file diff --git a/src/35-user-ringbuf/user_ringbuf.c b/src/35-user-ringbuf/user_ringbuf.c new file mode 100644 index 0000000..c1d9aeb --- /dev/null +++ b/src/35-user-ringbuf/user_ringbuf.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include +#include +#include +#include +#include +#include +#include "user_ringbuf.h" +#include "user_ringbuf.skel.h" + +static void drain_current_samples(void) +{ + printf("Draining current samples...\n"); +} + +static int write_samples(struct user_ring_buffer *ringbuf) +{ + int i, err = 0; + struct user_sample *entry; + + entry = user_ring_buffer__reserve(ringbuf, sizeof(*entry)); + if (!entry) { + err = -errno; + goto done; + } + + entry->i = getpid(); + strcpy(entry->comm, "hello"); + + int read = snprintf(entry->comm, sizeof(entry->comm), "%u", i); + if (read <= 0) { + /* Assert on the error path to avoid spamming logs with + * mostly success messages. + */ + err = read; + user_ring_buffer__discard(ringbuf, entry); + goto done; + } + + user_ring_buffer__submit(ringbuf, entry); + +done: + drain_current_samples(); + + return err; +} + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + return vfprintf(stderr, format, args); +} + +static volatile bool exiting = false; + +static void sig_handler(int sig) +{ + exiting = true; +} + + struct user_ring_buffer *user_ringbuf = NULL; + +static int handle_event(void *ctx, void *data, size_t data_sz) +{ + const struct event *e = data; + struct tm *tm; + char ts[32]; + time_t t; + + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + + printf("%-8s %-5s %-16s %-7d\n", + ts, "SIGN", e->comm, e->pid); + write_samples(user_ringbuf); + return 0; +} + +int main(int argc, char **argv) +{ + struct ring_buffer *rb = NULL; + struct user_ringbuf_bpf *skel; + int err; + + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); + + /* Cleaner handling of Ctrl-C */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + /* Load and verify BPF application */ + skel = user_ringbuf_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Parameterize BPF code with minimum duration parameter */ + skel->bss->read = 0; + + /* Load & verify BPF programs */ + err = user_ringbuf_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + /* Attach tracepoints */ + err = user_ringbuf_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + /* Set up ring buffer polling */ + rb = ring_buffer__new(bpf_map__fd(skel->maps.kernel_ringbuf), handle_event, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto cleanup; + } + user_ringbuf = user_ring_buffer__new(bpf_map__fd(skel->maps.user_ringbuf), NULL); + + write_samples(user_ringbuf); + + /* Process events */ + printf("%-8s %-5s %-16s %-7s %-7s %s\n", + "TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE"); + while (!exiting) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + } + +cleanup: + /* Clean up */ + ring_buffer__free(rb); + user_ringbuf_bpf__destroy(skel); + user_ring_buffer__free(user_ringbuf); + + return err < 0 ? -err : 0; +} diff --git a/src/35-user-ringbuf/user_ringbuf.h b/src/35-user-ringbuf/user_ringbuf.h new file mode 100644 index 0000000..28275ef --- /dev/null +++ b/src/35-user-ringbuf/user_ringbuf.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2020 Facebook */ +#ifndef __BOOTSTRAP_H +#define __BOOTSTRAP_H + +#define TASK_COMM_LEN 16 +#define MAX_FILENAME_LEN 127 + +struct event { + int pid; + char comm[TASK_COMM_LEN]; +}; + +struct user_sample { + int i; + char comm[TASK_COMM_LEN]; +}; + +#endif /* __BOOTSTRAP_H */