diff --git a/Makefile b/Makefile index 75454a5d..87024ea1 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,9 @@ build: ## build the package cd tools/cli-rs && cargo build release: ## build the package - cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=0 -DCMAKE_BUILD_TYPE:STRING=Release + cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=0 \ + -DCMAKE_BUILD_TYPE:STRING=Release \ + -DBPFTIME_ENABLE_LTO=1 cmake --build build --config Release --target install cd tools/cli-rs && cargo build --release diff --git a/benchmark/hash_maps/.gitignore b/benchmark/bpf-syscall/.gitignore similarity index 69% rename from benchmark/hash_maps/.gitignore rename to benchmark/bpf-syscall/.gitignore index 3b80e015..e606ec01 100644 --- a/benchmark/hash_maps/.gitignore +++ b/benchmark/bpf-syscall/.gitignore @@ -5,8 +5,7 @@ package.json *.skel.yaml package.yaml ecli -bootstrap .output -opensnoop -victim -dump* +test +uretprobe +bpf-syscall-bench diff --git a/benchmark/bpf-syscall/Makefile b/benchmark/bpf-syscall/Makefile new file mode 100644 index 00000000..5ead5370 --- /dev/null +++ b/benchmark/bpf-syscall/Makefile @@ -0,0 +1,101 @@ +# 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 +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 -I../../third_party/libbpf/include/uapi -I$(dir $(VMLINUX)) +CFLAGS := -g -Wall +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS = bpf-syscall-bench # minimal minimal_legacy 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 + +.output/bpf-syscall-bench.o: bpf-syscall-bench.c $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +# 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/benchmark/bpf-syscall/bpf-syscall-bench.c b/benchmark/bpf-syscall/bpf-syscall-bench.c new file mode 100644 index 00000000..4fc6a426 --- /dev/null +++ b/benchmark/bpf-syscall/bpf-syscall-bench.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/filter.h" + +int prog_fd; + +// The timespec struct holds seconds and nanoseconds +struct timespec start_time, end_time; + +void start_timer() +{ + clock_gettime(CLOCK_MONOTONIC_RAW, &start_time); +} + +void end_timer() +{ + clock_gettime(CLOCK_MONOTONIC_RAW, &end_time); +} + +__attribute_noinline__ uint64_t __benchmark_test_function1(const char *a, int b, + uint64_t c) +{ + return bpf_prog_test_run_opts(prog_fd, NULL); +} + +typedef uint64_t (*benchmark_test_function_t)(const char *, int, uint64_t); + +static double get_elapsed_time() +{ + long seconds = end_time.tv_sec - start_time.tv_sec; + long nanoseconds = end_time.tv_nsec - start_time.tv_nsec; + if (start_time.tv_nsec > end_time.tv_nsec) { // clock underflow + --seconds; + nanoseconds += 1000000000; + } + printf("Elapsed time: %ld.%09ld seconds\n", seconds, nanoseconds); + return seconds * 1.0 + nanoseconds / 1000000000.0; +} + +static double get_function_time(benchmark_test_function_t func, int iter) +{ + start_timer(); + // test base line + for (int i = 0; i < iter; i++) { + func("hello", i % 4, i); + } + end_timer(); + double time = get_elapsed_time(); + return time; +} + +void do_benchmark_userspace(benchmark_test_function_t func, int iter) +{ + double base_line_time; + + printf("a[b] + c for %d times\n", iter); + base_line_time = get_function_time(func, iter); + printf("Average time usage %lf ns\n\n", + (base_line_time) / iter * 1000000000.0); +} + +#define do_benchmark_func(func, iter) \ + do { \ + printf("Benchmarking %s\n", #func); \ + do_benchmark_userspace(func ,iter); \ + } while (0) + +int test_run_time() +{ + puts(""); + struct bpf_insn trival_prog_insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", + trival_prog_insns, 2, NULL); + if (prog_fd < 0) { + printf("Failed to load BPF program: %s\n", strerror(prog_fd)); + exit(1); + } + int iter = 100 * 1000; + do_benchmark_func(__benchmark_test_function1, iter); + return 0; +} + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, + va_list args) +{ + return vfprintf(stderr, format, args); +} + +int main(int argc, char **argv) +{ + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); + + test_run_time(); + + return 0; +} diff --git a/benchmark/hash_map/.gitignore b/benchmark/hash_map/.gitignore new file mode 100644 index 00000000..3028a00f --- /dev/null +++ b/benchmark/hash_map/.gitignore @@ -0,0 +1,10 @@ +.vscode +package.json +*.o +*.skel.json +*.skel.yaml +package.yaml +ecli +.output +test +uprobe diff --git a/benchmark/hash_maps/Makefile b/benchmark/hash_map/Makefile similarity index 96% rename from benchmark/hash_maps/Makefile rename to benchmark/hash_map/Makefile index 128e7af7..87a6287c 100644 --- a/benchmark/hash_maps/Makefile +++ b/benchmark/hash_map/Makefile @@ -21,7 +21,7 @@ INCLUDES := -I$(OUTPUT) -I../../third_party/libbpf/include/uapi -I$(dir $(VMLINU CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = opensnoop # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall +APPS = uprobe # minimal minimal_legacy kprobe fentry usdt sockfilter tc ksyscall CARGO ?= $(shell which cargo) ifeq ($(strip $(CARGO)),) @@ -66,7 +66,7 @@ $(call allow-override,CC,$(CROSS_COMPILE)cc) $(call allow-override,LD,$(CROSS_COMPILE)ld) .PHONY: all -all: $(APPS) victim +all: $(APPS) .PHONY: clean clean: @@ -136,6 +136,3 @@ $(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) # keep intermediate (.skel.h, .bpf.o, etc) targets .SECONDARY: - -victim: victim.cpp - $(CXX) -Wall -g victim.cpp -o victim diff --git a/benchmark/hash_map/README.md b/benchmark/hash_map/README.md new file mode 100644 index 00000000..c0b80d5a --- /dev/null +++ b/benchmark/hash_map/README.md @@ -0,0 +1,42 @@ +# benchmark of hash maps + +- __benchmark_test_function1: hashmap bpf_map_lookup_elem +- __benchmark_test_function2: hashmap bpf_map_delete_elem +- __benchmark_test_function3: hashmap bpf_map_update_elem + +run the uprobe: + +```console +$ LD_PRELOAD=build/runtime/syscall-server/libbpftime-syscall-server.so benchmark/hash_map/uprobe +manager constructed +global_shm_open_type 0 for bpftime_maps_shm +Closing 3 +libbpf: loading object 'uprobe_bpf' from buffer +libbpf: elf: section(2) .symtab, size 120, link 1, flags 0, type=2 +... +loaded ebpf program... +... +``` + +in another terminal, run the benchmark: + +```console +$ LD_PRELOAD=build/runtime/agent/libbpftime-agent.so benchmark/test + +Benchmarking __benchmark_test_function1 +a[b] + c for 100000 times +Elapsed time: 0.038217773 seconds +Average time usage 382.177730 ns + +Benchmarking __benchmark_test_function2 +a[b] + c for 100000 times +Elapsed time: 0.020004455 seconds +Average time usage 200.044550 ns + +Benchmarking __benchmark_test_function3 +a[b] + c for 100000 times +Elapsed time: 0.047916014 seconds +Average time usage 479.160140 ns + +INFO [34534]: Global shm destructed +``` diff --git a/benchmark/hash_map/uprobe.bpf.c b/benchmark/hash_map/uprobe.bpf.c new file mode 100644 index 00000000..ca9bc011 --- /dev/null +++ b/benchmark/hash_map/uprobe.bpf.c @@ -0,0 +1,43 @@ +#define BPF_NO_GLOBAL_DATA +#include +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, u32); + __type(value, u64); +} libc_malloc_calls_total SEC(".maps"); + +SEC("uprobe/benchmark/test:__benchmark_test_function3") +int test_update(struct pt_regs *ctx) +{ + u32 key = 0; + u64 value = 0; + bpf_map_update_elem(&libc_malloc_calls_total, &key, &value, 0); + + return 0; +} + +SEC("uprobe/benchmark/test:__benchmark_test_function2") +int test_delete(struct pt_regs *ctx) +{ + u32 key = 0; + u64 value = 0; + bpf_map_delete_elem(&libc_malloc_calls_total, &key); + + return 0; +} + +SEC("uprobe/benchmark/test:__benchmark_test_function1") +int test_lookup(struct pt_regs *ctx) +{ + u32 key = 0; + u64 value = 0; + bpf_map_lookup_elem(&libc_malloc_calls_total, &key); + + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/benchmark/hash_map/uprobe.c b/benchmark/hash_map/uprobe.c new file mode 100644 index 00000000..c0ed0303 --- /dev/null +++ b/benchmark/hash_map/uprobe.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "uprobe.skel.h" +#define warn(...) fprintf(stderr, __VA_ARGS__) + +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; +} + +int main(int argc, char **argv) +{ + struct uprobe_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 = uprobe_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Load & verify BPF programs */ + err = uprobe_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + err = uprobe_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + while (!exiting) { + sleep(1); + printf("loaded ebpf program...\n"); + } +cleanup: + /* Clean up */ + uprobe_bpf__destroy(skel); + + return err < 0 ? -err : 0; +} diff --git a/benchmark/hash_maps/README.md b/benchmark/hash_maps/README.md deleted file mode 100644 index 821cce30..00000000 --- a/benchmark/hash_maps/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# opensnoop - -Here is a example that demonstrates the usage of userspace syscall trace - -## Usage - -Build example, see [documents/build-and-test.md](../../documents/build-and-test.md) for how to build the project. - -Build the example: - -```sh -make -``` - -Start server: - -```sh -$ sudo ~/.bpftime/bpftime load benchmark/hash_maps/opensnoop -[2023-10-01 16:46:43.409] [info] manager constructed -[2023-10-01 16:46:43.409] [info] global_shm_open_type 0 for bpftime_maps_shm -[2023-10-01 16:46:43.410] [info] Closing 3 -[2023-10-01 16:46:43.411] [info] mmap64 0 -[2023-10-01 16:46:43.411] [info] Calling mocked mmap64 -[2023-10-01 16:46:43.411] [info] Closing 3 -[2023-10-01 16:46:43.411] [info] Closing 3 -[2023-10-01 16:46:43.423] [info] Closing 3 -[2023-10-01 16:46:43.423] [info] Closing 3 -``` - -Start victim: - -```console -$ sudo ~/.bpftime/bpftime start -s benchmark/hash_maps/victim -[2023-10-01 16:46:58.855] [info] Entering new main.. -[2023-10-01 16:46:58.855] [info] Using agent /root/.bpftime/libbpftime-agent.so -[2023-10-01 16:46:58.856] [info] Page zero setted up.. -[2023-10-01 16:46:58.856] [info] Rewriting segment from 559a839b4000 to 559a839b5000 -[2023-10-01 16:46:58.859] [info] Rewriting segment from 7f130aa22000 to 7f130ab9a000 -[2023-10-01 16:46:59.749] [info] Rewriting segment from 7f130acc3000 to 7f130adb0000 -[2023-10-01 16:47:00.342] [info] Rewriting segment from 7f130ae9c000 to 7f130afcd000 -[2023-10-01 16:47:01.072] [info] Rewriting segment from 7f130b125000 to 7f130b1a3000 -..... -[2023-10-01 16:47:02.084] [info] Attach successfully -[2023-10-01 16:47:02.084] [info] Transformer exiting.. - -Opening test.txt.. -VICTIM: get fd 3 -VICTIM: closing fd -Opening test.txt.. -VICTIM: get fd 3 -VICTIM: closing f -``` - -## Basic benchmark - -Measures average time usage when calling open(2) - -### Without bpf - -```console -Average open time usage 24216.86932ns, count 176 -``` - -### Use kernel bpf - -```console -Average open time usage 29619.08015ns, count 262 -``` - -### Use userspace syscall trace (ubpf) - -```console -Average open time usage 85299.80000ns, count 153 -``` - -### Use userspace syscall trace (llvm-jit) - -```console -Average open time usage 25202.18121ns, count 95 -``` diff --git a/benchmark/hash_maps/opensnoop.bpf.c b/benchmark/hash_maps/opensnoop.bpf.c deleted file mode 100644 index d7656067..00000000 --- a/benchmark/hash_maps/opensnoop.bpf.c +++ /dev/null @@ -1,109 +0,0 @@ -#define BPF_NO_GLOBAL_DATA - -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2019 Facebook -// Copyright (c) 2020 Netflix -#include -#include -#include "opensnoop.h" - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 10240); - __type(key, u32); - __type(value, struct args_t); -} start SEC(".maps"); - -static __always_inline bool valid_uid(uid_t uid) -{ - return uid != INVALID_UID; -} - -bool trace_allowed(u32 tgid, u32 pid) -{ - return true; -} - -SEC("tracepoint/syscalls/sys_enter_open") -int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter *ctx) -{ - u64 id = bpf_get_current_pid_tgid(); - /* use kernel terminology here for tgid/pid: */ - u32 tgid = id >> 32; - u32 pid = id; - - /* store arg info for later lookup */ - if (trace_allowed(tgid, pid)) { - struct args_t args = {}; - args.fname = (const char *)ctx->args[0]; - args.flags = (int)ctx->args[1]; - bpf_map_update_elem(&start, &pid, &args, 0); - } - return 0; -} - -SEC("tracepoint/syscalls/sys_enter_openat") -int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter *ctx) -{ - u64 id = bpf_get_current_pid_tgid(); - /* use kernel terminology here for tgid/pid: */ - u32 tgid = id >> 32; - u32 pid = id; - - /* store arg info for later lookup */ - if (trace_allowed(tgid, pid)) { - struct args_t args = {}; - args.fname = (const char *)ctx->args[1]; - args.flags = (int)ctx->args[2]; - bpf_map_update_elem(&start, &pid, &args, 0); - } - return 0; -} - -int trace_exit(struct trace_event_raw_sys_exit *ctx) -{ - struct event event = {}; - struct args_t *ap; - uintptr_t stack[3]; - int ret; - u32 pid = bpf_get_current_pid_tgid(); - - ap = bpf_map_lookup_elem(&start, &pid); - if (!ap) - return 0; /* missed entry */ - ret = ctx->ret; - - /* event data */ - event.pid = bpf_get_current_pid_tgid() >> 32; - event.uid = bpf_get_current_uid_gid(); - bpf_get_current_comm(&event.comm, sizeof(event.comm)); - bpf_probe_read_user_str(&event.fname, sizeof(event.fname), ap->fname); - event.flags = ap->flags; - event.ret = ret; - - bpf_get_stack(ctx, &stack, sizeof(stack), BPF_F_USER_STACK); - /* Skip the first address that is usually the syscall it-self */ - event.callers[0] = stack[1]; - event.callers[1] = stack[2]; - - bpf_printk("open pid=%d, fname=%s, comm=%s\n", event.pid, event.fname, - event.comm); - -cleanup: - bpf_map_delete_elem(&start, &pid); - return 0; -} - -SEC("tracepoint/syscalls/sys_exit_open") -int tracepoint__syscalls__sys_exit_open(struct trace_event_raw_sys_exit *ctx) -{ - return trace_exit(ctx); -} - -SEC("tracepoint/syscalls/sys_exit_openat") -int tracepoint__syscalls__sys_exit_openat(struct trace_event_raw_sys_exit *ctx) -{ - return trace_exit(ctx); -} - -char LICENSE[] SEC("license") = "GPL"; diff --git a/benchmark/hash_maps/opensnoop.c b/benchmark/hash_maps/opensnoop.c deleted file mode 100644 index 639feeb7..00000000 --- a/benchmark/hash_maps/opensnoop.c +++ /dev/null @@ -1,253 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -// Copyright (c) 2019 Facebook -// Copyright (c) 2020 Netflix -// -// Based on opensnoop(8) from BCC by Brendan Gregg and others. -// 14-Feb-2020 Brendan Gregg Created this. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "opensnoop.h" -#include "opensnoop.skel.h" - -/* Tune the buffer size and wakeup rate. These settings cope with roughly - * 50k opens/sec. - */ -#define PERF_BUFFER_PAGES 64 -#define PERF_BUFFER_TIME_MS 10 - -/* Set the poll timeout when no events occur. This can affect -d accuracy. */ -#define PERF_POLL_TIMEOUT_MS 100 - -#define NSEC_PER_SEC 1000000000ULL - -static volatile sig_atomic_t exiting = 0; - -#ifdef USE_BLAZESYM -static blazesym *symbolizer; -#endif - -static struct env { - pid_t pid; - pid_t tid; - uid_t uid; - int duration; - bool verbose; - bool timestamp; - bool print_uid; - bool extended; - bool failed; - char *name; -#ifdef USE_BLAZESYM - bool callers; -#endif -} env = { .uid = INVALID_UID }; - -const char *argp_program_version = "opensnoop 0.1"; -const char *argp_program_bug_address = - "https://github.com/iovisor/bcc/tree/master/libbpf-tools"; -const char argp_program_doc[] = - "Trace open family syscalls\n" - "\n" - "USAGE: opensnoop [-h] [-T] [-U] [-x] [-p PID] [-t TID] [-u UID] [-d DURATION]\n" -#ifdef USE_BLAZESYM - " [-n NAME] [-e] [-c]\n" -#else - " [-n NAME] [-e]\n" -#endif - "\n" - "EXAMPLES:\n" - " ./opensnoop # trace all open() syscalls\n" - " ./opensnoop -T # include timestamps\n" - " ./opensnoop -U # include UID\n" - " ./opensnoop -x # only show failed opens\n" - " ./opensnoop -p 181 # only trace PID 181\n" - " ./opensnoop -t 123 # only trace TID 123\n" - " ./opensnoop -u 1000 # only trace UID 1000\n" - " ./opensnoop -d 10 # trace for 10 seconds only\n" - " ./opensnoop -n main # only print process names containing \"main\"\n" - " ./opensnoop -e # show extended fields\n" -#ifdef USE_BLAZESYM - " ./opensnoop -c # show calling functions\n" -#endif - ""; - -static const struct argp_option opts[] = { - { "extended-fields", 'e', NULL, 0, "Print extended fields" }, - { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, - { "name", 'n', "NAME", 0, "Trace process names containing this" }, - { "pid", 'p', "PID", 0, "Process ID to trace" }, - { "tid", 't', "TID", 0, "Thread ID to trace" }, - { "timestamp", 'T', NULL, 0, "Print timestamp" }, - { "uid", 'u', "UID", 0, "User ID to trace" }, - { "print-uid", 'U', NULL, 0, "Print UID" }, - { "verbose", 'v', NULL, 0, "Verbose debug output" }, - { "failed", 'x', NULL, 0, "Failed opens only" }, -#ifdef USE_BLAZESYM - { "callers", 'c', NULL, 0, "Show calling functions" }, -#endif - {}, -}; - -static error_t parse_arg(int key, char *arg, struct argp_state *state) -{ - static int pos_args; - long int pid, uid; - - switch (key) { - case 'e': - env.extended = true; - break; - case 'h': - argp_usage(state); - break; - case 'T': - env.timestamp = true; - break; - case 'U': - env.print_uid = true; - break; - case 'v': - env.verbose = true; - break; - case 'x': - env.failed = true; - break; - case 'n': - errno = 0; - env.name = arg; - break; - case 'p': - errno = 0; - pid = strtol(arg, NULL, 10); - if (errno || pid <= 0) { - fprintf(stderr, "Invalid PID: %s\n", arg); - argp_usage(state); - } - env.pid = pid; - break; - case 't': - errno = 0; - pid = strtol(arg, NULL, 10); - if (errno || pid <= 0) { - fprintf(stderr, "Invalid TID: %s\n", arg); - argp_usage(state); - } - env.tid = pid; - break; - case 'u': - errno = 0; - uid = strtol(arg, NULL, 10); - if (errno || uid < 0 || uid >= INVALID_UID) { - fprintf(stderr, "Invalid UID %s\n", arg); - argp_usage(state); - } - env.uid = uid; - break; -#ifdef USE_BLAZESYM - case 'c': - env.callers = true; - break; -#endif - case ARGP_KEY_ARG: - if (pos_args++) { - fprintf(stderr, - "Unrecognized positional argument: %s\n", arg); - argp_usage(state); - } - errno = 0; - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static int libbpf_print_fn(enum libbpf_print_level level, const char *format, - va_list args) -{ - if (level == LIBBPF_DEBUG && !env.verbose) - return 0; - return vfprintf(stderr, format, args); -} - -static void sig_int(int signo) -{ - exiting = 1; -} - - -void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) -{ - fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu); -} - -int main(int argc, char **argv) -{ - LIBBPF_OPTS(bpf_object_open_opts, open_opts); - static const struct argp argp = { - .options = opts, - .parser = parse_arg, - .doc = argp_program_doc, - }; - struct opensnoop_bpf *obj; - int err; - - err = argp_parse(&argp, argc, argv, 0, NULL, NULL); - if (err) - return err; - - libbpf_set_print(libbpf_print_fn); - - obj = opensnoop_bpf__open_opts(&open_opts); - if (!obj) { - fprintf(stderr, "failed to open BPF object\n"); - return 1; - } - - err = opensnoop_bpf__load(obj); - if (err) { - fprintf(stderr, "failed to load BPF object: %d\n", err); - goto cleanup; - } - - err = opensnoop_bpf__attach(obj); - if (err) { - fprintf(stderr, "failed to attach BPF programs\n"); - goto cleanup; - } - /* print headers */ - if (env.timestamp) - printf("%-8s ", "TIME"); - if (env.print_uid) - printf("%-7s ", "UID"); - printf("%-6s %-16s %3s %3s ", "PID", "COMM", "FD", "ERR"); - if (env.extended) - printf("%-8s ", "FLAGS"); - printf("%s", "PATH"); - printf("\n"); - - - if (signal(SIGINT, sig_int) == SIG_ERR) { - fprintf(stderr, "can't set signal handler: %s\n", - strerror(errno)); - err = 1; - goto cleanup; - } - - /* main: poll */ - while (!exiting) - ; - -cleanup: - opensnoop_bpf__destroy(obj); - - return err != 0; -} diff --git a/benchmark/hash_maps/opensnoop.h b/benchmark/hash_maps/opensnoop.h deleted file mode 100644 index 97d76ee5..00000000 --- a/benchmark/hash_maps/opensnoop.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ -#ifndef __OPENSNOOP_H -#define __OPENSNOOP_H - -#define TASK_COMM_LEN 16 -#define NAME_MAX 255 -#define INVALID_UID ((uid_t)-1) - -struct args_t { - const char *fname; - int flags; -}; - -struct event { - /* user terminology for pid: */ - __u64 ts; - pid_t pid; - uid_t uid; - int ret; - int flags; - __u64 callers[2]; - char comm[TASK_COMM_LEN]; - char fname[NAME_MAX]; -}; - -#endif /* __OPENSNOOP_H */ diff --git a/benchmark/hash_maps/victim.cpp b/benchmark/hash_maps/victim.cpp deleted file mode 100644 index f9abfb7d..00000000 --- a/benchmark/hash_maps/victim.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -uint64_t total_time = 0; -uint64_t count = 0; - -void sigint_handler(int) -{ - double avg = (double)total_time / (double)count; - std::cout << "Average open time usage " << std::fixed - << std::setprecision(5) << avg << "ns, " - << " count " << count << std::endl; - exit(0); -} - -int main() -{ - { - struct sigaction sa; - - sa.sa_handler = sigint_handler; - sa.sa_flags = SA_RESTART; - if (sigaction(SIGINT, &sa, NULL) == -1) { - std::cerr << "Failed to set signal handler" - << std::endl; - return 1; - } - } - puts(""); - while (1) { - // DO NOT CHANGE THIS call to puts - puts("Opening test.txt.."); - auto time_begin = std::chrono::high_resolution_clock::now(); - int fd = open("test.txt", O_CREAT | O_RDWR); - auto time_end = std::chrono::high_resolution_clock::now(); - auto diff = - std::chrono::duration_cast( - (time_end - time_begin)); - count += 1; - total_time += diff.count(); - std::cout << "VICTIM: get fd " << fd << std::endl; - usleep(500 * 1000); - std::cout << "VICTIM: closing fd" << std::endl; - close(fd); - } - return 0; -} diff --git a/benchmark/ssl-nginx/.gitignore b/benchmark/ssl-nginx/.gitignore new file mode 100644 index 00000000..38edc3b7 --- /dev/null +++ b/benchmark/ssl-nginx/.gitignore @@ -0,0 +1,4 @@ +access.log +nginx.pid +nginx-error.txt +sslsniff \ No newline at end of file diff --git a/benchmark/ssl-nginx/README.md b/benchmark/ssl-nginx/README.md new file mode 100644 index 00000000..992a326d --- /dev/null +++ b/benchmark/ssl-nginx/README.md @@ -0,0 +1,110 @@ +# nginx test for sslsniff + +This test is to show the performance impact of kernel sslsniff and userspace sslsniff. sslsniff is a tool to intercept the ssl handshake and print the packet content of encrypted ssl handshake. The similar approach is very common in modern observability tools and security tools. + +This test shoes that: + +1. kernel sslsniff can significantly reduce the performance of nginx, lead to a 2x performance drop. + +The test program of sslsniff is from bcc and [bpftime/example](https://github.com/eunomia-bpf/bpftime/tree/master/example/sslsniff). The userspace part modified to not print the packet content out. + +## Environment + +test env: + +```console +$ uname -a +Linux yunwei37server 6.2.0-35-generic #35-Ubuntu SMP PREEMPT_DYNAMIC Tue Oct 3 13:14:56 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux +$ nginx -v +nginx version: nginx/1.22.0 (Ubuntu) +$ ./wrk -v +wrk 4.2.0 [epoll] Copyright (C) 2012 Will Glozer +``` + +## Setup + +start nginx server + +```sh +nginx -c $(pwd)/nginx.conf -p $(pwd) +``` + +## Test for no effect + +```console +$ ./wrk https://127.0.0.1:4043/index.html -c 100 -d 10 +Running 10s test @ https://127.0.0.1:4043/index.html + 2 threads and 100 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 9.18ms 18.95ms 165.75ms 94.73% + Req/Sec 9.71k 5.05k 32.14k 87.56% + 189498 requests in 10.02s, 49.70MB read +Requests/sec: 18916.15 +Transfer/sec: 4.96MB +``` + +## Test for kernel sslsniff + +in one console + +```console +$ sudo ./sslsniff +OpenSSL path: /lib/x86_64-linux-gnu/libssl.so.3 +GnuTLS path: /lib/x86_64-linux-gnu/libgnutls.so.30 +NSS path: /lib/x86_64-linux-gnu/libnspr4.so +FUNC TIME(s) COMM PID LEN +lost 194 events on CPU #2 +lost 61 events on CPU #3 +^CTotal events: 260335 +``` + +This sslsniff is from bpftime/example/sslsniff/sslsniff. The userspace part modified to not print the packet content out. + +In another shell + +```console +$ ./wrk https://127.0.0.1:4043/index.html -c 100 -d 10 +Running 10s test @ https://127.0.0.1:4043/index.html + 2 threads and 100 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 13.59ms 11.30ms 156.13ms 91.27% + Req/Sec 4.01k 0.95k 5.95k 71.50% + 80242 requests in 10.10s, 21.04MB read +Requests/sec: 7948.46 +Transfer/sec: 2.08MB +``` + +## test for userspace sslsniff + +Note: you need to config bpftime to: + +1. No locks in hash maps and array maps +2. Using ubpf JIT +3. Using LTO + +in one console, start userspace sslsniff + +```sh +sudo ~/.bpftime/bpftime load example/sslsniff/sslsniff +``` + +in another console, restart nginx + +```sh +sudo ~/.bpftime/bpftime start nginx -- -c nginx.conf -p benchmark/ssl-nginx +# or sudo LD_PRELOAD=build/runtime/agent/libbpftime-agent.so nginx -c nginx.conf -p benchmark/ssl-nginx +``` + +in another console, run wrk + +```console +$ ./wrk https://127.0.0.1:4043/index.html -c 100 -d 10 +Running 10s test @ https://127.0.0.1:4043/index.html + 2 threads and 100 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 6.32ms 6.18ms 164.79ms 97.80% + Req/Sec 8.41k 1.30k 11.20k 87.37% + 166451 requests in 10.04s, 43.65MB read +Requests/sec: 16580.88 +Transfer/sec: 4.35MB +``` diff --git a/benchmark/ssl-nginx/index.html b/benchmark/ssl-nginx/index.html new file mode 100644 index 00000000..69e9da41 --- /dev/null +++ b/benchmark/ssl-nginx/index.html @@ -0,0 +1,2 @@ + + diff --git a/benchmark/ssl-nginx/nginx.conf b/benchmark/ssl-nginx/nginx.conf new file mode 100644 index 00000000..5aaab9bc --- /dev/null +++ b/benchmark/ssl-nginx/nginx.conf @@ -0,0 +1,46 @@ +user root; +daemon off; +events { + worker_connections 768; +} + +# pid ./nginx.pid; +worker_processes 1; +http { + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + types_hash_max_size 2048; + client_max_body_size 2048m; + + + default_type application/octet-stream; + + ## + # SSL Settings + ## + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + + ## + # Logging Settings + ## + + access_log access.log; + + server { + listen 4043 ssl; + ssl_certificate test.cer; + ssl_certificate_key test.key; + location / { + index index.html; + root .; + } + } + +} diff --git a/benchmark/ssl-nginx/test.cer b/benchmark/ssl-nginx/test.cer new file mode 100644 index 00000000..312761af --- /dev/null +++ b/benchmark/ssl-nginx/test.cer @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfkCFEBpLqXph386N2QonbFS8D2OPWh+MA0GCSqGSIb3DQEBCwUAMEUx +CzAJBgNVBAYTAkNOMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMxMDE5MTU1OTAwWhcNMjQxMDE4MTU1 +OTAwWjBFMQswCQYDVQQGEwJDTjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE +CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAvbp21zeGy67IrXKLZ7TON05sMJGqaczuMvFPn+hMVzpEx9cb +4pgjEPL7u/RN+ylmHh/XZ+Du5SEsOQ9F1LG88OljmPW5nseHIqZqXL0lg6+6wjBq +vUe4CjL+0H6+fJpRj3YXY9Jq1R+mFu2FjoL6LyknwZ20c7dhYizCXeaWsx58Xgpr +S9WWtZXWG4O3tWdy8jsRxArZe/meM2yb8ZzRrPvMdhdW2ZlJjGZbLKcxyVxt0yZK +xR/ZyrjtheeVHyIUD94QbMW1SoGuQh1OfqpeGMog+ordYjWJWMlcvpQ2HE7sQ7AC +UCrJOeDFy5VlkzeFoffMbyg+zYg/7Ka8e+xZHQIDAQABMA0GCSqGSIb3DQEBCwUA +A4IBAQAmHpgjkn42hv4oK/9q1e2uZpzV5ao3nB7wZXWainxdYpGGtI5rdZnLXoCz +ibD1ZtzKf76pmNpnwoF3ibNW1PBsz2q3oFzJ9R9+BkO8JIV1+0i+1oXa0hKElqTy +PkzVh2UZ0Q11jJUH5n1vBbgjgFkjoaNUslZKC8GAF+e5z57O5MiDGTf+J0FFbM9W +Jby8ntTsdF5iaU6P8pDRzROpg7KH0NDSd8LA2m6Nm00RiHjv6stJ0yMrVK3j1gG8 +zwoNZ4aLYrOQyFnVJ4nZUJ55WwTTrsOWH5z8BbCHTxHHB9OoGB8tnY18y17YLHnn +dbZJA3E2gh7JU40p5mfiXq8Q1Vdm +-----END CERTIFICATE----- diff --git a/benchmark/ssl-nginx/test.csr b/benchmark/ssl-nginx/test.csr new file mode 100644 index 00000000..81ac0b19 --- /dev/null +++ b/benchmark/ssl-nginx/test.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICijCCAXICAQAwRTELMAkGA1UEBhMCQ04xEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAL26dtc3hsuuyK1yi2e0zjdObDCRqmnM7jLxT5/o +TFc6RMfXG+KYIxDy+7v0TfspZh4f12fg7uUhLDkPRdSxvPDpY5j1uZ7HhyKmaly9 +JYOvusIwar1HuAoy/tB+vnyaUY92F2PSatUfphbthY6C+i8pJ8GdtHO3YWIswl3m +lrMefF4Ka0vVlrWV1huDt7VncvI7EcQK2Xv5njNsm/Gc0az7zHYXVtmZSYxmWyyn +MclcbdMmSsUf2cq47YXnlR8iFA/eEGzFtUqBrkIdTn6qXhjKIPqK3WI1iVjJXL6U +NhxO7EOwAlAqyTngxcuVZZM3haH3zG8oPs2IP+ymvHvsWR0CAwEAAaAAMA0GCSqG +SIb3DQEBCwUAA4IBAQCdFFEJ2ZYToooJnp5mA8/G7TR/TGLVU/usaIAuqMOZ3SFr +1iMF/6Ep9waEAYKdgBMZ4Ag4rNsSylvi+qJ7NDD2n6o/ZEjwEbzOzr8ZAFtx44d8 +SZJMZar1PL/CFIkid7vfKy8y+vFu7P41tDRv66ncShUg41EbIj8VIguPlH26yZzM ++j2YzCQWpvfi+Ck17nHd3ighH378rYR9wZweHa9qbQnS25MuGEL56ZfeWya6H0N8 +I3EZKq5NHpLhytJWROh85UUK7l00aVdTM2AaRKBlXZGAmu3U4/19otb2qutEApat +Q8XbGFbWR7EhHxYfU34GyO5wBlFMIjOOBQAz5vGb +-----END CERTIFICATE REQUEST----- diff --git a/benchmark/ssl-nginx/test.key b/benchmark/ssl-nginx/test.key new file mode 100644 index 00000000..2e7d245e --- /dev/null +++ b/benchmark/ssl-nginx/test.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9unbXN4bLrsit +cotntM43TmwwkappzO4y8U+f6ExXOkTH1xvimCMQ8vu79E37KWYeH9dn4O7lISw5 +D0XUsbzw6WOY9bmex4cipmpcvSWDr7rCMGq9R7gKMv7Qfr58mlGPdhdj0mrVH6YW +7YWOgvovKSfBnbRzt2FiLMJd5pazHnxeCmtL1Za1ldYbg7e1Z3LyOxHECtl7+Z4z +bJvxnNGs+8x2F1bZmUmMZlsspzHJXG3TJkrFH9nKuO2F55UfIhQP3hBsxbVKga5C +HU5+ql4YyiD6it1iNYlYyVy+lDYcTuxDsAJQKsk54MXLlWWTN4Wh98xvKD7NiD/s +prx77FkdAgMBAAECggEABYNnh6ykssK8qDVinEa2vNVuIkfY0jC3SyitqsO/lG6F +8K61yaH9rUX6U5aJAfszBsYTGc3FwDa4uzpHn3BIyfwQ6H4F52iFHjYVc8P7u2S9 +/gYmwk2qdfElfTNYm0E0Wd9RiDeQBJXIfVsTJbZkDYLaFZpc09JGexRLSxrJH0xU +MNDT/wb6Jjn5LYIoBFuLCiH7SMX5kEH63cUZjmtzbp/NI3Zqoa2h/li5sTdEy2lP +BU6Qt5ntSOvhGbSZdYrKm8xotkzPEeSKXGNEwUnoZ1vkt2FAn9obi4kpdGLTtzhT +J6AgyvyzMysQ3rR7aVXxzL3fnvBu1a7KEgpbzvXUuwKBgQDylcW3wJIdJzLmxZyI +a2quvjJlZFjjIm/65XDNdicnIANhC7CJCpqUOVPur3UYIEra3wIRbJlXDWrnm2KC +kZ0lDCqFdaFkcp+LqsBtSgJpfJE+CCuXKmd73VEJ58L3FsP1P9xJGIY9QlM4bOCi +eBIOObzrbpKPpthN7LQJAAcGcwKBgQDIOGkelQx2ikdubRmUvssES4TtZ6P9o9ve +zFCJ9/dFJANDepeYirZ39WdB7NSYFEMBjzkR3sW9ofs6m4hjdgUQQRJ2TIG4t2Mr +cQRSCGZl+UnKky/TqXW6YpCfxaP7Lk/6mg0+xOwn3ceXTaHAon53vZZ8AQMZZagm +i9oPKsCuLwKBgEUopeGpKP7eughItkO+O//tgXiejyjSHJKu7uXklWz2F4CHUKEB +QOiZH3Cg3QUvz6duhKxrBZxn2t6E0iTfVqnFOioqSwTUGOA8ofeGbgGc3duNgL/r ++uy5P+1PAapn9x3oQukj8MFuM0OW5sPq71rW4yFLuMTzkB9HKFyYgWg5AoGBAIue +ssctvuwmz1sU32LXjXDoTMDUpgnoqxl5tqwPa7IeQ9xZb79i841BZwkU4sdOUc+T +lYN9qrzraOrcrMZW9X75Maum/ErYL2KKxC2ESAvSQ//slcxu/nbD5wBTbV9qU0F5 +DcV4wwml4VoEO8i+R5yua3L7rbKXLiP7IEu1T3nnAoGBAOg53+1RsiLvPqS9hBOA +WsxGPFTm4WuSrWZsQdMfOYsLWUFRffxzPBCEpK8yu45/qomvVzh/fKqGJycc2A6d +5IskLbNakzNUUNh/wlPKJX/qX6kdh/HN46ekyXWSkp5QTVKvgbN5kCQPFbsqI4Fw +0do+vq5pfVSoKPqwbtk45PWi +-----END PRIVATE KEY----- diff --git a/benchmark/test.c b/benchmark/test.c index c5ac7aec..bccd1eb7 100644 --- a/benchmark/test.c +++ b/benchmark/test.c @@ -22,6 +22,20 @@ __attribute_noinline__ uint64_t __benchmark_test_function3(const char *a, int b, return a[b] + c; } +__attribute_noinline__ uint64_t __benchmark_test_function2(const char *a, int b, + uint64_t c) +{ + return a[b] + c; +} + +__attribute_noinline__ uint64_t __benchmark_test_function1(const char *a, int b, + uint64_t c) +{ + return a[b] + c; +} + +typedef uint64_t (*benchmark_test_function_t)(const char *, int, uint64_t); + static double get_elapsed_time() { long seconds = end_time.tv_sec - start_time.tv_sec; @@ -34,32 +48,40 @@ static double get_elapsed_time() return seconds * 1.0 + nanoseconds / 1000000000.0; } -static double get_function_time(int iter) +static double get_function_time(benchmark_test_function_t func, int iter) { start_timer(); // test base line for (int i = 0; i < iter; i++) { - __benchmark_test_function3("hello", i % 4, i); + func("hello", i % 4, i); } end_timer(); double time = get_elapsed_time(); return time; } -void do_benchmark_userspace(int iter) +void do_benchmark_userspace(benchmark_test_function_t func, int iter) { double base_line_time, after_hook_time, total_time; printf("a[b] + c for %d times\n", iter); - base_line_time = get_function_time(iter); + base_line_time = get_function_time(func, iter); printf("Average time usage %lf ns\n\n", (base_line_time) / iter * 1000000000.0); } +#define do_benchmark_func(func, iter) \ + do { \ + printf("Benchmarking %s\n", #func); \ + do_benchmark_userspace(func ,iter); \ + } while (0) + int main() { puts(""); int iter = 100 * 1000; - do_benchmark_userspace(iter); + do_benchmark_func(__benchmark_test_function1, iter); + do_benchmark_func(__benchmark_test_function2, iter); + do_benchmark_func(__benchmark_test_function3, iter); return 0; } diff --git a/benchmark/uprobe/Makefile b/benchmark/uprobe/Makefile index 87a6287c..c381a332 100644 --- a/benchmark/uprobe/Makefile +++ b/benchmark/uprobe/Makefile @@ -17,7 +17,7 @@ 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)) +INCLUDES := -I$(OUTPUT) -I../../third_party/libbpf/include/ -I../../third_party/libbpf/include/uapi -I$(dir $(VMLINUX)) CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 57291a17..7f2a166c 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -64,6 +64,7 @@ function(set_project_warnings project_name) -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable + -Wno-missing-field-initializers ) if (BPFTIME_WARNINGS_AS_ERRORS) diff --git a/cmake/StandardSettings.cmake b/cmake/StandardSettings.cmake index 8020a70a..48c57094 100644 --- a/cmake/StandardSettings.cmake +++ b/cmake/StandardSettings.cmake @@ -51,7 +51,7 @@ if(BPFTIME_ENABLE_LTO) endif() endif() -option(BPFTIME_ENABLE_CCACHE "Enable the usage of Ccache, in order to speed up rebuild times." ON) +option(BPFTIME_ENABLE_CCACHE "Enable the usage of Ccache, in order to speed up rebuild times." OFF) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) diff --git a/daemon/kernel/bpf_tracer.bpf.c b/daemon/kernel/bpf_tracer.bpf.c index 43b536d9..6577a03c 100644 --- a/daemon/kernel/bpf_tracer.bpf.c +++ b/daemon/kernel/bpf_tracer.bpf.c @@ -384,7 +384,9 @@ int tracepoint__syscalls__sys_exit_bpf(struct trace_event_raw_sys_exit *ctx) // uprobe path buffer for bpf_probe_read_user_str char old_uprobe_path[PATH_LENTH] = "\0"; -struct perf_event_attr new_attr = {}; +// avoid type mismatch in userspace program +// struct perf_event_attr new_attr = {}; will result in compile error +char new_attr_buffer[sizeof(struct perf_event_attr)] = "\0"; static __always_inline int process_perf_event_open_enter(struct trace_event_raw_sys_enter *ctx) @@ -393,18 +395,18 @@ process_perf_event_open_enter(struct trace_event_raw_sys_enter *ctx) if (!attr) { return 0; } - bpf_probe_read_user(&new_attr, sizeof(new_attr), attr); - - if (new_attr.type == uprobe_perf_type) { + bpf_probe_read_user(&new_attr_buffer, sizeof(new_attr_buffer), attr); + struct perf_event_attr* new_attr_pointer = &new_attr_buffer; + if (new_attr_pointer->type == uprobe_perf_type) { // found uprobe if (enable_replace_uprobe) { - if (can_hook_uprobe_at(new_attr.probe_offset)) { - u64 old_offset = new_attr.probe_offset; - new_attr.probe_offset = 0; + if (can_hook_uprobe_at(new_attr_pointer->probe_offset)) { + u64 old_offset = new_attr_pointer->probe_offset; + new_attr_pointer->probe_offset = 0; long size = bpf_probe_read_user_str( old_uprobe_path, sizeof(old_uprobe_path), - (void *)new_attr.uprobe_path); + (void *)new_attr_pointer->uprobe_path); if (size <= 0) { // no uprobe path return 0; @@ -413,10 +415,10 @@ process_perf_event_open_enter(struct trace_event_raw_sys_enter *ctx) size = PATH_LENTH; } bpf_probe_write_user( - (void *)new_attr.uprobe_path, + (void *)new_attr_pointer->uprobe_path, &new_uprobe_path, (size_t)size); - bpf_probe_write_user(attr, &new_attr, - sizeof(new_attr)); + bpf_probe_write_user(attr, new_attr_pointer, + sizeof(*new_attr_pointer)); // This probe creation request should be // executed in userspace bpf_printk( diff --git a/daemon/user/bpf_tracer.cpp b/daemon/user/bpf_tracer.cpp index f21ee990..1bab1c28 100644 --- a/daemon/user/bpf_tracer.cpp +++ b/daemon/user/bpf_tracer.cpp @@ -144,9 +144,12 @@ int bpftime::start_daemon(struct daemon_config env) /* initialize global data (filtering options) */ obj->rodata->target_pid = env.pid; obj->rodata->enable_replace_prog = env.enable_replace_prog; -// strncpy(obj->rodata->new_uprobe_path, env.new_uprobe_path, -// PATH_LENTH); -#warning FIXME: currently using `/a` as the replacing executable path to uprobe perf event in the kernel, since long strings (such as bpftime_daemon it self) may break userspace memory. Find a better way to solve this in the future + + // strncpy(obj->rodata->new_uprobe_path, env.new_uprobe_path, PATH_LENTH); + // TODO: currently using `/a` as the replacing executable path to uprobe + // perf event in the kernel, since long strings (such as bpftime_daemon it self) + // may break userspace memory. + // Find a better way to solve this in the future strncpy(obj->rodata->new_uprobe_path, "/a", PATH_LENTH); obj->rodata->enable_replace_uprobe = env.enable_replace_uprobe; diff --git a/documents/build-and-test.md b/documents/build-and-test.md index 0852bfa7..ba0d9450 100644 --- a/documents/build-and-test.md +++ b/documents/build-and-test.md @@ -1,7 +1,7 @@ # Building and run bpftime ## Install Dependencies -> `gcc` >= 12.0.0 `clang` >= 16.0.0 + Install the required packages: ```bash @@ -10,10 +10,10 @@ sudo apt install -y --no-install-recommends \ binutils-dev libyaml-cpp-dev llvm git submodule update --init --recursive ``` ---- -We've tested on Ubuntu 23.04. -On Ubuntu 20.04 ,you should manually switch to gcc-12. +We've tested on Ubuntu 23.04. The recommended `gcc` >= 12.0.0 `clang` >= 16.0.0 + +On Ubuntu 20.04, you may need to manually switch to gcc-12. ### Build and install cli tool diff --git a/runtime/include/bpf_attach_ctx.hpp b/runtime/include/bpf_attach_ctx.hpp index 144d9f4b..99418ed8 100644 --- a/runtime/include/bpf_attach_ctx.hpp +++ b/runtime/include/bpf_attach_ctx.hpp @@ -59,11 +59,9 @@ class bpf_attach_ctx { // create bpf_attach_ctx from handler_manager in shared memory int init_attach_ctx_from_handlers(const handler_manager *manager, - agent_config &config); + const agent_config &config); // create bpf_attach_ctx from handler_manager in global_shared_memory - int init_attach_ctx_from_handlers(agent_config &config); - // attach progs with fds to the fds in manager - int attach_progs_in_manager(const handler_manager *manager); + int init_attach_ctx_from_handlers(const agent_config &config); // // find the function by name in current process // // must be called after init attach_ctx diff --git a/runtime/include/bpftime_config.hpp b/runtime/include/bpftime_config.hpp index 773ffae8..db4bd137 100644 --- a/runtime/include/bpftime_config.hpp +++ b/runtime/include/bpftime_config.hpp @@ -6,7 +6,7 @@ namespace bpftime struct agent_config { bool debug = false; // Enable JIT? - bool jit_enabled = false; + bool jit_enabled = true; // helper groups bool enable_kernel_helper_group = true; diff --git a/runtime/include/bpftime_prog.hpp b/runtime/include/bpftime_prog.hpp index 988a5757..57eeffb8 100644 --- a/runtime/include/bpftime_prog.hpp +++ b/runtime/include/bpftime_prog.hpp @@ -39,6 +39,7 @@ class bpftime_prog { struct ebpf_vm *vm; bool jitted; + // used in jit ebpf_jit_fn fn; std::vector insns; @@ -47,6 +48,9 @@ class bpftime_prog { // ffi ctx struct bpftime_ffi_ctx *ffi_ctx; + + // kernel runtime + }; } // namespace bpftime diff --git a/runtime/include/bpftime_shm.hpp b/runtime/include/bpftime_shm.hpp index 55bfc7c1..4a49ba73 100644 --- a/runtime/include/bpftime_shm.hpp +++ b/runtime/include/bpftime_shm.hpp @@ -148,7 +148,8 @@ enum class bpf_prog_type { extern const shm_open_type global_shm_open_type; -bpftime::agent_config &bpftime_get_agent_config(); +const bpftime::agent_config &bpftime_get_agent_config(); +void bpftime_set_agent_config(bpftime::agent_config &cfg); } // namespace bpftime extern "C" { diff --git a/runtime/src/attach/bpf_attach_ctx.cpp b/runtime/src/attach/bpf_attach_ctx.cpp index 9c9ace8e..5d09458e 100644 --- a/runtime/src/attach/bpf_attach_ctx.cpp +++ b/runtime/src/attach/bpf_attach_ctx.cpp @@ -21,7 +21,7 @@ namespace bpftime { -static int load_prog_and_helpers(bpftime_prog *prog, agent_config &config) +static int load_prog_and_helpers(bpftime_prog *prog, const agent_config &config) { if (config.enable_kernel_helper_group) { bpftime_helper_group::get_kernel_utils_helper_group() @@ -38,7 +38,7 @@ static int load_prog_and_helpers(bpftime_prog *prog, agent_config &config) return prog->bpftime_prog_load(config.jit_enabled); } -int bpf_attach_ctx::init_attach_ctx_from_handlers(agent_config &config) +int bpf_attach_ctx::init_attach_ctx_from_handlers(const agent_config &config) { const handler_manager *manager = shm_holder.global_shared_memory.get_manager(); @@ -162,7 +162,7 @@ int64_t bpf_attach_ctx::run_syscall_hooker(int64_t sys_nr, int64_t arg1, // create a attach context and progs from handlers int bpf_attach_ctx::init_attach_ctx_from_handlers( - const handler_manager *manager, agent_config &config) + const handler_manager *manager, const agent_config &config) { // Maintain perf_event fd -> [(prog fd,bpftime_prog*)] std::map > > diff --git a/runtime/src/bpf_helper.cpp b/runtime/src/bpf_helper.cpp index 8059801c..6dc5fc9b 100644 --- a/runtime/src/bpf_helper.cpp +++ b/runtime/src/bpf_helper.cpp @@ -73,13 +73,15 @@ uint64_t bpftime_ktime_get_coarse_ns(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t bpftime_get_current_pid_tgid(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { - return ((uint64_t)getpid() << 32) | getpid(); + static int pid = getpid(); + return ((uint64_t)pid << 32) | pid; } uint64_t bpf_get_current_uid_gid(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { - return (((uint64_t)getgid()) << 32) | getuid(); + static int gid = getgid(); + return (((uint64_t)gid) << 32) | gid; } uint64_t bpftime_ktime_get_ns(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) diff --git a/runtime/src/bpftime_shm_internal.cpp b/runtime/src/bpftime_shm_internal.cpp index 560fb913..930fe642 100644 --- a/runtime/src/bpftime_shm_internal.cpp +++ b/runtime/src/bpftime_shm_internal.cpp @@ -563,6 +563,7 @@ bool bpftime_shm::is_perf_event_handler_fd(int fd) const auto &handler = get_handler(fd); return std::holds_alternative(handler); } + bool bpftime_shm::is_software_perf_event_handler_fd(int fd) const { if (!is_perf_event_handler_fd(fd)) @@ -571,11 +572,25 @@ bool bpftime_shm::is_software_perf_event_handler_fd(int fd) const return hd.type == bpf_event_type::PERF_TYPE_SOFTWARE; } -bpftime::agent_config &bpftime_get_agent_config() +void bpftime_shm::set_agent_config(const struct agent_config &config) +{ + if (agent_config == nullptr) { + spdlog::error("agent_config is nullptr, set error"); + return; + } + *agent_config = config; +} + +const bpftime::agent_config &bpftime_get_agent_config() { return shm_holder.global_shared_memory.get_agent_config(); } +void bpftime_set_agent_config(bpftime::agent_config &cfg) +{ + shm_holder.global_shared_memory.set_agent_config(cfg); +} + std::optional bpftime_shm::get_software_perf_event_raw_buffer(int fd, size_t buffer_sz) const { diff --git a/runtime/src/bpftime_shm_internal.hpp b/runtime/src/bpftime_shm_internal.hpp index 9f3b4d2e..bb1f45f0 100644 --- a/runtime/src/bpftime_shm_internal.hpp +++ b/runtime/src/bpftime_shm_internal.hpp @@ -33,10 +33,12 @@ class bpftime_shm { public: // Get the configuration object - struct agent_config &get_agent_config() + const struct agent_config &get_agent_config() { return *agent_config; } + // Set the configuration object + void set_agent_config(const struct agent_config &config); // Check whether a certain pid was already equipped with syscall tracer // Using a set stored in the shared memory bool check_syscall_trace_setup(int pid); diff --git a/runtime/syscall-server/syscall_server_utils.cpp b/runtime/syscall-server/syscall_server_utils.cpp index 579f112a..4f27118f 100644 --- a/runtime/syscall-server/syscall_server_utils.cpp +++ b/runtime/syscall-server/syscall_server_utils.cpp @@ -11,47 +11,43 @@ static bool already_setup = false; static bool disable_mock = true; using namespace bpftime; -static void process_token(const std::string_view& token, agent_config& config) { - if (token == "ffi") { - spdlog::info("Enabling ffi helper group"); - config.enable_ffi_helper_group = true; - } else if (token == "kernel") { - spdlog::info("Enabling kernel helper group"); - config.enable_kernel_helper_group = true; - } else if (token == "shm_map") { - spdlog::info("Enabling shm_map helper group"); - config.enable_shm_maps_helper_group = true; - } else { - spdlog::warn("Unknown helper group: {}", token); - } +static void process_token(const std::string_view &token, agent_config &config) +{ + if (token == "ffi") { + spdlog::info("Enabling ffi helper group"); + config.enable_ffi_helper_group = true; + } else if (token == "kernel") { + spdlog::info("Enabling kernel helper group"); + config.enable_kernel_helper_group = true; + } else if (token == "shm_map") { + spdlog::info("Enabling shm_map helper group"); + config.enable_shm_maps_helper_group = true; + } else { + spdlog::warn("Unknown helper group: {}", token); + } } -static void process_helper_sv(const std::string_view& str, const char delimiter, agent_config& config) { - std::string::size_type start = 0; - std::string::size_type end = str.find(delimiter); +static void process_helper_sv(const std::string_view &str, const char delimiter, + agent_config &config) +{ + std::string::size_type start = 0; + std::string::size_type end = str.find(delimiter); - while (end != std::string::npos) { - process_token(str.substr(start, end - start), config); - start = end + 1; - end = str.find(delimiter, start); - } + while (end != std::string::npos) { + process_token(str.substr(start, end - start), config); + start = end + 1; + end = str.find(delimiter, start); + } - // Handle the last token, if any - if (start < str.size()) { - process_token(str.substr(start), config); - } + // Handle the last token, if any + if (start < str.size()) { + process_token(str.substr(start), config); + } } -void start_up() +const bpftime::agent_config& set_agent_config_from_env() { - if (already_setup) - return; - already_setup = true; - spdlog::info("Initialize syscall server"); - spdlog::cfg::load_env_levels(); - spdlog::set_pattern("[%Y-%m-%d %H:%M:%S][%^%l%$][%t] %v"); - bpftime_initialize_global_shm(shm_open_type::SHM_REMOVE_AND_CREATE); - auto &agent_config = bpftime_get_agent_config(); + bpftime::agent_config agent_config; if (const char *custom_helpers = getenv("BPFTIME_HELPER_GROUPS"); custom_helpers != nullptr) { agent_config.enable_kernel_helper_group = @@ -67,6 +63,22 @@ void start_up() agent_config.enable_shm_maps_helper_group = agent_config.enable_ffi_helper_group = true; } + const char *use_jit = getenv("BPFTIME_USE_JIT"); + agent_config.jit_enabled = use_jit != nullptr; + bpftime_set_agent_config(agent_config); + return bpftime_get_agent_config(); +} + +void start_up() +{ + if (already_setup) + return; + already_setup = true; + spdlog::info("Initialize syscall server"); + spdlog::cfg::load_env_levels(); + spdlog::set_pattern("[%Y-%m-%d %H:%M:%S][%^%l%$][%t] %v"); + bpftime_initialize_global_shm(shm_open_type::SHM_REMOVE_AND_CREATE); + const auto &agent_config = set_agent_config_from_env(); #ifdef ENABLE_BPFTIME_VERIFIER std::vector helper_ids; std::map diff --git a/third_party/bpftool b/third_party/bpftool index b01941c8..8485b9fb 160000 --- a/third_party/bpftool +++ b/third_party/bpftool @@ -1 +1 @@ -Subproject commit b01941c8f7890489f09713348a7d89567538504b +Subproject commit 8485b9fba9b3bb3bd311b00632d2d22c0eee2e13